Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(43)

Side by Side Diff: content/renderer/media/crypto/encrypted_media_player_support_impl.cc

Issue 501473003: Move EME code out of WebMediaPlayerImpl. (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: Address CR comments Created 6 years, 4 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 "content/renderer/media/crypto/encrypted_media_player_support_impl.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/callback_helpers.h"
11 #include "base/metrics/histogram.h"
12 #include "base/strings/string_number_conversions.h"
13 #include "base/strings/string_util.h"
14 #include "base/strings/utf_string_conversions.h"
15 #include "content/renderer/media/crypto/key_systems.h"
16 #include "content/renderer/media/webcontentdecryptionmodule_impl.h"
17 #include "content/renderer/pepper/pepper_webplugin_impl.h"
18 #include "media/base/bind_to_current_loop.h"
19 #include "third_party/WebKit/public/platform/WebContentDecryptionModule.h"
20 #include "third_party/WebKit/public/platform/WebContentDecryptionModuleResult.h"
21 #include "third_party/WebKit/public/platform/WebMediaPlayerClient.h"
22 #include "third_party/WebKit/public/web/WebDocument.h"
23 #include "third_party/WebKit/public/web/WebLocalFrame.h"
24 #include "third_party/WebKit/public/web/WebRuntimeFeatures.h"
25
26 #if defined(ENABLE_PEPPER_CDMS)
27 #include "content/renderer/media/crypto/pepper_cdm_wrapper_impl.h"
28 #endif
29
30 using blink::WebMediaPlayer;
31 using blink::WebMediaPlayerClient;
32 using blink::WebString;
33
34 namespace content {
35
36 #define BIND_TO_RENDER_LOOP(function) \
37 (media::BindToCurrentLoop(base::Bind(function, AsWeakPtr())))
38
39 #define BIND_TO_RENDER_LOOP1(function, arg1) \
40 (media::BindToCurrentLoop(base::Bind(function, AsWeakPtr(), arg1)))
41
42
43 // Prefix for histograms related to Encrypted Media Extensions.
44 static const char* kMediaEme = "Media.EME.";
45
46 // Used for calls to decryptor_ready_cb where the result can be ignored.
47 static void DoNothing(bool success) {
48 }
49
50 // Convert a WebString to ASCII, falling back on an empty string in the case
51 // of a non-ASCII string.
52 static std::string ToASCIIOrEmpty(const WebString& string) {
53 return base::IsStringASCII(string) ? base::UTF16ToASCII(string)
54 : std::string();
55 }
56
57 // Helper functions to report media EME related stats to UMA. They follow the
58 // convention of more commonly used macros UMA_HISTOGRAM_ENUMERATION and
59 // UMA_HISTOGRAM_COUNTS. The reason that we cannot use those macros directly is
60 // that UMA_* macros require the names to be constant throughout the process'
61 // lifetime.
62 static void EmeUMAHistogramEnumeration(const std::string& key_system,
63 const std::string& method,
64 int sample,
65 int boundary_value) {
66 base::LinearHistogram::FactoryGet(
67 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
68 1, boundary_value, boundary_value + 1,
69 base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
70 }
71
72 static void EmeUMAHistogramCounts(const std::string& key_system,
73 const std::string& method,
74 int sample) {
75 // Use the same parameters as UMA_HISTOGRAM_COUNTS.
76 base::Histogram::FactoryGet(
77 kMediaEme + KeySystemNameForUMA(key_system) + "." + method,
78 1, 1000000, 50, base::Histogram::kUmaTargetedHistogramFlag)->Add(sample);
79 }
80
81 // Helper enum for reporting generateKeyRequest/addKey histograms.
82 enum MediaKeyException {
83 kUnknownResultId,
84 kSuccess,
85 kKeySystemNotSupported,
86 kInvalidPlayerState,
87 kMaxMediaKeyException
88 };
89
90 static MediaKeyException MediaKeyExceptionForUMA(
91 WebMediaPlayer::MediaKeyException e) {
92 switch (e) {
93 case WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported:
94 return kKeySystemNotSupported;
95 case WebMediaPlayer::MediaKeyExceptionInvalidPlayerState:
96 return kInvalidPlayerState;
97 case WebMediaPlayer::MediaKeyExceptionNoError:
98 return kSuccess;
99 default:
100 return kUnknownResultId;
101 }
102 }
103
104 // Helper for converting |key_system| name and exception |e| to a pair of enum
105 // values from above, for reporting to UMA.
106 static void ReportMediaKeyExceptionToUMA(const std::string& method,
107 const std::string& key_system,
108 WebMediaPlayer::MediaKeyException e) {
109 MediaKeyException result_id = MediaKeyExceptionForUMA(e);
110 DCHECK_NE(result_id, kUnknownResultId) << e;
111 EmeUMAHistogramEnumeration(
112 key_system, method, result_id, kMaxMediaKeyException);
113 }
114
115 // Guess the type of |init_data|. This is only used to handle some corner cases
116 // so we keep it as simple as possible without breaking major use cases.
117 static std::string GuessInitDataType(const unsigned char* init_data,
118 unsigned init_data_length) {
119 // Most WebM files use KeyId of 16 bytes. MP4 init data are always >16 bytes.
120 if (init_data_length == 16)
121 return "video/webm";
122
123 return "video/mp4";
124 }
125
126 scoped_ptr<EncryptedMediaPlayerSupport> EncryptedMediaPlayerSupport::create(
127 blink::WebMediaPlayerClient* client) {
128 return scoped_ptr<EncryptedMediaPlayerSupport>(
129 new EncryptedMediaPlayerSupportImpl(client));
130 }
131
132 EncryptedMediaPlayerSupportImpl::EncryptedMediaPlayerSupportImpl(
133 blink::WebMediaPlayerClient* client)
134 : client_(client),
135 web_cdm_(NULL) {
136 }
137
138 EncryptedMediaPlayerSupportImpl::~EncryptedMediaPlayerSupportImpl() {
139 }
140
141 WebMediaPlayer::MediaKeyException
142 EncryptedMediaPlayerSupportImpl::GenerateKeyRequest(
143 blink::WebLocalFrame* frame,
144 const WebString& key_system,
145 const unsigned char* init_data,
146 unsigned init_data_length) {
147 DVLOG(1) << "generateKeyRequest: " << base::string16(key_system) << ": "
148 << std::string(reinterpret_cast<const char*>(init_data),
149 static_cast<size_t>(init_data_length));
150
151 std::string ascii_key_system =
152 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
153
154 WebMediaPlayer::MediaKeyException e =
155 GenerateKeyRequestInternal(frame, ascii_key_system, init_data,
156 init_data_length);
157 ReportMediaKeyExceptionToUMA("generateKeyRequest", ascii_key_system, e);
158 return e;
159 }
160
161
162 WebMediaPlayer::MediaKeyException
163 EncryptedMediaPlayerSupportImpl::GenerateKeyRequestInternal(
164 blink::WebLocalFrame* frame,
165 const std::string& key_system,
166 const unsigned char* init_data,
167 unsigned init_data_length) {
168 if (!IsConcreteSupportedKeySystem(key_system))
169 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
170
171 // We do not support run-time switching between key systems for now.
172 if (current_key_system_.empty()) {
173 if (!proxy_decryptor_) {
174 proxy_decryptor_.reset(new ProxyDecryptor(
175 #if defined(ENABLE_PEPPER_CDMS)
176 // Create() must be called synchronously as |frame| may not be
177 // valid afterwards.
178 base::Bind(&PepperCdmWrapperImpl::Create, frame),
179 #elif defined(ENABLE_BROWSER_CDMS)
180 #error Browser side CDM in WMPI for prefixed EME API not supported yet.
181 #endif
182 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnKeyAdded),
183 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnKeyError),
184 BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnKeyMessage)));
185 }
186
187 GURL security_origin(frame->document().securityOrigin().toString());
188 if (!proxy_decryptor_->InitializeCDM(key_system, security_origin))
189 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
190
191 if (proxy_decryptor_ && !decryptor_ready_cb_.is_null()) {
192 base::ResetAndReturn(&decryptor_ready_cb_)
193 .Run(proxy_decryptor_->GetDecryptor(), base::Bind(DoNothing));
194 }
195
196 current_key_system_ = key_system;
197 } else if (key_system != current_key_system_) {
198 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
199 }
200
201 std::string init_data_type = init_data_type_;
202 if (init_data_type.empty())
203 init_data_type = GuessInitDataType(init_data, init_data_length);
204
205 // TODO(xhwang): We assume all streams are from the same container (thus have
206 // the same "type") for now. In the future, the "type" should be passed down
207 // from the application.
208 if (!proxy_decryptor_->GenerateKeyRequest(
209 init_data_type, init_data, init_data_length)) {
210 current_key_system_.clear();
211 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
212 }
213
214 return WebMediaPlayer::MediaKeyExceptionNoError;
215 }
216
217 WebMediaPlayer::MediaKeyException EncryptedMediaPlayerSupportImpl::AddKey(
218 const WebString& key_system,
219 const unsigned char* key,
220 unsigned key_length,
221 const unsigned char* init_data,
222 unsigned init_data_length,
223 const WebString& session_id) {
224 DVLOG(1) << "addKey: " << base::string16(key_system) << ": "
225 << std::string(reinterpret_cast<const char*>(key),
226 static_cast<size_t>(key_length)) << ", "
227 << std::string(reinterpret_cast<const char*>(init_data),
228 static_cast<size_t>(init_data_length)) << " ["
229 << base::string16(session_id) << "]";
230
231 std::string ascii_key_system =
232 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
233 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
234
235 WebMediaPlayer::MediaKeyException e = AddKeyInternal(ascii_key_system,
236 key,
237 key_length,
238 init_data,
239 init_data_length,
240 ascii_session_id);
241 ReportMediaKeyExceptionToUMA("addKey", ascii_key_system, e);
242 return e;
243 }
244
245 WebMediaPlayer::MediaKeyException
246 EncryptedMediaPlayerSupportImpl::AddKeyInternal(
247 const std::string& key_system,
248 const unsigned char* key,
249 unsigned key_length,
250 const unsigned char* init_data,
251 unsigned init_data_length,
252 const std::string& session_id) {
253 DCHECK(key);
254 DCHECK_GT(key_length, 0u);
255
256 if (!IsConcreteSupportedKeySystem(key_system))
257 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
258
259 if (current_key_system_.empty() || key_system != current_key_system_)
260 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
261
262 proxy_decryptor_->AddKey(
263 key, key_length, init_data, init_data_length, session_id);
264 return WebMediaPlayer::MediaKeyExceptionNoError;
265 }
266
267 WebMediaPlayer::MediaKeyException
268 EncryptedMediaPlayerSupportImpl::CancelKeyRequest(
269 const WebString& key_system,
270 const WebString& session_id) {
271 DVLOG(1) << "cancelKeyRequest: " << base::string16(key_system) << ": "
272 << " [" << base::string16(session_id) << "]";
273
274 std::string ascii_key_system =
275 GetUnprefixedKeySystemName(ToASCIIOrEmpty(key_system));
276 std::string ascii_session_id = ToASCIIOrEmpty(session_id);
277
278 WebMediaPlayer::MediaKeyException e =
279 CancelKeyRequestInternal(ascii_key_system, ascii_session_id);
280 ReportMediaKeyExceptionToUMA("cancelKeyRequest", ascii_key_system, e);
281 return e;
282 }
283
284 WebMediaPlayer::MediaKeyException
285 EncryptedMediaPlayerSupportImpl::CancelKeyRequestInternal(
286 const std::string& key_system,
287 const std::string& session_id) {
288 if (!IsConcreteSupportedKeySystem(key_system))
289 return WebMediaPlayer::MediaKeyExceptionKeySystemNotSupported;
290
291 if (current_key_system_.empty() || key_system != current_key_system_)
292 return WebMediaPlayer::MediaKeyExceptionInvalidPlayerState;
293
294 proxy_decryptor_->CancelKeyRequest(session_id);
295 return WebMediaPlayer::MediaKeyExceptionNoError;
296 }
297
298 void EncryptedMediaPlayerSupportImpl::SetContentDecryptionModule(
299 blink::WebContentDecryptionModule* cdm) {
300 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
301 if (!cdm)
302 return;
303
304 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
305
306 if (web_cdm_ && !decryptor_ready_cb_.is_null())
307 base::ResetAndReturn(&decryptor_ready_cb_)
308 .Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
309 }
310
311 void EncryptedMediaPlayerSupportImpl::SetContentDecryptionModule(
312 blink::WebContentDecryptionModule* cdm,
313 blink::WebContentDecryptionModuleResult result) {
314 // TODO(xhwang): Support setMediaKeys(0) if necessary: http://crbug.com/330324
315 if (!cdm) {
316 result.completeWithError(
317 blink::WebContentDecryptionModuleExceptionNotSupportedError,
318 0,
319 "Null MediaKeys object is not supported.");
320 return;
321 }
322
323 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
324
325 if (web_cdm_ && !decryptor_ready_cb_.is_null()) {
326 base::ResetAndReturn(&decryptor_ready_cb_)
327 .Run(web_cdm_->GetDecryptor(), BIND_TO_RENDER_LOOP1(
328 &EncryptedMediaPlayerSupportImpl::ContentDecryptionModuleAttached,
329 result));
330 } else {
331 // No pipeline/decoder connected, so resolve the promise. When something
332 // is connected, setting the CDM will happen in SetDecryptorReadyCB().
333 ContentDecryptionModuleAttached(result, true);
334 }
335 }
336
337 void EncryptedMediaPlayerSupportImpl::SetContentDecryptionModuleSync(
338 blink::WebContentDecryptionModule* cdm) {
339 // Used when loading media and no pipeline/decoder attached yet.
340 DCHECK(decryptor_ready_cb_.is_null());
341
342 web_cdm_ = ToWebContentDecryptionModuleImpl(cdm);
343 }
344
345 void EncryptedMediaPlayerSupportImpl::ContentDecryptionModuleAttached(
346 blink::WebContentDecryptionModuleResult result,
347 bool success) {
348 if (success) {
349 result.complete();
350 return;
351 }
352
353 result.completeWithError(
354 blink::WebContentDecryptionModuleExceptionNotSupportedError,
355 0,
356 "Unable to set MediaKeys object");
357 }
358
359 media::SetDecryptorReadyCB
360 EncryptedMediaPlayerSupportImpl::CreateSetDecryptorReadyCB() {
361 return BIND_TO_RENDER_LOOP(
362 &EncryptedMediaPlayerSupportImpl::SetDecryptorReadyCB);
363 }
364
365 media::Demuxer::NeedKeyCB
366 EncryptedMediaPlayerSupportImpl::CreateNeedKeyCB() {
367 return BIND_TO_RENDER_LOOP(&EncryptedMediaPlayerSupportImpl::OnNeedKey);
368 }
369
370 void EncryptedMediaPlayerSupportImpl::OnPipelineDecryptError() {
371 EmeUMAHistogramCounts(current_key_system_, "DecryptError", 1);
372 }
373
374 void EncryptedMediaPlayerSupportImpl::OnNeedKey(const std::string& type,
375 const std::vector<uint8>& init_data) {
376 // Do not fire NeedKey event if encrypted media is not enabled.
377 if (!blink::WebRuntimeFeatures::isPrefixedEncryptedMediaEnabled() &&
378 !blink::WebRuntimeFeatures::isEncryptedMediaEnabled()) {
379 return;
380 }
381
382 UMA_HISTOGRAM_COUNTS(kMediaEme + std::string("NeedKey"), 1);
383
384 DCHECK(init_data_type_.empty() || type.empty() || type == init_data_type_);
385 if (init_data_type_.empty())
386 init_data_type_ = type;
387
388 const uint8* init_data_ptr = init_data.empty() ? NULL : &init_data[0];
389 client_->keyNeeded(
390 WebString::fromUTF8(type), init_data_ptr, init_data.size());
391 }
392
393 void EncryptedMediaPlayerSupportImpl::OnKeyAdded(
394 const std::string& session_id) {
395 EmeUMAHistogramCounts(current_key_system_, "KeyAdded", 1);
396 client_->keyAdded(
397 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
398 WebString::fromUTF8(session_id));
399 }
400
401 void EncryptedMediaPlayerSupportImpl::OnKeyError(const std::string& session_id,
402 media::MediaKeys::KeyError error_code,
403 uint32 system_code) {
404 EmeUMAHistogramEnumeration(current_key_system_, "KeyError",
405 error_code, media::MediaKeys::kMaxKeyError);
406
407 uint16 short_system_code = 0;
408 if (system_code > std::numeric_limits<uint16>::max()) {
409 LOG(WARNING) << "system_code exceeds unsigned short limit.";
410 short_system_code = std::numeric_limits<uint16>::max();
411 } else {
412 short_system_code = static_cast<uint16>(system_code);
413 }
414
415 client_->keyError(
416 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
417 WebString::fromUTF8(session_id),
418 static_cast<WebMediaPlayerClient::MediaKeyErrorCode>(error_code),
419 short_system_code);
420 }
421
422 void EncryptedMediaPlayerSupportImpl::OnKeyMessage(
423 const std::string& session_id,
424 const std::vector<uint8>& message,
425 const GURL& destination_url) {
426 DCHECK(destination_url.is_empty() || destination_url.is_valid());
427
428 client_->keyMessage(
429 WebString::fromUTF8(GetPrefixedKeySystemName(current_key_system_)),
430 WebString::fromUTF8(session_id),
431 message.empty() ? NULL : &message[0],
432 message.size(),
433 destination_url);
434 }
435
436 void EncryptedMediaPlayerSupportImpl::SetDecryptorReadyCB(
437 const media::DecryptorReadyCB& decryptor_ready_cb) {
438 // Cancels the previous decryptor request.
439 if (decryptor_ready_cb.is_null()) {
440 if (!decryptor_ready_cb_.is_null()) {
441 base::ResetAndReturn(&decryptor_ready_cb_)
442 .Run(NULL, base::Bind(DoNothing));
443 }
444 return;
445 }
446
447 // TODO(xhwang): Support multiple decryptor notification request (e.g. from
448 // video and audio). The current implementation is okay for the current
449 // media pipeline since we initialize audio and video decoders in sequence.
450 // But WebMediaPlayerImpl should not depend on media pipeline's implementation
451 // detail.
452 DCHECK(decryptor_ready_cb_.is_null());
453
454 // Mixed use of prefixed and unprefixed EME APIs is disallowed by Blink.
455 DCHECK(!proxy_decryptor_ || !web_cdm_);
456
457 if (proxy_decryptor_) {
458 decryptor_ready_cb.Run(proxy_decryptor_->GetDecryptor(),
459 base::Bind(DoNothing));
460 return;
461 }
462
463 if (web_cdm_) {
464 decryptor_ready_cb.Run(web_cdm_->GetDecryptor(), base::Bind(DoNothing));
465 return;
466 }
467
468 decryptor_ready_cb_ = decryptor_ready_cb;
469 }
470
471 } // namespace content
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698