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 |