summaryrefslogtreecommitdiffstats
path: root/content/common/gpu/media/vaapi_wrapper.h
blob: 8951702c1eb4a72cf9ecf742b9d5eb7be6b297c9 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
// Copyright 2013 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
// This file contains an implementation of VaapiWrapper, used by
// VaapiVideoDecodeAccelerator and VaapiH264Decoder for decode,
// and VaapiVideoEncodeAccelerator for encode, to interface
// with libva (VA-API library for hardware video codec).

#ifndef CONTENT_COMMON_GPU_MEDIA_VAAPI_WRAPPER_H_
#define CONTENT_COMMON_GPU_MEDIA_VAAPI_WRAPPER_H_

#include <set>
#include <vector>

#include "base/files/file.h"
#include "base/lazy_instance.h"
#include "base/memory/ref_counted.h"
#include "base/synchronization/lock.h"
#include "content/common/content_export.h"
#include "content/common/gpu/media/va_surface.h"
#include "media/base/video_decoder_config.h"
#include "media/base/video_frame.h"
#include "media/video/video_decode_accelerator.h"
#include "media/video/video_encode_accelerator.h"
#include "third_party/libva/va/va.h"
#include "third_party/libva/va/va_vpp.h"
#include "ui/gfx/geometry/size.h"
#if defined(USE_X11)
#include "third_party/libva/va/va_x11.h"
#endif  // USE_X11

namespace content {

// This class handles VA-API calls and ensures proper locking of VA-API calls
// to libva, the userspace shim to the HW codec driver. libva is not
// thread-safe, so we have to perform locking ourselves. This class is fully
// synchronous and its methods can be called from any thread and may wait on
// the va_lock_ while other, concurrent calls run.
//
// This class is responsible for managing VAAPI connection, contexts and state.
// It is also responsible for managing and freeing VABuffers (not VASurfaces),
// which are used to queue parameters and slice data to the HW codec,
// as well as underlying memory for VASurfaces themselves.
class CONTENT_EXPORT VaapiWrapper {
 public:
  enum CodecMode {
    kDecode,
    kEncode,
    kCodecModeMax,
  };

  // Return an instance of VaapiWrapper initialized for |va_profile| and
  // |mode|. |report_error_to_uma_cb| will be called independently from
  // reporting errors to clients via method return values.
  static scoped_ptr<VaapiWrapper> Create(
      CodecMode mode,
      VAProfile va_profile,
      const base::Closure& report_error_to_uma_cb);

  // Create VaapiWrapper for VideoCodecProfile. It maps VideoCodecProfile
  // |profile| to VAProfile.
  // |report_error_to_uma_cb| will be called independently from reporting
  // errors to clients via method return values.
  static scoped_ptr<VaapiWrapper> CreateForVideoCodec(
      CodecMode mode,
      media::VideoCodecProfile profile,
      const base::Closure& report_error_to_uma_cb);

  // Return the supported encode profiles.
  static media::VideoEncodeAccelerator::SupportedProfiles
      GetSupportedEncodeProfiles();

  // Return the supported decode profiles.
  static media::VideoDecodeAccelerator::SupportedProfiles
      GetSupportedDecodeProfiles();

  ~VaapiWrapper();

  // Create |num_surfaces| backing surfaces in driver for VASurfaces, each
  // of size |size|. Returns true when successful, with the created IDs in
  // |va_surfaces| to be managed and later wrapped in VASurfaces.
  // The client must DestroySurfaces() each time before calling this method
  // again to free the allocated surfaces first, but is not required to do so
  // at destruction time, as this will be done automatically from
  // the destructor.
  bool CreateSurfaces(const gfx::Size& size,
                      size_t num_surfaces,
                      std::vector<VASurfaceID>* va_surfaces);

  // Free all memory allocated in CreateSurfaces.
  void DestroySurfaces();

  // Create a VASurface of |va_format|, |size| and using |va_attribs|
  // attributes. The ownership of the surface is transferred to the
  // caller. It differs from surfaces created using CreateSurfaces(),
  // where VaapiWrapper is the owner of the surfaces.
  scoped_refptr<VASurface> CreateUnownedSurface(
      unsigned int va_format,
      const gfx::Size& size,
      const std::vector<VASurfaceAttrib>& va_attribs);

  // Submit parameters or slice data of |va_buffer_type|, copying them from
  // |buffer| of size |size|, into HW codec. The data in |buffer| is no
  // longer needed and can be freed after this method returns.
  // Data submitted via this method awaits in the HW codec until
  // ExecuteAndDestroyPendingBuffers() is called to execute or
  // DestroyPendingBuffers() is used to cancel a pending job.
  bool SubmitBuffer(VABufferType va_buffer_type, size_t size, void* buffer);

  // Submit a VAEncMiscParameterBuffer of given |misc_param_type|, copying its
  // data from |buffer| of size |size|, into HW codec. The data in |buffer| is
  // no longer needed and can be freed after this method returns.
  // Data submitted via this method awaits in the HW codec until
  // ExecuteAndDestroyPendingBuffers() is called to execute or
  // DestroyPendingBuffers() is used to cancel a pending job.
  bool SubmitVAEncMiscParamBuffer(VAEncMiscParameterType misc_param_type,
                                  size_t size,
                                  void* buffer);

  // Cancel and destroy all buffers queued to the HW codec via SubmitBuffer().
  // Useful when a pending job is to be cancelled (on reset or error).
  void DestroyPendingBuffers();

  // Execute job in hardware on target |va_surface_id| and destroy pending
  // buffers. Return false if Execute() fails.
  bool ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id);

#if defined(USE_X11)
  // Put data from |va_surface_id| into |x_pixmap| of size
  // |dest_size|, converting/scaling to it.
  bool PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
                            Pixmap x_pixmap,
                            gfx::Size dest_size);
#endif  // USE_X11

  // Get a VAImage from a VASurface and map it into memory. The size and format
  // are derived from the surface. Use GetVaImage() instead if |format| or
  // |size| are different from surface internal representation. The VAImage
  // should be released using the ReturnVaImage function. Returns true when
  // successful.
  bool GetDerivedVaImage(VASurfaceID va_surface_id, VAImage* image, void** mem);

  // Get a VAImage from a VASurface |va_surface_id| and map it into memory with
  // given |format| and |size|. The output is |image| and the mapped memory is
  // |mem|. If |format| doesn't equal to the internal format, the underlying
  // implementation will do format conversion if supported. |size| should be
  // smaller than or equal to the surface. If |size| is smaller, the image will
  // be cropped. The VAImage should be released using the ReturnVaImage
  // function. Returns true when successful.
  bool GetVaImage(VASurfaceID va_surface_id,
                  VAImageFormat* format,
                  const gfx::Size& size,
                  VAImage* image,
                  void** mem);

  // Release the VAImage (and the associated memory mapping) obtained from
  // GetVaImage() or GetDerivedVaImage().
  void ReturnVaImage(VAImage* image);

  // Upload contents of |frame| into |va_surface_id| for encode.
  bool UploadVideoFrameToSurface(const scoped_refptr<media::VideoFrame>& frame,
                                 VASurfaceID va_surface_id);

  // Create a buffer of |size| bytes to be used as encode output.
  bool CreateCodedBuffer(size_t size, VABufferID* buffer_id);

  // Download the contents of the buffer with given |buffer_id| into a buffer of
  // size |target_size|, pointed to by |target_ptr|. The number of bytes
  // downloaded will be returned in |coded_data_size|. |sync_surface_id| will
  // be used as a sync point, i.e. it will have to become idle before starting
  // the download. |sync_surface_id| should be the source surface passed
  // to the encode job.
  bool DownloadAndDestroyCodedBuffer(VABufferID buffer_id,
                                     VASurfaceID sync_surface_id,
                                     uint8* target_ptr,
                                     size_t target_size,
                                     size_t* coded_data_size);

  // Destroy all previously-allocated (and not yet destroyed) coded buffers.
  void DestroyCodedBuffers();

  // Blits a VASurface |va_surface_id_src| into another VASurface
  // |va_surface_id_dest| applying pixel format conversion and scaling
  // if needed.
  bool BlitSurface(VASurfaceID va_surface_id_src,
                   const gfx::Size& src_size,
                   VASurfaceID va_surface_id_dest,
                   const gfx::Size& dest_size);

  // Initialize static data before sandbox is enabled.
  static void PreSandboxInitialization();

 private:
  struct ProfileInfo {
    VAProfile va_profile;
    gfx::Size max_resolution;
  };

  class LazyProfileInfos {
   public:
    LazyProfileInfos();
    ~LazyProfileInfos();
    std::vector<ProfileInfo> GetSupportedProfileInfosForCodecMode(
        CodecMode mode);
    bool IsProfileSupported(CodecMode mode, VAProfile va_profile);

   private:
    std::vector<ProfileInfo> supported_profiles_[kCodecModeMax];
  };

  class VADisplayState {
   public:
    VADisplayState();
    ~VADisplayState();

    // |va_lock_| must be held on entry.
    bool Initialize(VAStatus* status);
    void Deinitialize(VAStatus* status);

    base::Lock* va_lock() { return &va_lock_; }
    VADisplay va_display() const { return va_display_; }

#if defined(USE_OZONE)
    void SetDrmFd(base::PlatformFile fd);
#endif  // USE_OZONE

   private:
    friend class base::LazyInstance<VADisplayState>;

    // Returns true if the VAAPI version is less than the specified version.
    bool VAAPIVersionLessThan(int major, int minor);

    // Protected by |va_lock_|.
    int refcount_;

    // Libva is not thread safe, so we have to do locking for it ourselves.
    // This lock is to be taken for the duration of all VA-API calls and for
    // the entire job submission sequence in ExecuteAndDestroyPendingBuffers().
    base::Lock va_lock_;

#if defined(USE_OZONE)
    // Drm fd used to obtain access to the driver interface by VA.
    base::ScopedFD drm_fd_;
#endif  // USE_OZONE

    // The VADisplay handle.
    VADisplay va_display_;

    // The VAAPI version.
    int major_version_, minor_version_;

    // True if vaInitialize has been called successfully.
    bool va_initialized_;
  };

  VaapiWrapper();

  bool Initialize(CodecMode mode, VAProfile va_profile);
  void Deinitialize();
  bool VaInitialize(const base::Closure& report_error_to_uma_cb);
  bool GetSupportedVaProfiles(std::vector<VAProfile>* profiles);

  // Check if |va_profile| supports |entrypoint| or not. |va_lock_| must be
  // held on entry.
  bool IsEntrypointSupported_Locked(VAProfile va_profile,
                                    VAEntrypoint entrypoint);

  // Return true if |va_profile| for |entrypoint| with |required_attribs| is
  // supported. |va_lock_| must be held on entry.
  bool AreAttribsSupported_Locked(
      VAProfile va_profile,
      VAEntrypoint entrypoint,
      const std::vector<VAConfigAttrib>& required_attribs);

  // Get maximum resolution for |va_profile| and |entrypoint| with
  // |required_attribs|. If return value is true, |resolution| is the maximum
  // resolution. |va_lock_| must be held on entry.
  bool GetMaxResolution_Locked(
      VAProfile va_profile,
      VAEntrypoint entrypoint,
      std::vector<VAConfigAttrib>& required_attribs,
      gfx::Size* resolution);

  // Destroys a |va_surface| created using CreateUnownedSurface.
  void DestroyUnownedSurface(VASurfaceID va_surface_id);

  // Initialize the video post processing context with the |size| of
  // the input pictures to be processed.
  bool InitializeVpp_Locked();

  // Deinitialize the video post processing context.
  void DeinitializeVpp();

  // Execute pending job in hardware and destroy pending buffers. Return false
  // if vaapi driver refuses to accept parameter or slice buffers submitted
  // by client, or if execution fails in hardware.
  bool Execute(VASurfaceID va_surface_id);

  // Attempt to set render mode to "render to texture.". Failure is non-fatal.
  void TryToSetVADisplayAttributeToLocalGPU();

  // Get supported profile infos for |mode|.
  std::vector<ProfileInfo> GetSupportedProfileInfosForCodecModeInternal(
      CodecMode mode);

  // Lazily initialize static data after sandbox is enabled.  Return false on
  // init failure.
  static bool PostSandboxInitialization();

  // Map VideoCodecProfile enum values to VaProfile values. This function
  // includes a workaround for crbug.com/345569. If va_profile is h264 baseline
  // and it is not supported, we try constrained baseline.
  static VAProfile ProfileToVAProfile(media::VideoCodecProfile profile,
                                      CodecMode mode);

  // Pointer to VADisplayState's member |va_lock_|. Guaranteed to be valid for
  // the lifetime of VaapiWrapper.
  base::Lock* va_lock_;

  // Allocated ids for VASurfaces.
  std::vector<VASurfaceID> va_surface_ids_;

  // Singleton instance of VADisplayState.
  static base::LazyInstance<VADisplayState> va_display_state_;

  // VA handles.
  // All valid after successful Initialize() and until Deinitialize().
  VADisplay va_display_;
  VAConfigID va_config_id_;
  // Created for the current set of va_surface_ids_ in CreateSurfaces() and
  // valid until DestroySurfaces().
  VAContextID va_context_id_;

  // Data queued up for HW codec, to be committed on next execution.
  std::vector<VABufferID> pending_slice_bufs_;
  std::vector<VABufferID> pending_va_bufs_;

  // Bitstream buffers for encode.
  std::set<VABufferID> coded_buffers_;

  // Called to report codec errors to UMA. Errors to clients are reported via
  // return values from public methods.
  base::Closure report_error_to_uma_cb_;

  // VPP (Video Post Processing) context, this is used to convert
  // pictures used by the decoder to RGBA pictures usable by GL or the
  // display hardware.
  VAConfigID va_vpp_config_id_;
  VAContextID va_vpp_context_id_;
  VABufferID va_vpp_buffer_id_;

  // Singleton variable to store supported profile information for encode and
  // decode.
  static base::LazyInstance<LazyProfileInfos> profile_infos_;

  DISALLOW_COPY_AND_ASSIGN(VaapiWrapper);
};

}  // namespace content

#endif  // CONTENT_COMMON_GPU_MEDIA_VAAPI_WRAPPER_H_