Home | History | Annotate | Download | only in crypto
      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 <string>
      8 
      9 #include "base/containers/hash_tables.h"
     10 #include "base/lazy_instance.h"
     11 #include "base/logging.h"
     12 #include "base/strings/string_util.h"
     13 #include "base/threading/thread_checker.h"
     14 #include "base/time/time.h"
     15 #include "content/public/common/content_client.h"
     16 #include "content/public/common/eme_codec.h"
     17 #include "content/public/renderer/content_renderer_client.h"
     18 #include "content/public/renderer/key_system_info.h"
     19 #include "content/renderer/media/crypto/key_systems_support_uma.h"
     20 
     21 #if defined(OS_ANDROID)
     22 #include "media/base/android/media_codec_bridge.h"
     23 #endif
     24 
     25 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
     26 
     27 namespace content {
     28 
     29 const char kClearKeyKeySystem[] = "org.w3.clearkey";
     30 const char kPrefixedClearKeyKeySystem[] = "webkit-org.w3.clearkey";
     31 const char kUnsupportedClearKeyKeySystem[] = "unsupported-org.w3.clearkey";
     32 
     33 struct CodecMask {
     34   const char* type;
     35   EmeCodec mask;
     36 };
     37 
     38 // Mapping between container types and the masks of associated codecs.
     39 // Only audio codec can belong to a "audio/*" container. Both audio and video
     40 // codecs can belong to a "video/*" container.
     41 CodecMask kContainerCodecMasks[] = {
     42     {"audio/webm", EME_CODEC_WEBM_AUDIO_ALL},
     43     {"video/webm", EME_CODEC_WEBM_ALL},
     44 #if defined(USE_PROPRIETARY_CODECS)
     45     {"audio/mp4", EME_CODEC_MP4_AUDIO_ALL},
     46     {"video/mp4", EME_CODEC_MP4_ALL}
     47 #endif  // defined(USE_PROPRIETARY_CODECS)
     48 };
     49 
     50 // Mapping between codec types and their masks.
     51 CodecMask kCodecMasks[] = {
     52     {"vorbis", EME_CODEC_WEBM_VORBIS},
     53     {"vp8", EME_CODEC_WEBM_VP8},
     54     {"vp8.0", EME_CODEC_WEBM_VP8},
     55     {"vp9", EME_CODEC_WEBM_VP9},
     56     {"vp9.0", EME_CODEC_WEBM_VP9},
     57 #if defined(USE_PROPRIETARY_CODECS)
     58     {"mp4a", EME_CODEC_MP4_AAC},
     59     {"avc1", EME_CODEC_MP4_AVC1},
     60     {"avc3", EME_CODEC_MP4_AVC1}
     61 #endif  // defined(USE_PROPRIETARY_CODECS)
     62 };
     63 
     64 static void AddClearKey(std::vector<KeySystemInfo>* concrete_key_systems) {
     65   KeySystemInfo info(kClearKeyKeySystem);
     66 
     67   // On Android, Vorbis, VP8, AAC and AVC1 are supported in MediaCodec:
     68   // http://developer.android.com/guide/appendix/media-formats.html
     69   // VP9 support is device dependent.
     70 
     71   info.supported_codecs = EME_CODEC_WEBM_ALL;
     72 
     73 #if defined(OS_ANDROID)
     74   // Temporarily disable VP9 support for Android.
     75   // TODO(xhwang): Use mime_util.h to query VP9 support on Android.
     76   info.supported_codecs &= ~EME_CODEC_WEBM_VP9;
     77 #endif  // defined(OS_ANDROID)
     78 
     79 #if defined(USE_PROPRIETARY_CODECS)
     80   info.supported_codecs |= EME_CODEC_MP4_ALL;
     81 #endif  // defined(USE_PROPRIETARY_CODECS)
     82 
     83   info.use_aes_decryptor = true;
     84 
     85   concrete_key_systems->push_back(info);
     86 }
     87 
     88 class KeySystems {
     89  public:
     90   static KeySystems& GetInstance();
     91 
     92   void UpdateIfNeeded();
     93 
     94   bool IsConcreteSupportedKeySystem(const std::string& key_system);
     95 
     96   bool IsSupportedKeySystemWithMediaMimeType(
     97       const std::string& mime_type,
     98       const std::vector<std::string>& codecs,
     99       const std::string& key_system);
    100 
    101   bool UseAesDecryptor(const std::string& concrete_key_system);
    102 
    103 #if defined(ENABLE_PEPPER_CDMS)
    104   std::string GetPepperType(const std::string& concrete_key_system);
    105 #endif
    106 
    107   void AddContainerMask(const std::string& container, uint32 mask);
    108   void AddCodecMask(const std::string& codec, uint32 mask);
    109 
    110  private:
    111   void UpdateSupportedKeySystems();
    112 
    113   void AddConcreteSupportedKeySystems(
    114       const std::vector<KeySystemInfo>& concrete_key_systems);
    115 
    116   void AddConcreteSupportedKeySystem(
    117       const std::string& key_system,
    118       bool use_aes_decryptor,
    119 #if defined(ENABLE_PEPPER_CDMS)
    120       const std::string& pepper_type,
    121 #endif
    122       SupportedCodecs supported_codecs,
    123       const std::string& parent_key_system);
    124 
    125   friend struct base::DefaultLazyInstanceTraits<KeySystems>;
    126 
    127   struct KeySystemProperties {
    128     KeySystemProperties()
    129         : use_aes_decryptor(false), supported_codecs(EME_CODEC_NONE) {}
    130 
    131     bool use_aes_decryptor;
    132 #if defined(ENABLE_PEPPER_CDMS)
    133     std::string pepper_type;
    134 #endif
    135     SupportedCodecs supported_codecs;
    136   };
    137 
    138   typedef base::hash_map<std::string, KeySystemProperties>
    139       KeySystemPropertiesMap;
    140   typedef base::hash_map<std::string, std::string> ParentKeySystemMap;
    141   typedef base::hash_map<std::string, EmeCodec> CodecMaskMap;
    142 
    143   KeySystems();
    144   ~KeySystems() {}
    145 
    146   // Returns whether a |container| type is supported by checking
    147   // |key_system_supported_codecs|.
    148   // TODO(xhwang): Update this to actually check initDataType support.
    149   bool IsSupportedContainer(const std::string& container,
    150                             SupportedCodecs key_system_supported_codecs) const;
    151 
    152   // Returns true if all |codecs| are supported in |container| by checking
    153   // |key_system_supported_codecs|.
    154   bool IsSupportedContainerAndCodecs(
    155       const std::string& container,
    156       const std::vector<std::string>& codecs,
    157       SupportedCodecs key_system_supported_codecs) const;
    158 
    159   // Map from key system string to capabilities.
    160   KeySystemPropertiesMap concrete_key_system_map_;
    161 
    162   // Map from parent key system to the concrete key system that should be used
    163   // to represent its capabilities.
    164   ParentKeySystemMap parent_key_system_map_;
    165 
    166   KeySystemsSupportUMA key_systems_support_uma_;
    167 
    168   CodecMaskMap container_codec_masks_;
    169   CodecMaskMap codec_masks_;
    170 
    171   bool needs_update_;
    172   base::Time last_update_time_;
    173 
    174   // Makes sure all methods are called from the same thread.
    175   base::ThreadChecker thread_checker_;
    176 
    177   DISALLOW_COPY_AND_ASSIGN(KeySystems);
    178 };
    179 
    180 static base::LazyInstance<KeySystems> g_key_systems = LAZY_INSTANCE_INITIALIZER;
    181 
    182 KeySystems& KeySystems::GetInstance() {
    183   KeySystems& key_systems = g_key_systems.Get();
    184   key_systems.UpdateIfNeeded();
    185   return key_systems;
    186 }
    187 
    188 // Because we use a LazyInstance, the key systems info must be populated when
    189 // the instance is lazily initiated.
    190 KeySystems::KeySystems() : needs_update_(true) {
    191   // Build container and codec masks for quick look up.
    192   for (size_t i = 0; i < arraysize(kContainerCodecMasks); ++i) {
    193     const CodecMask& container_codec_mask = kContainerCodecMasks[i];
    194     DCHECK(container_codec_masks_.find(container_codec_mask.type) ==
    195            container_codec_masks_.end());
    196     container_codec_masks_[container_codec_mask.type] =
    197         container_codec_mask.mask;
    198   }
    199   for (size_t i = 0; i < arraysize(kCodecMasks); ++i) {
    200     const CodecMask& codec_mask = kCodecMasks[i];
    201     DCHECK(codec_masks_.find(codec_mask.type) == codec_masks_.end());
    202     codec_masks_[codec_mask.type] = codec_mask.mask;
    203   }
    204 
    205   UpdateSupportedKeySystems();
    206 
    207 #if defined(WIDEVINE_CDM_AVAILABLE)
    208   key_systems_support_uma_.AddKeySystemToReport(kWidevineKeySystem);
    209 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
    210 }
    211 
    212 void KeySystems::UpdateIfNeeded() {
    213 #if defined(WIDEVINE_CDM_AVAILABLE)
    214   DCHECK(thread_checker_.CalledOnValidThread());
    215   if (!needs_update_)
    216     return;
    217 
    218   // The update could involve a sync IPC to the browser process. Use a minimum
    219   // update interval to avoid unnecessary frequent IPC to the browser.
    220   static const int kMinUpdateIntervalInSeconds = 1;
    221   base::Time now = base::Time::Now();
    222   if (now - last_update_time_ <
    223       base::TimeDelta::FromSeconds(kMinUpdateIntervalInSeconds)) {
    224     return;
    225   }
    226 
    227   UpdateSupportedKeySystems();
    228 #endif
    229 }
    230 
    231 void KeySystems::UpdateSupportedKeySystems() {
    232   DCHECK(thread_checker_.CalledOnValidThread());
    233   DCHECK(needs_update_);
    234   concrete_key_system_map_.clear();
    235   parent_key_system_map_.clear();
    236 
    237   // Build KeySystemInfo.
    238   std::vector<KeySystemInfo> key_systems_info;
    239   GetContentClient()->renderer()->AddKeySystems(&key_systems_info);
    240   // Clear Key is always supported.
    241   AddClearKey(&key_systems_info);
    242 
    243   AddConcreteSupportedKeySystems(key_systems_info);
    244 
    245 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_IS_COMPONENT)
    246   if (IsConcreteSupportedKeySystem(kWidevineKeySystem))
    247     needs_update_ = false;
    248 #endif
    249 
    250   last_update_time_ = base::Time::Now();
    251 }
    252 
    253 void KeySystems::AddConcreteSupportedKeySystems(
    254     const std::vector<KeySystemInfo>& concrete_key_systems) {
    255   DCHECK(thread_checker_.CalledOnValidThread());
    256   DCHECK(concrete_key_system_map_.empty());
    257   DCHECK(parent_key_system_map_.empty());
    258 
    259   for (size_t i = 0; i < concrete_key_systems.size(); ++i) {
    260     const KeySystemInfo& key_system_info = concrete_key_systems[i];
    261     AddConcreteSupportedKeySystem(key_system_info.key_system,
    262                                   key_system_info.use_aes_decryptor,
    263 #if defined(ENABLE_PEPPER_CDMS)
    264                                   key_system_info.pepper_type,
    265 #endif
    266                                   key_system_info.supported_codecs,
    267                                   key_system_info.parent_key_system);
    268   }
    269 }
    270 
    271 void KeySystems::AddConcreteSupportedKeySystem(
    272     const std::string& concrete_key_system,
    273     bool use_aes_decryptor,
    274 #if defined(ENABLE_PEPPER_CDMS)
    275     const std::string& pepper_type,
    276 #endif
    277     SupportedCodecs supported_codecs,
    278     const std::string& parent_key_system) {
    279   DCHECK(thread_checker_.CalledOnValidThread());
    280   DCHECK(!IsConcreteSupportedKeySystem(concrete_key_system))
    281       << "Key system '" << concrete_key_system << "' already registered";
    282   DCHECK(parent_key_system_map_.find(concrete_key_system) ==
    283          parent_key_system_map_.end())
    284       <<  "'" << concrete_key_system << " is already registered as a parent";
    285 
    286   KeySystemProperties properties;
    287   properties.use_aes_decryptor = use_aes_decryptor;
    288 #if defined(ENABLE_PEPPER_CDMS)
    289   DCHECK_EQ(use_aes_decryptor, pepper_type.empty());
    290   properties.pepper_type = pepper_type;
    291 #endif
    292 
    293   properties.supported_codecs = supported_codecs;
    294 
    295   concrete_key_system_map_[concrete_key_system] = properties;
    296 
    297   if (!parent_key_system.empty()) {
    298     DCHECK(!IsConcreteSupportedKeySystem(parent_key_system))
    299         << "Parent '" << parent_key_system << "' already registered concrete";
    300     DCHECK(parent_key_system_map_.find(parent_key_system) ==
    301            parent_key_system_map_.end())
    302         << "Parent '" << parent_key_system << "' already registered";
    303     parent_key_system_map_[parent_key_system] = concrete_key_system;
    304   }
    305 }
    306 
    307 bool KeySystems::IsConcreteSupportedKeySystem(const std::string& key_system) {
    308   DCHECK(thread_checker_.CalledOnValidThread());
    309   return concrete_key_system_map_.find(key_system) !=
    310       concrete_key_system_map_.end();
    311 }
    312 
    313 bool KeySystems::IsSupportedContainer(
    314     const std::string& container,
    315     SupportedCodecs key_system_supported_codecs) const {
    316   DCHECK(thread_checker_.CalledOnValidThread());
    317   DCHECK(!container.empty());
    318 
    319   // When checking container support for EME, "audio/foo" should be treated the
    320   // same as "video/foo". Convert the |container| to achieve this.
    321   // TODO(xhwang): Replace this with real checks against supported initDataTypes
    322   // combined with supported demuxers.
    323   std::string canonical_container = container;
    324   if (container.find("audio/") == 0)
    325     canonical_container.replace(0, 6, "video/");
    326 
    327   CodecMaskMap::const_iterator container_iter =
    328       container_codec_masks_.find(canonical_container);
    329   // Unrecognized container.
    330   if (container_iter == container_codec_masks_.end())
    331     return false;
    332 
    333   EmeCodec container_codec_mask = container_iter->second;
    334   // A container is supported iif at least one codec in that container is
    335   // supported.
    336   return (container_codec_mask & key_system_supported_codecs) != 0;
    337 }
    338 
    339 bool KeySystems::IsSupportedContainerAndCodecs(
    340     const std::string& container,
    341     const std::vector<std::string>& codecs,
    342     SupportedCodecs key_system_supported_codecs) const {
    343   DCHECK(thread_checker_.CalledOnValidThread());
    344   DCHECK(!container.empty());
    345   DCHECK(!codecs.empty());
    346   DCHECK(IsSupportedContainer(container, key_system_supported_codecs));
    347 
    348   CodecMaskMap::const_iterator container_iter =
    349       container_codec_masks_.find(container);
    350   EmeCodec container_codec_mask = container_iter->second;
    351 
    352   for (size_t i = 0; i < codecs.size(); ++i) {
    353     const std::string& codec = codecs[i];
    354     if (codec.empty())
    355       continue;
    356     CodecMaskMap::const_iterator codec_iter = codec_masks_.find(codec);
    357     if (codec_iter == codec_masks_.end())  // Unrecognized codec.
    358       return false;
    359 
    360     EmeCodec codec_mask = codec_iter->second;
    361     if (!(codec_mask & key_system_supported_codecs))  // Unsupported codec.
    362       return false;
    363 
    364     // Unsupported codec/container combination, e.g. "video/webm" and "avc1".
    365     if (!(codec_mask & container_codec_mask))
    366       return false;
    367   }
    368 
    369   return true;
    370 }
    371 
    372 bool KeySystems::IsSupportedKeySystemWithMediaMimeType(
    373     const std::string& mime_type,
    374     const std::vector<std::string>& codecs,
    375     const std::string& key_system) {
    376   DCHECK(thread_checker_.CalledOnValidThread());
    377 
    378   // If |key_system| is a parent key_system, use its concrete child.
    379   // Otherwise, use |key_system|.
    380   std::string concrete_key_system;
    381   ParentKeySystemMap::iterator parent_key_system_iter =
    382       parent_key_system_map_.find(key_system);
    383   if (parent_key_system_iter != parent_key_system_map_.end())
    384     concrete_key_system = parent_key_system_iter->second;
    385   else
    386     concrete_key_system = key_system;
    387 
    388   bool has_type = !mime_type.empty();
    389 
    390   key_systems_support_uma_.ReportKeySystemQuery(key_system, has_type);
    391 
    392   // Check key system support.
    393   KeySystemPropertiesMap::const_iterator key_system_iter =
    394       concrete_key_system_map_.find(concrete_key_system);
    395   if (key_system_iter == concrete_key_system_map_.end())
    396     return false;
    397 
    398   key_systems_support_uma_.ReportKeySystemSupport(key_system, false);
    399 
    400   if (!has_type) {
    401     DCHECK(codecs.empty());
    402     return true;
    403   }
    404 
    405   SupportedCodecs key_system_supported_codecs =
    406       key_system_iter->second.supported_codecs;
    407 
    408   if (!IsSupportedContainer(mime_type, key_system_supported_codecs))
    409     return false;
    410 
    411   if (!codecs.empty() &&
    412       !IsSupportedContainerAndCodecs(
    413           mime_type, codecs, key_system_supported_codecs)) {
    414     return false;
    415   }
    416 
    417   key_systems_support_uma_.ReportKeySystemSupport(key_system, true);
    418   return true;
    419 }
    420 
    421 bool KeySystems::UseAesDecryptor(const std::string& concrete_key_system) {
    422   DCHECK(thread_checker_.CalledOnValidThread());
    423 
    424   KeySystemPropertiesMap::iterator key_system_iter =
    425       concrete_key_system_map_.find(concrete_key_system);
    426   if (key_system_iter == concrete_key_system_map_.end()) {
    427       DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
    428       return false;
    429   }
    430 
    431   return key_system_iter->second.use_aes_decryptor;
    432 }
    433 
    434 #if defined(ENABLE_PEPPER_CDMS)
    435 std::string KeySystems::GetPepperType(const std::string& concrete_key_system) {
    436   DCHECK(thread_checker_.CalledOnValidThread());
    437 
    438   KeySystemPropertiesMap::iterator key_system_iter =
    439       concrete_key_system_map_.find(concrete_key_system);
    440   if (key_system_iter == concrete_key_system_map_.end()) {
    441       DLOG(FATAL) << concrete_key_system << " is not a known concrete system";
    442       return std::string();
    443   }
    444 
    445   const std::string& type = key_system_iter->second.pepper_type;
    446   DLOG_IF(FATAL, type.empty()) << concrete_key_system << " is not Pepper-based";
    447   return type;
    448 }
    449 #endif
    450 
    451 void KeySystems::AddContainerMask(const std::string& container, uint32 mask) {
    452   DCHECK(thread_checker_.CalledOnValidThread());
    453   DCHECK(container_codec_masks_.find(container) ==
    454          container_codec_masks_.end());
    455 
    456   container_codec_masks_[container] = static_cast<EmeCodec>(mask);
    457 }
    458 
    459 void KeySystems::AddCodecMask(const std::string& codec, uint32 mask) {
    460   DCHECK(thread_checker_.CalledOnValidThread());
    461   DCHECK(codec_masks_.find(codec) == codec_masks_.end());
    462 
    463   codec_masks_[codec] = static_cast<EmeCodec>(mask);
    464 }
    465 
    466 //------------------------------------------------------------------------------
    467 
    468 std::string GetUnprefixedKeySystemName(const std::string& key_system) {
    469   if (key_system == kClearKeyKeySystem)
    470     return kUnsupportedClearKeyKeySystem;
    471 
    472   if (key_system == kPrefixedClearKeyKeySystem)
    473     return kClearKeyKeySystem;
    474 
    475   return key_system;
    476 }
    477 
    478 std::string GetPrefixedKeySystemName(const std::string& key_system) {
    479   DCHECK_NE(key_system, kPrefixedClearKeyKeySystem);
    480 
    481   if (key_system == kClearKeyKeySystem)
    482     return kPrefixedClearKeyKeySystem;
    483 
    484   return key_system;
    485 }
    486 
    487 bool IsConcreteSupportedKeySystem(const std::string& key_system) {
    488   return KeySystems::GetInstance().IsConcreteSupportedKeySystem(key_system);
    489 }
    490 
    491 bool IsSupportedKeySystemWithMediaMimeType(
    492     const std::string& mime_type,
    493     const std::vector<std::string>& codecs,
    494     const std::string& key_system) {
    495   return KeySystems::GetInstance().IsSupportedKeySystemWithMediaMimeType(
    496       mime_type, codecs, key_system);
    497 }
    498 
    499 std::string KeySystemNameForUMA(const std::string& key_system) {
    500   if (key_system == kClearKeyKeySystem)
    501     return "ClearKey";
    502 #if defined(WIDEVINE_CDM_AVAILABLE)
    503   if (key_system == kWidevineKeySystem)
    504     return "Widevine";
    505 #endif  // WIDEVINE_CDM_AVAILABLE
    506   return "Unknown";
    507 }
    508 
    509 bool CanUseAesDecryptor(const std::string& concrete_key_system) {
    510   return KeySystems::GetInstance().UseAesDecryptor(concrete_key_system);
    511 }
    512 
    513 #if defined(ENABLE_PEPPER_CDMS)
    514 std::string GetPepperType(const std::string& concrete_key_system) {
    515   return KeySystems::GetInstance().GetPepperType(concrete_key_system);
    516 }
    517 #endif
    518 
    519 // These two functions are for testing purpose only. The declaration in the
    520 // header file is guarded by "#if defined(UNIT_TEST)" so that they can be used
    521 // by tests but not non-test code. However, this .cc file is compiled as part of
    522 // "content" where "UNIT_TEST" is not defined. So we need to specify
    523 // "CONTENT_EXPORT" here again so that they are visible to tests.
    524 
    525 CONTENT_EXPORT void AddContainerMask(const std::string& container,
    526                                      uint32 mask) {
    527   KeySystems::GetInstance().AddContainerMask(container, mask);
    528 }
    529 
    530 CONTENT_EXPORT void AddCodecMask(const std::string& codec, uint32 mask) {
    531   KeySystems::GetInstance().AddCodecMask(codec, mask);
    532 }
    533 
    534 }  // namespace content
    535