Commit b78a48ba authored by Xiaohan Wang's avatar Xiaohan Wang Committed by Commit Bot

media: Add CdmCapability

This describes the capability (supported features) of a CDM. Add a new
struct for it to simplify a lot of code.

Bug: 848532
Change-Id: Iaf83bad1ebe4882e8fbc0a1786b2d9898ef9a52b
Reviewed-on: https://chromium-review.googlesource.com/1110602Reviewed-by: default avatarAlex Moshchuk <alexmos@chromium.org>
Reviewed-by: default avatarFrank Liberato <liberato@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Commit-Queue: Xiaohan Wang <xhwang@chromium.org>
Cr-Commit-Position: refs/heads/master@{#569711}
parent 7902aaa2
......@@ -172,11 +172,13 @@ bool IsCompatibleWithChrome(const base::DictionaryValue& manifest) {
media::IsSupportedCdmHostVersion);
}
// Returns true and updates |supported_video_codecs| (if provided) if the
// appropriate manifest entry is valid. Returns false and does not modify
// |supported_video_codecs| if the manifest entry is incorrectly formatted.
// Returns true and updates |video_codecs| if the appropriate manifest entry is
// valid. Returns false and does not modify |video_codecs| if the manifest entry
// is incorrectly formatted.
bool GetCodecs(const base::DictionaryValue& manifest,
std::vector<media::VideoCodec>* supported_video_codecs) {
std::vector<media::VideoCodec>* video_codecs) {
DCHECK(video_codecs);
const base::Value* value = manifest.FindKey(kCdmCodecsListName);
if (!value) {
DLOG(WARNING) << "Widevine CDM component manifest is missing codecs.";
......@@ -211,47 +213,49 @@ bool GetCodecs(const base::DictionaryValue& manifest,
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
}
if (supported_video_codecs)
supported_video_codecs->swap(result);
video_codecs->swap(result);
return true;
}
// Returns true and updates |supports_persistent_license| (if provided) if
// the appropriate manifest entry is valid. Returns false if the manifest
// entry is incorrectly formatted.
bool GetPersistentLicenseSupport(const base::DictionaryValue& manifest,
bool* supports_persistent_license) {
bool result = false;
// Returns true and updates |session_types| if the appropriate manifest entry is
// valid. Returns false if the manifest entry is incorrectly formatted.
bool GetSessionTypes(const base::DictionaryValue& manifest,
base::flat_set<media::CdmSessionType>* session_types) {
DCHECK(session_types);
bool is_persistent_license_supported = false;
const base::Value* value = manifest.FindKey(kCdmPersistentLicenseSupportName);
if (value) {
if (value->is_bool())
result = value->GetBool();
else
if (!value->is_bool())
return false;
is_persistent_license_supported = value->GetBool();
}
if (supports_persistent_license)
*supports_persistent_license = result;
// Temporary session is always supported.
session_types->insert(media::CdmSessionType::TEMPORARY_SESSION);
if (is_persistent_license_supported)
session_types->insert(media::CdmSessionType::PERSISTENT_LICENSE_SESSION);
return true;
}
// Returns true and updates |supported_encryption_schemes| (if provided) if
// the appropriate manifest entry is valid. Returns false and does not modify
// |supported_encryption_schemes| if the manifest entry is incorrectly
// formatted. It is assumed that all CDMs support 'cenc', so if the manifest
// entry is missing, the result will indicate support for 'cenc' only.
// Incorrect types in the manifest entry will log the error and fail.
// Unrecognized values will be reported but otherwise ignored.
// Returns true and updates |encryption_schemes| if the appropriate manifest
// entry is valid. Returns false and does not modify |encryption_schemes| if the
// manifest entry is incorrectly formatted. It is assumed that all CDMs support
// 'cenc', so if the manifest entry is missing, the result will indicate support
// for 'cenc' only. Incorrect types in the manifest entry will log the error and
// fail. Unrecognized values will be reported but otherwise ignored.
bool GetEncryptionSchemes(
const base::DictionaryValue& manifest,
base::flat_set<media::EncryptionMode>* supported_encryption_schemes) {
base::flat_set<media::EncryptionMode>* encryption_schemes) {
DCHECK(encryption_schemes);
const base::Value* value =
manifest.FindKey(kCdmSupportedEncryptionSchemesName);
if (!value) {
// No manifest entry found, so assume only 'cenc' supported for backwards
// compatibility.
if (supported_encryption_schemes)
supported_encryption_schemes->insert(media::EncryptionMode::kCenc);
encryption_schemes->insert(media::EncryptionMode::kCenc);
return true;
}
......@@ -286,24 +290,19 @@ bool GetEncryptionSchemes(
if (result.empty())
return false;
if (supported_encryption_schemes)
supported_encryption_schemes->swap(result);
encryption_schemes->swap(result);
return true;
}
// Returns true if the entries in the manifest can be parsed correctly,
// false otherwise. Updates |supported_video_codecs|,
// |supports_persistent_license|, and |supported_encryption_schemes|,
// with the values obtained from the manifest, if they are provided.
// If this method returns false, the values may or may not be updated.
bool ParseManifest(
const base::DictionaryValue& manifest,
std::vector<media::VideoCodec>* supported_video_codecs,
bool* supports_persistent_license,
base::flat_set<media::EncryptionMode>* supported_encryption_schemes) {
return GetEncryptionSchemes(manifest, supported_encryption_schemes) &&
GetPersistentLicenseSupport(manifest, supports_persistent_license) &&
GetCodecs(manifest, supported_video_codecs);
// false otherwise. Updates |capability|, with the values obtained from the
// manifest, if they are provided. If this method returns false, |capability|
// may or may not be updated.
bool ParseManifest(const base::DictionaryValue& manifest,
content::CdmCapability* capability) {
return GetCodecs(manifest, &capability->video_codecs) &&
GetEncryptionSchemes(manifest, &capability->encryption_schemes) &&
GetSessionTypes(manifest, &capability->session_types);
}
void RegisterWidevineCdmWithChrome(
......@@ -311,38 +310,25 @@ void RegisterWidevineCdmWithChrome(
const base::FilePath& cdm_install_dir,
std::unique_ptr<base::DictionaryValue> manifest) {
DCHECK_CURRENTLY_ON(BrowserThread::UI);
std::vector<media::VideoCodec> supported_video_codecs;
bool supports_persistent_license;
base::flat_set<media::EncryptionMode> supported_encryption_schemes;
// This check must be a subset of the check in VerifyInstallation() to
// avoid the case where the CDM is accepted by the component updater
// but not registered.
if (!ParseManifest(*manifest, &supported_video_codecs,
&supports_persistent_license,
&supported_encryption_schemes)) {
content::CdmCapability capability;
if (!ParseManifest(*manifest, &capability)) {
VLOG(1) << "Not registering Widevine CDM due to malformed manifest.";
return;
}
VLOG(1) << "Register Widevine CDM with Chrome";
// Temporary session is always supported.
base::flat_set<media::CdmSessionType> supported_session_types = {
media::CdmSessionType::TEMPORARY_SESSION};
if (supports_persistent_license) {
supported_session_types.insert(
media::CdmSessionType::PERSISTENT_LICENSE_SESSION);
}
const base::FilePath cdm_path =
GetPlatformDirectory(cdm_install_dir)
.AppendASCII(base::GetNativeLibraryName(kWidevineCdmLibraryName));
CdmRegistry::GetInstance()->RegisterCdm(content::CdmInfo(
kWidevineCdmDisplayName, kWidevineCdmGuid, cdm_version, cdm_path,
kWidevineCdmFileSystemId, supported_video_codecs, supported_session_types,
supported_encryption_schemes, kWidevineKeySystem, false));
CdmRegistry::GetInstance()->RegisterCdm(
content::CdmInfo(kWidevineCdmDisplayName, kWidevineCdmGuid, cdm_version,
cdm_path, kWidevineCdmFileSystemId,
std::move(capability), kWidevineKeySystem, false));
}
} // namespace
......@@ -419,11 +405,12 @@ void WidevineCdmComponentInstallerPolicy::ComponentReady(
bool WidevineCdmComponentInstallerPolicy::VerifyInstallation(
const base::DictionaryValue& manifest,
const base::FilePath& install_dir) const {
content::CdmCapability capability;
return IsCompatibleWithChrome(manifest) &&
base::PathExists(GetPlatformDirectory(install_dir)
.AppendASCII(base::GetNativeLibraryName(
kWidevineCdmLibraryName))) &&
ParseManifest(manifest, nullptr, nullptr, nullptr);
ParseManifest(manifest, &capability);
}
// The base directory on Windows looks like:
......
......@@ -130,11 +130,8 @@ content::PepperPluginInfo::PPP_ShutdownModuleFunc g_nacl_shutdown_module;
#endif
#if defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT)
bool IsWidevineAvailable(
base::FilePath* cdm_path,
std::vector<media::VideoCodec>* codecs_supported,
bool* supports_persistent_license,
base::flat_set<media::EncryptionMode>* modes_supported) {
bool IsWidevineAvailable(base::FilePath* cdm_path,
content::CdmCapability* capability) {
static enum {
NOT_CHECKED,
FOUND,
......@@ -148,25 +145,27 @@ bool IsWidevineAvailable(
if (widevine_cdm_file_check == FOUND) {
// Add the supported codecs as if they came from the component manifest.
// This list must match the CDM that is being bundled with Chrome.
codecs_supported->push_back(media::VideoCodec::kCodecVP8);
codecs_supported->push_back(media::VideoCodec::kCodecVP9);
capability->video_codecs.push_back(media::VideoCodec::kCodecVP8);
capability->video_codecs.push_back(media::VideoCodec::kCodecVP9);
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
codecs_supported->push_back(media::VideoCodec::kCodecH264);
capability->video_codecs.push_back(media::VideoCodec::kCodecH264);
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
// TODO(crbug.com/767941): Push persistent-license support info here once
// we check in a new CDM that supports it on Linux.
#if defined(OS_CHROMEOS)
*supports_persistent_license = true;
#else
*supports_persistent_license = false;
#endif // defined(OS_CHROMEOS)
// Add the supported encryption schemes as if they came from the
// component manifest. This list must match the CDM that is being
// bundled with Chrome.
modes_supported->insert(media::EncryptionMode::kCenc);
modes_supported->insert(media::EncryptionMode::kCbcs);
capability->encryption_schemes.insert(media::EncryptionMode::kCenc);
capability->encryption_schemes.insert(media::EncryptionMode::kCbcs);
// Temporary session is always supported.
capability->session_types.insert(
media::CdmSessionType::TEMPORARY_SESSION);
#if defined(OS_CHROMEOS)
// TODO(crbug.com/767941): Push persistent-license support info here once
// we check in a new CDM that supports it on Linux.
capability->session_types.insert(
media::CdmSessionType::PERSISTENT_LICENSE_SESSION);
#endif // defined(OS_CHROMEOS)
return true;
}
......@@ -532,28 +531,15 @@ void ChromeContentClient::AddContentDecryptionModules(
if (cdms) {
#if defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT)
base::FilePath cdm_path;
std::vector<media::VideoCodec> video_codecs_supported;
bool supports_persistent_license = false;
base::flat_set<media::EncryptionMode> encryption_modes_supported;
if (IsWidevineAvailable(&cdm_path, &video_codecs_supported,
&supports_persistent_license,
&encryption_modes_supported)) {
content::CdmCapability capability;
if (IsWidevineAvailable(&cdm_path, &capability)) {
const base::Version version(WIDEVINE_CDM_VERSION_STRING);
DCHECK(version.IsValid());
// Temporary session is always supported.
base::flat_set<media::CdmSessionType> supported_session_types = {
media::CdmSessionType::TEMPORARY_SESSION};
if (supports_persistent_license) {
supported_session_types.insert(
media::CdmSessionType::PERSISTENT_LICENSE_SESSION);
}
cdms->push_back(content::CdmInfo(
kWidevineCdmDisplayName, kWidevineCdmGuid, version, cdm_path,
kWidevineCdmFileSystemId, video_codecs_supported,
supported_session_types, encryption_modes_supported,
kWidevineKeySystem, false));
cdms->push_back(
content::CdmInfo(kWidevineCdmDisplayName, kWidevineCdmGuid, version,
cdm_path, kWidevineCdmFileSystemId,
std::move(capability), kWidevineKeySystem, false));
}
#endif // defined(WIDEVINE_CDM_AVAILABLE_NOT_COMPONENT)
......@@ -571,6 +557,12 @@ void ChromeContentClient::AddContentDecryptionModules(
const char kExternalClearKeyDifferentGuidTestKeySystem[] =
"org.chromium.externalclearkey.differentguid";
// Supported codecs are hard-coded in ExternalClearKeyProperties.
content::CdmCapability capability(
{}, {media::EncryptionMode::kCenc, media::EncryptionMode::kCbcs},
{media::CdmSessionType::TEMPORARY_SESSION,
media::CdmSessionType::PERSISTENT_LICENSE_SESSION});
// Register kExternalClearKeyDifferentGuidTestKeySystem first separately.
// Otherwise, it'll be treated as a sub-key-system of normal
// kExternalClearKeyKeySystem. See MultipleCdmTypes test in
......@@ -578,21 +570,14 @@ void ChromeContentClient::AddContentDecryptionModules(
cdms->push_back(content::CdmInfo(
media::kClearKeyCdmDisplayName, media::kClearKeyCdmDifferentGuid,
base::Version("0.1.0.0"), clear_key_cdm_path,
media::kClearKeyCdmFileSystemId, {},
{media::CdmSessionType::TEMPORARY_SESSION,
media::CdmSessionType::PERSISTENT_LICENSE_SESSION},
{media::EncryptionMode::kCenc, media::EncryptionMode::kCbcs},
media::kClearKeyCdmFileSystemId, capability,
kExternalClearKeyDifferentGuidTestKeySystem, false));
// Supported codecs are hard-coded in ExternalClearKeyProperties.
cdms->push_back(content::CdmInfo(
media::kClearKeyCdmDisplayName, media::kClearKeyCdmGuid,
base::Version("0.1.0.0"), clear_key_cdm_path,
media::kClearKeyCdmFileSystemId, {},
{media::CdmSessionType::TEMPORARY_SESSION,
media::CdmSessionType::PERSISTENT_LICENSE_SESSION},
{media::EncryptionMode::kCenc, media::EncryptionMode::kCbcs},
kExternalClearKeyKeySystem, true));
cdms->push_back(
content::CdmInfo(media::kClearKeyCdmDisplayName,
media::kClearKeyCdmGuid, base::Version("0.1.0.0"),
clear_key_cdm_path, media::kClearKeyCdmFileSystemId,
capability, kExternalClearKeyKeySystem, true));
}
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
}
......
......@@ -48,13 +48,13 @@ bool StlEquals(const Container a, std::initializer_list<T> b) {
} while (false)
#define EXPECT_VIDEO_CODECS(...) \
EXPECT_STL_EQ(cdm.supported_video_codecs, __VA_ARGS__)
EXPECT_STL_EQ(cdm.capability.video_codecs, __VA_ARGS__)
#define EXPECT_ENCRYPTION_SCHEMES(...) \
EXPECT_STL_EQ(cdm.supported_encryption_schemes, __VA_ARGS__)
EXPECT_STL_EQ(cdm.capability.encryption_schemes, __VA_ARGS__)
#define EXPECT_SESSION_TYPES(...) \
EXPECT_STL_EQ(cdm.supported_session_types, __VA_ARGS__)
EXPECT_STL_EQ(cdm.capability.session_types, __VA_ARGS__)
} // namespace
......@@ -66,21 +66,19 @@ class CdmRegistryImplTest : public testing::Test {
~CdmRegistryImplTest() override {}
protected:
void Register(
const std::string& name,
const std::string& version,
const std::string& path,
const std::vector<VideoCodec>& supported_video_codecs,
const base::flat_set<CdmSessionType>& supported_session_types,
const base::flat_set<EncryptionMode>& supported_encryption_schemes,
std::string supported_key_system,
bool supports_sub_key_systems = false) {
void Register(const std::string& name,
const std::string& version,
const std::string& path,
const std::vector<VideoCodec>& video_codecs,
const base::flat_set<CdmSessionType>& session_types,
const base::flat_set<EncryptionMode>& encryption_schemes,
std::string supported_key_system,
bool supports_sub_key_systems = false) {
cdm_registry_.RegisterCdm(
CdmInfo(name, kTestCdmGuid, base::Version(version),
base::FilePath::FromUTF8Unsafe(path), kTestFileSystemId,
supported_video_codecs, supported_session_types,
supported_encryption_schemes, supported_key_system,
supports_sub_key_systems));
CdmCapability(video_codecs, encryption_schemes, session_types),
supported_key_system, supports_sub_key_systems));
}
bool IsRegistered(const std::string& name, const std::string& version) {
......
......@@ -107,9 +107,9 @@ void KeySystemSupportImpl::IsKeySystemSupported(
// Supported codecs and encryption schemes.
auto capability = media::mojom::KeySystemCapability::New();
capability->video_codecs = cdm_info->supported_video_codecs;
capability->video_codecs = cdm_info->capability.video_codecs;
capability->encryption_schemes =
SetToVector(cdm_info->supported_encryption_schemes);
SetToVector(cdm_info->capability.encryption_schemes);
if (base::FeatureList::IsEnabled(media::kHardwareSecureDecryption)) {
capability->hw_secure_video_codecs =
......@@ -120,7 +120,7 @@ void KeySystemSupportImpl::IsKeySystemSupported(
NOTIMPLEMENTED();
}
capability->session_types = SetToVector(cdm_info->supported_session_types);
capability->session_types = SetToVector(cdm_info->capability.session_types);
std::move(callback).Run(true, std::move(capability));
}
......
......@@ -63,18 +63,17 @@ class KeySystemSupportTest : public testing::Test {
// Registers |key_system| with supported capabilities. All other values for
// CdmInfo have some default value as they're not returned by
// IsKeySystemSupported().
void Register(
const std::string& key_system,
const std::vector<VideoCodec>& supported_video_codecs,
const base::flat_set<CdmSessionType>& supported_session_types,
const base::flat_set<EncryptionMode>& supported_encryption_schemes) {
void Register(const std::string& key_system,
const std::vector<VideoCodec>& video_codecs,
const base::flat_set<CdmSessionType>& session_types,
const base::flat_set<EncryptionMode>& encryption_schemes) {
DVLOG(1) << __func__;
CdmRegistry::GetInstance()->RegisterCdm(
CdmInfo(key_system, kTestCdmGuid, base::Version(kVersion),
base::FilePath::FromUTF8Unsafe(kTestPath), kTestFileSystemId,
supported_video_codecs, supported_session_types,
supported_encryption_schemes, key_system, false));
CdmCapability(video_codecs, encryption_schemes, session_types),
key_system, false));
}
// Determines if |key_system| is registered. If it is, updates |codecs_|
......
......@@ -9,29 +9,38 @@
namespace content {
CdmInfo::CdmInfo(
const std::string& name,
const std::string& guid,
const base::Version& version,
const base::FilePath& path,
const std::string& file_system_id,
const std::vector<media::VideoCodec>& supported_video_codecs,
const base::flat_set<media::CdmSessionType>& supported_session_types,
const base::flat_set<media::EncryptionMode>& supported_encryption_schemes,
const std::string& supported_key_system,
bool supports_sub_key_systems)
CdmCapability::CdmCapability() = default;
CdmCapability::CdmCapability(
std::vector<media::VideoCodec> video_codecs,
base::flat_set<media::EncryptionMode> encryption_schemes,
base::flat_set<media::CdmSessionType> session_types)
: video_codecs(std::move(video_codecs)),
encryption_schemes(std::move(encryption_schemes)),
session_types(std::move(session_types)) {}
CdmCapability::CdmCapability(const CdmCapability& other) = default;
CdmCapability::~CdmCapability() = default;
CdmInfo::CdmInfo(const std::string& name,
const std::string& guid,
const base::Version& version,
const base::FilePath& path,
const std::string& file_system_id,
CdmCapability capability,
const std::string& supported_key_system,
bool supports_sub_key_systems)
: name(name),
guid(guid),
version(version),
path(path),
file_system_id(file_system_id),
supported_video_codecs(supported_video_codecs),
supported_session_types(supported_session_types),
supported_encryption_schemes(supported_encryption_schemes),
capability(std::move(capability)),
supported_key_system(supported_key_system),
supports_sub_key_systems(supports_sub_key_systems) {
DCHECK(base::IsValidGUID(guid));
DCHECK(!supported_encryption_schemes.empty());
DCHECK(!capability.encryption_schemes.empty());
}
CdmInfo::CdmInfo(const CdmInfo& other) = default;
......
......@@ -20,19 +20,39 @@
namespace content {
// Capabilites supported by a Content Decryption Module.
struct CONTENT_EXPORT CdmCapability {
CdmCapability();
CdmCapability(std::vector<media::VideoCodec> video_codecs,
base::flat_set<media::EncryptionMode> encryption_schemes,
base::flat_set<media::CdmSessionType> session_types);
CdmCapability(const CdmCapability& other);
~CdmCapability();
// List of video codecs supported by the CDM (e.g. vp8). This is the set of
// codecs that can be decrypted and decoded by the CDM. As this is generic,
// not all profiles or levels of the specified codecs may actually be
// supported.
// TODO(crbug.com/796725) Find a way to include profiles and levels.
std::vector<media::VideoCodec> video_codecs;
// List of encryption schemes supported by the CDM (e.g. cenc).
base::flat_set<media::EncryptionMode> encryption_schemes;
// List of session types supported by the CDM.
base::flat_set<media::CdmSessionType> session_types;
};
// Represents a Content Decryption Module implementation and its capabilities.
struct CONTENT_EXPORT CdmInfo {
CdmInfo(
const std::string& name,
const std::string& guid,
const base::Version& version,
const base::FilePath& path,
const std::string& file_system_id,
const std::vector<media::VideoCodec>& supported_video_codecs,
const base::flat_set<media::CdmSessionType>& supported_session_types,
const base::flat_set<media::EncryptionMode>& supported_encryption_schemes,
const std::string& supported_key_system,
bool supports_sub_key_systems);
CdmInfo(const std::string& name,
const std::string& guid,
const base::Version& version,
const base::FilePath& path,
const std::string& file_system_id,
CdmCapability capability,
const std::string& supported_key_system,
bool supports_sub_key_systems);
CdmInfo(const CdmInfo& other);
~CdmInfo();
......@@ -54,18 +74,8 @@ struct CONTENT_EXPORT CdmInfo {
// digits(0-9), or "._-".
std::string file_system_id;
// List of video codecs supported by the CDM (e.g. vp8). This is the set of
// codecs that can be decrypted and decoded by the CDM. As this is generic,
// not all profiles or levels of the specified codecs may actually be
// supported.
// TODO(crbug.com/796725) Find a way to include profiles and levels.
std::vector<media::VideoCodec> supported_video_codecs;
// List of session types supported by the CDM.
base::flat_set<media::CdmSessionType> supported_session_types;
// List of encryption schemes supported by the CDM (e.g. cenc).
base::flat_set<media::EncryptionMode> supported_encryption_schemes;
// CDM capability, e.g. video codecs, encryption schemes and session types.
CdmCapability capability;
// The key system supported by this CDM.
std::string supported_key_system;
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment