Pin Chrome's shortcut to the Win10 Start menu on install and OS upgrade.
[chromium-blink-merge.git] / content / common / gpu / media / vaapi_wrapper.cc
blob4e26bcc16d8bf88a079d144195395bd798dcf072
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::VADisplayState>
66 VaapiWrapper::va_display_state_ = LAZY_INSTANCE_INITIALIZER;
68 base::LazyInstance<VaapiWrapper::LazyProfileInfos>
69 VaapiWrapper::profile_infos_ = LAZY_INSTANCE_INITIALIZER;
71 // Config attributes common for both encode and decode.
72 static const VAConfigAttrib kCommonVAConfigAttribs[] = {
73 {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420},
76 // Attributes required for encode.
77 static const VAConfigAttrib kEncodeVAConfigAttribs[] = {
78 {VAConfigAttribRateControl, VA_RC_CBR},
79 {VAConfigAttribEncPackedHeaders,
80 VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE},
83 struct ProfileMap {
84 media::VideoCodecProfile profile;
85 VAProfile va_profile;
88 // A map between VideoCodecProfile and VAProfile.
89 static const ProfileMap kProfileMap[] = {
90 {media::H264PROFILE_BASELINE, VAProfileH264Baseline},
91 {media::H264PROFILE_MAIN, VAProfileH264Main},
92 // TODO(posciak): See if we can/want support other variants of
93 // media::H264PROFILE_HIGH*.
94 {media::H264PROFILE_HIGH, VAProfileH264High},
95 {media::VP8PROFILE_ANY, VAProfileVP8Version0_3},
98 static std::vector<VAConfigAttrib> GetRequiredAttribs(
99 VaapiWrapper::CodecMode mode) {
100 std::vector<VAConfigAttrib> required_attribs;
101 required_attribs.insert(
102 required_attribs.end(),
103 kCommonVAConfigAttribs,
104 kCommonVAConfigAttribs + arraysize(kCommonVAConfigAttribs));
105 if (mode == VaapiWrapper::kEncode) {
106 required_attribs.insert(
107 required_attribs.end(),
108 kEncodeVAConfigAttribs,
109 kEncodeVAConfigAttribs + arraysize(kEncodeVAConfigAttribs));
111 return required_attribs;
114 VASurface::VASurface(VASurfaceID va_surface_id,
115 const gfx::Size& size,
116 const ReleaseCB& release_cb)
117 : va_surface_id_(va_surface_id), size_(size), release_cb_(release_cb) {
118 DCHECK(!release_cb_.is_null());
121 VASurface::~VASurface() {
122 release_cb_.Run(va_surface_id_);
125 VaapiWrapper::VaapiWrapper()
126 : va_display_(NULL),
127 va_config_id_(VA_INVALID_ID),
128 va_context_id_(VA_INVALID_ID),
129 va_vpp_config_id_(VA_INVALID_ID),
130 va_vpp_context_id_(VA_INVALID_ID),
131 va_vpp_buffer_id_(VA_INVALID_ID) {
132 va_lock_ = va_display_state_.Get().va_lock();
135 VaapiWrapper::~VaapiWrapper() {
136 DestroyPendingBuffers();
137 DestroyCodedBuffers();
138 DestroySurfaces();
139 DeinitializeVpp();
140 Deinitialize();
143 // static
144 scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
145 CodecMode mode,
146 VAProfile va_profile,
147 const base::Closure& report_error_to_uma_cb) {
148 if (!profile_infos_.Get().IsProfileSupported(mode, va_profile)) {
149 DVLOG(1) << "Unsupported va_profile: " << va_profile;
150 return nullptr;
153 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper());
154 if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) {
155 if (vaapi_wrapper->Initialize(mode, va_profile))
156 return vaapi_wrapper.Pass();
158 LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " << va_profile;
159 return nullptr;
162 // static
163 scoped_ptr<VaapiWrapper> VaapiWrapper::CreateForVideoCodec(
164 CodecMode mode,
165 media::VideoCodecProfile profile,
166 const base::Closure& report_error_to_uma_cb) {
167 VAProfile va_profile = ProfileToVAProfile(profile, mode);
168 scoped_ptr<VaapiWrapper> vaapi_wrapper =
169 Create(mode, va_profile, report_error_to_uma_cb);
170 return vaapi_wrapper.Pass();
173 // static
174 media::VideoEncodeAccelerator::SupportedProfiles
175 VaapiWrapper::GetSupportedEncodeProfiles() {
176 media::VideoEncodeAccelerator::SupportedProfiles profiles;
177 const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess();
178 if (cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode))
179 return profiles;
181 std::vector<ProfileInfo> encode_profile_infos =
182 profile_infos_.Get().GetSupportedProfileInfosForCodecMode(kEncode);
184 for (size_t i = 0; i < arraysize(kProfileMap); ++i) {
185 VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode);
186 if (va_profile == VAProfileNone)
187 continue;
188 for (const auto& profile_info : encode_profile_infos) {
189 if (profile_info.va_profile == va_profile) {
190 media::VideoEncodeAccelerator::SupportedProfile profile;
191 profile.profile = kProfileMap[i].profile;
192 profile.max_resolution = profile_info.max_resolution;
193 profile.max_framerate_numerator = kMaxEncoderFramerate;
194 profile.max_framerate_denominator = 1;
195 profiles.push_back(profile);
196 break;
200 return profiles;
203 // static
204 media::VideoDecodeAccelerator::SupportedProfiles
205 VaapiWrapper::GetSupportedDecodeProfiles() {
206 media::VideoDecodeAccelerator::SupportedProfiles profiles;
207 std::vector<ProfileInfo> decode_profile_infos =
208 profile_infos_.Get().GetSupportedProfileInfosForCodecMode(kDecode);
210 for (size_t i = 0; i < arraysize(kProfileMap); ++i) {
211 VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kDecode);
212 if (va_profile == VAProfileNone)
213 continue;
214 for (const auto& profile_info : decode_profile_infos) {
215 if (profile_info.va_profile == va_profile) {
216 media::VideoDecodeAccelerator::SupportedProfile profile;
217 profile.profile = kProfileMap[i].profile;
218 profile.max_resolution = profile_info.max_resolution;
219 profile.min_resolution.SetSize(16, 16);
220 profiles.push_back(profile);
221 break;
225 return profiles;
228 // static
229 bool VaapiWrapper::IsJpegDecodeSupported() {
230 return profile_infos_.Get().IsProfileSupported(kDecode,
231 VAProfileJPEGBaseline);
234 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
235 base::AutoLock auto_lock(*va_lock_);
236 VADisplayAttribute item = {VADisplayAttribRenderMode,
237 1, // At least support '_LOCAL_OVERLAY'.
238 -1, // The maximum possible support 'ALL'.
239 VA_RENDER_MODE_LOCAL_GPU,
240 VA_DISPLAY_ATTRIB_SETTABLE};
242 VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1);
243 if (va_res != VA_STATUS_SUCCESS)
244 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
247 // static
248 VAProfile VaapiWrapper::ProfileToVAProfile(
249 media::VideoCodecProfile profile, CodecMode mode) {
250 VAProfile va_profile = VAProfileNone;
251 for (size_t i = 0; i < arraysize(kProfileMap); ++i) {
252 if (kProfileMap[i].profile == profile) {
253 va_profile = kProfileMap[i].va_profile;
254 break;
257 if (!profile_infos_.Get().IsProfileSupported(mode, va_profile) &&
258 va_profile == VAProfileH264Baseline) {
259 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips
260 // the information whether the profile is constrained or not, so we have no
261 // way to know here. Try for baseline first, but if it is not supported,
262 // try constrained baseline and hope this is what it actually is
263 // (which in practice is true for a great majority of cases).
264 if (profile_infos_.Get().IsProfileSupported(
265 mode, VAProfileH264ConstrainedBaseline)) {
266 va_profile = VAProfileH264ConstrainedBaseline;
267 DVLOG(1) << "Fall back to constrained baseline profile.";
270 return va_profile;
273 std::vector<VaapiWrapper::ProfileInfo>
274 VaapiWrapper::GetSupportedProfileInfosForCodecModeInternal(CodecMode mode) {
275 std::vector<ProfileInfo> supported_profile_infos;
276 std::vector<VAProfile> va_profiles;
277 if (!GetSupportedVaProfiles(&va_profiles))
278 return supported_profile_infos;
280 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode);
281 VAEntrypoint entrypoint =
282 (mode == kEncode ? VAEntrypointEncSlice: VAEntrypointVLD);
284 base::AutoLock auto_lock(*va_lock_);
285 for (const auto& va_profile : va_profiles) {
286 if (!IsEntrypointSupported_Locked(va_profile, entrypoint))
287 continue;
288 if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs))
289 continue;
290 ProfileInfo profile_info;
291 if (!GetMaxResolution_Locked(va_profile,
292 entrypoint,
293 required_attribs,
294 &profile_info.max_resolution)) {
295 LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile
296 << " and entrypoint " << entrypoint;
297 continue;
299 profile_info.va_profile = va_profile;
300 supported_profile_infos.push_back(profile_info);
302 return supported_profile_infos;
305 bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) {
306 static bool vaapi_functions_initialized = PostSandboxInitialization();
307 if (!vaapi_functions_initialized) {
308 bool running_on_chromeos = false;
309 #if defined(OS_CHROMEOS)
310 // When chrome runs on linux with chromeos=1, do not log error message
311 // without VAAPI libraries.
312 running_on_chromeos = base::SysInfo::IsRunningOnChromeOS();
313 #endif
314 static const char kErrorMsg[] = "Failed to initialize VAAPI libs";
315 if (running_on_chromeos)
316 LOG(ERROR) << kErrorMsg;
317 else
318 DVLOG(1) << kErrorMsg;
319 return false;
322 report_error_to_uma_cb_ = report_error_to_uma_cb;
324 base::AutoLock auto_lock(*va_lock_);
326 VADisplayState* va_display_state = &va_display_state_.Get();
327 if (!va_display_state) {
328 LOG(ERROR) << "Failed to allocate VA display state";
329 return false;
332 VAStatus va_res = VA_STATUS_SUCCESS;
333 if (!va_display_state->Initialize(&va_res)) {
334 VA_LOG_ON_ERROR(va_res, "vaInitialize failed");
335 return false;
338 va_display_ = va_display_state->va_display();
339 return true;
342 bool VaapiWrapper::GetSupportedVaProfiles(std::vector<VAProfile>* profiles) {
343 base::AutoLock auto_lock(*va_lock_);
344 // Query the driver for supported profiles.
345 int max_profiles = vaMaxNumProfiles(va_display_);
346 std::vector<VAProfile> supported_profiles(
347 base::checked_cast<size_t>(max_profiles));
349 int num_supported_profiles;
350 VAStatus va_res = vaQueryConfigProfiles(
351 va_display_, &supported_profiles[0], &num_supported_profiles);
352 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false);
353 if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) {
354 LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_supported_profiles;
355 return false;
358 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles));
359 *profiles = supported_profiles;
360 return true;
363 bool VaapiWrapper::IsEntrypointSupported_Locked(VAProfile va_profile,
364 VAEntrypoint entrypoint) {
365 va_lock_->AssertAcquired();
366 // Query the driver for supported entrypoints.
367 int max_entrypoints = vaMaxNumEntrypoints(va_display_);
368 std::vector<VAEntrypoint> supported_entrypoints(
369 base::checked_cast<size_t>(max_entrypoints));
371 int num_supported_entrypoints;
372 VAStatus va_res = vaQueryConfigEntrypoints(va_display_,
373 va_profile,
374 &supported_entrypoints[0],
375 &num_supported_entrypoints);
376 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false);
377 if (num_supported_entrypoints < 0 ||
378 num_supported_entrypoints > max_entrypoints) {
379 LOG(ERROR) << "vaQueryConfigEntrypoints returned: "
380 << num_supported_entrypoints;
381 return false;
384 if (std::find(supported_entrypoints.begin(),
385 supported_entrypoints.end(),
386 entrypoint) == supported_entrypoints.end()) {
387 DVLOG(1) << "Unsupported entrypoint";
388 return false;
390 return true;
393 bool VaapiWrapper::AreAttribsSupported_Locked(
394 VAProfile va_profile,
395 VAEntrypoint entrypoint,
396 const std::vector<VAConfigAttrib>& required_attribs) {
397 va_lock_->AssertAcquired();
398 // Query the driver for required attributes.
399 std::vector<VAConfigAttrib> attribs = required_attribs;
400 for (size_t i = 0; i < required_attribs.size(); ++i)
401 attribs[i].value = 0;
403 VAStatus va_res = vaGetConfigAttributes(
404 va_display_, va_profile, entrypoint, &attribs[0], attribs.size());
405 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false);
407 for (size_t i = 0; i < required_attribs.size(); ++i) {
408 if (attribs[i].type != required_attribs[i].type ||
409 (attribs[i].value & required_attribs[i].value) !=
410 required_attribs[i].value) {
411 DVLOG(1) << "Unsupported value " << required_attribs[i].value
412 << " for attribute type " << required_attribs[i].type;
413 return false;
416 return true;
419 bool VaapiWrapper::GetMaxResolution_Locked(
420 VAProfile va_profile,
421 VAEntrypoint entrypoint,
422 std::vector<VAConfigAttrib>& required_attribs,
423 gfx::Size* resolution) {
424 va_lock_->AssertAcquired();
425 VAConfigID va_config_id;
426 VAStatus va_res = vaCreateConfig(
427 va_display_,
428 va_profile,
429 entrypoint,
430 &required_attribs[0],
431 required_attribs.size(),
432 &va_config_id);
433 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
435 // Calls vaQuerySurfaceAttributes twice. The first time is to get the number
436 // of attributes to prepare the space and the second time is to get all
437 // attributes.
438 unsigned int num_attribs;
439 va_res = vaQuerySurfaceAttributes(
440 va_display_, va_config_id, nullptr, &num_attribs);
441 VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false);
442 if (!num_attribs)
443 return false;
445 std::vector<VASurfaceAttrib> attrib_list(
446 base::checked_cast<size_t>(num_attribs));
448 va_res = vaQuerySurfaceAttributes(
449 va_display_, va_config_id, &attrib_list[0], &num_attribs);
450 VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false);
452 resolution->SetSize(0, 0);
453 for (const auto& attrib : attrib_list) {
454 if (attrib.type == VASurfaceAttribMaxWidth)
455 resolution->set_width(attrib.value.value.i);
456 else if (attrib.type == VASurfaceAttribMaxHeight)
457 resolution->set_height(attrib.value.value.i);
459 if (resolution->IsEmpty()) {
460 LOG(ERROR) << "Codec resolution " << resolution->ToString()
461 << " cannot be zero.";
462 return false;
464 return true;
467 bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) {
468 TryToSetVADisplayAttributeToLocalGPU();
470 VAEntrypoint entrypoint =
471 (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD);
472 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode);
473 base::AutoLock auto_lock(*va_lock_);
474 VAStatus va_res = vaCreateConfig(va_display_,
475 va_profile,
476 entrypoint,
477 &required_attribs[0],
478 required_attribs.size(),
479 &va_config_id_);
480 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
482 return true;
485 void VaapiWrapper::Deinitialize() {
486 base::AutoLock auto_lock(*va_lock_);
488 if (va_config_id_ != VA_INVALID_ID) {
489 VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_);
490 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed");
493 VADisplayState* va_display_state = &va_display_state_.Get();
494 if (va_display_state) {
495 VAStatus va_res = VA_STATUS_SUCCESS;
496 va_display_state->Deinitialize(&va_res);
497 VA_LOG_ON_ERROR(va_res, "vaTerminate failed");
500 va_config_id_ = VA_INVALID_ID;
501 va_display_ = NULL;
504 bool VaapiWrapper::CreateSurfaces(unsigned int va_format,
505 const gfx::Size& size,
506 size_t num_surfaces,
507 std::vector<VASurfaceID>* va_surfaces) {
508 base::AutoLock auto_lock(*va_lock_);
509 DVLOG(2) << "Creating " << num_surfaces << " surfaces";
511 DCHECK(va_surfaces->empty());
512 DCHECK(va_surface_ids_.empty());
513 va_surface_ids_.resize(num_surfaces);
515 // Allocate surfaces in driver.
516 VAStatus va_res =
517 vaCreateSurfaces(va_display_, va_format, size.width(), size.height(),
518 &va_surface_ids_[0], va_surface_ids_.size(), NULL, 0);
520 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
521 if (va_res != VA_STATUS_SUCCESS) {
522 va_surface_ids_.clear();
523 return false;
526 // And create a context associated with them.
527 va_res = vaCreateContext(va_display_, va_config_id_,
528 size.width(), size.height(), VA_PROGRESSIVE,
529 &va_surface_ids_[0], va_surface_ids_.size(),
530 &va_context_id_);
532 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
533 if (va_res != VA_STATUS_SUCCESS) {
534 DestroySurfaces();
535 return false;
538 *va_surfaces = va_surface_ids_;
539 return true;
542 void VaapiWrapper::DestroySurfaces() {
543 base::AutoLock auto_lock(*va_lock_);
544 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces";
546 if (va_context_id_ != VA_INVALID_ID) {
547 VAStatus va_res = vaDestroyContext(va_display_, va_context_id_);
548 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed");
551 if (!va_surface_ids_.empty()) {
552 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0],
553 va_surface_ids_.size());
554 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed");
557 va_surface_ids_.clear();
558 va_context_id_ = VA_INVALID_ID;
561 scoped_refptr<VASurface> VaapiWrapper::CreateUnownedSurface(
562 unsigned int va_format,
563 const gfx::Size& size,
564 const std::vector<VASurfaceAttrib>& va_attribs) {
565 base::AutoLock auto_lock(*va_lock_);
567 std::vector<VASurfaceAttrib> attribs(va_attribs);
568 VASurfaceID va_surface_id;
569 VAStatus va_res =
570 vaCreateSurfaces(va_display_, va_format, size.width(), size.height(),
571 &va_surface_id, 1, &attribs[0], attribs.size());
573 scoped_refptr<VASurface> va_surface;
574 VA_SUCCESS_OR_RETURN(va_res, "Failed to create unowned VASurface",
575 va_surface);
577 // This is safe to use Unretained() here, because the VDA takes care
578 // of the destruction order. All the surfaces will be destroyed
579 // before VaapiWrapper.
580 va_surface = new VASurface(
581 va_surface_id, size,
582 base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this)));
584 return va_surface;
587 void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id) {
588 base::AutoLock auto_lock(*va_lock_);
590 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1);
591 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces on surface failed");
594 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type,
595 size_t size,
596 void* buffer) {
597 base::AutoLock auto_lock(*va_lock_);
599 VABufferID buffer_id;
600 VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_,
601 va_buffer_type, size,
602 1, buffer, &buffer_id);
603 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
605 switch (va_buffer_type) {
606 case VASliceParameterBufferType:
607 case VASliceDataBufferType:
608 case VAEncSliceParameterBufferType:
609 pending_slice_bufs_.push_back(buffer_id);
610 break;
612 default:
613 pending_va_bufs_.push_back(buffer_id);
614 break;
617 return true;
620 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
621 VAEncMiscParameterType misc_param_type,
622 size_t size,
623 void* buffer) {
624 base::AutoLock auto_lock(*va_lock_);
626 VABufferID buffer_id;
627 VAStatus va_res = vaCreateBuffer(va_display_,
628 va_context_id_,
629 VAEncMiscParameterBufferType,
630 sizeof(VAEncMiscParameterBuffer) + size,
632 NULL,
633 &buffer_id);
634 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false);
636 void* data_ptr = NULL;
637 va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr);
638 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
639 if (va_res != VA_STATUS_SUCCESS) {
640 vaDestroyBuffer(va_display_, buffer_id);
641 return false;
644 DCHECK(data_ptr);
646 VAEncMiscParameterBuffer* misc_param =
647 reinterpret_cast<VAEncMiscParameterBuffer*>(data_ptr);
648 misc_param->type = misc_param_type;
649 memcpy(misc_param->data, buffer, size);
650 va_res = vaUnmapBuffer(va_display_, buffer_id);
651 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
653 pending_va_bufs_.push_back(buffer_id);
654 return true;
657 void VaapiWrapper::DestroyPendingBuffers() {
658 base::AutoLock auto_lock(*va_lock_);
660 for (const auto& pending_va_buf : pending_va_bufs_) {
661 VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf);
662 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
665 for (const auto& pending_slice_buf : pending_slice_bufs_) {
666 VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf);
667 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
670 pending_va_bufs_.clear();
671 pending_slice_bufs_.clear();
674 bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) {
675 base::AutoLock auto_lock(*va_lock_);
676 VAStatus va_res = vaCreateBuffer(va_display_,
677 va_context_id_,
678 VAEncCodedBufferType,
679 size,
681 NULL,
682 buffer_id);
683 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false);
685 DCHECK(coded_buffers_.insert(*buffer_id).second);
686 return true;
689 void VaapiWrapper::DestroyCodedBuffers() {
690 base::AutoLock auto_lock(*va_lock_);
692 for (std::set<VABufferID>::const_iterator iter = coded_buffers_.begin();
693 iter != coded_buffers_.end();
694 ++iter) {
695 VAStatus va_res = vaDestroyBuffer(va_display_, *iter);
696 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
699 coded_buffers_.clear();
702 bool VaapiWrapper::Execute(VASurfaceID va_surface_id) {
703 base::AutoLock auto_lock(*va_lock_);
705 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size();
706 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size();
707 DVLOG(4) << "Target VA surface " << va_surface_id;
709 // Get ready to execute for given surface.
710 VAStatus va_res = vaBeginPicture(va_display_, va_context_id_,
711 va_surface_id);
712 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false);
714 if (pending_va_bufs_.size() > 0) {
715 // Commit parameter and slice buffers.
716 va_res = vaRenderPicture(va_display_,
717 va_context_id_,
718 &pending_va_bufs_[0],
719 pending_va_bufs_.size());
720 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false);
723 if (pending_slice_bufs_.size() > 0) {
724 va_res = vaRenderPicture(va_display_,
725 va_context_id_,
726 &pending_slice_bufs_[0],
727 pending_slice_bufs_.size());
728 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false);
731 // Instruct HW codec to start processing committed buffers.
732 // Does not block and the job is not finished after this returns.
733 va_res = vaEndPicture(va_display_, va_context_id_);
734 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false);
736 return true;
739 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
740 bool result = Execute(va_surface_id);
741 DestroyPendingBuffers();
742 return result;
745 #if defined(USE_X11)
746 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
747 Pixmap x_pixmap,
748 gfx::Size dest_size) {
749 base::AutoLock auto_lock(*va_lock_);
751 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
752 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
754 // Put the data into an X Pixmap.
755 va_res = vaPutSurface(va_display_,
756 va_surface_id,
757 x_pixmap,
758 0, 0, dest_size.width(), dest_size.height(),
759 0, 0, dest_size.width(), dest_size.height(),
760 NULL, 0, 0);
761 VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false);
762 return true;
764 #endif // USE_X11
766 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id,
767 VAImage* image,
768 void** mem) {
769 base::AutoLock auto_lock(*va_lock_);
771 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
772 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
774 // Derive a VAImage from the VASurface
775 va_res = vaDeriveImage(va_display_, va_surface_id, image);
776 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed");
777 if (va_res != VA_STATUS_SUCCESS)
778 return false;
780 // Map the VAImage into memory
781 va_res = vaMapBuffer(va_display_, image->buf, mem);
782 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
783 if (va_res == VA_STATUS_SUCCESS)
784 return true;
786 va_res = vaDestroyImage(va_display_, image->image_id);
787 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
789 return false;
792 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id,
793 VAImageFormat* format,
794 const gfx::Size& size,
795 VAImage* image,
796 void** mem) {
797 base::AutoLock auto_lock(*va_lock_);
799 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id);
800 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
802 va_res =
803 vaCreateImage(va_display_, format, size.width(), size.height(), image);
804 VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false);
806 va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(),
807 size.height(), image->image_id);
808 VA_LOG_ON_ERROR(va_res, "vaGetImage failed");
810 if (va_res == VA_STATUS_SUCCESS) {
811 // Map the VAImage into memory
812 va_res = vaMapBuffer(va_display_, image->buf, mem);
813 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed");
816 if (va_res != VA_STATUS_SUCCESS) {
817 va_res = vaDestroyImage(va_display_, image->image_id);
818 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
819 return false;
822 return true;
825 void VaapiWrapper::ReturnVaImage(VAImage* image) {
826 base::AutoLock auto_lock(*va_lock_);
828 VAStatus va_res = vaUnmapBuffer(va_display_, image->buf);
829 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
831 va_res = vaDestroyImage(va_display_, image->image_id);
832 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
835 static void DestroyVAImage(VADisplay va_display, VAImage image) {
836 if (image.image_id != VA_INVALID_ID)
837 vaDestroyImage(va_display, image.image_id);
840 bool VaapiWrapper::UploadVideoFrameToSurface(
841 const scoped_refptr<media::VideoFrame>& frame,
842 VASurfaceID va_surface_id) {
843 base::AutoLock auto_lock(*va_lock_);
845 VAImage image;
846 VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image);
847 VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false);
848 base::ScopedClosureRunner vaimage_deleter(
849 base::Bind(&DestroyVAImage, va_display_, image));
851 if (image.format.fourcc != VA_FOURCC_NV12) {
852 LOG(ERROR) << "Unsupported image format: " << image.format.fourcc;
853 return false;
856 if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) {
857 LOG(ERROR) << "Buffer too small to fit the frame.";
858 return false;
861 void* image_ptr = NULL;
862 va_res = vaMapBuffer(va_display_, image.buf, &image_ptr);
863 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
864 DCHECK(image_ptr);
866 int ret = 0;
868 base::AutoUnlock auto_unlock(*va_lock_);
869 ret = libyuv::I420ToNV12(frame->data(media::VideoFrame::kYPlane),
870 frame->stride(media::VideoFrame::kYPlane),
871 frame->data(media::VideoFrame::kUPlane),
872 frame->stride(media::VideoFrame::kUPlane),
873 frame->data(media::VideoFrame::kVPlane),
874 frame->stride(media::VideoFrame::kVPlane),
875 static_cast<uint8*>(image_ptr) + image.offsets[0],
876 image.pitches[0],
877 static_cast<uint8*>(image_ptr) + image.offsets[1],
878 image.pitches[1],
879 image.width,
880 image.height);
883 va_res = vaUnmapBuffer(va_display_, image.buf);
884 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
886 return ret == 0;
889 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id,
890 VASurfaceID sync_surface_id,
891 uint8* target_ptr,
892 size_t target_size,
893 size_t* coded_data_size) {
894 base::AutoLock auto_lock(*va_lock_);
896 VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id);
897 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false);
899 VACodedBufferSegment* buffer_segment = NULL;
900 va_res = vaMapBuffer(
901 va_display_, buffer_id, reinterpret_cast<void**>(&buffer_segment));
902 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false);
903 DCHECK(target_ptr);
906 base::AutoUnlock auto_unlock(*va_lock_);
907 *coded_data_size = 0;
909 while (buffer_segment) {
910 DCHECK(buffer_segment->buf);
912 if (buffer_segment->size > target_size) {
913 LOG(ERROR) << "Insufficient output buffer size";
914 break;
917 memcpy(target_ptr, buffer_segment->buf, buffer_segment->size);
919 target_ptr += buffer_segment->size;
920 *coded_data_size += buffer_segment->size;
921 target_size -= buffer_segment->size;
923 buffer_segment =
924 reinterpret_cast<VACodedBufferSegment*>(buffer_segment->next);
928 va_res = vaUnmapBuffer(va_display_, buffer_id);
929 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
931 va_res = vaDestroyBuffer(va_display_, buffer_id);
932 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed");
934 DCHECK(coded_buffers_.erase(buffer_id));
936 return buffer_segment == NULL;
939 bool VaapiWrapper::BlitSurface(VASurfaceID va_surface_id_src,
940 const gfx::Size& src_size,
941 VASurfaceID va_surface_id_dest,
942 const gfx::Size& dest_size) {
943 base::AutoLock auto_lock(*va_lock_);
945 // Initialize the post processing engine if not already done.
946 if (va_vpp_buffer_id_ == VA_INVALID_ID) {
947 if (!InitializeVpp_Locked())
948 return false;
951 VAProcPipelineParameterBuffer* pipeline_param;
952 VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_, va_vpp_buffer_id_,
953 reinterpret_cast<void**>(&pipeline_param)),
954 "Couldn't map vpp buffer", false);
956 memset(pipeline_param, 0, sizeof *pipeline_param);
958 VARectangle input_region;
959 input_region.x = input_region.y = 0;
960 input_region.width = src_size.width();
961 input_region.height = src_size.height();
962 pipeline_param->surface_region = &input_region;
963 pipeline_param->surface = va_surface_id_src;
964 pipeline_param->surface_color_standard = VAProcColorStandardNone;
966 VARectangle output_region;
967 output_region.x = output_region.y = 0;
968 output_region.width = dest_size.width();
969 output_region.height = dest_size.height();
970 pipeline_param->output_region = &output_region;
971 pipeline_param->output_background_color = 0xff000000;
972 pipeline_param->output_color_standard = VAProcColorStandardNone;
974 VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_, va_vpp_buffer_id_),
975 "Couldn't unmap vpp buffer", false);
977 VA_SUCCESS_OR_RETURN(
978 vaBeginPicture(va_display_, va_vpp_context_id_, va_surface_id_dest),
979 "Couldn't begin picture", false);
981 VA_SUCCESS_OR_RETURN(
982 vaRenderPicture(va_display_, va_vpp_context_id_, &va_vpp_buffer_id_, 1),
983 "Couldn't render picture", false);
985 VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_, va_vpp_context_id_),
986 "Couldn't end picture", false);
988 return true;
991 bool VaapiWrapper::InitializeVpp_Locked() {
992 va_lock_->AssertAcquired();
994 VA_SUCCESS_OR_RETURN(
995 vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0,
996 &va_vpp_config_id_),
997 "Couldn't create config", false);
999 // The size of the picture for the context is irrelevant in the case
1000 // of the VPP, just passing 1x1.
1001 VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_, va_vpp_config_id_, 1, 1, 0,
1002 NULL, 0, &va_vpp_context_id_),
1003 "Couldn't create context", false);
1005 VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_, va_vpp_context_id_,
1006 VAProcPipelineParameterBufferType,
1007 sizeof(VAProcPipelineParameterBuffer), 1,
1008 NULL, &va_vpp_buffer_id_),
1009 "Couldn't create buffer", false);
1011 return true;
1014 void VaapiWrapper::DeinitializeVpp() {
1015 base::AutoLock auto_lock(*va_lock_);
1017 if (va_vpp_buffer_id_ != VA_INVALID_ID) {
1018 vaDestroyBuffer(va_display_, va_vpp_buffer_id_);
1019 va_vpp_buffer_id_ = VA_INVALID_ID;
1021 if (va_vpp_context_id_ != VA_INVALID_ID) {
1022 vaDestroyContext(va_display_, va_vpp_context_id_);
1023 va_vpp_context_id_ = VA_INVALID_ID;
1025 if (va_vpp_config_id_ != VA_INVALID_ID) {
1026 vaDestroyConfig(va_display_, va_vpp_config_id_);
1027 va_vpp_config_id_ = VA_INVALID_ID;
1031 // static
1032 void VaapiWrapper::PreSandboxInitialization() {
1033 #if defined(USE_OZONE)
1034 const char* kDriRenderNode0Path = "/dev/dri/renderD128";
1035 base::File drm_file = base::File(
1036 base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path),
1037 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE);
1038 if (drm_file.IsValid())
1039 va_display_state_.Get().SetDrmFd(drm_file.GetPlatformFile());
1040 #endif
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 VaapiWrapper::VADisplayState::VADisplayState()
1090 : refcount_(0),
1091 va_display_(nullptr),
1092 major_version_(-1),
1093 minor_version_(-1),
1094 va_initialized_(false) {}
1096 VaapiWrapper::VADisplayState::~VADisplayState() {}
1098 bool VaapiWrapper::VADisplayState::Initialize(VAStatus* status) {
1099 va_lock_.AssertAcquired();
1100 if (refcount_++ == 0) {
1101 #if defined(USE_X11)
1102 va_display_ = vaGetDisplay(gfx::GetXDisplay());
1103 #elif defined(USE_OZONE)
1104 va_display_ = vaGetDisplayDRM(drm_fd_.get());
1105 #endif // USE_X11
1107 if (!vaDisplayIsValid(va_display_)) {
1108 LOG(ERROR) << "Could not get a valid VA display";
1109 return false;
1112 *status = vaInitialize(va_display_, &major_version_, &minor_version_);
1113 if (*status != VA_STATUS_SUCCESS)
1114 return false;
1116 va_initialized_ = true;
1117 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_;
1120 if (VAAPIVersionLessThan(0, 34)) {
1121 LOG(ERROR) << "VAAPI version < 0.34 is not supported.";
1122 return false;
1124 return true;
1127 void VaapiWrapper::VADisplayState::Deinitialize(VAStatus* status) {
1128 va_lock_.AssertAcquired();
1129 if (--refcount_ > 0)
1130 return;
1132 // Must check if vaInitialize completed successfully, to work around a bug in
1133 // libva. The bug was fixed upstream:
1134 // http://lists.freedesktop.org/archives/libva/2013-July/001807.html
1135 // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once
1136 // the fix has rolled out sufficiently.
1137 if (va_initialized_ && va_display_) {
1138 *status = vaTerminate(va_display_);
1140 va_initialized_ = false;
1141 va_display_ = nullptr;
1144 #if defined(USE_OZONE)
1145 void VaapiWrapper::VADisplayState::SetDrmFd(base::PlatformFile fd) {
1146 drm_fd_.reset(HANDLE_EINTR(dup(fd)));
1148 #endif // USE_OZONE
1150 bool VaapiWrapper::VADisplayState::VAAPIVersionLessThan(int major, int minor) {
1151 return (major_version_ < major) ||
1152 (major_version_ == major && minor_version_ < minor);
1155 } // namespace content