#include "components/cdm/renderer/key_system_support_update.h"
#include <stddef.h>
#include <memory>
#include <string>
#include <utility>
#include <vector>
#include "base/command_line.h"
#include "base/containers/contains.h"
#include "base/containers/flat_set.h"
#include "base/feature_list.h"
#include "base/logging.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "components/cdm/renderer/external_clear_key_key_system_info.h"
#include "content/public/renderer/key_system_support.h"
#include "content/public/renderer/render_frame.h"
#include "media/base/audio_codecs.h"
#include "media/base/cdm_capability.h"
#include "media/base/content_decryption_module.h"
#include "media/base/eme_constants.h"
#include "media/base/key_system_capability.h"
#include "media/base/key_system_info.h"
#include "media/base/media_switches.h"
#include "media/base/video_codecs.h"
#include "media/cdm/clear_key_cdm_common.h"
#include "media/media_buildflags.h"
#include "third_party/widevine/cdm/buildflags.h"
#if BUILDFLAG(ENABLE_WIDEVINE)
#include "components/cdm/renderer/widevine_key_system_info.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"
#endif
#if BUILDFLAG(IS_ANDROID)
#include "components/cdm/renderer/android_key_system_info.h"
#endif
CdmSessionType;
EmeFeatureSupport;
KeySystemInfo;
KeySystemInfos;
SupportedCodecs;
namespace cdm {
namespace {
#if BUILDFLAG(ENABLE_WIDEVINE) || BUILDFLAG(IS_ANDROID)
SupportedCodecs GetVP9Codecs(
const base::flat_set<media::VideoCodecProfile>& profiles) {
if (profiles.empty()) {
return media::EME_CODEC_VP9_PROFILE0 | media::EME_CODEC_VP9_PROFILE2;
}
SupportedCodecs supported_vp9_codecs = media::EME_CODEC_NONE;
for (const auto& profile : profiles) {
switch (profile) {
case media::VP9PROFILE_PROFILE0:
supported_vp9_codecs |= media::EME_CODEC_VP9_PROFILE0;
break;
case media::VP9PROFILE_PROFILE2:
supported_vp9_codecs |= media::EME_CODEC_VP9_PROFILE2;
break;
default:
DVLOG(1) << "Unexpected " << GetCodecName(media::VideoCodec::kVP9)
<< " profile: " << GetProfileName(profile);
break;
}
}
return supported_vp9_codecs;
}
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
SupportedCodecs GetHevcCodecs(
const base::flat_set<media::VideoCodecProfile>& profiles) {
#if BUILDFLAG(IS_CHROMEOS_LACROS)
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kLacrosEnablePlatformHevc)) {
return media::EME_CODEC_NONE;
}
#endif
#if BUILDFLAG(IS_CHROMEOS_ASH)
if (!base::FeatureList::IsEnabled(media::kPlatformHEVCDecoderSupport)) {
return media::EME_CODEC_NONE;
}
#endif
if (profiles.empty()) {
return media::EME_CODEC_HEVC_PROFILE_MAIN |
media::EME_CODEC_HEVC_PROFILE_MAIN10;
}
SupportedCodecs supported_hevc_codecs = media::EME_CODEC_NONE;
for (const auto& profile : profiles) {
switch (profile) {
case media::HEVCPROFILE_MAIN:
supported_hevc_codecs |= media::EME_CODEC_HEVC_PROFILE_MAIN;
break;
case media::HEVCPROFILE_MAIN10:
supported_hevc_codecs |= media::EME_CODEC_HEVC_PROFILE_MAIN10;
break;
default:
DVLOG(1) << "Unexpected " << GetCodecName(media::VideoCodec::kHEVC)
<< " profile: " << GetProfileName(profile);
break;
}
}
return supported_hevc_codecs;
}
#endif
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
SupportedCodecs GetDolbyVisionCodecs(
const base::flat_set<media::VideoCodecProfile>& profiles) {
if (profiles.empty()) {
return media::EME_CODEC_DOLBY_VISION_AVC |
media::EME_CODEC_DOLBY_VISION_HEVC;
}
SupportedCodecs supported_dv_codecs = media::EME_CODEC_NONE;
for (const auto& profile : profiles) {
switch (profile) {
case media::DOLBYVISION_PROFILE0:
supported_dv_codecs |= media::EME_CODEC_DOLBY_VISION_PROFILE0;
break;
case media::DOLBYVISION_PROFILE5:
supported_dv_codecs |= media::EME_CODEC_DOLBY_VISION_PROFILE5;
break;
case media::DOLBYVISION_PROFILE7:
supported_dv_codecs |= media::EME_CODEC_DOLBY_VISION_PROFILE7;
break;
case media::DOLBYVISION_PROFILE8:
supported_dv_codecs |= media::EME_CODEC_DOLBY_VISION_PROFILE8;
break;
case media::DOLBYVISION_PROFILE9:
supported_dv_codecs |= media::EME_CODEC_DOLBY_VISION_PROFILE9;
break;
default:
DVLOG(1) << "Unexpected "
<< GetCodecName(media::VideoCodec::kDolbyVision)
<< " profile: " << GetProfileName(profile);
break;
}
}
return supported_dv_codecs;
}
#endif
SupportedCodecs GetSupportedCodecs(const media::CdmCapability& capability,
bool requires_clear_lead_support = true) {
SupportedCodecs supported_codecs = media::EME_CODEC_NONE;
for (const auto& codec : capability.audio_codecs) {
switch (codec) {
case media::AudioCodec::kOpus:
supported_codecs |= media::EME_CODEC_OPUS;
break;
case media::AudioCodec::kVorbis:
supported_codecs |= media::EME_CODEC_VORBIS;
break;
case media::AudioCodec::kFLAC:
supported_codecs |= media::EME_CODEC_FLAC;
break;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case media::AudioCodec::kAAC:
supported_codecs |= media::EME_CODEC_AAC;
break;
#if BUILDFLAG(ENABLE_PLATFORM_DTS_AUDIO)
case media::AudioCodec::kDTS:
supported_codecs |= media::EME_CODEC_DTS;
supported_codecs |= media::EME_CODEC_DTSE;
supported_codecs |= media::EME_CODEC_DTSXP2;
break;
#endif
#if BUILDFLAG(ENABLE_PLATFORM_AC3_EAC3_AUDIO)
case media::AudioCodec::kAC3:
supported_codecs |= media::EME_CODEC_AC3;
break;
case media::AudioCodec::kEAC3:
supported_codecs |= media::EME_CODEC_EAC3;
break;
#endif
#if BUILDFLAG(ENABLE_PLATFORM_AC4_AUDIO)
case media::AudioCodec::kAC4:
supported_codecs |= media::EME_CODEC_AC4;
break;
#endif
#endif
default:
DVLOG(1) << "Unexpected supported codec: " << GetCodecName(codec);
break;
}
}
for (const auto& [codec, video_codec_info] : capability.video_codecs) {
if (requires_clear_lead_support && !video_codec_info.supports_clear_lead) {
continue;
}
switch (codec) {
case media::VideoCodec::kVP8:
supported_codecs |= media::EME_CODEC_VP8;
break;
case media::VideoCodec::kVP9:
supported_codecs |= GetVP9Codecs(video_codec_info.supported_profiles);
break;
case media::VideoCodec::kAV1:
supported_codecs |= media::EME_CODEC_AV1;
break;
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
case media::VideoCodec::kH264:
supported_codecs |= media::EME_CODEC_AVC1;
break;
#endif
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
case media::VideoCodec::kHEVC:
supported_codecs |= GetHevcCodecs(video_codec_info.supported_profiles);
break;
#endif
#if BUILDFLAG(ENABLE_PLATFORM_DOLBY_VISION)
case media::VideoCodec::kDolbyVision:
supported_codecs |=
GetDolbyVisionCodecs(video_codec_info.supported_profiles);
break;
#endif
default:
DVLOG(1) << "Unexpected supported codec: " << GetCodecName(codec);
break;
}
}
return supported_codecs;
}
#endif
#if BUILDFLAG(ENABLE_WIDEVINE)
bool CanSupportPersistentLicense() {
#if BUILDFLAG(IS_CHROMEOS)
return true;
#elif BUILDFLAG(IS_ANDROID)
return true;
#elif BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION) && \
BUILDFLAG(ENABLE_CDM_STORAGE_ID)
return true;
#else
DVLOG_IF(2, !BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION))
<< __func__ << ": Not supported without CDM host verification.";
DVLOG_IF(2, !BUILDFLAG(ENABLE_CDM_STORAGE_ID))
<< __func__ << ": Not supported without CDM storage ID.";
return false;
#endif
}
base::flat_set<CdmSessionType> UpdatePersistentLicenseSupport(
bool can_persist_data,
const base::flat_set<CdmSessionType> session_types) {
auto updated_session_types = session_types;
if (!can_persist_data || !CanSupportPersistentLicense()) {
updated_session_types.erase(CdmSessionType::kPersistentLicense);
}
return updated_session_types;
}
void AddWidevine(const media::KeySystemCapability& capability,
bool can_persist_data,
KeySystemInfos* key_systems) {
#if BUILDFLAG(IS_ANDROID)
if (!can_persist_data) {
DVLOG(2) << __func__ << ": Persistent data not supported.";
return;
}
#endif
SupportedCodecs codecs = media::EME_CODEC_NONE;
SupportedCodecs hw_secure_codecs = media::EME_CODEC_NONE;
#if BUILDFLAG(IS_WIN)
SupportedCodecs hw_secure_codecs_clear_lead_support_not_required =
media::EME_CODEC_NONE;
#endif
base::flat_set<::media::EncryptionScheme> encryption_schemes;
base::flat_set<::media::EncryptionScheme> hw_secure_encryption_schemes;
base::flat_set<CdmSessionType> session_types;
base::flat_set<CdmSessionType> hw_secure_session_types;
if (capability.sw_secure_capability) {
codecs = GetSupportedCodecs(capability.sw_secure_capability.value());
encryption_schemes = capability.sw_secure_capability->encryption_schemes;
session_types = UpdatePersistentLicenseSupport(
can_persist_data, capability.sw_secure_capability->session_types);
if (!base::Contains(session_types, CdmSessionType::kTemporary)) {
DVLOG(1) << "Temporary sessions must be supported.";
return;
}
DVLOG(2) << "Software secure Widevine supported";
} else {
DVLOG(2) << "Software secure Widevine NOT supported";
}
if (capability.hw_secure_capability) {
const bool force_support_clear_lead =
media::kHardwareSecureDecryptionForceSupportClearLead.Get();
hw_secure_codecs = GetSupportedCodecs(
capability.hw_secure_capability.value(), !force_support_clear_lead);
#if BUILDFLAG(IS_WIN)
hw_secure_codecs_clear_lead_support_not_required =
GetSupportedCodecs(capability.hw_secure_capability.value(),
false);
#endif
hw_secure_encryption_schemes =
capability.hw_secure_capability->encryption_schemes;
hw_secure_session_types = UpdatePersistentLicenseSupport(
can_persist_data, capability.hw_secure_capability->session_types);
if (!base::Contains(hw_secure_session_types, CdmSessionType::kTemporary)) {
DVLOG(1) << "Temporary sessions must be supported.";
return;
}
DVLOG(2) << "Hardware secure Widevine supported";
} else {
DVLOG(2) << "Hardware secure Widevine NOT supported";
}
#if BUILDFLAG(IS_ANDROID)
if (codecs == media::EME_CODEC_NONE) {
DCHECK(hw_secure_codecs == media::EME_CODEC_NONE);
DVLOG(3) << __func__ << " Widevine NOT supported.";
return;
}
#endif
using Robustness = WidevineKeySystemInfo::Robustness;
auto max_audio_robustness = Robustness::SW_SECURE_CRYPTO;
auto max_video_robustness = Robustness::SW_SECURE_DECODE;
#if BUILDFLAG(IS_WIN)
auto max_experimental_audio_robustness = Robustness::SW_SECURE_CRYPTO;
auto max_experimental_video_robustness = Robustness::SW_SECURE_DECODE;
#endif
#if BUILDFLAG(IS_CHROMEOS)
max_audio_robustness = Robustness::HW_SECURE_ALL;
max_video_robustness = Robustness::HW_SECURE_ALL;
#elif BUILDFLAG(IS_ANDROID)
max_audio_robustness = Robustness::HW_SECURE_CRYPTO;
max_video_robustness = Robustness::HW_SECURE_ALL;
#else
if (base::FeatureList::IsEnabled(media::kHardwareSecureDecryption)) {
max_audio_robustness = Robustness::HW_SECURE_CRYPTO;
max_video_robustness = Robustness::HW_SECURE_ALL;
}
#if BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(
media::kHardwareSecureDecryptionExperiment)) {
max_experimental_audio_robustness = Robustness::HW_SECURE_CRYPTO;
max_experimental_video_robustness = Robustness::HW_SECURE_ALL;
}
#endif
#endif
auto persistent_state_support = EmeFeatureSupport::REQUESTABLE;
auto distinctive_identifier_support = EmeFeatureSupport::NOT_SUPPORTED;
#if BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN)
distinctive_identifier_support = EmeFeatureSupport::REQUESTABLE;
#elif BUILDFLAG(IS_ANDROID)
persistent_state_support = EmeFeatureSupport::ALWAYS_ENABLED;
distinctive_identifier_support = EmeFeatureSupport::ALWAYS_ENABLED;
#endif
key_systems->emplace_back(std::make_unique<WidevineKeySystemInfo>(
codecs, encryption_schemes, session_types, hw_secure_codecs,
hw_secure_encryption_schemes, hw_secure_session_types,
max_audio_robustness, max_video_robustness, persistent_state_support,
distinctive_identifier_support));
#if BUILDFLAG(IS_WIN)
if (base::FeatureList::IsEnabled(
media::kHardwareSecureDecryptionExperiment)) {
auto experimental_key_system_info = std::make_unique<WidevineKeySystemInfo>(
codecs, encryption_schemes, session_types,
hw_secure_codecs_clear_lead_support_not_required,
hw_secure_encryption_schemes, hw_secure_session_types,
max_experimental_audio_robustness, max_experimental_video_robustness,
persistent_state_support, distinctive_identifier_support);
experimental_key_system_info->set_experimental();
key_systems->emplace_back(std::move(experimental_key_system_info));
auto experimental_two_key_system_info =
std::make_unique<WidevineKeySystemInfo>(
codecs, encryption_schemes, session_types, hw_secure_codecs,
hw_secure_encryption_schemes, hw_secure_session_types,
max_experimental_audio_robustness,
max_experimental_video_robustness, persistent_state_support,
distinctive_identifier_support);
experimental_two_key_system_info->set_experimental_two();
key_systems->emplace_back(std::move(experimental_two_key_system_info));
}
#endif
}
#endif
void AddExternalClearKey(const media::KeySystemCapability& ,
KeySystemInfos* key_systems) { … }
#if BUILDFLAG(IS_WIN)
void AddMediaFoundationClearKey(
const media::KeySystemCapability& ,
KeySystemInfos* key_systems) {
DVLOG(1) << __func__;
if (!base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting)) {
DLOG(ERROR) << "ExternalClearKey supported despite not enabled.";
return;
}
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
key_systems->push_back(std::make_unique<ExternalClearKeyKeySystemInfo>(
media::kMediaFoundationClearKeyKeySystem, std::vector<std::string>(),
media::EME_CODEC_AVC1 | media::EME_CODEC_AAC,
media::EmeConfig{
.identifier = media::EmeConfigRuleState::kRequired,
.persistence = media::EmeConfigRuleState::kRequired,
.hw_secure_codecs = media::EmeConfigRuleState::kRequired},
EmeFeatureSupport::ALWAYS_ENABLED, EmeFeatureSupport::ALWAYS_ENABLED));
#endif
}
#endif
#if BUILDFLAG(IS_ANDROID)
void AddAndroidPlatformKeySystem(const std::string& key_system,
const media::KeySystemCapability& capability,
bool can_persist_data,
KeySystemInfos* key_systems) {
DCHECK_NE(key_system, kWidevineKeySystem);
if (!can_persist_data) {
DVLOG(2) << __func__ << ": Key system " << key_system
<< " not supported in incognito process.";
return;
}
SupportedCodecs sw_secure_codecs = media::EME_CODEC_NONE;
SupportedCodecs hw_secure_codecs = media::EME_CODEC_NONE;
base::flat_set<::media::EncryptionScheme> sw_secure_encryption_schemes;
base::flat_set<::media::EncryptionScheme> hw_secure_encryption_schemes;
if (capability.sw_secure_capability) {
sw_secure_codecs =
GetSupportedCodecs(capability.sw_secure_capability.value());
sw_secure_encryption_schemes =
capability.sw_secure_capability->encryption_schemes;
DVLOG(2) << "Software secure " << key_system << " supported";
}
if (capability.hw_secure_capability) {
hw_secure_codecs =
GetSupportedCodecs(capability.hw_secure_capability.value());
hw_secure_encryption_schemes =
capability.hw_secure_capability->encryption_schemes;
DVLOG(2) << "Hardware secure " << key_system << " supported";
}
key_systems->push_back(std::make_unique<AndroidKeySystemInfo>(
key_system, sw_secure_codecs, sw_secure_encryption_schemes,
hw_secure_codecs, hw_secure_encryption_schemes));
}
#endif
void OnKeySystemSupportUpdated(
bool can_persist_data,
media::GetSupportedKeySystemsCB cb,
content::KeySystemCapabilities key_system_capabilities) { … }
}
std::unique_ptr<media::KeySystemSupportRegistration>
GetSupportedKeySystemsUpdates(content::RenderFrame* render_frame,
bool can_persist_data,
media::GetSupportedKeySystemsCB cb) { … }
}