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 559