Supervised user whitelists: Cleanup
[chromium-blink-merge.git] / content / common / gpu / media / vaapi_wrapper.cc
blob78d382e4c0aeb0e72b0dfb2c58e53eb348a5514d
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"
7 #include <dlfcn.h>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/command_line.h"
12 #include "base/logging.h"
13 #include "base/numerics/safe_conversions.h"
14 #include "base/sys_info.h"
15 // Auto-generated for dlopen libva libraries
16 #include "content/common/gpu/media/va_stubs.h"
17 #include "content/common/gpu/media/vaapi_picture.h"
18 #include "content/public/common/content_switches.h"
19 #include "third_party/libyuv/include/libyuv.h"
20 #include "ui/gl/gl_bindings.h"
21 #if defined(USE_X11)
22 #include "ui/gfx/x/x11_types.h"
23 #elif defined(USE_OZONE)
24 #include "third_party/libva/va/drm/va_drm.h"
25 #include "ui/ozone/public/ozone_platform.h"
26 #include "ui/ozone/public/surface_factory_ozone.h"
27 #endif // USE_X11
29 using content_common_gpu_media::kModuleVa;
30 #if defined(USE_X11)
31 using content_common_gpu_media::kModuleVa_x11;
32 #elif defined(USE_OZONE)
33 using content_common_gpu_media::kModuleVa_drm;
34 #endif // USE_X11
35 using content_common_gpu_media::InitializeStubs;
36 using content_common_gpu_media::StubPathMap;
38 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \
39 do { \
40 LOG(ERROR) << err_msg \
41 << " VA error: " << vaErrorStr(va_error); \
42 report_error_to_uma_cb_.Run(); \
43 } while (0)
45 #define VA_LOG_ON_ERROR(va_error, err_msg) \
46 do { \
47 if ((va_error) != VA_STATUS_SUCCESS) \
48 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
49 } while (0)
51 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \
52 do { \
53 if ((va_error) != VA_STATUS_SUCCESS) { \
54 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
55 return (ret); \
56 } \
57 } while (0)
59 namespace content {
61 // Maximum framerate of encoded profile. This value is an arbitary limit
62 // and not taken from HW documentation.
63 const int kMaxEncoderFramerate = 30;
65 base::LazyInstance<VaapiWrapper::LazyProfileInfos>
66 VaapiWrapper::profile_infos_ = LAZY_INSTANCE_INITIALIZER;
68 // Config attributes common for both encode and decode.
69 static const VAConfigAttrib kCommonVAConfigAttribs[] = {
70 {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420},
73 // Attributes required for encode.
74 static const VAConfigAttrib kEncodeVAConfigAttribs[] = {
75 {VAConfigAttribRateControl, VA_RC_CBR},
76 {VAConfigAttribEncPackedHeaders,
77 VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE},
80 struct ProfileMap {
81 media::VideoCodecProfile profile;
82 VAProfile va_profile;
85 // A map between VideoCodecProfile and VAProfile.
86 static const ProfileMap kProfileMap[] = {
87 {media::H264PROFILE_BASELINE, VAProfileH264Baseline},
88 {media::H264PROFILE_MAIN, VAProfileH264Main},
89 // TODO(posciak): See if we can/want support other variants of
90 // media::H264PROFILE_HIGH*.
91 {media::H264PROFILE_HIGH, VAProfileH264High},
94 static std::vector<VAConfigAttrib> GetRequiredAttribs(
95 VaapiWrapper::CodecMode mode) {
96 std::vector<VAConfigAttrib> required_attribs;
97 required_attribs.insert(
98 required_attribs.end(),
99 kCommonVAConfigAttribs,
100 kCommonVAConfigAttribs + arraysize(kCommonVAConfigAttribs));
101 if (mode == VaapiWrapper::kEncode) {
102 required_attribs.insert(
103 required_attribs.end(),
104 kEncodeVAConfigAttribs,
105 kEncodeVAConfigAttribs + arraysize(kEncodeVAConfigAttribs));
107 return required_attribs;
110 VASurface::VASurface(VASurfaceID va_surface_id,
111 const gfx::Size& size,
112 const ReleaseCB& release_cb)
113 : va_surface_id_(va_surface_id), size_(size), release_cb_(release_cb) {
114 DCHECK(!release_cb_.is_null());
117 VASurface::~VASurface() {
118 release_cb_.Run(va_surface_id_);
121 VaapiWrapper::VaapiWrapper()
122 : va_display_(NULL),
123 va_config_id_(VA_INVALID_ID),
124 va_context_id_(VA_INVALID_ID),
125 va_initialized_(false),
126 va_vpp_config_id_(VA_INVALID_ID),
127 va_vpp_context_id_(VA_INVALID_ID),
128 va_vpp_buffer_id_(VA_INVALID_ID) {
131 VaapiWrapper::~VaapiWrapper() {
132 DestroyPendingBuffers();
133 DestroyCodedBuffers();
134 DestroySurfaces();
135 DeinitializeVpp();
136 Deinitialize();
139 // static
140 scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
141 CodecMode mode,
142 VAProfile va_profile,
143 const base::Closure& report_error_to_uma_cb) {
144 if (!profile_infos_.Get().IsProfileSupported(mode, va_profile)) {
145 DVLOG(1) << "Unsupported va_profile: " << va_profile;
146 return nullptr;
149 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
150 if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) {
151 if (vaapi_wrapper->Initialize(mode, va_profile))
152 return vaapi_wrapper.Pass();
154 LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " << va_profile;
155 return nullptr;
158 // static
159 scoped_ptr<VaapiWrapper> VaapiWrapper::CreateForVideoCodec(
160 CodecMode mode,
161 media::VideoCodecProfile profile,
162 const base::Closure& report_error_to_uma_cb) {
163 VAProfile va_profile = ProfileToVAProfile(profile, mode);
164 scoped_ptr<VaapiWrapper> vaapi_wrapper =
165 Create(mode, va_profile, report_error_to_uma_cb);
166 return vaapi_wrapper.Pass();
169 // static
170 media::VideoEncodeAccelerator::SupportedProfiles
171 VaapiWrapper::GetSupportedEncodeProfiles() {
172 media::VideoEncodeAccelerator::SupportedProfiles profiles;
173 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
174 if (cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode))
175 return profiles;
177 std::vector<ProfileInfo> encode_profile_infos =
178 profile_infos_.Get().GetSupportedProfileInfosForCodecMode(kEncode);
180 for (size_t i = 0; i < arraysize(kProfileMap); ++i) {
181 VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode);
182 if (va_profile == VAProfileNone)
183 continue;
184 for (const auto& profile_info : encode_profile_infos) {
185 if (profile_info.va_profile == va_profile) {
186 media::VideoEncodeAccelerator::SupportedProfile profile;
187 profile.profile = kProfileMap[i].profile;
188 profile.max_resolution = profile_info.max_resolution;
189 profile.max_framerate_numerator = kMaxEncoderFramerate;
190 profile.max_framerate_denominator = 1;
191 profiles.push_back(profile);
192 break;
196 return profiles;
199 // static
200 media::VideoDecodeAccelerator::SupportedProfiles
201 VaapiWrapper::GetSupportedDecodeProfiles() {
202 media::VideoDecodeAccelerator::SupportedProfiles profiles;
203 std::vector<ProfileInfo> decode_profile_infos =
204 profile_infos_.Get().GetSupportedProfileInfosForCodecMode(kDecode);
206 for (size_t i = 0; i < arraysize(kProfileMap); ++i) {
207 VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kDecode);
208 if (va_profile == VAProfileNone)
209 continue;
210 for (const auto& profile_info : decode_profile_infos) {
211 if (profile_info.va_profile == va_profile) {
212 media::VideoDecodeAccelerator::SupportedProfile profile;
213 profile.profile = kProfileMap[i].profile;
214 profile.max_resolution = profile_info.max_resolution;
215 profile.min_resolution.SetSize(16, 16);
216 profiles.push_back(profile);
217 break;
221 return profiles;
224 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
225 base::AutoLock auto_lock(va_lock_);
226 VADisplayAttribute item = {VADisplayAttribRenderMode,
227 1, // At least support '_LOCAL_OVERLAY'.
228 -1, // The maximum possible support 'ALL'.
229 VA_RENDER_MODE_LOCAL_GPU,
230 VA_DISPLAY_ATTRIB_SETTABLE};
232 VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1);
233 if (va_res != VA_STATUS_SUCCESS)
234 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
237 // static
238 VAProfile VaapiWrapper::ProfileToVAProfile(
239 media::VideoCodecProfile profile, CodecMode mode) {
240 VAProfile va_profile = VAProfileNone;
241 for (size_t i = 0; i < arraysize(kProfileMap); ++i) {
242 if (kProfileMap[i].profile == profile) {
243 va_profile = kProfileMap[i].va_profile;
244 break;
247 if (!profile_infos_.Get().IsProfileSupported(mode, va_profile) &&
248 va_profile == VAProfileH264Baseline) {
249 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips
250 // the information whether the profile is constrained or not, so we have no
251 // way to know here. Try for baseline first, but if it is not supported,
252 // try constrained baseline and hope this is what it actually is
253 // (which in practice is true for a great majority of cases).
254 if (profile_infos_.Get().IsProfileSupported(
255 mode, VAProfileH264ConstrainedBaseline)) {
256 va_profile = VAProfileH264ConstrainedBaseline;
257 DVLOG(1) << "Fall back to constrained baseline profile.";
260 return va_profile;
263 std::vector<VaapiWrapper::ProfileInfo>
264 VaapiWrapper::GetSupportedProfileInfosForCodecModeInternal(CodecMode mode) {
265 std::vector<ProfileInfo> supported_profile_infos;
266 std::vector<VAProfile> va_profiles;
267 if (!GetSupportedVaProfiles(&va_profiles))
268 return supported_profile_infos;
270 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode);
271 VAEntrypoint entrypoint =
272 (mode == kEncode ? VAEntrypointEncSlice: VAEntrypointVLD);
274 base::AutoLock auto_lock(va_lock_);
275 for (const auto& va_profile : va_profiles) {
276 if (!IsEntrypointSupported_Locked(va_profile, entrypoint))
277 continue;
278 if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs))
279 continue;
280 ProfileInfo profile_info;
281 if (!GetMaxResolution_Locked(va_profile,
282 entrypoint,
283 required_attribs,
284 &profile_info.max_resolution)) {
285 LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile
286 << " and entrypoint " << entrypoint;
287 continue;
289 profile_info.va_profile = va_profile;
290 supported_profile_infos.push_back(profile_info);
292 return supported_profile_infos;
295 bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) {
296 static bool vaapi_functions_initialized = PostSandboxInitialization();
297 if (!vaapi_functions_initialized) {
298 bool running_on_chromeos = false;
299 #if defined(OS_CHROMEOS)
300 // When chrome runs on linux with chromeos=1, do not log error message
301 // without VAAPI libraries.
302 running_on_chromeos = base::SysInfo::IsRunningOnChromeOS();
303 #endif
304 static const char kErrorMsg[] = "Failed to initialize VAAPI libs";
305 if (running_on_chromeos)
306 LOG(ERROR) << kErrorMsg;
307 else
308 DVLOG(1) << kErrorMsg;
309 return false;
312 report_error_to_uma_cb_ = report_error_to_uma_cb;
314 base::AutoLock auto_lock(va_lock_);
316 #if defined(USE_X11)
317 va_display_ = vaGetDisplay(gfx::GetXDisplay());
318 #elif defined(USE_OZONE)
319 const char* kDriRenderNode0Path = "/dev/dri/renderD128";
320 drm_file_ = base::File(base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path),
321 base::File::FLAG_OPEN | base::File::FLAG_READ |
322 base::File::FLAG_WRITE);
323 va_display_ = vaGetDisplayDRM(drm_file_.GetPlatformFile());
324 #endif // USE_X11
326 if (!vaDisplayIsValid(va_display_)) {
327 LOG(ERROR) << "Could not get a valid VA display";
328 return false;
331 VAStatus va_res = vaInitialize(va_display_, &major_version_, &minor_version_);
332 VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false);
333 va_initialized_ = true;
334 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_;
336 if (VAAPIVersionLessThan(0, 34)) {
337 LOG(ERROR) << "VAAPI version < 0.34 is not supported.";
338 return false;
340 return true;
343 bool VaapiWrapper::GetSupportedVaProfiles(std::vector<VAProfile>* profiles) {
344 base::AutoLock auto_lock(va_lock_);
345 // Query the driver for supported profiles.
346 int max_profiles = vaMaxNumProfiles(va_display_);
347 std::vector<VAProfile> supported_profiles(
348 base::checked_cast<size_t>(max_profiles));
350 int num_supported_profiles;
351 VAStatus va_res = vaQueryConfigProfiles(
352 va_display_, &supported_profiles[0], &num_supported_profiles);
353 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false);
354 if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) {
355 LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_supported_profiles;
356 return false;
359 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles));
360 *profiles = supported_profiles;
361 return true;
364 bool VaapiWrapper::IsEntrypointSupported_Locked(VAProfile va_profile,
365 VAEntrypoint entrypoint) {
366 va_lock_.AssertAcquired();
367 // Query the driver for supported entrypoints.
368 int max_entrypoints = vaMaxNumEntrypoints(va_display_);
369 std::vector<VAEntrypoint> supported_entrypoints(
370 base::checked_cast<size_t>(max_entrypoints));
372 int num_supported_entrypoints;
373 VAStatus va_res = vaQueryConfigEntrypoints(va_display_,
374 va_profile,
375 &supported_entrypoints[0],
376 &num_supported_entrypoints);
377 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false);
378 if (num_supported_entrypoints < 0 ||
379 num_supported_entrypoints > max_entrypoints) {
380 LOG(ERROR) << "vaQueryConfigEntrypoints returned: "
381 << num_supported_entrypoints;
382 return false;
385 if (std::find(supported_entrypoints.begin(),
386 supported_entrypoints.end(),
387 entrypoint) == supported_entrypoints.end()) {
388 DVLOG(1) << "Unsupported entrypoint";
389 return false;
391 return true;
394 bool VaapiWrapper::AreAttribsSupported_Locked(
395 VAProfile va_profile,
396 VAEntrypoint entrypoint,
397 const std::vector<VAConfigAttrib>& required_attribs) {
398 va_lock_.AssertAcquired();
399 // Query the driver for required attributes.
400 std::vector<VAConfigAttrib> attribs = required_attribs;
401 for (size_t i = 0; i < required_attribs.size(); ++i)
402 attribs[i].value = 0;
404 VAStatus va_res = vaGetConfigAttributes(
405 va_display_, va_profile, entrypoint, &attribs[0], attribs.size());
406 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
408 for (size_t i = 0; i < required_attribs.size(); ++i) {
409 if (attribs[i].type != required_attribs[i].type ||
410 (attribs[i].value & required_attribs[i].value) !=
411 required_attribs[i].value) {
412 DVLOG(1) << "Unsupported value " << required_attribs[i].value
413 << " for attribute type " << required_attribs[i].type;
414 return false;
417 return true;
420 bool VaapiWrapper::GetMaxResolution_Locked(
421 VAProfile va_profile,
422 VAEntrypoint entrypoint,
423 std::vector<VAConfigAttrib>& required_attribs,
424 gfx::Size* resolution) {
425 va_lock_.AssertAcquired();
426 VAConfigID va_config_id;
427 VAStatus va_res = vaCreateConfig(
428 va_display_,
429 va_profile,
430 entrypoint,
431 &required_attribs[0],
432 required_attribs.size(),
433 &va_config_id);
434 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
436 // Calls vaQuerySurfaceAttributes twice. The first time is to get the number
437 // of attributes to prepare the space and the second time is to get all
438 // attributes.
439 unsigned int num_attribs;
440 va_res = vaQuerySurfaceAttributes(
441 va_display_, va_config_id, nullptr, &num_attribs);
442 VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false);
443 if (!num_attribs)
444 return false;
446 std::vector<VASurfaceAttrib> attrib_list(
447 base::checked_cast<size_t>(num_attribs));
449 va_res = vaQuerySurfaceAttributes(
450 va_display_, va_config_id, &attrib_list[0], &num_attribs);
451 VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false);
453 resolution->SetSize(0, 0);
454 for (const auto& attrib : attrib_list) {
455 if (attrib.type == VASurfaceAttribMaxWidth)
456 resolution->set_width(attrib.value.value.i);
457 else if (attrib.type == VASurfaceAttribMaxHeight)
458 resolution->set_height(attrib.value.value.i);
460 if (resolution->IsEmpty()) {
461 LOG(ERROR) << "Codec resolution " << resolution->ToString()
462 << " cannot be zero.";
463 return false;
465 return true;
468 bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) {
469 TryToSetVADisplayAttributeToLocalGPU();
471 VAEntrypoint entrypoint =
472 (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD);
473 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode);
474 base::AutoLock auto_lock(va_lock_);
475 VAStatus va_res = vaCreateConfig(va_display_,
476 va_profile,
477 entrypoint,
478 &required_attribs[0],
479 required_attribs.size(),
480 &va_config_id_);
481 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
483 return true;
486 void VaapiWrapper::Deinitialize() {
487 base::AutoLock auto_lock(va_lock_);
489 if (va_config_id_ != VA_INVALID_ID) {
490 VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_);
491 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed");
494 // Must check if vaInitialize completed successfully, to work around a bug in
495 // libva. The bug was fixed upstream:
496 // http://lists.freedesktop.org/archives/libva/2013-July/001807.html
497 // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once
498 // the fix has rolled out sufficiently.
499 if (va_initialized_ && va_display_) {
500 VAStatus va_res = vaTerminate(va_display_);
501 VA_LOG_ON_ERROR(va_res, "vaTerminate failed");
504 va_config_id_ = VA_INVALID_ID;
505 va_display_ = NULL;
506 va_initialized_ = false;
509 bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) {
510 return (major_version_ < major) ||
511 (major_version_ == major && minor_version_ < minor);
514 bool VaapiWrapper::CreateSurfaces(const gfx::Size& size,
515 size_t num_surfaces,
516 std::vector<VASurfaceID>* va_surfaces) {
517 base::AutoLock auto_lock(va_lock_);
518 DVLOG(2) << "Creating " << num_surfaces << " surfaces";
520 DCHECK(va_surfaces->empty());
521 DCHECK(va_surface_ids_.empty());
522 va_surface_ids_.resize(num_surfaces);
524 // Allocate surfaces in driver.
525 VAStatus va_res = vaCreateSurfaces(va_display_,
526 VA_RT_FORMAT_YUV420,
527 size.width(), size.height(),
528 &va_surface_ids_[0],
529 va_surface_ids_.size(),
530 NULL, 0);
532 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
533 if (va_res != VA_STATUS_SUCCESS) {
534 va_surface_ids_.clear();
535 return false;
538 // And create a context associated with them.
539 va_res = vaCreateContext(va_display_, va_config_id_,
540 size.width(), size.height(), VA_PROGRESSIVE,
541 &va_surface_ids_[0], va_surface_ids_.size(),
542 &va_context_id_);
544 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
545 if (va_res != VA_STATUS_SUCCESS) {
546 DestroySurfaces();
547 return false;
550 *va_surfaces = va_surface_ids_;
551 return true;
554 void VaapiWrapper::DestroySurfaces() {
555 base::AutoLock auto_lock(va_lock_);
556 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces";
558 if (va_context_id_ != VA_INVALID_ID) {
559 VAStatus va_res = vaDestroyContext(va_display_, va_context_id_);
560 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed");
563 if (!va_surface_ids_.empty()) {
564 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0],
565 va_surface_ids_.size());
566 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed");
569 va_surface_ids_.clear();
570 va_context_id_ = VA_INVALID_ID;
573 scoped_refptr<VASurface> VaapiWrapper::CreateUnownedSurface(
574 unsigned int va_format,
575 const gfx::Size& size,
576 const std::vector<VASurfaceAttrib>& va_attribs) {
577 base::AutoLock auto_lock(va_lock_);
579 std::vector<VASurfaceAttrib> attribs(va_attribs);
580 VASurfaceID va_surface_id;
581 VAStatus va_res =
582 vaCreateSurfaces(va_display_, va_format, size.width(), size.height(),
583 &va_surface_id, 1, &attribs[0], attribs.size());
585 scoped_refptr<VASurface> va_surface;
586 VA_SUCCESS_OR_RETURN(va_res, "Failed to create unowned VASurface",
587 va_surface);
589 // This is safe to use Unretained() here, because the VDA takes care
590 // of the destruction order. All the surfaces will be destroyed
591 // before VaapiWrapper.
592 va_surface = new VASurface(
593 va_surface_id, size,
594 base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this)));
596 return va_surface;
599 void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id) {
600 base::AutoLock auto_lock(va_lock_);
602 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1);
603 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces on surface failed");
606 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
607 size_t size,
608 void* buffer) {
609 base::AutoLock auto_lock(va_lock_);
611 VABufferID buffer_id;
612 VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_,
613 va_buffer_type, size,
614 1, buffer, &buffer_id);
615 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
617 switch (va_buffer_type) {
618 case VASliceParameterBufferType:
619 case VASliceDataBufferType:
620 case VAEncSliceParameterBufferType:
621 pending_slice_bufs_.push_back(buffer_id);
622 break;
624 default:
625 pending_va_bufs_.push_back(buffer_id);
626 break;
629 return true;
632 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
633 VAEncMiscParameterType misc_param_type,
634 size_t size,
635 void* buffer) {
636 base::AutoLock auto_lock(va_lock_);
638 VABufferID buffer_id;
639 VAStatus va_res = vaCreateBuffer(va_display_,
640 va_context_id_,
641 VAEncMiscParameterBufferType,
642 sizeof(VAEncMiscParameterBuffer) + size,
644 NULL,
645 &buffer_id);
646 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
648 void* data_ptr = NULL;
649 va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr);
650 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
651 if (va_res != VA_STATUS_SUCCESS) {
652 vaDestroyBuffer(va_display_, buffer_id);
653 return false;
656 DCHECK(data_ptr);
658 VAEncMiscParameterBuffer* misc_param =
659 reinterpret_cast<VAEncMiscParameterBuffer*>(data_ptr);
660 misc_param->type = misc_param_type;
661 memcpy(misc_param->data, buffer, size);
662 va_res = vaUnmapBuffer(va_display_, buffer_id);
663 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
665 pending_va_bufs_.push_back(buffer_id);
666 return true;
669 void VaapiWrapper::DestroyPendingBuffers() {
670 base::AutoLock auto_lock(va_lock_);
672 for (const auto& pending_va_buf : pending_va_bufs_) {
673 VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf);
674 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
677 for (const auto& pending_slice_buf : pending_slice_bufs_) {
678 VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf);
679 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
682 pending_va_bufs_.clear();
683 pending_slice_bufs_.clear();
686 bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) {
687 base::AutoLock auto_lock(va_lock_);
688 VAStatus va_res = vaCreateBuffer(va_display_,
689 va_context_id_,
690 VAEncCodedBufferType,
691 size,
693 NULL,
694 buffer_id);
695 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false);
697 DCHECK(coded_buffers_.insert(*buffer_id).second);
698 return true;
701 void VaapiWrapper::DestroyCodedBuffers() {
702 base::AutoLock auto_lock(va_lock_);
704 for (std::set<VABufferID>::const_iterator iter = coded_buffers_.begin();
705 iter != coded_buffers_.end();
706 ++iter) {
707 VAStatus va_res = vaDestroyBuffer(va_display_, *iter);
708 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
711 coded_buffers_.clear();
714 bool VaapiWrapper::Execute(VASurfaceID va_surface_id) {
715 base::AutoLock auto_lock(va_lock_);
717 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
718 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
719 DVLOG(4) << "Target VA surface " << va_surface_id;
721 // Get ready to execute for given surface.
722 VAStatus va_res = vaBeginPicture(va_display_, va_context_id_,
723 va_surface_id);
724 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
726 if (pending_va_bufs_.size() > 0) {
727 // Commit parameter and slice buffers.
728 va_res = vaRenderPicture(va_display_,
729 va_context_id_,
730 &pending_va_bufs_[0],
731 pending_va_bufs_.size());
732 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
735 if (pending_slice_bufs_.size() > 0) {
736 va_res = vaRenderPicture(va_display_,
737 va_context_id_,
738 &pending_slice_bufs_[0],
739 pending_slice_bufs_.size());
740 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
743 // Instruct HW codec to start processing committed buffers.
744 // Does not block and the job is not finished after this returns.
745 va_res = vaEndPicture(va_display_, va_context_id_);
746 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
748 return true;
751 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
752 bool result = Execute(va_surface_id);
753 DestroyPendingBuffers();
754 return result;
757 #if defined(USE_X11)
758 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
759 Pixmap x_pixmap,
760 gfx::Size dest_size) {
761 base::AutoLock auto_lock(va_lock_);
763 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
764 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
766 // Put the data into an X Pixmap.
767 va_res = vaPutSurface(va_display_,
768 va_surface_id,
769 x_pixmap,
770 0, 0, dest_size.width(), dest_size.height(),
771 0, 0, dest_size.width(), dest_size.height(),
772 NULL, 0, 0);
773 VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false);
774 return true;
776 #endif // USE_X11
778 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id,
779 VAImage* image,
780 void** mem) {
781 base::AutoLock auto_lock(va_lock_);
783 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
784 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
786 // Derive a VAImage from the VASurface
787 va_res = vaDeriveImage(va_display_, va_surface_id, image);
788 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed");
789 if (va_res != VA_STATUS_SUCCESS)
790 return false;
792 // Map the VAImage into memory
793 va_res = vaMapBuffer(va_display_, image->buf, mem);
794 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
795 if (va_res == VA_STATUS_SUCCESS)
796 return true;
798 va_res = vaDestroyImage(va_display_, image->image_id);
799 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
801 return false;
804 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id,
805 VAImageFormat* format,
806 const gfx::Size& size,
807 VAImage* image,
808 void** mem) {
809 base::AutoLock auto_lock(va_lock_);
811 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
812 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
814 va_res =
815 vaCreateImage(va_display_, format, size.width(), size.height(), image);
816 VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false);
818 va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(),
819 size.height(), image->image_id);
820 VA_LOG_ON_ERROR(va_res, "vaGetImage failed");
822 if (va_res == VA_STATUS_SUCCESS) {
823 // Map the VAImage into memory
824 va_res = vaMapBuffer(va_display_, image->buf, mem);
825 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
828 if (va_res != VA_STATUS_SUCCESS) {
829 va_res = vaDestroyImage(va_display_, image->image_id);
830 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
831 return false;
834 return true;
837 void VaapiWrapper::ReturnVaImage(VAImage* image) {
838 base::AutoLock auto_lock(va_lock_);
840 VAStatus va_res = vaUnmapBuffer(va_display_, image->buf);
841 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
843 va_res = vaDestroyImage(va_display_, image->image_id);
844 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
847 static void DestroyVAImage(VADisplay va_display, VAImage image) {
848 if (image.image_id != VA_INVALID_ID)
849 vaDestroyImage(va_display, image.image_id);
852 bool VaapiWrapper::UploadVideoFrameToSurface(
853 const scoped_refptr<media::VideoFrame>& frame,
854 VASurfaceID va_surface_id) {
855 base::AutoLock auto_lock(va_lock_);
857 VAImage image;
858 VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image);
859 VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false);
860 base::ScopedClosureRunner vaimage_deleter(
861 base::Bind(&DestroyVAImage, va_display_, image));
863 if (image.format.fourcc != VA_FOURCC_NV12) {
864 LOG(ERROR) << "Unsupported image format: " << image.format.fourcc;
865 return false;
868 if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) {
869 LOG(ERROR) << "Buffer too small to fit the frame.";
870 return false;
873 void* image_ptr = NULL;
874 va_res = vaMapBuffer(va_display_, image.buf, &image_ptr);
875 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
876 DCHECK(image_ptr);
878 int ret = 0;
880 base::AutoUnlock auto_unlock(va_lock_);
881 ret = libyuv::I420ToNV12(frame->data(media::VideoFrame::kYPlane),
882 frame->stride(media::VideoFrame::kYPlane),
883 frame->data(media::VideoFrame::kUPlane),
884 frame->stride(media::VideoFrame::kUPlane),
885 frame->data(media::VideoFrame::kVPlane),
886 frame->stride(media::VideoFrame::kVPlane),
887 static_cast<uint8*>(image_ptr) + image.offsets[0],
888 image.pitches[0],
889 static_cast<uint8*>(image_ptr) + image.offsets[1],
890 image.pitches[1],
891 image.width,
892 image.height);
895 va_res = vaUnmapBuffer(va_display_, image.buf);
896 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
898 return ret == 0;
901 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id,
902 VASurfaceID sync_surface_id,
903 uint8* target_ptr,
904 size_t target_size,
905 size_t* coded_data_size) {
906 base::AutoLock auto_lock(va_lock_);
908 VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id);
909 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
911 VACodedBufferSegment* buffer_segment = NULL;
912 va_res = vaMapBuffer(
913 va_display_, buffer_id, reinterpret_cast<void**>(&buffer_segment));
914 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
915 DCHECK(target_ptr);
918 base::AutoUnlock auto_unlock(va_lock_);
919 *coded_data_size = 0;
921 while (buffer_segment) {
922 DCHECK(buffer_segment->buf);
924 if (buffer_segment->size > target_size) {
925 LOG(ERROR) << "Insufficient output buffer size";
926 break;
929 memcpy(target_ptr, buffer_segment->buf, buffer_segment->size);
931 target_ptr += buffer_segment->size;
932 *coded_data_size += buffer_segment->size;
933 target_size -= buffer_segment->size;
935 buffer_segment =
936 reinterpret_cast<VACodedBufferSegment*>(buffer_segment->next);
940 va_res = vaUnmapBuffer(va_display_, buffer_id);
941 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
943 va_res = vaDestroyBuffer(va_display_, buffer_id);
944 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
946 DCHECK(coded_buffers_.erase(buffer_id));
948 return buffer_segment == NULL;
951 bool VaapiWrapper::BlitSurface(VASurfaceID va_surface_id_src,
952 const gfx::Size& src_size,
953 VASurfaceID va_surface_id_dest,
954 const gfx::Size& dest_size) {
955 base::AutoLock auto_lock(va_lock_);
957 // Initialize the post processing engine if not already done.
958 if (va_vpp_buffer_id_ == VA_INVALID_ID) {
959 if (!InitializeVpp_Locked())
960 return false;
963 VAProcPipelineParameterBuffer* pipeline_param;
964 VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_, va_vpp_buffer_id_,
965 reinterpret_cast<void**>(&pipeline_param)),
966 "Couldn't map vpp buffer", false);
968 memset(pipeline_param, 0, sizeof *pipeline_param);
970 VARectangle input_region;
971 input_region.x = input_region.y = 0;
972 input_region.width = src_size.width();
973 input_region.height = src_size.height();
974 pipeline_param->surface_region = &input_region;
975 pipeline_param->surface = va_surface_id_src;
976 pipeline_param->surface_color_standard = VAProcColorStandardNone;
978 VARectangle output_region;
979 output_region.x = output_region.y = 0;
980 output_region.width = dest_size.width();
981 output_region.height = dest_size.height();
982 pipeline_param->output_region = &output_region;
983 pipeline_param->output_background_color = 0xff000000;
984 pipeline_param->output_color_standard = VAProcColorStandardNone;
986 VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_, va_vpp_buffer_id_),
987 "Couldn't unmap vpp buffer", false);
989 VA_SUCCESS_OR_RETURN(
990 vaBeginPicture(va_display_, va_vpp_context_id_, va_surface_id_dest),
991 "Couldn't begin picture", false);
993 VA_SUCCESS_OR_RETURN(
994 vaRenderPicture(va_display_, va_vpp_context_id_, &va_vpp_buffer_id_, 1),
995 "Couldn't render picture", false);
997 VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_, va_vpp_context_id_),
998 "Couldn't end picture", false);
1000 return true;
1003 bool VaapiWrapper::InitializeVpp_Locked() {
1004 va_lock_.AssertAcquired();
1006 VA_SUCCESS_OR_RETURN(
1007 vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0,
1008 &va_vpp_config_id_),
1009 "Couldn't create config", false);
1011 // The size of the picture for the context is irrelevant in the case
1012 // of the VPP, just passing 1x1.
1013 VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_, va_vpp_config_id_, 1, 1, 0,
1014 NULL, 0, &va_vpp_context_id_),
1015 "Couldn't create context", false);
1017 VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_, va_vpp_context_id_,
1018 VAProcPipelineParameterBufferType,
1019 sizeof(VAProcPipelineParameterBuffer), 1,
1020 NULL, &va_vpp_buffer_id_),
1021 "Couldn't create buffer", false);
1023 return true;
1026 void VaapiWrapper::DeinitializeVpp() {
1027 base::AutoLock auto_lock(va_lock_);
1029 if (va_vpp_buffer_id_ != VA_INVALID_ID) {
1030 vaDestroyBuffer(va_display_, va_vpp_buffer_id_);
1031 va_vpp_buffer_id_ = VA_INVALID_ID;
1033 if (va_vpp_context_id_ != VA_INVALID_ID) {
1034 vaDestroyContext(va_display_, va_vpp_context_id_);
1035 va_vpp_context_id_ = VA_INVALID_ID;
1037 if (va_vpp_config_id_ != VA_INVALID_ID) {
1038 vaDestroyConfig(va_display_, va_vpp_config_id_);
1039 va_vpp_config_id_ = VA_INVALID_ID;
1043 // static
1044 bool VaapiWrapper::PostSandboxInitialization() {
1045 StubPathMap paths;
1047 paths[kModuleVa].push_back("libva.so.1");
1049 #if defined(USE_X11)
1050 paths[kModuleVa_x11].push_back("libva-x11.so.1");
1051 #elif defined(USE_OZONE)
1052 paths[kModuleVa_drm].push_back("libva-drm.so.1");
1053 #endif
1055 return InitializeStubs(paths);
1058 VaapiWrapper::LazyProfileInfos::LazyProfileInfos() {
1059 static_assert(arraysize(supported_profiles_) == kCodecModeMax,
1060 "The array size of supported profile is incorrect.");
1061 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
1062 if (!vaapi_wrapper->VaInitialize(base::Bind(&base::DoNothing)))
1063 return;
1064 for (size_t i = 0; i < kCodecModeMax; ++i) {
1065 supported_profiles_[i] =
1066 vaapi_wrapper->GetSupportedProfileInfosForCodecModeInternal(
1067 static_cast<CodecMode>(i));
1071 VaapiWrapper::LazyProfileInfos::~LazyProfileInfos() {
1074 std::vector<VaapiWrapper::ProfileInfo>
1075 VaapiWrapper::LazyProfileInfos::GetSupportedProfileInfosForCodecMode(
1076 CodecMode mode) {
1077 return supported_profiles_[mode];
1080 bool VaapiWrapper::LazyProfileInfos::IsProfileSupported(
1081 CodecMode mode, VAProfile va_profile) {
1082 for (const auto& profile : supported_profiles_[mode]) {
1083 if (profile.va_profile == va_profile)
1084 return true;
1086 return false;
1089 } // namespace content