blob: ccd989d654dd3eddf49440d93f9a74bf594f6dc4 [file] [log] [blame]
// 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