| OLD | NEW |
| (Empty) |
| 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 #include <string.h> | |
| 9 | |
| 10 #include "base/bind.h" | |
| 11 #include "base/callback_helpers.h" | |
| 12 #include "base/logging.h" | |
| 13 #include "base/macros.h" | |
| 14 #include "base/numerics/safe_conversions.h" | |
| 15 #include "base/sys_info.h" | |
| 16 #include "build/build_config.h" | |
| 17 // Auto-generated for dlopen libva libraries | |
| 18 #include "content/common/gpu/media/va_stubs.h" | |
| 19 #include "content/common/gpu/media/vaapi_picture.h" | |
| 20 #include "third_party/libyuv/include/libyuv.h" | |
| 21 #include "ui/gl/gl_bindings.h" | |
| 22 #if defined(USE_X11) | |
| 23 #include "ui/gfx/x/x11_types.h" | |
| 24 #elif defined(USE_OZONE) | |
| 25 #include "third_party/libva/va/drm/va_drm.h" | |
| 26 #include "third_party/libva/va/va_drmcommon.h" | |
| 27 #include "ui/ozone/public/ozone_platform.h" | |
| 28 #include "ui/ozone/public/surface_factory_ozone.h" | |
| 29 #endif // USE_X11 | |
| 30 | |
| 31 using content_common_gpu_media::kModuleVa; | |
| 32 #if defined(USE_X11) | |
| 33 using content_common_gpu_media::kModuleVa_x11; | |
| 34 #elif defined(USE_OZONE) | |
| 35 using content_common_gpu_media::kModuleVa_drm; | |
| 36 #endif // USE_X11 | |
| 37 using content_common_gpu_media::InitializeStubs; | |
| 38 using content_common_gpu_media::StubPathMap; | |
| 39 | |
| 40 #define LOG_VA_ERROR_AND_REPORT(va_error, err_msg) \ | |
| 41 do { \ | |
| 42 LOG(ERROR) << err_msg \ | |
| 43 << " VA error: " << vaErrorStr(va_error); \ | |
| 44 report_error_to_uma_cb_.Run(); \ | |
| 45 } while (0) | |
| 46 | |
| 47 #define VA_LOG_ON_ERROR(va_error, err_msg) \ | |
| 48 do { \ | |
| 49 if ((va_error) != VA_STATUS_SUCCESS) \ | |
| 50 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ | |
| 51 } while (0) | |
| 52 | |
| 53 #define VA_SUCCESS_OR_RETURN(va_error, err_msg, ret) \ | |
| 54 do { \ | |
| 55 if ((va_error) != VA_STATUS_SUCCESS) { \ | |
| 56 LOG_VA_ERROR_AND_REPORT(va_error, err_msg); \ | |
| 57 return (ret); \ | |
| 58 } \ | |
| 59 } while (0) | |
| 60 | |
| 61 #if defined(USE_OZONE) | |
| 62 namespace { | |
| 63 | |
| 64 uint32_t BufferFormatToVAFourCC(gfx::BufferFormat fmt) { | |
| 65 switch (fmt) { | |
| 66 case gfx::BufferFormat::BGRX_8888: | |
| 67 return VA_FOURCC_BGRX; | |
| 68 case gfx::BufferFormat::UYVY_422: | |
| 69 return VA_FOURCC_UYVY; | |
| 70 default: | |
| 71 NOTREACHED(); | |
| 72 return 0; | |
| 73 } | |
| 74 } | |
| 75 | |
| 76 uint32_t BufferFormatToVARTFormat(gfx::BufferFormat fmt) { | |
| 77 switch (fmt) { | |
| 78 case gfx::BufferFormat::UYVY_422: | |
| 79 return VA_RT_FORMAT_YUV422; | |
| 80 case gfx::BufferFormat::BGRX_8888: | |
| 81 return VA_RT_FORMAT_RGB32; | |
| 82 default: | |
| 83 NOTREACHED(); | |
| 84 return 0; | |
| 85 } | |
| 86 } | |
| 87 | |
| 88 } // namespace | |
| 89 #endif | |
| 90 | |
| 91 namespace content { | |
| 92 | |
| 93 // Maximum framerate of encoded profile. This value is an arbitary limit | |
| 94 // and not taken from HW documentation. | |
| 95 const int kMaxEncoderFramerate = 30; | |
| 96 | |
| 97 base::LazyInstance<VaapiWrapper::VADisplayState> | |
| 98 VaapiWrapper::va_display_state_ = LAZY_INSTANCE_INITIALIZER; | |
| 99 | |
| 100 base::LazyInstance<VaapiWrapper::LazyProfileInfos> | |
| 101 VaapiWrapper::profile_infos_ = LAZY_INSTANCE_INITIALIZER; | |
| 102 | |
| 103 // Config attributes common for both encode and decode. | |
| 104 static const VAConfigAttrib kCommonVAConfigAttribs[] = { | |
| 105 {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, | |
| 106 }; | |
| 107 | |
| 108 // Attributes required for encode. | |
| 109 static const VAConfigAttrib kEncodeVAConfigAttribs[] = { | |
| 110 {VAConfigAttribRateControl, VA_RC_CBR}, | |
| 111 {VAConfigAttribEncPackedHeaders, | |
| 112 VA_ENC_PACKED_HEADER_SEQUENCE | VA_ENC_PACKED_HEADER_PICTURE}, | |
| 113 }; | |
| 114 | |
| 115 struct ProfileMap { | |
| 116 media::VideoCodecProfile profile; | |
| 117 VAProfile va_profile; | |
| 118 }; | |
| 119 | |
| 120 // A map between VideoCodecProfile and VAProfile. | |
| 121 static const ProfileMap kProfileMap[] = { | |
| 122 {media::H264PROFILE_BASELINE, VAProfileH264Baseline}, | |
| 123 {media::H264PROFILE_MAIN, VAProfileH264Main}, | |
| 124 // TODO(posciak): See if we can/want support other variants of | |
| 125 // media::H264PROFILE_HIGH*. | |
| 126 {media::H264PROFILE_HIGH, VAProfileH264High}, | |
| 127 {media::VP8PROFILE_ANY, VAProfileVP8Version0_3}, | |
| 128 // TODO(servolk): Need to add VP9 profiles 1,2,3 here after rolling | |
| 129 // third_party/libva to 1.7. crbug.com/598118 | |
| 130 {media::VP9PROFILE_PROFILE0, VAProfileVP9Profile0}, | |
| 131 }; | |
| 132 | |
| 133 static std::vector<VAConfigAttrib> GetRequiredAttribs( | |
| 134 VaapiWrapper::CodecMode mode) { | |
| 135 std::vector<VAConfigAttrib> required_attribs; | |
| 136 required_attribs.insert( | |
| 137 required_attribs.end(), | |
| 138 kCommonVAConfigAttribs, | |
| 139 kCommonVAConfigAttribs + arraysize(kCommonVAConfigAttribs)); | |
| 140 if (mode == VaapiWrapper::kEncode) { | |
| 141 required_attribs.insert( | |
| 142 required_attribs.end(), | |
| 143 kEncodeVAConfigAttribs, | |
| 144 kEncodeVAConfigAttribs + arraysize(kEncodeVAConfigAttribs)); | |
| 145 } | |
| 146 return required_attribs; | |
| 147 } | |
| 148 | |
| 149 VASurface::VASurface(VASurfaceID va_surface_id, | |
| 150 const gfx::Size& size, | |
| 151 unsigned int format, | |
| 152 const ReleaseCB& release_cb) | |
| 153 : va_surface_id_(va_surface_id), | |
| 154 size_(size), | |
| 155 format_(format), | |
| 156 release_cb_(release_cb) { | |
| 157 DCHECK(!release_cb_.is_null()); | |
| 158 } | |
| 159 | |
| 160 VASurface::~VASurface() { | |
| 161 release_cb_.Run(va_surface_id_); | |
| 162 } | |
| 163 | |
| 164 VaapiWrapper::VaapiWrapper() | |
| 165 : va_surface_format_(0), | |
| 166 va_display_(NULL), | |
| 167 va_config_id_(VA_INVALID_ID), | |
| 168 va_context_id_(VA_INVALID_ID), | |
| 169 va_vpp_config_id_(VA_INVALID_ID), | |
| 170 va_vpp_context_id_(VA_INVALID_ID), | |
| 171 va_vpp_buffer_id_(VA_INVALID_ID) { | |
| 172 va_lock_ = va_display_state_.Get().va_lock(); | |
| 173 } | |
| 174 | |
| 175 VaapiWrapper::~VaapiWrapper() { | |
| 176 DestroyPendingBuffers(); | |
| 177 DestroyCodedBuffers(); | |
| 178 DestroySurfaces(); | |
| 179 DeinitializeVpp(); | |
| 180 Deinitialize(); | |
| 181 } | |
| 182 | |
| 183 // static | |
| 184 scoped_refptr<VaapiWrapper> VaapiWrapper::Create( | |
| 185 CodecMode mode, | |
| 186 VAProfile va_profile, | |
| 187 const base::Closure& report_error_to_uma_cb) { | |
| 188 if (!profile_infos_.Get().IsProfileSupported(mode, va_profile)) { | |
| 189 DVLOG(1) << "Unsupported va_profile: " << va_profile; | |
| 190 return nullptr; | |
| 191 } | |
| 192 | |
| 193 scoped_refptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper()); | |
| 194 if (vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) { | |
| 195 if (vaapi_wrapper->Initialize(mode, va_profile)) | |
| 196 return vaapi_wrapper; | |
| 197 } | |
| 198 LOG(ERROR) << "Failed to create VaapiWrapper for va_profile: " << va_profile; | |
| 199 return nullptr; | |
| 200 } | |
| 201 | |
| 202 // static | |
| 203 scoped_refptr<VaapiWrapper> VaapiWrapper::CreateForVideoCodec( | |
| 204 CodecMode mode, | |
| 205 media::VideoCodecProfile profile, | |
| 206 const base::Closure& report_error_to_uma_cb) { | |
| 207 VAProfile va_profile = ProfileToVAProfile(profile, mode); | |
| 208 scoped_refptr<VaapiWrapper> vaapi_wrapper = | |
| 209 Create(mode, va_profile, report_error_to_uma_cb); | |
| 210 return vaapi_wrapper; | |
| 211 } | |
| 212 | |
| 213 // static | |
| 214 media::VideoEncodeAccelerator::SupportedProfiles | |
| 215 VaapiWrapper::GetSupportedEncodeProfiles() { | |
| 216 media::VideoEncodeAccelerator::SupportedProfiles profiles; | |
| 217 std::vector<ProfileInfo> encode_profile_infos = | |
| 218 profile_infos_.Get().GetSupportedProfileInfosForCodecMode(kEncode); | |
| 219 | |
| 220 for (size_t i = 0; i < arraysize(kProfileMap); ++i) { | |
| 221 VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode); | |
| 222 if (va_profile == VAProfileNone) | |
| 223 continue; | |
| 224 for (const auto& profile_info : encode_profile_infos) { | |
| 225 if (profile_info.va_profile == va_profile) { | |
| 226 media::VideoEncodeAccelerator::SupportedProfile profile; | |
| 227 profile.profile = kProfileMap[i].profile; | |
| 228 profile.max_resolution = profile_info.max_resolution; | |
| 229 profile.max_framerate_numerator = kMaxEncoderFramerate; | |
| 230 profile.max_framerate_denominator = 1; | |
| 231 profiles.push_back(profile); | |
| 232 break; | |
| 233 } | |
| 234 } | |
| 235 } | |
| 236 return profiles; | |
| 237 } | |
| 238 | |
| 239 // static | |
| 240 media::VideoDecodeAccelerator::SupportedProfiles | |
| 241 VaapiWrapper::GetSupportedDecodeProfiles() { | |
| 242 media::VideoDecodeAccelerator::SupportedProfiles profiles; | |
| 243 std::vector<ProfileInfo> decode_profile_infos = | |
| 244 profile_infos_.Get().GetSupportedProfileInfosForCodecMode(kDecode); | |
| 245 | |
| 246 for (size_t i = 0; i < arraysize(kProfileMap); ++i) { | |
| 247 VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kDecode); | |
| 248 if (va_profile == VAProfileNone) | |
| 249 continue; | |
| 250 for (const auto& profile_info : decode_profile_infos) { | |
| 251 if (profile_info.va_profile == va_profile) { | |
| 252 media::VideoDecodeAccelerator::SupportedProfile profile; | |
| 253 profile.profile = kProfileMap[i].profile; | |
| 254 profile.max_resolution = profile_info.max_resolution; | |
| 255 profile.min_resolution.SetSize(16, 16); | |
| 256 profiles.push_back(profile); | |
| 257 break; | |
| 258 } | |
| 259 } | |
| 260 } | |
| 261 return profiles; | |
| 262 } | |
| 263 | |
| 264 // static | |
| 265 bool VaapiWrapper::IsJpegDecodeSupported() { | |
| 266 return profile_infos_.Get().IsProfileSupported(kDecode, | |
| 267 VAProfileJPEGBaseline); | |
| 268 } | |
| 269 | |
| 270 void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { | |
| 271 base::AutoLock auto_lock(*va_lock_); | |
| 272 VADisplayAttribute item = {VADisplayAttribRenderMode, | |
| 273 1, // At least support '_LOCAL_OVERLAY'. | |
| 274 -1, // The maximum possible support 'ALL'. | |
| 275 VA_RENDER_MODE_LOCAL_GPU, | |
| 276 VA_DISPLAY_ATTRIB_SETTABLE}; | |
| 277 | |
| 278 VAStatus va_res = vaSetDisplayAttributes(va_display_, &item, 1); | |
| 279 if (va_res != VA_STATUS_SUCCESS) | |
| 280 DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; | |
| 281 } | |
| 282 | |
| 283 // static | |
| 284 VAProfile VaapiWrapper::ProfileToVAProfile( | |
| 285 media::VideoCodecProfile profile, CodecMode mode) { | |
| 286 VAProfile va_profile = VAProfileNone; | |
| 287 for (size_t i = 0; i < arraysize(kProfileMap); ++i) { | |
| 288 if (kProfileMap[i].profile == profile) { | |
| 289 va_profile = kProfileMap[i].va_profile; | |
| 290 break; | |
| 291 } | |
| 292 } | |
| 293 if (!profile_infos_.Get().IsProfileSupported(mode, va_profile) && | |
| 294 va_profile == VAProfileH264Baseline) { | |
| 295 // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips | |
| 296 // the information whether the profile is constrained or not, so we have no | |
| 297 // way to know here. Try for baseline first, but if it is not supported, | |
| 298 // try constrained baseline and hope this is what it actually is | |
| 299 // (which in practice is true for a great majority of cases). | |
| 300 if (profile_infos_.Get().IsProfileSupported( | |
| 301 mode, VAProfileH264ConstrainedBaseline)) { | |
| 302 va_profile = VAProfileH264ConstrainedBaseline; | |
| 303 DVLOG(1) << "Fall back to constrained baseline profile."; | |
| 304 } | |
| 305 } | |
| 306 return va_profile; | |
| 307 } | |
| 308 | |
| 309 std::vector<VaapiWrapper::ProfileInfo> | |
| 310 VaapiWrapper::GetSupportedProfileInfosForCodecModeInternal(CodecMode mode) { | |
| 311 std::vector<ProfileInfo> supported_profile_infos; | |
| 312 std::vector<VAProfile> va_profiles; | |
| 313 if (!GetSupportedVaProfiles(&va_profiles)) | |
| 314 return supported_profile_infos; | |
| 315 | |
| 316 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode); | |
| 317 VAEntrypoint entrypoint = | |
| 318 (mode == kEncode ? VAEntrypointEncSlice: VAEntrypointVLD); | |
| 319 | |
| 320 base::AutoLock auto_lock(*va_lock_); | |
| 321 for (const auto& va_profile : va_profiles) { | |
| 322 if (!IsEntrypointSupported_Locked(va_profile, entrypoint)) | |
| 323 continue; | |
| 324 if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs)) | |
| 325 continue; | |
| 326 ProfileInfo profile_info; | |
| 327 if (!GetMaxResolution_Locked(va_profile, | |
| 328 entrypoint, | |
| 329 required_attribs, | |
| 330 &profile_info.max_resolution)) { | |
| 331 LOG(ERROR) << "GetMaxResolution failed for va_profile " << va_profile | |
| 332 << " and entrypoint " << entrypoint; | |
| 333 continue; | |
| 334 } | |
| 335 profile_info.va_profile = va_profile; | |
| 336 supported_profile_infos.push_back(profile_info); | |
| 337 } | |
| 338 return supported_profile_infos; | |
| 339 } | |
| 340 | |
| 341 bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) { | |
| 342 static bool vaapi_functions_initialized = PostSandboxInitialization(); | |
| 343 if (!vaapi_functions_initialized) { | |
| 344 bool running_on_chromeos = false; | |
| 345 #if defined(OS_CHROMEOS) | |
| 346 // When chrome runs on linux with chromeos=1, do not log error message | |
| 347 // without VAAPI libraries. | |
| 348 running_on_chromeos = base::SysInfo::IsRunningOnChromeOS(); | |
| 349 #endif | |
| 350 static const char kErrorMsg[] = "Failed to initialize VAAPI libs"; | |
| 351 if (running_on_chromeos) | |
| 352 LOG(ERROR) << kErrorMsg; | |
| 353 else | |
| 354 DVLOG(1) << kErrorMsg; | |
| 355 return false; | |
| 356 } | |
| 357 | |
| 358 report_error_to_uma_cb_ = report_error_to_uma_cb; | |
| 359 | |
| 360 base::AutoLock auto_lock(*va_lock_); | |
| 361 | |
| 362 VADisplayState* va_display_state = &va_display_state_.Get(); | |
| 363 if (!va_display_state) { | |
| 364 LOG(ERROR) << "Failed to allocate VA display state"; | |
| 365 return false; | |
| 366 } | |
| 367 | |
| 368 if (!va_display_state->Initialize()) | |
| 369 return false; | |
| 370 | |
| 371 va_display_ = va_display_state->va_display(); | |
| 372 return true; | |
| 373 } | |
| 374 | |
| 375 bool VaapiWrapper::GetSupportedVaProfiles(std::vector<VAProfile>* profiles) { | |
| 376 base::AutoLock auto_lock(*va_lock_); | |
| 377 // Query the driver for supported profiles. | |
| 378 int max_profiles = vaMaxNumProfiles(va_display_); | |
| 379 std::vector<VAProfile> supported_profiles( | |
| 380 base::checked_cast<size_t>(max_profiles)); | |
| 381 | |
| 382 int num_supported_profiles; | |
| 383 VAStatus va_res = vaQueryConfigProfiles( | |
| 384 va_display_, &supported_profiles[0], &num_supported_profiles); | |
| 385 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigProfiles failed", false); | |
| 386 if (num_supported_profiles < 0 || num_supported_profiles > max_profiles) { | |
| 387 LOG(ERROR) << "vaQueryConfigProfiles returned: " << num_supported_profiles; | |
| 388 return false; | |
| 389 } | |
| 390 | |
| 391 supported_profiles.resize(base::checked_cast<size_t>(num_supported_profiles)); | |
| 392 *profiles = supported_profiles; | |
| 393 return true; | |
| 394 } | |
| 395 | |
| 396 bool VaapiWrapper::IsEntrypointSupported_Locked(VAProfile va_profile, | |
| 397 VAEntrypoint entrypoint) { | |
| 398 va_lock_->AssertAcquired(); | |
| 399 // Query the driver for supported entrypoints. | |
| 400 int max_entrypoints = vaMaxNumEntrypoints(va_display_); | |
| 401 std::vector<VAEntrypoint> supported_entrypoints( | |
| 402 base::checked_cast<size_t>(max_entrypoints)); | |
| 403 | |
| 404 int num_supported_entrypoints; | |
| 405 VAStatus va_res = vaQueryConfigEntrypoints(va_display_, | |
| 406 va_profile, | |
| 407 &supported_entrypoints[0], | |
| 408 &num_supported_entrypoints); | |
| 409 VA_SUCCESS_OR_RETURN(va_res, "vaQueryConfigEntrypoints failed", false); | |
| 410 if (num_supported_entrypoints < 0 || | |
| 411 num_supported_entrypoints > max_entrypoints) { | |
| 412 LOG(ERROR) << "vaQueryConfigEntrypoints returned: " | |
| 413 << num_supported_entrypoints; | |
| 414 return false; | |
| 415 } | |
| 416 | |
| 417 if (std::find(supported_entrypoints.begin(), | |
| 418 supported_entrypoints.end(), | |
| 419 entrypoint) == supported_entrypoints.end()) { | |
| 420 DVLOG(1) << "Unsupported entrypoint"; | |
| 421 return false; | |
| 422 } | |
| 423 return true; | |
| 424 } | |
| 425 | |
| 426 bool VaapiWrapper::AreAttribsSupported_Locked( | |
| 427 VAProfile va_profile, | |
| 428 VAEntrypoint entrypoint, | |
| 429 const std::vector<VAConfigAttrib>& required_attribs) { | |
| 430 va_lock_->AssertAcquired(); | |
| 431 // Query the driver for required attributes. | |
| 432 std::vector<VAConfigAttrib> attribs = required_attribs; | |
| 433 for (size_t i = 0; i < required_attribs.size(); ++i) | |
| 434 attribs[i].value = 0; | |
| 435 | |
| 436 VAStatus va_res = vaGetConfigAttributes( | |
| 437 va_display_, va_profile, entrypoint, &attribs[0], attribs.size()); | |
| 438 VA_SUCCESS_OR_RETURN(va_res, "vaGetConfigAttributes failed", false); | |
| 439 | |
| 440 for (size_t i = 0; i < required_attribs.size(); ++i) { | |
| 441 if (attribs[i].type != required_attribs[i].type || | |
| 442 (attribs[i].value & required_attribs[i].value) != | |
| 443 required_attribs[i].value) { | |
| 444 DVLOG(1) << "Unsupported value " << required_attribs[i].value | |
| 445 << " for attribute type " << required_attribs[i].type; | |
| 446 return false; | |
| 447 } | |
| 448 } | |
| 449 return true; | |
| 450 } | |
| 451 | |
| 452 bool VaapiWrapper::GetMaxResolution_Locked( | |
| 453 VAProfile va_profile, | |
| 454 VAEntrypoint entrypoint, | |
| 455 std::vector<VAConfigAttrib>& required_attribs, | |
| 456 gfx::Size* resolution) { | |
| 457 va_lock_->AssertAcquired(); | |
| 458 VAConfigID va_config_id; | |
| 459 VAStatus va_res = vaCreateConfig( | |
| 460 va_display_, | |
| 461 va_profile, | |
| 462 entrypoint, | |
| 463 &required_attribs[0], | |
| 464 required_attribs.size(), | |
| 465 &va_config_id); | |
| 466 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); | |
| 467 | |
| 468 // Calls vaQuerySurfaceAttributes twice. The first time is to get the number | |
| 469 // of attributes to prepare the space and the second time is to get all | |
| 470 // attributes. | |
| 471 unsigned int num_attribs; | |
| 472 va_res = vaQuerySurfaceAttributes( | |
| 473 va_display_, va_config_id, nullptr, &num_attribs); | |
| 474 VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); | |
| 475 if (!num_attribs) | |
| 476 return false; | |
| 477 | |
| 478 std::vector<VASurfaceAttrib> attrib_list( | |
| 479 base::checked_cast<size_t>(num_attribs)); | |
| 480 | |
| 481 va_res = vaQuerySurfaceAttributes( | |
| 482 va_display_, va_config_id, &attrib_list[0], &num_attribs); | |
| 483 VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); | |
| 484 | |
| 485 resolution->SetSize(0, 0); | |
| 486 for (const auto& attrib : attrib_list) { | |
| 487 if (attrib.type == VASurfaceAttribMaxWidth) | |
| 488 resolution->set_width(attrib.value.value.i); | |
| 489 else if (attrib.type == VASurfaceAttribMaxHeight) | |
| 490 resolution->set_height(attrib.value.value.i); | |
| 491 } | |
| 492 if (resolution->IsEmpty()) { | |
| 493 LOG(ERROR) << "Codec resolution " << resolution->ToString() | |
| 494 << " cannot be zero."; | |
| 495 return false; | |
| 496 } | |
| 497 return true; | |
| 498 } | |
| 499 | |
| 500 bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) { | |
| 501 TryToSetVADisplayAttributeToLocalGPU(); | |
| 502 | |
| 503 VAEntrypoint entrypoint = | |
| 504 (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD); | |
| 505 std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode); | |
| 506 base::AutoLock auto_lock(*va_lock_); | |
| 507 VAStatus va_res = vaCreateConfig(va_display_, | |
| 508 va_profile, | |
| 509 entrypoint, | |
| 510 &required_attribs[0], | |
| 511 required_attribs.size(), | |
| 512 &va_config_id_); | |
| 513 VA_SUCCESS_OR_RETURN(va_res, "vaCreateConfig failed", false); | |
| 514 | |
| 515 return true; | |
| 516 } | |
| 517 | |
| 518 void VaapiWrapper::Deinitialize() { | |
| 519 base::AutoLock auto_lock(*va_lock_); | |
| 520 | |
| 521 if (va_config_id_ != VA_INVALID_ID) { | |
| 522 VAStatus va_res = vaDestroyConfig(va_display_, va_config_id_); | |
| 523 VA_LOG_ON_ERROR(va_res, "vaDestroyConfig failed"); | |
| 524 } | |
| 525 | |
| 526 VADisplayState* va_display_state = &va_display_state_.Get(); | |
| 527 if (va_display_state) { | |
| 528 VAStatus va_res = VA_STATUS_SUCCESS; | |
| 529 va_display_state->Deinitialize(&va_res); | |
| 530 VA_LOG_ON_ERROR(va_res, "vaTerminate failed"); | |
| 531 } | |
| 532 | |
| 533 va_config_id_ = VA_INVALID_ID; | |
| 534 va_display_ = NULL; | |
| 535 } | |
| 536 | |
| 537 bool VaapiWrapper::CreateSurfaces(unsigned int va_format, | |
| 538 const gfx::Size& size, | |
| 539 size_t num_surfaces, | |
| 540 std::vector<VASurfaceID>* va_surfaces) { | |
| 541 base::AutoLock auto_lock(*va_lock_); | |
| 542 DVLOG(2) << "Creating " << num_surfaces << " surfaces"; | |
| 543 | |
| 544 DCHECK(va_surfaces->empty()); | |
| 545 DCHECK(va_surface_ids_.empty()); | |
| 546 DCHECK_EQ(va_surface_format_, 0u); | |
| 547 va_surface_ids_.resize(num_surfaces); | |
| 548 | |
| 549 // Allocate surfaces in driver. | |
| 550 VAStatus va_res = | |
| 551 vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), | |
| 552 &va_surface_ids_[0], va_surface_ids_.size(), NULL, 0); | |
| 553 | |
| 554 VA_LOG_ON_ERROR(va_res, "vaCreateSurfaces failed"); | |
| 555 if (va_res != VA_STATUS_SUCCESS) { | |
| 556 va_surface_ids_.clear(); | |
| 557 return false; | |
| 558 } | |
| 559 | |
| 560 // And create a context associated with them. | |
| 561 va_res = vaCreateContext(va_display_, va_config_id_, | |
| 562 size.width(), size.height(), VA_PROGRESSIVE, | |
| 563 &va_surface_ids_[0], va_surface_ids_.size(), | |
| 564 &va_context_id_); | |
| 565 | |
| 566 VA_LOG_ON_ERROR(va_res, "vaCreateContext failed"); | |
| 567 if (va_res != VA_STATUS_SUCCESS) { | |
| 568 DestroySurfaces(); | |
| 569 return false; | |
| 570 } | |
| 571 | |
| 572 *va_surfaces = va_surface_ids_; | |
| 573 va_surface_format_ = va_format; | |
| 574 return true; | |
| 575 } | |
| 576 | |
| 577 void VaapiWrapper::DestroySurfaces() { | |
| 578 base::AutoLock auto_lock(*va_lock_); | |
| 579 DVLOG(2) << "Destroying " << va_surface_ids_.size() << " surfaces"; | |
| 580 | |
| 581 if (va_context_id_ != VA_INVALID_ID) { | |
| 582 VAStatus va_res = vaDestroyContext(va_display_, va_context_id_); | |
| 583 VA_LOG_ON_ERROR(va_res, "vaDestroyContext failed"); | |
| 584 } | |
| 585 | |
| 586 if (!va_surface_ids_.empty()) { | |
| 587 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_ids_[0], | |
| 588 va_surface_ids_.size()); | |
| 589 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces failed"); | |
| 590 } | |
| 591 | |
| 592 va_surface_ids_.clear(); | |
| 593 va_context_id_ = VA_INVALID_ID; | |
| 594 va_surface_format_ = 0; | |
| 595 } | |
| 596 | |
| 597 scoped_refptr<VASurface> VaapiWrapper::CreateUnownedSurface( | |
| 598 unsigned int va_format, | |
| 599 const gfx::Size& size, | |
| 600 const std::vector<VASurfaceAttrib>& va_attribs) { | |
| 601 base::AutoLock auto_lock(*va_lock_); | |
| 602 | |
| 603 std::vector<VASurfaceAttrib> attribs(va_attribs); | |
| 604 VASurfaceID va_surface_id; | |
| 605 VAStatus va_res = | |
| 606 vaCreateSurfaces(va_display_, va_format, size.width(), size.height(), | |
| 607 &va_surface_id, 1, &attribs[0], attribs.size()); | |
| 608 | |
| 609 scoped_refptr<VASurface> va_surface; | |
| 610 VA_SUCCESS_OR_RETURN(va_res, "Failed to create unowned VASurface", | |
| 611 va_surface); | |
| 612 | |
| 613 // This is safe to use Unretained() here, because the VDA takes care | |
| 614 // of the destruction order. All the surfaces will be destroyed | |
| 615 // before VaapiWrapper. | |
| 616 va_surface = new VASurface( | |
| 617 va_surface_id, size, va_format, | |
| 618 base::Bind(&VaapiWrapper::DestroyUnownedSurface, base::Unretained(this))); | |
| 619 | |
| 620 return va_surface; | |
| 621 } | |
| 622 | |
| 623 #if defined(USE_OZONE) | |
| 624 scoped_refptr<VASurface> VaapiWrapper::CreateVASurfaceForPixmap( | |
| 625 const scoped_refptr<ui::NativePixmap>& pixmap) { | |
| 626 // Get the dmabuf of the created buffer. | |
| 627 int dmabuf_fd = pixmap->GetDmaBufFd(); | |
| 628 if (dmabuf_fd < 0) { | |
| 629 LOG(ERROR) << "Failed to get dmabuf from an Ozone NativePixmap"; | |
| 630 return nullptr; | |
| 631 } | |
| 632 int dmabuf_pitch = pixmap->GetDmaBufPitch(); | |
| 633 gfx::Size pixmap_size = pixmap->GetBufferSize(); | |
| 634 | |
| 635 // Create a VASurface out of the created buffer using the dmabuf. | |
| 636 VASurfaceAttribExternalBuffers va_attrib_extbuf; | |
| 637 memset(&va_attrib_extbuf, 0, sizeof(va_attrib_extbuf)); | |
| 638 va_attrib_extbuf.pixel_format = | |
| 639 BufferFormatToVAFourCC(pixmap->GetBufferFormat()); | |
| 640 va_attrib_extbuf.width = pixmap_size.width(); | |
| 641 va_attrib_extbuf.height = pixmap_size.height(); | |
| 642 va_attrib_extbuf.data_size = pixmap_size.height() * dmabuf_pitch; | |
| 643 va_attrib_extbuf.num_planes = 1; | |
| 644 va_attrib_extbuf.pitches[0] = dmabuf_pitch; | |
| 645 va_attrib_extbuf.offsets[0] = 0; | |
| 646 va_attrib_extbuf.buffers = reinterpret_cast<unsigned long*>(&dmabuf_fd); | |
| 647 va_attrib_extbuf.num_buffers = 1; | |
| 648 va_attrib_extbuf.flags = 0; | |
| 649 va_attrib_extbuf.private_data = NULL; | |
| 650 | |
| 651 std::vector<VASurfaceAttrib> va_attribs; | |
| 652 va_attribs.resize(2); | |
| 653 | |
| 654 va_attribs[0].type = VASurfaceAttribMemoryType; | |
| 655 va_attribs[0].flags = VA_SURFACE_ATTRIB_SETTABLE; | |
| 656 va_attribs[0].value.type = VAGenericValueTypeInteger; | |
| 657 va_attribs[0].value.value.i = VA_SURFACE_ATTRIB_MEM_TYPE_DRM_PRIME; | |
| 658 | |
| 659 va_attribs[1].type = VASurfaceAttribExternalBufferDescriptor; | |
| 660 va_attribs[1].flags = VA_SURFACE_ATTRIB_SETTABLE; | |
| 661 va_attribs[1].value.type = VAGenericValueTypePointer; | |
| 662 va_attribs[1].value.value.p = &va_attrib_extbuf; | |
| 663 | |
| 664 scoped_refptr<VASurface> va_surface = | |
| 665 CreateUnownedSurface(BufferFormatToVARTFormat(pixmap->GetBufferFormat()), | |
| 666 pixmap_size, va_attribs); | |
| 667 if (!va_surface) { | |
| 668 LOG(ERROR) << "Failed to create VASurface for an Ozone NativePixmap"; | |
| 669 return nullptr; | |
| 670 } | |
| 671 | |
| 672 return va_surface; | |
| 673 } | |
| 674 | |
| 675 bool VaapiWrapper::ProcessPixmap( | |
| 676 const scoped_refptr<ui::NativePixmap>& source_pixmap, | |
| 677 scoped_refptr<ui::NativePixmap> target_pixmap) { | |
| 678 scoped_refptr<VASurface> va_surface = CreateVASurfaceForPixmap(source_pixmap); | |
| 679 if (!va_surface) { | |
| 680 LOG(ERROR) << "Failed creating VA Surface for source_pixmap"; | |
| 681 return false; | |
| 682 } | |
| 683 | |
| 684 scoped_refptr<VASurface> processed_va_surface = | |
| 685 CreateVASurfaceForPixmap(target_pixmap); | |
| 686 if (!processed_va_surface) { | |
| 687 LOG(ERROR) << "Failed creating processed VA Surface for pixmap"; | |
| 688 return false; | |
| 689 } | |
| 690 | |
| 691 if (!BlitSurface(va_surface, processed_va_surface)) { | |
| 692 LOG(ERROR) << "Failed scaling NativePixmap"; | |
| 693 return false; | |
| 694 } | |
| 695 | |
| 696 return true; | |
| 697 } | |
| 698 | |
| 699 #endif | |
| 700 | |
| 701 void VaapiWrapper::DestroyUnownedSurface(VASurfaceID va_surface_id) { | |
| 702 base::AutoLock auto_lock(*va_lock_); | |
| 703 | |
| 704 VAStatus va_res = vaDestroySurfaces(va_display_, &va_surface_id, 1); | |
| 705 VA_LOG_ON_ERROR(va_res, "vaDestroySurfaces on surface failed"); | |
| 706 } | |
| 707 | |
| 708 bool VaapiWrapper::SubmitBuffer(VABufferType va_buffer_type, | |
| 709 size_t size, | |
| 710 void* buffer) { | |
| 711 base::AutoLock auto_lock(*va_lock_); | |
| 712 | |
| 713 VABufferID buffer_id; | |
| 714 VAStatus va_res = vaCreateBuffer(va_display_, va_context_id_, | |
| 715 va_buffer_type, size, | |
| 716 1, buffer, &buffer_id); | |
| 717 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); | |
| 718 | |
| 719 switch (va_buffer_type) { | |
| 720 case VASliceParameterBufferType: | |
| 721 case VASliceDataBufferType: | |
| 722 case VAEncSliceParameterBufferType: | |
| 723 pending_slice_bufs_.push_back(buffer_id); | |
| 724 break; | |
| 725 | |
| 726 default: | |
| 727 pending_va_bufs_.push_back(buffer_id); | |
| 728 break; | |
| 729 } | |
| 730 | |
| 731 return true; | |
| 732 } | |
| 733 | |
| 734 bool VaapiWrapper::SubmitVAEncMiscParamBuffer( | |
| 735 VAEncMiscParameterType misc_param_type, | |
| 736 size_t size, | |
| 737 void* buffer) { | |
| 738 base::AutoLock auto_lock(*va_lock_); | |
| 739 | |
| 740 VABufferID buffer_id; | |
| 741 VAStatus va_res = vaCreateBuffer(va_display_, | |
| 742 va_context_id_, | |
| 743 VAEncMiscParameterBufferType, | |
| 744 sizeof(VAEncMiscParameterBuffer) + size, | |
| 745 1, | |
| 746 NULL, | |
| 747 &buffer_id); | |
| 748 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a VA buffer", false); | |
| 749 | |
| 750 void* data_ptr = NULL; | |
| 751 va_res = vaMapBuffer(va_display_, buffer_id, &data_ptr); | |
| 752 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); | |
| 753 if (va_res != VA_STATUS_SUCCESS) { | |
| 754 vaDestroyBuffer(va_display_, buffer_id); | |
| 755 return false; | |
| 756 } | |
| 757 | |
| 758 DCHECK(data_ptr); | |
| 759 | |
| 760 VAEncMiscParameterBuffer* misc_param = | |
| 761 reinterpret_cast<VAEncMiscParameterBuffer*>(data_ptr); | |
| 762 misc_param->type = misc_param_type; | |
| 763 memcpy(misc_param->data, buffer, size); | |
| 764 va_res = vaUnmapBuffer(va_display_, buffer_id); | |
| 765 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); | |
| 766 | |
| 767 pending_va_bufs_.push_back(buffer_id); | |
| 768 return true; | |
| 769 } | |
| 770 | |
| 771 void VaapiWrapper::DestroyPendingBuffers() { | |
| 772 base::AutoLock auto_lock(*va_lock_); | |
| 773 | |
| 774 for (const auto& pending_va_buf : pending_va_bufs_) { | |
| 775 VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf); | |
| 776 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); | |
| 777 } | |
| 778 | |
| 779 for (const auto& pending_slice_buf : pending_slice_bufs_) { | |
| 780 VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf); | |
| 781 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); | |
| 782 } | |
| 783 | |
| 784 pending_va_bufs_.clear(); | |
| 785 pending_slice_bufs_.clear(); | |
| 786 } | |
| 787 | |
| 788 bool VaapiWrapper::CreateCodedBuffer(size_t size, VABufferID* buffer_id) { | |
| 789 base::AutoLock auto_lock(*va_lock_); | |
| 790 VAStatus va_res = vaCreateBuffer(va_display_, | |
| 791 va_context_id_, | |
| 792 VAEncCodedBufferType, | |
| 793 size, | |
| 794 1, | |
| 795 NULL, | |
| 796 buffer_id); | |
| 797 VA_SUCCESS_OR_RETURN(va_res, "Failed to create a coded buffer", false); | |
| 798 | |
| 799 const auto is_new_entry = coded_buffers_.insert(*buffer_id).second; | |
| 800 DCHECK(is_new_entry); | |
| 801 return true; | |
| 802 } | |
| 803 | |
| 804 void VaapiWrapper::DestroyCodedBuffers() { | |
| 805 base::AutoLock auto_lock(*va_lock_); | |
| 806 | |
| 807 for (std::set<VABufferID>::const_iterator iter = coded_buffers_.begin(); | |
| 808 iter != coded_buffers_.end(); | |
| 809 ++iter) { | |
| 810 VAStatus va_res = vaDestroyBuffer(va_display_, *iter); | |
| 811 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); | |
| 812 } | |
| 813 | |
| 814 coded_buffers_.clear(); | |
| 815 } | |
| 816 | |
| 817 bool VaapiWrapper::Execute(VASurfaceID va_surface_id) { | |
| 818 base::AutoLock auto_lock(*va_lock_); | |
| 819 | |
| 820 DVLOG(4) << "Pending VA bufs to commit: " << pending_va_bufs_.size(); | |
| 821 DVLOG(4) << "Pending slice bufs to commit: " << pending_slice_bufs_.size(); | |
| 822 DVLOG(4) << "Target VA surface " << va_surface_id; | |
| 823 | |
| 824 // Get ready to execute for given surface. | |
| 825 VAStatus va_res = vaBeginPicture(va_display_, va_context_id_, | |
| 826 va_surface_id); | |
| 827 VA_SUCCESS_OR_RETURN(va_res, "vaBeginPicture failed", false); | |
| 828 | |
| 829 if (pending_va_bufs_.size() > 0) { | |
| 830 // Commit parameter and slice buffers. | |
| 831 va_res = vaRenderPicture(va_display_, | |
| 832 va_context_id_, | |
| 833 &pending_va_bufs_[0], | |
| 834 pending_va_bufs_.size()); | |
| 835 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for va_bufs failed", false); | |
| 836 } | |
| 837 | |
| 838 if (pending_slice_bufs_.size() > 0) { | |
| 839 va_res = vaRenderPicture(va_display_, | |
| 840 va_context_id_, | |
| 841 &pending_slice_bufs_[0], | |
| 842 pending_slice_bufs_.size()); | |
| 843 VA_SUCCESS_OR_RETURN(va_res, "vaRenderPicture for slices failed", false); | |
| 844 } | |
| 845 | |
| 846 // Instruct HW codec to start processing committed buffers. | |
| 847 // Does not block and the job is not finished after this returns. | |
| 848 va_res = vaEndPicture(va_display_, va_context_id_); | |
| 849 VA_SUCCESS_OR_RETURN(va_res, "vaEndPicture failed", false); | |
| 850 | |
| 851 return true; | |
| 852 } | |
| 853 | |
| 854 bool VaapiWrapper::ExecuteAndDestroyPendingBuffers(VASurfaceID va_surface_id) { | |
| 855 bool result = Execute(va_surface_id); | |
| 856 DestroyPendingBuffers(); | |
| 857 return result; | |
| 858 } | |
| 859 | |
| 860 #if defined(USE_X11) | |
| 861 bool VaapiWrapper::PutSurfaceIntoPixmap(VASurfaceID va_surface_id, | |
| 862 Pixmap x_pixmap, | |
| 863 gfx::Size dest_size) { | |
| 864 base::AutoLock auto_lock(*va_lock_); | |
| 865 | |
| 866 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); | |
| 867 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); | |
| 868 | |
| 869 // Put the data into an X Pixmap. | |
| 870 va_res = vaPutSurface(va_display_, | |
| 871 va_surface_id, | |
| 872 x_pixmap, | |
| 873 0, 0, dest_size.width(), dest_size.height(), | |
| 874 0, 0, dest_size.width(), dest_size.height(), | |
| 875 NULL, 0, 0); | |
| 876 VA_SUCCESS_OR_RETURN(va_res, "Failed putting surface to pixmap", false); | |
| 877 return true; | |
| 878 } | |
| 879 #endif // USE_X11 | |
| 880 | |
| 881 bool VaapiWrapper::GetDerivedVaImage(VASurfaceID va_surface_id, | |
| 882 VAImage* image, | |
| 883 void** mem) { | |
| 884 base::AutoLock auto_lock(*va_lock_); | |
| 885 | |
| 886 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); | |
| 887 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); | |
| 888 | |
| 889 // Derive a VAImage from the VASurface | |
| 890 va_res = vaDeriveImage(va_display_, va_surface_id, image); | |
| 891 VA_LOG_ON_ERROR(va_res, "vaDeriveImage failed"); | |
| 892 if (va_res != VA_STATUS_SUCCESS) | |
| 893 return false; | |
| 894 | |
| 895 // Map the VAImage into memory | |
| 896 va_res = vaMapBuffer(va_display_, image->buf, mem); | |
| 897 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); | |
| 898 if (va_res == VA_STATUS_SUCCESS) | |
| 899 return true; | |
| 900 | |
| 901 va_res = vaDestroyImage(va_display_, image->image_id); | |
| 902 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); | |
| 903 | |
| 904 return false; | |
| 905 } | |
| 906 | |
| 907 bool VaapiWrapper::GetVaImage(VASurfaceID va_surface_id, | |
| 908 VAImageFormat* format, | |
| 909 const gfx::Size& size, | |
| 910 VAImage* image, | |
| 911 void** mem) { | |
| 912 base::AutoLock auto_lock(*va_lock_); | |
| 913 | |
| 914 VAStatus va_res = vaSyncSurface(va_display_, va_surface_id); | |
| 915 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); | |
| 916 | |
| 917 va_res = | |
| 918 vaCreateImage(va_display_, format, size.width(), size.height(), image); | |
| 919 VA_SUCCESS_OR_RETURN(va_res, "vaCreateImage failed", false); | |
| 920 | |
| 921 va_res = vaGetImage(va_display_, va_surface_id, 0, 0, size.width(), | |
| 922 size.height(), image->image_id); | |
| 923 VA_LOG_ON_ERROR(va_res, "vaGetImage failed"); | |
| 924 | |
| 925 if (va_res == VA_STATUS_SUCCESS) { | |
| 926 // Map the VAImage into memory | |
| 927 va_res = vaMapBuffer(va_display_, image->buf, mem); | |
| 928 VA_LOG_ON_ERROR(va_res, "vaMapBuffer failed"); | |
| 929 } | |
| 930 | |
| 931 if (va_res != VA_STATUS_SUCCESS) { | |
| 932 va_res = vaDestroyImage(va_display_, image->image_id); | |
| 933 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); | |
| 934 return false; | |
| 935 } | |
| 936 | |
| 937 return true; | |
| 938 } | |
| 939 | |
| 940 void VaapiWrapper::ReturnVaImage(VAImage* image) { | |
| 941 base::AutoLock auto_lock(*va_lock_); | |
| 942 | |
| 943 VAStatus va_res = vaUnmapBuffer(va_display_, image->buf); | |
| 944 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); | |
| 945 | |
| 946 va_res = vaDestroyImage(va_display_, image->image_id); | |
| 947 VA_LOG_ON_ERROR(va_res, "vaDestroyImage failed"); | |
| 948 } | |
| 949 | |
| 950 static void DestroyVAImage(VADisplay va_display, VAImage image) { | |
| 951 if (image.image_id != VA_INVALID_ID) | |
| 952 vaDestroyImage(va_display, image.image_id); | |
| 953 } | |
| 954 | |
| 955 bool VaapiWrapper::UploadVideoFrameToSurface( | |
| 956 const scoped_refptr<media::VideoFrame>& frame, | |
| 957 VASurfaceID va_surface_id) { | |
| 958 base::AutoLock auto_lock(*va_lock_); | |
| 959 | |
| 960 VAImage image; | |
| 961 VAStatus va_res = vaDeriveImage(va_display_, va_surface_id, &image); | |
| 962 VA_SUCCESS_OR_RETURN(va_res, "vaDeriveImage failed", false); | |
| 963 base::ScopedClosureRunner vaimage_deleter( | |
| 964 base::Bind(&DestroyVAImage, va_display_, image)); | |
| 965 | |
| 966 if (image.format.fourcc != VA_FOURCC_NV12) { | |
| 967 LOG(ERROR) << "Unsupported image format: " << image.format.fourcc; | |
| 968 return false; | |
| 969 } | |
| 970 | |
| 971 if (gfx::Rect(image.width, image.height) < gfx::Rect(frame->coded_size())) { | |
| 972 LOG(ERROR) << "Buffer too small to fit the frame."; | |
| 973 return false; | |
| 974 } | |
| 975 | |
| 976 void* image_ptr = NULL; | |
| 977 va_res = vaMapBuffer(va_display_, image.buf, &image_ptr); | |
| 978 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); | |
| 979 DCHECK(image_ptr); | |
| 980 | |
| 981 int ret = 0; | |
| 982 { | |
| 983 base::AutoUnlock auto_unlock(*va_lock_); | |
| 984 ret = libyuv::I420ToNV12( | |
| 985 frame->data(media::VideoFrame::kYPlane), | |
| 986 frame->stride(media::VideoFrame::kYPlane), | |
| 987 frame->data(media::VideoFrame::kUPlane), | |
| 988 frame->stride(media::VideoFrame::kUPlane), | |
| 989 frame->data(media::VideoFrame::kVPlane), | |
| 990 frame->stride(media::VideoFrame::kVPlane), | |
| 991 static_cast<uint8_t*>(image_ptr) + image.offsets[0], image.pitches[0], | |
| 992 static_cast<uint8_t*>(image_ptr) + image.offsets[1], image.pitches[1], | |
| 993 image.width, image.height); | |
| 994 } | |
| 995 | |
| 996 va_res = vaUnmapBuffer(va_display_, image.buf); | |
| 997 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); | |
| 998 | |
| 999 return ret == 0; | |
| 1000 } | |
| 1001 | |
| 1002 bool VaapiWrapper::DownloadAndDestroyCodedBuffer(VABufferID buffer_id, | |
| 1003 VASurfaceID sync_surface_id, | |
| 1004 uint8_t* target_ptr, | |
| 1005 size_t target_size, | |
| 1006 size_t* coded_data_size) { | |
| 1007 base::AutoLock auto_lock(*va_lock_); | |
| 1008 | |
| 1009 VAStatus va_res = vaSyncSurface(va_display_, sync_surface_id); | |
| 1010 VA_SUCCESS_OR_RETURN(va_res, "Failed syncing surface", false); | |
| 1011 | |
| 1012 VACodedBufferSegment* buffer_segment = NULL; | |
| 1013 va_res = vaMapBuffer( | |
| 1014 va_display_, buffer_id, reinterpret_cast<void**>(&buffer_segment)); | |
| 1015 VA_SUCCESS_OR_RETURN(va_res, "vaMapBuffer failed", false); | |
| 1016 DCHECK(target_ptr); | |
| 1017 | |
| 1018 { | |
| 1019 base::AutoUnlock auto_unlock(*va_lock_); | |
| 1020 *coded_data_size = 0; | |
| 1021 | |
| 1022 while (buffer_segment) { | |
| 1023 DCHECK(buffer_segment->buf); | |
| 1024 | |
| 1025 if (buffer_segment->size > target_size) { | |
| 1026 LOG(ERROR) << "Insufficient output buffer size"; | |
| 1027 break; | |
| 1028 } | |
| 1029 | |
| 1030 memcpy(target_ptr, buffer_segment->buf, buffer_segment->size); | |
| 1031 | |
| 1032 target_ptr += buffer_segment->size; | |
| 1033 *coded_data_size += buffer_segment->size; | |
| 1034 target_size -= buffer_segment->size; | |
| 1035 | |
| 1036 buffer_segment = | |
| 1037 reinterpret_cast<VACodedBufferSegment*>(buffer_segment->next); | |
| 1038 } | |
| 1039 } | |
| 1040 | |
| 1041 va_res = vaUnmapBuffer(va_display_, buffer_id); | |
| 1042 VA_LOG_ON_ERROR(va_res, "vaUnmapBuffer failed"); | |
| 1043 | |
| 1044 va_res = vaDestroyBuffer(va_display_, buffer_id); | |
| 1045 VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); | |
| 1046 | |
| 1047 const auto was_found = coded_buffers_.erase(buffer_id); | |
| 1048 DCHECK(was_found); | |
| 1049 | |
| 1050 return buffer_segment == NULL; | |
| 1051 } | |
| 1052 | |
| 1053 bool VaapiWrapper::BlitSurface( | |
| 1054 const scoped_refptr<VASurface>& va_surface_src, | |
| 1055 const scoped_refptr<VASurface>& va_surface_dest) { | |
| 1056 base::AutoLock auto_lock(*va_lock_); | |
| 1057 | |
| 1058 // Initialize the post processing engine if not already done. | |
| 1059 if (va_vpp_buffer_id_ == VA_INVALID_ID) { | |
| 1060 if (!InitializeVpp_Locked()) | |
| 1061 return false; | |
| 1062 } | |
| 1063 | |
| 1064 VAProcPipelineParameterBuffer* pipeline_param; | |
| 1065 VA_SUCCESS_OR_RETURN(vaMapBuffer(va_display_, va_vpp_buffer_id_, | |
| 1066 reinterpret_cast<void**>(&pipeline_param)), | |
| 1067 "Couldn't map vpp buffer", false); | |
| 1068 | |
| 1069 memset(pipeline_param, 0, sizeof *pipeline_param); | |
| 1070 const gfx::Size src_size = va_surface_src->size(); | |
| 1071 const gfx::Size dest_size = va_surface_dest->size(); | |
| 1072 | |
| 1073 VARectangle input_region; | |
| 1074 input_region.x = input_region.y = 0; | |
| 1075 input_region.width = src_size.width(); | |
| 1076 input_region.height = src_size.height(); | |
| 1077 pipeline_param->surface_region = &input_region; | |
| 1078 pipeline_param->surface = va_surface_src->id(); | |
| 1079 pipeline_param->surface_color_standard = VAProcColorStandardNone; | |
| 1080 | |
| 1081 VARectangle output_region; | |
| 1082 output_region.x = output_region.y = 0; | |
| 1083 output_region.width = dest_size.width(); | |
| 1084 output_region.height = dest_size.height(); | |
| 1085 pipeline_param->output_region = &output_region; | |
| 1086 pipeline_param->output_background_color = 0xff000000; | |
| 1087 pipeline_param->output_color_standard = VAProcColorStandardNone; | |
| 1088 pipeline_param->filter_flags = VA_FILTER_SCALING_HQ; | |
| 1089 | |
| 1090 VA_SUCCESS_OR_RETURN(vaUnmapBuffer(va_display_, va_vpp_buffer_id_), | |
| 1091 "Couldn't unmap vpp buffer", false); | |
| 1092 | |
| 1093 VA_SUCCESS_OR_RETURN( | |
| 1094 vaBeginPicture(va_display_, va_vpp_context_id_, va_surface_dest->id()), | |
| 1095 "Couldn't begin picture", false); | |
| 1096 | |
| 1097 VA_SUCCESS_OR_RETURN( | |
| 1098 vaRenderPicture(va_display_, va_vpp_context_id_, &va_vpp_buffer_id_, 1), | |
| 1099 "Couldn't render picture", false); | |
| 1100 | |
| 1101 VA_SUCCESS_OR_RETURN(vaEndPicture(va_display_, va_vpp_context_id_), | |
| 1102 "Couldn't end picture", false); | |
| 1103 | |
| 1104 return true; | |
| 1105 } | |
| 1106 | |
| 1107 bool VaapiWrapper::InitializeVpp_Locked() { | |
| 1108 va_lock_->AssertAcquired(); | |
| 1109 | |
| 1110 VA_SUCCESS_OR_RETURN( | |
| 1111 vaCreateConfig(va_display_, VAProfileNone, VAEntrypointVideoProc, NULL, 0, | |
| 1112 &va_vpp_config_id_), | |
| 1113 "Couldn't create config", false); | |
| 1114 | |
| 1115 // The size of the picture for the context is irrelevant in the case | |
| 1116 // of the VPP, just passing 1x1. | |
| 1117 VA_SUCCESS_OR_RETURN(vaCreateContext(va_display_, va_vpp_config_id_, 1, 1, 0, | |
| 1118 NULL, 0, &va_vpp_context_id_), | |
| 1119 "Couldn't create context", false); | |
| 1120 | |
| 1121 VA_SUCCESS_OR_RETURN(vaCreateBuffer(va_display_, va_vpp_context_id_, | |
| 1122 VAProcPipelineParameterBufferType, | |
| 1123 sizeof(VAProcPipelineParameterBuffer), 1, | |
| 1124 NULL, &va_vpp_buffer_id_), | |
| 1125 "Couldn't create buffer", false); | |
| 1126 | |
| 1127 return true; | |
| 1128 } | |
| 1129 | |
| 1130 void VaapiWrapper::DeinitializeVpp() { | |
| 1131 base::AutoLock auto_lock(*va_lock_); | |
| 1132 | |
| 1133 if (va_vpp_buffer_id_ != VA_INVALID_ID) { | |
| 1134 vaDestroyBuffer(va_display_, va_vpp_buffer_id_); | |
| 1135 va_vpp_buffer_id_ = VA_INVALID_ID; | |
| 1136 } | |
| 1137 if (va_vpp_context_id_ != VA_INVALID_ID) { | |
| 1138 vaDestroyContext(va_display_, va_vpp_context_id_); | |
| 1139 va_vpp_context_id_ = VA_INVALID_ID; | |
| 1140 } | |
| 1141 if (va_vpp_config_id_ != VA_INVALID_ID) { | |
| 1142 vaDestroyConfig(va_display_, va_vpp_config_id_); | |
| 1143 va_vpp_config_id_ = VA_INVALID_ID; | |
| 1144 } | |
| 1145 } | |
| 1146 | |
| 1147 // static | |
| 1148 void VaapiWrapper::PreSandboxInitialization() { | |
| 1149 #if defined(USE_OZONE) | |
| 1150 const char kDriRenderNode0Path[] = "/dev/dri/renderD128"; | |
| 1151 base::File drm_file = base::File( | |
| 1152 base::FilePath::FromUTF8Unsafe(kDriRenderNode0Path), | |
| 1153 base::File::FLAG_OPEN | base::File::FLAG_READ | base::File::FLAG_WRITE); | |
| 1154 if (drm_file.IsValid()) | |
| 1155 va_display_state_.Get().SetDrmFd(drm_file.GetPlatformFile()); | |
| 1156 #endif | |
| 1157 } | |
| 1158 | |
| 1159 // static | |
| 1160 bool VaapiWrapper::PostSandboxInitialization() { | |
| 1161 StubPathMap paths; | |
| 1162 | |
| 1163 paths[kModuleVa].push_back("libva.so.1"); | |
| 1164 | |
| 1165 #if defined(USE_X11) | |
| 1166 paths[kModuleVa_x11].push_back("libva-x11.so.1"); | |
| 1167 #elif defined(USE_OZONE) | |
| 1168 paths[kModuleVa_drm].push_back("libva-drm.so.1"); | |
| 1169 #endif | |
| 1170 | |
| 1171 return InitializeStubs(paths); | |
| 1172 } | |
| 1173 | |
| 1174 VaapiWrapper::LazyProfileInfos::LazyProfileInfos() { | |
| 1175 static_assert(arraysize(supported_profiles_) == kCodecModeMax, | |
| 1176 "The array size of supported profile is incorrect."); | |
| 1177 scoped_refptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper()); | |
| 1178 if (!vaapi_wrapper->VaInitialize(base::Bind(&base::DoNothing))) | |
| 1179 return; | |
| 1180 for (size_t i = 0; i < kCodecModeMax; ++i) { | |
| 1181 supported_profiles_[i] = | |
| 1182 vaapi_wrapper->GetSupportedProfileInfosForCodecModeInternal( | |
| 1183 static_cast<CodecMode>(i)); | |
| 1184 } | |
| 1185 } | |
| 1186 | |
| 1187 VaapiWrapper::LazyProfileInfos::~LazyProfileInfos() { | |
| 1188 } | |
| 1189 | |
| 1190 std::vector<VaapiWrapper::ProfileInfo> | |
| 1191 VaapiWrapper::LazyProfileInfos::GetSupportedProfileInfosForCodecMode( | |
| 1192 CodecMode mode) { | |
| 1193 return supported_profiles_[mode]; | |
| 1194 } | |
| 1195 | |
| 1196 bool VaapiWrapper::LazyProfileInfos::IsProfileSupported( | |
| 1197 CodecMode mode, VAProfile va_profile) { | |
| 1198 for (const auto& profile : supported_profiles_[mode]) { | |
| 1199 if (profile.va_profile == va_profile) | |
| 1200 return true; | |
| 1201 } | |
| 1202 return false; | |
| 1203 } | |
| 1204 | |
| 1205 VaapiWrapper::VADisplayState::VADisplayState() | |
| 1206 : refcount_(0), | |
| 1207 va_display_(nullptr), | |
| 1208 major_version_(-1), | |
| 1209 minor_version_(-1), | |
| 1210 va_initialized_(false) {} | |
| 1211 | |
| 1212 VaapiWrapper::VADisplayState::~VADisplayState() {} | |
| 1213 | |
| 1214 bool VaapiWrapper::VADisplayState::Initialize() { | |
| 1215 va_lock_.AssertAcquired(); | |
| 1216 if (refcount_++ == 0) { | |
| 1217 #if defined(USE_X11) | |
| 1218 va_display_ = vaGetDisplay(gfx::GetXDisplay()); | |
| 1219 #elif defined(USE_OZONE) | |
| 1220 va_display_ = vaGetDisplayDRM(drm_fd_.get()); | |
| 1221 #endif // USE_X11 | |
| 1222 | |
| 1223 if (!vaDisplayIsValid(va_display_)) { | |
| 1224 LOG(ERROR) << "Could not get a valid VA display"; | |
| 1225 return false; | |
| 1226 } | |
| 1227 | |
| 1228 VAStatus va_res = | |
| 1229 vaInitialize(va_display_, &major_version_, &minor_version_); | |
| 1230 if (va_res != VA_STATUS_SUCCESS) { | |
| 1231 LOG(WARNING) << "vaInitialize failed: " << vaErrorStr(va_res); | |
| 1232 return false; | |
| 1233 } | |
| 1234 | |
| 1235 va_initialized_ = true; | |
| 1236 DVLOG(1) << "VAAPI version: " << major_version_ << "." << minor_version_; | |
| 1237 } | |
| 1238 | |
| 1239 if (VAAPIVersionLessThan(0, 34)) { | |
| 1240 LOG(ERROR) << "VAAPI version < 0.34 is not supported."; | |
| 1241 return false; | |
| 1242 } | |
| 1243 return true; | |
| 1244 } | |
| 1245 | |
| 1246 void VaapiWrapper::VADisplayState::Deinitialize(VAStatus* status) { | |
| 1247 va_lock_.AssertAcquired(); | |
| 1248 if (--refcount_ > 0) | |
| 1249 return; | |
| 1250 | |
| 1251 // Must check if vaInitialize completed successfully, to work around a bug in | |
| 1252 // libva. The bug was fixed upstream: | |
| 1253 // http://lists.freedesktop.org/archives/libva/2013-July/001807.html | |
| 1254 // TODO(mgiuca): Remove this check, and the |va_initialized_| variable, once | |
| 1255 // the fix has rolled out sufficiently. | |
| 1256 if (va_initialized_ && va_display_) { | |
| 1257 *status = vaTerminate(va_display_); | |
| 1258 } | |
| 1259 va_initialized_ = false; | |
| 1260 va_display_ = nullptr; | |
| 1261 } | |
| 1262 | |
| 1263 #if defined(USE_OZONE) | |
| 1264 void VaapiWrapper::VADisplayState::SetDrmFd(base::PlatformFile fd) { | |
| 1265 drm_fd_.reset(HANDLE_EINTR(dup(fd))); | |
| 1266 } | |
| 1267 #endif // USE_OZONE | |
| 1268 | |
| 1269 bool VaapiWrapper::VADisplayState::VAAPIVersionLessThan(int major, int minor) { | |
| 1270 return (major_version_ < major) || | |
| 1271 (major_version_ == major && minor_version_ < minor); | |
| 1272 } | |
| 1273 | |
| 1274 } // namespace content | |
| OLD | NEW |