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.
5 #include "content/renderer/media/crypto/ppapi_decryptor.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"
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
31 class SessionUpdatedPromise : public media::SimpleCdmPromise {
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) {}
38 virtual void resolve() OVERRIDE {
41 additional_resolve_cb_.Run();
42 caller_promise_->resolve();
45 virtual void reject(media::MediaKeys::Exception exception_code,
47 const std::string& error_message) OVERRIDE {
50 caller_promise_->reject(exception_code, system_code, error_message);
54 scoped_ptr<media::SimpleCdmPromise> caller_promise_;
55 base::Closure additional_resolve_cb_;
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
62 class SessionLoadedPromise : public media::NewSessionCdmPromise {
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) {}
69 virtual void resolve(const std::string& web_session_id) OVERRIDE {
72 additional_resolve_cb_.Run();
73 caller_promise_->resolve(web_session_id);
76 virtual void reject(media::MediaKeys::Exception exception_code,
78 const std::string& error_message) OVERRIDE {
81 caller_promise_->reject(exception_code, system_code, error_message);
85 scoped_ptr<media::NewSessionCdmPromise> caller_promise_;
86 base::Closure additional_resolve_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>();
108 return scoped_ptr<PpapiDecryptor>(
109 new PpapiDecryptor(key_system,
110 pepper_cdm_wrapper.Pass(),
115 session_keys_change_cb,
116 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());
145 base::WeakPtr<PpapiDecryptor> weak_this = weak_ptr_factory_.GetWeakPtr();
146 CdmDelegate()->Initialize(
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));
157 PpapiDecryptor::~PpapiDecryptor() {
158 pepper_cdm_wrapper_.reset();
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());
168 if (!CdmDelegate()) {
169 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
173 CdmDelegate()->SetServerCertificate(
174 certificate_data, certificate_data_length, promise.Pass());
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());
186 if (!CdmDelegate()) {
187 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
191 CdmDelegate()->CreateSession(init_data_type,
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());
204 if (!CdmDelegate()) {
205 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
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())));
216 CdmDelegate()->LoadSession(
218 session_loaded_promise.PassAs<media::NewSessionCdmPromise>());
221 void PpapiDecryptor::UpdateSession(
222 const std::string& web_session_id,
223 const uint8* response,
225 scoped_ptr<media::SimpleCdmPromise> promise) {
226 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
228 if (!CdmDelegate()) {
229 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
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(
243 session_updated_promise.PassAs<media::SimpleCdmPromise>());
246 void PpapiDecryptor::CloseSession(const std::string& web_session_id,
247 scoped_ptr<media::SimpleCdmPromise> promise) {
248 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
250 if (!CdmDelegate()) {
251 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
255 CdmDelegate()->CloseSession(web_session_id, promise.Pass());
258 void PpapiDecryptor::RemoveSession(
259 const std::string& web_session_id,
260 scoped_ptr<media::SimpleCdmPromise> promise) {
261 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
263 if (!CdmDelegate()) {
264 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
268 CdmDelegate()->RemoveSession(web_session_id, promise.Pass());
271 void PpapiDecryptor::GetUsableKeyIds(const std::string& web_session_id,
272 scoped_ptr<media::KeyIdsPromise> promise) {
273 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
275 if (!CdmDelegate()) {
276 promise->reject(INVALID_STATE_ERROR, 0, "CdmDelegate() does not exist.");
280 CdmDelegate()->GetUsableKeyIds(web_session_id, promise.Pass());
283 media::Decryptor* PpapiDecryptor::GetDecryptor() {
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(),
298 DVLOG(3) << __FUNCTION__ << " - stream_type: " << stream_type;
299 switch (stream_type) {
301 new_audio_key_cb_ = new_key_cb;
304 new_video_key_cb_ = new_key_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(),
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);
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(),
341 DVLOG(1) << __FUNCTION__ << " - stream_type: " << stream_type;
343 CdmDelegate()->CancelDecrypt(stream_type);
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(
352 base::Bind(&PpapiDecryptor::InitializeAudioDecoder,
353 weak_ptr_factory_.GetWeakPtr(),
359 DVLOG(2) << __FUNCTION__;
360 DCHECK(config.is_encrypted());
361 DCHECK(config.IsValidConfig());
363 audio_decoder_init_cb_ = init_cb;
364 if (!CdmDelegate() || !CdmDelegate()->InitializeAudioDecoder(
366 base::Bind(&PpapiDecryptor::OnDecoderInitialized,
367 weak_ptr_factory_.GetWeakPtr(),
369 base::ResetAndReturn(&audio_decoder_init_cb_).Run(false);
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(
380 base::Bind(&PpapiDecryptor::InitializeVideoDecoder,
381 weak_ptr_factory_.GetWeakPtr(),
387 DVLOG(2) << __FUNCTION__;
388 DCHECK(config.is_encrypted());
389 DCHECK(config.IsValidConfig());
391 video_decoder_init_cb_ = init_cb;
392 if (!CdmDelegate() || !CdmDelegate()->InitializeVideoDecoder(
394 base::Bind(&PpapiDecryptor::OnDecoderInitialized,
395 weak_ptr_factory_.GetWeakPtr(),
397 base::ResetAndReturn(&video_decoder_init_cb_).Run(false);
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(
408 base::Bind(&PpapiDecryptor::DecryptAndDecodeAudio,
409 weak_ptr_factory_.GetWeakPtr(),
415 DVLOG(3) << __FUNCTION__;
416 if (!CdmDelegate() ||
417 !CdmDelegate()->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) {
418 audio_decode_cb.Run(kError, AudioBuffers());
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(
428 base::Bind(&PpapiDecryptor::DecryptAndDecodeVideo,
429 weak_ptr_factory_.GetWeakPtr(),
435 DVLOG(3) << __FUNCTION__;
436 if (!CdmDelegate() ||
437 !CdmDelegate()->DecryptAndDecodeVideo(encrypted, video_decode_cb)) {
438 video_decode_cb.Run(kError, NULL);
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(),
451 DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
453 CdmDelegate()->ResetDecoder(stream_type);
456 void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) {
457 if (!render_loop_proxy_->BelongsToCurrentThread()) {
458 render_loop_proxy_->PostTask(
460 base::Bind(&PpapiDecryptor::DeinitializeDecoder,
461 weak_ptr_factory_.GetWeakPtr(),
466 DVLOG(2) << __FUNCTION__ << " - stream_type: " << stream_type;
468 CdmDelegate()->DeinitializeDecoder(stream_type);
471 void PpapiDecryptor::OnDecoderInitialized(StreamType stream_type,
473 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
474 switch (stream_type) {
476 DCHECK(!audio_decoder_init_cb_.is_null());
477 base::ResetAndReturn(&audio_decoder_init_cb_).Run(success);
480 DCHECK(!video_decoder_init_cb_.is_null());
481 base::ResetAndReturn(&video_decoder_init_cb_).Run(success);
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);
495 void PpapiDecryptor::OnSessionKeysChange(const std::string& web_session_id,
496 bool has_additional_usable_key) {
497 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
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)
504 session_keys_change_cb_.Run(web_session_id, has_additional_usable_key);
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);
514 void PpapiDecryptor::OnSessionReady(const std::string& web_session_id) {
515 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
517 // TODO(jrummell): Calling ResumePlayback() here should not be necessary once
518 // OnSessionKeysChange() is called in all cases. http://crbug.com/413413.
520 session_ready_cb_.Run(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);
528 void PpapiDecryptor::OnSessionError(const std::string& web_session_id,
529 MediaKeys::Exception exception_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);
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();
544 if (!new_video_key_cb_.is_null())
545 new_video_key_cb_.Run();
548 void PpapiDecryptor::OnFatalPluginError() {
549 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
550 pepper_cdm_wrapper_.reset();
553 ContentDecryptorDelegate* PpapiDecryptor::CdmDelegate() {
554 DCHECK(render_loop_proxy_->BelongsToCurrentThread());
555 return (pepper_cdm_wrapper_) ? pepper_cdm_wrapper_->GetCdmDelegate() : NULL;
558 } // namespace content