| OLD | NEW |
| (Empty) |
| 1 // Copyright 2014 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 "media/blink/encrypted_media_player_support.h" | |
| 6 | |
| 7 #include <stddef.h> | |
| 8 | |
| 9 #include "base/bind.h" | |
| 10 #include "base/callback_helpers.h" | |
| 11 #include "base/metrics/histogram.h" | |
| 12 #include "base/numerics/safe_conversions.h" | |
| 13 #include "base/strings/string_number_conversions.h" | |
| 14 #include "base/strings/string_util.h" | |
| 15 #include "base/strings/utf_string_conversions.h" | |
| 16 #include "media/base/bind_to_current_loop.h" | |
| 17 #include "media/base/key_systems.h" | |
| 18 #include "media/blink/webcontentdecryptionmodule_impl.h" | |
| 19 #include "third_party/WebKit/public/platform/URLConversion.h" | |
| 20 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h" | |
| 21 #include "third_party/WebKit/public/platform/WebMediaPlayerEncryptedMediaClient.
h" | |
| 22 #include "third_party/WebKit/public/web/WebDocument.h" | |
| 23 #include "third_party/WebKit/public/web/WebLocalFrame.h" | |
| 24 | |
| 25 using blink::WebMediaPlayer; | |
| 26 using blink::WebMediaPlayerEncryptedMediaClient; | |
| 27 using blink::WebString; | |
| 28 | |
| 29 namespace media { | |
| 30 | |
| 31 #define BIND_TO_RENDER_LOOP(function) \ | |
| 32 (BindToCurrentLoop(base::Bind(function, AsWeakPtr()))) | |
| 33 | |
| 34 #define BIND_TO_RENDER_LOOP1(function, arg1) \ | |
| 35 (BindToCurrentLoop(base::Bind(function, AsWeakPtr(), arg1))) | |
| 36 | |
| 37 // Prefix for histograms related to Encrypted Media Extensions. | |
| 38 static const char* kMediaEme = "Media.EME."; | |
| 39 | |
| 40 // Convert a WebString to ASCII, falling back on an empty string in the case | |
| 41 // of a non-ASCII string. | |
| 42 static std::string ToASCIIOrEmpty(const WebString& string) { | |
| 43 return base::IsStringASCII(string) | |
| 44 ? base::UTF16ToASCII(base::StringPiece16(string)) | |
| 45 : std::string(); | |
| 46 } | |
| 47 | |
| 48 // Helper functions to report media EME related stats to UMA. They follow the | |
| 49 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and | |
| 50 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is | |
| 51 // that UMA_* macros require the names to be constant throughout the process' | |
| 52 // lifetime. | |
| 53 static void EmeUMAHistogramEnumeration(const std::string& key_system, | |
| 54 const std::string& method, | |
| 55 int sample, | |
| 56 int boundary_value) { | |
| 57 base::LinearHistogram::FactoryGet( | |
| 58 kMediaEme + GetKeySystemNameForUMA(key_system) + "." + method, | |
| 59 1, boundary_value, boundary_value + 1, | |
| 60 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
| 61 } | |
| 62 | |
| 63 static void EmeUMAHistogramCounts(const std::string& key_system, | |
| 64 const std::string& method, | |
| 65 int sample) { | |
| 66 // Use the same parameters as UMA_HISTOGRAM_COUNTS. | |
| 67 base::Histogram::FactoryGet( | |
| 68 kMediaEme + GetKeySystemNameForUMA(key_system) + "." + method, | |
| 69 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample); | |
| 70 } | |
| 71 | |
| 72 // Helper enum for reporting generateKeyRequest/addKey histograms. | |
| 73 enum MediaKeyException { | |
| 74 kUnknownResultId, | |
| 75 kSuccess, | |
| 76 kKeySystemNotSupported, | |
| 77 kInvalidPlayerState, | |
| 78 kMaxMediaKeyException | |
| 79 }; | |
| 80 | |
| 81 static MediaKeyException MediaKeyExceptionForUMA( | |
| 82 WebMediaPlayer::MediaKeyException e) { | |
| 83 switch (e) { | |
| 84 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported: | |
| 85 return kKeySystemNotSupported; | |
| 86 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState: | |
| 87 return kInvalidPlayerState; | |
| 88 case WebMediaPlayer::MediaKeyExceptionNoError: | |
| 89 return kSuccess; | |
| 90 default: | |
| 91 return kUnknownResultId; | |
| 92 } | |
| 93 } | |
| 94 | |
| 95 // Helper for converting |key_system| name and exception |e| to a pair of enum | |
| 96 // values from above, for reporting to UMA. | |
| 97 static void ReportMediaKeyExceptionToUMA(const std::string& method, | |
| 98 const std::string& key_system, | |
| 99 WebMediaPlayer::MediaKeyException e) { | |
| 100 MediaKeyException result_id = MediaKeyExceptionForUMA(e); | |
| 101 DCHECK_NE(result_id, kUnknownResultId) << e; | |
| 102 EmeUMAHistogramEnumeration( | |
| 103 key_system, method, result_id, kMaxMediaKeyException); | |
| 104 } | |
| 105 | |
| 106 // Guess the type of |init_data|. This is only used to handle some corner cases | |
| 107 // so we keep it as simple as possible without breaking major use cases. | |
| 108 static EmeInitDataType GuessInitDataType(const unsigned char* init_data, | |
| 109 unsigned init_data_length) { | |
| 110 #if defined(USE_PROPRIETARY_CODECS) | |
| 111 // Most WebM files use KeyId of 16 bytes. CENC init data is always >16 bytes. | |
| 112 if (init_data_length > 16) | |
| 113 return EmeInitDataType::CENC; | |
| 114 #endif | |
| 115 | |
| 116 return EmeInitDataType::WEBM; | |
| 117 } | |
| 118 | |
| 119 EncryptedMediaPlayerSupport::EncryptedMediaPlayerSupport( | |
| 120 CdmFactory* cdm_factory, | |
| 121 WebMediaPlayerEncryptedMediaClient* client, | |
| 122 MediaPermission* media_permission, | |
| 123 const CdmContextReadyCB& cdm_context_ready_cb) | |
| 124 : cdm_factory_(cdm_factory), | |
| 125 client_(client), | |
| 126 media_permission_(media_permission), | |
| 127 init_data_type_(EmeInitDataType::UNKNOWN), | |
| 128 cdm_context_ready_cb_(cdm_context_ready_cb) { | |
| 129 } | |
| 130 | |
| 131 EncryptedMediaPlayerSupport::~EncryptedMediaPlayerSupport() { | |
| 132 } | |
| 133 | |
| 134 WebMediaPlayer::MediaKeyException | |
| 135 EncryptedMediaPlayerSupport::GenerateKeyRequest( | |
| 136 blink::WebLocalFrame* frame, | |
| 137 const WebString& key_system, | |
| 138 const unsigned char* init_data, | |
| 139 unsigned init_data_length) { | |
| 140 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": " | |
| 141 << std::string(reinterpret_cast<const char*>(init_data), | |
| 142 static_cast<size_t>(init_data_length)); | |
| 143 | |
| 144 std::string ascii_key_system = | |
| 145 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); | |
| 146 | |
| 147 WebMediaPlayer::MediaKeyException e = GenerateKeyRequestInternal( | |
| 148 frame, ascii_key_system, init_data, init_data_length); | |
| 149 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e); | |
| 150 return e; | |
| 151 } | |
| 152 | |
| 153 WebMediaPlayer::MediaKeyException | |
| 154 EncryptedMediaPlayerSupport::GenerateKeyRequestInternal( | |
| 155 blink::WebLocalFrame* frame, | |
| 156 const std::string& key_system, | |
| 157 const unsigned char* init_data, | |
| 158 unsigned init_data_length) { | |
| 159 if (!PrefixedIsSupportedConcreteKeySystem(key_system)) | |
| 160 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 161 | |
| 162 // |use_hw_secure_codecs| is only supported on Android, and Android (WMPA) | |
| 163 // does not use EncryptedMediaPlayerSupport. | |
| 164 bool use_hw_secure_codecs = false; | |
| 165 | |
| 166 if (!proxy_decryptor_) { | |
| 167 DCHECK(current_key_system_.empty()); | |
| 168 DCHECK(!cdm_context_ready_cb_.is_null()); | |
| 169 proxy_decryptor_.reset(new ProxyDecryptor( | |
| 170 media_permission_, use_hw_secure_codecs, | |
| 171 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyAdded), | |
| 172 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyError), | |
| 173 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupport::OnKeyMessage))); | |
| 174 | |
| 175 GURL security_origin( | |
| 176 blink::WebStringToGURL(frame->document().securityOrigin().toString())); | |
| 177 proxy_decryptor_->CreateCdm(cdm_factory_, key_system, security_origin, | |
| 178 cdm_context_ready_cb_); | |
| 179 current_key_system_ = key_system; | |
| 180 } | |
| 181 | |
| 182 // We do not support run-time switching between key systems for now. | |
| 183 DCHECK(!current_key_system_.empty()); | |
| 184 if (key_system != current_key_system_) | |
| 185 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 186 | |
| 187 EmeInitDataType init_data_type = init_data_type_; | |
| 188 if (init_data_type == EmeInitDataType::UNKNOWN) | |
| 189 init_data_type = GuessInitDataType(init_data, init_data_length); | |
| 190 | |
| 191 proxy_decryptor_->GenerateKeyRequest(init_data_type, init_data, | |
| 192 init_data_length); | |
| 193 | |
| 194 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 195 } | |
| 196 | |
| 197 WebMediaPlayer::MediaKeyException EncryptedMediaPlayerSupport::AddKey( | |
| 198 const WebString& key_system, | |
| 199 const unsigned char* key, | |
| 200 unsigned key_length, | |
| 201 const unsigned char* init_data, | |
| 202 unsigned init_data_length, | |
| 203 const WebString& session_id) { | |
| 204 DVLOG(1) << "addKey: " << base::string16(key_system) << ": " | |
| 205 << std::string(reinterpret_cast<const char*>(key), | |
| 206 static_cast<size_t>(key_length)) << ", " | |
| 207 << std::string(reinterpret_cast<const char*>(init_data), | |
| 208 static_cast<size_t>(init_data_length)) << " [" | |
| 209 << base::string16(session_id) << "]"; | |
| 210 | |
| 211 std::string ascii_key_system = | |
| 212 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); | |
| 213 std::string ascii_session_id = ToASCIIOrEmpty(session_id); | |
| 214 | |
| 215 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system, | |
| 216 key, | |
| 217 key_length, | |
| 218 init_data, | |
| 219 init_data_length, | |
| 220 ascii_session_id); | |
| 221 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e); | |
| 222 return e; | |
| 223 } | |
| 224 | |
| 225 WebMediaPlayer::MediaKeyException | |
| 226 EncryptedMediaPlayerSupport::AddKeyInternal( | |
| 227 const std::string& key_system, | |
| 228 const unsigned char* key, | |
| 229 unsigned key_length, | |
| 230 const unsigned char* init_data, | |
| 231 unsigned init_data_length, | |
| 232 const std::string& session_id) { | |
| 233 DCHECK(key); | |
| 234 DCHECK_GT(key_length, 0u); | |
| 235 | |
| 236 if (!PrefixedIsSupportedConcreteKeySystem(key_system)) | |
| 237 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 238 | |
| 239 if (current_key_system_.empty() || key_system != current_key_system_) | |
| 240 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 241 | |
| 242 proxy_decryptor_->AddKey( | |
| 243 key, key_length, init_data, init_data_length, session_id); | |
| 244 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 245 } | |
| 246 | |
| 247 WebMediaPlayer::MediaKeyException | |
| 248 EncryptedMediaPlayerSupport::CancelKeyRequest( | |
| 249 const WebString& key_system, | |
| 250 const WebString& session_id) { | |
| 251 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": " | |
| 252 << " [" << base::string16(session_id) << "]"; | |
| 253 | |
| 254 std::string ascii_key_system = | |
| 255 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system)); | |
| 256 std::string ascii_session_id = ToASCIIOrEmpty(session_id); | |
| 257 | |
| 258 WebMediaPlayer::MediaKeyException e = | |
| 259 CancelKeyRequestInternal(ascii_key_system, ascii_session_id); | |
| 260 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e); | |
| 261 return e; | |
| 262 } | |
| 263 | |
| 264 WebMediaPlayer::MediaKeyException | |
| 265 EncryptedMediaPlayerSupport::CancelKeyRequestInternal( | |
| 266 const std::string& key_system, | |
| 267 const std::string& session_id) { | |
| 268 if (!PrefixedIsSupportedConcreteKeySystem(key_system)) | |
| 269 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported; | |
| 270 | |
| 271 if (current_key_system_.empty() || key_system != current_key_system_) | |
| 272 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState; | |
| 273 | |
| 274 proxy_decryptor_->CancelKeyRequest(session_id); | |
| 275 return WebMediaPlayer::MediaKeyExceptionNoError; | |
| 276 } | |
| 277 | |
| 278 void EncryptedMediaPlayerSupport::SetInitDataType( | |
| 279 EmeInitDataType init_data_type) { | |
| 280 DCHECK(init_data_type != EmeInitDataType::UNKNOWN); | |
| 281 DLOG_IF(WARNING, init_data_type_ != EmeInitDataType::UNKNOWN && | |
| 282 init_data_type != init_data_type_) | |
| 283 << "Mixed init data type not supported. The new type is ignored."; | |
| 284 if (init_data_type_ == EmeInitDataType::UNKNOWN) | |
| 285 init_data_type_ = init_data_type; | |
| 286 } | |
| 287 | |
| 288 void EncryptedMediaPlayerSupport::OnKeyAdded(const std::string& session_id) { | |
| 289 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1); | |
| 290 client_->keyAdded( | |
| 291 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), | |
| 292 WebString::fromUTF8(session_id)); | |
| 293 } | |
| 294 | |
| 295 void EncryptedMediaPlayerSupport::OnKeyError(const std::string& session_id, | |
| 296 MediaKeys::KeyError error_code, | |
| 297 uint32_t system_code) { | |
| 298 EmeUMAHistogramEnumeration(current_key_system_, "KeyError", | |
| 299 error_code, MediaKeys::kMaxKeyError); | |
| 300 | |
| 301 uint16_t short_system_code = 0; | |
| 302 if (system_code > std::numeric_limits<uint16_t>::max()) { | |
| 303 LOG(WARNING) << "system_code exceeds unsigned short limit."; | |
| 304 short_system_code = std::numeric_limits<uint16_t>::max(); | |
| 305 } else { | |
| 306 short_system_code = static_cast<uint16_t>(system_code); | |
| 307 } | |
| 308 | |
| 309 client_->keyError( | |
| 310 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), | |
| 311 WebString::fromUTF8(session_id), | |
| 312 static_cast<WebMediaPlayerEncryptedMediaClient::MediaKeyErrorCode>( | |
| 313 error_code), | |
| 314 short_system_code); | |
| 315 } | |
| 316 | |
| 317 void EncryptedMediaPlayerSupport::OnKeyMessage( | |
| 318 const std::string& session_id, | |
| 319 const std::vector<uint8_t>& message, | |
| 320 const GURL& destination_url) { | |
| 321 DCHECK(destination_url.is_empty() || destination_url.is_valid()); | |
| 322 | |
| 323 client_->keyMessage( | |
| 324 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)), | |
| 325 WebString::fromUTF8(session_id), | |
| 326 message.empty() ? NULL : &message[0], | |
| 327 base::saturated_cast<unsigned int>(message.size()), | |
| 328 destination_url); | |
| 329 } | |
| 330 | |
| 331 } // namespace media | |
| OLD | NEW |