ozone: evdev: Sync caps lock LED state to evdev
[chromium-blink-merge.git] / content / common / gpu / media / vaapi_wrapper.cc
blob588fee3981e59072c6118996099258acaa51e82b
1 // Copyright 2013 The Chromium Authors. All rights reserved.
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
5 #include "content/common/gpu/media/vaapi_wrapper.h"
7 #include <dlfcn.h>
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/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"
19 #if defined(USE_X11)
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"
25 #endif // USE_X11
27 using content_common_gpu_media::kModuleVa;
28 #if defined(USE_X11)
29 using content_common_gpu_media::kModuleVa_x11;
30 #elif defined(USE_OZONE)
31 using content_common_gpu_media::kModuleVa_drm;
32 #endif // USE_X11
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) \
37 do { \
38 LOG(ERROR) << err_msg \
39 << " VA error: " << vaErrorStr(va_error); \
40 report_error_to_uma_cb_.Run(); \
41 } while (0)
43 #define VA_LOG_ON_ERROR(va_error, err_msg) \
44 do { \
45 if ((va_error) != VA_STATUS_SUCCESS) \
46 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
47 } while (0)
49 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \
50 do { \
51 if ((va_error) != VA_STATUS_SUCCESS) { \
52 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \
53 return (ret); \
54 } \
55 } while (0)
57 namespace content {
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},
71 struct ProfileMap {
72 media::VideoCodecProfile profile;
73 VAProfile va_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;
110 break;
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.";
133 return va_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()
148 : va_display_(NULL),
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();
160 DestroySurfaces();
161 DeinitializeVpp();
162 Deinitialize();
165 scoped_ptr<VaapiWrapper> VaapiWrapper::Create(
166 CodecMode mode,
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))
172 return nullptr;
173 if (!vaapi_wrapper->Initialize(mode, va_profile))
174 return nullptr;
176 return vaapi_wrapper.Pass();
179 scoped_ptr<VaapiWrapper> VaapiWrapper::CreateForVideoCodec(
180 CodecMode mode,
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))
186 return nullptr;
188 std::vector<VAProfile> supported_va_profiles;
189 if (!vaapi_wrapper->GetSupportedVaProfiles(&supported_va_profiles))
190 return nullptr;
192 VAProfile va_profile = ProfileToVAProfile(profile, supported_va_profiles);
193 if (!vaapi_wrapper->Initialize(mode, va_profile))
194 return nullptr;
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();
247 #endif
248 static const char kErrorMsg[] = "Failed to initialize VAAPI libs";
249 if (running_on_chromeos)
250 LOG(ERROR) << kErrorMsg;
251 else
252 DVLOG(1) << kErrorMsg;
253 return false;
256 report_error_to_uma_cb_ = report_error_to_uma_cb;
258 base::AutoLock auto_lock(va_lock_);
260 #if defined(USE_X11)
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());
267 #endif // USE_X11
269 if (!vaDisplayIsValid(va_display_)) {
270 LOG(ERROR) << "Could not get a valid VA display";
271 return false;
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.";
281 return false;
283 return true;
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;
299 return false;
302 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles));
303 *profiles = supported_profiles;
304 return true;
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_,
317 va_profile,
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;
325 return false;
328 if (std::find(supported_entrypoints.begin(),
329 supported_entrypoints.end(),
330 entrypoint) == supported_entrypoints.end()) {
331 DVLOG(1) << "Unsupported entrypoint";
332 return false;
334 return true;
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;
357 return false;
360 return true;
363 bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) {
364 if (va_profile == VAProfileNone) {
365 DVLOG(1) << "Unsupported profile";
366 return false;
368 VAEntrypoint entrypoint =
369 (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD);
370 if (!IsEntrypointSupported(va_profile, entrypoint))
371 return false;
372 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode);
373 if (!AreAttribsSupported(va_profile, entrypoint, required_attribs))
374 return false;
376 TryToSetVADisplayAttributeToLocalGPU();
378 base::AutoLock auto_lock(va_lock_);
379 VAStatus va_res = vaCreateConfig(va_display_,
380 va_profile,
381 entrypoint,
382 &required_attribs[0],
383 required_attribs.size(),
384 &va_config_id_);
385 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false);
387 return true;
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;
409 va_display_ = NULL;
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,
419 size_t num_surfaces,
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_,
430 VA_RT_FORMAT_YUV420,
431 size.width(), size.height(),
432 &va_surface_ids_[0],
433 va_surface_ids_.size(),
434 NULL, 0);
436 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed");
437 if (va_res != VA_STATUS_SUCCESS) {
438 va_surface_ids_.clear();
439 return false;
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(),
446 &va_context_id_);
448 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed");
449 if (va_res != VA_STATUS_SUCCESS) {
450 DestroySurfaces();
451 return false;
454 *va_surfaces = va_surface_ids_;
455 return true;
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;
485 VAStatus va_res =
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",
491 va_surface);
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(
497 va_surface_id, size,
498 base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this)));
500 return va_surface;
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,
511 size_t size,
512 void* buffer) {
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);
526 break;
528 default:
529 pending_va_bufs_.push_back(buffer_id);
530 break;
533 return true;
536 bool VaapiWrapper::SubmitVAEncMiscParamBuffer(
537 VAEncMiscParameterType misc_param_type,
538 size_t size,
539 void* buffer) {
540 base::AutoLock auto_lock(va_lock_);
542 VABufferID buffer_id;
543 VAStatus va_res = vaCreateBuffer(va_display_,
544 va_context_id_,
545 VAEncMiscParameterBufferType,
546 sizeof(VAEncMiscParameterBuffer) + size,
548 NULL,
549 &buffer_id);
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);
557 return false;
560 DCHECK(data_ptr);
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);
570 return true;
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_,
593 va_context_id_,
594 VAEncCodedBufferType,
595 size,
597 NULL,
598 buffer_id);
599 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false);
601 DCHECK(coded_buffers_.insert(*buffer_id).second);
602 return true;
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();
610 ++iter) {
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_,
627 va_surface_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_,
633 va_context_id_,
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_,
641 va_context_id_,
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);
652 return true;
655 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) {
656 bool result = Execute(va_surface_id);
657 DestroyPendingBuffers();
658 return result;
661 #if defined(USE_X11)
662 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id,
663 Pixmap x_pixmap,
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_,
672 va_surface_id,
673 x_pixmap,
674 0, 0, dest_size.width(), dest_size.height(),
675 0, 0, dest_size.width(), dest_size.height(),
676 NULL, 0, 0);
677 VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false);
678 return true;
680 #endif // USE_X11
682 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id,
683 VAImage* image,
684 void** mem) {
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)
694 return false;
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)
700 return true;
702 va_res = vaDestroyImage(va_display_, image->image_id);
703 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed");
705 return false;
708 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id,
709 VAImageFormat* format,
710 const gfx::Size& size,
711 VAImage* image,
712 void** mem) {
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);
718 va_res =
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");
735 return false;
738 return true;
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_);
761 VAImage image;
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;
769 return false;
772 if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) {
773 LOG(ERROR) << "Buffer too small to fit the frame.";
774 return false;
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);
780 DCHECK(image_ptr);
782 int ret = 0;
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],
792 image.pitches[0],
793 static_cast<uint8*>(image_ptr) + image.offsets[1],
794 image.pitches[1],
795 image.width,
796 image.height);
799 va_res = vaUnmapBuffer(va_display_, image.buf);
800 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed");
802 return ret == 0;
805 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id,
806 VASurfaceID sync_surface_id,
807 uint8* target_ptr,
808 size_t target_size,
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);
819 DCHECK(target_ptr);
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";
830 break;
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;
839 buffer_segment =
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())
864 return false;
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);
904 return true;
907 bool VaapiWrapper::InitializeVpp_Locked() {
908 va_lock_.AssertAcquired();
910 VA_SUCCESS_OR_RETURN(
911 vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0,
912 &va_vpp_config_id_),
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);
927 return true;
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;
947 // static
948 bool VaapiWrapper::PostSandboxInitialization() {
949 StubPathMap paths;
951 paths[kModuleVa].push_back("libva.so.1");
953 #if defined(USE_X11)
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");
957 #endif
959 return InitializeStubs(paths);
962 } // namespace content