Commit 54c6ac50 authored by John Rummell's avatar John Rummell Committed by Commit Bot

Use manifest.json to determine features supported by Widevine CDM on Linux

Use the manifest.json file bundled with Widevine CDM (on Linux) to
determine the features supported by Widevine rather than have them
explicitly specified in code. This is handled by the component updater
for Windows and Mac, so extending it to Linux now that the Widevine CDM
for Linux is in a separate directory.

Bug: 971433
Test: encrypted media browser_tests pass
Change-Id: I696ce5e77b675deb7f97b9b56a6da23814038521
Reviewed-on: https://chromium-review.googlesource.com/c/chromium/src/+/1808309
Commit-Queue: John Rummell <jrummell@chromium.org>
Reviewed-by: default avatarXiaohan Wang <xhwang@chromium.org>
Reviewed-by: default avatarLei Zhang <thestig@chromium.org>
Cr-Commit-Position: refs/heads/master@{#699548}
parent bb12635c
...@@ -90,18 +90,17 @@ ...@@ -90,18 +90,17 @@
#if BUILDFLAG(ENABLE_LIBRARY_CDMS) #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
#include "media/cdm/cdm_paths.h" // nogncheck #include "media/cdm/cdm_paths.h" // nogncheck
// Registers Widevine CDM if Widevine is enabled, the Widevine CDM is #endif
// bundled and not a component. When the Widevine CDM is a component, it is
// registered in widevine_cdm_component_installer.cc. #if BUILDFLAG(BUNDLE_WIDEVINE_CDM)
#if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && !BUILDFLAG(ENABLE_WIDEVINE_CDM_COMPONENT) #include "base/native_library.h"
#define REGISTER_BUNDLED_WIDEVINE_CDM #include "chrome/common/media/cdm_manifest.h"
#include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck #include "third_party/widevine/cdm/widevine_cdm_common.h" // nogncheck
// TODO(crbug.com/663554): Needed for WIDEVINE_CDM_VERSION_STRING. Support // TODO(crbug.com/663554): Needed for WIDEVINE_CDM_VERSION_STRING. Support
// component updated CDM on all desktop platforms and remove this. // component updated CDM on all desktop platforms and remove this.
// This file is In SHARED_INTERMEDIATE_DIR. // This file is In SHARED_INTERMEDIATE_DIR.
#include "widevine_cdm_version.h" // nogncheck #include "widevine_cdm_version.h" // nogncheck
#endif #endif
#endif // BUILDFLAG(ENABLE_LIBRARY_CDMS)
#if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION) #if BUILDFLAG(ENABLE_CDM_HOST_VERIFICATION)
#include "chrome/common/media/cdm_host_file_path.h" #include "chrome/common/media/cdm_host_file_path.h"
...@@ -341,22 +340,53 @@ bool GetSystemPepperFlash(content::PepperPluginInfo* plugin) { ...@@ -341,22 +340,53 @@ bool GetSystemPepperFlash(content::PepperPluginInfo* plugin) {
} }
#endif // BUILDFLAG(ENABLE_PLUGINS) #endif // BUILDFLAG(ENABLE_PLUGINS)
#if defined(REGISTER_BUNDLED_WIDEVINE_CDM) #if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
bool IsWidevineAvailable(base::FilePath* cdm_path, // 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 updates |version| with the version,
// |cdm_path| with the location of the Widevine CDM library, |capability| with
// the capabilities of this version. and returns true. Otherwise it returns
// false, and may or may not have touched the other parameters.
bool GetBundledWidevine(base::Version* version,
base::FilePath* cdm_path,
content::CdmCapability* capability) { content::CdmCapability* capability) {
// Check if the Widevine library can be found.
static enum { static enum {
NOT_CHECKED, NOT_CHECKED,
FOUND, FOUND,
NOT_FOUND, NOT_FOUND,
} widevine_cdm_file_check = NOT_CHECKED; } widevine_cdm_file_check = NOT_CHECKED;
if (base::PathService::Get(chrome::FILE_WIDEVINE_CDM, cdm_path)) { // If we've already checked and not found the CDM, no need to try again,
// hoping that it shows up.
if (widevine_cdm_file_check == NOT_FOUND)
return false;
base::FilePath install_dir;
if (!base::PathService::Get(chrome::DIR_BUNDLED_WIDEVINE_CDM, &install_dir))
return false;
#if defined(OS_CHROMEOS)
// On ChromeOS the Widevine CDM library is in the directory returned above,
// and does not have a manifest.
// TODO(crbug.com/971433): Move Widevine CDM to a separate folder so that the
// manifest can be included.
*cdm_path = install_dir.AppendASCII(
base::GetNativeLibraryName(kWidevineCdmLibraryName));
if (widevine_cdm_file_check == NOT_CHECKED) if (widevine_cdm_file_check == NOT_CHECKED)
widevine_cdm_file_check = base::PathExists(*cdm_path) ? FOUND : NOT_FOUND; widevine_cdm_file_check = base::PathExists(*cdm_path) ? FOUND : NOT_FOUND;
if (widevine_cdm_file_check == FOUND) { if (widevine_cdm_file_check != FOUND)
return false;
// As there is no manifest, set |version| and |capability| as if it came from
// one. These values must match the CDM that is being bundled with Chrome.
*version = base::Version(WIDEVINE_CDM_VERSION_STRING);
// Add the supported codecs as if they came from the component manifest. // Add the supported codecs as if they came from the component manifest.
// This list must match the CDM that is being bundled with Chrome.
capability->video_codecs.push_back(media::VideoCodec::kCodecVP8); capability->video_codecs.push_back(media::VideoCodec::kCodecVP8);
capability->video_codecs.push_back(media::VideoCodec::kCodecVP9); capability->video_codecs.push_back(media::VideoCodec::kCodecVP9);
capability->video_codecs.push_back(media::VideoCodec::kCodecAV1); capability->video_codecs.push_back(media::VideoCodec::kCodecAV1);
...@@ -367,28 +397,30 @@ bool IsWidevineAvailable(base::FilePath* cdm_path, ...@@ -367,28 +397,30 @@ bool IsWidevineAvailable(base::FilePath* cdm_path,
capability->video_codecs.push_back(media::VideoCodec::kCodecH264); capability->video_codecs.push_back(media::VideoCodec::kCodecH264);
#endif // BUILDFLAG(USE_PROPRIETARY_CODECS) #endif // BUILDFLAG(USE_PROPRIETARY_CODECS)
// Add the supported encryption schemes as if they came from the // Both encryption schemes are supported on ChromeOS.
// component manifest. This list must match the CDM that is being
// bundled with Chrome.
capability->encryption_schemes.insert(media::EncryptionMode::kCenc); capability->encryption_schemes.insert(media::EncryptionMode::kCenc);
capability->encryption_schemes.insert(media::EncryptionMode::kCbcs); capability->encryption_schemes.insert(media::EncryptionMode::kCbcs);
// Temporary session is always supported. // Both temporary and persistent sessions are supported on ChromeOS.
capability->session_types.insert(media::CdmSessionType::kTemporary); capability->session_types.insert(media::CdmSessionType::kTemporary);
#if defined(OS_CHROMEOS) capability->session_types.insert(media::CdmSessionType::kPersistentLicense);
// 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::kPersistentLicense);
#endif // defined(OS_CHROMEOS)
return true; return true;
} #else
} // On desktop Linux the MANIFEST is bundled with the CDM.
*cdm_path =
media::GetPlatformSpecificDirectory(install_dir)
.AppendASCII(base::GetNativeLibraryName(kWidevineCdmLibraryName));
if (widevine_cdm_file_check == NOT_CHECKED)
widevine_cdm_file_check = base::PathExists(*cdm_path) ? FOUND : NOT_FOUND;
if (widevine_cdm_file_check != FOUND)
return false; return false;
auto manifest_path = install_dir.Append(FILE_PATH_LITERAL("manifest.json"));
return ParseCdmManifestFromPath(manifest_path, version, capability);
#endif // defined(OS_CHROMEOS)
} }
#endif // defined(REGISTER_BUNDLED_WIDEVINE_CDM) #endif // BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
} // namespace } // namespace
...@@ -513,19 +545,17 @@ void ChromeContentClient::AddContentDecryptionModules( ...@@ -513,19 +545,17 @@ void ChromeContentClient::AddContentDecryptionModules(
std::vector<content::CdmInfo>* cdms, std::vector<content::CdmInfo>* cdms,
std::vector<media::CdmHostFilePath>* cdm_host_file_paths) { std::vector<media::CdmHostFilePath>* cdm_host_file_paths) {
if (cdms) { if (cdms) {
#if defined(REGISTER_BUNDLED_WIDEVINE_CDM) #if BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
base::Version version;
base::FilePath cdm_path; base::FilePath cdm_path;
content::CdmCapability capability; content::CdmCapability capability;
if (IsWidevineAvailable(&cdm_path, &capability)) { if (GetBundledWidevine(&version, &cdm_path, &capability)) {
const base::Version version(WIDEVINE_CDM_VERSION_STRING);
DCHECK(version.IsValid());
cdms->push_back( cdms->push_back(
content::CdmInfo(kWidevineCdmDisplayName, kWidevineCdmGuid, version, content::CdmInfo(kWidevineCdmDisplayName, kWidevineCdmGuid, version,
cdm_path, kWidevineCdmFileSystemId, cdm_path, kWidevineCdmFileSystemId,
std::move(capability), kWidevineKeySystem, false)); std::move(capability), kWidevineKeySystem, false));
} }
#endif // defined(REGISTER_BUNDLED_WIDEVINE_CDM) #endif // BUILDFLAG(BUNDLE_WIDEVINE_CDM) && defined(OS_LINUX)
#if BUILDFLAG(ENABLE_LIBRARY_CDMS) #if BUILDFLAG(ENABLE_LIBRARY_CDMS)
// Register Clear Key CDM if specified in command line. // Register Clear Key CDM if specified in command line.
......
...@@ -372,15 +372,14 @@ bool PathProvider(int key, base::FilePath* result) { ...@@ -372,15 +372,14 @@ bool PathProvider(int key, base::FilePath* result) {
cur = cur.Append(FILE_PATH_LITERAL("pnacl")); cur = cur.Append(FILE_PATH_LITERAL("pnacl"));
break; break;
#if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS) #if BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
// TODO(crbug.com/663554): Remove this after component updated CDM is case chrome::DIR_BUNDLED_WIDEVINE_CDM:
// supported on Linux and ChromeOS.
case chrome::FILE_WIDEVINE_CDM:
if (!GetComponentDirectory(&cur)) if (!GetComponentDirectory(&cur))
return false; return false;
cur = #if !defined(OS_CHROMEOS)
cur.Append( // TODO(crbug.com/971433): Move Widevine CDM to a separate folder on
media::GetPlatformSpecificDirectory(kWidevineCdmBaseDirectory)) // ChromeOS so that the manifest can be included.
.AppendASCII(base::GetNativeLibraryName(kWidevineCdmLibraryName)); cur = cur.AppendASCII(kWidevineCdmBaseDirectory);
#endif // !defined(OS_CHROMEOS)
break; break;
#endif // BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS) #endif // BUILDFLAG(ENABLE_WIDEVINE) && BUILDFLAG(ENABLE_LIBRARY_CDMS)
case chrome::FILE_RESOURCES_PACK: // Falls through. case chrome::FILE_RESOURCES_PACK: // Falls through.
......
...@@ -93,7 +93,8 @@ enum { ...@@ -93,7 +93,8 @@ enum {
DIR_PNACL_BASE, // Full path to the base dir for PNaCl. DIR_PNACL_BASE, // Full path to the base dir for PNaCl.
DIR_PNACL_COMPONENT, // Full path to the latest PNaCl version DIR_PNACL_COMPONENT, // Full path to the latest PNaCl version
// (subdir of DIR_PNACL_BASE). // (subdir of DIR_PNACL_BASE).
FILE_WIDEVINE_CDM, // Full path to the Widevine CDM. DIR_BUNDLED_WIDEVINE_CDM, // Full path to the directory containing the
// bundled Widevine CDM.
FILE_RESOURCES_PACK, // Full path to the .pak file containing binary data. FILE_RESOURCES_PACK, // Full path to the .pak file containing binary data.
// This includes data for internal pages (e.g., html // This includes data for internal pages (e.g., html
// files and images), unless these resources are // files and images), unless these resources are
......
...@@ -5,17 +5,22 @@ ...@@ -5,17 +5,22 @@
#include "chrome/common/media/cdm_manifest.h" #include "chrome/common/media/cdm_manifest.h"
#include <stddef.h> #include <stddef.h>
#include <memory>
#include <string> #include <string>
#include <vector> #include <vector>
#include "base/containers/flat_set.h" #include "base/containers/flat_set.h"
#include "base/containers/span.h" #include "base/containers/span.h"
#include "base/files/file_path.h"
#include "base/json/json_file_value_serializer.h"
#include "base/logging.h" #include "base/logging.h"
#include "base/strings/string_number_conversions.h" #include "base/strings/string_number_conversions.h"
#include "base/strings/string_piece.h" #include "base/strings/string_piece.h"
#include "base/strings/string_split.h" #include "base/strings/string_split.h"
#include "base/values.h" #include "base/values.h"
#include "base/version.h"
#include "content/public/common/cdm_info.h" #include "content/public/common/cdm_info.h"
#include "extensions/common/manifest_constants.h"
#include "media/base/content_decryption_module.h" #include "media/base/content_decryption_module.h"
#include "media/base/decrypt_config.h" #include "media/base/decrypt_config.h"
#include "media/base/video_codecs.h" #include "media/base/video_codecs.h"
...@@ -75,7 +80,9 @@ const char kCdmSupportedCodecLegacyVp9[] = "vp9.0"; ...@@ -75,7 +80,9 @@ const char kCdmSupportedCodecLegacyVp9[] = "vp9.0";
// Supports at least VP9 profile 0 and profile 2. // Supports at least VP9 profile 0 and profile 2.
const char kCdmSupportedCodecVp9[] = "vp09"; const char kCdmSupportedCodecVp9[] = "vp09";
const char kCdmSupportedCodecAv1[] = "av01"; const char kCdmSupportedCodecAv1[] = "av01";
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
const char kCdmSupportedCodecAvc1[] = "avc1"; const char kCdmSupportedCodecAvc1[] = "avc1";
#endif
// The following strings are used to specify supported encryption schemes in // The following strings are used to specify supported encryption schemes in
// the parameter |kCdmSupportedEncryptionSchemesName|. // the parameter |kCdmSupportedEncryptionSchemesName|.
...@@ -168,8 +175,10 @@ bool GetCodecs(const base::Value& manifest, ...@@ -168,8 +175,10 @@ bool GetCodecs(const base::Value& manifest,
*supports_vp9_profile2 = true; *supports_vp9_profile2 = true;
} else if (codec == kCdmSupportedCodecAv1) { } else if (codec == kCdmSupportedCodecAv1) {
result.push_back(media::VideoCodec::kCodecAV1); result.push_back(media::VideoCodec::kCodecAV1);
#if BUILDFLAG(USE_PROPRIETARY_CODECS)
} else if (codec == kCdmSupportedCodecAvc1) { } else if (codec == kCdmSupportedCodecAvc1) {
result.push_back(media::VideoCodec::kCodecH264); result.push_back(media::VideoCodec::kCodecH264);
#endif
} }
} }
...@@ -299,6 +308,25 @@ bool GetCdmProxyProtocols( ...@@ -299,6 +308,25 @@ bool GetCdmProxyProtocols(
return true; return true;
} }
bool GetVersion(const base::Value& manifest, base::Version* version) {
DCHECK(manifest.is_dict());
auto* version_string =
manifest.FindStringKey(extensions::manifest_keys::kVersion);
if (!version_string) {
DLOG(ERROR) << "CDM manifest missing "
<< extensions::manifest_keys::kVersion;
return false;
}
*version = base::Version(*version_string);
if (!version->IsValid()) {
DLOG(ERROR) << "CDM manifest version " << version_string << " is invalid.";
return false;
}
return true;
}
} // namespace } // namespace
bool IsCdmManifestCompatibleWithChrome(const base::Value& manifest) { bool IsCdmManifestCompatibleWithChrome(const base::Value& manifest) {
...@@ -313,10 +341,6 @@ bool IsCdmManifestCompatibleWithChrome(const base::Value& manifest) { ...@@ -313,10 +341,6 @@ bool IsCdmManifestCompatibleWithChrome(const base::Value& manifest) {
media::IsSupportedCdmHostVersion); media::IsSupportedCdmHostVersion);
} }
// Returns true if the entries in the manifest can be parsed correctly,
// false otherwise. Updates |version| and |capability| with the values obtained
// from the manifest, if they are provided. If this method returns false,
// |version| and |capability| may or may not be updated.
bool ParseCdmManifest(const base::Value& manifest, bool ParseCdmManifest(const base::Value& manifest,
content::CdmCapability* capability) { content::CdmCapability* capability) {
DCHECK(manifest.is_dict()); DCHECK(manifest.is_dict());
...@@ -327,3 +351,21 @@ bool ParseCdmManifest(const base::Value& manifest, ...@@ -327,3 +351,21 @@ bool ParseCdmManifest(const base::Value& manifest,
GetSessionTypes(manifest, &capability->session_types) && GetSessionTypes(manifest, &capability->session_types) &&
GetCdmProxyProtocols(manifest, &capability->cdm_proxy_protocols); GetCdmProxyProtocols(manifest, &capability->cdm_proxy_protocols);
} }
bool ParseCdmManifestFromPath(const base::FilePath& manifest_path,
base::Version* version,
content::CdmCapability* capability) {
JSONFileValueDeserializer deserializer(manifest_path);
int error_code;
std::string error_message;
std::unique_ptr<base::Value> manifest =
deserializer.Deserialize(&error_code, &error_message);
if (!manifest || !manifest->is_dict()) {
DLOG(ERROR) << "Could not deserialize CDM manifest from " << manifest_path
<< ". Error: " << error_code << " / " << error_message;
return false;
}
return GetVersion(*manifest, version) &&
ParseCdmManifest(*manifest, capability);
}
...@@ -6,7 +6,9 @@ ...@@ -6,7 +6,9 @@
#define CHROME_COMMON_MEDIA_CDM_MANIFEST_H_ #define CHROME_COMMON_MEDIA_CDM_MANIFEST_H_
namespace base { namespace base {
class FilePath;
class Value; class Value;
class Version;
} }
namespace content { namespace content {
...@@ -26,4 +28,13 @@ bool IsCdmManifestCompatibleWithChrome(const base::Value& manifest); ...@@ -26,4 +28,13 @@ bool IsCdmManifestCompatibleWithChrome(const base::Value& manifest);
bool ParseCdmManifest(const base::Value& manifest, bool ParseCdmManifest(const base::Value& manifest,
content::CdmCapability* capability); content::CdmCapability* capability);
// Reads the file |manifest_path| which is assumed to be a CDM manifest and
// extracts the necessary information from it to update |version| and
// |capability|. Returns true on success, false if there are errors in the file.
// If this method returns false, |version| and |capability| may or may not be
// updated.
bool ParseCdmManifestFromPath(const base::FilePath& manifest_path,
base::Version* version,
content::CdmCapability* capability);
#endif // CHROME_COMMON_MEDIA_CDM_MANIFEST_H_ #endif // CHROME_COMMON_MEDIA_CDM_MANIFEST_H_
...@@ -24,16 +24,19 @@ const base::Token kClearKeyCdmDifferentGuid{0xc3914773474bdb02ull, ...@@ -24,16 +24,19 @@ const base::Token kClearKeyCdmDifferentGuid{0xc3914773474bdb02ull,
// this ID is based on the pepper plugin MIME type. // this ID is based on the pepper plugin MIME type.
const char kClearKeyCdmFileSystemId[] = "application_x-ppapi-clearkey-cdm"; const char kClearKeyCdmFileSystemId[] = "application_x-ppapi-clearkey-cdm";
base::FilePath GetPlatformSpecificDirectory(const std::string& cdm_base_path) { base::FilePath GetPlatformSpecificDirectory(
const base::FilePath& cdm_base_path) {
// CDM_PLATFORM_SPECIFIC_PATH is specified in cdm_paths.gni. // CDM_PLATFORM_SPECIFIC_PATH is specified in cdm_paths.gni.
const std::string kPlatformSpecific = BUILDFLAG(CDM_PLATFORM_SPECIFIC_PATH); const std::string kPlatformSpecific = BUILDFLAG(CDM_PLATFORM_SPECIFIC_PATH);
if (kPlatformSpecific.empty()) if (kPlatformSpecific.empty())
return base::FilePath(); return base::FilePath();
return base::FilePath() return cdm_base_path.AppendASCII(kPlatformSpecific).NormalizePathSeparators();
.AppendASCII(cdm_base_path) }
.AppendASCII(kPlatformSpecific)
.NormalizePathSeparators(); base::FilePath GetPlatformSpecificDirectory(const std::string& cdm_base_path) {
return GetPlatformSpecificDirectory(
base::FilePath::FromUTF8Unsafe(cdm_base_path));
} }
} // namespace media } // namespace media
...@@ -37,6 +37,8 @@ extern const char kClearKeyCdmFileSystemId[]; ...@@ -37,6 +37,8 @@ extern const char kClearKeyCdmFileSystemId[];
// e.g. WidevineCdm/_platform_specific/win_x64 // e.g. WidevineCdm/_platform_specific/win_x64
// Otherwise, returns an empty path. // Otherwise, returns an empty path.
// TODO(xhwang): Use this function in Widevine CDM component installer. // TODO(xhwang): Use this function in Widevine CDM component installer.
base::FilePath GetPlatformSpecificDirectory(
const base::FilePath& cdm_base_path);
base::FilePath GetPlatformSpecificDirectory(const std::string& cdm_base_path); base::FilePath GetPlatformSpecificDirectory(const std::string& cdm_base_path);
} // namespace media } // namespace media
......
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