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.
5 #include "vaapi_wrapper.h"
9 #include <wayland-client.h>
11 #include "base/bind.h"
12 #include "base/logging.h"
13 #include "base/numerics/safe_conversions.h"
14 // Auto-generated for dlopen libva libraries
15 #include "media/media/va_stubs.h"
17 #include "third_party/libva/va/wayland/va_wayland.h"
18 #include "third_party/libva/va/va_drmcommon.h"
20 using media_media::kModuleVa_wayland;
21 using media_media::InitializeStubs;
22 using media_media::StubPathMap;
24 static const base::FilePath::CharType kVaLib[] =
25 FILE_PATH_LITERAL("libva-wayland.so.1");
27 static const char kVaLockBufferSymbol[] = "vaLockBuffer";
28 static const char kVaUnlockBufferSymbol[] = "vaUnlockBuffer";
30 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \
33 << " VA error: " << vaErrorStr(va_error); \
34 report_error_to_uma_cb_.Run(); \
37 #define VA_LOG_ON_ERROR(va_error, err_msg) \
39 if ((va_error) != VA_STATUS_SUCCESS) \
40 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
43 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \
45 if ((va_error) != VA_STATUS_SUCCESS) { \
46 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
53 // Maps Profile enum values to VaProfile values.
54 static VAProfile ProfileToVAProfile(
55 media::VideoCodecProfile profile,
56 const std::vector<VAProfile>& supported_profiles) {
58 VAProfile va_profile = VAProfileNone;
61 case media::H264PROFILE_BASELINE:
62 va_profile = VAProfileH264Baseline;
64 case media::H264PROFILE_MAIN:
65 va_profile = VAProfileH264Main;
67 // TODO(posciak): See if we can/want support other variants
68 // of media::H264PROFILE_HIGH*.
69 case media::H264PROFILE_HIGH:
70 va_profile = VAProfileH264High;
76 bool supported = std::find(supported_profiles.begin(),
77 supported_profiles.end(),
78 va_profile) != supported_profiles.end();
80 if (!supported && va_profile == VAProfileH264Baseline) {
81 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips
82 // the information whether the profile is constrained or not, so we have no
83 // way to know here. Try for baseline first, but if it is not supported,
84 // try constrained baseline and hope this is what it actually is
85 // (which in practice is true for a great majority of cases).
86 if (std::find(supported_profiles.begin(),
87 supported_profiles.end(),
88 VAProfileH264ConstrainedBaseline) !=
89 supported_profiles.end()) {
90 va_profile = VAProfileH264ConstrainedBaseline;
91 DVLOG(1) << "Falling back to constrained baseline profile.";
98 VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb)
99 : va_surface_id_(va_surface_id),
100 release_cb_(release_cb) {
101 DCHECK(!release_cb_.is_null());
104 VASurface::~VASurface() {
105 release_cb_.Run(va_surface_id_);
108 VaapiWrapper::VaapiWrapper()
110 va_config_id_(VA_INVALID_ID),
111 va_context_id_(VA_INVALID_ID) {
114 VaapiWrapper::~VaapiWrapper() {
115 DestroyPendingBuffers();
120 scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
121 media::VideoCodecProfile profile,
123 const base::Closure& report_error_to_uma_cb) {
124 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
126 if (!vaapi_wrapper->Initialize(profile, display, report_error_to_uma_cb))
127 vaapi_wrapper.reset();
129 return vaapi_wrapper.Pass();
132 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
133 VADisplayAttribute item = {VADisplayAttribRenderMode,
134 1, // At least support '_LOCAL_OVERLAY'.
135 -1, // The maximum possible support 'ALL'.
136 VA_RENDER_MODE_LOCAL_GPU,
137 VA_DISPLAY_ATTRIB_SETTABLE};
139 VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1);
140 if (va_res != VA_STATUS_SUCCESS)
141 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
144 bool VaapiWrapper::Initialize(media::VideoCodecProfile profile,
146 const base::Closure& report_error_to_uma_cb) {
147 static bool vaapi_functions_initialized = PostSandboxInitialization();
148 if (!vaapi_functions_initialized) {
149 DVLOG(1) << "Failed to initialize VAAPI libs";
153 report_error_to_uma_cb_ = report_error_to_uma_cb;
155 base::AutoLock auto_lock(va_lock_);
157 va_display_ = vaGetDisplayWl(static_cast<wl_display *>(display));
158 if (!vaDisplayIsValid(va_display_)) {
159 DVLOG(1) << "Could not get a valid VA display";
163 VAStatus va_res = vaInitialize(va_display_, &major_version_, &minor_version_);
164 VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false);
165 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_;
167 if (VAAPIVersionLessThan(0, 34)) {
168 DVLOG(1) << "VAAPI version < 0.34 is not supported.";
172 // Query the driver for supported profiles.
173 int max_profiles = vaMaxNumProfiles(va_display_);
174 std::vector<VAProfile> supported_profiles(
175 base::checked_cast<size_t>(max_profiles));
177 int num_supported_profiles;
178 va_res = vaQueryConfigProfiles(
179 va_display_, &supported_profiles[0], &num_supported_profiles);
180 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false);
181 if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) {
182 DVLOG(1) << "vaQueryConfigProfiles returned: " << num_supported_profiles;
186 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles));
188 VAProfile va_profile = ProfileToVAProfile(profile, supported_profiles);
189 if (va_profile == VAProfileNone) {
190 DVLOG(1) << "Unsupported profile";
194 VAConfigAttrib attrib = {VAConfigAttribRTFormat, 0};
195 const VAEntrypoint kEntrypoint = VAEntrypointVLD;
196 va_res = vaGetConfigAttributes(va_display_, va_profile, kEntrypoint,
198 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
200 if (!(attrib.value & VA_RT_FORMAT_YUV420)) {
201 DVLOG(1) << "YUV420 not supported by this VAAPI implementation";
205 TryToSetVADisplayAttributeToLocalGPU();
207 va_res = vaCreateConfig(va_display_, va_profile, kEntrypoint,
208 &attrib, 1, &va_config_id_);
209 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
214 void VaapiWrapper::Deinitialize() {
215 base::AutoLock auto_lock(va_lock_);
217 if (va_config_id_ != VA_INVALID_ID) {
218 VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_);
219 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed");
223 VAStatus va_res = vaTerminate(va_display_);
224 VA_LOG_ON_ERROR(va_res, "vaTerminate failed");
227 va_config_id_ = VA_INVALID_ID;
231 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
232 return (major_version_ < major) ||
233 (major_version_ == major && minor_version_ < minor);
236 bool VaapiWrapper::CreateSurfaces(gfx::Size size,
238 std::vector<VASurfaceID>* va_surfaces) {
239 base::AutoLock auto_lock(va_lock_);
240 DVLOG(2) << "Creating " << num_surfaces << " surfaces";
242 DCHECK(va_surfaces->empty());
243 DCHECK(va_surface_ids_.empty());
244 va_surface_ids_.resize(num_surfaces);
246 // Allocate surfaces in driver.
247 VAStatus va_res = vaCreateSurfaces(va_display_,
249 size.width(), size.height(),
251 va_surface_ids_.size(),
254 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
255 if (va_res != VA_STATUS_SUCCESS) {
256 va_surface_ids_.clear();
260 // And create a context associated with them.
261 va_res = vaCreateContext(va_display_, va_config_id_,
262 size.width(), size.height(), VA_PROGRESSIVE,
263 &va_surface_ids_[0], va_surface_ids_.size(),
266 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
267 if (va_res != VA_STATUS_SUCCESS) {
272 *va_surfaces = va_surface_ids_;
276 void VaapiWrapper::DestroySurfaces() {
277 base::AutoLock auto_lock(va_lock_);
278 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces";
280 if (va_context_id_ != VA_INVALID_ID) {
281 VAStatus va_res = vaDestroyContext(va_display_, va_context_id_);
282 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed");
285 if (!va_surface_ids_.empty()) {
286 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0],
287 va_surface_ids_.size());
288 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed");
291 va_surface_ids_.clear();
292 va_context_id_ = VA_INVALID_ID;
295 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
298 base::AutoLock auto_lock(va_lock_);
300 VABufferID buffer_id;
301 VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_,
302 va_buffer_type, size,
303 1, buffer, &buffer_id);
304 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
306 switch (va_buffer_type) {
307 case VASliceParameterBufferType:
308 case VASliceDataBufferType:
309 pending_slice_bufs_.push_back(buffer_id);
313 pending_va_bufs_.push_back(buffer_id);
320 void VaapiWrapper::DestroyPendingBuffers() {
321 base::AutoLock auto_lock(va_lock_);
323 for (size_t i = 0; i < pending_va_bufs_.size(); ++i) {
324 VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_bufs_[i]);
325 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
328 for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) {
329 VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_bufs_[i]);
330 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
333 pending_va_bufs_.clear();
334 pending_slice_bufs_.clear();
337 bool VaapiWrapper::SubmitDecode(VASurfaceID va_surface_id) {
338 base::AutoLock auto_lock(va_lock_);
340 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
341 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
342 DVLOG(4) << "Decoding into VA surface " << va_surface_id;
344 // Get ready to decode into surface.
345 VAStatus va_res = vaBeginPicture(va_display_, va_context_id_,
347 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
349 // Commit parameter and slice buffers.
350 va_res = vaRenderPicture(va_display_, va_context_id_,
351 &pending_va_bufs_[0], pending_va_bufs_.size());
352 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
354 va_res = vaRenderPicture(va_display_, va_context_id_,
355 &pending_slice_bufs_[0],
356 pending_slice_bufs_.size());
357 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
359 // Instruct HW decoder to start processing committed buffers (decode this
360 // picture). This does not block until the end of decode.
361 va_res = vaEndPicture(va_display_, va_context_id_);
362 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
367 bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
368 bool result = SubmitDecode(va_surface_id);
369 DestroyPendingBuffers();
373 bool VaapiWrapper::CreateRGBImage(gfx::Size size, VAImage* image) {
374 base::AutoLock auto_lock(va_lock_);
376 VAImageFormat format;
377 format.fourcc = VA_FOURCC_BGRX;
378 format.byte_order = VA_LSB_FIRST;
379 format.bits_per_pixel = 32;
381 format.red_mask = 0x0000ff;
382 format.green_mask = 0x00ff00;
383 format.blue_mask = 0xff0000;
384 format.alpha_mask = 0;
386 va_res = vaCreateImage(va_display_,
391 VA_SUCCESS_OR_RETURN(va_res, "Failed to create image", false);
395 void VaapiWrapper::DestroyImage(VAImage* image) {
396 base::AutoLock auto_lock(va_lock_);
397 vaDestroyImage(va_display_, image->image_id);
400 bool VaapiWrapper::MapImage(VAImage* image, void** buffer) {
401 base::AutoLock auto_lock(va_lock_);
403 VAStatus va_res = vaMapBuffer(va_display_, image->buf, buffer);
404 VA_SUCCESS_OR_RETURN(va_res, "Failed to map image", false);
408 void VaapiWrapper::UnmapImage(VAImage* image) {
409 base::AutoLock auto_lock(va_lock_);
410 vaUnmapBuffer(va_display_, image->buf);
413 bool VaapiWrapper::PutSurfaceIntoImage(VASurfaceID va_surface_id,
415 base::AutoLock auto_lock(va_lock_);
416 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
417 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
419 va_res = vaGetImage(va_display_,
426 VA_SUCCESS_OR_RETURN(va_res, "Failed to put surface into image", false);
430 bool VaapiWrapper::LockBuffer(VASurfaceID va_surface_id,
432 VABufferInfo* buf_info) {
434 base::AutoLock auto_lock(va_lock_);
436 buf_info->mem_type = VA_SURFACE_ATTRIB_MEM_TYPE_KERNEL_DRM;
437 VAStatus va_res = vaLockBuffer(va_display_, buf_id, buf_info);
438 VA_SUCCESS_OR_RETURN(va_res, "Failed to lock vabuffer", false);
443 bool VaapiWrapper::UnlockBuffer(VASurfaceID va_surface_id,
445 VABufferInfo* buf_info) {
447 base::AutoLock auto_lock(va_lock_);
448 VAStatus va_res = vaUnlockBuffer(va_display_, buf_id, buf_info);
449 VA_SUCCESS_OR_RETURN(va_res, "Failed to unlock vabuffer", false);
455 bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
458 base::AutoLock auto_lock(va_lock_);
460 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
461 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
463 // Derive a VAImage from the VASurface
464 va_res = vaDeriveImage(va_display_, va_surface_id, image);
465 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed");
466 if (va_res != VA_STATUS_SUCCESS)
469 // Map the VAImage into memory
470 va_res = vaMapBuffer(va_display_, image->buf, mem);
471 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
472 if (va_res == VA_STATUS_SUCCESS)
475 vaDestroyImage(va_display_, image->image_id);
479 void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) {
480 base::AutoLock auto_lock(va_lock_);
482 vaUnmapBuffer(va_display_, image->buf);
483 vaDestroyImage(va_display_, image->image_id);
487 bool VaapiWrapper::PostSandboxInitialization() {
489 paths[kModuleVa_wayland].push_back(kVaLib);
490 bool ret = InitializeStubs(paths);
492 LOG(WARNING) << "Could not open " << kVaLib;
496 bool VaapiWrapper::SupportsVaLockBufferApis() {
497 void* handle = dlopen(kVaLib, RTLD_LAZY);
499 LOG(ERROR) << "Could not open " << kVaLib;
502 return dlsym(handle, kVaLockBufferSymbol) &&
503 dlsym(handle, kVaUnlockBufferSymbol);