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"
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"
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"
29 using content_common_gpu_media::kModuleVa
;
31 using content_common_gpu_media::kModuleVa_x11
;
32 #elif defined(USE_OZONE)
33 using content_common_gpu_media::kModuleVa_drm
;
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) \
40 LOG(ERROR) << err_msg \
41 << " VA error: " << vaErrorStr(va_error); \
42 report_error_to_uma_cb_.Run(); \
45 #define VA_LOG_ON_ERROR(va_error, err_msg) \
47 if ((va_error) != VA_STATUS_SUCCESS) \
48 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
51 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \
53 if ((va_error) != VA_STATUS_SUCCESS) { \
54 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
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
},
84 media::VideoCodecProfile 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()
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();
144 scoped_ptr
<VaapiWrapper
> VaapiWrapper::Create(
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
;
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
;
163 scoped_ptr
<VaapiWrapper
> VaapiWrapper::CreateForVideoCodec(
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();
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
))
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
)
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
);
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
)
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
);
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.";
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
;
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.";
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
))
288 if (!AreAttribsSupported_Locked(va_profile
, entrypoint
, required_attribs
))
290 ProfileInfo profile_info
;
291 if (!GetMaxResolution_Locked(va_profile
,
294 &profile_info
.max_resolution
)) {
295 LOG(ERROR
) << "GetMaxResolution failed for va_profile " << va_profile
296 << " and entrypoint " << entrypoint
;
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();
314 static const char kErrorMsg
[] = "Failed to initialize VAAPI libs";
315 if (running_on_chromeos
)
316 LOG(ERROR
) << kErrorMsg
;
318 DVLOG(1) << kErrorMsg
;
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";
332 VAStatus va_res
= VA_STATUS_SUCCESS
;
333 if (!va_display_state
->Initialize(&va_res
)) {
334 VA_LOG_ON_ERROR(va_res
, "vaInitialize failed");
338 va_display_
= va_display_state
->va_display();
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
;
358 supported_profiles
.resize(base::checked_cast
<size_t>(num_supported_profiles
));
359 *profiles
= supported_profiles
;
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_
,
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
;
384 if (std::find(supported_entrypoints
.begin(),
385 supported_entrypoints
.end(),
386 entrypoint
) == supported_entrypoints
.end()) {
387 DVLOG(1) << "Unsupported entrypoint";
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
;
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(
430 &required_attribs
[0],
431 required_attribs
.size(),
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
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);
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.";
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_
,
477 &required_attribs
[0],
478 required_attribs
.size(),
480 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateConfig failed", false);
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
;
504 bool VaapiWrapper::CreateSurfaces(unsigned int va_format
,
505 const gfx::Size
& size
,
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.
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();
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(),
532 VA_LOG_ON_ERROR(va_res
, "vaCreateContext failed");
533 if (va_res
!= VA_STATUS_SUCCESS
) {
538 *va_surfaces
= va_surface_ids_
;
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
;
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",
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(
582 base::Bind(&VaapiWrapper::DestroyUnownedSurface
, base::Unretained(this)));
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
,
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
);
613 pending_va_bufs_
.push_back(buffer_id
);
620 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
621 VAEncMiscParameterType misc_param_type
,
624 base::AutoLock
auto_lock(*va_lock_
);
626 VABufferID buffer_id
;
627 VAStatus va_res
= vaCreateBuffer(va_display_
,
629 VAEncMiscParameterBufferType
,
630 sizeof(VAEncMiscParameterBuffer
) + size
,
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
);
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
);
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_
,
678 VAEncCodedBufferType
,
683 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a coded buffer", false);
685 const auto is_new_entry
= coded_buffers_
.insert(*buffer_id
).second
;
686 DCHECK(is_new_entry
);
690 void VaapiWrapper::DestroyCodedBuffers() {
691 base::AutoLock
auto_lock(*va_lock_
);
693 for (std::set
<VABufferID
>::const_iterator iter
= coded_buffers_
.begin();
694 iter
!= coded_buffers_
.end();
696 VAStatus va_res
= vaDestroyBuffer(va_display_
, *iter
);
697 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
700 coded_buffers_
.clear();
703 bool VaapiWrapper::Execute(VASurfaceID va_surface_id
) {
704 base::AutoLock
auto_lock(*va_lock_
);
706 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_
.size();
707 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_
.size();
708 DVLOG(4) << "Target VA surface " << va_surface_id
;
710 // Get ready to execute for given surface.
711 VAStatus va_res
= vaBeginPicture(va_display_
, va_context_id_
,
713 VA_SUCCESS_OR_RETURN(va_res
, "vaBeginPicture failed", false);
715 if (pending_va_bufs_
.size() > 0) {
716 // Commit parameter and slice buffers.
717 va_res
= vaRenderPicture(va_display_
,
719 &pending_va_bufs_
[0],
720 pending_va_bufs_
.size());
721 VA_SUCCESS_OR_RETURN(va_res
, "vaRenderPicture for va_bufs failed", false);
724 if (pending_slice_bufs_
.size() > 0) {
725 va_res
= vaRenderPicture(va_display_
,
727 &pending_slice_bufs_
[0],
728 pending_slice_bufs_
.size());
729 VA_SUCCESS_OR_RETURN(va_res
, "vaRenderPicture for slices failed", false);
732 // Instruct HW codec to start processing committed buffers.
733 // Does not block and the job is not finished after this returns.
734 va_res
= vaEndPicture(va_display_
, va_context_id_
);
735 VA_SUCCESS_OR_RETURN(va_res
, "vaEndPicture failed", false);
740 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id
) {
741 bool result
= Execute(va_surface_id
);
742 DestroyPendingBuffers();
747 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id
,
749 gfx::Size dest_size
) {
750 base::AutoLock
auto_lock(*va_lock_
);
752 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
753 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
755 // Put the data into an X Pixmap.
756 va_res
= vaPutSurface(va_display_
,
759 0, 0, dest_size
.width(), dest_size
.height(),
760 0, 0, dest_size
.width(), dest_size
.height(),
762 VA_SUCCESS_OR_RETURN(va_res
, "Failed putting surface to pixmap", false);
767 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id
,
770 base::AutoLock
auto_lock(*va_lock_
);
772 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
773 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
775 // Derive a VAImage from the VASurface
776 va_res
= vaDeriveImage(va_display_
, va_surface_id
, image
);
777 VA_LOG_ON_ERROR(va_res
, "vaDeriveImage failed");
778 if (va_res
!= VA_STATUS_SUCCESS
)
781 // Map the VAImage into memory
782 va_res
= vaMapBuffer(va_display_
, image
->buf
, mem
);
783 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
784 if (va_res
== VA_STATUS_SUCCESS
)
787 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
788 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
793 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id
,
794 VAImageFormat
* format
,
795 const gfx::Size
& size
,
798 base::AutoLock
auto_lock(*va_lock_
);
800 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
801 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
804 vaCreateImage(va_display_
, format
, size
.width(), size
.height(), image
);
805 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateImage failed", false);
807 va_res
= vaGetImage(va_display_
, va_surface_id
, 0, 0, size
.width(),
808 size
.height(), image
->image_id
);
809 VA_LOG_ON_ERROR(va_res
, "vaGetImage failed");
811 if (va_res
== VA_STATUS_SUCCESS
) {
812 // Map the VAImage into memory
813 va_res
= vaMapBuffer(va_display_
, image
->buf
, mem
);
814 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
817 if (va_res
!= VA_STATUS_SUCCESS
) {
818 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
819 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
826 void VaapiWrapper::ReturnVaImage(VAImage
* image
) {
827 base::AutoLock
auto_lock(*va_lock_
);
829 VAStatus va_res
= vaUnmapBuffer(va_display_
, image
->buf
);
830 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
832 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
833 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
836 static void DestroyVAImage(VADisplay va_display
, VAImage image
) {
837 if (image
.image_id
!= VA_INVALID_ID
)
838 vaDestroyImage(va_display
, image
.image_id
);
841 bool VaapiWrapper::UploadVideoFrameToSurface(
842 const scoped_refptr
<media::VideoFrame
>& frame
,
843 VASurfaceID va_surface_id
) {
844 base::AutoLock
auto_lock(*va_lock_
);
847 VAStatus va_res
= vaDeriveImage(va_display_
, va_surface_id
, &image
);
848 VA_SUCCESS_OR_RETURN(va_res
, "vaDeriveImage failed", false);
849 base::ScopedClosureRunner
vaimage_deleter(
850 base::Bind(&DestroyVAImage
, va_display_
, image
));
852 if (image
.format
.fourcc
!= VA_FOURCC_NV12
) {
853 LOG(ERROR
) << "Unsupported image format: " << image
.format
.fourcc
;
857 if (gfx::Rect(image
.width
, image
.height
) < gfx::Rect(frame
->coded_size())) {
858 LOG(ERROR
) << "Buffer too small to fit the frame.";
862 void* image_ptr
= NULL
;
863 va_res
= vaMapBuffer(va_display_
, image
.buf
, &image_ptr
);
864 VA_SUCCESS_OR_RETURN(va_res
, "vaMapBuffer failed", false);
869 base::AutoUnlock
auto_unlock(*va_lock_
);
870 ret
= libyuv::I420ToNV12(frame
->data(media::VideoFrame::kYPlane
),
871 frame
->stride(media::VideoFrame::kYPlane
),
872 frame
->data(media::VideoFrame::kUPlane
),
873 frame
->stride(media::VideoFrame::kUPlane
),
874 frame
->data(media::VideoFrame::kVPlane
),
875 frame
->stride(media::VideoFrame::kVPlane
),
876 static_cast<uint8
*>(image_ptr
) + image
.offsets
[0],
878 static_cast<uint8
*>(image_ptr
) + image
.offsets
[1],
884 va_res
= vaUnmapBuffer(va_display_
, image
.buf
);
885 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
890 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id
,
891 VASurfaceID sync_surface_id
,
894 size_t* coded_data_size
) {
895 base::AutoLock
auto_lock(*va_lock_
);
897 VAStatus va_res
= vaSyncSurface(va_display_
, sync_surface_id
);
898 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
900 VACodedBufferSegment
* buffer_segment
= NULL
;
901 va_res
= vaMapBuffer(
902 va_display_
, buffer_id
, reinterpret_cast<void**>(&buffer_segment
));
903 VA_SUCCESS_OR_RETURN(va_res
, "vaMapBuffer failed", false);
907 base::AutoUnlock
auto_unlock(*va_lock_
);
908 *coded_data_size
= 0;
910 while (buffer_segment
) {
911 DCHECK(buffer_segment
->buf
);
913 if (buffer_segment
->size
> target_size
) {
914 LOG(ERROR
) << "Insufficient output buffer size";
918 memcpy(target_ptr
, buffer_segment
->buf
, buffer_segment
->size
);
920 target_ptr
+= buffer_segment
->size
;
921 *coded_data_size
+= buffer_segment
->size
;
922 target_size
-= buffer_segment
->size
;
925 reinterpret_cast<VACodedBufferSegment
*>(buffer_segment
->next
);
929 va_res
= vaUnmapBuffer(va_display_
, buffer_id
);
930 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
932 va_res
= vaDestroyBuffer(va_display_
, buffer_id
);
933 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
935 const auto was_found
= coded_buffers_
.erase(buffer_id
);
938 return buffer_segment
== NULL
;
941 bool VaapiWrapper::BlitSurface(VASurfaceID va_surface_id_src
,
942 const gfx::Size
& src_size
,
943 VASurfaceID va_surface_id_dest
,
944 const gfx::Size
& dest_size
) {
945 base::AutoLock
auto_lock(*va_lock_
);
947 // Initialize the post processing engine if not already done.
948 if (va_vpp_buffer_id_
== VA_INVALID_ID
) {
949 if (!InitializeVpp_Locked())
953 VAProcPipelineParameterBuffer
* pipeline_param
;
954 VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_
, va_vpp_buffer_id_
,
955 reinterpret_cast<void**>(&pipeline_param
)),
956 "Couldn't map vpp buffer", false);
958 memset(pipeline_param
, 0, sizeof *pipeline_param
);
960 VARectangle input_region
;
961 input_region
.x
= input_region
.y
= 0;
962 input_region
.width
= src_size
.width();
963 input_region
.height
= src_size
.height();
964 pipeline_param
->surface_region
= &input_region
;
965 pipeline_param
->surface
= va_surface_id_src
;
966 pipeline_param
->surface_color_standard
= VAProcColorStandardNone
;
968 VARectangle output_region
;
969 output_region
.x
= output_region
.y
= 0;
970 output_region
.width
= dest_size
.width();
971 output_region
.height
= dest_size
.height();
972 pipeline_param
->output_region
= &output_region
;
973 pipeline_param
->output_background_color
= 0xff000000;
974 pipeline_param
->output_color_standard
= VAProcColorStandardNone
;
976 VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_
, va_vpp_buffer_id_
),
977 "Couldn't unmap vpp buffer", false);
979 VA_SUCCESS_OR_RETURN(
980 vaBeginPicture(va_display_
, va_vpp_context_id_
, va_surface_id_dest
),
981 "Couldn't begin picture", false);
983 VA_SUCCESS_OR_RETURN(
984 vaRenderPicture(va_display_
, va_vpp_context_id_
, &va_vpp_buffer_id_
, 1),
985 "Couldn't render picture", false);
987 VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_
, va_vpp_context_id_
),
988 "Couldn't end picture", false);
993 bool VaapiWrapper::InitializeVpp_Locked() {
994 va_lock_
->AssertAcquired();
996 VA_SUCCESS_OR_RETURN(
997 vaCreateConfig(va_display_
, VAProfileNone
, VAEntrypointVideoProc
, NULL
, 0,
999 "Couldn't create config", false);
1001 // The size of the picture for the context is irrelevant in the case
1002 // of the VPP, just passing 1x1.
1003 VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_
, va_vpp_config_id_
, 1, 1, 0,
1004 NULL
, 0, &va_vpp_context_id_
),
1005 "Couldn't create context", false);
1007 VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_
, va_vpp_context_id_
,
1008 VAProcPipelineParameterBufferType
,
1009 sizeof(VAProcPipelineParameterBuffer
), 1,
1010 NULL
, &va_vpp_buffer_id_
),
1011 "Couldn't create buffer", false);
1016 void VaapiWrapper::DeinitializeVpp() {
1017 base::AutoLock
auto_lock(*va_lock_
);
1019 if (va_vpp_buffer_id_
!= VA_INVALID_ID
) {
1020 vaDestroyBuffer(va_display_
, va_vpp_buffer_id_
);
1021 va_vpp_buffer_id_
= VA_INVALID_ID
;
1023 if (va_vpp_context_id_
!= VA_INVALID_ID
) {
1024 vaDestroyContext(va_display_
, va_vpp_context_id_
);
1025 va_vpp_context_id_
= VA_INVALID_ID
;
1027 if (va_vpp_config_id_
!= VA_INVALID_ID
) {
1028 vaDestroyConfig(va_display_
, va_vpp_config_id_
);
1029 va_vpp_config_id_
= VA_INVALID_ID
;
1034 void VaapiWrapper::PreSandboxInitialization() {
1035 #if defined(USE_OZONE)
1036 const char* kDriRenderNode0Path
= "/dev/dri/renderD128";
1037 base::File drm_file
= base::File(
1038 base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path
),
1039 base::File::FLAG_OPEN
| base::File::FLAG_READ
| base::File::FLAG_WRITE
);
1040 if (drm_file
.IsValid())
1041 va_display_state_
.Get().SetDrmFd(drm_file
.GetPlatformFile());
1046 bool VaapiWrapper::PostSandboxInitialization() {
1049 paths
[kModuleVa
].push_back("libva.so.1");
1051 #if defined(USE_X11)
1052 paths
[kModuleVa_x11
].push_back("libva-x11.so.1");
1053 #elif defined(USE_OZONE)
1054 paths
[kModuleVa_drm
].push_back("libva-drm.so.1");
1057 return InitializeStubs(paths
);
1060 VaapiWrapper::LazyProfileInfos::LazyProfileInfos() {
1061 static_assert(arraysize(supported_profiles_
) == kCodecModeMax
,
1062 "The array size of supported profile is incorrect.");
1063 scoped_ptr
<VaapiWrapper
> vaapi_wrapper(new VaapiWrapper());
1064 if (!vaapi_wrapper
->VaInitialize(base::Bind(&base::DoNothing
)))
1066 for (size_t i
= 0; i
< kCodecModeMax
; ++i
) {
1067 supported_profiles_
[i
] =
1068 vaapi_wrapper
->GetSupportedProfileInfosForCodecModeInternal(
1069 static_cast<CodecMode
>(i
));
1073 VaapiWrapper::LazyProfileInfos::~LazyProfileInfos() {
1076 std::vector
<VaapiWrapper::ProfileInfo
>
1077 VaapiWrapper::LazyProfileInfos::GetSupportedProfileInfosForCodecMode(
1079 return supported_profiles_
[mode
];
1082 bool VaapiWrapper::LazyProfileInfos::IsProfileSupported(
1083 CodecMode mode
, VAProfile va_profile
) {
1084 for (const auto& profile
: supported_profiles_
[mode
]) {
1085 if (profile
.va_profile
== va_profile
)
1091 VaapiWrapper::VADisplayState::VADisplayState()
1093 va_display_(nullptr),
1096 va_initialized_(false) {}
1098 VaapiWrapper::VADisplayState::~VADisplayState() {}
1100 bool VaapiWrapper::VADisplayState::Initialize(VAStatus
* status
) {
1101 va_lock_
.AssertAcquired();
1102 if (refcount_
++ == 0) {
1103 #if defined(USE_X11)
1104 va_display_
= vaGetDisplay(gfx::GetXDisplay());
1105 #elif defined(USE_OZONE)
1106 va_display_
= vaGetDisplayDRM(drm_fd_
.get());
1109 if (!vaDisplayIsValid(va_display_
)) {
1110 LOG(ERROR
) << "Could not get a valid VA display";
1114 *status
= vaInitialize(va_display_
, &major_version_
, &minor_version_
);
1115 if (*status
!= VA_STATUS_SUCCESS
)
1118 va_initialized_
= true;
1119 DVLOG(1) << "VAAPI version: " << major_version_
<< "." << minor_version_
;
1122 if (VAAPIVersionLessThan(0, 34)) {
1123 LOG(ERROR
) << "VAAPI version < 0.34 is not supported.";
1129 void VaapiWrapper::VADisplayState::Deinitialize(VAStatus
* status
) {
1130 va_lock_
.AssertAcquired();
1131 if (--refcount_
> 0)
1134 // Must check if vaInitialize completed successfully, to work around a bug in
1135 // libva. The bug was fixed upstream:
1136 // http://lists.freedesktop.org/archives/libva/2013-July/001807.html
1137 // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once
1138 // the fix has rolled out sufficiently.
1139 if (va_initialized_
&& va_display_
) {
1140 *status
= vaTerminate(va_display_
);
1142 va_initialized_
= false;
1143 va_display_
= nullptr;
1146 #if defined(USE_OZONE)
1147 void VaapiWrapper::VADisplayState::SetDrmFd(base::PlatformFile fd
) {
1148 drm_fd_
.reset(HANDLE_EINTR(dup(fd
)));
1152 bool VaapiWrapper::VADisplayState::VAAPIVersionLessThan(int major
, int minor
) {
1153 return (major_version_
< major
) ||
1154 (major_version_
== major
&& minor_version_
< minor
);
1157 } // namespace content