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 std::vector
<media::VideoEncodeAccelerator::SupportedProfile
>
171 VaapiWrapper::GetSupportedEncodeProfiles() {
172 std::vector
<media::VideoEncodeAccelerator::SupportedProfile
> 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
);
199 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
200 base::AutoLock
auto_lock(va_lock_
);
201 VADisplayAttribute item
= {VADisplayAttribRenderMode
,
202 1, // At least support '_LOCAL_OVERLAY'.
203 -1, // The maximum possible support 'ALL'.
204 VA_RENDER_MODE_LOCAL_GPU
,
205 VA_DISPLAY_ATTRIB_SETTABLE
};
207 VAStatus va_res
= vaSetDisplayAttributes(va_display_
, &item
, 1);
208 if (va_res
!= VA_STATUS_SUCCESS
)
209 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
213 VAProfile
VaapiWrapper::ProfileToVAProfile(
214 media::VideoCodecProfile profile
, CodecMode mode
) {
215 VAProfile va_profile
= VAProfileNone
;
216 for (size_t i
= 0; i
< arraysize(kProfileMap
); ++i
) {
217 if (kProfileMap
[i
].profile
== profile
) {
218 va_profile
= kProfileMap
[i
].va_profile
;
222 if (!profile_infos_
.Get().IsProfileSupported(mode
, va_profile
) &&
223 va_profile
== VAProfileH264Baseline
) {
224 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips
225 // the information whether the profile is constrained or not, so we have no
226 // way to know here. Try for baseline first, but if it is not supported,
227 // try constrained baseline and hope this is what it actually is
228 // (which in practice is true for a great majority of cases).
229 if (profile_infos_
.Get().IsProfileSupported(
230 mode
, VAProfileH264ConstrainedBaseline
)) {
231 va_profile
= VAProfileH264ConstrainedBaseline
;
232 DVLOG(1) << "Fall back to constrained baseline profile.";
238 std::vector
<VaapiWrapper::ProfileInfo
>
239 VaapiWrapper::GetSupportedProfileInfosForCodecModeInternal(CodecMode mode
) {
240 std::vector
<ProfileInfo
> supported_profile_infos
;
241 std::vector
<VAProfile
> va_profiles
;
242 if (!GetSupportedVaProfiles(&va_profiles
))
243 return supported_profile_infos
;
245 std::vector
<VAConfigAttrib
> required_attribs
= GetRequiredAttribs(mode
);
246 VAEntrypoint entrypoint
=
247 (mode
== kEncode
? VAEntrypointEncSlice
: VAEntrypointVLD
);
249 base::AutoLock
auto_lock(va_lock_
);
250 for (const auto& va_profile
: va_profiles
) {
251 if (!IsEntrypointSupported_Locked(va_profile
, entrypoint
))
253 if (!AreAttribsSupported_Locked(va_profile
, entrypoint
, required_attribs
))
255 ProfileInfo profile_info
;
256 if (!GetMaxResolution_Locked(va_profile
,
259 &profile_info
.max_resolution
)) {
260 LOG(ERROR
) << "GetMaxResolution failed for va_profile " << va_profile
261 << " and entrypoint " << entrypoint
;
264 profile_info
.va_profile
= va_profile
;
265 supported_profile_infos
.push_back(profile_info
);
267 return supported_profile_infos
;
270 bool VaapiWrapper::VaInitialize(const base::Closure
& report_error_to_uma_cb
) {
271 static bool vaapi_functions_initialized
= PostSandboxInitialization();
272 if (!vaapi_functions_initialized
) {
273 bool running_on_chromeos
= false;
274 #if defined(OS_CHROMEOS)
275 // When chrome runs on linux with chromeos=1, do not log error message
276 // without VAAPI libraries.
277 running_on_chromeos
= base::SysInfo::IsRunningOnChromeOS();
279 static const char kErrorMsg
[] = "Failed to initialize VAAPI libs";
280 if (running_on_chromeos
)
281 LOG(ERROR
) << kErrorMsg
;
283 DVLOG(1) << kErrorMsg
;
287 report_error_to_uma_cb_
= report_error_to_uma_cb
;
289 base::AutoLock
auto_lock(va_lock_
);
292 va_display_
= vaGetDisplay(gfx::GetXDisplay());
293 #elif defined(USE_OZONE)
294 ui::OzonePlatform
* platform
= ui::OzonePlatform::GetInstance();
295 ui::SurfaceFactoryOzone
* factory
= platform
->GetSurfaceFactoryOzone();
297 va_display_
= vaGetDisplayDRM(factory
->GetDrmFd());
300 if (!vaDisplayIsValid(va_display_
)) {
301 LOG(ERROR
) << "Could not get a valid VA display";
305 VAStatus va_res
= vaInitialize(va_display_
, &major_version_
, &minor_version_
);
306 VA_SUCCESS_OR_RETURN(va_res
, "vaInitialize failed", false);
307 va_initialized_
= true;
308 DVLOG(1) << "VAAPI version: " << major_version_
<< "." << minor_version_
;
310 if (VAAPIVersionLessThan(0, 34)) {
311 LOG(ERROR
) << "VAAPI version < 0.34 is not supported.";
317 bool VaapiWrapper::GetSupportedVaProfiles(std::vector
<VAProfile
>* profiles
) {
318 base::AutoLock
auto_lock(va_lock_
);
319 // Query the driver for supported profiles.
320 int max_profiles
= vaMaxNumProfiles(va_display_
);
321 std::vector
<VAProfile
> supported_profiles(
322 base::checked_cast
<size_t>(max_profiles
));
324 int num_supported_profiles
;
325 VAStatus va_res
= vaQueryConfigProfiles(
326 va_display_
, &supported_profiles
[0], &num_supported_profiles
);
327 VA_SUCCESS_OR_RETURN(va_res
, "vaQueryConfigProfiles failed", false);
328 if (num_supported_profiles
< 0 || num_supported_profiles
> max_profiles
) {
329 LOG(ERROR
) << "vaQueryConfigProfiles returned: " << num_supported_profiles
;
333 supported_profiles
.resize(base::checked_cast
<size_t>(num_supported_profiles
));
334 *profiles
= supported_profiles
;
338 bool VaapiWrapper::IsEntrypointSupported_Locked(VAProfile va_profile
,
339 VAEntrypoint entrypoint
) {
340 va_lock_
.AssertAcquired();
341 // Query the driver for supported entrypoints.
342 int max_entrypoints
= vaMaxNumEntrypoints(va_display_
);
343 std::vector
<VAEntrypoint
> supported_entrypoints(
344 base::checked_cast
<size_t>(max_entrypoints
));
346 int num_supported_entrypoints
;
347 VAStatus va_res
= vaQueryConfigEntrypoints(va_display_
,
349 &supported_entrypoints
[0],
350 &num_supported_entrypoints
);
351 VA_SUCCESS_OR_RETURN(va_res
, "vaQueryConfigEntrypoints failed", false);
352 if (num_supported_entrypoints
< 0 ||
353 num_supported_entrypoints
> max_entrypoints
) {
354 LOG(ERROR
) << "vaQueryConfigEntrypoints returned: "
355 << num_supported_entrypoints
;
359 if (std::find(supported_entrypoints
.begin(),
360 supported_entrypoints
.end(),
361 entrypoint
) == supported_entrypoints
.end()) {
362 DVLOG(1) << "Unsupported entrypoint";
368 bool VaapiWrapper::AreAttribsSupported_Locked(
369 VAProfile va_profile
,
370 VAEntrypoint entrypoint
,
371 const std::vector
<VAConfigAttrib
>& required_attribs
) {
372 va_lock_
.AssertAcquired();
373 // Query the driver for required attributes.
374 std::vector
<VAConfigAttrib
> attribs
= required_attribs
;
375 for (size_t i
= 0; i
< required_attribs
.size(); ++i
)
376 attribs
[i
].value
= 0;
378 VAStatus va_res
= vaGetConfigAttributes(
379 va_display_
, va_profile
, entrypoint
, &attribs
[0], attribs
.size());
380 VA_SUCCESS_OR_RETURN(va_res
, "vaGetConfigAttributes failed", false);
382 for (size_t i
= 0; i
< required_attribs
.size(); ++i
) {
383 if (attribs
[i
].type
!= required_attribs
[i
].type
||
384 (attribs
[i
].value
& required_attribs
[i
].value
) !=
385 required_attribs
[i
].value
) {
386 DVLOG(1) << "Unsupported value " << required_attribs
[i
].value
387 << " for attribute type " << required_attribs
[i
].type
;
394 bool VaapiWrapper::GetMaxResolution_Locked(
395 VAProfile va_profile
,
396 VAEntrypoint entrypoint
,
397 std::vector
<VAConfigAttrib
>& required_attribs
,
398 gfx::Size
* resolution
) {
399 va_lock_
.AssertAcquired();
400 VAConfigID va_config_id
;
401 VAStatus va_res
= vaCreateConfig(
405 &required_attribs
[0],
406 required_attribs
.size(),
408 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateConfig failed", false);
410 // Calls vaQuerySurfaceAttributes twice. The first time is to get the number
411 // of attributes to prepare the space and the second time is to get all
413 unsigned int num_attribs
;
414 va_res
= vaQuerySurfaceAttributes(
415 va_display_
, va_config_id
, nullptr, &num_attribs
);
416 VA_SUCCESS_OR_RETURN(va_res
, "vaQuerySurfaceAttributes failed", false);
420 std::vector
<VASurfaceAttrib
> attrib_list(
421 base::checked_cast
<size_t>(num_attribs
));
423 va_res
= vaQuerySurfaceAttributes(
424 va_display_
, va_config_id
, &attrib_list
[0], &num_attribs
);
425 VA_SUCCESS_OR_RETURN(va_res
, "vaQuerySurfaceAttributes failed", false);
427 resolution
->SetSize(0, 0);
428 for (const auto& attrib
: attrib_list
) {
429 if (attrib
.type
== VASurfaceAttribMaxWidth
)
430 resolution
->set_width(attrib
.value
.value
.i
);
431 else if (attrib
.type
== VASurfaceAttribMaxHeight
)
432 resolution
->set_height(attrib
.value
.value
.i
);
434 if (resolution
->IsEmpty()) {
435 LOG(ERROR
) << "Codec resolution " << resolution
->ToString()
436 << " cannot be zero.";
442 bool VaapiWrapper::Initialize(CodecMode mode
, VAProfile va_profile
) {
443 TryToSetVADisplayAttributeToLocalGPU();
445 VAEntrypoint entrypoint
=
446 (mode
== kEncode
? VAEntrypointEncSlice
: VAEntrypointVLD
);
447 std::vector
<VAConfigAttrib
> required_attribs
= GetRequiredAttribs(mode
);
448 base::AutoLock
auto_lock(va_lock_
);
449 VAStatus va_res
= vaCreateConfig(va_display_
,
452 &required_attribs
[0],
453 required_attribs
.size(),
455 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateConfig failed", false);
460 void VaapiWrapper::Deinitialize() {
461 base::AutoLock
auto_lock(va_lock_
);
463 if (va_config_id_
!= VA_INVALID_ID
) {
464 VAStatus va_res
= vaDestroyConfig(va_display_
, va_config_id_
);
465 VA_LOG_ON_ERROR(va_res
, "vaDestroyConfig failed");
468 // Must check if vaInitialize completed successfully, to work around a bug in
469 // libva. The bug was fixed upstream:
470 // http://lists.freedesktop.org/archives/libva/2013-July/001807.html
471 // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once
472 // the fix has rolled out sufficiently.
473 if (va_initialized_
&& va_display_
) {
474 VAStatus va_res
= vaTerminate(va_display_
);
475 VA_LOG_ON_ERROR(va_res
, "vaTerminate failed");
478 va_config_id_
= VA_INVALID_ID
;
480 va_initialized_
= false;
483 bool VaapiWrapper::VAAPIVersionLessThan(int major
, int minor
) {
484 return (major_version_
< major
) ||
485 (major_version_
== major
&& minor_version_
< minor
);
488 bool VaapiWrapper::CreateSurfaces(const gfx::Size
& size
,
490 std::vector
<VASurfaceID
>* va_surfaces
) {
491 base::AutoLock
auto_lock(va_lock_
);
492 DVLOG(2) << "Creating " << num_surfaces
<< " surfaces";
494 DCHECK(va_surfaces
->empty());
495 DCHECK(va_surface_ids_
.empty());
496 va_surface_ids_
.resize(num_surfaces
);
498 // Allocate surfaces in driver.
499 VAStatus va_res
= vaCreateSurfaces(va_display_
,
501 size
.width(), size
.height(),
503 va_surface_ids_
.size(),
506 VA_LOG_ON_ERROR(va_res
, "vaCreateSurfaces failed");
507 if (va_res
!= VA_STATUS_SUCCESS
) {
508 va_surface_ids_
.clear();
512 // And create a context associated with them.
513 va_res
= vaCreateContext(va_display_
, va_config_id_
,
514 size
.width(), size
.height(), VA_PROGRESSIVE
,
515 &va_surface_ids_
[0], va_surface_ids_
.size(),
518 VA_LOG_ON_ERROR(va_res
, "vaCreateContext failed");
519 if (va_res
!= VA_STATUS_SUCCESS
) {
524 *va_surfaces
= va_surface_ids_
;
528 void VaapiWrapper::DestroySurfaces() {
529 base::AutoLock
auto_lock(va_lock_
);
530 DVLOG(2) << "Destroying " << va_surface_ids_
.size() << " surfaces";
532 if (va_context_id_
!= VA_INVALID_ID
) {
533 VAStatus va_res
= vaDestroyContext(va_display_
, va_context_id_
);
534 VA_LOG_ON_ERROR(va_res
, "vaDestroyContext failed");
537 if (!va_surface_ids_
.empty()) {
538 VAStatus va_res
= vaDestroySurfaces(va_display_
, &va_surface_ids_
[0],
539 va_surface_ids_
.size());
540 VA_LOG_ON_ERROR(va_res
, "vaDestroySurfaces failed");
543 va_surface_ids_
.clear();
544 va_context_id_
= VA_INVALID_ID
;
547 scoped_refptr
<VASurface
> VaapiWrapper::CreateUnownedSurface(
548 unsigned int va_format
,
549 const gfx::Size
& size
,
550 const std::vector
<VASurfaceAttrib
>& va_attribs
) {
551 base::AutoLock
auto_lock(va_lock_
);
553 std::vector
<VASurfaceAttrib
> attribs(va_attribs
);
554 VASurfaceID va_surface_id
;
556 vaCreateSurfaces(va_display_
, va_format
, size
.width(), size
.height(),
557 &va_surface_id
, 1, &attribs
[0], attribs
.size());
559 scoped_refptr
<VASurface
> va_surface
;
560 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create unowned VASurface",
563 // This is safe to use Unretained() here, because the VDA takes care
564 // of the destruction order. All the surfaces will be destroyed
565 // before VaapiWrapper.
566 va_surface
= new VASurface(
568 base::Bind(&VaapiWrapper::DestroyUnownedSurface
, base::Unretained(this)));
573 void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id
) {
574 base::AutoLock
auto_lock(va_lock_
);
576 VAStatus va_res
= vaDestroySurfaces(va_display_
, &va_surface_id
, 1);
577 VA_LOG_ON_ERROR(va_res
, "vaDestroySurfaces on surface failed");
580 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type
,
583 base::AutoLock
auto_lock(va_lock_
);
585 VABufferID buffer_id
;
586 VAStatus va_res
= vaCreateBuffer(va_display_
, va_context_id_
,
587 va_buffer_type
, size
,
588 1, buffer
, &buffer_id
);
589 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a VA buffer", false);
591 switch (va_buffer_type
) {
592 case VASliceParameterBufferType
:
593 case VASliceDataBufferType
:
594 case VAEncSliceParameterBufferType
:
595 pending_slice_bufs_
.push_back(buffer_id
);
599 pending_va_bufs_
.push_back(buffer_id
);
606 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
607 VAEncMiscParameterType misc_param_type
,
610 base::AutoLock
auto_lock(va_lock_
);
612 VABufferID buffer_id
;
613 VAStatus va_res
= vaCreateBuffer(va_display_
,
615 VAEncMiscParameterBufferType
,
616 sizeof(VAEncMiscParameterBuffer
) + size
,
620 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a VA buffer", false);
622 void* data_ptr
= NULL
;
623 va_res
= vaMapBuffer(va_display_
, buffer_id
, &data_ptr
);
624 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
625 if (va_res
!= VA_STATUS_SUCCESS
) {
626 vaDestroyBuffer(va_display_
, buffer_id
);
632 VAEncMiscParameterBuffer
* misc_param
=
633 reinterpret_cast<VAEncMiscParameterBuffer
*>(data_ptr
);
634 misc_param
->type
= misc_param_type
;
635 memcpy(misc_param
->data
, buffer
, size
);
636 va_res
= vaUnmapBuffer(va_display_
, buffer_id
);
637 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
639 pending_va_bufs_
.push_back(buffer_id
);
643 void VaapiWrapper::DestroyPendingBuffers() {
644 base::AutoLock
auto_lock(va_lock_
);
646 for (const auto& pending_va_buf
: pending_va_bufs_
) {
647 VAStatus va_res
= vaDestroyBuffer(va_display_
, pending_va_buf
);
648 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
651 for (const auto& pending_slice_buf
: pending_slice_bufs_
) {
652 VAStatus va_res
= vaDestroyBuffer(va_display_
, pending_slice_buf
);
653 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
656 pending_va_bufs_
.clear();
657 pending_slice_bufs_
.clear();
660 bool VaapiWrapper::CreateCodedBuffer(size_t size
, VABufferID
* buffer_id
) {
661 base::AutoLock
auto_lock(va_lock_
);
662 VAStatus va_res
= vaCreateBuffer(va_display_
,
664 VAEncCodedBufferType
,
669 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a coded buffer", false);
671 DCHECK(coded_buffers_
.insert(*buffer_id
).second
);
675 void VaapiWrapper::DestroyCodedBuffers() {
676 base::AutoLock
auto_lock(va_lock_
);
678 for (std::set
<VABufferID
>::const_iterator iter
= coded_buffers_
.begin();
679 iter
!= coded_buffers_
.end();
681 VAStatus va_res
= vaDestroyBuffer(va_display_
, *iter
);
682 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
685 coded_buffers_
.clear();
688 bool VaapiWrapper::Execute(VASurfaceID va_surface_id
) {
689 base::AutoLock
auto_lock(va_lock_
);
691 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_
.size();
692 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_
.size();
693 DVLOG(4) << "Target VA surface " << va_surface_id
;
695 // Get ready to execute for given surface.
696 VAStatus va_res
= vaBeginPicture(va_display_
, va_context_id_
,
698 VA_SUCCESS_OR_RETURN(va_res
, "vaBeginPicture failed", false);
700 if (pending_va_bufs_
.size() > 0) {
701 // Commit parameter and slice buffers.
702 va_res
= vaRenderPicture(va_display_
,
704 &pending_va_bufs_
[0],
705 pending_va_bufs_
.size());
706 VA_SUCCESS_OR_RETURN(va_res
, "vaRenderPicture for va_bufs failed", false);
709 if (pending_slice_bufs_
.size() > 0) {
710 va_res
= vaRenderPicture(va_display_
,
712 &pending_slice_bufs_
[0],
713 pending_slice_bufs_
.size());
714 VA_SUCCESS_OR_RETURN(va_res
, "vaRenderPicture for slices failed", false);
717 // Instruct HW codec to start processing committed buffers.
718 // Does not block and the job is not finished after this returns.
719 va_res
= vaEndPicture(va_display_
, va_context_id_
);
720 VA_SUCCESS_OR_RETURN(va_res
, "vaEndPicture failed", false);
725 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id
) {
726 bool result
= Execute(va_surface_id
);
727 DestroyPendingBuffers();
732 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id
,
734 gfx::Size dest_size
) {
735 base::AutoLock
auto_lock(va_lock_
);
737 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
738 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
740 // Put the data into an X Pixmap.
741 va_res
= vaPutSurface(va_display_
,
744 0, 0, dest_size
.width(), dest_size
.height(),
745 0, 0, dest_size
.width(), dest_size
.height(),
747 VA_SUCCESS_OR_RETURN(va_res
, "Failed putting surface to pixmap", false);
752 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id
,
755 base::AutoLock
auto_lock(va_lock_
);
757 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
758 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
760 // Derive a VAImage from the VASurface
761 va_res
= vaDeriveImage(va_display_
, va_surface_id
, image
);
762 VA_LOG_ON_ERROR(va_res
, "vaDeriveImage failed");
763 if (va_res
!= VA_STATUS_SUCCESS
)
766 // Map the VAImage into memory
767 va_res
= vaMapBuffer(va_display_
, image
->buf
, mem
);
768 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
769 if (va_res
== VA_STATUS_SUCCESS
)
772 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
773 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
778 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id
,
779 VAImageFormat
* format
,
780 const gfx::Size
& size
,
783 base::AutoLock
auto_lock(va_lock_
);
785 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
786 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
789 vaCreateImage(va_display_
, format
, size
.width(), size
.height(), image
);
790 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateImage failed", false);
792 va_res
= vaGetImage(va_display_
, va_surface_id
, 0, 0, size
.width(),
793 size
.height(), image
->image_id
);
794 VA_LOG_ON_ERROR(va_res
, "vaGetImage failed");
796 if (va_res
== VA_STATUS_SUCCESS
) {
797 // Map the VAImage into memory
798 va_res
= vaMapBuffer(va_display_
, image
->buf
, mem
);
799 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
802 if (va_res
!= VA_STATUS_SUCCESS
) {
803 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
804 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
811 void VaapiWrapper::ReturnVaImage(VAImage
* image
) {
812 base::AutoLock
auto_lock(va_lock_
);
814 VAStatus va_res
= vaUnmapBuffer(va_display_
, image
->buf
);
815 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
817 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
818 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
821 static void DestroyVAImage(VADisplay va_display
, VAImage image
) {
822 if (image
.image_id
!= VA_INVALID_ID
)
823 vaDestroyImage(va_display
, image
.image_id
);
826 bool VaapiWrapper::UploadVideoFrameToSurface(
827 const scoped_refptr
<media::VideoFrame
>& frame
,
828 VASurfaceID va_surface_id
) {
829 base::AutoLock
auto_lock(va_lock_
);
832 VAStatus va_res
= vaDeriveImage(va_display_
, va_surface_id
, &image
);
833 VA_SUCCESS_OR_RETURN(va_res
, "vaDeriveImage failed", false);
834 base::ScopedClosureRunner
vaimage_deleter(
835 base::Bind(&DestroyVAImage
, va_display_
, image
));
837 if (image
.format
.fourcc
!= VA_FOURCC_NV12
) {
838 LOG(ERROR
) << "Unsupported image format: " << image
.format
.fourcc
;
842 if (gfx::Rect(image
.width
, image
.height
) < gfx::Rect(frame
->coded_size())) {
843 LOG(ERROR
) << "Buffer too small to fit the frame.";
847 void* image_ptr
= NULL
;
848 va_res
= vaMapBuffer(va_display_
, image
.buf
, &image_ptr
);
849 VA_SUCCESS_OR_RETURN(va_res
, "vaMapBuffer failed", false);
854 base::AutoUnlock
auto_unlock(va_lock_
);
855 ret
= libyuv::I420ToNV12(frame
->data(media::VideoFrame::kYPlane
),
856 frame
->stride(media::VideoFrame::kYPlane
),
857 frame
->data(media::VideoFrame::kUPlane
),
858 frame
->stride(media::VideoFrame::kUPlane
),
859 frame
->data(media::VideoFrame::kVPlane
),
860 frame
->stride(media::VideoFrame::kVPlane
),
861 static_cast<uint8
*>(image_ptr
) + image
.offsets
[0],
863 static_cast<uint8
*>(image_ptr
) + image
.offsets
[1],
869 va_res
= vaUnmapBuffer(va_display_
, image
.buf
);
870 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
875 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id
,
876 VASurfaceID sync_surface_id
,
879 size_t* coded_data_size
) {
880 base::AutoLock
auto_lock(va_lock_
);
882 VAStatus va_res
= vaSyncSurface(va_display_
, sync_surface_id
);
883 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
885 VACodedBufferSegment
* buffer_segment
= NULL
;
886 va_res
= vaMapBuffer(
887 va_display_
, buffer_id
, reinterpret_cast<void**>(&buffer_segment
));
888 VA_SUCCESS_OR_RETURN(va_res
, "vaMapBuffer failed", false);
892 base::AutoUnlock
auto_unlock(va_lock_
);
893 *coded_data_size
= 0;
895 while (buffer_segment
) {
896 DCHECK(buffer_segment
->buf
);
898 if (buffer_segment
->size
> target_size
) {
899 LOG(ERROR
) << "Insufficient output buffer size";
903 memcpy(target_ptr
, buffer_segment
->buf
, buffer_segment
->size
);
905 target_ptr
+= buffer_segment
->size
;
906 *coded_data_size
+= buffer_segment
->size
;
907 target_size
-= buffer_segment
->size
;
910 reinterpret_cast<VACodedBufferSegment
*>(buffer_segment
->next
);
914 va_res
= vaUnmapBuffer(va_display_
, buffer_id
);
915 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
917 va_res
= vaDestroyBuffer(va_display_
, buffer_id
);
918 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
920 DCHECK(coded_buffers_
.erase(buffer_id
));
922 return buffer_segment
== NULL
;
925 bool VaapiWrapper::BlitSurface(VASurfaceID va_surface_id_src
,
926 const gfx::Size
& src_size
,
927 VASurfaceID va_surface_id_dest
,
928 const gfx::Size
& dest_size
) {
929 base::AutoLock
auto_lock(va_lock_
);
931 // Initialize the post processing engine if not already done.
932 if (va_vpp_buffer_id_
== VA_INVALID_ID
) {
933 if (!InitializeVpp_Locked())
937 VAProcPipelineParameterBuffer
* pipeline_param
;
938 VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_
, va_vpp_buffer_id_
,
939 reinterpret_cast<void**>(&pipeline_param
)),
940 "Couldn't map vpp buffer", false);
942 memset(pipeline_param
, 0, sizeof *pipeline_param
);
944 VARectangle input_region
;
945 input_region
.x
= input_region
.y
= 0;
946 input_region
.width
= src_size
.width();
947 input_region
.height
= src_size
.height();
948 pipeline_param
->surface_region
= &input_region
;
949 pipeline_param
->surface
= va_surface_id_src
;
950 pipeline_param
->surface_color_standard
= VAProcColorStandardNone
;
952 VARectangle output_region
;
953 output_region
.x
= output_region
.y
= 0;
954 output_region
.width
= dest_size
.width();
955 output_region
.height
= dest_size
.height();
956 pipeline_param
->output_region
= &output_region
;
957 pipeline_param
->output_background_color
= 0xff000000;
958 pipeline_param
->output_color_standard
= VAProcColorStandardNone
;
960 VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_
, va_vpp_buffer_id_
),
961 "Couldn't unmap vpp buffer", false);
963 VA_SUCCESS_OR_RETURN(
964 vaBeginPicture(va_display_
, va_vpp_context_id_
, va_surface_id_dest
),
965 "Couldn't begin picture", false);
967 VA_SUCCESS_OR_RETURN(
968 vaRenderPicture(va_display_
, va_vpp_context_id_
, &va_vpp_buffer_id_
, 1),
969 "Couldn't render picture", false);
971 VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_
, va_vpp_context_id_
),
972 "Couldn't end picture", false);
977 bool VaapiWrapper::InitializeVpp_Locked() {
978 va_lock_
.AssertAcquired();
980 VA_SUCCESS_OR_RETURN(
981 vaCreateConfig(va_display_
, VAProfileNone
, VAEntrypointVideoProc
, NULL
, 0,
983 "Couldn't create config", false);
985 // The size of the picture for the context is irrelevant in the case
986 // of the VPP, just passing 1x1.
987 VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_
, va_vpp_config_id_
, 1, 1, 0,
988 NULL
, 0, &va_vpp_context_id_
),
989 "Couldn't create context", false);
991 VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_
, va_vpp_context_id_
,
992 VAProcPipelineParameterBufferType
,
993 sizeof(VAProcPipelineParameterBuffer
), 1,
994 NULL
, &va_vpp_buffer_id_
),
995 "Couldn't create buffer", false);
1000 void VaapiWrapper::DeinitializeVpp() {
1001 base::AutoLock
auto_lock(va_lock_
);
1003 if (va_vpp_buffer_id_
!= VA_INVALID_ID
) {
1004 vaDestroyBuffer(va_display_
, va_vpp_buffer_id_
);
1005 va_vpp_buffer_id_
= VA_INVALID_ID
;
1007 if (va_vpp_context_id_
!= VA_INVALID_ID
) {
1008 vaDestroyContext(va_display_
, va_vpp_context_id_
);
1009 va_vpp_context_id_
= VA_INVALID_ID
;
1011 if (va_vpp_config_id_
!= VA_INVALID_ID
) {
1012 vaDestroyConfig(va_display_
, va_vpp_config_id_
);
1013 va_vpp_config_id_
= VA_INVALID_ID
;
1018 bool VaapiWrapper::PostSandboxInitialization() {
1021 paths
[kModuleVa
].push_back("libva.so.1");
1023 #if defined(USE_X11)
1024 paths
[kModuleVa_x11
].push_back("libva-x11.so.1");
1025 #elif defined(USE_OZONE)
1026 paths
[kModuleVa_drm
].push_back("libva-drm.so.1");
1029 return InitializeStubs(paths
);
1032 VaapiWrapper::LazyProfileInfos::LazyProfileInfos() {
1033 static_assert(arraysize(supported_profiles_
) == kCodecModeMax
,
1034 "The array size of supported profile is incorrect.");
1035 scoped_ptr
<VaapiWrapper
> vaapi_wrapper(new VaapiWrapper());
1036 if (!vaapi_wrapper
->VaInitialize(base::Bind(&base::DoNothing
)))
1038 for (size_t i
= 0; i
< kCodecModeMax
; ++i
) {
1039 supported_profiles_
[i
] =
1040 vaapi_wrapper
->GetSupportedProfileInfosForCodecModeInternal(
1041 static_cast<CodecMode
>(i
));
1045 VaapiWrapper::LazyProfileInfos::~LazyProfileInfos() {
1048 std::vector
<VaapiWrapper::ProfileInfo
>
1049 VaapiWrapper::LazyProfileInfos::GetSupportedProfileInfosForCodecMode(
1051 return supported_profiles_
[mode
];
1054 bool VaapiWrapper::LazyProfileInfos::IsProfileSupported(
1055 CodecMode mode
, VAProfile va_profile
) {
1056 for (const auto& profile
: supported_profiles_
[mode
]) {
1057 if (profile
.va_profile
== va_profile
)
1063 } // namespace content