Home | History | Annotate | Download | only in media
      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 
     32 namespace content {
     33 
     34 static void *vaapi_handle = NULL;
     35 static void *vaapi_x11_handle = NULL;
     36 
     37 typedef VAStatus (*VaapiBeginPicture)(VADisplay dpy,
     38                                       VAContextID context,
     39                                       VASurfaceID render_target);
     40 typedef 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);
     47 typedef VAStatus (*VaapiCreateConfig)(VADisplay dpy,
     48                                       VAProfile profile,
     49                                       VAEntrypoint entrypoint,
     50                                       VAConfigAttrib *attrib_list,
     51                                       int num_attribs,
     52                                       VAConfigID *config_id);
     53 typedef 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.
     64 typedef void *VaapiCreateSurfaces;
     65 typedef VAStatus (*VaapiCreateSurfaces6)(VADisplay dpy,
     66                                          int width,
     67                                          int height,
     68                                          int format,
     69                                          int num_surfaces,
     70                                          VASurfaceID *surfaces);
     71 typedef 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);
     79 typedef VAStatus (*VaapiDestroyBuffer)(VADisplay dpy, VABufferID buffer_id);
     80 typedef VAStatus (*VaapiDestroyConfig)(VADisplay dpy, VAConfigID config_id);
     81 typedef VAStatus (*VaapiDestroyContext)(VADisplay dpy, VAContextID context);
     82 typedef VAStatus (*VaapiDestroySurfaces)(VADisplay dpy,
     83                                          VASurfaceID *surfaces,
     84                                          int num_surfaces);
     85 typedef int (*VaapiDisplayIsValid)(VADisplay dpy);
     86 typedef VAStatus (*VaapiEndPicture)(VADisplay dpy, VAContextID context);
     87 typedef const char* (*VaapiErrorStr)(VAStatus error_status);
     88 typedef VAStatus (*VaapiGetConfigAttributes)(VADisplay dpy,
     89                                              VAProfile profile,
     90                                              VAEntrypoint entrypoint,
     91                                              VAConfigAttrib *attrib_list,
     92                                              int num_attribs);
     93 typedef VADisplay (*VaapiGetDisplay)(Display *dpy);
     94 typedef VAStatus (*VaapiInitialize)(VADisplay dpy,
     95                                     int *major_version,
     96                                     int *minor_version);
     97 typedef 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);
    111 typedef VAStatus (*VaapiRenderPicture)(VADisplay dpy,
    112                                        VAContextID context,
    113                                        VABufferID *buffers,
    114                                        int num_buffers);
    115 typedef VAStatus (*VaapiSyncSurface)(VADisplay dpy, VASurfaceID render_target);
    116 typedef VAStatus (*VaapiTerminate)(VADisplay dpy);
    117 
    118 #define VAAPI_SYM(name, handle) Vaapi##name VAAPI_##name = NULL
    119 
    120 VAAPI_SYM(BeginPicture, vaapi_handle);
    121 VAAPI_SYM(CreateBuffer, vaapi_handle);
    122 VAAPI_SYM(CreateConfig, vaapi_handle);
    123 VAAPI_SYM(CreateContext, vaapi_handle);
    124 VAAPI_SYM(CreateSurfaces, vaapi_handle);
    125 VAAPI_SYM(DestroyBuffer, vaapi_handle);
    126 VAAPI_SYM(DestroyConfig, vaapi_handle);
    127 VAAPI_SYM(DestroyContext, vaapi_handle);
    128 VAAPI_SYM(DestroySurfaces, vaapi_handle);
    129 VAAPI_SYM(DisplayIsValid, vaapi_handle);
    130 VAAPI_SYM(EndPicture, vaapi_handle);
    131 VAAPI_SYM(ErrorStr, vaapi_handle);
    132 VAAPI_SYM(GetConfigAttributes, vaapi_handle);
    133 VAAPI_SYM(GetDisplay, vaapi_x11_handle);
    134 VAAPI_SYM(Initialize, vaapi_handle);
    135 VAAPI_SYM(PutSurface, vaapi_x11_handle);
    136 VAAPI_SYM(RenderPicture, vaapi_handle);
    137 VAAPI_SYM(SyncSurface, vaapi_x11_handle);
    138 VAAPI_SYM(Terminate, vaapi_handle);
    139 
    140 #undef VAAPI_SYM
    141 
    142 // Maps Profile enum values to VaProfile values.
    143 static 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 
    163 VASurface::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 
    169 VASurface::~VASurface() {
    170   release_cb_.Run(va_surface_id_);
    171 }
    172 
    173 VaapiWrapper::VaapiWrapper()
    174     : va_display_(NULL),
    175       va_config_id_(VA_INVALID_ID),
    176       va_context_id_(VA_INVALID_ID) {
    177 }
    178 
    179 VaapiWrapper::~VaapiWrapper() {
    180   DestroyPendingBuffers();
    181   DestroySurfaces();
    182   Deinitialize();
    183 }
    184 
    185 scoped_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 
    197 bool 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 
    246 void 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 
    263 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
    264   return (major_version_ < major) ||
    265       (major_version_ == major && minor_version_ < minor);
    266 }
    267 
    268 bool 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 
    319 void 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 
    338 bool 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 
    363 void 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 
    380 bool 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 
    410 bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
    411   bool result = SubmitDecode(va_surface_id);
    412   DestroyPendingBuffers();
    413   return result;
    414 }
    415 
    416 bool 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
    437 bool 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