diff options
-rw-r--r-- | chrome/renderer/media/chrome_key_systems.cc | 43 | ||||
-rw-r--r-- | chromecast/renderer/key_systems_cast.cc | 10 | ||||
-rw-r--r-- | components/cdm/renderer/android_key_systems.cc | 45 | ||||
-rw-r--r-- | components/cdm/renderer/widevine_key_systems.cc | 4 | ||||
-rw-r--r-- | components/cdm/renderer/widevine_key_systems.h | 2 | ||||
-rw-r--r-- | content/renderer/media/render_media_client_unittest.cc | 4 | ||||
-rw-r--r-- | media/base/android/browser_cdm_factory_android.cc | 2 | ||||
-rw-r--r-- | media/base/eme_constants.h | 53 | ||||
-rw-r--r-- | media/base/key_system_info.cc | 2 | ||||
-rw-r--r-- | media/base/key_system_info.h | 7 | ||||
-rw-r--r-- | media/base/key_systems.cc | 390 | ||||
-rw-r--r-- | media/base/key_systems.h | 35 | ||||
-rw-r--r-- | media/base/key_systems_unittest.cc | 4 | ||||
-rw-r--r-- | media/blink/webencryptedmediaclient_impl.cc | 323 | ||||
-rw-r--r-- | media/test/data/eme_player_js/player_utils.js | 4 |
15 files changed, 640 insertions, 288 deletions
diff --git a/chrome/renderer/media/chrome_key_systems.cc b/chrome/renderer/media/chrome_key_systems.cc index 5307481..33fea4a 100644 --- a/chrome/renderer/media/chrome_key_systems.cc +++ b/chrome/renderer/media/chrome_key_systems.cc @@ -75,22 +75,22 @@ static void AddExternalClearKey( KeySystemInfo info; info.key_system = kExternalClearKeyKeySystem; - info.supported_codecs = media::EME_CODEC_WEBM_ALL; info.supported_init_data_types = media::EME_INIT_DATA_TYPE_WEBM | media::EME_INIT_DATA_TYPE_KEYIDS; + info.supported_codecs = media::EME_CODEC_WEBM_ALL; #if defined(USE_PROPRIETARY_CODECS) - info.supported_codecs |= media::EME_CODEC_MP4_ALL; info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC; + info.supported_codecs |= media::EME_CODEC_MP4_ALL; #endif // defined(USE_PROPRIETARY_CODECS) + info.max_audio_robustness = media::EmeRobustness::EMPTY; + info.max_video_robustness = media::EmeRobustness::EMPTY; + // Persistent sessions are faked. info.persistent_license_support = media::EME_SESSION_TYPE_SUPPORTED; info.persistent_release_message_support = media::EME_SESSION_TYPE_NOT_SUPPORTED; - // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from - // succeeding. Change this to REQUESTABLE once the state can be blocked. - // http://crbug.com/457482 - info.persistent_state_support = media::EME_FEATURE_ALWAYS_ENABLED; + info.persistent_state_support = media::EME_FEATURE_REQUESTABLE; info.distinctive_identifier_support = media::EME_FEATURE_NOT_SUPPORTED; info.pepper_type = kExternalClearKeyPepperType; @@ -193,24 +193,21 @@ static void AddPepperBasedWidevine( cdm::AddWidevineWithCodecs( cdm::WIDEVINE, supported_codecs, #if defined(OS_CHROMEOS) - // Persistent licenses are supported if remote attestation succeeds. - media::EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION, - media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent release message. - // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from - // succeeding. Change this to REQUESTABLE once the state can be blocked. - // http://crbug.com/457482 - media::EME_FEATURE_ALWAYS_ENABLED, // Persistent state. - // A distinctive identifier will be available if remote attestation - // succeeds. - media::EME_FEATURE_REQUESTABLE_WITH_PERMISSION, + media::EmeRobustness::HW_SECURE_ALL, // Maximum audio robustness. + media::EmeRobustness::HW_SECURE_ALL, // Maximim video robustness. + // persistent-license. + media::EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER, + media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. + media::EME_FEATURE_REQUESTABLE, // Persistent state. + // Distinctive identifier. + media::EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER, #else // (Desktop) - media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent license. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent release message. - // TODO(sandersd): Using ALWAYS_ENABLED prevents "not-allowed" from - // succeeding. Change this to REQUESTABLE once the state can be blocked. - // http://crbug.com/457482 - media::EME_FEATURE_ALWAYS_ENABLED, // Persistent state. - media::EME_FEATURE_NOT_SUPPORTED, // Distinctive identifier. + media::EmeRobustness::SW_SECURE_CRYPTO, // Maximum audio robustness. + media::EmeRobustness::SW_SECURE_DECODE, // Maximum video robustness. + media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. + media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. + media::EME_FEATURE_REQUESTABLE, // Persistent state. + media::EME_FEATURE_NOT_SUPPORTED, // Distinctive identifier. #endif // defined(OS_CHROMEOS) concrete_key_systems); } diff --git a/chromecast/renderer/key_systems_cast.cc b/chromecast/renderer/key_systems_cast.cc index 246d6ad..995092d 100644 --- a/chromecast/renderer/key_systems_cast.cc +++ b/chromecast/renderer/key_systems_cast.cc @@ -22,9 +22,11 @@ void AddKeySystemWithCodecs( std::vector<::media::KeySystemInfo>* key_systems_info) { ::media::KeySystemInfo info; info.key_system = key_system_name; + info.supported_init_data_types = ::media::EME_INIT_DATA_TYPE_CENC; info.supported_codecs = ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1; - info.supported_init_data_types = ::media::EME_INIT_DATA_TYPE_CENC; + info.max_audio_robustness = ::media::EmeRobustness::EMPTY; + info.max_video_robustness = ::media::EmeRobustness::EMPTY; info.persistent_license_support = ::media::EME_SESSION_TYPE_NOT_SUPPORTED; info.persistent_release_message_support = ::media::EME_SESSION_TYPE_NOT_SUPPORTED; @@ -39,8 +41,10 @@ void AddChromecastKeySystems( AddWidevineWithCodecs( cdm::WIDEVINE, ::media::EME_CODEC_MP4_AAC | ::media::EME_CODEC_MP4_AVC1, - ::media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent license. - ::media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent release message. + ::media::EmeRobustness::HW_SECURE_ALL, // Max audio robustness. + ::media::EmeRobustness::HW_SECURE_ALL, // Max video robustness. + ::media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. + ::media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. ::media::EME_FEATURE_NOT_SUPPORTED, // Persistent state. ::media::EME_FEATURE_ALWAYS_ENABLED, // Distinctive identifier. key_systems_info); diff --git a/components/cdm/renderer/android_key_systems.cc b/components/cdm/renderer/android_key_systems.cc index d1e0f2a..e773656 100644 --- a/components/cdm/renderer/android_key_systems.cc +++ b/components/cdm/renderer/android_key_systems.cc @@ -7,14 +7,17 @@ #include <string> #include <vector> +#include "base/command_line.h" #include "base/logging.h" #include "components/cdm/common/cdm_messages_android.h" #include "components/cdm/renderer/widevine_key_systems.h" #include "content/public/renderer/render_thread.h" #include "media/base/eme_constants.h" +#include "media/base/media_switches.h" #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. +using media::EmeRobustness; using media::KeySystemInfo; using media::SupportedCodecs; @@ -39,24 +42,48 @@ static SupportedKeySystemResponse QueryKeySystemSupport( void AddAndroidWidevine(std::vector<KeySystemInfo>* concrete_key_systems) { SupportedKeySystemResponse response = QueryKeySystemSupport( kWidevineKeySystem); - if (response.compositing_codecs != media::EME_CODEC_NONE) { + + // When creating the WIDEVINE key system, BrowserCdmFactoryAndroid configures + // the CDM's security level based on the --mediadrm-enable-non-compositing + // flag (L1 if the flag is enabled, L3 otherwise). Therefore the supported + // codec/robustenss combinations depend on that flag. + // TODO(sandersd): For unprefixed, set the security level based on the + // requested robustness instead of the flag. http://crbug.com/467779 + SupportedCodecs codecs = response.compositing_codecs; + EmeRobustness max_audio_robustness = EmeRobustness::SW_SECURE_CRYPTO; + EmeRobustness max_video_robustness = EmeRobustness::SW_SECURE_CRYPTO; + if (base::CommandLine::ForCurrentProcess()->HasSwitch( + switches::kMediaDrmEnableNonCompositing)) { + codecs = response.non_compositing_codecs; + max_audio_robustness = EmeRobustness::HW_SECURE_CRYPTO; + max_video_robustness = EmeRobustness::HW_SECURE_ALL; + } + if (codecs != media::EME_CODEC_NONE) { AddWidevineWithCodecs( WIDEVINE, - static_cast<SupportedCodecs>(response.compositing_codecs), - media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent license. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent release message. + codecs, + max_audio_robustness, + max_video_robustness, + media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. + media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. media::EME_FEATURE_NOT_SUPPORTED, // Persistent state. media::EME_FEATURE_ALWAYS_ENABLED, // Distinctive identifier. concrete_key_systems); } + // For compatibility with the prefixed API, register a separate L1 key system. + // When creating the WIDEVINE_HR_NON_COMPOSITING key system, + // BrowserCdmFactoryAndroid does not configure the CDM's security level (that + // is, leaves it as L1); therefore only secure codecs are supported. + // TODO(ddorwin): Remove with unprefixed. http://crbug.com/249976 if (response.non_compositing_codecs != media::EME_CODEC_NONE) { - // TODO(ddorwin): Remove with unprefixed. http://crbug.com/249976 AddWidevineWithCodecs( WIDEVINE_HR_NON_COMPOSITING, - static_cast<SupportedCodecs>(response.non_compositing_codecs), - media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent license. - media::EME_SESSION_TYPE_NOT_SUPPORTED, // Persistent release message. + response.non_compositing_codecs, + EmeRobustness::HW_SECURE_CRYPTO, // Max audio robustness. + EmeRobustness::HW_SECURE_ALL, // Max video robustness. + media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-license. + media::EME_SESSION_TYPE_NOT_SUPPORTED, // persistent-release-message. media::EME_FEATURE_NOT_SUPPORTED, // Persistent state. media::EME_FEATURE_ALWAYS_ENABLED, // Distinctive identifier. concrete_key_systems); @@ -85,6 +112,8 @@ void AddAndroidPlatformKeySystems( if (response.compositing_codecs & media::EME_CODEC_MP4_ALL) info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC; #endif // defined(USE_PROPRIETARY_CODECS) + info.max_audio_robustness = EmeRobustness::EMPTY; + info.max_video_robustness = EmeRobustness::EMPTY; // Assume the worst case (from a user point of view). info.persistent_license_support = media::EME_SESSION_TYPE_NOT_SUPPORTED; info.persistent_release_message_support = diff --git a/components/cdm/renderer/widevine_key_systems.cc b/components/cdm/renderer/widevine_key_systems.cc index 037df1b..ec38ab6 100644 --- a/components/cdm/renderer/widevine_key_systems.cc +++ b/components/cdm/renderer/widevine_key_systems.cc @@ -29,6 +29,8 @@ static std::string GetDirectParentName(std::string name) { void AddWidevineWithCodecs( WidevineCdmType widevine_cdm_type, SupportedCodecs supported_codecs, + media::EmeRobustness max_audio_robustness, + media::EmeRobustness max_video_robustness, media::EmeSessionTypeSupport persistent_license_support, media::EmeSessionTypeSupport persistent_release_message_support, media::EmeFeatureSupport persistent_state_support, @@ -66,6 +68,8 @@ void AddWidevineWithCodecs( info.supported_init_data_types |= media::EME_INIT_DATA_TYPE_CENC; #endif // defined(USE_PROPRIETARY_CODECS) + info.max_audio_robustness = max_audio_robustness; + info.max_video_robustness = max_video_robustness; info.persistent_license_support = persistent_license_support; info.persistent_release_message_support = persistent_release_message_support; info.persistent_state_support = persistent_state_support; diff --git a/components/cdm/renderer/widevine_key_systems.h b/components/cdm/renderer/widevine_key_systems.h index 15c55da..dc05bb8 100644 --- a/components/cdm/renderer/widevine_key_systems.h +++ b/components/cdm/renderer/widevine_key_systems.h @@ -22,6 +22,8 @@ enum WidevineCdmType { void AddWidevineWithCodecs( WidevineCdmType widevine_cdm_type, media::SupportedCodecs supported_codecs, + media::EmeRobustness max_audio_robustness, + media::EmeRobustness max_video_robustness, media::EmeSessionTypeSupport persistent_license_support, media::EmeSessionTypeSupport persistent_release_message_support, media::EmeFeatureSupport persistent_state_support, diff --git a/content/renderer/media/render_media_client_unittest.cc b/content/renderer/media/render_media_client_unittest.cc index 2366156..7ac253e 100644 --- a/content/renderer/media/render_media_client_unittest.cc +++ b/content/renderer/media/render_media_client_unittest.cc @@ -27,6 +27,8 @@ class TestContentRendererClient : public ContentRendererClient { // TODO(sandersd): Was this supposed to be added to the list? media::KeySystemInfo key_system_info; key_system_info.key_system = "test.keysystem"; + key_system_info.max_audio_robustness = media::EmeRobustness::EMPTY; + key_system_info.max_video_robustness = media::EmeRobustness::EMPTY; key_system_info.persistent_license_support = media::EME_SESSION_TYPE_NOT_SUPPORTED; key_system_info.persistent_release_message_support = @@ -40,6 +42,8 @@ class TestContentRendererClient : public ContentRendererClient { if (is_extra_key_system_enabled_) { media::KeySystemInfo wv_key_system_info; wv_key_system_info.key_system = kWidevineKeySystem; + wv_key_system_info.max_audio_robustness = media::EmeRobustness::EMPTY; + wv_key_system_info.max_video_robustness = media::EmeRobustness::EMPTY; wv_key_system_info.persistent_license_support = media::EME_SESSION_TYPE_NOT_SUPPORTED; wv_key_system_info.persistent_release_message_support = diff --git a/media/base/android/browser_cdm_factory_android.cc b/media/base/android/browser_cdm_factory_android.cc index e116863..614c8d1 100644 --- a/media/base/android/browser_cdm_factory_android.cc +++ b/media/base/android/browser_cdm_factory_android.cc @@ -34,7 +34,7 @@ scoped_ptr<BrowserCdm> BrowserCdmFactoryAndroid::CreateBrowserCdm( } // TODO(xhwang/ddorwin): Pass the security level from key system. - // http://crbug.com/459400 + // http://crbug.com/467779 if (key_system == kWidevineKeySystem) { MediaDrmBridge::SecurityLevel security_level = MediaDrmBridge::SECURITY_LEVEL_3; diff --git a/media/base/eme_constants.h b/media/base/eme_constants.h index 2f8f033..7882fae 100644 --- a/media/base/eme_constants.h +++ b/media/base/eme_constants.h @@ -55,32 +55,75 @@ enum EmeSessionTypeSupport { EME_SESSION_TYPE_INVALID, // The session type is not supported. EME_SESSION_TYPE_NOT_SUPPORTED, - // The session type is supported if the encrypted media permission is granted. - EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION, + // The session type is supported if a distinctive identifier is available. + EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER, // The session type is always supported. EME_SESSION_TYPE_SUPPORTED, }; +// Used to declare support for distinctive identifier and persistent state. enum EmeFeatureSupport { // Invalid default value. EME_FEATURE_INVALID, // Access to the feature is not supported at all. EME_FEATURE_NOT_SUPPORTED, - // Access to the feature may be requested if the encrypted media permission is - // granted. - EME_FEATURE_REQUESTABLE_WITH_PERMISSION, + // Access to the feature may be requested if a distinctive identifier is + // available. (This is the correct choice for declaring support for a + // requestable distinctive identifier.) + EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER, // Access to the feature may be requested. EME_FEATURE_REQUESTABLE, // Access to the feature cannot be blocked. EME_FEATURE_ALWAYS_ENABLED, }; +// Used to query support for distinctive identifier and persistent state. enum EmeFeatureRequirement { EME_FEATURE_NOT_ALLOWED, EME_FEATURE_OPTIONAL, EME_FEATURE_REQUIRED, }; +enum class EmeMediaType { + AUDIO, + VIDEO, +}; + +// Robustness values understood by KeySystems. +// Note: KeySystems expects this ordering (in GetRobustnessConfigRule()), +// changes may be required there if this list changes. +enum class EmeRobustness { + INVALID, + EMPTY, + SW_SECURE_CRYPTO, + SW_SECURE_DECODE, + HW_SECURE_CRYPTO, + HW_SECURE_DECODE, + HW_SECURE_ALL, +}; + +// Configuration rules indicate the configuration state required to support a +// configuration option (note: a configuration option may be disallowing a +// feature). Configuration rules are used to answer queries about distinctive +// identifier, persistent state, and robustness requirements, as well as to +// describe support for different session types. +// +// If in the future there are reasons to request user permission other than +// access to a distinctive identifier, then additional rules should be added. +// Rules are implemented in ConfigState and are otherwise opaque. +enum class EmeConfigRule { + // The configuration option is not supported. + NOT_SUPPORTED, + // The configuration option is supported if a distinctive identifier is + // available. + IDENTIFIER_REQUIRED, + // The configuration option is supported, but the user experience may be + // improved if a distinctive identifier is available. + IDENTIFIER_RECOMMENDED, + // The configuration option is supported without conditions. + SUPPORTED, +}; + } // namespace media #endif // MEDIA_BASE_EME_CONSTANTS_H_ diff --git a/media/base/key_system_info.cc b/media/base/key_system_info.cc index b656f0f..55927790 100644 --- a/media/base/key_system_info.cc +++ b/media/base/key_system_info.cc @@ -9,6 +9,8 @@ namespace media { KeySystemInfo::KeySystemInfo() : supported_init_data_types(EME_INIT_DATA_TYPE_NONE), supported_codecs(EME_CODEC_NONE), + max_audio_robustness(EmeRobustness::INVALID), + max_video_robustness(EmeRobustness::INVALID), persistent_license_support(EME_SESSION_TYPE_INVALID), persistent_release_message_support(EME_SESSION_TYPE_INVALID), persistent_state_support(EME_FEATURE_INVALID), diff --git a/media/base/key_system_info.h b/media/base/key_system_info.h index e66d8be..218cebd 100644 --- a/media/base/key_system_info.h +++ b/media/base/key_system_info.h @@ -35,13 +35,10 @@ struct MEDIA_EXPORT KeySystemInfo { std::string key_system; - // Specifies registered initialization data types supported by |key_system|. SupportedInitDataTypes supported_init_data_types; - - // Specifies codecs supported by |key_system|. SupportedCodecs supported_codecs; - - // Specifies session types and features supported by |key_system|. + EmeRobustness max_audio_robustness; + EmeRobustness max_video_robustness; EmeSessionTypeSupport persistent_license_support; EmeSessionTypeSupport persistent_release_message_support; EmeFeatureSupport persistent_state_support; diff --git a/media/base/key_systems.cc b/media/base/key_systems.cc index 92bcc38e..bf33dcc 100644 --- a/media/base/key_systems.cc +++ b/media/base/key_systems.cc @@ -76,6 +76,39 @@ static NamedCodec kCodecStrings[] = { #endif // defined(USE_PROPRIETARY_CODECS) }; +static EmeConfigRule ConvertSessionTypeSupport( + EmeSessionTypeSupport support) { + switch (support) { + case EME_SESSION_TYPE_INVALID: + NOTREACHED(); + return EmeConfigRule::NOT_SUPPORTED; + case EME_SESSION_TYPE_NOT_SUPPORTED: + return EmeConfigRule::NOT_SUPPORTED; + case EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER: + return EmeConfigRule::IDENTIFIER_REQUIRED; + case EME_SESSION_TYPE_SUPPORTED: + return EmeConfigRule::SUPPORTED; + } + NOTREACHED(); + return EmeConfigRule::NOT_SUPPORTED; +} + +static EmeRobustness ConvertRobustness(const std::string& robustness) { + if (robustness.empty()) + return EmeRobustness::EMPTY; + if (robustness == "SW_SECURE_CRYPTO") + return EmeRobustness::SW_SECURE_CRYPTO; + if (robustness == "SW_SECURE_DECODE") + return EmeRobustness::SW_SECURE_DECODE; + if (robustness == "HW_SECURE_CRYPTO") + return EmeRobustness::HW_SECURE_CRYPTO; + if (robustness == "HW_SECURE_DECODE") + return EmeRobustness::HW_SECURE_DECODE; + if (robustness == "HW_SECURE_ALL") + return EmeRobustness::HW_SECURE_ALL; + return EmeRobustness::INVALID; +} + static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) { KeySystemInfo info; info.key_system = kClearKeyKeySystem; @@ -103,6 +136,8 @@ static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) { info.supported_codecs |= EME_CODEC_MP4_ALL; #endif // defined(USE_PROPRIETARY_CODECS) + info.max_audio_robustness = EmeRobustness::EMPTY; + info.max_video_robustness = EmeRobustness::EMPTY; info.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED; info.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED; info.persistent_state_support = EME_FEATURE_NOT_SUPPORTED; @@ -191,23 +226,24 @@ class KeySystems { std::string GetPepperType(const std::string& concrete_key_system); #endif - bool IsPersistentLicenseSessionSupported( + EmeConfigRule GetRobustnessConfigRule( const std::string& key_system, - bool is_permission_granted); + EmeMediaType media_type, + const std::string& requested_robustness); - bool IsPersistentReleaseMessageSessionSupported( - const std::string& key_system, - bool is_permission_granted); + EmeConfigRule GetPersistentLicenseSessionConfigRule( + const std::string& key_system); - bool IsPersistentStateRequirementSupported( + EmeConfigRule GetPersistentReleaseMessageSessionConfigRule( + const std::string& key_system); + + EmeConfigRule GetPersistentStateConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted); + EmeFeatureRequirement requirement); - bool IsDistinctiveIdentifierRequirementSupported( + EmeConfigRule GetDistinctiveIdentifierConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted); + EmeFeatureRequirement requirement); void AddContainerMask(const std::string& container, uint32 mask); void AddCodecMask(const std::string& codec, uint32 mask); @@ -394,45 +430,71 @@ void KeySystems::AddConcreteSupportedKeySystems( for (const KeySystemInfo& info : concrete_key_systems) { DCHECK(!info.key_system.empty()); - DCHECK_NE(info.persistent_license_support, EME_SESSION_TYPE_INVALID); - DCHECK_NE(info.persistent_release_message_support, - EME_SESSION_TYPE_INVALID); - // TODO(sandersd): Add REQUESTABLE and REQUESTABLE_WITH_PERMISSION for - // persistent_state_support once we can block access per-CDM-instance - // (http://crbug.com/457482). - DCHECK(info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED || - info.persistent_state_support == EME_FEATURE_ALWAYS_ENABLED); -// TODO(sandersd): Allow REQUESTABLE_WITH_PERMISSION for all key systems on -// all platforms once we have proper enforcement (http://crbug.com/457482). -// On Chrome OS, an ID will not be used without permission, but we cannot -// currently prevent the CDM from requesting the permission again when no -// there was no initial prompt. Thus, we block "not-allowed" below. -#if defined(OS_CHROMEOS) - DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED || - (info.distinctive_identifier_support == - EME_FEATURE_REQUESTABLE_WITH_PERMISSION && - info.key_system == kWidevineKeySystem) || - info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED); -#else - DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED || - info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED); -#endif + DCHECK(info.max_audio_robustness != EmeRobustness::INVALID); + DCHECK(info.max_video_robustness != EmeRobustness::INVALID); + DCHECK(info.persistent_license_support != EME_SESSION_TYPE_INVALID); + DCHECK(info.persistent_release_message_support != EME_SESSION_TYPE_INVALID); + DCHECK(info.persistent_state_support != EME_FEATURE_INVALID); + DCHECK(info.distinctive_identifier_support != EME_FEATURE_INVALID); + + // Supporting persistent state is a prerequsite for supporting persistent + // sessions. if (info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED) { - DCHECK_EQ(info.persistent_license_support, - EME_SESSION_TYPE_NOT_SUPPORTED); - DCHECK_EQ(info.persistent_release_message_support, - EME_SESSION_TYPE_NOT_SUPPORTED); + DCHECK(info.persistent_license_support == EME_SESSION_TYPE_NOT_SUPPORTED); + DCHECK(info.persistent_release_message_support == + EME_SESSION_TYPE_NOT_SUPPORTED); } - DCHECK(!IsSupportedKeySystem(info.key_system)) - << "Key system '" << info.key_system << "' already registered"; - DCHECK(!parent_key_system_map_.count(info.key_system)) - << "'" << info.key_system << "' is already registered as a parent"; + else if (info.persistent_state_support == + EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER) { + // Must be either NOT_SUPPORTED or SUPPORTED_WITH_IDENTIFIER. + DCHECK(info.persistent_license_support != EME_SESSION_TYPE_SUPPORTED); + DCHECK(info.persistent_release_message_support != + EME_SESSION_TYPE_SUPPORTED); + } + + // persistent-release-message sessions are not currently supported. + // http://crbug.com/448888 + DCHECK(info.persistent_release_message_support == + EME_SESSION_TYPE_NOT_SUPPORTED); + + // Because an optional persistent state value is resolved after an optional + // distinctive identifier, persistent state requiring a distinctive + // identifier may not resolve correctly. + DCHECK(info.persistent_state_support != + EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER); + + // If supported, distinctive identifiers always require permission. + DCHECK(info.distinctive_identifier_support != EME_FEATURE_REQUESTABLE); + + // If distinctive identifiers are not supported, then no other features can + // require them. + if (info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED) { + DCHECK(info.persistent_license_support != + EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER); + DCHECK(info.persistent_release_message_support != + EME_SESSION_TYPE_SUPPORTED_WITH_IDENTIFIER); + } + + // Distinctive identifiers and persistent state can only be reliably blocked + // (and therefore be safely configurable) for Pepper-hosted key systems. For + // other platforms, only non-configurable values are valid. + bool can_block = false; #if defined(ENABLE_PEPPER_CDMS) DCHECK_EQ(info.use_aes_decryptor, info.pepper_type.empty()); + can_block = !info.pepper_type.empty(); #endif + if (!can_block) { + DCHECK(info.distinctive_identifier_support == EME_FEATURE_NOT_SUPPORTED || + info.distinctive_identifier_support == EME_FEATURE_ALWAYS_ENABLED); + DCHECK(info.persistent_state_support == EME_FEATURE_NOT_SUPPORTED || + info.persistent_state_support == EME_FEATURE_ALWAYS_ENABLED); + } + DCHECK(!IsSupportedKeySystem(info.key_system)) + << "Key system '" << info.key_system << "' already registered"; + DCHECK(!parent_key_system_map_.count(info.key_system)) + << "'" << info.key_system << "' is already registered as a parent"; concrete_key_system_map_[info.key_system] = info; - if (!info.parent_key_system.empty()) { DCHECK(!IsConcreteSupportedKeySystem(info.parent_key_system)) << "Parent '" << info.parent_key_system << "' " @@ -620,132 +682,172 @@ std::string KeySystems::GetPepperType(const std::string& concrete_key_system) { } #endif -bool KeySystems::IsPersistentLicenseSessionSupported( +EmeConfigRule KeySystems::GetRobustnessConfigRule( const std::string& key_system, - bool is_permission_granted) { + EmeMediaType media_type, + const std::string& requested_robustness) { DCHECK(thread_checker_.CalledOnValidThread()); + EmeRobustness robustness = ConvertRobustness(requested_robustness); + if (robustness == EmeRobustness::INVALID) + return EmeConfigRule::NOT_SUPPORTED; + if (robustness == EmeRobustness::EMPTY) + return EmeConfigRule::SUPPORTED; + KeySystemInfoMap::const_iterator key_system_iter = concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return false; + return EmeConfigRule::NOT_SUPPORTED; } - switch (key_system_iter->second.persistent_license_support) { - case EME_SESSION_TYPE_INVALID: - NOTREACHED(); - return false; - case EME_SESSION_TYPE_NOT_SUPPORTED: - return false; - case EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION: - return is_permission_granted; - case EME_SESSION_TYPE_SUPPORTED: - return true; + EmeRobustness max_robustness = EmeRobustness::INVALID; + switch (media_type) { + case EmeMediaType::AUDIO: + max_robustness = key_system_iter->second.max_audio_robustness; + break; + case EmeMediaType::VIDEO: + max_robustness = key_system_iter->second.max_video_robustness; + break; } - NOTREACHED(); - return false; + // We can compare robustness levels whenever they are not HW_SECURE_CRYPTO + // and SW_SECURE_DECODE in some order. If they are exactly those two then the + // robustness requirement is not supported. + if ((max_robustness == EmeRobustness::HW_SECURE_CRYPTO && + robustness == EmeRobustness::SW_SECURE_DECODE) || + (max_robustness == EmeRobustness::SW_SECURE_DECODE && + robustness == EmeRobustness::HW_SECURE_CRYPTO) || + robustness > max_robustness) { + return EmeConfigRule::NOT_SUPPORTED; + } + +#if defined(OS_CHROMEOS) + if (key_system == kWidevineKeySystem) { + // Hardware security requires remote attestation. + if (robustness >= EmeRobustness::HW_SECURE_CRYPTO) + return EmeConfigRule::IDENTIFIER_REQUIRED; + + // For video, recommend remote attestation if HW_SECURE_ALL is available, + // because it enables hardware accelerated decoding. + // TODO(sandersd): Only do this when hardware accelerated decoding is + // available for the requested codecs. + if (media_type == EmeMediaType::VIDEO && + max_robustness == EmeRobustness::HW_SECURE_ALL) { + return EmeConfigRule::IDENTIFIER_RECOMMENDED; + } + } +#endif // defined(OS_CHROMEOS) + + return EmeConfigRule::SUPPORTED; } -bool KeySystems::IsPersistentReleaseMessageSessionSupported( - const std::string& key_system, - bool is_permission_granted) { +EmeConfigRule KeySystems::GetPersistentLicenseSessionConfigRule( + const std::string& key_system) { DCHECK(thread_checker_.CalledOnValidThread()); KeySystemInfoMap::const_iterator key_system_iter = concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return false; + return EmeConfigRule::NOT_SUPPORTED; } + return ConvertSessionTypeSupport( + key_system_iter->second.persistent_license_support); +} - switch (key_system_iter->second.persistent_release_message_support) { - case EME_SESSION_TYPE_INVALID: - NOTREACHED(); - return false; - case EME_SESSION_TYPE_NOT_SUPPORTED: - return false; - case EME_SESSION_TYPE_SUPPORTED_WITH_PERMISSION: - return is_permission_granted; - case EME_SESSION_TYPE_SUPPORTED: - return true; - } +EmeConfigRule KeySystems::GetPersistentReleaseMessageSessionConfigRule( + const std::string& key_system) { + DCHECK(thread_checker_.CalledOnValidThread()); - NOTREACHED(); - return false; + KeySystemInfoMap::const_iterator key_system_iter = + concrete_key_system_map_.find(key_system); + if (key_system_iter == concrete_key_system_map_.end()) { + NOTREACHED(); + return EmeConfigRule::NOT_SUPPORTED; + } + return ConvertSessionTypeSupport( + key_system_iter->second.persistent_release_message_support); } -bool KeySystems::IsPersistentStateRequirementSupported( +EmeConfigRule KeySystems::GetPersistentStateConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted) { + EmeFeatureRequirement requirement) { DCHECK(thread_checker_.CalledOnValidThread()); KeySystemInfoMap::const_iterator key_system_iter = concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return false; + return EmeConfigRule::NOT_SUPPORTED; } - switch (key_system_iter->second.persistent_state_support) { - case EME_FEATURE_INVALID: - NOTREACHED(); - return false; - case EME_FEATURE_NOT_SUPPORTED: - return requirement != EME_FEATURE_REQUIRED; - case EME_FEATURE_REQUESTABLE_WITH_PERMISSION: - return (requirement != EME_FEATURE_REQUIRED) || is_permission_granted; - case EME_FEATURE_REQUESTABLE: - return true; - case EME_FEATURE_ALWAYS_ENABLED: - // Persistent state does not require user permission, but the session - // types that use it might. - return requirement != EME_FEATURE_NOT_ALLOWED; + // For NOT_ALLOWED and REQUIRED, the result is as expected. For OPTIONAL, we + // return the least restrictive of the two rules; this guarantees that if + // OPTIONAL is accepted, then it can always be resolved to some value. (In + // fact OPTIONAL is always accepted, because the least restrictive rule is + // always SUPPORTED.) + // + // Note that even if permission is not required for persistent state, it may + // be required for specific persistent session types. + // + // NOT_ALLOWED OPTIONAL REQUIRED + // NOT_SUPPORTED SUPPORTED SUPPORTED NOT_SUPPORTED + // REQUESTABLE_WITH_IDENTIFIER SUPPORTED SUPPORTED IDENTIFIER_REQ + // REQUESTABLE SUPPORTED SUPPORTED SUPPORTED + // ALWAYS_ENABLED NOT_SUPPORTED SUPPORTED SUPPORTED + EmeFeatureSupport support = key_system_iter->second.persistent_state_support; + if (support == EME_FEATURE_NOT_SUPPORTED && + requirement == EME_FEATURE_REQUIRED) { + return EmeConfigRule::NOT_SUPPORTED; } - - NOTREACHED(); - return false; + if (support == EME_FEATURE_ALWAYS_ENABLED && + requirement == EME_FEATURE_NOT_ALLOWED) { + return EmeConfigRule::NOT_SUPPORTED; + } + if (support == EME_FEATURE_REQUESTABLE_WITH_IDENTIFIER && + requirement == EME_FEATURE_REQUIRED) { + return EmeConfigRule::IDENTIFIER_REQUIRED; + } + return EmeConfigRule::SUPPORTED; } -bool KeySystems::IsDistinctiveIdentifierRequirementSupported( +EmeConfigRule KeySystems::GetDistinctiveIdentifierConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted) { + EmeFeatureRequirement requirement) { DCHECK(thread_checker_.CalledOnValidThread()); KeySystemInfoMap::const_iterator key_system_iter = concrete_key_system_map_.find(key_system); if (key_system_iter == concrete_key_system_map_.end()) { NOTREACHED(); - return false; + return EmeConfigRule::NOT_SUPPORTED; } - switch (key_system_iter->second.distinctive_identifier_support) { - case EME_FEATURE_INVALID: - NOTREACHED(); - return false; - case EME_FEATURE_NOT_SUPPORTED: - return requirement != EME_FEATURE_REQUIRED; - case EME_FEATURE_REQUESTABLE_WITH_PERMISSION: - // TODO(sandersd): Remove this hack once crbug.com/457482 and - // crbug.com/460616 are addressed. - // We cannot currently enforce "not-allowed", so don't allow it. - // Note: Removing this check will expose crbug.com/460616. - if (requirement == EME_FEATURE_NOT_ALLOWED) - return false; - return (requirement != EME_FEATURE_REQUIRED) || is_permission_granted; - case EME_FEATURE_REQUESTABLE: - NOTREACHED(); - return true; - case EME_FEATURE_ALWAYS_ENABLED: - // Distinctive identifiers always require user permission. - return (requirement != EME_FEATURE_NOT_ALLOWED) && is_permission_granted; + // Permission is required for REQUIRED, but not for NOT_ALLOWED. For OPTIONAL, + // we return the least restrictive of the two rules; this guarantees that if + // OPTIONAL is accepted, then it can always be resolved to some value. + // + // NOT_ALLOWED OPTIONAL REQUIRED + // NOT_SUPPORTED SUPPORTED SUPPORTED NOT_SUPPORTED + // REQUESTABLE_WITH_IDENTIFIER SUPPORTED SUPPORTED IDENTIFIER_REQ + // ALWAYS_ENABLED NOT_SUPPORTED IDENTIFIER_REQ IDENTIFIER_REQ + EmeFeatureSupport support = + key_system_iter->second.distinctive_identifier_support; + DCHECK(support != EME_FEATURE_REQUESTABLE); + if (support == EME_FEATURE_NOT_SUPPORTED && + requirement == EME_FEATURE_REQUIRED) { + return EmeConfigRule::NOT_SUPPORTED; } - - NOTREACHED(); - return false; + if (support == EME_FEATURE_ALWAYS_ENABLED && + requirement == EME_FEATURE_NOT_ALLOWED) { + return EmeConfigRule::NOT_SUPPORTED; + } + if (support == EME_FEATURE_ALWAYS_ENABLED || + requirement == EME_FEATURE_REQUIRED) { + return EmeConfigRule::IDENTIFIER_REQUIRED; + } + return EmeConfigRule::SUPPORTED; } void KeySystems::AddContainerMask(const std::string& container, uint32 mask) { @@ -850,34 +952,38 @@ std::string GetPepperType(const std::string& concrete_key_system) { } #endif -bool IsPersistentLicenseSessionSupported( +EmeConfigRule GetRobustnessConfigRule( const std::string& key_system, - bool is_permission_granted) { - return KeySystems::GetInstance().IsPersistentLicenseSessionSupported( - key_system, is_permission_granted); + EmeMediaType media_type, + const std::string& robustness) { + return KeySystems::GetInstance().GetRobustnessConfigRule( + key_system, media_type, robustness); } -bool IsPersistentReleaseMessageSessionSupported( - const std::string& key_system, - bool is_permission_granted) { - return KeySystems::GetInstance().IsPersistentReleaseMessageSessionSupported( - key_system, is_permission_granted); +EmeConfigRule GetPersistentLicenseSessionConfigRule( + const std::string& key_system) { + return KeySystems::GetInstance().GetPersistentLicenseSessionConfigRule( + key_system); +} + +EmeConfigRule GetPersistentReleaseMessageSessionConfigRule( + const std::string& key_system) { + return KeySystems::GetInstance().GetPersistentReleaseMessageSessionConfigRule( + key_system); } -bool IsPersistentStateRequirementSupported( +EmeConfigRule GetPersistentStateConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted) { - return KeySystems::GetInstance().IsPersistentStateRequirementSupported( - key_system, requirement, is_permission_granted); + EmeFeatureRequirement requirement) { + return KeySystems::GetInstance().GetPersistentStateConfigRule( + key_system, requirement); } -bool IsDistinctiveIdentifierRequirementSupported( +EmeConfigRule GetDistinctiveIdentifierConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted) { - return KeySystems::GetInstance().IsDistinctiveIdentifierRequirementSupported( - key_system, requirement, is_permission_granted); + EmeFeatureRequirement requirement) { + return KeySystems::GetInstance().GetDistinctiveIdentifierConfigRule( + key_system, requirement); } // These two functions are for testing purpose only. The declaration in the diff --git a/media/base/key_systems.h b/media/base/key_systems.h index 93317a1..9cb727f 100644 --- a/media/base/key_systems.h +++ b/media/base/key_systems.h @@ -85,27 +85,32 @@ MEDIA_EXPORT std::string GetPepperType( const std::string& concrete_key_system); #endif -// Returns whether |key_system| supports persistent-license sessions. -MEDIA_EXPORT bool IsPersistentLicenseSessionSupported( +// Returns the configuration rule for supporting a robustness requirement. +// TODO(sandersd): Also take a list of codecs, as they may affect the result. +MEDIA_EXPORT EmeConfigRule GetRobustnessConfigRule( const std::string& key_system, - bool is_permission_granted); + EmeMediaType media_type, + const std::string& requested_robustness); -// Returns whether |key_system| supports persistent-release-message sessions. -MEDIA_EXPORT bool IsPersistentReleaseMessageSessionSupported( - const std::string& key_system, - bool is_permission_granted); +// Returns the configuration rule for supporting persistent-license sessions. +MEDIA_EXPORT EmeConfigRule GetPersistentLicenseSessionConfigRule( + const std::string& key_system); + +// Returns the configuration rule for supporting persistent-release-message +// sessions. +MEDIA_EXPORT EmeConfigRule GetPersistentReleaseMessageSessionConfigRule( + const std::string& key_system); -// Returns whether |key_system| supports persistent state as requested. -MEDIA_EXPORT bool IsPersistentStateRequirementSupported( +// Returns the configuration rule for supporting a persistent state requirement. +MEDIA_EXPORT EmeConfigRule GetPersistentStateConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted); + EmeFeatureRequirement requirement); -// Returns whether |key_system| supports distinctive identifiers as requested. -MEDIA_EXPORT bool IsDistinctiveIdentifierRequirementSupported( +// Returns the configuration rule for supporting a distinctive identifier +// requirement. +MEDIA_EXPORT EmeConfigRule GetDistinctiveIdentifierConfigRule( const std::string& key_system, - EmeFeatureRequirement requirement, - bool is_permission_granted); + EmeFeatureRequirement requirement); #if defined(UNIT_TEST) // Helper functions to add container/codec types for testing purposes. diff --git a/media/base/key_systems_unittest.cc b/media/base/key_systems_unittest.cc index bb60e8b..9c5c46b 100644 --- a/media/base/key_systems_unittest.cc +++ b/media/base/key_systems_unittest.cc @@ -166,6 +166,8 @@ void TestMediaClient::AddUsesAesKeySystem( system.supported_codecs = EME_CODEC_WEBM_ALL; system.supported_codecs |= TEST_CODEC_FOO_ALL; system.supported_init_data_types = EME_INIT_DATA_TYPE_WEBM; + system.max_audio_robustness = EmeRobustness::EMPTY; + system.max_video_robustness = EmeRobustness::EMPTY; system.persistent_license_support = EME_SESSION_TYPE_NOT_SUPPORTED; system.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED; system.persistent_state_support = EME_FEATURE_NOT_SUPPORTED; @@ -181,6 +183,8 @@ void TestMediaClient::AddExternalKeySystem( ext.supported_codecs = EME_CODEC_WEBM_ALL; ext.supported_codecs |= TEST_CODEC_FOO_ALL; ext.supported_init_data_types = EME_INIT_DATA_TYPE_WEBM; + ext.max_audio_robustness = EmeRobustness::EMPTY; + ext.max_video_robustness = EmeRobustness::EMPTY; ext.persistent_license_support = EME_SESSION_TYPE_SUPPORTED; ext.persistent_release_message_support = EME_SESSION_TYPE_NOT_SUPPORTED; ext.persistent_state_support = EME_FEATURE_ALWAYS_ENABLED; diff --git a/media/blink/webencryptedmediaclient_impl.cc b/media/blink/webencryptedmediaclient_impl.cc index e770c4e..d9b6c96 100644 --- a/media/blink/webencryptedmediaclient_impl.cc +++ b/media/blink/webencryptedmediaclient_impl.cc @@ -31,6 +31,101 @@ enum ConfigurationSupport { CONFIGURATION_SUPPORTED, }; +// Accumulates configuration rules to determine if a feature (additional +// configuration rule) can be added to an accumulated configuration. +class ConfigState { + public: + ConfigState(bool was_permission_requested, bool is_permission_granted) + : was_permission_requested_(was_permission_requested), + is_permission_granted_(is_permission_granted), + is_identifier_required_(false), + is_identifier_recommended_(false){ + } + + bool IsPermissionGranted() const { + return is_permission_granted_; + } + + // Permission is possible if it has not been denied. + bool IsPermissionPossible() const { + return is_permission_granted_ || !was_permission_requested_; + } + + bool IsIdentifierRequired() const { + return is_identifier_required_; + } + + bool IsIdentifierRecommended() const { + return is_identifier_recommended_; + } + + // Checks whether a rule is compatible with all previously added rules. + bool IsRuleSupported(EmeConfigRule rule) const { + switch (rule) { + case EmeConfigRule::NOT_SUPPORTED: + return false; + case EmeConfigRule::IDENTIFIER_REQUIRED: + return IsPermissionPossible(); + case EmeConfigRule::IDENTIFIER_RECOMMENDED: + return true; + case EmeConfigRule::SUPPORTED: + return true; + } + NOTREACHED(); + return false; + } + + // Checks whether a rule is compatible with all previously added rules, and + // can be accepted without needing to add it to the configuration state. This + // allows considering more rules after the configuration state is final (that + // is, after distinctiveIdentifier has been resolved). + bool IsRuleSupportedWithCurrentState(EmeConfigRule rule) const { + switch (rule) { + case EmeConfigRule::NOT_SUPPORTED: + return false; + case EmeConfigRule::IDENTIFIER_REQUIRED: + return is_permission_granted_; + case EmeConfigRule::IDENTIFIER_RECOMMENDED: + return true; + case EmeConfigRule::SUPPORTED: + return true; + } + NOTREACHED(); + return false; + } + + // Add a rule to the accumulated configuration state. + void AddRule(EmeConfigRule rule) { + switch (rule) { + case EmeConfigRule::NOT_SUPPORTED: + return; + case EmeConfigRule::IDENTIFIER_REQUIRED: + is_identifier_required_ = true; + return; + case EmeConfigRule::IDENTIFIER_RECOMMENDED: + is_identifier_recommended_ = true; + return; + case EmeConfigRule::SUPPORTED: + return; + } + NOTREACHED(); + } + + private: + // Whether permission to use a distinctive identifier was requested. If set, + // |is_permission_granted_| represents the final decision. + const bool was_permission_requested_; + + // Whether permission to use a distinctive identifier has been granted. + const bool is_permission_granted_; + + // Whether a rule has been added that requires a distinctive identifier. + bool is_identifier_required_; + + // Whether a rule has been added that recommends a distinctive identifier. + bool is_identifier_recommended_; +}; + static bool IsSupportedContentType(const std::string& key_system, const std::string& mime_type, const std::string& codecs) { @@ -65,23 +160,31 @@ static bool IsSupportedContentType(const std::string& key_system, static bool GetSupportedCapabilities( const std::string& key_system, const blink::WebVector<blink::WebMediaKeySystemMediaCapability>& - capabilities, + requested_media_capabilities, + EmeMediaType media_type, std::vector<blink::WebMediaKeySystemMediaCapability>* - media_type_capabilities) { + supported_media_capabilities, + ConfigState* config_state) { // From // https://w3c.github.io/encrypted-media/#get-supported-capabilities-for-media-type - // 1. Let accumulated capabilities be partial configuration. - // (Skipped as there are no configuration-based codec restrictions.) - // 2. Let media type capabilities be empty. - DCHECK_EQ(media_type_capabilities->size(), 0ul); - // 3. For each value in capabilities: - for (size_t i = 0; i < capabilities.size(); i++) { + // 1. Let local accumulated capabilities be a local copy of partial + // configuration. + // (Skipped as we directly update |config_state|. This is safe because we + // only do so when at least one requested media capability is supported.) + // 2. Let supported media capabilities be empty. + DCHECK_EQ(supported_media_capabilities->size(), 0ul); + // 3. For each value in requested media capabilities: + for (size_t i = 0; i < requested_media_capabilities.size(); i++) { // 3.1. Let contentType be the value's contentType member. // 3.2. Let robustness be the value's robustness member. - const blink::WebMediaKeySystemMediaCapability& capability = capabilities[i]; + const blink::WebMediaKeySystemMediaCapability& capability = + requested_media_capabilities[i]; // 3.3. If contentType is the empty string, return null. - if (capability.mimeType.isEmpty()) + if (capability.mimeType.isEmpty()) { + DVLOG(2) << "Rejecting requested configuration because " + << "a capability contentType was empty."; return false; + } // 3.4-3.11. (Implemented by IsSupportedContentType().) if (!base::IsStringASCII(capability.mimeType) || !base::IsStringASCII(capability.codecs) || @@ -91,26 +194,38 @@ static bool GetSupportedCapabilities( continue; } // 3.12. If robustness is not the empty string, run the following steps: - // (Robustness is not supported.) - // TODO(sandersd): Implement robustness. http://crbug.com/442586 if (!capability.robustness.isEmpty()) { - LOG(WARNING) << "Configuration rejected because rubustness strings are " - << "not yet supported."; - continue; + // 3.12.1. If robustness is an unrecognized value or not supported by + // implementation, continue to the next iteration. String + // comparison is case-sensitive. + if (!base::IsStringASCII(capability.robustness)) + continue; + EmeConfigRule robustness_rule = GetRobustnessConfigRule( + key_system, media_type, base::UTF16ToASCII(capability.robustness)); + if (!config_state->IsRuleSupported(robustness_rule)) + continue; + config_state->AddRule(robustness_rule); + // 3.12.2. Add robustness to configuration. + // (It's already added, we use capability as configuration.) } // 3.13. If the user agent and implementation do not support playback of // encrypted media data as specified by configuration, including all - // media types, in combination with accumulated capabilities, + // media types, in combination with local accumulated capabilities, // continue to the next iteration. - // (Skipped as there are no configuration-based codec restrictions.) - // 3.14. Add configuration to media type capabilities. - media_type_capabilities->push_back(capability); - // 3.15. Add configuration to accumulated capabilities. - // (Skipped as there are no configuration-based codec restrictions.) + // (This is handled when adding rules to |config_state|.) + // 3.14. Add configuration to supported media capabilities. + supported_media_capabilities->push_back(capability); + // 3.15. Add configuration to local accumulated capabilities. + // (Skipped as we directly update |config_state|.) + } + // 4. If supported media capabilities is empty, return null. + if (supported_media_capabilities->empty()) { + DVLOG(2) << "Rejecting requested configuration because " + << "no capabilities were supported."; + return false; } - // 4. If media type capabilities is empty, return null. // 5. Return media type capabilities. - return !media_type_capabilities->empty(); + return true; } static EmeFeatureRequirement ConvertRequirement( @@ -131,15 +246,10 @@ static EmeFeatureRequirement ConvertRequirement( static ConfigurationSupport GetSupportedConfiguration( const std::string& key_system, const blink::WebMediaKeySystemConfiguration& candidate, - blink::WebMediaKeySystemConfiguration* accumulated_configuration, bool was_permission_requested, - bool is_permission_granted) { - DCHECK(was_permission_requested || !is_permission_granted); - - // It is possible to obtain user permission unless permission was already - // requested and denied. - bool is_permission_possible = - !was_permission_requested || is_permission_granted; + bool is_permission_granted, + blink::WebMediaKeySystemConfiguration* accumulated_configuration) { + ConfigState config_state(was_permission_requested, is_permission_granted); // From https://w3c.github.io/encrypted-media/#get-supported-configuration // 1. Let accumulated configuration be empty. (Done by caller.) @@ -184,8 +294,11 @@ static ConfigurationSupport GetSupportedConfiguration( } // 2.3. If supported types is empty, return null. - if (supported_types.empty()) + if (supported_types.empty()) { + DVLOG(2) << "Rejecting requested configuration because " + << "no initDataType values were supported."; return CONFIGURATION_NOT_SUPPORTED; + } // 2.4. Add supported types to accumulated configuration. accumulated_configuration->initDataTypes = supported_types; @@ -200,12 +313,16 @@ static ConfigurationSupport GetSupportedConfiguration( // - "not-allowed": If the implementation requires a Distinctive // Identifier in combination with accumulated configuration, return // null. - EmeFeatureRequirement di_requirement = - ConvertRequirement(candidate.distinctiveIdentifier); - if (!IsDistinctiveIdentifierRequirementSupported(key_system, di_requirement, - is_permission_possible)) { + // We also reject OPTIONAL when distinctive identifiers are ALWAYS_ENABLED and + // permission has already been denied. This would happen anyway at step 11. + EmeConfigRule di_rule = GetDistinctiveIdentifierConfigRule( + key_system, ConvertRequirement(candidate.distinctiveIdentifier)); + if (!config_state.IsRuleSupported(di_rule)) { + DVLOG(2) << "Rejecting requested configuration because " + << "the distinctiveIdentifier requirement was not supported."; return CONFIGURATION_NOT_SUPPORTED; } + config_state.AddRule(di_rule); // 4. Add the value of the candidate configuration's distinctiveIdentifier // attribute to accumulated configuration. @@ -219,12 +336,14 @@ static ConfigurationSupport GetSupportedConfiguration( // - "optional": Continue. // - "not-allowed": If the implementation requires persisting state in // combination with accumulated configuration, return null. - EmeFeatureRequirement ps_requirement = - ConvertRequirement(candidate.persistentState); - if (!IsPersistentStateRequirementSupported(key_system, ps_requirement, - is_permission_possible)) { + EmeConfigRule ps_rule = GetPersistentStateConfigRule( + key_system, ConvertRequirement(candidate.persistentState)); + if (!config_state.IsRuleSupported(ps_rule)) { + DVLOG(2) << "Rejecting requested configuration because " + << "the persistentState requirement was not supported."; return CONFIGURATION_NOT_SUPPORTED; } + config_state.AddRule(ps_rule); // 6. Add the value of the candidate configuration's persistentState // attribute to accumulated configuration. @@ -240,7 +359,8 @@ static ConfigurationSupport GetSupportedConfiguration( // 7.2. If video capabilities is null, return null. std::vector<blink::WebMediaKeySystemMediaCapability> video_capabilities; if (!GetSupportedCapabilities(key_system, candidate.videoCapabilities, - &video_capabilities)) { + EmeMediaType::VIDEO, &video_capabilities, + &config_state)) { return CONFIGURATION_NOT_SUPPORTED; } @@ -258,7 +378,8 @@ static ConfigurationSupport GetSupportedConfiguration( // 8.2. If audio capabilities is null, return null. std::vector<blink::WebMediaKeySystemMediaCapability> audio_capabilities; if (!GetSupportedCapabilities(key_system, candidate.audioCapabilities, - &audio_capabilities)) { + EmeMediaType::AUDIO, &audio_capabilities, + &config_state)) { return CONFIGURATION_NOT_SUPPORTED; } @@ -274,20 +395,50 @@ static ConfigurationSupport GetSupportedConfiguration( // configuration's distinctiveIdentifier value to "required". // - Otherwise, change accumulated configuration's distinctiveIdentifier // value to "not-allowed". - // (Without robustness support, capabilities do not affect this.) - // TODO(sandersd): Implement robustness. http://crbug.com/442586 if (accumulated_configuration->distinctiveIdentifier == blink::WebMediaKeySystemConfiguration::Requirement::Optional) { - if (IsDistinctiveIdentifierRequirementSupported( - key_system, EME_FEATURE_NOT_ALLOWED, is_permission_possible)) { - accumulated_configuration->distinctiveIdentifier = - blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed; - } else { + EmeConfigRule not_allowed_rule = + GetDistinctiveIdentifierConfigRule(key_system, EME_FEATURE_NOT_ALLOWED); + EmeConfigRule required_rule = + GetDistinctiveIdentifierConfigRule(key_system, EME_FEATURE_REQUIRED); + bool not_allowed_supported = config_state.IsRuleSupported(not_allowed_rule); + bool required_supported = config_state.IsRuleSupported(required_rule); + if (not_allowed_supported) { + bool prefer_required = config_state.IsIdentifierRequired() || + (config_state.IsIdentifierRecommended() && + config_state.IsPermissionPossible()); + if (required_supported && prefer_required) { + accumulated_configuration->distinctiveIdentifier = + blink::WebMediaKeySystemConfiguration::Requirement::Required; + config_state.AddRule(required_rule); + DCHECK(config_state.IsIdentifierRequired()); + } else { + accumulated_configuration->distinctiveIdentifier = + blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed; + config_state.AddRule(not_allowed_rule); + } + } else if (required_supported) { accumulated_configuration->distinctiveIdentifier = blink::WebMediaKeySystemConfiguration::Requirement::Required; + config_state.AddRule(required_rule); + } else { + // We should not have passed step 3. + NOTREACHED(); + return CONFIGURATION_NOT_SUPPORTED; } } + // If permission is required but we couldn't enable it, reject the + // configuration. + if (config_state.IsIdentifierRequired() && + accumulated_configuration->distinctiveIdentifier != + blink::WebMediaKeySystemConfiguration::Requirement::Required) { + DVLOG(2) << "Rejecting requested configuration because " + << "distinctiveIdentifier was implicitly required but " + << "not allowed."; + return CONFIGURATION_NOT_SUPPORTED; + } + // 10. If accumulated configuration's persistentState value is "optional", // follow the steps for the first matching condition from the following // list: @@ -298,59 +449,57 @@ static ConfigurationSupport GetSupportedConfiguration( // to "not-allowed". if (accumulated_configuration->persistentState == blink::WebMediaKeySystemConfiguration::Requirement::Optional) { - if (IsPersistentStateRequirementSupported( - key_system, EME_FEATURE_NOT_ALLOWED, is_permission_possible)) { + EmeConfigRule not_allowed_rule = + GetPersistentStateConfigRule(key_system, EME_FEATURE_NOT_ALLOWED); + EmeConfigRule required_rule = + GetPersistentStateConfigRule(key_system, EME_FEATURE_REQUIRED); + // Now that distinctiveIdentifier has been resolved, it is too late to allow + // persistentState to affect the configuration. + bool not_allowed_supported = + config_state.IsRuleSupportedWithCurrentState(not_allowed_rule); + bool required_supported = + config_state.IsRuleSupportedWithCurrentState(required_rule); + if (not_allowed_supported) { accumulated_configuration->persistentState = blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed; - } else { + } else if (required_supported) { accumulated_configuration->persistentState = blink::WebMediaKeySystemConfiguration::Requirement::Required; + } else { + // We should not have passed step 5. + NOTREACHED(); + return CONFIGURATION_NOT_SUPPORTED; } } // 11. If implementation in the configuration specified by the combination of // the values in accumulated configuration is not supported or not allowed // in the origin, return null. - di_requirement = - ConvertRequirement(accumulated_configuration->distinctiveIdentifier); - if (!IsDistinctiveIdentifierRequirementSupported(key_system, di_requirement, - is_permission_granted)) { - if (was_permission_requested) { - // The optional permission was requested and denied. - // TODO(sandersd): Avoid the need for this logic - crbug.com/460616. - DCHECK(candidate.distinctiveIdentifier == - blink::WebMediaKeySystemConfiguration::Requirement::Optional); - DCHECK(di_requirement == EME_FEATURE_REQUIRED); - DCHECK(!is_permission_granted); - accumulated_configuration->distinctiveIdentifier = - blink::WebMediaKeySystemConfiguration::Requirement::NotAllowed; - } else { + // 12. If accumulated configuration's distinctiveIdentifier value is + // "required", [prompt the user for consent]. + if (accumulated_configuration->distinctiveIdentifier == + blink::WebMediaKeySystemConfiguration::Requirement::Required) { + // The caller is responsible for resolving what to do if permission is + // required but has been denied (it should treat it as NOT_SUPPORTED). + if (!config_state.IsPermissionGranted()) return CONFIGURATION_REQUIRES_PERMISSION; - } } - ps_requirement = - ConvertRequirement(accumulated_configuration->persistentState); - if (!IsPersistentStateRequirementSupported(key_system, ps_requirement, - is_permission_granted)) { - DCHECK(!was_permission_requested); // Should have failed at step 5. - return CONFIGURATION_REQUIRES_PERMISSION; - } - - // 12. Return accumulated configuration. - // (As an extra step, we record the available session types so that - // createSession() can be synchronous.) + // 13. Return accumulated configuration. + // + // We also record the available session types so that createSession() can be + // synchronous. std::vector<blink::WebEncryptedMediaSessionType> session_types; session_types.push_back(blink::WebEncryptedMediaSessionType::Temporary); if (accumulated_configuration->persistentState == blink::WebMediaKeySystemConfiguration::Requirement::Required) { - if (IsPersistentLicenseSessionSupported(key_system, - is_permission_granted)) { + if (config_state.IsRuleSupportedWithCurrentState( + GetPersistentLicenseSessionConfigRule(key_system))) { session_types.push_back( blink::WebEncryptedMediaSessionType::PersistentLicense); } - if (IsPersistentReleaseMessageSessionSupported(key_system, - is_permission_granted)) { + if (config_state.IsRuleSupportedWithCurrentState( + GetPersistentReleaseMessageSessionConfigRule(key_system))) { session_types.push_back( blink::WebEncryptedMediaSessionType::PersistentReleaseMessage); } @@ -474,13 +623,17 @@ void WebEncryptedMediaClientImpl::SelectSupportedConfiguration( // new MediaKeySystemAccess object.] blink::WebMediaKeySystemConfiguration accumulated_configuration; ConfigurationSupport supported = GetSupportedConfiguration( - key_system, candidate_configuration, &accumulated_configuration, - was_permission_requested, is_permission_granted); + key_system, candidate_configuration, was_permission_requested, + is_permission_granted, &accumulated_configuration); switch (supported) { case CONFIGURATION_NOT_SUPPORTED: continue; case CONFIGURATION_REQUIRES_PERMISSION: - DCHECK(!was_permission_requested); + if (was_permission_requested) { + DVLOG(2) << "Rejecting requested configuration because " + << "permission was denied."; + continue; + } media_permission_->RequestPermission( MediaPermission::PROTECTED_MEDIA_IDENTIFIER, GURL(request.securityOrigin().toString()), diff --git a/media/test/data/eme_player_js/player_utils.js b/media/test/data/eme_player_js/player_utils.js index d72bef7..86a3e0e 100644 --- a/media/test/data/eme_player_js/player_utils.js +++ b/media/test/data/eme_player_js/player_utils.js @@ -85,8 +85,10 @@ PlayerUtils.registerEMEEventListeners = function(player) { this.registerDefaultEventListeners(player); Utils.timeLog('Setting video media keys: ' + player.testConfig.keySystem); + var persistentState = player.testConfig.sessionToLoad ? "required" + : "optional"; return navigator.requestMediaKeySystemAccess( - player.testConfig.keySystem, [{}]) + player.testConfig.keySystem, [{persistentState: persistentState}]) .then(function(access) { return access.createMediaKeys(); }) .then(function(mediaKeys) { return player.video.setMediaKeys(mediaKeys); |