普通文本  |  396行  |  13.45 KB

// 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