#include "chrome/common/media/cdm_registration.h"
#include <memory>
#include <optional>
#include <utility>
#include "base/check.h"
#include "base/command_line.h"
#include "base/feature_list.h"
#include "base/files/file_path.h"
#include "base/files/file_util.h"
#include "base/logging.h"
#include "base/path_service.h"
#include "base/version.h"
#include "build/build_config.h"
#include "build/chromeos_buildflags.h"
#include "content/public/common/cdm_info.h"
#include "media/base/cdm_capability.h"
#include "media/base/media_switches.h"
#include "media/cdm/cdm_type.h"
#include "media/cdm/clear_key_cdm_common.h"
#include "third_party/widevine/cdm/buildflags.h"
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
#include "media/base/video_codecs.h"
#include "media/cdm/supported_audio_codecs.h"
#endif
#if BUILDFLAG(ENABLE_WIDEVINE)
#include "components/cdm/common/cdm_manifest.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h"
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#include "base/native_library.h"
#include "chrome/common/chrome_paths.h"
#include "chrome/common/media/component_widevine_cdm_hint_file_linux.h"
#include "media/cdm/cdm_paths.h"
#include "widevine_cdm_version.h"
#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
#include <gnu/libc-version.h>
#endif
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
#include "base/metrics/histogram_functions.h"
#include "content/public/common/content_switches.h"
#endif
#endif
#if BUILDFLAG(IS_ANDROID)
#include "components/cdm/common/android_cdm_registration.h"
#endif
namespace {
Robustness;
#if BUILDFLAG(ENABLE_WIDEVINE)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
enum class LacrosBundledWidevine {
kNone = 0,
kLacros = 1,
kAsh = 2,
kMaxValue = kAsh,
};
void ReportLacrosUMA(LacrosBundledWidevine value) {
const auto* command_line = base::CommandLine::ForCurrentProcess();
std::string process_type =
command_line->GetSwitchValueASCII(switches::kProcessType);
if (process_type.empty()) {
base::UmaHistogramEnumeration("Media.EME.Widevine.LacrosBundledCdm", value);
}
}
#endif
#if (BUILDFLAG(BUNDLE_WIDEVINE_CDM) || \
BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)) && \
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
std::unique_ptr<content::CdmInfo> CreateWidevineCdmInfo(
const base::Version& version,
const base::FilePath& cdm_library_path,
media::CdmCapability capability) {
return std::make_unique<content::CdmInfo>(
kWidevineKeySystem, Robustness::kSoftwareSecure, std::move(capability),
false, kWidevineCdmDisplayName,
kWidevineCdmType, version, cdm_library_path);
}
std::unique_ptr<content::CdmInfo> CreateCdmInfoFromWidevineDirectory(
const base::FilePath& cdm_base_path) {
auto cdm_library_path =
media::GetPlatformSpecificDirectory(cdm_base_path)
.Append(base::GetNativeLibraryName(kWidevineCdmLibraryName));
if (!base::PathExists(cdm_library_path)) {
DLOG(ERROR) << __func__ << " no directory: " << cdm_library_path;
return nullptr;
}
auto manifest_path = cdm_base_path.Append(FILE_PATH_LITERAL("manifest.json"));
base::Version version;
media::CdmCapability capability;
if (!ParseCdmManifestFromPath(manifest_path, &version, &capability)) {
DLOG(ERROR) << __func__ << " no manifest: " << manifest_path;
return nullptr;
}
return CreateWidevineCdmInfo(version, cdm_library_path,
std::move(capability));
}
#endif
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && \
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS))
std::unique_ptr<content::CdmInfo> GetBundledWidevine() {
base::FilePath install_dir;
if (!base::PathService::Get(chrome::DIR_BUNDLED_WIDEVINE_CDM, &install_dir)) {
return nullptr;
}
return CreateCdmInfoFromWidevineDirectory(install_dir);
}
#endif
#if BUILDFLAG(IS_CHROMEOS_LACROS)
std::unique_ptr<content::CdmInfo> GetAshBundledWidevine() {
if (base::FeatureList::IsEnabled(media::kLacrosUseAshWidevine)) {
const auto* command_line = base::CommandLine::ForCurrentProcess();
if (command_line->HasSwitch(switches::kCrosWidevineBundledDir)) {
ReportLacrosUMA(LacrosBundledWidevine::kAsh);
base::FilePath install_dir =
command_line->GetSwitchValuePath(switches::kCrosWidevineBundledDir);
return CreateCdmInfoFromWidevineDirectory(install_dir);
}
}
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM)
ReportLacrosUMA(LacrosBundledWidevine::kLacros);
return GetBundledWidevine();
#else
ReportLacrosUMA(LacrosBundledWidevine::kNone);
return nullptr;
#endif
}
#endif
#if (BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) && \
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH))) || \
BUILDFLAG(IS_CHROMEOS_LACROS)
std::unique_ptr<content::CdmInfo> GetHintedWidevine() {
auto install_dir = GetHintedWidevineCdmDirectory();
if (install_dir.empty()) {
DVLOG(1) << __func__ << ": no version available";
return nullptr;
}
return CreateCdmInfoFromWidevineDirectory(install_dir);
}
#endif
void AddSoftwareSecureWidevine(std::vector<content::CdmInfo>* cdms) {
DVLOG(1) << __func__;
#if BUILDFLAG(IS_ANDROID)
cdms->emplace_back(
kWidevineKeySystem, Robustness::kSoftwareSecure, std::nullopt,
false, kWidevineCdmDisplayName,
kWidevineCdmType, base::Version(), base::FilePath());
#elif BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)
#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
base::Version glibc_version(gnu_get_libc_version());
DCHECK(glibc_version.IsValid());
if (glibc_version < base::Version(WIDEVINE_CDM_MIN_GLIBC_VERSION)) {
LOG(WARNING) << "Widevine not registered because glibc version is too low";
return;
}
#endif
std::unique_ptr<content::CdmInfo> bundled_widevine = nullptr;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
bundled_widevine = GetAshBundledWidevine();
#elif BUILDFLAG(BUNDLE_WIDEVINE_CDM)
bundled_widevine = GetBundledWidevine();
#endif
std::unique_ptr<content::CdmInfo> hinted_widevine;
#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) || BUILDFLAG(IS_CHROMEOS_LACROS)
hinted_widevine = GetHintedWidevine();
#endif
if (bundled_widevine && !hinted_widevine) {
VLOG(1) << "Registering bundled Widevine " << bundled_widevine->version;
cdms->push_back(*bundled_widevine);
} else if (!bundled_widevine && hinted_widevine) {
VLOG(1) << "Registering hinted Widevine " << hinted_widevine->version;
cdms->push_back(*hinted_widevine);
} else if (!bundled_widevine && !hinted_widevine) {
VLOG(1) << "Widevine enabled but no library found";
} else {
base::Version bundled_version = bundled_widevine->version;
base::Version hinted_version = hinted_widevine->version;
DVLOG(1) << __func__ << " bundled: " << bundled_version;
DVLOG(1) << __func__ << " hinted: " << hinted_version;
bool choose_bundled;
#if BUILDFLAG(IS_CHROMEOS_LACROS)
choose_bundled = bundled_version >= hinted_version;
#else
choose_bundled =
bundled_version > hinted_version &&
bundled_version != GetBundledVersionDuringLastComponentUpdate();
#endif
if (choose_bundled) {
VLOG(1) << "Choosing bundled Widevine " << bundled_version << " from "
<< bundled_widevine->path;
cdms->push_back(*bundled_widevine);
} else {
VLOG(1) << "Choosing hinted Widevine " << hinted_version << " from "
<< hinted_widevine->path;
cdms->push_back(*hinted_widevine);
}
}
#endif
}
void AddHardwareSecureWidevine(std::vector<content::CdmInfo>* cdms) {
DVLOG(1) << __func__;
#if BUILDFLAG(IS_ANDROID)
cdms->emplace_back(
kWidevineKeySystem, Robustness::kHardwareSecure, std::nullopt,
false, kWidevineCdmDisplayName,
kWidevineCdmType, base::Version(), base::FilePath());
#elif BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
if (!base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kLacrosUseChromeosProtectedMedia)) {
return;
}
#endif
media::CdmCapability capability;
capability.audio_codecs = media::GetCdmSupportedAudioCodecs();
const media::VideoCodecInfo kAllProfiles;
capability.video_codecs.emplace(media::VideoCodec::kVP9, kAllProfiles);
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
capability.video_codecs.emplace(media::VideoCodec::kH264, kAllProfiles);
#endif
#if BUILDFLAG(ENABLE_PLATFORM_HEVC)
#if BUILDFLAG(IS_CHROMEOS_LACROS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kLacrosEnablePlatformHevc)) {
capability.video_codecs.emplace(media::VideoCodec::kHEVC, kAllProfiles);
}
#elif BUILDFLAG(IS_CHROMEOS_ASH)
if (base::FeatureList::IsEnabled(media::kPlatformHEVCDecoderSupport)) {
capability.video_codecs.emplace(media::VideoCodec::kHEVC, kAllProfiles);
}
#else
capability.video_codecs.emplace(media::VideoCodec::kHEVC, kAllProfiles);
#endif
#endif
#if BUILDFLAG(USE_CHROMEOS_PROTECTED_AV1)
capability.video_codecs.emplace(media::VideoCodec::kAV1, kAllProfiles);
#elif BUILDFLAG(IS_CHROMEOS_LACROS)
if (base::CommandLine::ForCurrentProcess()->HasSwitch(
switches::kLacrosUseChromeosProtectedAv1)) {
capability.video_codecs.emplace(media::VideoCodec::kAV1, kAllProfiles);
}
#endif
capability.encryption_schemes.insert(media::EncryptionScheme::kCenc);
capability.encryption_schemes.insert(media::EncryptionScheme::kCbcs);
capability.session_types.insert(media::CdmSessionType::kTemporary);
capability.session_types.insert(media::CdmSessionType::kPersistentLicense);
cdms->push_back(
content::CdmInfo(kWidevineKeySystem, Robustness::kHardwareSecure,
std::move(capability), content::kChromeOsCdmType));
#endif
}
void AddWidevine(std::vector<content::CdmInfo>* cdms) {
AddSoftwareSecureWidevine(cdms);
AddHardwareSecureWidevine(cdms);
}
#endif
#if BUILDFLAG(ENABLE_LIBRARY_CDMS)
void AddExternalClearKey(std::vector<content::CdmInfo>* cdms) { … }
#endif
#if BUILDFLAG(IS_WIN)
void AddMediaFoundationClearKey(std::vector<content::CdmInfo>* cdms) {
if (!base::FeatureList::IsEnabled(media::kExternalClearKeyForTesting)) {
return;
}
base::FilePath clear_key_cdm_path = base::FilePath::FromASCII(
media::kMediaFoundationClearKeyCdmPathForTesting.Get());
if (clear_key_cdm_path.empty() || !base::PathExists(clear_key_cdm_path)) {
return;
}
media::CdmCapability capability(
{}, {}, {media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs},
{media::CdmSessionType::kTemporary});
cdms->push_back(
content::CdmInfo(media::kMediaFoundationClearKeyKeySystem,
Robustness::kHardwareSecure, capability,
false,
media::kMediaFoundationClearKeyCdmDisplayName,
media::kMediaFoundationClearKeyCdmType,
base::Version("0.1.0.0"), clear_key_cdm_path));
}
#endif
}
void RegisterCdmInfo(std::vector<content::CdmInfo>* cdms) { … }
#if BUILDFLAG(ENABLE_WIDEVINE) && \
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS_ASH))
std::vector<content::CdmInfo> GetSoftwareSecureWidevine() {
std::vector<content::CdmInfo> cdms;
AddSoftwareSecureWidevine(&cdms);
return cdms;
}
#endif