Chromium Code Reviews| Index: content/common/gpu/media/vaapi_wrapper.cc |
| diff --git a/content/common/gpu/media/vaapi_wrapper.cc b/content/common/gpu/media/vaapi_wrapper.cc |
| index 588fee3981e59072c6118996099258acaa51e82b..fcc6a4608528af0a7c27d46f56745dc67383461a 100644 |
| --- a/content/common/gpu/media/vaapi_wrapper.cc |
| +++ b/content/common/gpu/media/vaapi_wrapper.cc |
| @@ -8,12 +8,14 @@ |
| #include "base/bind.h" |
| #include "base/callback_helpers.h" |
| +#include "base/command_line.h" |
| #include "base/logging.h" |
| #include "base/numerics/safe_conversions.h" |
| #include "base/sys_info.h" |
| // Auto-generated for dlopen libva libraries |
| #include "content/common/gpu/media/va_stubs.h" |
| #include "content/common/gpu/media/vaapi_picture.h" |
| +#include "content/public/common/content_switches.h" |
| #include "third_party/libyuv/include/libyuv.h" |
| #include "ui/gl/gl_bindings.h" |
| #if defined(USE_X11) |
| @@ -56,6 +58,13 @@ using content_common_gpu_media::StubPathMap; |
| namespace content { |
| +// Maximum framerate of encoded profile. This value is an arbitary limit |
| +// and not taken from HW documentation. |
| +const int kMaxEncoderFramerate = 30; |
| + |
| +base::LazyInstance<VaapiWrapper::LazyProfileInfos> |
| + VaapiWrapper::profile_infos_ = LAZY_INSTANCE_INITIALIZER; |
| + |
| // Config attributes common for both encode and decode. |
| static const VAConfigAttrib kCommonVAConfigAttribs[] = { |
| {VAConfigAttribRTFormat, VA_RT_FORMAT_YUV420}, |
| @@ -98,41 +107,6 @@ static std::vector<VAConfigAttrib> GetRequiredAttribs( |
| return required_attribs; |
| } |
| -// Maps Profile enum values to VaProfile values. |
| -static VAProfile ProfileToVAProfile( |
| - media::VideoCodecProfile profile, |
| - const std::vector<VAProfile>& supported_profiles) { |
| - |
| - VAProfile va_profile = VAProfileNone; |
| - for (size_t i = 0; i < arraysize(kProfileMap); i++) { |
| - if (kProfileMap[i].profile == profile) { |
| - va_profile = kProfileMap[i].va_profile; |
| - break; |
| - } |
| - } |
| - |
| - bool supported = std::find(supported_profiles.begin(), |
| - supported_profiles.end(), |
| - va_profile) != supported_profiles.end(); |
| - |
| - if (!supported && va_profile == VAProfileH264Baseline) { |
| - // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips |
| - // the information whether the profile is constrained or not, so we have no |
| - // way to know here. Try for baseline first, but if it is not supported, |
| - // try constrained baseline and hope this is what it actually is |
| - // (which in practice is true for a great majority of cases). |
| - if (std::find(supported_profiles.begin(), |
| - supported_profiles.end(), |
| - VAProfileH264ConstrainedBaseline) != |
| - supported_profiles.end()) { |
| - va_profile = VAProfileH264ConstrainedBaseline; |
| - DVLOG(1) << "Falling back to constrained baseline profile."; |
| - } |
| - } |
| - |
| - return va_profile; |
| -} |
| - |
| VASurface::VASurface(VASurfaceID va_surface_id, |
| const gfx::Size& size, |
| const ReleaseCB& release_cb) |
| @@ -162,6 +136,7 @@ VaapiWrapper::~VaapiWrapper() { |
| Deinitialize(); |
| } |
| +// static |
| scoped_ptr<VaapiWrapper> VaapiWrapper::Create( |
| CodecMode mode, |
| VAProfile va_profile, |
| @@ -170,12 +145,14 @@ scoped_ptr<VaapiWrapper> VaapiWrapper::Create( |
| if (!vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) |
| return nullptr; |
| + |
| if (!vaapi_wrapper->Initialize(mode, va_profile)) |
| return nullptr; |
| return vaapi_wrapper.Pass(); |
| } |
| +// static |
| scoped_ptr<VaapiWrapper> VaapiWrapper::CreateForVideoCodec( |
| CodecMode mode, |
| media::VideoCodecProfile profile, |
| @@ -185,42 +162,50 @@ scoped_ptr<VaapiWrapper> VaapiWrapper::CreateForVideoCodec( |
| if (!vaapi_wrapper->VaInitialize(report_error_to_uma_cb)) |
| return nullptr; |
| - std::vector<VAProfile> supported_va_profiles; |
| - if (!vaapi_wrapper->GetSupportedVaProfiles(&supported_va_profiles)) |
| - return nullptr; |
| - |
| - VAProfile va_profile = ProfileToVAProfile(profile, supported_va_profiles); |
| + VAProfile va_profile = ProfileToVAProfile(profile, mode); |
| if (!vaapi_wrapper->Initialize(mode, va_profile)) |
| return nullptr; |
| return vaapi_wrapper.Pass(); |
| } |
| -std::vector<media::VideoCodecProfile> VaapiWrapper::GetSupportedEncodeProfiles( |
| - const base::Closure& report_error_to_uma_cb) { |
| - std::vector<media::VideoCodecProfile> supported_profiles; |
| - |
| - scoped_ptr<VaapiWrapper> wrapper(new VaapiWrapper()); |
| - if (!wrapper->VaInitialize(report_error_to_uma_cb)) { |
| - return supported_profiles; |
| - } |
| - |
| - std::vector<VAProfile> va_profiles; |
| - if (!wrapper->GetSupportedVaProfiles(&va_profiles)) |
| - return supported_profiles; |
| - |
| - std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(kEncode); |
| - for (size_t i = 0; i < arraysize(kProfileMap); i++) { |
| - VAProfile va_profile = |
| - ProfileToVAProfile(kProfileMap[i].profile, va_profiles); |
| - if (va_profile != VAProfileNone && |
| - wrapper->IsEntrypointSupported(va_profile, VAEntrypointEncSlice) && |
| - wrapper->AreAttribsSupported( |
| - va_profile, VAEntrypointEncSlice, required_attribs)) { |
| - supported_profiles.push_back(kProfileMap[i].profile); |
| +// static |
| +std::vector<media::VideoEncodeAccelerator::SupportedProfile> |
| +VaapiWrapper::GetSupportedEncodeProfiles() { |
| + std::vector<media::VideoEncodeAccelerator::SupportedProfile> profiles; |
| + const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| + if (cmd_line->HasSwitch(switches::kDisableVaapiAcceleratedVideoEncode)) |
| + return profiles; |
| + |
| + std::vector<ProfileInfo> encode_profile_infos = |
| + profile_infos_.Get().GetSupportedProfileInfosForCodecMode(kEncode); |
| + |
| + for (size_t i = 0; i < arraysize(kProfileMap); ++i) { |
| + VAProfile va_profile = ProfileToVAProfile(kProfileMap[i].profile, kEncode); |
| + if (va_profile == VAProfileNone) |
| + continue; |
| + for (const auto& profile_info : encode_profile_infos) { |
| + if (profile_info.va_profile == va_profile) { |
| + media::VideoEncodeAccelerator::SupportedProfile profile; |
| + profile.profile = kProfileMap[i].profile; |
| + profile.max_resolution = profile_info.max_resolution; |
| + profile.max_framerate_numerator = kMaxEncoderFramerate; |
| + profile.max_framerate_denominator = 1; |
| + profiles.push_back(profile); |
| + break; |
| + } |
| } |
| } |
| - return supported_profiles; |
| + return profiles; |
| +} |
| + |
| +// static |
| +std::vector<VaapiWrapper::ProfileInfo> |
| +VaapiWrapper::GetSupportedProfileInfosForCodecMode(CodecMode mode) { |
| + scoped_ptr<VaapiWrapper> vaapi_wrapper(new VaapiWrapper()); |
| + if (!vaapi_wrapper->VaInitialize(base::Bind(&base::DoNothing))) |
| + return std::vector<ProfileInfo>(); |
| + return vaapi_wrapper->GetSupportedProfileInfosForCodecModeInternal(mode); |
| } |
| void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { |
| @@ -236,6 +221,73 @@ void VaapiWrapper::TryToSetVADisplayAttributeToLocalGPU() { |
| DVLOG(2) << "vaSetDisplayAttributes unsupported, ignoring by default."; |
| } |
| +// static |
| +VAProfile VaapiWrapper::ProfileToVAProfile( |
| + media::VideoCodecProfile profile, CodecMode mode) { |
| + VAProfile va_profile = VAProfileNone; |
| + for (size_t i = 0; i < arraysize(kProfileMap); ++i) { |
| + if (kProfileMap[i].profile == profile) { |
| + va_profile = kProfileMap[i].va_profile; |
| + break; |
| + } |
| + } |
| + if (!profile_infos_.Get().IsProfileSupported(mode, va_profile) && |
| + va_profile == VAProfileH264Baseline) { |
| + // crbug.com/345569: media::ProfileIDToVideoCodecProfile() currently strips |
| + // the information whether the profile is constrained or not, so we have no |
| + // way to know here. Try for baseline first, but if it is not supported, |
| + // try constrained baseline and hope this is what it actually is |
| + // (which in practice is true for a great majority of cases). |
| + if (profile_infos_.Get().IsProfileSupported( |
| + mode, VAProfileH264ConstrainedBaseline)) { |
| + va_profile = VAProfileH264ConstrainedBaseline; |
| + DVLOG(1) << "Fall back to constrained baseline profile."; |
| + } |
| + } |
| + return va_profile; |
| +} |
| + |
| +std::vector<VaapiWrapper::ProfileInfo> |
| +VaapiWrapper::GetSupportedProfileInfosForCodecModeInternal(CodecMode mode) { |
| + std::vector<ProfileInfo> supported_profile_infos; |
| + std::vector<VAProfile> va_profiles; |
| + if (!GetSupportedVaProfiles(&va_profiles)) |
| + return supported_profile_infos; |
| + |
| + std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode); |
| + VAEntrypoint entrypoint = |
| + (mode == kEncode ? VAEntrypointEncSlice: VAEntrypointVLD); |
| + |
| + base::AutoLock auto_lock(va_lock_); |
| + for (const auto& va_profile : va_profiles) { |
| + if (!IsEntrypointSupported_Locked(va_profile, entrypoint)) |
| + continue; |
| + if (!AreAttribsSupported_Locked(va_profile, entrypoint, required_attribs)) |
| + continue; |
| + VAConfigID config_id; |
| + VAStatus va_res = vaCreateConfig( |
|
wuchengli
2015/03/09 05:17:30
Pawel and I discussed. vaCreateConfig should be mo
henryhsu
2015/03/09 09:21:53
Done.
|
| + va_display_, |
| + va_profile, |
| + entrypoint, |
| + &required_attribs[0], |
| + required_attribs.size(), |
| + &config_id); |
| + if (va_res != VA_STATUS_SUCCESS) { |
| + LOG_VA_ERROR_AND_REPORT(va_res, "vaCreateConfig failed"); |
| + continue; |
| + } |
| + ProfileInfo profile_info; |
| + if (!GetMaxResolutionForVAConfigID( |
| + config_id, &profile_info.max_resolution)) { |
| + LOG(ERROR) << "GetMaxResolution failed by va_profile " << va_profile; |
| + continue; |
| + } |
| + profile_info.va_profile = va_profile; |
| + supported_profile_infos.push_back(profile_info); |
| + } |
| + return supported_profile_infos; |
| +} |
| + |
| bool VaapiWrapper::VaInitialize(const base::Closure& report_error_to_uma_cb) { |
| static bool vaapi_functions_initialized = PostSandboxInitialization(); |
| if (!vaapi_functions_initialized) { |
| @@ -304,9 +356,9 @@ bool VaapiWrapper::GetSupportedVaProfiles(std::vector<VAProfile>* profiles) { |
| return true; |
| } |
| -bool VaapiWrapper::IsEntrypointSupported(VAProfile va_profile, |
| - VAEntrypoint entrypoint) { |
| - base::AutoLock auto_lock(va_lock_); |
| +bool VaapiWrapper::IsEntrypointSupported_Locked(VAProfile va_profile, |
| + VAEntrypoint entrypoint) { |
| + va_lock_.AssertAcquired(); |
| // Query the driver for supported entrypoints. |
| int max_entrypoints = vaMaxNumEntrypoints(va_display_); |
| std::vector<VAEntrypoint> supported_entrypoints( |
| @@ -334,11 +386,11 @@ bool VaapiWrapper::IsEntrypointSupported(VAProfile va_profile, |
| return true; |
| } |
| -bool VaapiWrapper::AreAttribsSupported( |
| +bool VaapiWrapper::AreAttribsSupported_Locked( |
| VAProfile va_profile, |
| VAEntrypoint entrypoint, |
| const std::vector<VAConfigAttrib>& required_attribs) { |
| - base::AutoLock auto_lock(va_lock_); |
| + va_lock_.AssertAcquired(); |
| // Query the driver for required attributes. |
| std::vector<VAConfigAttrib> attribs = required_attribs; |
| for (size_t i = 0; i < required_attribs.size(); ++i) |
| @@ -360,21 +412,54 @@ bool VaapiWrapper::AreAttribsSupported( |
| return true; |
| } |
| -bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) { |
| - if (va_profile == VAProfileNone) { |
| - DVLOG(1) << "Unsupported profile"; |
| +bool VaapiWrapper::GetMaxResolutionForVAConfigID(VAConfigID va_config_id, |
| + gfx::Size* resolution) { |
| + va_lock_.AssertAcquired(); |
| + unsigned int num_attribs; |
| + |
| + // Calls vaQuerySurfaceAttributes twice. The first time is to get the number |
| + // of attributes to prepare the space and the second time is to get all |
| + // attributes. |
| + VAStatus va_res; |
| + va_res = vaQuerySurfaceAttributes( |
| + va_display_, va_config_id, nullptr, &num_attribs); |
| + VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); |
| + if (!num_attribs) |
| return false; |
| + |
| + std::vector<VASurfaceAttrib> attrib_list( |
| + base::checked_cast<size_t>(num_attribs)); |
| + |
| + va_res = vaQuerySurfaceAttributes( |
| + va_display_, va_config_id, &attrib_list[0], &num_attribs); |
| + VA_SUCCESS_OR_RETURN(va_res, "vaQuerySurfaceAttributes failed", false); |
| + |
| + resolution->SetSize(0, 0); |
| + for (const auto& attrib : attrib_list) { |
| + if (attrib.type == VASurfaceAttribMaxWidth) |
| + resolution->set_width(attrib.value.value.i); |
| + else if (attrib.type == VASurfaceAttribMaxHeight) |
| + resolution->set_height(attrib.value.value.i); |
| } |
| - VAEntrypoint entrypoint = |
| - (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD); |
| - if (!IsEntrypointSupported(va_profile, entrypoint)) |
| + if (resolution->IsEmpty()) { |
| + LOG(ERROR) << "Codec resolution " << resolution->ToString() |
| + << " cannot be zero."; |
| return false; |
| - std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode); |
| - if (!AreAttribsSupported(va_profile, entrypoint, required_attribs)) |
| + } |
| + return true; |
| +} |
| + |
| +bool VaapiWrapper::Initialize(CodecMode mode, VAProfile va_profile) { |
| + if (!profile_infos_.Get().IsProfileSupported(mode, va_profile)) { |
| + DVLOG(1) << "Unsupported va profile: " << va_profile; |
| return false; |
| + } |
| TryToSetVADisplayAttributeToLocalGPU(); |
| + VAEntrypoint entrypoint = |
| + (mode == kEncode ? VAEntrypointEncSlice : VAEntrypointVLD); |
| + std::vector<VAConfigAttrib> required_attribs = GetRequiredAttribs(mode); |
| base::AutoLock auto_lock(va_lock_); |
| VAStatus va_res = vaCreateConfig(va_display_, |
| va_profile, |
| @@ -573,13 +658,13 @@ bool VaapiWrapper::SubmitVAEncMiscParamBuffer( |
| void VaapiWrapper::DestroyPendingBuffers() { |
| base::AutoLock auto_lock(va_lock_); |
| - for (size_t i = 0; i < pending_va_bufs_.size(); ++i) { |
| - VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_bufs_[i]); |
| + for (const auto& pending_va_buf : pending_va_bufs_) { |
| + VAStatus va_res = vaDestroyBuffer(va_display_, pending_va_buf); |
| VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); |
| } |
| - for (size_t i = 0; i < pending_slice_bufs_.size(); ++i) { |
| - VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_bufs_[i]); |
| + for (const auto& pending_slice_buf : pending_slice_bufs_) { |
| + VAStatus va_res = vaDestroyBuffer(va_display_, pending_slice_buf); |
| VA_LOG_ON_ERROR(va_res, "vaDestroyBuffer failed"); |
| } |
| @@ -959,4 +1044,32 @@ bool VaapiWrapper::PostSandboxInitialization() { |
| return InitializeStubs(paths); |
| } |
| +VaapiWrapper::LazyProfileInfos::LazyProfileInfos() { |
| + static_assert(arraysize(supported_profiles_) == kCodecModeMax, |
| + "The array size of supported profile is incorrect."); |
| + for (size_t i = 0; i < kCodecModeMax; ++i) { |
| + supported_profiles_[i] = |
| + VaapiWrapper::GetSupportedProfileInfosForCodecMode( |
| + static_cast<CodecMode>(i)); |
| + } |
| +} |
| + |
| +VaapiWrapper::LazyProfileInfos::~LazyProfileInfos() { |
| +} |
| + |
| +std::vector<VaapiWrapper::ProfileInfo> |
| +VaapiWrapper::LazyProfileInfos::GetSupportedProfileInfosForCodecMode( |
| + CodecMode mode) { |
| + return supported_profiles_[mode]; |
| +} |
| + |
| +bool VaapiWrapper::LazyProfileInfos::IsProfileSupported( |
| + CodecMode mode, VAProfile va_profile) { |
| + for (const auto& profile : supported_profiles_[mode]) { |
| + if (profile.va_profile == va_profile) |
| + return true; |
| + } |
| + return false; |
| +} |
| + |
| } // namespace content |