Upstream version 10.39.225.0
[platform/framework/web/crosswalk.git] / src / content / renderer / media / crypto / ppapi_decryptor.cc
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 "content/renderer/media/crypto/ppapi_decryptor.h"
6
7 #include <string>
8
9 #include "base/bind.h"
10 #include "base/callback.h"
11 #include "base/callback_helpers.h"
12 #include "base/location.h"
13 #include "base/logging.h"
14 #include "base/message_loop/message_loop.h"
15 #include "base/message_loop/message_loop_proxy.h"
16 #include "content/renderer/media/crypto/key_systems.h"
17 #include "content/renderer/pepper/content_decryptor_delegate.h"
18 #include "content/renderer/pepper/pepper_plugin_instance_impl.h"
19 #include "media/base/audio_decoder_config.h"
20 #include "media/base/cdm_promise.h"
21 #include "media/base/data_buffer.h"
22 #include "media/base/decoder_buffer.h"
23 #include "media/base/video_decoder_config.h"
24 #include "media/base/video_frame.h"
25
26 namespace content {
27
28 // This class is needed so that resolving an Update() promise triggers playback
29 // of the stream. It intercepts the resolve() call to invoke an additional
30 // callback.
31 class SessionUpdatedPromise : public media::SimpleCdmPromise {
32  public:
33   SessionUpdatedPromise(scoped_ptr<media::SimpleCdmPromise> caller_promise,
34                         base::Closure additional_resolve_cb)
35       : caller_promise_(caller_promise.Pass()),
36         additional_resolve_cb_(additional_resolve_cb) {}
37
38   virtual void resolve() OVERRIDE {
39     DCHECK(is_pending_);
40     is_pending_ = false;
41     additional_resolve_cb_.Run();
42     caller_promise_->resolve();
43   }
44
45   virtual void reject(media::MediaKeys::Exception exception_code,
46                       uint32 system_code,
47                       const std::string& error_message) OVERRIDE {
48     DCHECK(is_pending_);
49     is_pending_ = false;
50     caller_promise_->reject(exception_code, system_code, error_message);
51   }
52
53  protected:
54   scoped_ptr<media::SimpleCdmPromise> caller_promise_;
55   base::Closure additional_resolve_cb_;
56 };
57
58 // This class is needed so that resolving a SessionLoaded() promise triggers
59 // playback of the stream. It intercepts the resolve() call to invoke an
60 // additional callback. This is only needed until KeysChange event gets passed
61 // through Pepper.
62 class SessionLoadedPromise : public media::NewSessionCdmPromise {
63  public:
64   SessionLoadedPromise(scoped_ptr<media::NewSessionCdmPromise> caller_promise,
65                        base::Closure additional_resolve_cb)
66       : caller_promise_(caller_promise.Pass()),
67         additional_resolve_cb_(additional_resolve_cb) {}
68
69   virtual void resolve(const std::string& web_session_id) OVERRIDE {
70     DCHECK(is_pending_);
71     is_pending_ = false;
72     additional_resolve_cb_.Run();
73     caller_promise_->resolve(web_session_id);
74   }
75
76   virtual void reject(media::MediaKeys::Exception exception_code,
77                       uint32 system_code,
78                       const std::string& error_message) OVERRIDE {
79     DCHECK(is_pending_);
80     is_pending_ = false;
81     caller_promise_->reject(exception_code, system_code, error_message);
82   }
83
84  protected:
85   scoped_ptr<media::NewSessionCdmPromise> caller_promise_;
86   base::Closure additional_resolve_cb_;
87 };
88
89 scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create(
90     const std::string& key_system,
91     const GURL& security_origin,
92     const CreatePepperCdmCB& create_pepper_cdm_cb,
93     const media::SessionMessageCB& session_message_cb,
94     const media::SessionReadyCB& session_ready_cb,
95     const media::SessionClosedCB& session_closed_cb,
96     const media::SessionErrorCB& session_error_cb,
97     const media::SessionKeysChangeCB& session_keys_change_cb,
98     const media::SessionExpirationUpdateCB& session_expiration_update_cb) {
99   std::string plugin_type = GetPepperType(key_system);
100   DCHECK(!plugin_type.empty());
101   scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper =
102       create_pepper_cdm_cb.Run(plugin_type, security_origin);
103   if (!pepper_cdm_wrapper) {
104     DLOG(ERROR) << "Plugin instance creation failed.";
105     return scoped_ptr<PpapiDecryptor>();
106   }
107
108   return scoped_ptr<PpapiDecryptor>(
109       new PpapiDecryptor(key_system,
110                          pepper_cdm_wrapper.Pass(),
111                          session_message_cb,
112                          session_ready_cb,
113                          session_closed_cb,
114                          session_error_cb,
115                          session_keys_change_cb,
116                          session_expiration_update_cb));
117 }
118
119 PpapiDecryptor::PpapiDecryptor(
120     const std::string& key_system,
121     scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper,
122     const media::SessionMessageCB& session_message_cb,
123     const media::SessionReadyCB& session_ready_cb,
124     const media::SessionClosedCB& session_closed_cb,
125     const media::SessionErrorCB& session_error_cb,
126     const media::SessionKeysChangeCB& session_keys_change_cb,
127     const media::SessionExpirationUpdateCB& session_expiration_update_cb)
128     : pepper_cdm_wrapper_(pepper_cdm_wrapper.Pass()),
129       session_message_cb_(session_message_cb),
130       session_ready_cb_(session_ready_cb),
131       session_closed_cb_(session_closed_cb),
132       session_error_cb_(session_error_cb),
133       session_keys_change_cb_(session_keys_change_cb),
134       session_expiration_update_cb_(session_expiration_update_cb),
135       render_loop_proxy_(base::MessageLoopProxy::current()),
136       weak_ptr_factory_(this) {
137   DCHECK(pepper_cdm_wrapper_.get());
138   DCHECK(!session_message_cb_.is_null());
139   DCHECK(!session_ready_cb_.is_null());
140   DCHECK(!session_closed_cb_.is_null());
141   DCHECK(!session_error_cb_.is_null());
142   DCHECK(!session_keys_change_cb.is_null());
143   DCHECK(!session_expiration_update_cb.is_null());
144
145   base::WeakPtr<PpapiDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
146   CdmDelegate()->Initialize(
147       key_system,
148       base::Bind(&PpapiDecryptor::OnSessionMessage, weak_this),
149       base::Bind(&PpapiDecryptor::OnSessionReady, weak_this),
150       base::Bind(&PpapiDecryptor::OnSessionClosed, weak_this),
151       base::Bind(&PpapiDecryptor::OnSessionError, weak_this),
152       base::Bind(&PpapiDecryptor::OnSessionKeysChange, weak_this),
153       base::Bind(&PpapiDecryptor::OnSessionExpirationUpdate, weak_this),
154       base::Bind(&PpapiDecryptor::OnFatalPluginError, weak_this));
155 }
156
157 PpapiDecryptor::~PpapiDecryptor() {
158   pepper_cdm_wrapper_.reset();
159 }
160
161 void PpapiDecryptor::SetServerCertificate(
162     const uint8* certificate_data,
163     int certificate_data_length,
164     scoped_ptr<media::SimpleCdmPromise> promise) {
165   DVLOG(2) << __FUNCTION__;
166   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
167
168   if (!CdmDelegate()) {
169     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
170     return;
171   }
172
173   CdmDelegate()->SetServerCertificate(
174       certificate_data, certificate_data_length, promise.Pass());
175 }
176
177 void PpapiDecryptor::CreateSession(
178     const std::string& init_data_type,
179     const uint8* init_data,
180     int init_data_length,
181     SessionType session_type,
182     scoped_ptr<media::NewSessionCdmPromise> promise) {
183   DVLOG(2) << __FUNCTION__;
184   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
185
186   if (!CdmDelegate()) {
187     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
188     return;
189   }
190
191   CdmDelegate()->CreateSession(init_data_type,
192                                init_data,
193                                init_data_length,
194                                session_type,
195                                promise.Pass());
196 }
197
198 void PpapiDecryptor::LoadSession(
199     const std::string& web_session_id,
200     scoped_ptr<media::NewSessionCdmPromise> promise) {
201   DVLOG(2) << __FUNCTION__;
202   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
203
204   if (!CdmDelegate()) {
205     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
206     return;
207   }
208
209   // TODO(jrummell): Intercepting the promise should not be necessary once
210   // OnSessionKeysChange() is called in all cases. http://crbug.com/413413.
211   scoped_ptr<SessionLoadedPromise> session_loaded_promise(
212       new SessionLoadedPromise(promise.Pass(),
213                                base::Bind(&PpapiDecryptor::ResumePlayback,
214                                           weak_ptr_factory_.GetWeakPtr())));
215
216   CdmDelegate()->LoadSession(
217       web_session_id,
218       session_loaded_promise.PassAs<media::NewSessionCdmPromise>());
219 }
220
221 void PpapiDecryptor::UpdateSession(
222     const std::string& web_session_id,
223     const uint8* response,
224     int response_length,
225     scoped_ptr<media::SimpleCdmPromise> promise) {
226   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
227
228   if (!CdmDelegate()) {
229     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
230     return;
231   }
232
233   // TODO(jrummell): Intercepting the promise should not be necessary once
234   // OnSessionKeysChange() is called in all cases. http://crbug.com/413413.
235   scoped_ptr<SessionUpdatedPromise> session_updated_promise(
236       new SessionUpdatedPromise(promise.Pass(),
237                                 base::Bind(&PpapiDecryptor::ResumePlayback,
238                                            weak_ptr_factory_.GetWeakPtr())));
239   CdmDelegate()->UpdateSession(
240       web_session_id,
241       response,
242       response_length,
243       session_updated_promise.PassAs<media::SimpleCdmPromise>());
244 }
245
246 void PpapiDecryptor::CloseSession(const std::string& web_session_id,
247                                   scoped_ptr<media::SimpleCdmPromise> promise) {
248   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
249
250   if (!CdmDelegate()) {
251     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
252     return;
253   }
254
255   CdmDelegate()->CloseSession(web_session_id, promise.Pass());
256 }
257
258 void PpapiDecryptor::RemoveSession(
259     const std::string& web_session_id,
260     scoped_ptr<media::SimpleCdmPromise> promise) {
261   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
262
263   if (!CdmDelegate()) {
264     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
265     return;
266   }
267
268   CdmDelegate()->RemoveSession(web_session_id, promise.Pass());
269 }
270
271 void PpapiDecryptor::GetUsableKeyIds(const std::string& web_session_id,
272                                      scoped_ptr<media::KeyIdsPromise> promise) {
273   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
274
275   if (!CdmDelegate()) {
276     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
277     return;
278   }
279
280   CdmDelegate()->GetUsableKeyIds(web_session_id, promise.Pass());
281 }
282
283 media::Decryptor* PpapiDecryptor::GetDecryptor() {
284   return this;
285 }
286
287 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type,
288                                       const NewKeyCB& new_key_cb) {
289   if (!render_loop_proxy_->BelongsToCurrentThread()) {
290     render_loop_proxy_->PostTask(FROM_HERE,
291                                  base::Bind(&PpapiDecryptor::RegisterNewKeyCB,
292                                             weak_ptr_factory_.GetWeakPtr(),
293                                             stream_type,
294                                             new_key_cb));
295     return;
296   }
297
298   DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
299   switch (stream_type) {
300     case kAudio:
301       new_audio_key_cb_ = new_key_cb;
302       break;
303     case kVideo:
304       new_video_key_cb_ = new_key_cb;
305       break;
306     default:
307       NOTREACHED();
308   }
309 }
310
311 void PpapiDecryptor::Decrypt(
312     StreamType stream_type,
313     const scoped_refptr<media::DecoderBuffer>& encrypted,
314     const DecryptCB& decrypt_cb) {
315   if (!render_loop_proxy_->BelongsToCurrentThread()) {
316     render_loop_proxy_->PostTask(FROM_HERE,
317                                  base::Bind(&PpapiDecryptor::Decrypt,
318                                             weak_ptr_factory_.GetWeakPtr(),
319                                             stream_type,
320                                             encrypted,
321                                             decrypt_cb));
322     return;
323   }
324
325   DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
326   if (!CdmDelegate() ||
327       !CdmDelegate()->Decrypt(stream_type, encrypted, decrypt_cb)) {
328     decrypt_cb.Run(kError, NULL);
329   }
330 }
331
332 void PpapiDecryptor::CancelDecrypt(StreamType stream_type) {
333   if (!render_loop_proxy_->BelongsToCurrentThread()) {
334     render_loop_proxy_->PostTask(FROM_HERE,
335                                  base::Bind(&PpapiDecryptor::CancelDecrypt,
336                                             weak_ptr_factory_.GetWeakPtr(),
337                                             stream_type));
338     return;
339   }
340
341   DVLOG(1) << __FUNCTION__ << " - stream_type: " << stream_type;
342   if (CdmDelegate())
343     CdmDelegate()->CancelDecrypt(stream_type);
344 }
345
346 void PpapiDecryptor::InitializeAudioDecoder(
347       const media::AudioDecoderConfig& config,
348       const DecoderInitCB& init_cb) {
349   if (!render_loop_proxy_->BelongsToCurrentThread()) {
350     render_loop_proxy_->PostTask(
351         FROM_HERE,
352         base::Bind(&PpapiDecryptor::InitializeAudioDecoder,
353                    weak_ptr_factory_.GetWeakPtr(),
354                    config,
355                    init_cb));
356     return;
357   }
358
359   DVLOG(2) << __FUNCTION__;
360   DCHECK(config.is_encrypted());
361   DCHECK(config.IsValidConfig());
362
363   audio_decoder_init_cb_ = init_cb;
364   if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
365                             config,
366                             base::Bind(&PpapiDecryptor::OnDecoderInitialized,
367                                        weak_ptr_factory_.GetWeakPtr(),
368                                        kAudio))) {
369     base::ResetAndReturn(&audio_decoder_init_cb_).Run(false);
370     return;
371   }
372 }
373
374 void PpapiDecryptor::InitializeVideoDecoder(
375     const media::VideoDecoderConfig& config,
376     const DecoderInitCB& init_cb) {
377   if (!render_loop_proxy_->BelongsToCurrentThread()) {
378     render_loop_proxy_->PostTask(
379         FROM_HERE,
380         base::Bind(&PpapiDecryptor::InitializeVideoDecoder,
381                    weak_ptr_factory_.GetWeakPtr(),
382                    config,
383                    init_cb));
384     return;
385   }
386
387   DVLOG(2) << __FUNCTION__;
388   DCHECK(config.is_encrypted());
389   DCHECK(config.IsValidConfig());
390
391   video_decoder_init_cb_ = init_cb;
392   if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
393                             config,
394                             base::Bind(&PpapiDecryptor::OnDecoderInitialized,
395                                        weak_ptr_factory_.GetWeakPtr(),
396                                        kVideo))) {
397     base::ResetAndReturn(&video_decoder_init_cb_).Run(false);
398     return;
399   }
400 }
401
402 void PpapiDecryptor::DecryptAndDecodeAudio(
403     const scoped_refptr<media::DecoderBuffer>& encrypted,
404     const AudioDecodeCB& audio_decode_cb) {
405   if (!render_loop_proxy_->BelongsToCurrentThread()) {
406     render_loop_proxy_->PostTask(
407         FROM_HERE,
408         base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio,
409                    weak_ptr_factory_.GetWeakPtr(),
410                    encrypted,
411                    audio_decode_cb));
412     return;
413   }
414
415   DVLOG(3) << __FUNCTION__;
416   if (!CdmDelegate() ||
417       !CdmDelegate()->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) {
418     audio_decode_cb.Run(kError, AudioBuffers());
419   }
420 }
421
422 void PpapiDecryptor::DecryptAndDecodeVideo(
423     const scoped_refptr<media::DecoderBuffer>& encrypted,
424     const VideoDecodeCB& video_decode_cb) {
425   if (!render_loop_proxy_->BelongsToCurrentThread()) {
426     render_loop_proxy_->PostTask(
427         FROM_HERE,
428         base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo,
429                    weak_ptr_factory_.GetWeakPtr(),
430                    encrypted,
431                    video_decode_cb));
432     return;
433   }
434
435   DVLOG(3) << __FUNCTION__;
436   if (!CdmDelegate() ||
437       !CdmDelegate()->DecryptAndDecodeVideo(encrypted, video_decode_cb)) {
438     video_decode_cb.Run(kError, NULL);
439   }
440 }
441
442 void PpapiDecryptor::ResetDecoder(StreamType stream_type) {
443   if (!render_loop_proxy_->BelongsToCurrentThread()) {
444     render_loop_proxy_->PostTask(FROM_HERE,
445                                  base::Bind(&PpapiDecryptor::ResetDecoder,
446                                             weak_ptr_factory_.GetWeakPtr(),
447                                             stream_type));
448     return;
449   }
450
451   DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
452   if (CdmDelegate())
453     CdmDelegate()->ResetDecoder(stream_type);
454 }
455
456 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) {
457   if (!render_loop_proxy_->BelongsToCurrentThread()) {
458     render_loop_proxy_->PostTask(
459         FROM_HERE,
460         base::Bind(&PpapiDecryptor::DeinitializeDecoder,
461                    weak_ptr_factory_.GetWeakPtr(),
462                    stream_type));
463     return;
464   }
465
466   DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
467   if (CdmDelegate())
468     CdmDelegate()->DeinitializeDecoder(stream_type);
469 }
470
471 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
472                                           bool success) {
473   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
474   switch (stream_type) {
475     case kAudio:
476       DCHECK(!audio_decoder_init_cb_.is_null());
477       base::ResetAndReturn(&audio_decoder_init_cb_).Run(success);
478       break;
479     case kVideo:
480       DCHECK(!video_decoder_init_cb_.is_null());
481       base::ResetAndReturn(&video_decoder_init_cb_).Run(success);
482       break;
483     default:
484       NOTREACHED();
485   }
486 }
487
488 void PpapiDecryptor::OnSessionMessage(const std::string& web_session_id,
489                                       const std::vector<uint8>& message,
490                                       const GURL& destination_url) {
491   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
492   session_message_cb_.Run(web_session_id, message, destination_url);
493 }
494
495 void PpapiDecryptor::OnSessionKeysChange(const std::string& web_session_id,
496                                          bool has_additional_usable_key) {
497   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
498
499   // TODO(jrummell): Handling resume playback should be done in the media
500   // player, not in the Decryptors. http://crbug.com/413413.
501   if (has_additional_usable_key)
502     ResumePlayback();
503
504   session_keys_change_cb_.Run(web_session_id, has_additional_usable_key);
505 }
506
507 void PpapiDecryptor::OnSessionExpirationUpdate(
508     const std::string& web_session_id,
509     const base::Time& new_expiry_time) {
510   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
511   session_expiration_update_cb_.Run(web_session_id, new_expiry_time);
512 }
513
514 void PpapiDecryptor::OnSessionReady(const std::string& web_session_id) {
515   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
516
517   // TODO(jrummell): Calling ResumePlayback() here should not be necessary once
518   // OnSessionKeysChange() is called in all cases. http://crbug.com/413413.
519   ResumePlayback();
520   session_ready_cb_.Run(web_session_id);
521 }
522
523 void PpapiDecryptor::OnSessionClosed(const std::string& web_session_id) {
524   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
525   session_closed_cb_.Run(web_session_id);
526 }
527
528 void PpapiDecryptor::OnSessionError(const std::string& web_session_id,
529                                     MediaKeys::Exception exception_code,
530                                     uint32 system_code,
531                                     const std::string& error_description) {
532   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
533   session_error_cb_.Run(
534       web_session_id, exception_code, system_code, error_description);
535 }
536
537 void PpapiDecryptor::ResumePlayback() {
538   // Based on the spec, we need to resume playback when update() completes
539   // successfully, or when a session is successfully loaded (triggered by
540   // OnSessionReady()). So we choose to call the NewKeyCBs here.
541   if (!new_audio_key_cb_.is_null())
542     new_audio_key_cb_.Run();
543
544   if (!new_video_key_cb_.is_null())
545     new_video_key_cb_.Run();
546 }
547
548 void PpapiDecryptor::OnFatalPluginError() {
549   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
550   pepper_cdm_wrapper_.reset();
551 }
552
553 ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() {
554   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
555   return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL;
556 }
557
558 }  // namespace content