key_systems.cc revision d0247b1b59f9c528cb6df88b4f2b9afaf80d181e
1// Copyright 2013 The Chromium Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style license that can be
3// found in the LICENSE file.
4
5#include "content/renderer/media/crypto/key_systems.h"
6
7#include <map>
8
9#include "base/lazy_instance.h"
10#include "base/logging.h"
11#include "base/strings/string_util.h"
12#include "content/public/common/content_client.h"
13#include "content/public/renderer/content_renderer_client.h"
14#include "content/public/renderer/key_system_info.h"
15#include "content/renderer/media/crypto/key_systems_info.h"
16#include "net/base/mime_util.h"
17#include "third_party/WebKit/public/platform/WebCString.h"
18#include "third_party/WebKit/public/platform/WebString.h"
19
20namespace content {
21
22// Convert a WebString to ASCII, falling back on an empty string in the case
23// of a non-ASCII string.
24static std::string ToASCIIOrEmpty(const WebKit::WebString& string) {
25  return IsStringASCII(string) ? UTF16ToASCII(string) : std::string();
26}
27
28static const char kClearKeyKeySystem[] = "webkit-org.w3.clearkey";
29
30static const char kAudioWebM[] = "audio/webm";
31static const char kVideoWebM[] = "video/webm";
32static const char kVorbis[] = "vorbis";
33static const char kVorbisVP8[] = "vorbis,vp8,vp8.0";
34
35static const char kAudioMp4[] = "audio/mp4";
36static const char kVideoMp4[] = "video/mp4";
37static const char kMp4a[] = "mp4a";
38static const char kAvc1[] = "avc1";
39static const char kMp4aAvc1[] = "mp4a,avc1";
40
41static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) {
42  KeySystemInfo info(kClearKeyKeySystem);
43
44  info.supported_types.push_back(std::make_pair(kAudioWebM, kVorbis));
45  info.supported_types.push_back(std::make_pair(kVideoWebM, kVorbisVP8));
46#if defined(USE_PROPRIETARY_CODECS)
47  info.supported_types.push_back(std::make_pair(kAudioMp4, kMp4a));
48  info.supported_types.push_back(std::make_pair(kVideoMp4, kMp4aAvc1));
49#endif  // defined(USE_PROPRIETARY_CODECS)
50
51  info.use_aes_decryptor = true;
52
53  concrete_key_systems->push_back(info);
54}
55
56class KeySystems {
57 public:
58  static KeySystems& GetInstance();
59
60  bool IsConcreteSupportedKeySystem(const std::string& key_system);
61
62  bool IsSupportedKeySystemWithMediaMimeType(
63      const std::string& mime_type,
64      const std::vector<std::string>& codecs,
65      const std::string& key_system);
66
67  bool UseAesDecryptor(const std::string& concrete_key_system);
68
69#if defined(ENABLE_PEPPER_CDMS)
70  std::string GetPepperType(const std::string& concrete_key_system);
71#elif defined(OS_ANDROID)
72  std::vector<uint8> GetUUID(const std::string& concrete_key_system);
73#endif
74
75 private:
76  void AddConcreteSupportedKeySystems(
77      const std::vector<KeySystemInfo>& concrete_key_systems);
78
79  void AddConcreteSupportedKeySystem(
80      const std::string& key_system,
81      bool use_aes_decryptor,
82#if defined(ENABLE_PEPPER_CDMS)
83      const std::string& pepper_type,
84#elif defined(OS_ANDROID)
85      const std::vector<uint8>& uuid,
86#endif
87      const std::vector<KeySystemInfo::ContainerCodecsPair>& supported_types,
88      const std::string& parent_key_system);
89
90
91  friend struct base::DefaultLazyInstanceTraits<KeySystems>;
92
93  typedef base::hash_set<std::string> CodecSet;
94  typedef std::map<std::string, CodecSet> MimeTypeMap;
95
96  struct KeySystemProperties {
97    KeySystemProperties() : use_aes_decryptor(false) {}
98
99    bool use_aes_decryptor;
100#if defined(ENABLE_PEPPER_CDMS)
101    std::string pepper_type;
102#elif defined(OS_ANDROID)
103    std::vector<uint8> uuid;
104#endif
105    MimeTypeMap types;
106  };
107
108  typedef std::map<std::string, KeySystemProperties> KeySystemPropertiesMap;
109
110  typedef std::map<std::string, std::string> ParentKeySystemMap;
111
112  KeySystems();
113  ~KeySystems() {}
114
115  void AddSupportedType(const std::string& mime_type,
116                        const std::string& codecs_list,
117                        KeySystemProperties* properties);
118
119  bool IsSupportedKeySystemWithContainerAndCodec(
120      const std::string& mime_type,
121      const std::string& codec,
122      const std::string& key_system);
123
124  // Map from key system string to capabilities.
125  KeySystemPropertiesMap concrete_key_system_map_;
126
127  // Map from parent key system to the concrete key system that should be used
128  // to represent its capabilities.
129  ParentKeySystemMap parent_key_system_map_;
130
131  DISALLOW_COPY_AND_ASSIGN(KeySystems);
132};
133
134static base::LazyInstance<KeySystems> g_key_systems = LAZY_INSTANCE_INITIALIZER;
135
136KeySystems& KeySystems::GetInstance() {
137  return g_key_systems.Get();
138}
139
140// Because we use a LazyInstance, the key systems info must be populated when
141// the instance is lazily initiated.
142KeySystems::KeySystems() {
143  std::vector<KeySystemInfo> key_systems_info;
144  GetContentClient()->renderer()->AddKeySystems(&key_systems_info);
145  // Clear Key is always supported.
146  AddClearKey(&key_systems_info);
147  AddConcreteSupportedKeySystems(key_systems_info);
148}
149
150void KeySystems::AddConcreteSupportedKeySystems(
151    const std::vector<KeySystemInfo>& concrete_key_systems) {
152  for (size_t i = 0; i < concrete_key_systems.size(); ++i) {
153    const KeySystemInfo& key_system_info = concrete_key_systems[i];
154    AddConcreteSupportedKeySystem(key_system_info.key_system,
155                                  key_system_info.use_aes_decryptor,
156#if defined(ENABLE_PEPPER_CDMS)
157                                  key_system_info.pepper_type,
158#elif defined(OS_ANDROID)
159                                  key_system_info.uuid,
160#endif
161                                  key_system_info.supported_types,
162                                  key_system_info.parent_key_system);
163  }
164}
165
166void KeySystems::AddConcreteSupportedKeySystem(
167    const std::string& concrete_key_system,
168    bool use_aes_decryptor,
169#if defined(ENABLE_PEPPER_CDMS)
170    const std::string& pepper_type,
171#elif defined(OS_ANDROID)
172    const std::vector<uint8>& uuid,
173#endif
174    const std::vector<KeySystemInfo::ContainerCodecsPair>& supported_types,
175    const std::string& parent_key_system) {
176  DCHECK(!IsConcreteSupportedKeySystem(concrete_key_system))
177      << "Key system '" << concrete_key_system << "' already registered";
178  DCHECK(parent_key_system_map_.find(concrete_key_system) ==
179         parent_key_system_map_.end())
180      <<  "'" << concrete_key_system << " is already registered as a parent";
181
182  KeySystemProperties properties;
183  properties.use_aes_decryptor = use_aes_decryptor;
184#if defined(ENABLE_PEPPER_CDMS)
185  DCHECK_EQ(use_aes_decryptor, pepper_type.empty());
186  properties.pepper_type = pepper_type;
187#elif defined(OS_ANDROID)
188  DCHECK_EQ(use_aes_decryptor, uuid.empty());
189  DCHECK(use_aes_decryptor || uuid.size() == 16);
190  properties.uuid = uuid;
191#endif
192
193  for (size_t i = 0; i < supported_types.size(); ++i) {
194    const KeySystemInfo::ContainerCodecsPair& pair = supported_types[i];
195    const std::string& mime_type = pair.first;
196    const std::string& codecs_list = pair.second;
197    AddSupportedType(mime_type, codecs_list, &properties);
198  }
199
200  concrete_key_system_map_[concrete_key_system] = properties;
201
202  if (!parent_key_system.empty()) {
203    DCHECK(!IsConcreteSupportedKeySystem(parent_key_system))
204        << "Parent '" << parent_key_system << "' already registered concrete";
205    DCHECK(parent_key_system_map_.find(parent_key_system) ==
206           parent_key_system_map_.end())
207        << "Parent '" << parent_key_system << "' already registered";
208    parent_key_system_map_[parent_key_system] = concrete_key_system;
209  }
210}
211
212void KeySystems::AddSupportedType(const std::string& mime_type,
213                                  const std::string& codecs_list,
214                                  KeySystemProperties* properties) {
215  std::vector<std::string> mime_type_codecs;
216  net::ParseCodecString(codecs_list, &mime_type_codecs, false);
217
218  CodecSet codecs(mime_type_codecs.begin(), mime_type_codecs.end());
219  // Support the MIME type string alone, without codec(s) specified.
220  codecs.insert(std::string());
221
222  MimeTypeMap& mime_types_map = properties->types;
223  // mime_types_map must not be repeated for a given key system.
224  DCHECK(mime_types_map.find(mime_type) == mime_types_map.end());
225  mime_types_map[mime_type] = codecs;
226}
227
228bool KeySystems::IsConcreteSupportedKeySystem(const std::string& key_system) {
229  return concrete_key_system_map_.find(key_system) !=
230      concrete_key_system_map_.end();
231}
232
233bool KeySystems::IsSupportedKeySystemWithContainerAndCodec(
234    const std::string& mime_type,
235    const std::string& codec,
236    const std::string& key_system) {
237  KeySystemPropertiesMap::const_iterator key_system_iter =
238      concrete_key_system_map_.find(key_system);
239  if (key_system_iter == concrete_key_system_map_.end())
240    return false;
241
242  const MimeTypeMap& mime_types_map = key_system_iter->second.types;
243  MimeTypeMap::const_iterator mime_iter = mime_types_map.find(mime_type);
244  if (mime_iter == mime_types_map.end())
245    return false;
246
247  const CodecSet& codecs = mime_iter->second;
248  return (codecs.find(codec) != codecs.end());
249}
250
251bool KeySystems::IsSupportedKeySystemWithMediaMimeType(
252    const std::string& mime_type,
253    const std::vector<std::string>& codecs,
254    const std::string& key_system) {
255  // If |key_system| is a parent key_system, use its concrete child.
256  // Otherwise, use |key_system|.
257  std::string concrete_key_system;
258  ParentKeySystemMap::iterator parent_key_system_iter =
259      parent_key_system_map_.find(key_system);
260  if (parent_key_system_iter != parent_key_system_map_.end())
261    concrete_key_system = parent_key_system_iter->second;
262  else
263    concrete_key_system = key_system;
264
265  // This method is only used by the canPlaytType() path (not the EME methods),
266  // so we check for suppressed key_systems here.
267  if(IsCanPlayTypeSuppressed(concrete_key_system))
268    return false;
269
270  if (codecs.empty()) {
271    return IsSupportedKeySystemWithContainerAndCodec(
272        mime_type, std::string(), concrete_key_system);
273  }
274
275  for (size_t i = 0; i < codecs.size(); ++i) {
276    if (!IsSupportedKeySystemWithContainerAndCodec(
277            mime_type, codecs[i], concrete_key_system)) {
278      return false;
279    }
280  }
281
282  return true;
283}
284
285bool KeySystems::UseAesDecryptor(const std::string& concrete_key_system) {
286  KeySystemPropertiesMap::iterator key_system_iter =
287      concrete_key_system_map_.find(concrete_key_system);
288  if (key_system_iter == concrete_key_system_map_.end()) {
289      DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
290      return false;
291  }
292
293  return key_system_iter->second.use_aes_decryptor;
294}
295
296#if defined(ENABLE_PEPPER_CDMS)
297std::string KeySystems::GetPepperType(const std::string& concrete_key_system) {
298  KeySystemPropertiesMap::iterator key_system_iter =
299      concrete_key_system_map_.find(concrete_key_system);
300  if (key_system_iter == concrete_key_system_map_.end()) {
301      DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
302      return std::string();
303  }
304
305  const std::string& type = key_system_iter->second.pepper_type;
306  DLOG_IF(FATAL, type.empty()) << concrete_key_system << " is not Pepper-based";
307  return type;
308}
309#elif defined(OS_ANDROID)
310std::vector<uint8> KeySystems::GetUUID(const std::string& concrete_key_system) {
311  KeySystemPropertiesMap::iterator key_system_iter =
312      concrete_key_system_map_.find(concrete_key_system);
313  if (key_system_iter == concrete_key_system_map_.end()) {
314      DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
315      return std::vector<uint8>();
316  }
317
318  return key_system_iter->second.uuid;
319}
320#endif
321
322//------------------------------------------------------------------------------
323
324bool IsConcreteSupportedKeySystem(const WebKit::WebString& key_system) {
325  return KeySystems::GetInstance().IsConcreteSupportedKeySystem(
326      ToASCIIOrEmpty(key_system));
327}
328
329bool IsSupportedKeySystemWithMediaMimeType(
330    const std::string& mime_type,
331    const std::vector<std::string>& codecs,
332    const std::string& key_system) {
333  return KeySystems::GetInstance().IsSupportedKeySystemWithMediaMimeType(
334      mime_type, codecs, key_system);
335}
336
337std::string KeySystemNameForUMA(const WebKit::WebString& key_system) {
338  return KeySystemNameForUMAInternal(key_system);
339}
340
341bool CanUseAesDecryptor(const std::string& concrete_key_system) {
342  return KeySystems::GetInstance().UseAesDecryptor(concrete_key_system);
343}
344
345#if defined(ENABLE_PEPPER_CDMS)
346std::string GetPepperType(const std::string& concrete_key_system) {
347  return KeySystems::GetInstance().GetPepperType(concrete_key_system);
348}
349#elif defined(OS_ANDROID)
350std::vector<uint8> GetUUID(const std::string& concrete_key_system) {
351  return KeySystems::GetInstance().GetUUID(concrete_key_system);
352}
353#endif
354
355}  // namespace content
356