-
-
Notifications
You must be signed in to change notification settings - Fork 153
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
c0a8a68
commit 43bda2a
Showing
1 changed file
with
309 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,309 @@ | ||
// Copyright 2021 The Chromium Authors. All rights reserved. | ||
// Use of this source code is governed by a BSD-style license that can be | ||
// found in the LICENSE file. | ||
|
||
#include "chrome/common/media/cdm_registration.h" | ||
|
||
#include "base/check.h" | ||
#include "base/files/file_path.h" | ||
#include "base/files/file_util.h" | ||
#include "base/logging.h" | ||
#include "base/path_service.h" | ||
#include "build/build_config.h" | ||
#include "build/chromeos_buildflags.h" | ||
#include "content/public/common/cdm_info.h" | ||
#include "media/cdm/cdm_capability.h" | ||
#include "media/cdm/cdm_type.h" | ||
#include "third_party/widevine/cdm/buildflags.h" | ||
|
||
#if BUILDFLAG(ENABLE_LIBRARY_CDMS) | ||
#include "base/command_line.h" | ||
#include "media/base/media_switches.h" | ||
#include "media/cdm/cdm_paths.h" // nogncheck | ||
#endif | ||
|
||
#if BUILDFLAG(ENABLE_WIDEVINE) | ||
#include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck | ||
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) | ||
#include "base/native_library.h" | ||
#include "chrome/common/chrome_paths.h" | ||
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) || BUILDFLAG(IS_WIN) | ||
#if BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | ||
#include "base/no_destructor.h" | ||
#include "components/cdm/common/cdm_manifest.h" | ||
#include "media/cdm/supported_audio_codecs.h" | ||
// Needed for WIDEVINE_CDM_MIN_GLIBC_VERSION. This file is in | ||
// SHARED_INTERMEDIATE_DIR. | ||
#include "widevine_cdm_version.h" // nogncheck | ||
// The following must be after widevine_cdm_version.h. | ||
#if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) | ||
#include <gnu/libc-version.h> | ||
#include "base/version.h" | ||
#endif // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) | ||
#if !BUILDFLAG(IS_CHROMEOS_ASH) | ||
#include "chrome/common/media/component_widevine_cdm_hint_file_linux.h" | ||
#endif // !BUILDFLAG(IS_CHROMEOS_ASH) | ||
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | ||
#endif // BUILDFLAG(ENABLE_WIDEVINE) | ||
|
||
namespace { | ||
|
||
using Robustness = content::CdmInfo::Robustness; | ||
|
||
#if BUILDFLAG(ENABLE_WIDEVINE) | ||
#if (BUILDFLAG(BUNDLE_WIDEVINE_CDM) || \ | ||
BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)) && \ | ||
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) | ||
// Create a CdmInfo for a Widevine CDM, using |version|, |cdm_library_path|, and | ||
// |capability|. | ||
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), | ||
/*supports_sub_key_systems=*/false, kWidevineCdmDisplayName, | ||
kWidevineCdmType, version, cdm_library_path); | ||
} | ||
|
||
// On desktop Linux and ChromeOS, given |cdm_base_path| that points to a folder | ||
// containing the Widevine CDM and associated files, read the manifest included | ||
// in that directory and create a CdmInfo. If that is successful, return the | ||
// CdmInfo. If not, return nullptr. | ||
std::unique_ptr<content::CdmInfo> CreateCdmInfoFromWidevineDirectory( | ||
const base::FilePath& cdm_base_path) { | ||
// Library should be inside a platform specific directory. | ||
auto cdm_library_path = | ||
media::GetPlatformSpecificDirectory(cdm_base_path) | ||
.Append(base::GetNativeLibraryName(kWidevineCdmLibraryName)); | ||
if (!base::PathExists(cdm_library_path)) | ||
return nullptr; | ||
|
||
// Manifest should be at the top level. | ||
auto manifest_path = cdm_base_path.Append(FILE_PATH_LITERAL("manifest.json")); | ||
base::Version version; | ||
media::CdmCapability capability; | ||
if (!ParseCdmManifestFromPath(manifest_path, &version, &capability)) | ||
return nullptr; | ||
|
||
return CreateWidevineCdmInfo(version, cdm_library_path, | ||
std::move(capability)); | ||
} | ||
#endif // (BUILDFLAG(BUNDLE_WIDEVINE_CDM) || | ||
// BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT)) && (BUILDFLAG(IS_LINUX) || | ||
// BUILDFLAG(IS_CHROMEOS)) | ||
|
||
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && \ | ||
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) | ||
// On Linux/ChromeOS we have to preload the CDM since it uses the zygote | ||
// sandbox. On Windows and Mac, the bundled CDM is handled by the component | ||
// updater. | ||
|
||
// This code checks to see if the Widevine CDM was bundled with Chrome. If one | ||
// can be found and looks valid, it returns the CdmInfo for the CDM. Otherwise | ||
// it returns nullptr. | ||
content::CdmInfo* GetBundledWidevine() { | ||
// We only want to do this on the first call, as if Widevine wasn't bundled | ||
// with Chrome (or it was deleted/removed) it won't be loaded into the zygote. | ||
static base::NoDestructor<std::unique_ptr<content::CdmInfo>> s_cdm_info( | ||
[]() -> std::unique_ptr<content::CdmInfo> { | ||
base::FilePath install_dir; | ||
CHECK(base::PathService::Get(chrome::DIR_BUNDLED_WIDEVINE_CDM, | ||
&install_dir)); | ||
return CreateCdmInfoFromWidevineDirectory(install_dir); | ||
}()); | ||
return s_cdm_info->get(); | ||
} | ||
#endif // BUILDFLAG(BUNDLE_WIDEVINE_CDM) && (BUILDFLAG(IS_LINUX) || | ||
// BUILDFLAG(IS_CHROMEOS)) | ||
|
||
#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) && \ | ||
(BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS)) | ||
// This code checks to see if a component updated Widevine CDM can be found. If | ||
// there is one and it looks valid, return the CdmInfo for that CDM. Otherwise | ||
// return nullptr. | ||
content::CdmInfo* GetComponentUpdatedWidevine() { | ||
// We only want to do this on the first call, as the component updater may run | ||
// and download a new version once Chrome has been running for a while. Since | ||
// the first returned version will be the one loaded into the zygote, we want | ||
// to return the same thing on subsequent calls. | ||
static base::NoDestructor<std::unique_ptr<content::CdmInfo>> s_cdm_info( | ||
[]() -> std::unique_ptr<content::CdmInfo> { | ||
auto install_dir = GetLatestComponentUpdatedWidevineCdmDirectory(); | ||
if (install_dir.empty()) | ||
return nullptr; | ||
|
||
return CreateCdmInfoFromWidevineDirectory(install_dir); | ||
}()); | ||
return s_cdm_info->get(); | ||
} | ||
#endif // BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) && (BUILDFLAG(IS_LINUX) || | ||
// BUILDFLAG(IS_CHROMEOS)) | ||
|
||
void AddSoftwareSecureWidevine(std::vector<content::CdmInfo>* cdms) { | ||
#if 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 // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION) | ||
|
||
// The Widevine CDM on Linux needs to be registered (and loaded) before the | ||
// zygote is locked down. The CDM can be found from the version bundled with | ||
// Chrome (if BUNDLE_WIDEVINE_CDM = true) and/or the version downloaded by | ||
// the component updater (if ENABLE_WIDEVINE_CDM_COMPONENT = true). If two | ||
// versions exist, take the one with the higher version number. | ||
// | ||
// Note that the component updater will detect the bundled version, and if | ||
// there is no newer version available, select the bundled version. In this | ||
// case both versions will be the same and point to the same directory, so | ||
// it doesn't matter which one is loaded. | ||
content::CdmInfo* bundled_widevine = nullptr; | ||
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) | ||
bundled_widevine = GetBundledWidevine(); | ||
#endif | ||
|
||
content::CdmInfo* updated_widevine = nullptr; | ||
#if BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) | ||
updated_widevine = GetComponentUpdatedWidevine(); | ||
#endif | ||
|
||
// If only a bundled version is available, or both are available and the | ||
// bundled version is not less than the updated version, register the | ||
// bundled version. If only the updated version is available, or both are | ||
// available and the updated version is greater, then register the updated | ||
// version. If neither are available, then nothing is registered. | ||
if (bundled_widevine && | ||
(!updated_widevine || | ||
bundled_widevine->version >= updated_widevine->version)) { | ||
VLOG(1) << "Registering bundled Widevine " << bundled_widevine->version; | ||
cdms->push_back(*bundled_widevine); | ||
} else if (updated_widevine) { | ||
VLOG(1) << "Registering component updated Widevine " | ||
<< updated_widevine->version; | ||
cdms->push_back(*updated_widevine); | ||
} else { | ||
VLOG(1) << "Widevine enabled but no library found"; | ||
} | ||
#endif // BUILDFLAG(IS_LINUX) || BUILDFLAG(IS_CHROMEOS) | ||
} | ||
|
||
void AddHardwareSecureWidevine(std::vector<content::CdmInfo>* cdms) { | ||
#if BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) | ||
#if BUILDFLAG(IS_CHROMEOS_LACROS) | ||
if (!base::CommandLine::ForCurrentProcess()->HasSwitch( | ||
switches::kLacrosUseChromeosProtectedMedia)) { | ||
return; | ||
} | ||
#endif // BUILDFLAG(IS_CHROMEOS_LACROS) | ||
media::CdmCapability capability; | ||
|
||
// The following audio formats are supported for decrypt-only. | ||
capability.audio_codecs = media::GetCdmSupportedAudioCodecs(); | ||
|
||
// We currently support VP9, H264 and HEVC video formats with | ||
// decrypt-and-decode. Not specifying any profiles to indicate that all | ||
// relevant profiles should be considered supported. | ||
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); | ||
} | ||
#else | ||
capability.video_codecs.emplace(media::VideoCodec::kHEVC, kAllProfiles); | ||
#endif // BUILDFLAG(IS_CHROMEOS_LACROS) | ||
#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 | ||
|
||
// Both encryption schemes are supported on ChromeOS. | ||
capability.encryption_schemes.insert(media::EncryptionScheme::kCenc); | ||
capability.encryption_schemes.insert(media::EncryptionScheme::kCbcs); | ||
|
||
// Both temporary and persistent sessions are supported on ChromeOS. | ||
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 // BUILDFLAG(USE_CHROMEOS_PROTECTED_MEDIA) | ||
} | ||
|
||
void AddWidevine(std::vector<content::CdmInfo>* cdms) { | ||
AddSoftwareSecureWidevine(cdms); | ||
AddHardwareSecureWidevine(cdms); | ||
} | ||
#endif // BUILDFLAG(ENABLE_WIDEVINE) | ||
|
||
#if BUILDFLAG(ENABLE_LIBRARY_CDMS) | ||
void AddExternalClearKey(std::vector<content::CdmInfo>* cdms) { | ||
// Register Clear Key CDM if specified in command line. | ||
base::CommandLine* command_line = base::CommandLine::ForCurrentProcess(); | ||
base::FilePath clear_key_cdm_path = | ||
command_line->GetSwitchValuePath(switches::kClearKeyCdmPathForTesting); | ||
if (clear_key_cdm_path.empty() || !base::PathExists(clear_key_cdm_path)) | ||
return; | ||
|
||
// TODO(crbug.com/764480): Remove these after we have a central place for | ||
// External Clear Key (ECK) related information. | ||
// Normal External Clear Key key system. | ||
const char kExternalClearKeyKeySystem[] = "org.chromium.externalclearkey"; | ||
// A variant of ECK key system that has a different CDM type. | ||
const char kkExternalClearKeyDifferentCdmTypeTestKeySystem[] = | ||
"org.chromium.externalclearkey.differentcdmtype"; | ||
|
||
// Supported codecs are hard-coded in ExternalClearKeyProperties. | ||
media::CdmCapability capability( | ||
{}, {}, {media::EncryptionScheme::kCenc, media::EncryptionScheme::kCbcs}, | ||
{media::CdmSessionType::kTemporary, | ||
media::CdmSessionType::kPersistentLicense}); | ||
|
||
// Register kkExternalClearKeyDifferentCdmTypeTestKeySystem first separately. | ||
// Otherwise, it'll be treated as a sub-key-system of normal | ||
// kExternalClearKeyKeySystem. See MultipleCdmTypes test in | ||
// ECKEncryptedMediaTest. | ||
cdms->push_back(content::CdmInfo( | ||
kkExternalClearKeyDifferentCdmTypeTestKeySystem, | ||
Robustness::kSoftwareSecure, capability, | ||
/*supports_sub_key_systems=*/false, media::kClearKeyCdmDisplayName, | ||
media::kClearKeyCdmDifferentCdmType, base::Version("0.1.0.0"), | ||
clear_key_cdm_path)); | ||
|
||
cdms->push_back(content::CdmInfo( | ||
kExternalClearKeyKeySystem, Robustness::kSoftwareSecure, capability, | ||
/*supports_sub_key_systems=*/true, media::kClearKeyCdmDisplayName, | ||
media::kClearKeyCdmType, base::Version("0.1.0.0"), clear_key_cdm_path)); | ||
} | ||
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS) | ||
|
||
} // namespace | ||
|
||
void RegisterCdmInfo(std::vector<content::CdmInfo>* cdms) { | ||
DVLOG(1) << __func__; | ||
DCHECK(cdms); | ||
DCHECK(cdms->empty()); | ||
|
||
#if BUILDFLAG(ENABLE_WIDEVINE) | ||
AddWidevine(cdms); | ||
#endif | ||
|
||
#if BUILDFLAG(ENABLE_LIBRARY_CDMS) | ||
AddExternalClearKey(cdms); | ||
#endif | ||
} |