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 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 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 114 PpapiDecryptor::~PpapiDecryptor() { 115 pepper_cdm_wrapper_.reset(); 116 } 117 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 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 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 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 189 media::Decryptor* PpapiDecryptor::GetDecryptor() { 190 return this; 191 } 192 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 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 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 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 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 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 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 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 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 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 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 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 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 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 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 433 void PpapiDecryptor::OnFatalPluginError() { 434 DCHECK(render_loop_proxy_->BelongsToCurrentThread()); 435 pepper_cdm_wrapper_.reset(); 436 } 437 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