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::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
},
81 media::VideoCodecProfile 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()
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();
140 scoped_ptr
<VaapiWrapper
> VaapiWrapper::Create(
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
;
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
;
159 scoped_ptr
<VaapiWrapper
> VaapiWrapper::CreateForVideoCodec(
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();
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
))
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
)
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
);
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
)
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
);
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.";
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
;
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.";
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
))
278 if (!AreAttribsSupported_Locked(va_profile
, entrypoint
, required_attribs
))
280 ProfileInfo profile_info
;
281 if (!GetMaxResolution_Locked(va_profile
,
284 &profile_info
.max_resolution
)) {
285 LOG(ERROR
) << "GetMaxResolution failed for va_profile " << va_profile
286 << " and entrypoint " << entrypoint
;
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();
304 static const char kErrorMsg
[] = "Failed to initialize VAAPI libs";
305 if (running_on_chromeos
)
306 LOG(ERROR
) << kErrorMsg
;
308 DVLOG(1) << kErrorMsg
;
312 report_error_to_uma_cb_
= report_error_to_uma_cb
;
314 base::AutoLock
auto_lock(va_lock_
);
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());
326 if (!vaDisplayIsValid(va_display_
)) {
327 LOG(ERROR
) << "Could not get a valid VA display";
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.";
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
;
359 supported_profiles
.resize(base::checked_cast
<size_t>(num_supported_profiles
));
360 *profiles
= supported_profiles
;
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_
,
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
;
385 if (std::find(supported_entrypoints
.begin(),
386 supported_entrypoints
.end(),
387 entrypoint
) == supported_entrypoints
.end()) {
388 DVLOG(1) << "Unsupported entrypoint";
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
;
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(
431 &required_attribs
[0],
432 required_attribs
.size(),
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
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);
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.";
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_
,
478 &required_attribs
[0],
479 required_attribs
.size(),
481 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateConfig failed", false);
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
;
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
,
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_
,
527 size
.width(), size
.height(),
529 va_surface_ids_
.size(),
532 VA_LOG_ON_ERROR(va_res
, "vaCreateSurfaces failed");
533 if (va_res
!= VA_STATUS_SUCCESS
) {
534 va_surface_ids_
.clear();
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(),
544 VA_LOG_ON_ERROR(va_res
, "vaCreateContext failed");
545 if (va_res
!= VA_STATUS_SUCCESS
) {
550 *va_surfaces
= va_surface_ids_
;
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
;
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",
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(
594 base::Bind(&VaapiWrapper::DestroyUnownedSurface
, base::Unretained(this)));
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
,
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
);
625 pending_va_bufs_
.push_back(buffer_id
);
632 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
633 VAEncMiscParameterType misc_param_type
,
636 base::AutoLock
auto_lock(va_lock_
);
638 VABufferID buffer_id
;
639 VAStatus va_res
= vaCreateBuffer(va_display_
,
641 VAEncMiscParameterBufferType
,
642 sizeof(VAEncMiscParameterBuffer
) + size
,
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
);
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
);
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_
,
690 VAEncCodedBufferType
,
695 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a coded buffer", false);
697 DCHECK(coded_buffers_
.insert(*buffer_id
).second
);
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();
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_
,
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_
,
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_
,
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);
751 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id
) {
752 bool result
= Execute(va_surface_id
);
753 DestroyPendingBuffers();
758 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id
,
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_
,
770 0, 0, dest_size
.width(), dest_size
.height(),
771 0, 0, dest_size
.width(), dest_size
.height(),
773 VA_SUCCESS_OR_RETURN(va_res
, "Failed putting surface to pixmap", false);
778 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id
,
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
)
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
)
798 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
799 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
804 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id
,
805 VAImageFormat
* format
,
806 const gfx::Size
& size
,
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);
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");
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_
);
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
;
868 if (gfx::Rect(image
.width
, image
.height
) < gfx::Rect(frame
->coded_size())) {
869 LOG(ERROR
) << "Buffer too small to fit the frame.";
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);
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],
889 static_cast<uint8
*>(image_ptr
) + image
.offsets
[1],
895 va_res
= vaUnmapBuffer(va_display_
, image
.buf
);
896 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
901 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id
,
902 VASurfaceID sync_surface_id
,
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);
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";
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
;
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())
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);
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);
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
;
1044 bool VaapiWrapper::PostSandboxInitialization() {
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");
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
)))
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(
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
)
1089 } // namespace content