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. 4 5#include "content/common/gpu/media/vaapi_wrapper.h" 6 7#include <dlfcn.h> 8 9#include "base/bind.h" 10#include "base/callback_helpers.h" 11#include "base/logging.h" 12#include "base/numerics/safe_conversions.h" 13// Auto-generated for dlopen libva libraries 14#include "content/common/gpu/media/va_stubs.h" 15#include "third_party/libyuv/include/libyuv.h" 16 17using content_common_gpu_media::kModuleVa; 18using content_common_gpu_media::InitializeStubs; 19using content_common_gpu_media::StubPathMap; 20 21// libva-x11 depends on libva, so dlopen libva-x11 is enough 22static const base::FilePath::CharType kVaLib[] = 23 FILE_PATH_LITERAL("libva-x11.so.1"); 24 25#define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ 26 do { \ 27 DVLOG(1) << err_msg \ 28 << " VA error: " << vaErrorStr(va_error); \ 29 report_error_to_uma_cb_.Run(); \ 30 } while (0) 31 32#define VA_LOG_ON_ERROR(va_error, err_msg) \ 33 do { \ 34 if ((va_error) != VA_STATUS_SUCCESS) \ 35 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ 36 } while (0) 37 38#define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ 39 do { \ 40 if ((va_error) != VA_STATUS_SUCCESS) { \ 41 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ 42 return (ret); \ 43 } \ 44 } while (0) 45 46namespace content { 47 48// Config attributes common for both encode and decode. 49static const VAConfigAttrib kCommonVAConfigAttribs[] = { 50 {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, 51}; 52 53// Attributes required for encode. 54static const VAConfigAttrib kEncodeVAConfigAttribs[] = { 55 {VAConfigAttribRateControl, VA_RC_CBR}, 56 {VAConfigAttribEncPackedHeaders, 57 VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE}, 58}; 59 60struct ProfileMap { 61 media::VideoCodecProfile profile; 62 VAProfile va_profile; 63}; 64 65// A map between VideoCodecProfile and VAProfile. 66static const ProfileMap kProfileMap[] = { 67 {media::H264PROFILE_BASELINE, VAProfileH264Baseline}, 68 {media::H264PROFILE_MAIN, VAProfileH264Main}, 69 // TODO(posciak): See if we can/want support other variants of 70 // media::H264PROFILE_HIGH*. 71 {media::H264PROFILE_HIGH, VAProfileH264High}, 72}; 73 74static std::vector<VAConfigAttrib> GetRequiredAttribs( 75 VaapiWrapper::CodecMode mode) { 76 std::vector<VAConfigAttrib> required_attribs; 77 required_attribs.insert( 78 required_attribs.end(), 79 kCommonVAConfigAttribs, 80 kCommonVAConfigAttribs + arraysize(kCommonVAConfigAttribs)); 81 if (mode == VaapiWrapper::kEncode) { 82 required_attribs.insert( 83 required_attribs.end(), 84 kEncodeVAConfigAttribs, 85 kEncodeVAConfigAttribs + arraysize(kEncodeVAConfigAttribs)); 86 } 87 return required_attribs; 88} 89 90// Maps Profile enum values to VaProfile values. 91static VAProfile ProfileToVAProfile( 92 media::VideoCodecProfile profile, 93 const std::vector<VAProfile>& supported_profiles) { 94 95 VAProfile va_profile = VAProfileNone; 96 for (size_t i = 0; i < arraysize(kProfileMap); i++) { 97 if (kProfileMap[i].profile == profile) { 98 va_profile = kProfileMap[i].va_profile; 99 break; 100 } 101 } 102 103 bool supported = std::find(supported_profiles.begin(), 104 supported_profiles.end(), 105 va_profile) != supported_profiles.end(); 106 107 if (!supported && va_profile == VAProfileH264Baseline) { 108 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips 109 // the information whether the profile is constrained or not, so we have no 110 // way to know here. Try for baseline first, but if it is not supported, 111 // try constrained baseline and hope this is what it actually is 112 // (which in practice is true for a great majority of cases). 113 if (std::find(supported_profiles.begin(), 114 supported_profiles.end(), 115 VAProfileH264ConstrainedBaseline) != 116 supported_profiles.end()) { 117 va_profile = VAProfileH264ConstrainedBaseline; 118 DVLOG(1) << "Falling back to constrained baseline profile."; 119 } 120 } 121 122 return va_profile; 123} 124 125VASurface::VASurface(VASurfaceID va_surface_id, const ReleaseCB& release_cb) 126 : va_surface_id_(va_surface_id), 127 release_cb_(release_cb) { 128 DCHECK(!release_cb_.is_null()); 129} 130 131VASurface::~VASurface() { 132 release_cb_.Run(va_surface_id_); 133} 134 135VaapiWrapper::VaapiWrapper() 136 : va_display_(NULL), 137 va_config_id_(VA_INVALID_ID), 138 va_context_id_(VA_INVALID_ID) { 139} 140 141VaapiWrapper::~VaapiWrapper() { 142 DestroyPendingBuffers(); 143 DestroyCodedBuffers(); 144 DestroySurfaces(); 145 Deinitialize(); 146} 147 148scoped_ptr<VaapiWrapper> VaapiWrapper::Create( 149 CodecMode mode, 150 media::VideoCodecProfile profile, 151 Display* x_display, 152 const base::Closure& report_error_to_uma_cb) { 153 scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper()); 154 155 if (!vaapi_wrapper->Initialize( 156 mode, profile, x_display, report_error_to_uma_cb)) 157 vaapi_wrapper.reset(); 158 159 return vaapi_wrapper.Pass(); 160} 161 162std::vector<media::VideoCodecProfile> VaapiWrapper::GetSupportedEncodeProfiles( 163 Display* x_display, 164 const base::Closure& report_error_to_uma_cb) { 165 std::vector<media::VideoCodecProfile> supported_profiles; 166 167 scoped_ptr<VaapiWrapper> wrapper(new VaapiWrapper()); 168 if (!wrapper->VaInitialize(x_display, report_error_to_uma_cb)) { 169 return supported_profiles; 170 } 171 172 std::vector<VAProfile> va_profiles; 173 if (!wrapper->GetSupportedVaProfiles(&va_profiles)) 174 return supported_profiles; 175 176 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(kEncode); 177 for (size_t i = 0; i < arraysize(kProfileMap); i++) { 178 VAProfile va_profile = 179 ProfileToVAProfile(kProfileMap[i].profile, va_profiles); 180 if (va_profile != VAProfileNone && 181 wrapper->IsEntrypointSupported(va_profile, VAEntrypointEncSlice) && 182 wrapper->AreAttribsSupported( 183 va_profile, VAEntrypointEncSlice, required_attribs)) { 184 supported_profiles.push_back(kProfileMap[i].profile); 185 } 186 } 187 return supported_profiles; 188} 189 190void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { 191 base::AutoLock auto_lock(va_lock_); 192 VADisplayAttribute item = {VADisplayAttribRenderMode, 193 1, // At least support '_LOCAL_OVERLAY'. 194 -1, // The maximum possible support 'ALL'. 195 VA_RENDER_MODE_LOCAL_GPU, 196 VA_DISPLAY_ATTRIB_SETTABLE}; 197 198 VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1); 199 if (va_res != VA_STATUS_SUCCESS) 200 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; 201} 202 203bool VaapiWrapper::VaInitialize(Display* x_display, 204 const base::Closure& report_error_to_uma_cb) { 205 static bool vaapi_functions_initialized = PostSandboxInitialization(); 206 if (!vaapi_functions_initialized) { 207 DVLOG(1) << "Failed to initialize VAAPI libs"; 208 return false; 209 } 210 211 report_error_to_uma_cb_ = report_error_to_uma_cb; 212 213 base::AutoLock auto_lock(va_lock_); 214 215 va_display_ = vaGetDisplay(x_display); 216 if (!vaDisplayIsValid(va_display_)) { 217 DVLOG(1) << "Could not get a valid VA display"; 218 return false; 219 } 220 221 VAStatus va_res = vaInitialize(va_display_, &major_version_, &minor_version_); 222 VA_SUCCESS_OR_RETURN(va_res, "vaInitialize failed", false); 223 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_; 224 225 if (VAAPIVersionLessThan(0, 34)) { 226 DVLOG(1) << "VAAPI version < 0.34 is not supported."; 227 return false; 228 } 229 return true; 230} 231 232bool VaapiWrapper::GetSupportedVaProfiles(std::vector<VAProfile>* profiles) { 233 base::AutoLock auto_lock(va_lock_); 234 // Query the driver for supported profiles. 235 int max_profiles = vaMaxNumProfiles(va_display_); 236 std::vector<VAProfile> supported_profiles( 237 base::checked_cast<size_t>(max_profiles)); 238 239 int num_supported_profiles; 240 VAStatus va_res = vaQueryConfigProfiles( 241 va_display_, &supported_profiles[0], &num_supported_profiles); 242 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false); 243 if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) { 244 DVLOG(1) << "vaQueryConfigProfiles returned: " << num_supported_profiles; 245 return false; 246 } 247 248 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles)); 249 *profiles = supported_profiles; 250 return true; 251} 252 253bool VaapiWrapper::IsEntrypointSupported(VAProfile va_profile, 254 VAEntrypoint entrypoint) { 255 base::AutoLock auto_lock(va_lock_); 256 // Query the driver for supported entrypoints. 257 int max_entrypoints = vaMaxNumEntrypoints(va_display_); 258 std::vector<VAEntrypoint> supported_entrypoints( 259 base::checked_cast<size_t>(max_entrypoints)); 260 261 int num_supported_entrypoints; 262 VAStatus va_res = vaQueryConfigEntrypoints(va_display_, 263 va_profile, 264 &supported_entrypoints[0], 265 &num_supported_entrypoints); 266 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false); 267 if (num_supported_entrypoints < 0 || 268 num_supported_entrypoints > max_entrypoints) { 269 DVLOG(1) << "vaQueryConfigEntrypoints returned: " 270 << num_supported_entrypoints; 271 return false; 272 } 273 274 if (std::find(supported_entrypoints.begin(), 275 supported_entrypoints.end(), 276 entrypoint) == supported_entrypoints.end()) { 277 DVLOG(1) << "Unsupported entrypoint"; 278 return false; 279 } 280 return true; 281} 282 283bool VaapiWrapper::AreAttribsSupported( 284 VAProfile va_profile, 285 VAEntrypoint entrypoint, 286 const std::vector<VAConfigAttrib>& required_attribs) { 287 base::AutoLock auto_lock(va_lock_); 288 // Query the driver for required attributes. 289 std::vector<VAConfigAttrib> attribs = required_attribs; 290 for (size_t i = 0; i < required_attribs.size(); ++i) 291 attribs[i].value = 0; 292 293 VAStatus va_res = vaGetConfigAttributes( 294 va_display_, va_profile, entrypoint, &attribs[0], attribs.size()); 295 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); 296 297 for (size_t i = 0; i < required_attribs.size(); ++i) { 298 if (attribs[i].type != required_attribs[i].type || 299 (attribs[i].value & required_attribs[i].value) != 300 required_attribs[i].value) { 301 DVLOG(1) << "Unsupported value " << required_attribs[i].value 302 << " for attribute type " << required_attribs[i].type; 303 return false; 304 } 305 } 306 return true; 307} 308 309bool VaapiWrapper::Initialize(CodecMode mode, 310 media::VideoCodecProfile profile, 311 Display* x_display, 312 const base::Closure& report_error_to_uma_cb) { 313 if (!VaInitialize(x_display, report_error_to_uma_cb)) 314 return false; 315 std::vector<VAProfile> supported_va_profiles; 316 if (!GetSupportedVaProfiles(&supported_va_profiles)) 317 return false; 318 VAProfile va_profile = ProfileToVAProfile(profile, supported_va_profiles); 319 if (va_profile == VAProfileNone) { 320 DVLOG(1) << "Unsupported profile"; 321 return false; 322 } 323 VAEntrypoint entrypoint = 324 (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD); 325 if (!IsEntrypointSupported(va_profile, entrypoint)) 326 return false; 327 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode); 328 if (!AreAttribsSupported(va_profile, entrypoint, required_attribs)) 329 return false; 330 331 TryToSetVADisplayAttributeToLocalGPU(); 332 333 base::AutoLock auto_lock(va_lock_); 334 VAStatus va_res = vaCreateConfig(va_display_, 335 va_profile, 336 entrypoint, 337 &required_attribs[0], 338 required_attribs.size(), 339 &va_config_id_); 340 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); 341 342 return true; 343} 344 345void VaapiWrapper::Deinitialize() { 346 base::AutoLock auto_lock(va_lock_); 347 348 if (va_config_id_ != VA_INVALID_ID) { 349 VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); 350 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); 351 } 352 353 if (va_display_) { 354 VAStatus va_res = vaTerminate(va_display_); 355 VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); 356 } 357 358 va_config_id_ = VA_INVALID_ID; 359 va_display_ = NULL; 360} 361 362bool VaapiWrapper::VAAPIVersionLessThan(int major, int minor) { 363 return (major_version_ < major) || 364 (major_version_ == major && minor_version_ < minor); 365} 366 367bool VaapiWrapper::CreateSurfaces(gfx::Size size, 368 size_t num_surfaces, 369 std::vector<VASurfaceID>* va_surfaces) { 370 base::AutoLock auto_lock(va_lock_); 371 DVLOG(2) << "Creating " << num_surfaces << " surfaces"; 372 373 DCHECK(va_surfaces->empty()); 374 DCHECK(va_surface_ids_.empty()); 375 va_surface_ids_.resize(num_surfaces); 376 377 // Allocate surfaces in driver. 378 VAStatus va_res = vaCreateSurfaces(va_display_, 379 VA_RT_FORMAT_YUV420, 380 size.width(), size.height(), 381 &va_surface_ids_[0], 382 va_surface_ids_.size(), 383 NULL, 0); 384 385 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); 386 if (va_res != VA_STATUS_SUCCESS) { 387 va_surface_ids_.clear(); 388 return false; 389 } 390 391 // And create a context associated with them. 392 va_res = vaCreateContext(va_display_, va_config_id_, 393 size.width(), size.height(), VA_PROGRESSIVE, 394 &va_surface_ids_[0], va_surface_ids_.size(), 395 &va_context_id_); 396 397 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); 398 if (va_res != VA_STATUS_SUCCESS) { 399 DestroySurfaces(); 400 return false; 401 } 402 403 *va_surfaces = va_surface_ids_; 404 return true; 405} 406 407void VaapiWrapper::DestroySurfaces() { 408 base::AutoLock auto_lock(va_lock_); 409 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; 410 411 if (va_context_id_ != VA_INVALID_ID) { 412 VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); 413 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); 414 } 415 416 if (!va_surface_ids_.empty()) { 417 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0], 418 va_surface_ids_.size()); 419 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); 420 } 421 422 va_surface_ids_.clear(); 423 va_context_id_ = VA_INVALID_ID; 424} 425 426bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, 427 size_t size, 428 void* buffer) { 429 base::AutoLock auto_lock(va_lock_); 430 431 VABufferID buffer_id; 432 VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, 433 va_buffer_type, size, 434 1, buffer, &buffer_id); 435 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); 436 437 switch (va_buffer_type) { 438 case VASliceParameterBufferType: 439 case VASliceDataBufferType: 440 case VAEncSliceParameterBufferType: 441 pending_slice_bufs_.push_back(buffer_id); 442 break; 443 444 default: 445 pending_va_bufs_.push_back(buffer_id); 446 break; 447 } 448 449 return true; 450} 451 452bool VaapiWrapper::SubmitVAEncMiscParamBuffer( 453 VAEncMiscParameterType misc_param_type, 454 size_t size, 455 void* buffer) { 456 base::AutoLock auto_lock(va_lock_); 457 458 VABufferID buffer_id; 459 VAStatus va_res = vaCreateBuffer(va_display_, 460 va_context_id_, 461 VAEncMiscParameterBufferType, 462 sizeof(VAEncMiscParameterBuffer) + size, 463 1, 464 NULL, 465 &buffer_id); 466 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); 467 468 void* data_ptr = NULL; 469 va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr); 470 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); 471 if (va_res != VA_STATUS_SUCCESS) { 472 vaDestroyBuffer(va_display_, buffer_id); 473 return false; 474 } 475 476 DCHECK(data_ptr); 477 478 VAEncMiscParameterBuffer* misc_param = 479 reinterpret_cast<VAEncMiscParameterBuffer*>(data_ptr); 480 misc_param->type = misc_param_type; 481 memcpy(misc_param->data, buffer, size); 482 va_res = vaUnmapBuffer(va_display_, buffer_id); 483 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); 484 485 pending_va_bufs_.push_back(buffer_id); 486 return true; 487} 488 489void VaapiWrapper::DestroyPendingBuffers() { 490 base::AutoLock auto_lock(va_lock_); 491 492 for (size_t i = 0; i < pending_va_bufs_.size(); ++i) { 493 VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_bufs_[i]); 494 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 495 } 496 497 for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) { 498 VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_bufs_[i]); 499 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 500 } 501 502 pending_va_bufs_.clear(); 503 pending_slice_bufs_.clear(); 504} 505 506bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) { 507 base::AutoLock auto_lock(va_lock_); 508 VAStatus va_res = vaCreateBuffer(va_display_, 509 va_context_id_, 510 VAEncCodedBufferType, 511 size, 512 1, 513 NULL, 514 buffer_id); 515 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false); 516 517 DCHECK(coded_buffers_.insert(*buffer_id).second); 518 return true; 519} 520 521void VaapiWrapper::DestroyCodedBuffers() { 522 base::AutoLock auto_lock(va_lock_); 523 524 for (std::set<VABufferID>::const_iterator iter = coded_buffers_.begin(); 525 iter != coded_buffers_.end(); 526 ++iter) { 527 VAStatus va_res = vaDestroyBuffer(va_display_, *iter); 528 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 529 } 530 531 coded_buffers_.clear(); 532} 533 534bool VaapiWrapper::Execute(VASurfaceID va_surface_id) { 535 base::AutoLock auto_lock(va_lock_); 536 537 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); 538 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); 539 DVLOG(4) << "Target VA surface " << va_surface_id; 540 541 // Get ready to execute for given surface. 542 VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, 543 va_surface_id); 544 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); 545 546 if (pending_va_bufs_.size() > 0) { 547 // Commit parameter and slice buffers. 548 va_res = vaRenderPicture(va_display_, 549 va_context_id_, 550 &pending_va_bufs_[0], 551 pending_va_bufs_.size()); 552 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); 553 } 554 555 if (pending_slice_bufs_.size() > 0) { 556 va_res = vaRenderPicture(va_display_, 557 va_context_id_, 558 &pending_slice_bufs_[0], 559 pending_slice_bufs_.size()); 560 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); 561 } 562 563 // Instruct HW codec to start processing committed buffers. 564 // Does not block and the job is not finished after this returns. 565 va_res = vaEndPicture(va_display_, va_context_id_); 566 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); 567 568 return true; 569} 570 571bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { 572 bool result = Execute(va_surface_id); 573 DestroyPendingBuffers(); 574 return result; 575} 576 577bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, 578 Pixmap x_pixmap, 579 gfx::Size dest_size) { 580 base::AutoLock auto_lock(va_lock_); 581 582 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); 583 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); 584 585 // Put the data into an X Pixmap. 586 va_res = vaPutSurface(va_display_, 587 va_surface_id, 588 x_pixmap, 589 0, 0, dest_size.width(), dest_size.height(), 590 0, 0, dest_size.width(), dest_size.height(), 591 NULL, 0, 0); 592 VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false); 593 return true; 594} 595 596bool VaapiWrapper::GetVaImageForTesting(VASurfaceID va_surface_id, 597 VAImage* image, 598 void** mem) { 599 base::AutoLock auto_lock(va_lock_); 600 601 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); 602 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); 603 604 // Derive a VAImage from the VASurface 605 va_res = vaDeriveImage(va_display_, va_surface_id, image); 606 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed"); 607 if (va_res != VA_STATUS_SUCCESS) 608 return false; 609 610 // Map the VAImage into memory 611 va_res = vaMapBuffer(va_display_, image->buf, mem); 612 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); 613 if (va_res == VA_STATUS_SUCCESS) 614 return true; 615 616 va_res = vaDestroyImage(va_display_, image->image_id); 617 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); 618 619 return false; 620} 621 622void VaapiWrapper::ReturnVaImageForTesting(VAImage* image) { 623 base::AutoLock auto_lock(va_lock_); 624 625 VAStatus va_res = vaUnmapBuffer(va_display_, image->buf); 626 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); 627 628 va_res = vaDestroyImage(va_display_, image->image_id); 629 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); 630} 631 632static void DestroyVAImage(VADisplay va_display, VAImage image) { 633 if (image.image_id != VA_INVALID_ID) 634 vaDestroyImage(va_display, image.image_id); 635} 636 637bool VaapiWrapper::UploadVideoFrameToSurface( 638 const scoped_refptr<media::VideoFrame>& frame, 639 VASurfaceID va_surface_id) { 640 base::AutoLock auto_lock(va_lock_); 641 642 VAImage image; 643 VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image); 644 VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false); 645 base::ScopedClosureRunner vaimage_deleter( 646 base::Bind(&DestroyVAImage, va_display_, image)); 647 648 if (image.format.fourcc != VA_FOURCC_NV12) { 649 DVLOG(1) << "Unsupported image format: " << image.format.fourcc; 650 return false; 651 } 652 653 if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) { 654 DVLOG(1) << "Buffer too small to fit the frame."; 655 return false; 656 } 657 658 void* image_ptr = NULL; 659 va_res = vaMapBuffer(va_display_, image.buf, &image_ptr); 660 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); 661 DCHECK(image_ptr); 662 663 int ret = 0; 664 { 665 base::AutoUnlock auto_unlock(va_lock_); 666 ret = libyuv::I420ToNV12(frame->data(media::VideoFrame::kYPlane), 667 frame->stride(media::VideoFrame::kYPlane), 668 frame->data(media::VideoFrame::kUPlane), 669 frame->stride(media::VideoFrame::kUPlane), 670 frame->data(media::VideoFrame::kVPlane), 671 frame->stride(media::VideoFrame::kVPlane), 672 static_cast<uint8*>(image_ptr) + image.offsets[0], 673 image.pitches[0], 674 static_cast<uint8*>(image_ptr) + image.offsets[1], 675 image.pitches[1], 676 image.width, 677 image.height); 678 } 679 680 va_res = vaUnmapBuffer(va_display_, image.buf); 681 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); 682 683 return ret == 0; 684} 685 686bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id, 687 VASurfaceID sync_surface_id, 688 uint8* target_ptr, 689 size_t target_size, 690 size_t* coded_data_size) { 691 base::AutoLock auto_lock(va_lock_); 692 693 VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id); 694 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); 695 696 VACodedBufferSegment* buffer_segment = NULL; 697 va_res = vaMapBuffer( 698 va_display_, buffer_id, reinterpret_cast<void**>(&buffer_segment)); 699 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); 700 DCHECK(target_ptr); 701 702 { 703 base::AutoUnlock auto_unlock(va_lock_); 704 *coded_data_size = 0; 705 706 while (buffer_segment) { 707 DCHECK(buffer_segment->buf); 708 709 if (buffer_segment->size > target_size) { 710 DVLOG(1) << "Insufficient output buffer size"; 711 break; 712 } 713 714 memcpy(target_ptr, buffer_segment->buf, buffer_segment->size); 715 716 target_ptr += buffer_segment->size; 717 *coded_data_size += buffer_segment->size; 718 target_size -= buffer_segment->size; 719 720 buffer_segment = 721 reinterpret_cast<VACodedBufferSegment*>(buffer_segment->next); 722 } 723 } 724 725 va_res = vaUnmapBuffer(va_display_, buffer_id); 726 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); 727 728 va_res = vaDestroyBuffer(va_display_, buffer_id); 729 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); 730 731 DCHECK(coded_buffers_.erase(buffer_id)); 732 733 return buffer_segment == NULL; 734} 735 736// static 737bool VaapiWrapper::PostSandboxInitialization() { 738 StubPathMap paths; 739 paths[kModuleVa].push_back(kVaLib); 740 741 return InitializeStubs(paths); 742} 743 744} // namespace content 745