blob: 41025337a03223a328b68ffc2fef280965bcebfd [file] [log] [blame]
// 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