| // Copyright 2016 The Chromium Authors |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/browser/media/cdm_registry_impl.h" |
| |
| #include <stddef.h> |
| |
| #include "base/check.h" |
| #include "base/command_line.h" |
| #include "base/feature_list.h" |
| #include "base/logging.h" |
| #include "base/metrics/histogram_functions.h" |
| #include "base/no_destructor.h" |
| #include "base/strings/string_split.h" |
| #include "base/task/bind_post_task.h" |
| #include "base/types/expected.h" |
| #include "base/types/optional_util.h" |
| #include "build/build_config.h" |
| #include "content/public/common/cdm_info.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/common/content_switches.h" |
| #include "media/base/cdm_capability.h" |
| #include "media/base/key_system_capability.h" |
| #include "media/base/key_system_names.h" |
| #include "media/base/key_systems.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/video_codecs.h" |
| #include "media/media_buildflags.h" |
| #include "media/mojo/buildflags.h" |
| |
| #if BUILDFLAG(IS_ANDROID) |
| #include "content/browser/media/key_system_support_android.h" |
| #include "media/base/android/media_drm_bridge.h" |
| #endif |
| |
| #if BUILDFLAG(IS_WIN) |
| #include "content/browser/gpu/gpu_data_manager_impl.h" |
| #include "content/browser/media/key_system_support_win.h" |
| #include "gpu/config/gpu_driver_bug_workaround_type.h" |
| #endif |
| |
| namespace content { |
| |
| namespace { |
| |
| bool MatchKeySystem(const CdmInfo& cdm_info, const std::string& key_system) { |
| return cdm_info.key_system == key_system || |
| (cdm_info.supports_sub_key_systems && |
| media::IsSubKeySystemOf(key_system, cdm_info.key_system)); |
| } |
| |
| // Reports whether the software secure CDM is available. |
| void ReportSoftwareSecureCdmAvailableUMA(const std::string& key_system, |
| bool available) { |
| // Use GetKeySystemNameForUMA() without specifying `use_hw_secure_codecs` for |
| // backward compatibility. |
| base::UmaHistogramBoolean("Media.EME." + |
| media::GetKeySystemNameForUMA(key_system) + |
| ".LibraryCdmAvailable", |
| available); |
| } |
| |
| constexpr media::VideoCodec kVideoCodecsToReportToUma[] = { |
| #if BUILDFLAG(USE_PROPRIETARY_CODECS) |
| media::VideoCodec::kH264, |
| #if BUILDFLAG(ENABLE_PLATFORM_HEVC) |
| media::VideoCodec::kHEVC, |
| #if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION) |
| media::VideoCodec::kDolbyVision, |
| #endif // BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION) |
| #endif // BUILDFLAG(ENABLE_PLATFORM_HEVC) |
| #endif // BUILDFLAG(USE_PROPRIETARY_CODECS) |
| media::VideoCodec::kVP9, media::VideoCodec::kAV1}; |
| |
| // Reports the status and capabilities of the hardware secure CDM. Only reported |
| // once per browser session per `key_system`. |
| void ReportHardwareSecureCapabilityStatusUMA( |
| const std::string& key_system, |
| CdmInfo::Status status, |
| const media::CdmCapability* hw_secure_capability) { |
| // Use a set to track whether the UMA has been reported for `key_system` to |
| // make sure we only report once. |
| static base::NoDestructor<std::set<std::string>> reported_key_systems; |
| if (reported_key_systems->count(key_system)) { |
| return; |
| } |
| |
| reported_key_systems->insert(key_system); |
| |
| auto uma_prefix = |
| "Media.EME." + |
| media::GetKeySystemNameForUMA(key_system, /*use_hw_secure_codecs=*/true); |
| |
| // Report whether hardware secure decryption is disabled and if so why. |
| base::UmaHistogramEnumeration(uma_prefix + ".CdmInfoStatus", status); |
| |
| // When hardware secure decryption is enabled, report whether it is supported. |
| if (status == CdmInfo::Status::kEnabled) { |
| base::UmaHistogramBoolean(uma_prefix + ".Support", hw_secure_capability); |
| // When supported, report whether a particular video codec is supported. |
| if (hw_secure_capability) { |
| const auto& video_codecs = hw_secure_capability->video_codecs; |
| for (const auto& video_codec : kVideoCodecsToReportToUma) { |
| bool is_supported = video_codecs.count(video_codec); |
| base::UmaHistogramBoolean( |
| uma_prefix + ".Support." + media::GetCodecNameForUMA(video_codec), |
| is_supported); |
| } |
| } |
| } |
| } |
| |
| // Reports the status of the hardware secure capability query. Only reported |
| // once per browser session per `key_system`. |
| void ReportHardwareSecureCapabilityQueryStatusUMA( |
| const std::string& key_system, |
| media::CdmCapabilityQueryStatus capability_query_status) { |
| // Use a set to track whether the UMA has been reported for `key_system` to |
| // make sure we only report once. |
| static base::NoDestructor<std::set<std::string>> reported_key_systems; |
| if (reported_key_systems->count(key_system)) { |
| return; |
| } |
| |
| reported_key_systems->insert(key_system); |
| |
| auto uma_prefix = |
| "Media.EME." + |
| media::GetKeySystemNameForUMA(key_system, /*use_hw_secure_codecs=*/true); |
| base::UmaHistogramEnumeration(uma_prefix + ".CdmCapabilityQueryStatus", |
| capability_query_status); |
| } |
| |
| bool IsEnabled(CdmInfo::Status status) { |
| return status == CdmInfo::Status::kEnabled || |
| status == CdmInfo::Status::kCommandLineOverridden; |
| } |
| |
| // Returns a CdmCapability with codecs specified on command line. Returns null |
| // if kOverrideHardwareSecureCodecsForTesting was not specified or not valid |
| // codecs specified. |
| std::optional<media::CdmCapability> |
| GetHardwareSecureCapabilityOverriddenFromCommandLine() { |
| auto* command_line = base::CommandLine::ForCurrentProcess(); |
| if (!command_line || !command_line->HasSwitch( |
| switches::kOverrideHardwareSecureCodecsForTesting)) { |
| return std::nullopt; |
| } |
| |
| auto overridden_codecs_string = command_line->GetSwitchValueASCII( |
| switches::kOverrideHardwareSecureCodecsForTesting); |
| const auto overridden_codecs = |
| base::SplitStringPiece(overridden_codecs_string, ",", |
| base::TRIM_WHITESPACE, base::SPLIT_WANT_NONEMPTY); |
| |
| // As the command line switch does not include profiles, specify {} to |
| // indicate that all relevant profiles should be considered supported. |
| std::vector<media::AudioCodec> audio_codecs; |
| media::CdmCapability::VideoCodecMap video_codecs; |
| const media::VideoCodecInfo kAllProfiles; |
| const media::VideoCodecInfo kAllProfilesNoClearLead = {{}, false}; |
| for (const auto& codec : overridden_codecs) { |
| if (codec == "vp8") { |
| video_codecs.emplace(media::VideoCodec::kVP8, kAllProfiles); |
| } else if (codec == "vp9") { |
| video_codecs.emplace(media::VideoCodec::kVP9, kAllProfiles); |
| } else if (codec == "avc1") { |
| video_codecs.emplace(media::VideoCodec::kH264, kAllProfiles); |
| } else if (codec == "hevc") { |
| video_codecs.emplace(media::VideoCodec::kHEVC, kAllProfiles); |
| } else if (codec == "dolbyvision") { |
| video_codecs.emplace(media::VideoCodec::kDolbyVision, kAllProfiles); |
| } else if (codec == "av01") { |
| video_codecs.emplace(media::VideoCodec::kAV1, kAllProfiles); |
| } else if (codec == "vp8-no-clearlead") { |
| video_codecs.emplace(media::VideoCodec::kVP8, kAllProfilesNoClearLead); |
| } else if (codec == "vp9-no-clearlead") { |
| video_codecs.emplace(media::VideoCodec::kVP9, kAllProfilesNoClearLead); |
| } else if (codec == "avc1-no-clearlead") { |
| video_codecs.emplace(media::VideoCodec::kH264, kAllProfilesNoClearLead); |
| } else if (codec == "hevc-no-clearlead") { |
| video_codecs.emplace(media::VideoCodec::kHEVC, kAllProfilesNoClearLead); |
| } else if (codec == "dolbyvision-no-clearlead") { |
| video_codecs.emplace(media::VideoCodec::kDolbyVision, |
| kAllProfilesNoClearLead); |
| } else if (codec == "av01-no-clearlead") { |
| video_codecs.emplace(media::VideoCodec::kAV1, kAllProfilesNoClearLead); |
| } else if (codec == "mp4a") { |
| audio_codecs.push_back(media::AudioCodec::kAAC); |
| } else if (codec == "vorbis") { |
| audio_codecs.push_back(media::AudioCodec::kVorbis); |
| } else { |
| DVLOG(1) << "Unsupported codec specified on command line: " << codec; |
| } |
| } |
| |
| if (video_codecs.empty()) { |
| DVLOG(1) << "No codec codec specified on command line"; |
| return std::nullopt; |
| } |
| |
| // Overridden codecs assume CENC and temporary session support. |
| // The EncryptedMediaSupportedTypesWidevineHwSecureTest tests depend |
| // on 'cbcs' not being supported. |
| return media::CdmCapability(std::move(audio_codecs), std::move(video_codecs), |
| {media::EncryptionScheme::kCenc}, |
| {media::CdmSessionType::kTemporary}, |
| base::Version("0.1.0.0")); |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| bool IsMediaFoundationHardwareSecurityDisabledByGpuFeature() { |
| auto* gpu_data_manager = GpuDataManagerImpl::GetInstance(); |
| DCHECK(gpu_data_manager->IsGpuFeatureInfoAvailable()); |
| return gpu_data_manager->GetGpuFeatureInfo().IsWorkaroundEnabled( |
| gpu::DISABLE_MEDIA_FOUNDATION_HARDWARE_SECURITY); |
| } |
| |
| bool IsGpuHardwareCompositionDisabled() { |
| auto* gpu_data_manager = GpuDataManagerImpl::GetInstance(); |
| return gpu_data_manager->IsGpuCompositingDisabled() || |
| !gpu_data_manager->GetGPUInfo().overlay_info.direct_composition; |
| } |
| |
| bool IsGpuSoftwareEmulated() { |
| auto* gpu_data_manager = GpuDataManagerImpl::GetInstance(); |
| const bool is_gpu_software_emulated = |
| gpu_data_manager->GetGPUInfo().active_gpu().IsSoftwareRenderer(); |
| return is_gpu_software_emulated; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| |
| } // namespace |
| |
| // static |
| CdmRegistry* CdmRegistry::GetInstance() { |
| return CdmRegistryImpl::GetInstance(); |
| } |
| |
| // static |
| CdmRegistryImpl* CdmRegistryImpl::GetInstance() { |
| static CdmRegistryImpl* registry = new CdmRegistryImpl(); |
| return registry; |
| } |
| |
| CdmRegistryImpl::CdmRegistryImpl() { |
| #if BUILDFLAG(IS_WIN) |
| GpuDataManagerImpl::GetInstance()->AddObserver(this); |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| CdmRegistryImpl::~CdmRegistryImpl() { |
| #if BUILDFLAG(IS_WIN) |
| GpuDataManagerImpl::GetInstance()->RemoveObserver(this); |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| void CdmRegistryImpl::Init() { |
| DVLOG(1) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // Let embedders register CDMs. |
| GetContentClient()->AddContentDecryptionModules(&cdms_, nullptr); |
| } |
| |
| void CdmRegistryImpl::RegisterCdm(const CdmInfo& info) { |
| DVLOG(1) << __func__ << ": key_system=" << info.key_system |
| << ", robustness=" << info.robustness |
| << ", status=" << static_cast<int>(info.status); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // Always register new CDMs at the end of the list, so that the behavior is |
| // consistent across the browser process's lifetime. For example, we'll always |
| // use the same registered CDM for a given key system. This also means that |
| // some later registered CDMs (component updated) will not be used until |
| // browser restart, which is fine in most cases. |
| cdms_.push_back(info); |
| |
| // Reset cached `key_system_capabilities_` to avoid notifying new observers |
| // with the old capabilities and then update them again with new ones. |
| // This could cause notifying observers with the same capabilities multiple |
| // times, which is okay. |
| key_system_capabilities_.reset(); |
| |
| // If there are `key_system_capabilities_update_callbacks_` registered, |
| // finalize key system capabilities and notify the callbacks. Otherwise we'll |
| // finalize key system capabilities in `ObserveKeySystemCapabilities()`. |
| if (!key_system_capabilities_update_callbacks_.empty()) { |
| FinalizeKeySystemCapabilities(); |
| } |
| } |
| |
| void CdmRegistryImpl::SetHardwareSecureCdmStatus(CdmInfo::Status status) { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(status != CdmInfo::Status::kUninitialized && |
| status != CdmInfo::Status::kEnabled && |
| status != CdmInfo::Status::kCommandLineOverridden); |
| |
| bool updated = false; |
| for (auto& cdm_info : cdms_) { |
| if (cdm_info.robustness == CdmInfo::Robustness::kHardwareSecure) { |
| cdm_info.status = status; |
| updated = true; |
| } |
| } |
| |
| if (!updated) { |
| DVLOG(1) << "No hardware secure CDMs to update"; |
| return; |
| } |
| |
| key_system_capabilities_.reset(); |
| |
| // If there are `key_system_capabilities_update_callbacks_` registered, |
| // finalize key system capabilities and notify the callbacks. Otherwise we'll |
| // finalize key system capabilities in `ObserveKeySystemCapabilities()`. |
| if (!key_system_capabilities_update_callbacks_.empty()) { |
| FinalizeKeySystemCapabilities(); |
| } |
| } |
| |
| void CdmRegistryImpl::OnGpuInfoUpdate() { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| #if BUILDFLAG(IS_WIN) |
| if (IsGpuHardwareCompositionDisabled()) { |
| SetHardwareSecureCdmStatus(CdmInfo::Status::kGpuCompositionDisabled); |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| const std::vector<CdmInfo>& CdmRegistryImpl::GetRegisteredCdms() const { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| return cdms_; |
| } |
| |
| std::unique_ptr<CdmInfo> CdmRegistryImpl::GetCdmInfo( |
| const std::string& key_system, |
| CdmInfo::Robustness robustness) const { |
| DVLOG(2) << __func__ << ": key_system=" << key_system |
| << ", robustness=" << robustness; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| for (const auto& cdm : cdms_) { |
| if (cdm.robustness == robustness && MatchKeySystem(cdm, key_system)) { |
| return std::make_unique<CdmInfo>(cdm); |
| } |
| } |
| |
| return nullptr; |
| } |
| |
| base::CallbackListSubscription CdmRegistryImpl::ObserveKeySystemCapabilities( |
| bool allow_hw_secure_capability_check, |
| KeySystemCapabilitiesUpdateCB cb) { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| auto subscription = key_system_capabilities_update_callbacks_.Add(cb); |
| |
| // Re-trigger Hardware secure capability check when we encounter the first |
| // observer that allows the check. |
| if (allow_hw_secure_capability_check && !allow_hw_secure_capability_check_) { |
| allow_hw_secure_capability_check_ = true; |
| key_system_capabilities_.reset(); |
| pending_lazy_initializations_.clear(); |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| FinalizeKeySystemCapabilities(); |
| return subscription; |
| } |
| |
| if (!pending_lazy_initializations_.empty()) { |
| // Lazy initializing some key systems. All callbacks will be notified when |
| // that's finished. |
| return subscription; |
| } |
| |
| if (key_system_capabilities_.has_value()) { |
| cb.Run(key_system_capabilities_.value()); |
| return subscription; |
| } |
| |
| FinalizeKeySystemCapabilities(); |
| return subscription; |
| } |
| |
| std::pair<std::optional<media::CdmCapability>, CdmInfo::Status> |
| CdmRegistryImpl::GetCapability(const std::string& key_system, |
| CdmInfo::Robustness robustness) { |
| DVLOG(2) << __func__ << ": key_system=" << key_system |
| << ", robustness=" << robustness; |
| using Status = CdmInfo::Status; |
| |
| if (robustness == CdmInfo::Robustness::kHardwareSecure) { |
| #if !BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) |
| if (!media::IsHardwareSecureDecryptionEnabled()) { |
| DVLOG(1) << "Hardware secure decryption disabled"; |
| return {std::nullopt, Status::kHardwareSecureDecryptionDisabled}; |
| } |
| #endif // !BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) |
| |
| // Secure codecs override takes precedence over other checks. |
| auto overridden_capability = |
| GetHardwareSecureCapabilityOverriddenFromCommandLine(); |
| if (overridden_capability) { |
| DVLOG(1) << "Hardware secure codecs overridden from command line"; |
| return {overridden_capability, Status::kCommandLineOverridden}; |
| } |
| |
| // Hardware secure video codecs need hardware video decoder support. |
| // TODO(xhwang): Make sure this check is as close as possible to the check |
| // in the render process. For example, also check check GPU features like |
| // GPU_FEATURE_TYPE_ACCELERATED_VIDEO_DECODE. |
| auto* command_line = base::CommandLine::ForCurrentProcess(); |
| if (command_line && |
| command_line->HasSwitch(switches::kDisableAcceleratedVideoDecode)) { |
| DVLOG(1) << "Hardware security not supported because accelerated video " |
| "decode disabled"; |
| return {std::nullopt, Status::kAcceleratedVideoDecodeDisabled}; |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| // Check if the GPU is disabled from gpu/config/gpu_driver_bug_list.json. |
| if (IsMediaFoundationHardwareSecurityDisabledByGpuFeature()) { |
| DVLOG(1) << "Hardware security not supported: GPU workarounds"; |
| return {std::nullopt, Status::kGpuFeatureDisabled}; |
| } |
| |
| if (IsGpuHardwareCompositionDisabled()) { |
| DVLOG(1) << "Hardware security not supported: GPU composition disabled"; |
| return {std::nullopt, Status::kGpuCompositionDisabled}; |
| } |
| |
| // Due to the bugs (crbug.com/41496376 and crbug.com/41497095), |
| // `disable_media_foundation_hardware_security` workaround flag cannot be |
| // enabled for the vendor ID 0x0000 and 0x1414. All software emulated GPUs |
| // are considered as disabled for the media foundation hardware security. |
| if (IsGpuSoftwareEmulated()) { |
| DVLOG(1) |
| << "Hardware security not supported: software emulated GPU enabled"; |
| return {std::nullopt, Status::kDisabledBySoftwareEmulatedGpu}; |
| } |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| auto cdm_info = GetCdmInfo(key_system, robustness); |
| if (!cdm_info) { |
| DVLOG(1) << "No " << robustness << " decryption CDM registered for " |
| << key_system; |
| return {std::nullopt, Status::kEnabled}; |
| } |
| |
| DCHECK(!(cdm_info->status == Status::kUninitialized && cdm_info->capability)) |
| << "Capability for " << robustness << " " << key_system |
| << " should not have value if uninitialized."; |
| |
| return {cdm_info->capability, cdm_info->status}; |
| } |
| |
| std::pair<std::optional<media::CdmCapability>, CdmInfo::Status> |
| CdmRegistryImpl::GetFinalCapability(const std::string& key_system, |
| CdmInfo::Robustness robustness) { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| // `status` could be kUninitialized if HW secure capability checking is not |
| // allowed. |
| const auto [capability, status] = GetCapability(key_system, robustness); |
| |
| return {IsEnabled(status) ? capability : std::nullopt, status}; |
| } |
| |
| void CdmRegistryImpl::FinalizeKeySystemCapabilities() { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(!key_system_capabilities_.has_value()); |
| |
| // Abort existing pending LazyInitializeHardwareSecureCapability() operations |
| // to avoid updating the observer twice. |
| pending_lazy_initializations_.clear(); |
| weak_ptr_factory_.InvalidateWeakPtrs(); |
| |
| // Get the set of supported key systems in case two CDMs are registered with |
| // the same key system and robustness; this also avoids updating `cdms_` |
| // while iterating through it. |
| std::set<std::string> supported_key_systems = GetSupportedKeySystems(); |
| |
| // Attempt to finalize capabilities for all key systems. |
| for (const auto& key_system : supported_key_systems) { |
| for (const auto robustness : {CdmInfo::Robustness::kSoftwareSecure, |
| CdmInfo::Robustness::kHardwareSecure}) { |
| AttemptToFinalizeKeySystemCapability(key_system, robustness); |
| } |
| } |
| |
| // If not empty, we'll handle it in OnCapabilityInitialized(). |
| if (pending_lazy_initializations_.empty()) { |
| UpdateAndNotifyKeySystemCapabilities(); |
| } |
| } |
| |
| void CdmRegistryImpl::AttemptToFinalizeKeySystemCapability( |
| const std::string& key_system, |
| CdmInfo::Robustness robustness) { |
| auto cdm_info = GetCdmInfo(key_system, robustness); |
| if (!cdm_info) { |
| DVLOG(1) << "No " << robustness << " CDM registered for " << key_system; |
| return; |
| } |
| |
| if (cdm_info->status != CdmInfo::Status::kUninitialized) { |
| DVLOG(1) << robustness << " capability already finalized for " |
| << key_system; |
| return; |
| } |
| |
| if (robustness == CdmInfo::Robustness::kHardwareSecure && |
| !allow_hw_secure_capability_check_) { |
| DVLOG(1) << robustness |
| << " Not allowed to get hardware secure capability for " |
| << key_system; |
| return; |
| } |
| |
| const auto [capability, status] = GetCapability(key_system, robustness); |
| if (status != CdmInfo::Status::kUninitialized) { |
| media::CdmCapabilityOrStatus cdm_capability_or_status = |
| base::unexpected(media::CdmCapabilityQueryStatus::kUnknown); |
| if (capability.has_value()) { |
| cdm_capability_or_status = std::move(capability.value()); |
| } |
| FinalizeCapability(key_system, robustness, cdm_capability_or_status, |
| status); |
| return; |
| } |
| |
| // Needs lazy initialize. Use base::BindPostTaskToCurrentDefault() to force a |
| // post. |
| pending_lazy_initializations_.insert({key_system, robustness}); |
| LazyInitializeCapability( |
| key_system, robustness, |
| base::BindPostTaskToCurrentDefault(base::BindOnce( |
| &CdmRegistryImpl::OnCapabilityInitialized, |
| weak_ptr_factory_.GetWeakPtr(), key_system, robustness))); |
| } |
| |
| // TODO(xhwang): Find a way to register this as callbacks so we don't have to |
| // hardcode platform-specific logic here. |
| void CdmRegistryImpl::LazyInitializeCapability( |
| const std::string& key_system, |
| CdmInfo::Robustness robustness, |
| media::CdmCapabilityCB cdm_capability_cb) { |
| DVLOG(2) << __func__ << ": key_system=" << key_system |
| << ", robustness=" << robustness; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| if (capability_cb_for_testing_) { |
| capability_cb_for_testing_.Run(key_system, robustness, |
| std::move(cdm_capability_cb)); |
| return; |
| } |
| |
| #if BUILDFLAG(IS_WIN) |
| if (robustness == CdmInfo::Robustness::kHardwareSecure) { |
| auto cdm_info = |
| GetCdmInfo(key_system, CdmInfo::Robustness::kHardwareSecure); |
| DCHECK(cdm_info && !cdm_info->capability); |
| GetMediaFoundationServiceCdmCapability( |
| key_system, cdm_info->type, cdm_info->path, |
| /*is_hw_secure=*/true, std::move(cdm_capability_cb)); |
| } else { |
| // kSoftwareSecure should have been determined from the manifest. |
| std::move(cdm_capability_cb) |
| .Run(base::unexpected(media::CdmCapabilityQueryStatus::kUnknown)); |
| } |
| #elif BUILDFLAG(IS_ANDROID) |
| GetAndroidCdmCapability(key_system, robustness, std::move(cdm_capability_cb)); |
| #else |
| std::move(cdm_capability_cb) |
| .Run(base::unexpected(media::CdmCapabilityQueryStatus::kUnknown)); |
| #endif // BUILDFLAG(IS_WIN) |
| } |
| |
| void CdmRegistryImpl::OnCapabilityInitialized( |
| const std::string& key_system, |
| const CdmInfo::Robustness robustness, |
| media::CdmCapabilityOrStatus cdm_capability_or_status) { |
| DVLOG(1) << __func__ << ": key_system=" << key_system |
| << ", robustness=" << robustness << ", cdm_capability_or_status=" |
| << cdm_capability_or_status.ToString(); |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(pending_lazy_initializations_.count({key_system, robustness})); |
| |
| // Report the status of the hardware secure capability query. |
| if (robustness == CdmInfo::Robustness::kHardwareSecure) { |
| ReportHardwareSecureCapabilityQueryStatusUMA( |
| key_system, cdm_capability_or_status.has_value() |
| ? media::CdmCapabilityQueryStatus::kSuccess |
| : cdm_capability_or_status.error()); |
| } |
| |
| FinalizeCapability(key_system, robustness, |
| std::move(cdm_capability_or_status), |
| CdmInfo::Status::kEnabled); |
| |
| pending_lazy_initializations_.erase({key_system, robustness}); |
| if (pending_lazy_initializations_.empty()) { |
| UpdateAndNotifyKeySystemCapabilities(); |
| } |
| } |
| |
| void CdmRegistryImpl::FinalizeCapability( |
| const std::string& key_system, |
| const CdmInfo::Robustness robustness, |
| media::CdmCapabilityOrStatus cdm_capability_or_status, |
| CdmInfo::Status status) { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(status != CdmInfo::Status::kUninitialized); |
| |
| auto itr = cdms_.begin(); |
| for (; itr != cdms_.end(); itr++) { |
| if (itr->robustness == robustness && MatchKeySystem(*itr, key_system)) { |
| break; |
| } |
| } |
| |
| if (itr == cdms_.end()) { |
| DLOG(ERROR) << __func__ << ": Cannot find CdmInfo to finalize for " |
| << key_system << " with robustness " << robustness; |
| return; |
| } |
| |
| if (itr->status != CdmInfo::Status::kUninitialized) { |
| DLOG(ERROR) << __func__ << ": CdmCapability already finalized for " |
| << key_system << " with robustness " << robustness; |
| return; |
| } |
| |
| itr->status = status; |
| if (cdm_capability_or_status.has_value()) { |
| itr->capability = std::move(cdm_capability_or_status).value(); |
| itr->capability_query_status = std::nullopt; |
| } else { |
| itr->capability = std::nullopt; |
| itr->capability_query_status = std::move(cdm_capability_or_status).error(); |
| } |
| } |
| |
| void CdmRegistryImpl::UpdateAndNotifyKeySystemCapabilities() { |
| DVLOG(2) << __func__; |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| DCHECK(pending_lazy_initializations_.empty()); |
| |
| auto key_system_capabilities = GetKeySystemCapabilities(); |
| |
| if (key_system_capabilities_.has_value() && |
| key_system_capabilities_.value() == key_system_capabilities) { |
| DVLOG(3) << "Same key system capabilities; no need to update"; |
| return; |
| } |
| |
| key_system_capabilities_ = key_system_capabilities; |
| key_system_capabilities_update_callbacks_.Notify(key_system_capabilities); |
| } |
| |
| std::set<std::string> CdmRegistryImpl::GetSupportedKeySystems() const { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| std::set<std::string> supported_key_systems; |
| for (const auto& cdm : cdms_) { |
| supported_key_systems.insert(cdm.key_system); |
| } |
| |
| return supported_key_systems; |
| } |
| |
| KeySystemCapabilities CdmRegistryImpl::GetKeySystemCapabilities() { |
| DCHECK_CALLED_ON_VALID_SEQUENCE(sequence_checker_); |
| |
| KeySystemCapabilities key_system_capabilities; |
| |
| std::set<std::string> supported_key_systems = GetSupportedKeySystems(); |
| for (const auto& key_system : supported_key_systems) { |
| CdmInfo::Status status; |
| |
| // Software secure capability. |
| std::optional<media::CdmCapability> sw_cdm_capability; |
| std::tie(sw_cdm_capability, status) = |
| GetFinalCapability(key_system, CdmInfo::Robustness::kSoftwareSecure); |
| ReportSoftwareSecureCdmAvailableUMA(key_system, |
| sw_cdm_capability != std::nullopt); |
| |
| // Hardware secure capability. |
| std::optional<media::CdmCapability> hw_cdm_capability; |
| std::tie(hw_cdm_capability, status) = |
| GetFinalCapability(key_system, CdmInfo::Robustness::kHardwareSecure); |
| ReportHardwareSecureCapabilityStatusUMA( |
| key_system, status, base::OptionalToPtr(hw_cdm_capability)); |
| |
| if (sw_cdm_capability || hw_cdm_capability) { |
| key_system_capabilities[key_system] = media::KeySystemCapability( |
| sw_cdm_capability.has_value() |
| ? media::CdmCapabilityOrStatus(sw_cdm_capability.value()) |
| : base::unexpected(media::CdmCapabilityQueryStatus::kUnknown), |
| hw_cdm_capability.has_value() |
| ? media::CdmCapabilityOrStatus(hw_cdm_capability.value()) |
| : base::unexpected(media::CdmCapabilityQueryStatus::kUnknown)); |
| } |
| } |
| |
| return key_system_capabilities; |
| } |
| |
| void CdmRegistryImpl::SetCapabilityCBForTesting(CapabilityCB cb) { |
| capability_cb_for_testing_ = std::move(cb); |
| } |
| |
| } // namespace content |