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/logging.h"
12 #include "base/numerics/safe_conversions.h"
13 #include "base/sys_info.h"
14 // Auto-generated for dlopen libva libraries
15 #include "content/common/gpu/media/va_stubs.h"
16 #include "content/common/gpu/media/vaapi_picture.h"
17 #include "third_party/libyuv/include/libyuv.h"
18 #include "ui/gl/gl_bindings.h"
20 #include "ui/gfx/x/x11_types.h"
21 #elif defined(USE_OZONE)
22 #include "third_party/libva/va/drm/va_drm.h"
23 #include "ui/ozone/public/ozone_platform.h"
24 #include "ui/ozone/public/surface_factory_ozone.h"
27 using content_common_gpu_media::kModuleVa
;
29 using content_common_gpu_media::kModuleVa_x11
;
30 #elif defined(USE_OZONE)
31 using content_common_gpu_media::kModuleVa_drm
;
33 using content_common_gpu_media::InitializeStubs
;
34 using content_common_gpu_media::StubPathMap
;
36 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \
38 LOG(ERROR) << err_msg \
39 << " VA error: " << vaErrorStr(va_error); \
40 report_error_to_uma_cb_.Run(); \
43 #define VA_LOG_ON_ERROR(va_error, err_msg) \
45 if ((va_error) != VA_STATUS_SUCCESS) \
46 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
49 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \
51 if ((va_error) != VA_STATUS_SUCCESS) { \
52 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
59 // Config attributes common for both encode and decode.
60 static const VAConfigAttrib kCommonVAConfigAttribs
[] = {
61 {VAConfigAttribRTFormat
, VA_RT_FORMAT_YUV420
},
64 // Attributes required for encode.
65 static const VAConfigAttrib kEncodeVAConfigAttribs
[] = {
66 {VAConfigAttribRateControl
, VA_RC_CBR
},
67 {VAConfigAttribEncPackedHeaders
,
68 VA_ENC_PACKED_HEADER_SEQUENCE
| VA_ENC_PACKED_HEADER_PICTURE
},
72 media::VideoCodecProfile profile
;
76 // A map between VideoCodecProfile and VAProfile.
77 static const ProfileMap kProfileMap
[] = {
78 {media::H264PROFILE_BASELINE
, VAProfileH264Baseline
},
79 {media::H264PROFILE_MAIN
, VAProfileH264Main
},
80 // TODO(posciak): See if we can/want support other variants of
81 // media::H264PROFILE_HIGH*.
82 {media::H264PROFILE_HIGH
, VAProfileH264High
},
85 static std::vector
<VAConfigAttrib
> GetRequiredAttribs(
86 VaapiWrapper::CodecMode mode
) {
87 std::vector
<VAConfigAttrib
> required_attribs
;
88 required_attribs
.insert(
89 required_attribs
.end(),
90 kCommonVAConfigAttribs
,
91 kCommonVAConfigAttribs
+ arraysize(kCommonVAConfigAttribs
));
92 if (mode
== VaapiWrapper::kEncode
) {
93 required_attribs
.insert(
94 required_attribs
.end(),
95 kEncodeVAConfigAttribs
,
96 kEncodeVAConfigAttribs
+ arraysize(kEncodeVAConfigAttribs
));
98 return required_attribs
;
101 // Maps Profile enum values to VaProfile values.
102 static VAProfile
ProfileToVAProfile(
103 media::VideoCodecProfile profile
,
104 const std::vector
<VAProfile
>& supported_profiles
) {
106 VAProfile va_profile
= VAProfileNone
;
107 for (size_t i
= 0; i
< arraysize(kProfileMap
); i
++) {
108 if (kProfileMap
[i
].profile
== profile
) {
109 va_profile
= kProfileMap
[i
].va_profile
;
114 bool supported
= std::find(supported_profiles
.begin(),
115 supported_profiles
.end(),
116 va_profile
) != supported_profiles
.end();
118 if (!supported
&& va_profile
== VAProfileH264Baseline
) {
119 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips
120 // the information whether the profile is constrained or not, so we have no
121 // way to know here. Try for baseline first, but if it is not supported,
122 // try constrained baseline and hope this is what it actually is
123 // (which in practice is true for a great majority of cases).
124 if (std::find(supported_profiles
.begin(),
125 supported_profiles
.end(),
126 VAProfileH264ConstrainedBaseline
) !=
127 supported_profiles
.end()) {
128 va_profile
= VAProfileH264ConstrainedBaseline
;
129 DVLOG(1) << "Falling back to constrained baseline profile.";
136 VASurface::VASurface(VASurfaceID va_surface_id
,
137 const gfx::Size
& size
,
138 const ReleaseCB
& release_cb
)
139 : va_surface_id_(va_surface_id
), size_(size
), release_cb_(release_cb
) {
140 DCHECK(!release_cb_
.is_null());
143 VASurface::~VASurface() {
144 release_cb_
.Run(va_surface_id_
);
147 VaapiWrapper::VaapiWrapper()
149 va_config_id_(VA_INVALID_ID
),
150 va_context_id_(VA_INVALID_ID
),
151 va_initialized_(false),
152 va_vpp_config_id_(VA_INVALID_ID
),
153 va_vpp_context_id_(VA_INVALID_ID
),
154 va_vpp_buffer_id_(VA_INVALID_ID
) {
157 VaapiWrapper::~VaapiWrapper() {
158 DestroyPendingBuffers();
159 DestroyCodedBuffers();
165 scoped_ptr
<VaapiWrapper
> VaapiWrapper::Create(
167 VAProfile va_profile
,
168 const base::Closure
& report_error_to_uma_cb
) {
169 scoped_ptr
<VaapiWrapper
> vaapi_wrapper(new VaapiWrapper());
171 if (!vaapi_wrapper
->VaInitialize(report_error_to_uma_cb
))
173 if (!vaapi_wrapper
->Initialize(mode
, va_profile
))
176 return vaapi_wrapper
.Pass();
179 scoped_ptr
<VaapiWrapper
> VaapiWrapper::CreateForVideoCodec(
181 media::VideoCodecProfile profile
,
182 const base::Closure
& report_error_to_uma_cb
) {
183 scoped_ptr
<VaapiWrapper
> vaapi_wrapper(new VaapiWrapper());
185 if (!vaapi_wrapper
->VaInitialize(report_error_to_uma_cb
))
188 std::vector
<VAProfile
> supported_va_profiles
;
189 if (!vaapi_wrapper
->GetSupportedVaProfiles(&supported_va_profiles
))
192 VAProfile va_profile
= ProfileToVAProfile(profile
, supported_va_profiles
);
193 if (!vaapi_wrapper
->Initialize(mode
, va_profile
))
196 return vaapi_wrapper
.Pass();
199 std::vector
<media::VideoCodecProfile
> VaapiWrapper::GetSupportedEncodeProfiles(
200 const base::Closure
& report_error_to_uma_cb
) {
201 std::vector
<media::VideoCodecProfile
> supported_profiles
;
203 scoped_ptr
<VaapiWrapper
> wrapper(new VaapiWrapper());
204 if (!wrapper
->VaInitialize(report_error_to_uma_cb
)) {
205 return supported_profiles
;
208 std::vector
<VAProfile
> va_profiles
;
209 if (!wrapper
->GetSupportedVaProfiles(&va_profiles
))
210 return supported_profiles
;
212 std::vector
<VAConfigAttrib
> required_attribs
= GetRequiredAttribs(kEncode
);
213 for (size_t i
= 0; i
< arraysize(kProfileMap
); i
++) {
214 VAProfile va_profile
=
215 ProfileToVAProfile(kProfileMap
[i
].profile
, va_profiles
);
216 if (va_profile
!= VAProfileNone
&&
217 wrapper
->IsEntrypointSupported(va_profile
, VAEntrypointEncSlice
) &&
218 wrapper
->AreAttribsSupported(
219 va_profile
, VAEntrypointEncSlice
, required_attribs
)) {
220 supported_profiles
.push_back(kProfileMap
[i
].profile
);
223 return supported_profiles
;
226 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() {
227 base::AutoLock
auto_lock(va_lock_
);
228 VADisplayAttribute item
= {VADisplayAttribRenderMode
,
229 1, // At least support '_LOCAL_OVERLAY'.
230 -1, // The maximum possible support 'ALL'.
231 VA_RENDER_MODE_LOCAL_GPU
,
232 VA_DISPLAY_ATTRIB_SETTABLE
};
234 VAStatus va_res
= vaSetDisplayAttributes(va_display_
, &item
, 1);
235 if (va_res
!= VA_STATUS_SUCCESS
)
236 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default.";
239 bool VaapiWrapper::VaInitialize(const base::Closure
& report_error_to_uma_cb
) {
240 static bool vaapi_functions_initialized
= PostSandboxInitialization();
241 if (!vaapi_functions_initialized
) {
242 bool running_on_chromeos
= false;
243 #if defined(OS_CHROMEOS)
244 // When chrome runs on linux with chromeos=1, do not log error message
245 // without VAAPI libraries.
246 running_on_chromeos
= base::SysInfo::IsRunningOnChromeOS();
248 static const char kErrorMsg
[] = "Failed to initialize VAAPI libs";
249 if (running_on_chromeos
)
250 LOG(ERROR
) << kErrorMsg
;
252 DVLOG(1) << kErrorMsg
;
256 report_error_to_uma_cb_
= report_error_to_uma_cb
;
258 base::AutoLock
auto_lock(va_lock_
);
261 va_display_
= vaGetDisplay(gfx::GetXDisplay());
262 #elif defined(USE_OZONE)
263 ui::OzonePlatform
* platform
= ui::OzonePlatform::GetInstance();
264 ui::SurfaceFactoryOzone
* factory
= platform
->GetSurfaceFactoryOzone();
266 va_display_
= vaGetDisplayDRM(factory
->GetDrmFd());
269 if (!vaDisplayIsValid(va_display_
)) {
270 LOG(ERROR
) << "Could not get a valid VA display";
274 VAStatus va_res
= vaInitialize(va_display_
, &major_version_
, &minor_version_
);
275 VA_SUCCESS_OR_RETURN(va_res
, "vaInitialize failed", false);
276 va_initialized_
= true;
277 DVLOG(1) << "VAAPI version: " << major_version_
<< "." << minor_version_
;
279 if (VAAPIVersionLessThan(0, 34)) {
280 LOG(ERROR
) << "VAAPI version < 0.34 is not supported.";
286 bool VaapiWrapper::GetSupportedVaProfiles(std::vector
<VAProfile
>* profiles
) {
287 base::AutoLock
auto_lock(va_lock_
);
288 // Query the driver for supported profiles.
289 int max_profiles
= vaMaxNumProfiles(va_display_
);
290 std::vector
<VAProfile
> supported_profiles(
291 base::checked_cast
<size_t>(max_profiles
));
293 int num_supported_profiles
;
294 VAStatus va_res
= vaQueryConfigProfiles(
295 va_display_
, &supported_profiles
[0], &num_supported_profiles
);
296 VA_SUCCESS_OR_RETURN(va_res
, "vaQueryConfigProfiles failed", false);
297 if (num_supported_profiles
< 0 || num_supported_profiles
> max_profiles
) {
298 LOG(ERROR
) << "vaQueryConfigProfiles returned: " << num_supported_profiles
;
302 supported_profiles
.resize(base::checked_cast
<size_t>(num_supported_profiles
));
303 *profiles
= supported_profiles
;
307 bool VaapiWrapper::IsEntrypointSupported(VAProfile va_profile
,
308 VAEntrypoint entrypoint
) {
309 base::AutoLock
auto_lock(va_lock_
);
310 // Query the driver for supported entrypoints.
311 int max_entrypoints
= vaMaxNumEntrypoints(va_display_
);
312 std::vector
<VAEntrypoint
> supported_entrypoints(
313 base::checked_cast
<size_t>(max_entrypoints
));
315 int num_supported_entrypoints
;
316 VAStatus va_res
= vaQueryConfigEntrypoints(va_display_
,
318 &supported_entrypoints
[0],
319 &num_supported_entrypoints
);
320 VA_SUCCESS_OR_RETURN(va_res
, "vaQueryConfigEntrypoints failed", false);
321 if (num_supported_entrypoints
< 0 ||
322 num_supported_entrypoints
> max_entrypoints
) {
323 LOG(ERROR
) << "vaQueryConfigEntrypoints returned: "
324 << num_supported_entrypoints
;
328 if (std::find(supported_entrypoints
.begin(),
329 supported_entrypoints
.end(),
330 entrypoint
) == supported_entrypoints
.end()) {
331 DVLOG(1) << "Unsupported entrypoint";
337 bool VaapiWrapper::AreAttribsSupported(
338 VAProfile va_profile
,
339 VAEntrypoint entrypoint
,
340 const std::vector
<VAConfigAttrib
>& required_attribs
) {
341 base::AutoLock
auto_lock(va_lock_
);
342 // Query the driver for required attributes.
343 std::vector
<VAConfigAttrib
> attribs
= required_attribs
;
344 for (size_t i
= 0; i
< required_attribs
.size(); ++i
)
345 attribs
[i
].value
= 0;
347 VAStatus va_res
= vaGetConfigAttributes(
348 va_display_
, va_profile
, entrypoint
, &attribs
[0], attribs
.size());
349 VA_SUCCESS_OR_RETURN(va_res
, "vaGetConfigAttributes failed", false);
351 for (size_t i
= 0; i
< required_attribs
.size(); ++i
) {
352 if (attribs
[i
].type
!= required_attribs
[i
].type
||
353 (attribs
[i
].value
& required_attribs
[i
].value
) !=
354 required_attribs
[i
].value
) {
355 DVLOG(1) << "Unsupported value " << required_attribs
[i
].value
356 << " for attribute type " << required_attribs
[i
].type
;
363 bool VaapiWrapper::Initialize(CodecMode mode
, VAProfile va_profile
) {
364 if (va_profile
== VAProfileNone
) {
365 DVLOG(1) << "Unsupported profile";
368 VAEntrypoint entrypoint
=
369 (mode
== kEncode
? VAEntrypointEncSlice
: VAEntrypointVLD
);
370 if (!IsEntrypointSupported(va_profile
, entrypoint
))
372 std::vector
<VAConfigAttrib
> required_attribs
= GetRequiredAttribs(mode
);
373 if (!AreAttribsSupported(va_profile
, entrypoint
, required_attribs
))
376 TryToSetVADisplayAttributeToLocalGPU();
378 base::AutoLock
auto_lock(va_lock_
);
379 VAStatus va_res
= vaCreateConfig(va_display_
,
382 &required_attribs
[0],
383 required_attribs
.size(),
385 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateConfig failed", false);
390 void VaapiWrapper::Deinitialize() {
391 base::AutoLock
auto_lock(va_lock_
);
393 if (va_config_id_
!= VA_INVALID_ID
) {
394 VAStatus va_res
= vaDestroyConfig(va_display_
, va_config_id_
);
395 VA_LOG_ON_ERROR(va_res
, "vaDestroyConfig failed");
398 // Must check if vaInitialize completed successfully, to work around a bug in
399 // libva. The bug was fixed upstream:
400 // http://lists.freedesktop.org/archives/libva/2013-July/001807.html
401 // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once
402 // the fix has rolled out sufficiently.
403 if (va_initialized_
&& va_display_
) {
404 VAStatus va_res
= vaTerminate(va_display_
);
405 VA_LOG_ON_ERROR(va_res
, "vaTerminate failed");
408 va_config_id_
= VA_INVALID_ID
;
410 va_initialized_
= false;
413 bool VaapiWrapper::VAAPIVersionLessThan(int major
, int minor
) {
414 return (major_version_
< major
) ||
415 (major_version_
== major
&& minor_version_
< minor
);
418 bool VaapiWrapper::CreateSurfaces(const gfx::Size
& size
,
420 std::vector
<VASurfaceID
>* va_surfaces
) {
421 base::AutoLock
auto_lock(va_lock_
);
422 DVLOG(2) << "Creating " << num_surfaces
<< " surfaces";
424 DCHECK(va_surfaces
->empty());
425 DCHECK(va_surface_ids_
.empty());
426 va_surface_ids_
.resize(num_surfaces
);
428 // Allocate surfaces in driver.
429 VAStatus va_res
= vaCreateSurfaces(va_display_
,
431 size
.width(), size
.height(),
433 va_surface_ids_
.size(),
436 VA_LOG_ON_ERROR(va_res
, "vaCreateSurfaces failed");
437 if (va_res
!= VA_STATUS_SUCCESS
) {
438 va_surface_ids_
.clear();
442 // And create a context associated with them.
443 va_res
= vaCreateContext(va_display_
, va_config_id_
,
444 size
.width(), size
.height(), VA_PROGRESSIVE
,
445 &va_surface_ids_
[0], va_surface_ids_
.size(),
448 VA_LOG_ON_ERROR(va_res
, "vaCreateContext failed");
449 if (va_res
!= VA_STATUS_SUCCESS
) {
454 *va_surfaces
= va_surface_ids_
;
458 void VaapiWrapper::DestroySurfaces() {
459 base::AutoLock
auto_lock(va_lock_
);
460 DVLOG(2) << "Destroying " << va_surface_ids_
.size() << " surfaces";
462 if (va_context_id_
!= VA_INVALID_ID
) {
463 VAStatus va_res
= vaDestroyContext(va_display_
, va_context_id_
);
464 VA_LOG_ON_ERROR(va_res
, "vaDestroyContext failed");
467 if (!va_surface_ids_
.empty()) {
468 VAStatus va_res
= vaDestroySurfaces(va_display_
, &va_surface_ids_
[0],
469 va_surface_ids_
.size());
470 VA_LOG_ON_ERROR(va_res
, "vaDestroySurfaces failed");
473 va_surface_ids_
.clear();
474 va_context_id_
= VA_INVALID_ID
;
477 scoped_refptr
<VASurface
> VaapiWrapper::CreateUnownedSurface(
478 unsigned int va_format
,
479 const gfx::Size
& size
,
480 const std::vector
<VASurfaceAttrib
>& va_attribs
) {
481 base::AutoLock
auto_lock(va_lock_
);
483 std::vector
<VASurfaceAttrib
> attribs(va_attribs
);
484 VASurfaceID va_surface_id
;
486 vaCreateSurfaces(va_display_
, va_format
, size
.width(), size
.height(),
487 &va_surface_id
, 1, &attribs
[0], attribs
.size());
489 scoped_refptr
<VASurface
> va_surface
;
490 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create unowned VASurface",
493 // This is safe to use Unretained() here, because the VDA takes care
494 // of the destruction order. All the surfaces will be destroyed
495 // before VaapiWrapper.
496 va_surface
= new VASurface(
498 base::Bind(&VaapiWrapper::DestroyUnownedSurface
, base::Unretained(this)));
503 void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id
) {
504 base::AutoLock
auto_lock(va_lock_
);
506 VAStatus va_res
= vaDestroySurfaces(va_display_
, &va_surface_id
, 1);
507 VA_LOG_ON_ERROR(va_res
, "vaDestroySurfaces on surface failed");
510 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type
,
513 base::AutoLock
auto_lock(va_lock_
);
515 VABufferID buffer_id
;
516 VAStatus va_res
= vaCreateBuffer(va_display_
, va_context_id_
,
517 va_buffer_type
, size
,
518 1, buffer
, &buffer_id
);
519 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a VA buffer", false);
521 switch (va_buffer_type
) {
522 case VASliceParameterBufferType
:
523 case VASliceDataBufferType
:
524 case VAEncSliceParameterBufferType
:
525 pending_slice_bufs_
.push_back(buffer_id
);
529 pending_va_bufs_
.push_back(buffer_id
);
536 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
537 VAEncMiscParameterType misc_param_type
,
540 base::AutoLock
auto_lock(va_lock_
);
542 VABufferID buffer_id
;
543 VAStatus va_res
= vaCreateBuffer(va_display_
,
545 VAEncMiscParameterBufferType
,
546 sizeof(VAEncMiscParameterBuffer
) + size
,
550 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a VA buffer", false);
552 void* data_ptr
= NULL
;
553 va_res
= vaMapBuffer(va_display_
, buffer_id
, &data_ptr
);
554 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
555 if (va_res
!= VA_STATUS_SUCCESS
) {
556 vaDestroyBuffer(va_display_
, buffer_id
);
562 VAEncMiscParameterBuffer
* misc_param
=
563 reinterpret_cast<VAEncMiscParameterBuffer
*>(data_ptr
);
564 misc_param
->type
= misc_param_type
;
565 memcpy(misc_param
->data
, buffer
, size
);
566 va_res
= vaUnmapBuffer(va_display_
, buffer_id
);
567 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
569 pending_va_bufs_
.push_back(buffer_id
);
573 void VaapiWrapper::DestroyPendingBuffers() {
574 base::AutoLock
auto_lock(va_lock_
);
576 for (size_t i
= 0; i
< pending_va_bufs_
.size(); ++i
) {
577 VAStatus va_res
= vaDestroyBuffer(va_display_
, pending_va_bufs_
[i
]);
578 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
581 for (size_t i
= 0; i
< pending_slice_bufs_
.size(); ++i
) {
582 VAStatus va_res
= vaDestroyBuffer(va_display_
, pending_slice_bufs_
[i
]);
583 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
586 pending_va_bufs_
.clear();
587 pending_slice_bufs_
.clear();
590 bool VaapiWrapper::CreateCodedBuffer(size_t size
, VABufferID
* buffer_id
) {
591 base::AutoLock
auto_lock(va_lock_
);
592 VAStatus va_res
= vaCreateBuffer(va_display_
,
594 VAEncCodedBufferType
,
599 VA_SUCCESS_OR_RETURN(va_res
, "Failed to create a coded buffer", false);
601 DCHECK(coded_buffers_
.insert(*buffer_id
).second
);
605 void VaapiWrapper::DestroyCodedBuffers() {
606 base::AutoLock
auto_lock(va_lock_
);
608 for (std::set
<VABufferID
>::const_iterator iter
= coded_buffers_
.begin();
609 iter
!= coded_buffers_
.end();
611 VAStatus va_res
= vaDestroyBuffer(va_display_
, *iter
);
612 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
615 coded_buffers_
.clear();
618 bool VaapiWrapper::Execute(VASurfaceID va_surface_id
) {
619 base::AutoLock
auto_lock(va_lock_
);
621 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_
.size();
622 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_
.size();
623 DVLOG(4) << "Target VA surface " << va_surface_id
;
625 // Get ready to execute for given surface.
626 VAStatus va_res
= vaBeginPicture(va_display_
, va_context_id_
,
628 VA_SUCCESS_OR_RETURN(va_res
, "vaBeginPicture failed", false);
630 if (pending_va_bufs_
.size() > 0) {
631 // Commit parameter and slice buffers.
632 va_res
= vaRenderPicture(va_display_
,
634 &pending_va_bufs_
[0],
635 pending_va_bufs_
.size());
636 VA_SUCCESS_OR_RETURN(va_res
, "vaRenderPicture for va_bufs failed", false);
639 if (pending_slice_bufs_
.size() > 0) {
640 va_res
= vaRenderPicture(va_display_
,
642 &pending_slice_bufs_
[0],
643 pending_slice_bufs_
.size());
644 VA_SUCCESS_OR_RETURN(va_res
, "vaRenderPicture for slices failed", false);
647 // Instruct HW codec to start processing committed buffers.
648 // Does not block and the job is not finished after this returns.
649 va_res
= vaEndPicture(va_display_
, va_context_id_
);
650 VA_SUCCESS_OR_RETURN(va_res
, "vaEndPicture failed", false);
655 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id
) {
656 bool result
= Execute(va_surface_id
);
657 DestroyPendingBuffers();
662 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id
,
664 gfx::Size dest_size
) {
665 base::AutoLock
auto_lock(va_lock_
);
667 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
668 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
670 // Put the data into an X Pixmap.
671 va_res
= vaPutSurface(va_display_
,
674 0, 0, dest_size
.width(), dest_size
.height(),
675 0, 0, dest_size
.width(), dest_size
.height(),
677 VA_SUCCESS_OR_RETURN(va_res
, "Failed putting surface to pixmap", false);
682 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id
,
685 base::AutoLock
auto_lock(va_lock_
);
687 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
688 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
690 // Derive a VAImage from the VASurface
691 va_res
= vaDeriveImage(va_display_
, va_surface_id
, image
);
692 VA_LOG_ON_ERROR(va_res
, "vaDeriveImage failed");
693 if (va_res
!= VA_STATUS_SUCCESS
)
696 // Map the VAImage into memory
697 va_res
= vaMapBuffer(va_display_
, image
->buf
, mem
);
698 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
699 if (va_res
== VA_STATUS_SUCCESS
)
702 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
703 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
708 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id
,
709 VAImageFormat
* format
,
710 const gfx::Size
& size
,
713 base::AutoLock
auto_lock(va_lock_
);
715 VAStatus va_res
= vaSyncSurface(va_display_
, va_surface_id
);
716 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
719 vaCreateImage(va_display_
, format
, size
.width(), size
.height(), image
);
720 VA_SUCCESS_OR_RETURN(va_res
, "vaCreateImage failed", false);
722 va_res
= vaGetImage(va_display_
, va_surface_id
, 0, 0, size
.width(),
723 size
.height(), image
->image_id
);
724 VA_LOG_ON_ERROR(va_res
, "vaGetImage failed");
726 if (va_res
== VA_STATUS_SUCCESS
) {
727 // Map the VAImage into memory
728 va_res
= vaMapBuffer(va_display_
, image
->buf
, mem
);
729 VA_LOG_ON_ERROR(va_res
, "vaMapBuffer failed");
732 if (va_res
!= VA_STATUS_SUCCESS
) {
733 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
734 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
741 void VaapiWrapper::ReturnVaImage(VAImage
* image
) {
742 base::AutoLock
auto_lock(va_lock_
);
744 VAStatus va_res
= vaUnmapBuffer(va_display_
, image
->buf
);
745 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
747 va_res
= vaDestroyImage(va_display_
, image
->image_id
);
748 VA_LOG_ON_ERROR(va_res
, "vaDestroyImage failed");
751 static void DestroyVAImage(VADisplay va_display
, VAImage image
) {
752 if (image
.image_id
!= VA_INVALID_ID
)
753 vaDestroyImage(va_display
, image
.image_id
);
756 bool VaapiWrapper::UploadVideoFrameToSurface(
757 const scoped_refptr
<media::VideoFrame
>& frame
,
758 VASurfaceID va_surface_id
) {
759 base::AutoLock
auto_lock(va_lock_
);
762 VAStatus va_res
= vaDeriveImage(va_display_
, va_surface_id
, &image
);
763 VA_SUCCESS_OR_RETURN(va_res
, "vaDeriveImage failed", false);
764 base::ScopedClosureRunner
vaimage_deleter(
765 base::Bind(&DestroyVAImage
, va_display_
, image
));
767 if (image
.format
.fourcc
!= VA_FOURCC_NV12
) {
768 LOG(ERROR
) << "Unsupported image format: " << image
.format
.fourcc
;
772 if (gfx::Rect(image
.width
, image
.height
) < gfx::Rect(frame
->coded_size())) {
773 LOG(ERROR
) << "Buffer too small to fit the frame.";
777 void* image_ptr
= NULL
;
778 va_res
= vaMapBuffer(va_display_
, image
.buf
, &image_ptr
);
779 VA_SUCCESS_OR_RETURN(va_res
, "vaMapBuffer failed", false);
784 base::AutoUnlock
auto_unlock(va_lock_
);
785 ret
= libyuv::I420ToNV12(frame
->data(media::VideoFrame::kYPlane
),
786 frame
->stride(media::VideoFrame::kYPlane
),
787 frame
->data(media::VideoFrame::kUPlane
),
788 frame
->stride(media::VideoFrame::kUPlane
),
789 frame
->data(media::VideoFrame::kVPlane
),
790 frame
->stride(media::VideoFrame::kVPlane
),
791 static_cast<uint8
*>(image_ptr
) + image
.offsets
[0],
793 static_cast<uint8
*>(image_ptr
) + image
.offsets
[1],
799 va_res
= vaUnmapBuffer(va_display_
, image
.buf
);
800 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
805 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id
,
806 VASurfaceID sync_surface_id
,
809 size_t* coded_data_size
) {
810 base::AutoLock
auto_lock(va_lock_
);
812 VAStatus va_res
= vaSyncSurface(va_display_
, sync_surface_id
);
813 VA_SUCCESS_OR_RETURN(va_res
, "Failed syncing surface", false);
815 VACodedBufferSegment
* buffer_segment
= NULL
;
816 va_res
= vaMapBuffer(
817 va_display_
, buffer_id
, reinterpret_cast<void**>(&buffer_segment
));
818 VA_SUCCESS_OR_RETURN(va_res
, "vaMapBuffer failed", false);
822 base::AutoUnlock
auto_unlock(va_lock_
);
823 *coded_data_size
= 0;
825 while (buffer_segment
) {
826 DCHECK(buffer_segment
->buf
);
828 if (buffer_segment
->size
> target_size
) {
829 LOG(ERROR
) << "Insufficient output buffer size";
833 memcpy(target_ptr
, buffer_segment
->buf
, buffer_segment
->size
);
835 target_ptr
+= buffer_segment
->size
;
836 *coded_data_size
+= buffer_segment
->size
;
837 target_size
-= buffer_segment
->size
;
840 reinterpret_cast<VACodedBufferSegment
*>(buffer_segment
->next
);
844 va_res
= vaUnmapBuffer(va_display_
, buffer_id
);
845 VA_LOG_ON_ERROR(va_res
, "vaUnmapBuffer failed");
847 va_res
= vaDestroyBuffer(va_display_
, buffer_id
);
848 VA_LOG_ON_ERROR(va_res
, "vaDestroyBuffer failed");
850 DCHECK(coded_buffers_
.erase(buffer_id
));
852 return buffer_segment
== NULL
;
855 bool VaapiWrapper::BlitSurface(VASurfaceID va_surface_id_src
,
856 const gfx::Size
& src_size
,
857 VASurfaceID va_surface_id_dest
,
858 const gfx::Size
& dest_size
) {
859 base::AutoLock
auto_lock(va_lock_
);
861 // Initialize the post processing engine if not already done.
862 if (va_vpp_buffer_id_
== VA_INVALID_ID
) {
863 if (!InitializeVpp_Locked())
867 VAProcPipelineParameterBuffer
* pipeline_param
;
868 VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_
, va_vpp_buffer_id_
,
869 reinterpret_cast<void**>(&pipeline_param
)),
870 "Couldn't map vpp buffer", false);
872 memset(pipeline_param
, 0, sizeof *pipeline_param
);
874 VARectangle input_region
;
875 input_region
.x
= input_region
.y
= 0;
876 input_region
.width
= src_size
.width();
877 input_region
.height
= src_size
.height();
878 pipeline_param
->surface_region
= &input_region
;
879 pipeline_param
->surface
= va_surface_id_src
;
880 pipeline_param
->surface_color_standard
= VAProcColorStandardNone
;
882 VARectangle output_region
;
883 output_region
.x
= output_region
.y
= 0;
884 output_region
.width
= dest_size
.width();
885 output_region
.height
= dest_size
.height();
886 pipeline_param
->output_region
= &output_region
;
887 pipeline_param
->output_background_color
= 0xff000000;
888 pipeline_param
->output_color_standard
= VAProcColorStandardNone
;
890 VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_
, va_vpp_buffer_id_
),
891 "Couldn't unmap vpp buffer", false);
893 VA_SUCCESS_OR_RETURN(
894 vaBeginPicture(va_display_
, va_vpp_context_id_
, va_surface_id_dest
),
895 "Couldn't begin picture", false);
897 VA_SUCCESS_OR_RETURN(
898 vaRenderPicture(va_display_
, va_vpp_context_id_
, &va_vpp_buffer_id_
, 1),
899 "Couldn't render picture", false);
901 VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_
, va_vpp_context_id_
),
902 "Couldn't end picture", false);
907 bool VaapiWrapper::InitializeVpp_Locked() {
908 va_lock_
.AssertAcquired();
910 VA_SUCCESS_OR_RETURN(
911 vaCreateConfig(va_display_
, VAProfileNone
, VAEntrypointVideoProc
, NULL
, 0,
913 "Couldn't create config", false);
915 // The size of the picture for the context is irrelevant in the case
916 // of the VPP, just passing 1x1.
917 VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_
, va_vpp_config_id_
, 1, 1, 0,
918 NULL
, 0, &va_vpp_context_id_
),
919 "Couldn't create context", false);
921 VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_
, va_vpp_context_id_
,
922 VAProcPipelineParameterBufferType
,
923 sizeof(VAProcPipelineParameterBuffer
), 1,
924 NULL
, &va_vpp_buffer_id_
),
925 "Couldn't create buffer", false);
930 void VaapiWrapper::DeinitializeVpp() {
931 base::AutoLock
auto_lock(va_lock_
);
933 if (va_vpp_buffer_id_
!= VA_INVALID_ID
) {
934 vaDestroyBuffer(va_display_
, va_vpp_buffer_id_
);
935 va_vpp_buffer_id_
= VA_INVALID_ID
;
937 if (va_vpp_context_id_
!= VA_INVALID_ID
) {
938 vaDestroyContext(va_display_
, va_vpp_context_id_
);
939 va_vpp_context_id_
= VA_INVALID_ID
;
941 if (va_vpp_config_id_
!= VA_INVALID_ID
) {
942 vaDestroyConfig(va_display_
, va_vpp_config_id_
);
943 va_vpp_config_id_
= VA_INVALID_ID
;
948 bool VaapiWrapper::PostSandboxInitialization() {
951 paths
[kModuleVa
].push_back("libva.so.1");
954 paths
[kModuleVa_x11
].push_back("libva-x11.so.1");
955 #elif defined(USE_OZONE)
956 paths
[kModuleVa_drm
].push_back("libva-drm.so.1");
959 return InitializeStubs(paths
);
962 } // namespace content