| // Copyright 2013 The Chromium Authors. All rights reserved. |
| // Use of this source code is governed by a BSD-style license that can be |
| // found in the LICENSE file. |
| |
| #include "content/renderer/media/crypto/key_systems.h" |
| |
| #include <map> |
| #include <string> |
| |
| #include "base/lazy_instance.h" |
| #include "base/logging.h" |
| #include "base/strings/string_util.h" |
| #include "content/public/common/content_client.h" |
| #include "content/public/renderer/content_renderer_client.h" |
| #include "content/public/renderer/key_system_info.h" |
| #include "content/renderer/media/crypto/key_systems_support_uma.h" |
| #include "net/base/mime_util.h" |
| #include "third_party/WebKit/public/platform/WebCString.h" |
| #include "third_party/WebKit/public/platform/WebString.h" |
| |
| #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR. |
| |
| namespace content { |
| |
| const char kClearKeyKeySystem[] = "webkit-org.w3.clearkey"; |
| |
| const char kAudioWebM[] = "audio/webm"; |
| const char kVideoWebM[] = "video/webm"; |
| const char kVorbis[] = "vorbis"; |
| const char kVorbisVP8[] = "vorbis,vp8,vp8.0"; |
| |
| #if defined(USE_PROPRIETARY_CODECS) |
| const char kAudioMp4[] = "audio/mp4"; |
| const char kVideoMp4[] = "video/mp4"; |
| const char kMp4a[] = "mp4a"; |
| const char kMp4aAvc1Avc3[] = "mp4a,avc1,avc3"; |
| #endif // defined(USE_PROPRIETARY_CODECS) |
| |
| #if !defined(GOOGLE_TV) |
| inline std::string KeySystemNameForUMAInternal( |
| const blink::WebString& key_system) { |
| if (key_system == kClearKeyKeySystem) |
| return "ClearKey"; |
| #if defined(WIDEVINE_CDM_AVAILABLE) |
| if (key_system == kWidevineKeySystem) |
| return "Widevine"; |
| #endif // WIDEVINE_CDM_AVAILABLE |
| return "Unknown"; |
| } |
| #else |
| // Declares the function, which is defined in another file. |
| std::string KeySystemNameForUMAInternal(const blink::WebString& key_system); |
| #endif // !defined(GOOGLE_TV) |
| |
| // Convert a WebString to ASCII, falling back on an empty string in the case |
| // of a non-ASCII string. |
| static std::string ToASCIIOrEmpty(const blink::WebString& string) { |
| return IsStringASCII(string) ? UTF16ToASCII(string) : std::string(); |
| } |
| |
| static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) { |
| KeySystemInfo info(kClearKeyKeySystem); |
| |
| info.supported_types.push_back(std::make_pair(kAudioWebM, kVorbis)); |
| info.supported_types.push_back(std::make_pair(kVideoWebM, kVorbisVP8)); |
| #if defined(USE_PROPRIETARY_CODECS) |
| info.supported_types.push_back(std::make_pair(kAudioMp4, kMp4a)); |
| info.supported_types.push_back(std::make_pair(kVideoMp4, kMp4aAvc1Avc3)); |
| #endif // defined(USE_PROPRIETARY_CODECS) |
| |
| info.use_aes_decryptor = true; |
| |
| concrete_key_systems->push_back(info); |
| } |
| |
| class KeySystems { |
| public: |
| static KeySystems& GetInstance(); |
| |
| bool IsConcreteSupportedKeySystem(const std::string& key_system); |
| |
| bool IsSupportedKeySystemWithMediaMimeType( |
| const std::string& mime_type, |
| const std::vector<std::string>& codecs, |
| const std::string& key_system); |
| |
| bool UseAesDecryptor(const std::string& concrete_key_system); |
| |
| #if defined(ENABLE_PEPPER_CDMS) |
| std::string GetPepperType(const std::string& concrete_key_system); |
| #elif defined(OS_ANDROID) |
| std::vector<uint8> GetUUID(const std::string& concrete_key_system); |
| #endif |
| |
| private: |
| void AddConcreteSupportedKeySystems( |
| const std::vector<KeySystemInfo>& concrete_key_systems); |
| |
| void AddConcreteSupportedKeySystem( |
| const std::string& key_system, |
| bool use_aes_decryptor, |
| #if defined(ENABLE_PEPPER_CDMS) |
| const std::string& pepper_type, |
| #elif defined(OS_ANDROID) |
| const std::vector<uint8>& uuid, |
| #endif |
| const std::vector<KeySystemInfo::ContainerCodecsPair>& supported_types, |
| const std::string& parent_key_system); |
| |
| |
| friend struct base::DefaultLazyInstanceTraits<KeySystems>; |
| |
| typedef base::hash_set<std::string> CodecSet; |
| typedef std::map<std::string, CodecSet> MimeTypeMap; |
| |
| struct KeySystemProperties { |
| KeySystemProperties() : use_aes_decryptor(false) {} |
| |
| bool use_aes_decryptor; |
| #if defined(ENABLE_PEPPER_CDMS) |
| std::string pepper_type; |
| #elif defined(OS_ANDROID) |
| std::vector<uint8> uuid; |
| #endif |
| MimeTypeMap types; |
| }; |
| |
| typedef std::map<std::string, KeySystemProperties> KeySystemPropertiesMap; |
| |
| typedef std::map<std::string, std::string> ParentKeySystemMap; |
| |
| KeySystems(); |
| ~KeySystems() {} |
| |
| void AddSupportedType(const std::string& mime_type, |
| const std::string& codecs_list, |
| KeySystemProperties* properties); |
| |
| bool IsSupportedKeySystemWithContainerAndCodec(const std::string& mime_type, |
| const std::string& codec, |
| const std::string& key_system); |
| |
| // Map from key system string to capabilities. |
| KeySystemPropertiesMap concrete_key_system_map_; |
| |
| // Map from parent key system to the concrete key system that should be used |
| // to represent its capabilities. |
| ParentKeySystemMap parent_key_system_map_; |
| |
| KeySystemsSupportUMA key_systems_support_uma_; |
| |
| DISALLOW_COPY_AND_ASSIGN(KeySystems); |
| }; |
| |
| static base::LazyInstance<KeySystems> g_key_systems = LAZY_INSTANCE_INITIALIZER; |
| |
| KeySystems& KeySystems::GetInstance() { |
| return g_key_systems.Get(); |
| } |
| |
| // Because we use a LazyInstance, the key systems info must be populated when |
| // the instance is lazily initiated. |
| KeySystems::KeySystems() { |
| std::vector<KeySystemInfo> key_systems_info; |
| GetContentClient()->renderer()->AddKeySystems(&key_systems_info); |
| // Clear Key is always supported. |
| AddClearKey(&key_systems_info); |
| AddConcreteSupportedKeySystems(key_systems_info); |
| #if defined(WIDEVINE_CDM_AVAILABLE) |
| key_systems_support_uma_.AddKeySystemToReport(kWidevineKeySystem); |
| #endif // defined(WIDEVINE_CDM_AVAILABLE) |
| } |
| |
| void KeySystems::AddConcreteSupportedKeySystems( |
| const std::vector<KeySystemInfo>& concrete_key_systems) { |
| for (size_t i = 0; i < concrete_key_systems.size(); ++i) { |
| const KeySystemInfo& key_system_info = concrete_key_systems[i]; |
| AddConcreteSupportedKeySystem(key_system_info.key_system, |
| key_system_info.use_aes_decryptor, |
| #if defined(ENABLE_PEPPER_CDMS) |
| key_system_info.pepper_type, |
| #elif defined(OS_ANDROID) |
| key_system_info.uuid, |
| #endif |
| key_system_info.supported_types, |
| key_system_info.parent_key_system); |
| } |
| } |
| |
| void KeySystems::AddConcreteSupportedKeySystem( |
| const std::string& concrete_key_system, |
| bool use_aes_decryptor, |
| #if defined(ENABLE_PEPPER_CDMS) |
| const std::string& pepper_type, |
| #elif defined(OS_ANDROID) |
| const std::vector<uint8>& uuid, |
| #endif |
| const std::vector<KeySystemInfo::ContainerCodecsPair>& supported_types, |
| const std::string& parent_key_system) { |
| DCHECK(!IsConcreteSupportedKeySystem(concrete_key_system)) |
| << "Key system '" << concrete_key_system << "' already registered"; |
| DCHECK(parent_key_system_map_.find(concrete_key_system) == |
| parent_key_system_map_.end()) |
| << "'" << concrete_key_system << " is already registered as a parent"; |
| |
| KeySystemProperties properties; |
| properties.use_aes_decryptor = use_aes_decryptor; |
| #if defined(ENABLE_PEPPER_CDMS) |
| DCHECK_EQ(use_aes_decryptor, pepper_type.empty()); |
| properties.pepper_type = pepper_type; |
| #elif defined(OS_ANDROID) |
| DCHECK_EQ(use_aes_decryptor, uuid.empty()); |
| DCHECK(use_aes_decryptor || uuid.size() == 16); |
| properties.uuid = uuid; |
| #endif |
| |
| for (size_t i = 0; i < supported_types.size(); ++i) { |
| const KeySystemInfo::ContainerCodecsPair& pair = supported_types[i]; |
| const std::string& mime_type = pair.first; |
| const std::string& codecs_list = pair.second; |
| AddSupportedType(mime_type, codecs_list, &properties); |
| } |
| |
| concrete_key_system_map_[concrete_key_system] = properties; |
| |
| if (!parent_key_system.empty()) { |
| DCHECK(!IsConcreteSupportedKeySystem(parent_key_system)) |
| << "Parent '" << parent_key_system << "' already registered concrete"; |
| DCHECK(parent_key_system_map_.find(parent_key_system) == |
| parent_key_system_map_.end()) |
| << "Parent '" << parent_key_system << "' already registered"; |
| parent_key_system_map_[parent_key_system] = concrete_key_system; |
| } |
| } |
| |
| void KeySystems::AddSupportedType(const std::string& mime_type, |
| const std::string& codecs_list, |
| KeySystemProperties* properties) { |
| std::vector<std::string> mime_type_codecs; |
| net::ParseCodecString(codecs_list, &mime_type_codecs, false); |
| |
| CodecSet codecs(mime_type_codecs.begin(), mime_type_codecs.end()); |
| |
| MimeTypeMap& mime_types_map = properties->types; |
| // mime_types_map must not be repeated for a given key system. |
| DCHECK(mime_types_map.find(mime_type) == mime_types_map.end()); |
| mime_types_map[mime_type] = codecs; |
| } |
| |
| bool KeySystems::IsConcreteSupportedKeySystem(const std::string& key_system) { |
| return concrete_key_system_map_.find(key_system) != |
| concrete_key_system_map_.end(); |
| } |
| |
| bool KeySystems::IsSupportedKeySystemWithContainerAndCodec( |
| const std::string& mime_type, |
| const std::string& codec, |
| const std::string& key_system) { |
| bool has_type = !mime_type.empty(); |
| DCHECK(has_type || codec.empty()); |
| |
| key_systems_support_uma_.ReportKeySystemQuery(key_system, has_type); |
| |
| KeySystemPropertiesMap::const_iterator key_system_iter = |
| concrete_key_system_map_.find(key_system); |
| if (key_system_iter == concrete_key_system_map_.end()) |
| return false; |
| |
| key_systems_support_uma_.ReportKeySystemSupport(key_system, false); |
| |
| if (mime_type.empty()) |
| return true; |
| |
| const MimeTypeMap& mime_types_map = key_system_iter->second.types; |
| MimeTypeMap::const_iterator mime_iter = mime_types_map.find(mime_type); |
| if (mime_iter == mime_types_map.end()) |
| return false; |
| |
| if (codec.empty()) { |
| key_systems_support_uma_.ReportKeySystemSupport(key_system, true); |
| return true; |
| } |
| |
| const CodecSet& codecs = mime_iter->second; |
| if (codecs.find(codec) == codecs.end()) |
| return false; |
| |
| key_systems_support_uma_.ReportKeySystemSupport(key_system, true); |
| return true; |
| } |
| |
| bool KeySystems::IsSupportedKeySystemWithMediaMimeType( |
| const std::string& mime_type, |
| const std::vector<std::string>& codecs, |
| const std::string& key_system) { |
| // If |key_system| is a parent key_system, use its concrete child. |
| // Otherwise, use |key_system|. |
| std::string concrete_key_system; |
| ParentKeySystemMap::iterator parent_key_system_iter = |
| parent_key_system_map_.find(key_system); |
| if (parent_key_system_iter != parent_key_system_map_.end()) |
| concrete_key_system = parent_key_system_iter->second; |
| else |
| concrete_key_system = key_system; |
| |
| if (codecs.empty()) { |
| return IsSupportedKeySystemWithContainerAndCodec( |
| mime_type, std::string(), concrete_key_system); |
| } |
| |
| for (size_t i = 0; i < codecs.size(); ++i) { |
| if (!IsSupportedKeySystemWithContainerAndCodec( |
| mime_type, codecs[i], concrete_key_system)) { |
| return false; |
| } |
| } |
| |
| return true; |
| } |
| |
| bool KeySystems::UseAesDecryptor(const std::string& concrete_key_system) { |
| KeySystemPropertiesMap::iterator key_system_iter = |
| concrete_key_system_map_.find(concrete_key_system); |
| if (key_system_iter == concrete_key_system_map_.end()) { |
| DLOG(FATAL) << concrete_key_system << " is not a known concrete system"; |
| return false; |
| } |
| |
| return key_system_iter->second.use_aes_decryptor; |
| } |
| |
| #if defined(ENABLE_PEPPER_CDMS) |
| std::string KeySystems::GetPepperType(const std::string& concrete_key_system) { |
| KeySystemPropertiesMap::iterator key_system_iter = |
| concrete_key_system_map_.find(concrete_key_system); |
| if (key_system_iter == concrete_key_system_map_.end()) { |
| DLOG(FATAL) << concrete_key_system << " is not a known concrete system"; |
| return std::string(); |
| } |
| |
| const std::string& type = key_system_iter->second.pepper_type; |
| DLOG_IF(FATAL, type.empty()) << concrete_key_system << " is not Pepper-based"; |
| return type; |
| } |
| #elif defined(OS_ANDROID) |
| std::vector<uint8> KeySystems::GetUUID(const std::string& concrete_key_system) { |
| KeySystemPropertiesMap::iterator key_system_iter = |
| concrete_key_system_map_.find(concrete_key_system); |
| if (key_system_iter == concrete_key_system_map_.end()) { |
| DLOG(FATAL) << concrete_key_system << " is not a known concrete system"; |
| return std::vector<uint8>(); |
| } |
| |
| return key_system_iter->second.uuid; |
| } |
| #endif |
| |
| //------------------------------------------------------------------------------ |
| |
| bool IsConcreteSupportedKeySystem(const blink::WebString& key_system) { |
| return KeySystems::GetInstance().IsConcreteSupportedKeySystem( |
| ToASCIIOrEmpty(key_system)); |
| } |
| |
| bool IsSupportedKeySystemWithMediaMimeType( |
| const std::string& mime_type, |
| const std::vector<std::string>& codecs, |
| const std::string& key_system) { |
| return KeySystems::GetInstance().IsSupportedKeySystemWithMediaMimeType( |
| mime_type, codecs, key_system); |
| } |
| |
| std::string KeySystemNameForUMA(const blink::WebString& key_system) { |
| return KeySystemNameForUMAInternal(key_system); |
| } |
| |
| std::string KeySystemNameForUMA(const std::string& key_system) { |
| return KeySystemNameForUMAInternal(blink::WebString::fromUTF8(key_system)); |
| } |
| |
| bool CanUseAesDecryptor(const std::string& concrete_key_system) { |
| return KeySystems::GetInstance().UseAesDecryptor(concrete_key_system); |
| } |
| |
| #if defined(ENABLE_PEPPER_CDMS) |
| std::string GetPepperType(const std::string& concrete_key_system) { |
| return KeySystems::GetInstance().GetPepperType(concrete_key_system); |
| } |
| #elif defined(OS_ANDROID) |
| std::vector<uint8> GetUUID(const std::string& concrete_key_system) { |
| return KeySystems::GetInstance().GetUUID(concrete_key_system); |
| } |
| #endif |
| |
| } // namespace content |