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