• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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:
SessionUpdatedPromise(scoped_ptr<media::SimpleCdmPromise> caller_promise,base::Closure additional_resolve_cb)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 
resolve()38   virtual void resolve() OVERRIDE {
39     DCHECK(is_pending_);
40     is_pending_ = false;
41     additional_resolve_cb_.Run();
42     caller_promise_->resolve();
43   }
44 
reject(media::MediaKeys::Exception exception_code,uint32 system_code,const std::string & error_message)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 
Create(const std::string & key_system,const GURL & security_origin,const CreatePepperCdmCB & create_pepper_cdm_cb,const media::SessionMessageCB & session_message_cb,const media::SessionReadyCB & session_ready_cb,const media::SessionClosedCB & session_closed_cb,const media::SessionErrorCB & session_error_cb)58 scoped_ptr<PpapiDecryptor> PpapiDecryptor::Create(
59     const std::string& key_system,
60     const GURL& security_origin,
61     const CreatePepperCdmCB& create_pepper_cdm_cb,
62     const media::SessionMessageCB& session_message_cb,
63     const media::SessionReadyCB& session_ready_cb,
64     const media::SessionClosedCB& session_closed_cb,
65     const media::SessionErrorCB& session_error_cb) {
66   std::string plugin_type = GetPepperType(key_system);
67   DCHECK(!plugin_type.empty());
68   scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper =
69       create_pepper_cdm_cb.Run(plugin_type, security_origin);
70   if (!pepper_cdm_wrapper) {
71     DLOG(ERROR) << "Plugin instance creation failed.";
72     return scoped_ptr<PpapiDecryptor>();
73   }
74 
75   return scoped_ptr<PpapiDecryptor>(
76       new PpapiDecryptor(key_system,
77                          pepper_cdm_wrapper.Pass(),
78                          session_message_cb,
79                          session_ready_cb,
80                          session_closed_cb,
81                          session_error_cb));
82 }
83 
PpapiDecryptor(const std::string & key_system,scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper,const media::SessionMessageCB & session_message_cb,const media::SessionReadyCB & session_ready_cb,const media::SessionClosedCB & session_closed_cb,const media::SessionErrorCB & session_error_cb)84 PpapiDecryptor::PpapiDecryptor(
85     const std::string& key_system,
86     scoped_ptr<PepperCdmWrapper> pepper_cdm_wrapper,
87     const media::SessionMessageCB& session_message_cb,
88     const media::SessionReadyCB& session_ready_cb,
89     const media::SessionClosedCB& session_closed_cb,
90     const media::SessionErrorCB& session_error_cb)
91     : pepper_cdm_wrapper_(pepper_cdm_wrapper.Pass()),
92       session_message_cb_(session_message_cb),
93       session_ready_cb_(session_ready_cb),
94       session_closed_cb_(session_closed_cb),
95       session_error_cb_(session_error_cb),
96       render_loop_proxy_(base::MessageLoopProxy::current()),
97       weak_ptr_factory_(this) {
98   DCHECK(pepper_cdm_wrapper_.get());
99   DCHECK(!session_message_cb_.is_null());
100   DCHECK(!session_ready_cb_.is_null());
101   DCHECK(!session_closed_cb_.is_null());
102   DCHECK(!session_error_cb_.is_null());
103 
104   base::WeakPtr<PpapiDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
105   CdmDelegate()->Initialize(
106       key_system,
107       base::Bind(&PpapiDecryptor::OnSessionMessage, weak_this),
108       base::Bind(&PpapiDecryptor::OnSessionReady, weak_this),
109       base::Bind(&PpapiDecryptor::OnSessionClosed, weak_this),
110       base::Bind(&PpapiDecryptor::OnSessionError, weak_this),
111       base::Bind(&PpapiDecryptor::OnFatalPluginError, weak_this));
112 }
113 
~PpapiDecryptor()114 PpapiDecryptor::~PpapiDecryptor() {
115   pepper_cdm_wrapper_.reset();
116 }
117 
CreateSession(const std::string & init_data_type,const uint8 * init_data,int init_data_length,SessionType session_type,scoped_ptr<media::NewSessionCdmPromise> promise)118 void PpapiDecryptor::CreateSession(
119     const std::string& init_data_type,
120     const uint8* init_data,
121     int init_data_length,
122     SessionType session_type,
123     scoped_ptr<media::NewSessionCdmPromise> promise) {
124   DVLOG(2) << __FUNCTION__;
125   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
126 
127   if (!CdmDelegate()) {
128     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
129     return;
130   }
131 
132   CdmDelegate()->CreateSession(init_data_type,
133                                init_data,
134                                init_data_length,
135                                session_type,
136                                promise.Pass());
137 }
138 
LoadSession(const std::string & web_session_id,scoped_ptr<media::NewSessionCdmPromise> promise)139 void PpapiDecryptor::LoadSession(
140     const std::string& web_session_id,
141     scoped_ptr<media::NewSessionCdmPromise> promise) {
142   DVLOG(2) << __FUNCTION__;
143   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
144 
145   if (!CdmDelegate()) {
146     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
147     return;
148   }
149 
150   CdmDelegate()->LoadSession(web_session_id, promise.Pass());
151 }
152 
UpdateSession(const std::string & web_session_id,const uint8 * response,int response_length,scoped_ptr<media::SimpleCdmPromise> promise)153 void PpapiDecryptor::UpdateSession(
154     const std::string& web_session_id,
155     const uint8* response,
156     int response_length,
157     scoped_ptr<media::SimpleCdmPromise> promise) {
158   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
159 
160   if (!CdmDelegate()) {
161     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
162     return;
163   }
164 
165   scoped_ptr<SessionUpdatedPromise> session_updated_promise(
166       new SessionUpdatedPromise(promise.Pass(),
167                                 base::Bind(&PpapiDecryptor::ResumePlayback,
168                                            weak_ptr_factory_.GetWeakPtr())));
169   CdmDelegate()->UpdateSession(
170       web_session_id,
171       response,
172       response_length,
173       session_updated_promise.PassAs<media::SimpleCdmPromise>());
174 }
175 
ReleaseSession(const std::string & web_session_id,scoped_ptr<media::SimpleCdmPromise> promise)176 void PpapiDecryptor::ReleaseSession(
177     const std::string& web_session_id,
178     scoped_ptr<media::SimpleCdmPromise> promise) {
179   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
180 
181   if (!CdmDelegate()) {
182     promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
183     return;
184   }
185 
186   CdmDelegate()->ReleaseSession(web_session_id, promise.Pass());
187 }
188 
GetDecryptor()189 media::Decryptor* PpapiDecryptor::GetDecryptor() {
190   return this;
191 }
192 
RegisterNewKeyCB(StreamType stream_type,const NewKeyCB & new_key_cb)193 void PpapiDecryptor::RegisterNewKeyCB(StreamType stream_type,
194                                       const NewKeyCB& new_key_cb) {
195   if (!render_loop_proxy_->BelongsToCurrentThread()) {
196     render_loop_proxy_->PostTask(FROM_HERE,
197                                  base::Bind(&PpapiDecryptor::RegisterNewKeyCB,
198                                             weak_ptr_factory_.GetWeakPtr(),
199                                             stream_type,
200                                             new_key_cb));
201     return;
202   }
203 
204   DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
205   switch (stream_type) {
206     case kAudio:
207       new_audio_key_cb_ = new_key_cb;
208       break;
209     case kVideo:
210       new_video_key_cb_ = new_key_cb;
211       break;
212     default:
213       NOTREACHED();
214   }
215 }
216 
Decrypt(StreamType stream_type,const scoped_refptr<media::DecoderBuffer> & encrypted,const DecryptCB & decrypt_cb)217 void PpapiDecryptor::Decrypt(
218     StreamType stream_type,
219     const scoped_refptr<media::DecoderBuffer>& encrypted,
220     const DecryptCB& decrypt_cb) {
221   if (!render_loop_proxy_->BelongsToCurrentThread()) {
222     render_loop_proxy_->PostTask(FROM_HERE,
223                                  base::Bind(&PpapiDecryptor::Decrypt,
224                                             weak_ptr_factory_.GetWeakPtr(),
225                                             stream_type,
226                                             encrypted,
227                                             decrypt_cb));
228     return;
229   }
230 
231   DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
232   if (!CdmDelegate() ||
233       !CdmDelegate()->Decrypt(stream_type, encrypted, decrypt_cb)) {
234     decrypt_cb.Run(kError, NULL);
235   }
236 }
237 
CancelDecrypt(StreamType stream_type)238 void PpapiDecryptor::CancelDecrypt(StreamType stream_type) {
239   if (!render_loop_proxy_->BelongsToCurrentThread()) {
240     render_loop_proxy_->PostTask(FROM_HERE,
241                                  base::Bind(&PpapiDecryptor::CancelDecrypt,
242                                             weak_ptr_factory_.GetWeakPtr(),
243                                             stream_type));
244     return;
245   }
246 
247   DVLOG(1) << __FUNCTION__ << " - stream_type: " << stream_type;
248   if (CdmDelegate())
249     CdmDelegate()->CancelDecrypt(stream_type);
250 }
251 
InitializeAudioDecoder(const media::AudioDecoderConfig & config,const DecoderInitCB & init_cb)252 void PpapiDecryptor::InitializeAudioDecoder(
253       const media::AudioDecoderConfig& config,
254       const DecoderInitCB& init_cb) {
255   if (!render_loop_proxy_->BelongsToCurrentThread()) {
256     render_loop_proxy_->PostTask(
257         FROM_HERE,
258         base::Bind(&PpapiDecryptor::InitializeAudioDecoder,
259                    weak_ptr_factory_.GetWeakPtr(),
260                    config,
261                    init_cb));
262     return;
263   }
264 
265   DVLOG(2) << __FUNCTION__;
266   DCHECK(config.is_encrypted());
267   DCHECK(config.IsValidConfig());
268 
269   audio_decoder_init_cb_ = init_cb;
270   if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
271                             config,
272                             base::Bind(&PpapiDecryptor::OnDecoderInitialized,
273                                        weak_ptr_factory_.GetWeakPtr(),
274                                        kAudio))) {
275     base::ResetAndReturn(&audio_decoder_init_cb_).Run(false);
276     return;
277   }
278 }
279 
InitializeVideoDecoder(const media::VideoDecoderConfig & config,const DecoderInitCB & init_cb)280 void PpapiDecryptor::InitializeVideoDecoder(
281     const media::VideoDecoderConfig& config,
282     const DecoderInitCB& init_cb) {
283   if (!render_loop_proxy_->BelongsToCurrentThread()) {
284     render_loop_proxy_->PostTask(
285         FROM_HERE,
286         base::Bind(&PpapiDecryptor::InitializeVideoDecoder,
287                    weak_ptr_factory_.GetWeakPtr(),
288                    config,
289                    init_cb));
290     return;
291   }
292 
293   DVLOG(2) << __FUNCTION__;
294   DCHECK(config.is_encrypted());
295   DCHECK(config.IsValidConfig());
296 
297   video_decoder_init_cb_ = init_cb;
298   if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
299                             config,
300                             base::Bind(&PpapiDecryptor::OnDecoderInitialized,
301                                        weak_ptr_factory_.GetWeakPtr(),
302                                        kVideo))) {
303     base::ResetAndReturn(&video_decoder_init_cb_).Run(false);
304     return;
305   }
306 }
307 
DecryptAndDecodeAudio(const scoped_refptr<media::DecoderBuffer> & encrypted,const AudioDecodeCB & audio_decode_cb)308 void PpapiDecryptor::DecryptAndDecodeAudio(
309     const scoped_refptr<media::DecoderBuffer>& encrypted,
310     const AudioDecodeCB& audio_decode_cb) {
311   if (!render_loop_proxy_->BelongsToCurrentThread()) {
312     render_loop_proxy_->PostTask(
313         FROM_HERE,
314         base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio,
315                    weak_ptr_factory_.GetWeakPtr(),
316                    encrypted,
317                    audio_decode_cb));
318     return;
319   }
320 
321   DVLOG(3) << __FUNCTION__;
322   if (!CdmDelegate() ||
323       !CdmDelegate()->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) {
324     audio_decode_cb.Run(kError, AudioBuffers());
325   }
326 }
327 
DecryptAndDecodeVideo(const scoped_refptr<media::DecoderBuffer> & encrypted,const VideoDecodeCB & video_decode_cb)328 void PpapiDecryptor::DecryptAndDecodeVideo(
329     const scoped_refptr<media::DecoderBuffer>& encrypted,
330     const VideoDecodeCB& video_decode_cb) {
331   if (!render_loop_proxy_->BelongsToCurrentThread()) {
332     render_loop_proxy_->PostTask(
333         FROM_HERE,
334         base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo,
335                    weak_ptr_factory_.GetWeakPtr(),
336                    encrypted,
337                    video_decode_cb));
338     return;
339   }
340 
341   DVLOG(3) << __FUNCTION__;
342   if (!CdmDelegate() ||
343       !CdmDelegate()->DecryptAndDecodeVideo(encrypted, video_decode_cb)) {
344     video_decode_cb.Run(kError, NULL);
345   }
346 }
347 
ResetDecoder(StreamType stream_type)348 void PpapiDecryptor::ResetDecoder(StreamType stream_type) {
349   if (!render_loop_proxy_->BelongsToCurrentThread()) {
350     render_loop_proxy_->PostTask(FROM_HERE,
351                                  base::Bind(&PpapiDecryptor::ResetDecoder,
352                                             weak_ptr_factory_.GetWeakPtr(),
353                                             stream_type));
354     return;
355   }
356 
357   DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
358   if (CdmDelegate())
359     CdmDelegate()->ResetDecoder(stream_type);
360 }
361 
DeinitializeDecoder(StreamType stream_type)362 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) {
363   if (!render_loop_proxy_->BelongsToCurrentThread()) {
364     render_loop_proxy_->PostTask(
365         FROM_HERE,
366         base::Bind(&PpapiDecryptor::DeinitializeDecoder,
367                    weak_ptr_factory_.GetWeakPtr(),
368                    stream_type));
369     return;
370   }
371 
372   DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
373   if (CdmDelegate())
374     CdmDelegate()->DeinitializeDecoder(stream_type);
375 }
376 
OnDecoderInitialized(StreamType stream_type,bool success)377 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
378                                           bool success) {
379   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
380   switch (stream_type) {
381     case kAudio:
382       DCHECK(!audio_decoder_init_cb_.is_null());
383       base::ResetAndReturn(&audio_decoder_init_cb_).Run(success);
384       break;
385     case kVideo:
386       DCHECK(!video_decoder_init_cb_.is_null());
387       base::ResetAndReturn(&video_decoder_init_cb_).Run(success);
388       break;
389     default:
390       NOTREACHED();
391   }
392 }
393 
OnSessionMessage(const std::string & web_session_id,const std::vector<uint8> & message,const GURL & destination_url)394 void PpapiDecryptor::OnSessionMessage(const std::string& web_session_id,
395                                       const std::vector<uint8>& message,
396                                       const GURL& destination_url) {
397   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
398   session_message_cb_.Run(web_session_id, message, destination_url);
399 }
400 
OnSessionReady(const std::string & web_session_id)401 void PpapiDecryptor::OnSessionReady(const std::string& web_session_id) {
402   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
403 
404   ResumePlayback();
405   session_ready_cb_.Run(web_session_id);
406 }
407 
OnSessionClosed(const std::string & web_session_id)408 void PpapiDecryptor::OnSessionClosed(const std::string& web_session_id) {
409   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
410   session_closed_cb_.Run(web_session_id);
411 }
412 
OnSessionError(const std::string & web_session_id,MediaKeys::Exception exception_code,uint32 system_code,const std::string & error_description)413 void PpapiDecryptor::OnSessionError(const std::string& web_session_id,
414                                     MediaKeys::Exception exception_code,
415                                     uint32 system_code,
416                                     const std::string& error_description) {
417   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
418   session_error_cb_.Run(
419       web_session_id, exception_code, system_code, error_description);
420 }
421 
ResumePlayback()422 void PpapiDecryptor::ResumePlayback() {
423   // Based on the spec, we need to resume playback when update() completes
424   // successfully, or when a session is successfully loaded (triggered by
425   // OnSessionReady()). So we choose to call the NewKeyCBs here.
426   if (!new_audio_key_cb_.is_null())
427     new_audio_key_cb_.Run();
428 
429   if (!new_video_key_cb_.is_null())
430     new_video_key_cb_.Run();
431 }
432 
OnFatalPluginError()433 void PpapiDecryptor::OnFatalPluginError() {
434   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
435   pepper_cdm_wrapper_.reset();
436 }
437 
CdmDelegate()438 ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() {
439   DCHECK(render_loop_proxy_->BelongsToCurrentThread());
440   return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL;
441 }
442 
443 }  // namespace content
444