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.
8 #include "base/logging.h"
9 #include "content/common/gpu/media/vaapi_wrapper.h"
11 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \
14 << " VA error: " << VAAPI_ErrorStr(va_error); \
15 report_error_to_uma_cb_.Run(); \
18 #define VA_LOG_ON_ERROR(va_error, err_msg) \
20 if ((va_error) != VA_STATUS_SUCCESS) \
21 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
24 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \
26 if ((va_error) != VA_STATUS_SUCCESS) { \
27 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
34 static void *vaapi_handle = NULL;
35 static void *vaapi_x11_handle = NULL;
37 typedef VAStatus (*VaapiBeginPicture)(VADisplay dpy,
39 VASurfaceID render_target);
40 typedef VAStatus (*VaapiCreateBuffer)(VADisplay dpy,
44 unsigned int num_elements,
47 typedef VAStatus (*VaapiCreateConfig)(VADisplay dpy,
49 VAEntrypoint entrypoint,
50 VAConfigAttrib *attrib_list,
52 VAConfigID *config_id);
53 typedef VAStatus (*VaapiCreateContext)(VADisplay dpy,
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,
70 VASurfaceID *surfaces);
71 typedef VAStatus (*VaapiCreateSurfaces8)(VADisplay dpy,
75 VASurfaceID *surfaces,
76 unsigned int num_surfaces,
77 VASurfaceAttrib *attrib_list,
78 unsigned int num_attribs);
79 typedef VAStatus (*VaapiDeriveImage)(VADisplay dpy,
82 typedef VAStatus (*VaapiDestroyBuffer)(VADisplay dpy, VABufferID buffer_id);
83 typedef VAStatus (*VaapiDestroyConfig)(VADisplay dpy, VAConfigID config_id);
84 typedef VAStatus (*VaapiDestroyContext)(VADisplay dpy, VAContextID context);
85 typedef VAStatus (*VaapiDestroyImage)(VADisplay dpy, VAImageID image);
86 typedef VAStatus (*VaapiDestroySurfaces)(VADisplay dpy,
87 VASurfaceID *surfaces,
89 typedef int (*VaapiDisplayIsValid)(VADisplay dpy);
90 typedef VAStatus (*VaapiEndPicture)(VADisplay dpy, VAContextID context);
91 typedef const char* (*VaapiErrorStr)(VAStatus error_status);
92 typedef VAStatus (*VaapiGetConfigAttributes)(VADisplay dpy,
94 VAEntrypoint entrypoint,
95 VAConfigAttrib *attrib_list,
97 typedef VADisplay (*VaapiGetDisplay)(Display *dpy);
98 typedef VAStatus (*VaapiInitialize)(VADisplay dpy,
101 typedef VAStatus (*VaapiMapBuffer)(VADisplay dpy,
104 typedef VAStatus (*VaapiPutSurface)(VADisplay dpy,
113 unsigned short destw,
114 unsigned short desth,
115 VARectangle *cliprects,
116 unsigned int number_cliprects,
118 typedef VAStatus (*VaapiRenderPicture)(VADisplay dpy,
122 typedef VAStatus (*VaapiSetDisplayAttributes)(VADisplay dpy,
123 VADisplayAttribute *type,
125 typedef VAStatus (*VaapiSyncSurface)(VADisplay dpy, VASurfaceID render_target);
126 typedef VAStatus (*VaapiTerminate)(VADisplay dpy);
127 typedef VAStatus (*VaapiUnmapBuffer)(VADisplay dpy, VABufferID buf_id);
129 #define VAAPI_SYM(name, handle) Vaapi##name VAAPI_##name = NULL
131 VAAPI_SYM(BeginPicture, vaapi_handle);
132 VAAPI_SYM(CreateBuffer, vaapi_handle);
133 VAAPI_SYM(CreateConfig, vaapi_handle);
134 VAAPI_SYM(CreateContext, vaapi_handle);
135 VAAPI_SYM(CreateSurfaces, vaapi_handle);
136 VAAPI_SYM(DeriveImage, vaapi_handle);
137 VAAPI_SYM(DestroyBuffer, vaapi_handle);
138 VAAPI_SYM(DestroyConfig, vaapi_handle);
139 VAAPI_SYM(DestroyContext, vaapi_handle);
140 VAAPI_SYM(DestroyImage, vaapi_handle);
141 VAAPI_SYM(DestroySurfaces, vaapi_handle);
142 VAAPI_SYM(DisplayIsValid, vaapi_handle);
143 VAAPI_SYM(EndPicture, vaapi_handle);
144 VAAPI_SYM(ErrorStr, vaapi_handle);
145 VAAPI_SYM(GetConfigAttributes, vaapi_handle);
146 VAAPI_SYM(GetDisplay, vaapi_x11_handle);
147 VAAPI_SYM(Initialize, vaapi_handle);
148 VAAPI_SYM(MapBuffer, vaapi_handle);
149 VAAPI_SYM(PutSurface, vaapi_x11_handle);
150 VAAPI_SYM(RenderPicture, vaapi_handle);
151 VAAPI_SYM(SetDisplayAttributes, vaapi_handle);
152 VAAPI_SYM(SyncSurface, vaapi_x11_handle);
153 VAAPI_SYM(Terminate, vaapi_handle);
154 VAAPI_SYM(UnmapBuffer, vaapi_handle);
158 // Maps Profile enum values to VaProfile values.
159 static bool ProfileToVAProfile(media::VideoCodecProfile profile,
160 VAProfile* va_profile) {
162 case media::H264PROFILE_BASELINE:
163 *va_profile = VAProfileH264Baseline;
165 case media::H264PROFILE_MAIN:
166 *va_profile = VAProfileH264Main;
168 // TODO(posciak): See if we can/want support other variants
169 // of media::H264PROFILE_HIGH*.
170 case media::H264PROFILE_HIGH:
171 *va_profile = VAProfileH264High;
179 VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb)
180 : va_surface_id_(va_surface_id),
181 release_cb_(release_cb) {
182 DCHECK(!release_cb_.is_null());
185 VASurface::~VASurface() {
186 release_cb_.Run(va_surface_id_);
189 VaapiWrapper::VaapiWrapper()
191 va_config_id_(VA_INVALID_ID),
192 va_context_id_(VA_INVALID_ID) {
195 VaapiWrapper::~VaapiWrapper() {
196 DestroyPendingBuffers();
201 scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
202 media::VideoCodecProfile profile,
204 const base::Closure& report_error_to_uma_cb) {
205 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
207 if (!vaapi_wrapper->Initialize(profile, x_display, report_error_to_uma_cb))
208 vaapi_wrapper.reset();
210 return vaapi_wrapper.Pass();
213 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
214 VADisplayAttribute item = {VADisplayAttribRenderMode,
215 1, // At least support '_LOCAL_OVERLAY'.
216 -1, // The maximum possible support 'ALL'.
217 VA_RENDER_MODE_LOCAL_GPU,
218 VA_DISPLAY_ATTRIB_SETTABLE};
220 VAStatus va_res = VAAPI_SetDisplayAttributes(va_display_, &item, 1);
221 if (va_res != VA_STATUS_SUCCESS)
222 DVLOG(2) << "VAAPI_SetDisplayAttributes unsupported, ignoring by default.";
225 bool VaapiWrapper::Initialize(media::VideoCodecProfile profile,
227 const base::Closure& report_error_to_uma_cb) {
228 static bool vaapi_functions_initialized = PostSandboxInitialization();
229 if (!vaapi_functions_initialized) {
230 DVLOG(1) << "Failed to initialize VAAPI libs";
234 report_error_to_uma_cb_ = report_error_to_uma_cb;
236 base::AutoLock auto_lock(va_lock_);
238 VAProfile va_profile;
239 if (!ProfileToVAProfile(profile, &va_profile)) {
240 DVLOG(1) << "Unsupported profile";
244 va_display_ = VAAPI_GetDisplay(x_display);
245 if (!VAAPI_DisplayIsValid(va_display_)) {
246 DVLOG(1) << "Could not get a valid VA display";
251 va_res = VAAPI_Initialize(va_display_, &major_version_, &minor_version_);
252 VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false);
253 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_;
255 VAConfigAttrib attrib = {VAConfigAttribRTFormat, 0};
257 const VAEntrypoint kEntrypoint = VAEntrypointVLD;
258 va_res = VAAPI_GetConfigAttributes(va_display_, va_profile, kEntrypoint,
260 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
262 if (!(attrib.value & VA_RT_FORMAT_YUV420)) {
263 DVLOG(1) << "YUV420 not supported by this VAAPI implementation";
267 TryToSetVADisplayAttributeToLocalGPU();
269 va_res = VAAPI_CreateConfig(va_display_, va_profile, kEntrypoint,
270 &attrib, 1, &va_config_id_);
271 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
276 void VaapiWrapper::Deinitialize() {
277 base::AutoLock auto_lock(va_lock_);
279 if (va_config_id_ != VA_INVALID_ID) {
280 VAStatus va_res = VAAPI_DestroyConfig(va_display_, va_config_id_);
281 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed");
285 VAStatus va_res = VAAPI_Terminate(va_display_);
286 VA_LOG_ON_ERROR(va_res, "vaTerminate failed");
289 va_config_id_ = VA_INVALID_ID;
293 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
294 return (major_version_ < major) ||
295 (major_version_ == major && minor_version_ < minor);
298 bool VaapiWrapper::CreateSurfaces(gfx::Size size,
300 std::vector<VASurfaceID>* va_surfaces) {
301 base::AutoLock auto_lock(va_lock_);
302 DVLOG(2) << "Creating " << num_surfaces << " surfaces";
304 DCHECK(va_surfaces->empty());
305 DCHECK(va_surface_ids_.empty());
306 va_surface_ids_.resize(num_surfaces);
308 // Allocate surfaces in driver.
310 if (VAAPIVersionLessThan(0, 34)) {
311 va_res = reinterpret_cast<VaapiCreateSurfaces6>(VAAPI_CreateSurfaces)(
313 size.width(), size.height(),
315 va_surface_ids_.size(),
316 &va_surface_ids_[0]);
318 va_res = reinterpret_cast<VaapiCreateSurfaces8>(VAAPI_CreateSurfaces)(
321 size.width(), size.height(),
323 va_surface_ids_.size(),
327 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
328 if (va_res != VA_STATUS_SUCCESS) {
329 va_surface_ids_.clear();
333 // And create a context associated with them.
334 va_res = VAAPI_CreateContext(va_display_, va_config_id_,
335 size.width(), size.height(), VA_PROGRESSIVE,
336 &va_surface_ids_[0], va_surface_ids_.size(),
339 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
340 if (va_res != VA_STATUS_SUCCESS) {
345 *va_surfaces = va_surface_ids_;
349 void VaapiWrapper::DestroySurfaces() {
350 base::AutoLock auto_lock(va_lock_);
351 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces";
353 if (va_context_id_ != VA_INVALID_ID) {
354 VAStatus va_res = VAAPI_DestroyContext(va_display_, va_context_id_);
355 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed");
358 if (!va_surface_ids_.empty()) {
359 VAStatus va_res = VAAPI_DestroySurfaces(va_display_, &va_surface_ids_[0],
360 va_surface_ids_.size());
361 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed");
364 va_surface_ids_.clear();
365 va_context_id_ = VA_INVALID_ID;
368 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
371 base::AutoLock auto_lock(va_lock_);
373 VABufferID buffer_id;
374 VAStatus va_res = VAAPI_CreateBuffer(va_display_, va_context_id_,
375 va_buffer_type, size,
376 1, buffer, &buffer_id);
377 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
379 switch (va_buffer_type) {
380 case VASliceParameterBufferType:
381 case VASliceDataBufferType:
382 pending_slice_bufs_.push_back(buffer_id);
386 pending_va_bufs_.push_back(buffer_id);
393 void VaapiWrapper::DestroyPendingBuffers() {
394 base::AutoLock auto_lock(va_lock_);
396 for (size_t i = 0; i < pending_va_bufs_.size(); ++i) {
397 VAStatus va_res = VAAPI_DestroyBuffer(va_display_, pending_va_bufs_[i]);
398 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
401 for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) {
402 VAStatus va_res = VAAPI_DestroyBuffer(va_display_, pending_slice_bufs_[i]);
403 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
406 pending_va_bufs_.clear();
407 pending_slice_bufs_.clear();
410 bool VaapiWrapper::SubmitDecode(VASurfaceID va_surface_id) {
411 base::AutoLock auto_lock(va_lock_);
413 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
414 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
415 DVLOG(4) << "Decoding into VA surface " << va_surface_id;
417 // Get ready to decode into surface.
418 VAStatus va_res = VAAPI_BeginPicture(va_display_, va_context_id_,
420 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
422 // Commit parameter and slice buffers.
423 va_res = VAAPI_RenderPicture(va_display_, va_context_id_,
424 &pending_va_bufs_[0], pending_va_bufs_.size());
425 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
427 va_res = VAAPI_RenderPicture(va_display_, va_context_id_,
428 &pending_slice_bufs_[0],
429 pending_slice_bufs_.size());
430 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
432 // Instruct HW decoder to start processing committed buffers (decode this
433 // picture). This does not block until the end of decode.
434 va_res = VAAPI_EndPicture(va_display_, va_context_id_);
435 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
440 bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
441 bool result = SubmitDecode(va_surface_id);
442 DestroyPendingBuffers();
446 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
448 gfx::Size dest_size) {
449 base::AutoLock auto_lock(va_lock_);
451 VAStatus va_res = VAAPI_SyncSurface(va_display_, va_surface_id);
452 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
454 // Put the data into an X Pixmap.
455 va_res = VAAPI_PutSurface(va_display_,
458 0, 0, dest_size.width(), dest_size.height(),
459 0, 0, dest_size.width(), dest_size.height(),
461 VA_SUCCESS_OR_RETURN(va_res, "Failed putting decode surface to pixmap",
466 bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
469 base::AutoLock auto_lock(va_lock_);
471 VAStatus va_res = VAAPI_SyncSurface(va_display_, va_surface_id);
472 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
474 // Derive a VAImage from the VASurface
475 va_res = VAAPI_DeriveImage(va_display_, va_surface_id, image);
476 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed");
477 if (va_res != VA_STATUS_SUCCESS)
480 // Map the VAImage into memory
481 va_res = VAAPI_MapBuffer(va_display_, image->buf, mem);
482 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
483 if (va_res == VA_STATUS_SUCCESS)
486 VAAPI_DestroyImage(va_display_, image->image_id);
490 void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) {
491 base::AutoLock auto_lock(va_lock_);
493 VAAPI_UnmapBuffer(va_display_, image->buf);
494 VAAPI_DestroyImage(va_display_, image->image_id);
498 bool VaapiWrapper::PostSandboxInitialization() {
499 vaapi_handle = dlopen("libva.so.1", RTLD_NOW);
500 vaapi_x11_handle = dlopen("libva-x11.so.1", RTLD_NOW);
502 if (!vaapi_handle || !vaapi_x11_handle)
504 #define VAAPI_DLSYM_OR_RETURN_ON_ERROR(name, handle) \
506 VAAPI_##name = reinterpret_cast<Vaapi##name>(dlsym((handle), "va"#name)); \
507 if (VAAPI_##name == NULL) { \
508 DVLOG(1) << "Failed to dlsym va"#name; \
513 VAAPI_DLSYM_OR_RETURN_ON_ERROR(BeginPicture, vaapi_handle);
514 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateBuffer, vaapi_handle);
515 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateConfig, vaapi_handle);
516 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateContext, vaapi_handle);
517 VAAPI_DLSYM_OR_RETURN_ON_ERROR(CreateSurfaces, vaapi_handle);
518 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DeriveImage, vaapi_handle);
519 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyBuffer, vaapi_handle);
520 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyConfig, vaapi_handle);
521 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyContext, vaapi_handle);
522 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroyImage, vaapi_handle);
523 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DestroySurfaces, vaapi_handle);
524 VAAPI_DLSYM_OR_RETURN_ON_ERROR(DisplayIsValid, vaapi_handle);
525 VAAPI_DLSYM_OR_RETURN_ON_ERROR(EndPicture, vaapi_handle);
526 VAAPI_DLSYM_OR_RETURN_ON_ERROR(ErrorStr, vaapi_handle);
527 VAAPI_DLSYM_OR_RETURN_ON_ERROR(GetConfigAttributes, vaapi_handle);
528 VAAPI_DLSYM_OR_RETURN_ON_ERROR(GetDisplay, vaapi_x11_handle);
529 VAAPI_DLSYM_OR_RETURN_ON_ERROR(Initialize, vaapi_handle);
530 VAAPI_DLSYM_OR_RETURN_ON_ERROR(MapBuffer, vaapi_handle);
531 VAAPI_DLSYM_OR_RETURN_ON_ERROR(PutSurface, vaapi_x11_handle);
532 VAAPI_DLSYM_OR_RETURN_ON_ERROR(RenderPicture, vaapi_handle);
533 VAAPI_DLSYM_OR_RETURN_ON_ERROR(SetDisplayAttributes, vaapi_handle);
534 VAAPI_DLSYM_OR_RETURN_ON_ERROR(SyncSurface, vaapi_handle);
535 VAAPI_DLSYM_OR_RETURN_ON_ERROR(Terminate, vaapi_handle);
536 VAAPI_DLSYM_OR_RETURN_ON_ERROR(UnmapBuffer, vaapi_handle);
542 } // namespace content