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
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:
SessionLoadedPromise(scoped_ptr<media::NewSessionCdmPromise> caller_promise,base::Closure additional_resolve_cb)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
resolve(const std::string & web_session_id)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
reject(media::MediaKeys::Exception exception_code,uint32 system_code,const std::string & error_message)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
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,const media::SessionKeysChangeCB & session_keys_change_cb,const media::SessionExpirationUpdateCB & session_expiration_update_cb)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
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,const media::SessionKeysChangeCB & session_keys_change_cb,const media::SessionExpirationUpdateCB & session_expiration_update_cb)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
~PpapiDecryptor()157 PpapiDecryptor::~PpapiDecryptor() {
158 pepper_cdm_wrapper_.reset();
159 }
160
SetServerCertificate(const uint8 * certificate_data,int certificate_data_length,scoped_ptr<media::SimpleCdmPromise> promise)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
CreateSession(const std::string & init_data_type,const uint8 * init_data,int init_data_length,SessionType session_type,scoped_ptr<media::NewSessionCdmPromise> promise)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
LoadSession(const std::string & web_session_id,scoped_ptr<media::NewSessionCdmPromise> promise)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
UpdateSession(const std::string & web_session_id,const uint8 * response,int response_length,scoped_ptr<media::SimpleCdmPromise> promise)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
CloseSession(const std::string & web_session_id,scoped_ptr<media::SimpleCdmPromise> promise)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
RemoveSession(const std::string & web_session_id,scoped_ptr<media::SimpleCdmPromise> promise)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
GetUsableKeyIds(const std::string & web_session_id,scoped_ptr<media::KeyIdsPromise> promise)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
GetDecryptor()283 media::Decryptor* PpapiDecryptor::GetDecryptor() {
284 return this;
285 }
286
RegisterNewKeyCB(StreamType stream_type,const NewKeyCB & new_key_cb)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
Decrypt(StreamType stream_type,const scoped_refptr<media::DecoderBuffer> & encrypted,const DecryptCB & decrypt_cb)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
CancelDecrypt(StreamType stream_type)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
InitializeAudioDecoder(const media::AudioDecoderConfig & config,const DecoderInitCB & init_cb)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
InitializeVideoDecoder(const media::VideoDecoderConfig & config,const DecoderInitCB & init_cb)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
DecryptAndDecodeAudio(const scoped_refptr<media::DecoderBuffer> & encrypted,const AudioDecodeCB & audio_decode_cb)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
DecryptAndDecodeVideo(const scoped_refptr<media::DecoderBuffer> & encrypted,const VideoDecodeCB & video_decode_cb)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
ResetDecoder(StreamType stream_type)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
DeinitializeDecoder(StreamType stream_type)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
OnDecoderInitialized(StreamType stream_type,bool success)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
OnSessionMessage(const std::string & web_session_id,const std::vector<uint8> & message,const GURL & destination_url)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
OnSessionKeysChange(const std::string & web_session_id,bool has_additional_usable_key)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
OnSessionExpirationUpdate(const std::string & web_session_id,const base::Time & new_expiry_time)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
OnSessionReady(const std::string & web_session_id)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
OnSessionClosed(const std::string & web_session_id)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
OnSessionError(const std::string & web_session_id,MediaKeys::Exception exception_code,uint32 system_code,const std::string & error_description)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
ResumePlayback()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
OnFatalPluginError()548 void PpapiDecryptor::OnFatalPluginError() {
549 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
550 pepper_cdm_wrapper_.reset();
551 }
552
CdmDelegate()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
559