Home | History | Annotate | Download | only in media
      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 "chrome/renderer/media/chrome_key_systems.h"
      6 
      7 #include <string>
      8 
      9 #include "base/logging.h"
     10 #include "base/strings/string16.h"
     11 #include "base/strings/string_split.h"
     12 #include "base/strings/utf_string_conversions.h"
     13 #include "chrome/common/render_messages.h"
     14 #include "content/public/renderer/render_thread.h"
     15 
     16 #include "widevine_cdm_version.h" // In SHARED_INTERMEDIATE_DIR.
     17 
     18 // The following must be after widevine_cdm_version.h.
     19 
     20 #if defined(WIDEVINE_CDM_AVAILABLE) && defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
     21 #include <gnu/libc-version.h>
     22 #include "base/version.h"
     23 #endif
     24 
     25 #if defined(OS_ANDROID)
     26 #include "chrome/common/encrypted_media_messages_android.h"
     27 #endif
     28 
     29 using content::KeySystemInfo;
     30 
     31 const char kAudioWebM[] = "audio/webm";
     32 const char kVideoWebM[] = "video/webm";
     33 const char kVorbis[] = "vorbis";
     34 const char kVorbisVP8[] = "vorbis,vp8,vp8.0";
     35 
     36 #if defined(USE_PROPRIETARY_CODECS)
     37 const char kAudioMp4[] = "audio/mp4";
     38 const char kVideoMp4[] = "video/mp4";
     39 const char kMp4a[] = "mp4a";
     40 #if defined(WIDEVINE_CDM_AVAILABLE)
     41 const char kAvc1Avc3[] = "avc1,avc3";
     42 #endif  // WIDEVINE_CDM_AVAILABLE
     43 const char kMp4aAvc1Avc3[] = "mp4a,avc1,avc3";
     44 #endif  // defined(USE_PROPRIETARY_CODECS)
     45 
     46 #if defined(ENABLE_PEPPER_CDMS)
     47 static bool IsPepperCdmRegistered(
     48     const std::string& pepper_type,
     49     std::vector<base::string16>* additional_param_names,
     50     std::vector<base::string16>* additional_param_values) {
     51   bool is_registered = false;
     52   content::RenderThread::Get()->Send(
     53       new ChromeViewHostMsg_IsInternalPluginRegisteredForMimeType(
     54           pepper_type,
     55           &is_registered,
     56           additional_param_names,
     57           additional_param_values));
     58 
     59   return is_registered;
     60 }
     61 
     62 // External Clear Key (used for testing).
     63 static void AddExternalClearKey(
     64     std::vector<KeySystemInfo>* concrete_key_systems) {
     65   static const char kExternalClearKeyKeySystem[] =
     66       "org.chromium.externalclearkey";
     67   static const char kExternalClearKeyDecryptOnlyKeySystem[] =
     68       "org.chromium.externalclearkey.decryptonly";
     69   static const char kExternalClearKeyInitializeFailKeySystem[] =
     70       "org.chromium.externalclearkey.initializefail";
     71   static const char kExternalClearKeyPepperType[] =
     72       "application/x-ppapi-clearkey-cdm";
     73 
     74   std::vector<base::string16> additional_param_names;
     75   std::vector<base::string16> additional_param_values;
     76   if (!IsPepperCdmRegistered(kExternalClearKeyPepperType,
     77                              &additional_param_names,
     78                              &additional_param_values)) {
     79     return;
     80   }
     81 
     82   KeySystemInfo info(kExternalClearKeyKeySystem);
     83 
     84   info.supported_types.push_back(std::make_pair(kAudioWebM, kVorbis));
     85   info.supported_types.push_back(std::make_pair(kVideoWebM, kVorbisVP8));
     86 #if defined(USE_PROPRIETARY_CODECS)
     87   info.supported_types.push_back(std::make_pair(kAudioMp4, kMp4a));
     88   info.supported_types.push_back(std::make_pair(kVideoMp4, kMp4aAvc1Avc3));
     89 #endif  // defined(USE_PROPRIETARY_CODECS)
     90   info.pepper_type = kExternalClearKeyPepperType;
     91 
     92   concrete_key_systems->push_back(info);
     93 
     94   // A key system that Chrome thinks is supported by ClearKeyCdm, but actually
     95   // will be refused by ClearKeyCdm. This is to test the CDM initialization
     96   // failure case.
     97   info.key_system = kExternalClearKeyInitializeFailKeySystem;
     98   concrete_key_systems->push_back(info);
     99 
    100   // Add support of decrypt-only mode in ClearKeyCdm.
    101   info.key_system = kExternalClearKeyDecryptOnlyKeySystem;
    102   concrete_key_systems->push_back(info);
    103 }
    104 #endif  // defined(ENABLE_PEPPER_CDMS)
    105 
    106 
    107 #if defined(WIDEVINE_CDM_AVAILABLE)
    108 enum WidevineCdmType {
    109   WIDEVINE,
    110   WIDEVINE_HR,
    111 #if defined(OS_ANDROID)
    112   WIDEVINE_HR_NON_COMPOSITING,
    113 #endif
    114 };
    115 
    116 // Defines bitmask values used to specify supported codecs.
    117 // Each value represents a codec within a specific container.
    118 // The mask values are stored in a SupportedCodecs.
    119 typedef uint32 SupportedCodecs;
    120 enum SupportedCodecMasks {
    121   NO_CODECS = 0,
    122   WEBM_VP8_AND_VORBIS = 1 << 0,
    123 #if defined(USE_PROPRIETARY_CODECS)
    124   MP4_AAC = 1 << 1,
    125   MP4_AVC1 = 1 << 2,
    126   MP4_CODECS = (MP4_AAC | MP4_AVC1),
    127 #endif  // defined(USE_PROPRIETARY_CODECS)
    128 };
    129 
    130 #if defined(OS_ANDROID)
    131 #define COMPILE_ASSERT_MATCHING_ENUM(name) \
    132   COMPILE_ASSERT(static_cast<int>(name) == \
    133                  static_cast<int>(android::name), \
    134                  mismatching_enums)
    135 COMPILE_ASSERT_MATCHING_ENUM(WEBM_VP8_AND_VORBIS);
    136 COMPILE_ASSERT_MATCHING_ENUM(MP4_AAC);
    137 COMPILE_ASSERT_MATCHING_ENUM(MP4_AVC1);
    138 #undef COMPILE_ASSERT_MATCHING_ENUM
    139 
    140 static const uint8 kWidevineUuid[16] = {
    141     0xED, 0xEF, 0x8B, 0xA9, 0x79, 0xD6, 0x4A, 0xCE,
    142     0xA3, 0xC8, 0x27, 0xDC, 0xD5, 0x1D, 0x21, 0xED };
    143 #else
    144 static bool IsWidevineHrSupported() {
    145   // TODO(jrummell): Need to call CheckPlatformState() but it is
    146   // asynchronous, and needs to be done in the browser.
    147   return false;
    148 }
    149 #endif
    150 
    151 // Return |name|'s parent key system.
    152 static std::string GetDirectParentName(std::string name) {
    153   int last_period = name.find_last_of('.');
    154   DCHECK_GT(last_period, 0);
    155   return name.substr(0, last_period);
    156 }
    157 
    158 static void AddWidevineWithCodecs(
    159     WidevineCdmType widevine_cdm_type,
    160     SupportedCodecs supported_codecs,
    161     std::vector<KeySystemInfo>* concrete_key_systems) {
    162 
    163   KeySystemInfo info(kWidevineKeySystem);
    164 
    165   switch (widevine_cdm_type) {
    166     case WIDEVINE:
    167       // For standard Widevine, add parent name.
    168       info.parent_key_system = GetDirectParentName(kWidevineKeySystem);
    169       break;
    170     case WIDEVINE_HR:
    171       info.key_system.append(".hr");
    172       break;
    173 #if defined(OS_ANDROID)
    174     case WIDEVINE_HR_NON_COMPOSITING:
    175       info.key_system.append(".hrnoncompositing");
    176       break;
    177 #endif
    178     default:
    179       NOTREACHED();
    180   }
    181 
    182   if (supported_codecs & WEBM_VP8_AND_VORBIS) {
    183     info.supported_types.push_back(std::make_pair(kAudioWebM, kVorbis));
    184     info.supported_types.push_back(std::make_pair(kVideoWebM, kVorbisVP8));
    185   }
    186 
    187 #if defined(USE_PROPRIETARY_CODECS)
    188   if (supported_codecs & MP4_CODECS) {
    189     // MP4 container is supported for audio and video if any codec is supported.
    190     bool is_aac_supported = (supported_codecs & MP4_AAC) != NO_CODECS;
    191     bool is_avc1_supported = (supported_codecs & MP4_AVC1) != NO_CODECS;
    192     const char* video_codecs = is_avc1_supported ?
    193                                (is_aac_supported ? kMp4aAvc1Avc3 : kAvc1Avc3) :
    194                                "";
    195     const char* audio_codecs = is_aac_supported ? kMp4a : "";
    196 
    197     info.supported_types.push_back(std::make_pair(kAudioMp4, audio_codecs));
    198     info.supported_types.push_back(std::make_pair(kVideoMp4, video_codecs));
    199   }
    200 #endif  // defined(USE_PROPRIETARY_CODECS)
    201 
    202 #if defined(ENABLE_PEPPER_CDMS)
    203   info.pepper_type = kWidevineCdmPluginMimeType;
    204 #elif defined(OS_ANDROID)
    205   info.uuid.assign(kWidevineUuid, kWidevineUuid + arraysize(kWidevineUuid));
    206 #endif  // defined(ENABLE_PEPPER_CDMS)
    207 
    208   concrete_key_systems->push_back(info);
    209 }
    210 
    211 #if defined(ENABLE_PEPPER_CDMS)
    212 // When the adapter is registered, a name-value pair is inserted in
    213 // additional_param_* that lists the supported codecs. The name is "codecs" and
    214 // the value is a comma-delimited list of codecs.
    215 // This function finds "codecs" and parses the value into the vector |codecs|.
    216 // Converts the codec strings to UTF-8 since we only expect ASCII strings and
    217 // this simplifies the rest of the code in this file.
    218 void GetSupportedCodecs(
    219     const std::vector<base::string16>& additional_param_names,
    220     const std::vector<base::string16>& additional_param_values,
    221     std::vector<std::string>* codecs) {
    222   DCHECK(codecs->empty());
    223   DCHECK_EQ(additional_param_names.size(), additional_param_values.size());
    224   for (size_t i = 0; i < additional_param_names.size(); ++i) {
    225     if (additional_param_names[i] ==
    226         base::ASCIIToUTF16(kCdmSupportedCodecsParamName)) {
    227       const base::string16& codecs_string16 = additional_param_values[i];
    228       std::string codecs_string;
    229       if (!base::UTF16ToUTF8(codecs_string16.c_str(),
    230                              codecs_string16.length(),
    231                              &codecs_string)) {
    232         DLOG(WARNING) << "Non-UTF-8 codecs string.";
    233         // Continue using the best effort conversion.
    234       }
    235       base::SplitString(codecs_string,
    236                         kCdmSupportedCodecsValueDelimiter,
    237                         codecs);
    238       break;
    239     }
    240   }
    241 }
    242 
    243 static void AddPepperBasedWidevine(
    244     std::vector<KeySystemInfo>* concrete_key_systems) {
    245 #if defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
    246   Version glibc_version(gnu_get_libc_version());
    247   DCHECK(glibc_version.IsValid());
    248   if (glibc_version.IsOlderThan(WIDEVINE_CDM_MIN_GLIBC_VERSION))
    249     return;
    250 #endif  // defined(WIDEVINE_CDM_MIN_GLIBC_VERSION)
    251 
    252   std::vector<base::string16> additional_param_names;
    253   std::vector<base::string16> additional_param_values;
    254   if (!IsPepperCdmRegistered(kWidevineCdmPluginMimeType,
    255                              &additional_param_names,
    256                              &additional_param_values)) {
    257     DVLOG(1) << "Widevine CDM is not currently available.";
    258     return;
    259   }
    260 
    261   std::vector<std::string> codecs;
    262   GetSupportedCodecs(additional_param_names, additional_param_values, &codecs);
    263 
    264   SupportedCodecs supported_codecs = NO_CODECS;
    265   for (size_t i = 0; i < codecs.size(); ++i) {
    266     // TODO(ddorwin): Break up VP8 and Vorbis. For now, "vp8" implies both.
    267     if (codecs[i] == kCdmSupportedCodecVp8)
    268       supported_codecs |= WEBM_VP8_AND_VORBIS;
    269 #if defined(USE_PROPRIETARY_CODECS)
    270     if (codecs[i] == kCdmSupportedCodecAac)
    271       supported_codecs |= MP4_AAC;
    272     if (codecs[i] == kCdmSupportedCodecAvc1)
    273       supported_codecs |= MP4_AVC1;
    274 #endif  // defined(USE_PROPRIETARY_CODECS)
    275   }
    276 
    277   AddWidevineWithCodecs(WIDEVINE, supported_codecs, concrete_key_systems);
    278 
    279   if (IsWidevineHrSupported())
    280     AddWidevineWithCodecs(WIDEVINE_HR, supported_codecs, concrete_key_systems);
    281 }
    282 #elif defined(OS_ANDROID)
    283 static void AddAndroidWidevine(
    284     std::vector<KeySystemInfo>* concrete_key_systems) {
    285   SupportedKeySystemRequest request;
    286   SupportedKeySystemResponse response;
    287 
    288   request.uuid.insert(request.uuid.begin(), kWidevineUuid,
    289                       kWidevineUuid + arraysize(kWidevineUuid));
    290 #if defined(USE_PROPRIETARY_CODECS)
    291   request.codecs = static_cast<android::SupportedCodecs>(
    292       android::MP4_AAC | android::MP4_AVC1);
    293 #endif  // defined(USE_PROPRIETARY_CODECS)
    294   content::RenderThread::Get()->Send(
    295       new ChromeViewHostMsg_GetSupportedKeySystems(request, &response));
    296   DCHECK_EQ(response.compositing_codecs >> 3, 0) << "unrecognized codec";
    297   DCHECK_EQ(response.non_compositing_codecs >> 3, 0) << "unrecognized codec";
    298   if (response.compositing_codecs != android::NO_SUPPORTED_CODECS) {
    299     AddWidevineWithCodecs(
    300         WIDEVINE,
    301         static_cast<SupportedCodecs>(response.compositing_codecs),
    302         concrete_key_systems);
    303   }
    304 
    305   if (response.non_compositing_codecs != android::NO_SUPPORTED_CODECS) {
    306     AddWidevineWithCodecs(
    307         WIDEVINE_HR_NON_COMPOSITING,
    308         static_cast<SupportedCodecs>(response.non_compositing_codecs),
    309         concrete_key_systems);
    310   }
    311 }
    312 #endif  // defined(ENABLE_PEPPER_CDMS)
    313 #endif  // defined(WIDEVINE_CDM_AVAILABLE)
    314 
    315 void AddChromeKeySystems(std::vector<KeySystemInfo>* key_systems_info) {
    316 #if defined(ENABLE_PEPPER_CDMS)
    317   AddExternalClearKey(key_systems_info);
    318 #endif
    319 
    320 #if defined(WIDEVINE_CDM_AVAILABLE)
    321 #if defined(ENABLE_PEPPER_CDMS)
    322   AddPepperBasedWidevine(key_systems_info);
    323 #elif defined(OS_ANDROID)
    324   AddAndroidWidevine(key_systems_info);
    325 #endif
    326 #endif
    327 }
    328