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 "content/common/gpu/media/vaapi_wrapper.h"
8 #if defined (USE_OZONE)
9 #include <wayland-client.h>
12 #include "base/bind.h"
13 #include "base/logging.h"
14 #include "base/numerics/safe_conversions.h"
15 // Auto-generated for dlopen libva libraries
16 #include "content/common/gpu/media/va_stubs.h"
18 #if defined (USE_OZONE)
19 #include "third_party/libva/va/wayland/va_wayland.h"
21 using content_common_gpu_media::kModuleVa_wayland;
23 using content_common_gpu_media::kModuleVa;
25 using content_common_gpu_media::InitializeStubs;
26 using content_common_gpu_media::StubPathMap;
28 // libva-x11 depends on libva, so dlopen libva-x11 is enough
29 static const base::FilePath::CharType kVaLib[] =
30 #if defined (USE_OZONE)
31 FILE_PATH_LITERAL("libva-wayland.so.1");
33 FILE_PATH_LITERAL("libva-x11.so.1");
36 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \
39 << " VA error: " << vaErrorStr(va_error); \
40 report_error_to_uma_cb_.Run(); \
43 #define VA_LOG_ON_ERROR(va_error, err_msg) \
45 if ((va_error) != VA_STATUS_SUCCESS) \
46 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
49 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \
51 if ((va_error) != VA_STATUS_SUCCESS) { \
52 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
59 // Maps Profile enum values to VaProfile values.
60 static VAProfile ProfileToVAProfile(
61 media::VideoCodecProfile profile,
62 const std::vector<VAProfile>& supported_profiles) {
64 VAProfile va_profile = VAProfileNone;
67 case media::H264PROFILE_BASELINE:
68 va_profile = VAProfileH264Baseline;
70 case media::H264PROFILE_MAIN:
71 va_profile = VAProfileH264Main;
73 // TODO(posciak): See if we can/want support other variants
74 // of media::H264PROFILE_HIGH*.
75 case media::H264PROFILE_HIGH:
76 va_profile = VAProfileH264High;
82 bool supported = std::find(supported_profiles.begin(),
83 supported_profiles.end(),
84 va_profile) != supported_profiles.end();
86 if (!supported && va_profile == VAProfileH264Baseline) {
87 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips
88 // the information whether the profile is constrained or not, so we have no
89 // way to know here. Try for baseline first, but if it is not supported,
90 // try constrained baseline and hope this is what it actually is
91 // (which in practice is true for a great majority of cases).
92 if (std::find(supported_profiles.begin(),
93 supported_profiles.end(),
94 VAProfileH264ConstrainedBaseline) !=
95 supported_profiles.end()) {
96 va_profile = VAProfileH264ConstrainedBaseline;
97 DVLOG(1) << "Falling back to constrained baseline profile.";
104 VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb)
105 : va_surface_id_(va_surface_id),
106 release_cb_(release_cb) {
107 DCHECK(!release_cb_.is_null());
110 VASurface::~VASurface() {
111 release_cb_.Run(va_surface_id_);
114 VaapiWrapper::VaapiWrapper()
116 va_config_id_(VA_INVALID_ID),
117 va_context_id_(VA_INVALID_ID) {
120 VaapiWrapper::~VaapiWrapper() {
121 DestroyPendingBuffers();
126 scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
127 media::VideoCodecProfile profile,
129 const base::Closure& report_error_to_uma_cb) {
130 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
132 if (!vaapi_wrapper->Initialize(profile, display, report_error_to_uma_cb))
133 vaapi_wrapper.reset();
135 return vaapi_wrapper.Pass();
138 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
139 VADisplayAttribute item = {VADisplayAttribRenderMode,
140 1, // At least support '_LOCAL_OVERLAY'.
141 -1, // The maximum possible support 'ALL'.
142 VA_RENDER_MODE_LOCAL_GPU,
143 VA_DISPLAY_ATTRIB_SETTABLE};
145 VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1);
146 if (va_res != VA_STATUS_SUCCESS)
147 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
150 bool VaapiWrapper::Initialize(media::VideoCodecProfile profile,
152 const base::Closure& report_error_to_uma_cb) {
153 static bool vaapi_functions_initialized = PostSandboxInitialization();
154 if (!vaapi_functions_initialized) {
155 DVLOG(1) << "Failed to initialize VAAPI libs";
159 report_error_to_uma_cb_ = report_error_to_uma_cb;
161 base::AutoLock auto_lock(va_lock_);
163 #if defined (USE_OZONE)
164 va_display_ = vaGetDisplayWl(static_cast<wl_display *>(display));
166 va_display_ = vaGetDisplay(static_cast<Display *>(display));
168 if (!vaDisplayIsValid(va_display_)) {
169 DVLOG(1) << "Could not get a valid VA display";
173 VAStatus va_res = vaInitialize(va_display_, &major_version_, &minor_version_);
174 VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false);
175 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_;
177 if (VAAPIVersionLessThan(0, 34)) {
178 DVLOG(1) << "VAAPI version < 0.34 is not supported.";
182 // Query the driver for supported profiles.
183 int max_profiles = vaMaxNumProfiles(va_display_);
184 std::vector<VAProfile> supported_profiles(
185 base::checked_cast<size_t>(max_profiles));
187 int num_supported_profiles;
188 va_res = vaQueryConfigProfiles(
189 va_display_, &supported_profiles[0], &num_supported_profiles);
190 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false);
191 if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) {
192 DVLOG(1) << "vaQueryConfigProfiles returned: " << num_supported_profiles;
196 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles));
198 VAProfile va_profile = ProfileToVAProfile(profile, supported_profiles);
199 if (va_profile == VAProfileNone) {
200 DVLOG(1) << "Unsupported profile";
204 VAConfigAttrib attrib = {VAConfigAttribRTFormat, 0};
205 const VAEntrypoint kEntrypoint = VAEntrypointVLD;
206 va_res = vaGetConfigAttributes(va_display_, va_profile, kEntrypoint,
208 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
210 if (!(attrib.value & VA_RT_FORMAT_YUV420)) {
211 DVLOG(1) << "YUV420 not supported by this VAAPI implementation";
215 TryToSetVADisplayAttributeToLocalGPU();
217 va_res = vaCreateConfig(va_display_, va_profile, kEntrypoint,
218 &attrib, 1, &va_config_id_);
219 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
224 void VaapiWrapper::Deinitialize() {
225 base::AutoLock auto_lock(va_lock_);
227 if (va_config_id_ != VA_INVALID_ID) {
228 VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_);
229 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed");
233 VAStatus va_res = vaTerminate(va_display_);
234 VA_LOG_ON_ERROR(va_res, "vaTerminate failed");
237 va_config_id_ = VA_INVALID_ID;
241 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
242 return (major_version_ < major) ||
243 (major_version_ == major && minor_version_ < minor);
246 bool VaapiWrapper::CreateSurfaces(gfx::Size size,
248 std::vector<VASurfaceID>* va_surfaces) {
249 base::AutoLock auto_lock(va_lock_);
250 DVLOG(2) << "Creating " << num_surfaces << " surfaces";
252 DCHECK(va_surfaces->empty());
253 DCHECK(va_surface_ids_.empty());
254 va_surface_ids_.resize(num_surfaces);
256 // Allocate surfaces in driver.
257 VAStatus va_res = vaCreateSurfaces(va_display_,
259 size.width(), size.height(),
261 va_surface_ids_.size(),
264 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
265 if (va_res != VA_STATUS_SUCCESS) {
266 va_surface_ids_.clear();
270 // And create a context associated with them.
271 va_res = vaCreateContext(va_display_, va_config_id_,
272 size.width(), size.height(), VA_PROGRESSIVE,
273 &va_surface_ids_[0], va_surface_ids_.size(),
276 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
277 if (va_res != VA_STATUS_SUCCESS) {
282 *va_surfaces = va_surface_ids_;
286 void VaapiWrapper::DestroySurfaces() {
287 base::AutoLock auto_lock(va_lock_);
288 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces";
290 if (va_context_id_ != VA_INVALID_ID) {
291 VAStatus va_res = vaDestroyContext(va_display_, va_context_id_);
292 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed");
295 if (!va_surface_ids_.empty()) {
296 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0],
297 va_surface_ids_.size());
298 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed");
301 va_surface_ids_.clear();
302 va_context_id_ = VA_INVALID_ID;
305 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
308 base::AutoLock auto_lock(va_lock_);
310 VABufferID buffer_id;
311 VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_,
312 va_buffer_type, size,
313 1, buffer, &buffer_id);
314 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
316 switch (va_buffer_type) {
317 case VASliceParameterBufferType:
318 case VASliceDataBufferType:
319 pending_slice_bufs_.push_back(buffer_id);
323 pending_va_bufs_.push_back(buffer_id);
330 void VaapiWrapper::DestroyPendingBuffers() {
331 base::AutoLock auto_lock(va_lock_);
333 for (size_t i = 0; i < pending_va_bufs_.size(); ++i) {
334 VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_bufs_[i]);
335 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
338 for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) {
339 VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_bufs_[i]);
340 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
343 pending_va_bufs_.clear();
344 pending_slice_bufs_.clear();
347 bool VaapiWrapper::SubmitDecode(VASurfaceID va_surface_id) {
348 base::AutoLock auto_lock(va_lock_);
350 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
351 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
352 DVLOG(4) << "Decoding into VA surface " << va_surface_id;
354 // Get ready to decode into surface.
355 VAStatus va_res = vaBeginPicture(va_display_, va_context_id_,
357 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
359 // Commit parameter and slice buffers.
360 va_res = vaRenderPicture(va_display_, va_context_id_,
361 &pending_va_bufs_[0], pending_va_bufs_.size());
362 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
364 va_res = vaRenderPicture(va_display_, va_context_id_,
365 &pending_slice_bufs_[0],
366 pending_slice_bufs_.size());
367 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
369 // Instruct HW decoder to start processing committed buffers (decode this
370 // picture). This does not block until the end of decode.
371 va_res = vaEndPicture(va_display_, va_context_id_);
372 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
377 bool VaapiWrapper::DecodeAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
378 bool result = SubmitDecode(va_surface_id);
379 DestroyPendingBuffers();
382 #if defined (USE_OZONE)
383 bool VaapiWrapper::CreateRGBImage(gfx::Size size, VAImage* image) {
384 base::AutoLock auto_lock(va_lock_);
386 VAImageFormat format;
387 format.fourcc = VA_FOURCC_RGBX;
388 format.byte_order = VA_LSB_FIRST;
389 format.bits_per_pixel = 32;
391 format.red_mask = 0xff;
392 format.green_mask = 0xff00;
393 format.blue_mask = 0xff0000;
394 format.alpha_mask = 0;
395 va_res = vaCreateImage(va_display_,
400 VA_SUCCESS_OR_RETURN(va_res, "Failed to create image", false);
404 void VaapiWrapper::DestroyImage(VAImage* image) {
405 base::AutoLock auto_lock(va_lock_);
406 vaDestroyImage(va_display_, image->image_id);
409 bool VaapiWrapper::MapImage(VAImage* image, void** buffer) {
410 base::AutoLock auto_lock(va_lock_);
412 VAStatus va_res = vaMapBuffer(va_display_, image->buf, buffer);
413 VA_SUCCESS_OR_RETURN(va_res, "Failed to map image", false);
417 void VaapiWrapper::UnmapImage(VAImage* image) {
418 base::AutoLock auto_lock(va_lock_);
419 vaUnmapBuffer(va_display_, image->buf);
422 bool VaapiWrapper::PutSurfaceIntoImage(VASurfaceID va_surface_id,
424 base::AutoLock auto_lock(va_lock_);
425 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
426 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
428 va_res = vaGetImage(va_display_,
435 VA_SUCCESS_OR_RETURN(va_res, "Failed to put surface into image", false);
439 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
441 gfx::Size dest_size) {
442 base::AutoLock auto_lock(va_lock_);
444 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
445 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
447 // Put the data into an X Pixmap.
448 va_res = vaPutSurface(va_display_,
451 0, 0, dest_size.width(), dest_size.height(),
452 0, 0, dest_size.width(), dest_size.height(),
454 VA_SUCCESS_OR_RETURN(va_res, "Failed putting decode surface to pixmap",
459 bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id,
462 base::AutoLock auto_lock(va_lock_);
464 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
465 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
467 // Derive a VAImage from the VASurface
468 va_res = vaDeriveImage(va_display_, va_surface_id, image);
469 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed");
470 if (va_res != VA_STATUS_SUCCESS)
473 // Map the VAImage into memory
474 va_res = vaMapBuffer(va_display_, image->buf, mem);
475 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
476 if (va_res == VA_STATUS_SUCCESS)
479 vaDestroyImage(va_display_, image->image_id);
483 void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) {
484 base::AutoLock auto_lock(va_lock_);
486 vaUnmapBuffer(va_display_, image->buf);
487 vaDestroyImage(va_display_, image->image_id);
491 bool VaapiWrapper::PostSandboxInitialization() {
493 #if defined (USE_OZONE)
494 paths[kModuleVa_wayland].push_back(kVaLib);
496 paths[kModuleVa].push_back(kVaLib);
498 return InitializeStubs(paths);
501 } // namespace content