vaapi_wrapper.cc revision f2477e01787aa58f445919b809d89e252beef54f
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include <dlfcn.h>
6
7#include "base/bind.h"
8#include "base/logging.h"
9#include "content/common/gpu/media/vaapi_wrapper.h"
10
11#define LOG_VA_ERROR_AND_REPORT(va_error, err_msg)         \
12  do {                                                     \
13    DVLOG(1) << err_msg                                    \
14             << " VA error: " << VAAPI_ErrorStr(va_error); \
15    report_error_to_uma_cb_.Run();                         \
16  } while (0)
17
18#define VA_LOG_ON_ERROR(va_error, err_msg)                 \
19  do {                                                     \
20    if ((va_error) != VA_STATUS_SUCCESS)                   \
21      LOG_VA_ERROR_AND_REPORT(va_error, err_msg);          \
22  } while (0)
23
24#define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret)       \
25  do {                                                     \
26    if ((va_error) != VA_STATUS_SUCCESS) {                 \
27      LOG_VA_ERROR_AND_REPORT(va_error, err_msg);          \
28      return (ret);                                        \
29    }                                                      \
30  } while (0)
31
32namespace content {
33
34static void *vaapi_handle = NULL;
35static void *vaapi_x11_handle = NULL;
36
37typedef VAStatus (*VaapiBeginPicture)(VADisplay dpy,
38                                      VAContextID context,
39                                      VASurfaceID render_target);
40typedef VAStatus (*VaapiCreateBuffer)(VADisplay dpy,
41                                      VAContextID context,
42                                      VABufferType type,
43                                      unsigned int size,
44                                      unsigned int num_elements,
45                                      void *data,
46                                      VABufferID *buf_id);
47typedef VAStatus (*VaapiCreateConfig)(VADisplay dpy,
48                                      VAProfile profile,
49                                      VAEntrypoint entrypoint,
50                                      VAConfigAttrib *attrib_list,
51                                      int num_attribs,
52                                      VAConfigID *config_id);
53typedef VAStatus (*VaapiCreateContext)(VADisplay dpy,
54                                       VAConfigID config_id,
55                                       int picture_width,
56                                       int picture_height,
57                                       int flag,
58                                       VASurfaceID *render_targets,
59                                       int num_render_targets,
60                                       VAContextID *context);
61// In VAAPI version < 0.34, vaCreateSurface has 6 parameters, but in VAAPI
62// version >= 0.34, vaCreateSurface has 8 parameters.
63// TODO(chihchung): Remove the old path once ChromeOS updates to 1.2.1.
64typedef void *VaapiCreateSurfaces;
65typedef VAStatus (*VaapiCreateSurfaces6)(VADisplay dpy,
66                                         int width,
67                                         int height,
68                                         int format,
69                                         int num_surfaces,
70                                         VASurfaceID *surfaces);
71typedef VAStatus (*VaapiCreateSurfaces8)(VADisplay dpy,
72                                         unsigned int format,
73                                         unsigned int width,
74                                         unsigned int height,
75                                         VASurfaceID *surfaces,
76                                         unsigned int num_surfaces,
77                                         VASurfaceAttrib *attrib_list,
78                                         unsigned int num_attribs);
79typedef VAStatus (*VaapiDestroyBuffer)(VADisplay dpy, VABufferID buffer_id);
80typedef VAStatus (*VaapiDestroyConfig)(VADisplay dpy, VAConfigID config_id);
81typedef VAStatus (*VaapiDestroyContext)(VADisplay dpy, VAContextID context);
82typedef VAStatus (*VaapiDestroySurfaces)(VADisplay dpy,
83                                         VASurfaceID *surfaces,
84                                         int num_surfaces);
85typedef int (*VaapiDisplayIsValid)(VADisplay dpy);
86typedef VAStatus (*VaapiEndPicture)(VADisplay dpy, VAContextID context);
87typedef const char* (*VaapiErrorStr)(VAStatus error_status);
88typedef VAStatus (*VaapiGetConfigAttributes)(VADisplay dpy,
89                                             VAProfile profile,
90                                             VAEntrypoint entrypoint,
91                                             VAConfigAttrib *attrib_list,
92                                             int num_attribs);
93typedef VADisplay (*VaapiGetDisplay)(Display *dpy);
94typedef VAStatus (*VaapiInitialize)(VADisplay dpy,
95                                    int *major_version,
96                                    int *minor_version);
97typedef VAStatus (*VaapiPutSurface)(VADisplay dpy,
98                                    VASurfaceID surface,
99                                    Drawable draw,
100                                    short srcx,
101                                    short srcy,
102                                    unsigned short srcw,
103                                    unsigned short srch,
104                                    short destx,
105                                    short desty,
106                                    unsigned short destw,
107                                    unsigned short desth,
108                                    VARectangle *cliprects,
109                                    unsigned int number_cliprects,
110                                    unsigned int flags);
111typedef VAStatus (*VaapiRenderPicture)(VADisplay dpy,
112                                       VAContextID context,
113                                       VABufferID *buffers,
114                                       int num_buffers);
115typedef VAStatus (*VaapiSyncSurface)(VADisplay dpy, VASurfaceID render_target);
116typedef VAStatus (*VaapiTerminate)(VADisplay dpy);
117
118#define VAAPI_SYM(name, handle) Vaapi##name VAAPI_##name = NULL
119
120VAAPI_SYM(BeginPicture, vaapi_handle);
121VAAPI_SYM(CreateBuffer, vaapi_handle);
122VAAPI_SYM(CreateConfig, vaapi_handle);
123VAAPI_SYM(CreateContext, vaapi_handle);
124VAAPI_SYM(CreateSurfaces, vaapi_handle);
125VAAPI_SYM(DestroyBuffer, vaapi_handle);
126VAAPI_SYM(DestroyConfig, vaapi_handle);
127VAAPI_SYM(DestroyContext, vaapi_handle);
128VAAPI_SYM(DestroySurfaces, vaapi_handle);
129VAAPI_SYM(DisplayIsValid, vaapi_handle);
130VAAPI_SYM(EndPicture, vaapi_handle);
131VAAPI_SYM(ErrorStr, vaapi_handle);
132VAAPI_SYM(GetConfigAttributes, vaapi_handle);
133VAAPI_SYM(GetDisplay, vaapi_x11_handle);
134VAAPI_SYM(Initialize, vaapi_handle);
135VAAPI_SYM(PutSurface, vaapi_x11_handle);
136VAAPI_SYM(RenderPicture, vaapi_handle);
137VAAPI_SYM(SyncSurface, vaapi_x11_handle);
138VAAPI_SYM(Terminate, vaapi_handle);
139
140#undef VAAPI_SYM
141
142// Maps Profile enum values to VaProfile values.
143static bool ProfileToVAProfile(media::VideoCodecProfile profile,
144                               VAProfile* va_profile) {
145  switch (profile) {
146    case media::H264PROFILE_BASELINE:
147      *va_profile = VAProfileH264Baseline;
148      break;
149    case media::H264PROFILE_MAIN:
150      *va_profile = VAProfileH264Main;
151      break;
152    // TODO(posciak): See if we can/want support other variants
153    // of media::H264PROFILE_HIGH*.
154    case media::H264PROFILE_HIGH:
155      *va_profile = VAProfileH264High;
156      break;
157    default:
158      return false;
159  }
160  return true;
161}
162
163VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb)
164    : va_surface_id_(va_surface_id),
165      release_cb_(release_cb) {
166  DCHECK(!release_cb_.is_null());
167}
168
169VASurface::~VASurface() {
170  release_cb_.Run(va_surface_id_);
171}
172
173VaapiWrapper::VaapiWrapper()
174    : va_display_(NULL),
175      va_config_id_(VA_INVALID_ID),
176      va_context_id_(VA_INVALID_ID) {
177}
178
179VaapiWrapper::~VaapiWrapper() {
180  DestroyPendingBuffers();
181  DestroySurfaces();
182  Deinitialize();
183}
184
185scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
186    media::VideoCodecProfile profile,
187    Display* x_display,
188    const base::Closure& report_error_to_uma_cb) {
189  scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
190
191  if (!vaapi_wrapper->Initialize(profile, x_display, report_error_to_uma_cb))
192    vaapi_wrapper.reset();
193
194  return vaapi_wrapper.Pass();
195}
196
197bool VaapiWrapper::Initialize(media::VideoCodecProfile profile,
198                              Display* x_display,
199                              const base::Closure& report_error_to_uma_cb) {
200  static bool vaapi_functions_initialized = PostSandboxInitialization();
201  if (!vaapi_functions_initialized) {
202    DVLOG(1) << "Failed to initialize VAAPI libs";
203    return false;
204  }
205
206  report_error_to_uma_cb_ = report_error_to_uma_cb;
207
208  base::AutoLock auto_lock(va_lock_);
209
210  VAProfile va_profile;
211  if (!ProfileToVAProfile(profile, &va_profile)) {
212    DVLOG(1) << "Unsupported profile";
213    return false;
214  }
215
216  va_display_ = VAAPI_GetDisplay(x_display);
217  if (!VAAPI_DisplayIsValid(va_display_)) {
218    DVLOG(1) << "Could not get a valid VA display";
219    return false;
220  }
221
222  VAStatus va_res;
223  va_res = VAAPI_Initialize(va_display_, &major_version_, &minor_version_);
224  VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false);
225  DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_;
226
227  VAConfigAttrib attrib = {VAConfigAttribRTFormat, 0};
228
229  const VAEntrypoint kEntrypoint = VAEntrypointVLD;
230  va_res = VAAPI_GetConfigAttributes(va_display_, va_profile, kEntrypoint,
231                                     &attrib, 1);
232  VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
233
234  if (!(attrib.value & VA_RT_FORMAT_YUV420)) {
235    DVLOG(1) << "YUV420 not supported by this VAAPI implementation";
236    return false;
237  }
238
239  va_res = VAAPI_CreateConfig(va_display_, va_profile, kEntrypoint,
240                              &attrib, 1, &va_config_id_);
241  VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
242
243  return true;
244}
245
246void VaapiWrapper::Deinitialize() {
247  base::AutoLock auto_lock(va_lock_);
248
249  if (va_config_id_ != VA_INVALID_ID) {
250    VAStatus va_res = VAAPI_DestroyConfig(va_display_, va_config_id_);
251    VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed");
252  }
253
254  if (va_display_) {
255    VAStatus va_res = VAAPI_Terminate(va_display_);
256    VA_LOG_ON_ERROR(va_res, "vaTerminate failed");
257  }
258
259  va_config_id_ = VA_INVALID_ID;
260  va_display_ = NULL;
261}
262
263bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
264  return (major_version_ < major) ||
265      (major_version_ == major && minor_version_ < minor);
266}
267
268bool VaapiWrapper::CreateSurfaces(gfx::Size size,
269                                  size_t num_surfaces,
270                                   std::vector<VASurfaceID>* va_surfaces) {
271  base::AutoLock auto_lock(va_lock_);
272  DVLOG(2) << "Creating " << num_surfaces << " surfaces";
273
274  DCHECK(va_surfaces->empty());
275  DCHECK(va_surface_ids_.empty());
276  va_surface_ids_.resize(num_surfaces);
277
278  // Allocate surfaces in driver.
279  VAStatus va_res;
280  if (VAAPIVersionLessThan(0, 34)) {
281    va_res = reinterpret_cast<VaapiCreateSurfaces6>(VAAPI_CreateSurfaces)(
282        va_display_,
283        size.width(), size.height(),
284        VA_RT_FORMAT_YUV420,
285        va_surface_ids_.size(),
286        &va_surface_ids_[0]);
287  } else {
288    va_res = reinterpret_cast<VaapiCreateSurfaces8>(VAAPI_CreateSurfaces)(
289        va_display_,
290        VA_RT_FORMAT_YUV420,
291        size.width(), size.height(),
292        &va_surface_ids_[0],
293        va_surface_ids_.size(),
294        NULL, 0);
295  }
296
297  VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
298  if (va_res != VA_STATUS_SUCCESS) {
299    va_surface_ids_.clear();
300    return false;
301  }
302
303  // And create a context associated with them.
304  va_res = VAAPI_CreateContext(va_display_, va_config_id_,
305                               size.width(), size.height(), VA_PROGRESSIVE,
306                               &va_surface_ids_[0], va_surface_ids_.size(),
307                               &va_context_id_);
308
309  VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
310  if (va_res != VA_STATUS_SUCCESS) {
311    DestroySurfaces();
312    return false;
313  }
314
315  *va_surfaces = va_surface_ids_;
316  return true;
317}
318
319void VaapiWrapper::DestroySurfaces() {
320  base::AutoLock auto_lock(va_lock_);
321  DVLOG(2) << "Destroying " << va_surface_ids_.size()  << " surfaces";
322
323  if (va_context_id_ != VA_INVALID_ID) {
324    VAStatus va_res = VAAPI_DestroyContext(va_display_, va_context_id_);
325    VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed");
326  }
327
328  if (!va_surface_ids_.empty()) {
329    VAStatus va_res = VAAPI_DestroySurfaces(va_display_, &va_surface_ids_[0],
330                                            va_surface_ids_.size());
331    VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed");
332  }
333
334  va_surface_ids_.clear();
335  va_context_id_ = VA_INVALID_ID;
336}
337
338bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
339                                size_t size,
340                                void* buffer) {
341  base::AutoLock auto_lock(va_lock_);
342
343  VABufferID buffer_id;
344  VAStatus va_res = VAAPI_CreateBuffer(va_display_, va_context_id_,
345                                       va_buffer_type, size,
346                                       1, buffer, &buffer_id);
347  VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
348
349  switch (va_buffer_type) {
350    case VASliceParameterBufferType:
351    case VASliceDataBufferType:
352      pending_slice_bufs_.push_back(buffer_id);
353      break;
354
355    default:
356      pending_va_bufs_.push_back(buffer_id);
357      break;
358  }
359
360  return true;
361}
362
363void VaapiWrapper::DestroyPendingBuffers() {
364  base::AutoLock auto_lock(va_lock_);
365
366  for (size_t i = 0; i < pending_va_bufs_.size(); ++i) {
367    VAStatus va_res = VAAPI_DestroyBuffer(va_display_, pending_va_bufs_[i]);
368    VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
369  }
370
371  for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) {
372    VAStatus va_res = VAAPI_DestroyBuffer(va_display_, pending_slice_bufs_[i]);
373    VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
374  }
375
376  pending_va_bufs_.clear();
377  pending_slice_bufs_.clear();
378}
379
380bool VaapiWrapper::SubmitDecode(VASurfaceID va_surface_id) {
381  base::AutoLock auto_lock(va_lock_);
382
383  DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
384  DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
385  DVLOG(4) << "Decoding into VA surface " << va_surface_id;
386
387  // Get ready to decode into surface.
388  VAStatus va_res = VAAPI_BeginPicture(va_display_, va_context_id_,
389                                       va_surface_id);
390  VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
391
392  // Commit parameter and slice buffers.
393  va_res = VAAPI_RenderPicture(va_display_, va_context_id_,
394                               &pending_va_bufs_[0], pending_va_bufs_.size());
395  VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
396
397  va_res = VAAPI_RenderPicture(va_display_, va_context_id_,
398                               &pending_slice_bufs_[0],
399                               pending_slice_bufs_.size());
400  VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
401
402  // Instruct HW decoder to start processing committed buffers (decode this
403  // picture). This does not block until the end of decode.
404  va_res = VAAPI_EndPicture(va_display_, va_context_id_);
405  VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
406
407  return true;
408}
409
410bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
411  bool result = SubmitDecode(va_surface_id);
412  DestroyPendingBuffers();
413  return result;
414}
415
416bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
417                                        Pixmap x_pixmap,
418                                        gfx::Size dest_size) {
419  base::AutoLock auto_lock(va_lock_);
420
421  VAStatus va_res = VAAPI_SyncSurface(va_display_, va_surface_id);
422  VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
423
424  // Put the data into an X Pixmap.
425  va_res = VAAPI_PutSurface(va_display_,
426                            va_surface_id,
427                            x_pixmap,
428                            0, 0, dest_size.width(), dest_size.height(),
429                            0, 0, dest_size.width(), dest_size.height(),
430                            NULL, 0, 0);
431  VA_SUCCESS_OR_RETURN(va_res, "Failed putting decode surface to pixmap",
432                       false);
433  return true;
434}
435
436// static
437bool VaapiWrapper::PostSandboxInitialization() {
438  vaapi_handle = dlopen("libva.so.1", RTLD_NOW);
439  vaapi_x11_handle = dlopen("libva-x11.so.1", RTLD_NOW);
440
441  if (!vaapi_handle || !vaapi_x11_handle)
442    return false;
443#define VAAPI_DLSYM_OR_RETURN_ON_ERROR(name, handle)                          \
444  do {                                                                        \
445    VAAPI_##name = reinterpret_cast<Vaapi##name>(dlsym((handle), "va"#name)); \
446    if (VAAPI_##name == NULL) {                                               \
447      DVLOG(1) << "Failed to dlsym va"#name;                                  \
448      return false;                                                           \
449    }                                                                         \
450  } while (0)
451
452  VAAPI_DLSYM_OR_RETURN_ON_ERROR(BeginPicture, vaapi_handle);
453  VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateBuffer, vaapi_handle);
454  VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateConfig, vaapi_handle);
455  VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateContext, vaapi_handle);
456  VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateSurfaces, vaapi_handle);
457  VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyBuffer, vaapi_handle);
458  VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyConfig, vaapi_handle);
459  VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyContext, vaapi_handle);
460  VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroySurfaces, vaapi_handle);
461  VAAPI_DLSYM_OR_RETURN_ON_ERROR(DisplayIsValid, vaapi_handle);
462  VAAPI_DLSYM_OR_RETURN_ON_ERROR(EndPicture, vaapi_handle);
463  VAAPI_DLSYM_OR_RETURN_ON_ERROR(ErrorStr, vaapi_handle);
464  VAAPI_DLSYM_OR_RETURN_ON_ERROR(GetConfigAttributes, vaapi_handle);
465  VAAPI_DLSYM_OR_RETURN_ON_ERROR(GetDisplay, vaapi_x11_handle);
466  VAAPI_DLSYM_OR_RETURN_ON_ERROR(Initialize, vaapi_handle);
467  VAAPI_DLSYM_OR_RETURN_ON_ERROR(PutSurface, vaapi_x11_handle);
468  VAAPI_DLSYM_OR_RETURN_ON_ERROR(RenderPicture, vaapi_handle);
469  VAAPI_DLSYM_OR_RETURN_ON_ERROR(SyncSurface, vaapi_handle);
470  VAAPI_DLSYM_OR_RETURN_ON_ERROR(Terminate, vaapi_handle);
471#undef VAAPI_DLSYM
472
473  return true;
474}
475
476}  // namespace content
477