1 // Copyright (c) 2012 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/pepper/content_decryptor_delegate.h" 6 7 #include "base/callback_helpers.h" 8 #include "base/debug/trace_event.h" 9 #include "base/message_loop/message_loop_proxy.h" 10 #include "base/numerics/safe_conversions.h" 11 #include "content/renderer/pepper/ppb_buffer_impl.h" 12 #include "media/base/audio_buffer.h" 13 #include "media/base/audio_decoder_config.h" 14 #include "media/base/bind_to_current_loop.h" 15 #include "media/base/cdm_promise.h" 16 #include "media/base/channel_layout.h" 17 #include "media/base/data_buffer.h" 18 #include "media/base/decoder_buffer.h" 19 #include "media/base/decrypt_config.h" 20 #include "media/base/video_decoder_config.h" 21 #include "media/base/video_frame.h" 22 #include "media/base/video_util.h" 23 #include "ppapi/shared_impl/scoped_pp_resource.h" 24 #include "ppapi/shared_impl/var.h" 25 #include "ppapi/shared_impl/var_tracker.h" 26 #include "ppapi/thunk/enter.h" 27 #include "ppapi/thunk/ppb_buffer_api.h" 28 #include "ui/gfx/rect.h" 29 30 using media::CdmPromise; 31 using media::Decryptor; 32 using media::MediaKeys; 33 using media::NewSessionCdmPromise; 34 using media::SimpleCdmPromise; 35 using ppapi::ArrayBufferVar; 36 using ppapi::PpapiGlobals; 37 using ppapi::ScopedPPResource; 38 using ppapi::StringVar; 39 using ppapi::thunk::EnterResourceNoLock; 40 using ppapi::thunk::PPB_Buffer_API; 41 42 namespace content { 43 44 namespace { 45 46 // Fills |resource| with a PPB_Buffer_Impl and copies |data| into the buffer 47 // resource. The |*resource|, if valid, will be in the ResourceTracker with a 48 // reference-count of 0. If |data| is NULL, sets |*resource| to NULL. Returns 49 // true upon success and false if any error happened. 50 bool MakeBufferResource(PP_Instance instance, 51 const uint8* data, 52 uint32_t size, 53 scoped_refptr<PPB_Buffer_Impl>* resource) { 54 TRACE_EVENT0("media", "ContentDecryptorDelegate - MakeBufferResource"); 55 DCHECK(resource); 56 57 if (!data || !size) { 58 DCHECK(!data && !size); 59 resource = NULL; 60 return true; 61 } 62 63 scoped_refptr<PPB_Buffer_Impl> buffer( 64 PPB_Buffer_Impl::CreateResource(instance, size)); 65 if (!buffer.get()) 66 return false; 67 68 BufferAutoMapper mapper(buffer.get()); 69 if (!mapper.data() || mapper.size() < size) 70 return false; 71 memcpy(mapper.data(), data, size); 72 73 *resource = buffer; 74 return true; 75 } 76 77 // Copies the content of |str| into |array|. 78 // Returns true if copy succeeded. Returns false if copy failed, e.g. if the 79 // |array_size| is smaller than the |str| length. 80 template <uint32_t array_size> 81 bool CopyStringToArray(const std::string& str, uint8 (&array)[array_size]) { 82 if (array_size < str.size()) 83 return false; 84 85 memcpy(array, str.data(), str.size()); 86 return true; 87 } 88 89 // Fills the |block_info| with information from |encrypted_buffer|. 90 // 91 // Returns true if |block_info| is successfully filled. Returns false 92 // otherwise. 93 static bool MakeEncryptedBlockInfo( 94 const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, 95 uint32_t request_id, 96 PP_EncryptedBlockInfo* block_info) { 97 // TODO(xhwang): Fix initialization of PP_EncryptedBlockInfo here and 98 // anywhere else. 99 memset(block_info, 0, sizeof(*block_info)); 100 block_info->tracking_info.request_id = request_id; 101 102 // EOS buffers need a request ID and nothing more. 103 if (encrypted_buffer->end_of_stream()) 104 return true; 105 106 DCHECK(encrypted_buffer->data_size()) 107 << "DecryptConfig is set on an empty buffer"; 108 109 block_info->tracking_info.timestamp = 110 encrypted_buffer->timestamp().InMicroseconds(); 111 block_info->data_size = encrypted_buffer->data_size(); 112 113 const media::DecryptConfig* decrypt_config = 114 encrypted_buffer->decrypt_config(); 115 116 if (!CopyStringToArray(decrypt_config->key_id(), block_info->key_id) || 117 !CopyStringToArray(decrypt_config->iv(), block_info->iv)) 118 return false; 119 120 block_info->key_id_size = decrypt_config->key_id().size(); 121 block_info->iv_size = decrypt_config->iv().size(); 122 123 if (decrypt_config->subsamples().size() > arraysize(block_info->subsamples)) 124 return false; 125 126 block_info->num_subsamples = decrypt_config->subsamples().size(); 127 for (uint32_t i = 0; i < block_info->num_subsamples; ++i) { 128 block_info->subsamples[i].clear_bytes = 129 decrypt_config->subsamples()[i].clear_bytes; 130 block_info->subsamples[i].cipher_bytes = 131 decrypt_config->subsamples()[i].cypher_bytes; 132 } 133 134 return true; 135 } 136 137 PP_AudioCodec MediaAudioCodecToPpAudioCodec(media::AudioCodec codec) { 138 switch (codec) { 139 case media::kCodecVorbis: 140 return PP_AUDIOCODEC_VORBIS; 141 case media::kCodecAAC: 142 return PP_AUDIOCODEC_AAC; 143 default: 144 return PP_AUDIOCODEC_UNKNOWN; 145 } 146 } 147 148 PP_VideoCodec MediaVideoCodecToPpVideoCodec(media::VideoCodec codec) { 149 switch (codec) { 150 case media::kCodecVP8: 151 return PP_VIDEOCODEC_VP8; 152 case media::kCodecH264: 153 return PP_VIDEOCODEC_H264; 154 case media::kCodecVP9: 155 return PP_VIDEOCODEC_VP9; 156 default: 157 return PP_VIDEOCODEC_UNKNOWN; 158 } 159 } 160 161 PP_VideoCodecProfile MediaVideoCodecProfileToPpVideoCodecProfile( 162 media::VideoCodecProfile profile) { 163 switch (profile) { 164 // TODO(xhwang): VP8 and VP9 do not have profiles. Clean up 165 // media::VideoCodecProfile and remove these two cases. 166 case media::VP8PROFILE_MAIN: 167 case media::VP9PROFILE_MAIN: 168 return PP_VIDEOCODECPROFILE_NOT_NEEDED; 169 case media::H264PROFILE_BASELINE: 170 return PP_VIDEOCODECPROFILE_H264_BASELINE; 171 case media::H264PROFILE_MAIN: 172 return PP_VIDEOCODECPROFILE_H264_MAIN; 173 case media::H264PROFILE_EXTENDED: 174 return PP_VIDEOCODECPROFILE_H264_EXTENDED; 175 case media::H264PROFILE_HIGH: 176 return PP_VIDEOCODECPROFILE_H264_HIGH; 177 case media::H264PROFILE_HIGH10PROFILE: 178 return PP_VIDEOCODECPROFILE_H264_HIGH_10; 179 case media::H264PROFILE_HIGH422PROFILE: 180 return PP_VIDEOCODECPROFILE_H264_HIGH_422; 181 case media::H264PROFILE_HIGH444PREDICTIVEPROFILE: 182 return PP_VIDEOCODECPROFILE_H264_HIGH_444_PREDICTIVE; 183 default: 184 return PP_VIDEOCODECPROFILE_UNKNOWN; 185 } 186 } 187 188 PP_DecryptedFrameFormat MediaVideoFormatToPpDecryptedFrameFormat( 189 media::VideoFrame::Format format) { 190 switch (format) { 191 case media::VideoFrame::YV12: 192 return PP_DECRYPTEDFRAMEFORMAT_YV12; 193 case media::VideoFrame::I420: 194 return PP_DECRYPTEDFRAMEFORMAT_I420; 195 default: 196 return PP_DECRYPTEDFRAMEFORMAT_UNKNOWN; 197 } 198 } 199 200 Decryptor::Status PpDecryptResultToMediaDecryptorStatus( 201 PP_DecryptResult result) { 202 switch (result) { 203 case PP_DECRYPTRESULT_SUCCESS: 204 return Decryptor::kSuccess; 205 case PP_DECRYPTRESULT_DECRYPT_NOKEY: 206 return Decryptor::kNoKey; 207 case PP_DECRYPTRESULT_NEEDMOREDATA: 208 return Decryptor::kNeedMoreData; 209 case PP_DECRYPTRESULT_DECRYPT_ERROR: 210 return Decryptor::kError; 211 case PP_DECRYPTRESULT_DECODE_ERROR: 212 return Decryptor::kError; 213 default: 214 NOTREACHED(); 215 return Decryptor::kError; 216 } 217 } 218 219 PP_DecryptorStreamType MediaDecryptorStreamTypeToPpStreamType( 220 Decryptor::StreamType stream_type) { 221 switch (stream_type) { 222 case Decryptor::kAudio: 223 return PP_DECRYPTORSTREAMTYPE_AUDIO; 224 case Decryptor::kVideo: 225 return PP_DECRYPTORSTREAMTYPE_VIDEO; 226 default: 227 NOTREACHED(); 228 return PP_DECRYPTORSTREAMTYPE_VIDEO; 229 } 230 } 231 232 media::SampleFormat PpDecryptedSampleFormatToMediaSampleFormat( 233 PP_DecryptedSampleFormat result) { 234 switch (result) { 235 case PP_DECRYPTEDSAMPLEFORMAT_U8: 236 return media::kSampleFormatU8; 237 case PP_DECRYPTEDSAMPLEFORMAT_S16: 238 return media::kSampleFormatS16; 239 case PP_DECRYPTEDSAMPLEFORMAT_S32: 240 return media::kSampleFormatS32; 241 case PP_DECRYPTEDSAMPLEFORMAT_F32: 242 return media::kSampleFormatF32; 243 case PP_DECRYPTEDSAMPLEFORMAT_PLANAR_S16: 244 return media::kSampleFormatPlanarS16; 245 case PP_DECRYPTEDSAMPLEFORMAT_PLANAR_F32: 246 return media::kSampleFormatPlanarF32; 247 default: 248 NOTREACHED(); 249 return media::kUnknownSampleFormat; 250 } 251 } 252 253 PP_SessionType MediaSessionTypeToPpSessionType( 254 MediaKeys::SessionType session_type) { 255 switch (session_type) { 256 case MediaKeys::TEMPORARY_SESSION: 257 return PP_SESSIONTYPE_TEMPORARY; 258 case MediaKeys::PERSISTENT_SESSION: 259 return PP_SESSIONTYPE_PERSISTENT; 260 default: 261 NOTREACHED(); 262 return PP_SESSIONTYPE_TEMPORARY; 263 } 264 } 265 266 MediaKeys::Exception PpExceptionTypeToMediaException( 267 PP_CdmExceptionCode exception_code) { 268 switch (exception_code) { 269 case PP_CDMEXCEPTIONCODE_NOTSUPPORTEDERROR: 270 return MediaKeys::NOT_SUPPORTED_ERROR; 271 case PP_CDMEXCEPTIONCODE_INVALIDSTATEERROR: 272 return MediaKeys::INVALID_STATE_ERROR; 273 case PP_CDMEXCEPTIONCODE_INVALIDACCESSERROR: 274 return MediaKeys::INVALID_ACCESS_ERROR; 275 case PP_CDMEXCEPTIONCODE_QUOTAEXCEEDEDERROR: 276 return MediaKeys::QUOTA_EXCEEDED_ERROR; 277 case PP_CDMEXCEPTIONCODE_UNKNOWNERROR: 278 return MediaKeys::UNKNOWN_ERROR; 279 case PP_CDMEXCEPTIONCODE_CLIENTERROR: 280 return MediaKeys::CLIENT_ERROR; 281 case PP_CDMEXCEPTIONCODE_OUTPUTERROR: 282 return MediaKeys::OUTPUT_ERROR; 283 default: 284 NOTREACHED(); 285 return MediaKeys::UNKNOWN_ERROR; 286 } 287 } 288 289 } // namespace 290 291 ContentDecryptorDelegate::ContentDecryptorDelegate( 292 PP_Instance pp_instance, 293 const PPP_ContentDecryptor_Private* plugin_decryption_interface) 294 : pp_instance_(pp_instance), 295 plugin_decryption_interface_(plugin_decryption_interface), 296 next_decryption_request_id_(1), 297 audio_samples_per_second_(0), 298 audio_channel_count_(0), 299 audio_channel_layout_(media::CHANNEL_LAYOUT_NONE), 300 next_promise_id_(1), 301 weak_ptr_factory_(this) { 302 weak_this_ = weak_ptr_factory_.GetWeakPtr(); 303 } 304 305 ContentDecryptorDelegate::~ContentDecryptorDelegate() { 306 SatisfyAllPendingCallbacksOnError(); 307 } 308 309 void ContentDecryptorDelegate::Initialize( 310 const std::string& key_system, 311 const media::SessionMessageCB& session_message_cb, 312 const media::SessionReadyCB& session_ready_cb, 313 const media::SessionClosedCB& session_closed_cb, 314 const media::SessionErrorCB& session_error_cb, 315 const base::Closure& fatal_plugin_error_cb) { 316 DCHECK(!key_system.empty()); 317 DCHECK(key_system_.empty()); 318 key_system_ = key_system; 319 320 session_message_cb_ = session_message_cb; 321 session_ready_cb_ = session_ready_cb; 322 session_closed_cb_ = session_closed_cb; 323 session_error_cb_ = session_error_cb; 324 fatal_plugin_error_cb_ = fatal_plugin_error_cb; 325 326 plugin_decryption_interface_->Initialize( 327 pp_instance_, StringVar::StringToPPVar(key_system_)); 328 } 329 330 void ContentDecryptorDelegate::InstanceCrashed() { 331 fatal_plugin_error_cb_.Run(); 332 SatisfyAllPendingCallbacksOnError(); 333 } 334 335 void ContentDecryptorDelegate::CreateSession( 336 const std::string& init_data_type, 337 const uint8* init_data, 338 int init_data_length, 339 MediaKeys::SessionType session_type, 340 scoped_ptr<NewSessionCdmPromise> promise) { 341 uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>()); 342 PP_Var init_data_array = 343 PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( 344 init_data_length, init_data); 345 plugin_decryption_interface_->CreateSession( 346 pp_instance_, 347 promise_id, 348 StringVar::StringToPPVar(init_data_type), 349 init_data_array, 350 MediaSessionTypeToPpSessionType(session_type)); 351 } 352 353 void ContentDecryptorDelegate::LoadSession( 354 const std::string& web_session_id, 355 scoped_ptr<NewSessionCdmPromise> promise) { 356 uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>()); 357 plugin_decryption_interface_->LoadSession( 358 pp_instance_, promise_id, StringVar::StringToPPVar(web_session_id)); 359 } 360 361 void ContentDecryptorDelegate::UpdateSession( 362 const std::string& web_session_id, 363 const uint8* response, 364 int response_length, 365 scoped_ptr<SimpleCdmPromise> promise) { 366 uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>()); 367 PP_Var response_array = 368 PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( 369 response_length, response); 370 plugin_decryption_interface_->UpdateSession( 371 pp_instance_, 372 promise_id, 373 StringVar::StringToPPVar(web_session_id), 374 response_array); 375 } 376 377 void ContentDecryptorDelegate::ReleaseSession( 378 const std::string& web_session_id, 379 scoped_ptr<SimpleCdmPromise> promise) { 380 uint32_t promise_id = SavePromise(promise.PassAs<CdmPromise>()); 381 plugin_decryption_interface_->ReleaseSession( 382 pp_instance_, promise_id, StringVar::StringToPPVar(web_session_id)); 383 } 384 385 // TODO(xhwang): Remove duplication of code in Decrypt(), 386 // DecryptAndDecodeAudio() and DecryptAndDecodeVideo(). 387 bool ContentDecryptorDelegate::Decrypt( 388 Decryptor::StreamType stream_type, 389 const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, 390 const Decryptor::DecryptCB& decrypt_cb) { 391 DVLOG(3) << "Decrypt() - stream_type: " << stream_type; 392 393 // |{audio|video}_input_resource_| is not being used by the plugin 394 // now because there is only one pending audio/video decrypt request at any 395 // time. This is enforced by the media pipeline. 396 scoped_refptr<PPB_Buffer_Impl> encrypted_resource; 397 if (!MakeMediaBufferResource( 398 stream_type, encrypted_buffer, &encrypted_resource) || 399 !encrypted_resource.get()) { 400 return false; 401 } 402 ScopedPPResource pp_resource(encrypted_resource.get()); 403 404 const uint32_t request_id = next_decryption_request_id_++; 405 DVLOG(2) << "Decrypt() - request_id " << request_id; 406 407 PP_EncryptedBlockInfo block_info = {}; 408 DCHECK(encrypted_buffer->decrypt_config()); 409 if (!MakeEncryptedBlockInfo(encrypted_buffer, request_id, &block_info)) { 410 return false; 411 } 412 413 // There is only one pending decrypt request at any time per stream. This is 414 // enforced by the media pipeline. 415 switch (stream_type) { 416 case Decryptor::kAudio: 417 audio_decrypt_cb_.Set(request_id, decrypt_cb); 418 break; 419 case Decryptor::kVideo: 420 video_decrypt_cb_.Set(request_id, decrypt_cb); 421 break; 422 default: 423 NOTREACHED(); 424 return false; 425 } 426 427 SetBufferToFreeInTrackingInfo(&block_info.tracking_info); 428 429 plugin_decryption_interface_->Decrypt(pp_instance_, pp_resource, &block_info); 430 return true; 431 } 432 433 bool ContentDecryptorDelegate::CancelDecrypt( 434 Decryptor::StreamType stream_type) { 435 DVLOG(3) << "CancelDecrypt() - stream_type: " << stream_type; 436 437 Decryptor::DecryptCB decrypt_cb; 438 switch (stream_type) { 439 case Decryptor::kAudio: 440 // Release the shared memory as it can still be in use by the plugin. 441 // The next Decrypt() call will need to allocate a new shared memory 442 // buffer. 443 audio_input_resource_ = NULL; 444 decrypt_cb = audio_decrypt_cb_.ResetAndReturn(); 445 break; 446 case Decryptor::kVideo: 447 // Release the shared memory as it can still be in use by the plugin. 448 // The next Decrypt() call will need to allocate a new shared memory 449 // buffer. 450 video_input_resource_ = NULL; 451 decrypt_cb = video_decrypt_cb_.ResetAndReturn(); 452 break; 453 default: 454 NOTREACHED(); 455 return false; 456 } 457 458 if (!decrypt_cb.is_null()) 459 decrypt_cb.Run(Decryptor::kSuccess, NULL); 460 461 return true; 462 } 463 464 bool ContentDecryptorDelegate::InitializeAudioDecoder( 465 const media::AudioDecoderConfig& decoder_config, 466 const Decryptor::DecoderInitCB& init_cb) { 467 PP_AudioDecoderConfig pp_decoder_config; 468 pp_decoder_config.codec = 469 MediaAudioCodecToPpAudioCodec(decoder_config.codec()); 470 pp_decoder_config.channel_count = 471 media::ChannelLayoutToChannelCount(decoder_config.channel_layout()); 472 pp_decoder_config.bits_per_channel = decoder_config.bits_per_channel(); 473 pp_decoder_config.samples_per_second = decoder_config.samples_per_second(); 474 pp_decoder_config.request_id = next_decryption_request_id_++; 475 476 audio_samples_per_second_ = pp_decoder_config.samples_per_second; 477 audio_channel_count_ = pp_decoder_config.channel_count; 478 audio_channel_layout_ = decoder_config.channel_layout(); 479 480 scoped_refptr<PPB_Buffer_Impl> extra_data_resource; 481 if (!MakeBufferResource(pp_instance_, 482 decoder_config.extra_data(), 483 decoder_config.extra_data_size(), 484 &extra_data_resource)) { 485 return false; 486 } 487 ScopedPPResource pp_resource(extra_data_resource.get()); 488 489 audio_decoder_init_cb_.Set(pp_decoder_config.request_id, init_cb); 490 plugin_decryption_interface_->InitializeAudioDecoder( 491 pp_instance_, &pp_decoder_config, pp_resource); 492 return true; 493 } 494 495 bool ContentDecryptorDelegate::InitializeVideoDecoder( 496 const media::VideoDecoderConfig& decoder_config, 497 const Decryptor::DecoderInitCB& init_cb) { 498 PP_VideoDecoderConfig pp_decoder_config; 499 pp_decoder_config.codec = 500 MediaVideoCodecToPpVideoCodec(decoder_config.codec()); 501 pp_decoder_config.profile = 502 MediaVideoCodecProfileToPpVideoCodecProfile(decoder_config.profile()); 503 pp_decoder_config.format = 504 MediaVideoFormatToPpDecryptedFrameFormat(decoder_config.format()); 505 pp_decoder_config.width = decoder_config.coded_size().width(); 506 pp_decoder_config.height = decoder_config.coded_size().height(); 507 pp_decoder_config.request_id = next_decryption_request_id_++; 508 509 scoped_refptr<PPB_Buffer_Impl> extra_data_resource; 510 if (!MakeBufferResource(pp_instance_, 511 decoder_config.extra_data(), 512 decoder_config.extra_data_size(), 513 &extra_data_resource)) { 514 return false; 515 } 516 ScopedPPResource pp_resource(extra_data_resource.get()); 517 518 video_decoder_init_cb_.Set(pp_decoder_config.request_id, init_cb); 519 natural_size_ = decoder_config.natural_size(); 520 521 plugin_decryption_interface_->InitializeVideoDecoder( 522 pp_instance_, &pp_decoder_config, pp_resource); 523 return true; 524 } 525 526 bool ContentDecryptorDelegate::DeinitializeDecoder( 527 Decryptor::StreamType stream_type) { 528 CancelDecode(stream_type); 529 530 if (stream_type == Decryptor::kVideo) 531 natural_size_ = gfx::Size(); 532 533 // TODO(tomfinegan): Add decoder deinitialize request tracking, and get 534 // stream type from media stack. 535 plugin_decryption_interface_->DeinitializeDecoder( 536 pp_instance_, MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); 537 return true; 538 } 539 540 bool ContentDecryptorDelegate::ResetDecoder(Decryptor::StreamType stream_type) { 541 CancelDecode(stream_type); 542 543 // TODO(tomfinegan): Add decoder reset request tracking. 544 plugin_decryption_interface_->ResetDecoder( 545 pp_instance_, MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); 546 return true; 547 } 548 549 bool ContentDecryptorDelegate::DecryptAndDecodeAudio( 550 const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, 551 const Decryptor::AudioDecodeCB& audio_decode_cb) { 552 // |audio_input_resource_| is not being used by the plugin now 553 // because there is only one pending audio decode request at any time. 554 // This is enforced by the media pipeline. 555 scoped_refptr<PPB_Buffer_Impl> encrypted_resource; 556 if (!MakeMediaBufferResource( 557 Decryptor::kAudio, encrypted_buffer, &encrypted_resource)) { 558 return false; 559 } 560 561 // The resource should not be NULL for non-EOS buffer. 562 if (!encrypted_buffer->end_of_stream() && !encrypted_resource.get()) 563 return false; 564 565 const uint32_t request_id = next_decryption_request_id_++; 566 DVLOG(2) << "DecryptAndDecodeAudio() - request_id " << request_id; 567 568 PP_EncryptedBlockInfo block_info = {}; 569 if (!MakeEncryptedBlockInfo(encrypted_buffer, request_id, &block_info)) { 570 return false; 571 } 572 573 SetBufferToFreeInTrackingInfo(&block_info.tracking_info); 574 575 // There is only one pending audio decode request at any time. This is 576 // enforced by the media pipeline. If this DCHECK is violated, our buffer 577 // reuse policy is not valid, and we may have race problems for the shared 578 // buffer. 579 audio_decode_cb_.Set(request_id, audio_decode_cb); 580 581 ScopedPPResource pp_resource(encrypted_resource.get()); 582 plugin_decryption_interface_->DecryptAndDecode( 583 pp_instance_, PP_DECRYPTORSTREAMTYPE_AUDIO, pp_resource, &block_info); 584 return true; 585 } 586 587 bool ContentDecryptorDelegate::DecryptAndDecodeVideo( 588 const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, 589 const Decryptor::VideoDecodeCB& video_decode_cb) { 590 // |video_input_resource_| is not being used by the plugin now 591 // because there is only one pending video decode request at any time. 592 // This is enforced by the media pipeline. 593 scoped_refptr<PPB_Buffer_Impl> encrypted_resource; 594 if (!MakeMediaBufferResource( 595 Decryptor::kVideo, encrypted_buffer, &encrypted_resource)) { 596 return false; 597 } 598 599 // The resource should not be 0 for non-EOS buffer. 600 if (!encrypted_buffer->end_of_stream() && !encrypted_resource.get()) 601 return false; 602 603 const uint32_t request_id = next_decryption_request_id_++; 604 DVLOG(2) << "DecryptAndDecodeVideo() - request_id " << request_id; 605 TRACE_EVENT_ASYNC_BEGIN0( 606 "media", "ContentDecryptorDelegate::DecryptAndDecodeVideo", request_id); 607 608 PP_EncryptedBlockInfo block_info = {}; 609 if (!MakeEncryptedBlockInfo(encrypted_buffer, request_id, &block_info)) { 610 return false; 611 } 612 613 SetBufferToFreeInTrackingInfo(&block_info.tracking_info); 614 615 // Only one pending video decode request at any time. This is enforced by the 616 // media pipeline. If this DCHECK is violated, our buffer 617 // reuse policy is not valid, and we may have race problems for the shared 618 // buffer. 619 video_decode_cb_.Set(request_id, video_decode_cb); 620 621 // TODO(tomfinegan): Need to get stream type from media stack. 622 ScopedPPResource pp_resource(encrypted_resource.get()); 623 plugin_decryption_interface_->DecryptAndDecode( 624 pp_instance_, PP_DECRYPTORSTREAMTYPE_VIDEO, pp_resource, &block_info); 625 return true; 626 } 627 628 void ContentDecryptorDelegate::OnPromiseResolved(uint32 promise_id) { 629 scoped_ptr<CdmPromise> promise = TakePromise(promise_id); 630 if (promise) { 631 SimpleCdmPromise* simple_promise( 632 static_cast<SimpleCdmPromise*>(promise.get())); 633 simple_promise->resolve(); 634 } 635 } 636 637 void ContentDecryptorDelegate::OnPromiseResolvedWithSession( 638 uint32 promise_id, 639 PP_Var web_session_id) { 640 scoped_ptr<CdmPromise> promise = TakePromise(promise_id); 641 642 StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id); 643 DCHECK(web_session_id_string); 644 645 if (promise) { 646 NewSessionCdmPromise* session_promise( 647 static_cast<NewSessionCdmPromise*>(promise.get())); 648 session_promise->resolve(web_session_id_string->value()); 649 } 650 } 651 652 void ContentDecryptorDelegate::OnPromiseRejected( 653 uint32 promise_id, 654 PP_CdmExceptionCode exception_code, 655 uint32 system_code, 656 PP_Var error_description) { 657 StringVar* error_description_string = StringVar::FromPPVar(error_description); 658 DCHECK(error_description_string); 659 660 scoped_ptr<CdmPromise> promise = TakePromise(promise_id); 661 if (promise) { 662 promise->reject(PpExceptionTypeToMediaException(exception_code), 663 system_code, 664 error_description_string->value()); 665 } 666 } 667 668 void ContentDecryptorDelegate::OnSessionMessage(PP_Var web_session_id, 669 PP_Var message, 670 PP_Var destination_url) { 671 if (session_message_cb_.is_null()) 672 return; 673 674 StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id); 675 DCHECK(web_session_id_string); 676 677 ArrayBufferVar* message_array_buffer = ArrayBufferVar::FromPPVar(message); 678 std::vector<uint8> message_vector; 679 if (message_array_buffer) { 680 const uint8* data = static_cast<const uint8*>(message_array_buffer->Map()); 681 message_vector.assign(data, data + message_array_buffer->ByteLength()); 682 } 683 684 StringVar* destination_url_string = StringVar::FromPPVar(destination_url); 685 DCHECK(destination_url_string); 686 687 GURL verified_gurl = GURL(destination_url_string->value()); 688 if (!verified_gurl.is_valid() && !verified_gurl.is_empty()) { 689 DLOG(WARNING) << "SessionMessage default_url is invalid : " 690 << verified_gurl.possibly_invalid_spec(); 691 verified_gurl = GURL::EmptyGURL(); // Replace invalid destination_url. 692 } 693 694 session_message_cb_.Run( 695 web_session_id_string->value(), message_vector, verified_gurl); 696 } 697 698 void ContentDecryptorDelegate::OnSessionReady(PP_Var web_session_id) { 699 if (session_ready_cb_.is_null()) 700 return; 701 702 StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id); 703 DCHECK(web_session_id_string); 704 705 session_ready_cb_.Run(web_session_id_string->value()); 706 } 707 708 void ContentDecryptorDelegate::OnSessionClosed(PP_Var web_session_id) { 709 if (session_closed_cb_.is_null()) 710 return; 711 712 StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id); 713 DCHECK(web_session_id_string); 714 715 session_closed_cb_.Run(web_session_id_string->value()); 716 } 717 718 void ContentDecryptorDelegate::OnSessionError( 719 PP_Var web_session_id, 720 PP_CdmExceptionCode exception_code, 721 uint32 system_code, 722 PP_Var error_description) { 723 if (session_error_cb_.is_null()) 724 return; 725 726 StringVar* web_session_id_string = StringVar::FromPPVar(web_session_id); 727 DCHECK(web_session_id_string); 728 729 StringVar* error_description_string = StringVar::FromPPVar(error_description); 730 DCHECK(error_description_string); 731 732 session_error_cb_.Run(web_session_id_string->value(), 733 PpExceptionTypeToMediaException(exception_code), 734 system_code, 735 error_description_string->value()); 736 } 737 738 void ContentDecryptorDelegate::DecoderInitializeDone( 739 PP_DecryptorStreamType decoder_type, 740 uint32_t request_id, 741 PP_Bool success) { 742 if (decoder_type == PP_DECRYPTORSTREAMTYPE_AUDIO) { 743 // If the request ID is not valid or does not match what's saved, do 744 // nothing. 745 if (request_id == 0 || !audio_decoder_init_cb_.Matches(request_id)) 746 return; 747 748 audio_decoder_init_cb_.ResetAndReturn().Run(PP_ToBool(success)); 749 } else { 750 if (request_id == 0 || !video_decoder_init_cb_.Matches(request_id)) 751 return; 752 753 if (!success) 754 natural_size_ = gfx::Size(); 755 756 video_decoder_init_cb_.ResetAndReturn().Run(PP_ToBool(success)); 757 } 758 } 759 760 void ContentDecryptorDelegate::DecoderDeinitializeDone( 761 PP_DecryptorStreamType decoder_type, 762 uint32_t request_id) { 763 // TODO(tomfinegan): Add decoder stop completion handling. 764 } 765 766 void ContentDecryptorDelegate::DecoderResetDone( 767 PP_DecryptorStreamType decoder_type, 768 uint32_t request_id) { 769 // TODO(tomfinegan): Add decoder reset completion handling. 770 } 771 772 void ContentDecryptorDelegate::DeliverBlock( 773 PP_Resource decrypted_block, 774 const PP_DecryptedBlockInfo* block_info) { 775 DCHECK(block_info); 776 777 FreeBuffer(block_info->tracking_info.buffer_id); 778 779 const uint32_t request_id = block_info->tracking_info.request_id; 780 DVLOG(2) << "DeliverBlock() - request_id: " << request_id; 781 782 // If the request ID is not valid or does not match what's saved, do nothing. 783 if (request_id == 0) { 784 DVLOG(1) << "DeliverBlock() - invalid request_id " << request_id; 785 return; 786 } 787 788 Decryptor::DecryptCB decrypt_cb; 789 if (audio_decrypt_cb_.Matches(request_id)) { 790 decrypt_cb = audio_decrypt_cb_.ResetAndReturn(); 791 } else if (video_decrypt_cb_.Matches(request_id)) { 792 decrypt_cb = video_decrypt_cb_.ResetAndReturn(); 793 } else { 794 DVLOG(1) << "DeliverBlock() - request_id " << request_id << " not found"; 795 return; 796 } 797 798 Decryptor::Status status = 799 PpDecryptResultToMediaDecryptorStatus(block_info->result); 800 if (status != Decryptor::kSuccess) { 801 decrypt_cb.Run(status, NULL); 802 return; 803 } 804 805 EnterResourceNoLock<PPB_Buffer_API> enter(decrypted_block, true); 806 if (!enter.succeeded()) { 807 decrypt_cb.Run(Decryptor::kError, NULL); 808 return; 809 } 810 BufferAutoMapper mapper(enter.object()); 811 if (!mapper.data() || !mapper.size() || 812 mapper.size() < block_info->data_size) { 813 decrypt_cb.Run(Decryptor::kError, NULL); 814 return; 815 } 816 817 // TODO(tomfinegan): Find a way to take ownership of the shared memory 818 // managed by the PPB_Buffer_Dev, and avoid the extra copy. 819 scoped_refptr<media::DecoderBuffer> decrypted_buffer( 820 media::DecoderBuffer::CopyFrom(static_cast<uint8*>(mapper.data()), 821 block_info->data_size)); 822 decrypted_buffer->set_timestamp( 823 base::TimeDelta::FromMicroseconds(block_info->tracking_info.timestamp)); 824 decrypt_cb.Run(Decryptor::kSuccess, decrypted_buffer); 825 } 826 827 // Use a non-class-member function here so that if for some reason 828 // ContentDecryptorDelegate is destroyed before VideoFrame calls this callback, 829 // we can still get the shared memory unmapped. 830 static void BufferNoLongerNeeded( 831 const scoped_refptr<PPB_Buffer_Impl>& ppb_buffer, 832 base::Closure buffer_no_longer_needed_cb) { 833 ppb_buffer->Unmap(); 834 buffer_no_longer_needed_cb.Run(); 835 } 836 837 // Enters |resource|, maps shared memory and returns pointer of mapped data. 838 // Returns NULL if any error occurs. 839 static uint8* GetMappedBuffer(PP_Resource resource, 840 scoped_refptr<PPB_Buffer_Impl>* ppb_buffer) { 841 EnterResourceNoLock<PPB_Buffer_API> enter(resource, true); 842 if (!enter.succeeded()) 843 return NULL; 844 845 uint8* mapped_data = static_cast<uint8*>(enter.object()->Map()); 846 if (!enter.object()->IsMapped() || !mapped_data) 847 return NULL; 848 849 uint32_t mapped_size = 0; 850 if (!enter.object()->Describe(&mapped_size) || !mapped_size) { 851 enter.object()->Unmap(); 852 return NULL; 853 } 854 855 *ppb_buffer = static_cast<PPB_Buffer_Impl*>(enter.object()); 856 857 return mapped_data; 858 } 859 860 void ContentDecryptorDelegate::DeliverFrame( 861 PP_Resource decrypted_frame, 862 const PP_DecryptedFrameInfo* frame_info) { 863 DCHECK(frame_info); 864 865 const uint32_t request_id = frame_info->tracking_info.request_id; 866 DVLOG(2) << "DeliverFrame() - request_id: " << request_id; 867 868 // If the request ID is not valid or does not match what's saved, do nothing. 869 if (request_id == 0 || !video_decode_cb_.Matches(request_id)) { 870 DVLOG(1) << "DeliverFrame() - request_id " << request_id << " not found"; 871 FreeBuffer(frame_info->tracking_info.buffer_id); 872 return; 873 } 874 875 TRACE_EVENT_ASYNC_END0( 876 "media", "ContentDecryptorDelegate::DecryptAndDecodeVideo", request_id); 877 878 Decryptor::VideoDecodeCB video_decode_cb = video_decode_cb_.ResetAndReturn(); 879 880 Decryptor::Status status = 881 PpDecryptResultToMediaDecryptorStatus(frame_info->result); 882 if (status != Decryptor::kSuccess) { 883 DCHECK(!frame_info->tracking_info.buffer_id); 884 video_decode_cb.Run(status, NULL); 885 return; 886 } 887 888 scoped_refptr<PPB_Buffer_Impl> ppb_buffer; 889 uint8* frame_data = GetMappedBuffer(decrypted_frame, &ppb_buffer); 890 if (!frame_data) { 891 FreeBuffer(frame_info->tracking_info.buffer_id); 892 video_decode_cb.Run(Decryptor::kError, NULL); 893 return; 894 } 895 896 gfx::Size frame_size(frame_info->width, frame_info->height); 897 DCHECK_EQ(frame_info->format, PP_DECRYPTEDFRAMEFORMAT_YV12); 898 899 scoped_refptr<media::VideoFrame> decoded_frame = 900 media::VideoFrame::WrapExternalYuvData( 901 media::VideoFrame::YV12, 902 frame_size, 903 gfx::Rect(frame_size), 904 natural_size_, 905 frame_info->strides[PP_DECRYPTEDFRAMEPLANES_Y], 906 frame_info->strides[PP_DECRYPTEDFRAMEPLANES_U], 907 frame_info->strides[PP_DECRYPTEDFRAMEPLANES_V], 908 frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_Y], 909 frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_U], 910 frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_V], 911 base::TimeDelta::FromMicroseconds( 912 frame_info->tracking_info.timestamp), 913 media::BindToCurrentLoop( 914 base::Bind(&BufferNoLongerNeeded, 915 ppb_buffer, 916 base::Bind(&ContentDecryptorDelegate::FreeBuffer, 917 weak_this_, 918 frame_info->tracking_info.buffer_id)))); 919 920 video_decode_cb.Run(Decryptor::kSuccess, decoded_frame); 921 } 922 923 void ContentDecryptorDelegate::DeliverSamples( 924 PP_Resource audio_frames, 925 const PP_DecryptedSampleInfo* sample_info) { 926 DCHECK(sample_info); 927 928 FreeBuffer(sample_info->tracking_info.buffer_id); 929 930 const uint32_t request_id = sample_info->tracking_info.request_id; 931 DVLOG(2) << "DeliverSamples() - request_id: " << request_id; 932 933 // If the request ID is not valid or does not match what's saved, do nothing. 934 if (request_id == 0 || !audio_decode_cb_.Matches(request_id)) { 935 DVLOG(1) << "DeliverSamples() - request_id " << request_id << " not found"; 936 return; 937 } 938 939 Decryptor::AudioDecodeCB audio_decode_cb = audio_decode_cb_.ResetAndReturn(); 940 941 const Decryptor::AudioBuffers empty_frames; 942 943 Decryptor::Status status = 944 PpDecryptResultToMediaDecryptorStatus(sample_info->result); 945 if (status != Decryptor::kSuccess) { 946 audio_decode_cb.Run(status, empty_frames); 947 return; 948 } 949 950 media::SampleFormat sample_format = 951 PpDecryptedSampleFormatToMediaSampleFormat(sample_info->format); 952 953 Decryptor::AudioBuffers audio_frame_list; 954 if (!DeserializeAudioFrames(audio_frames, 955 sample_info->data_size, 956 sample_format, 957 &audio_frame_list)) { 958 NOTREACHED() << "CDM did not serialize the buffer correctly."; 959 audio_decode_cb.Run(Decryptor::kError, empty_frames); 960 return; 961 } 962 963 audio_decode_cb.Run(Decryptor::kSuccess, audio_frame_list); 964 } 965 966 // TODO(xhwang): Try to remove duplicate logic here and in CancelDecrypt(). 967 void ContentDecryptorDelegate::CancelDecode(Decryptor::StreamType stream_type) { 968 switch (stream_type) { 969 case Decryptor::kAudio: 970 // Release the shared memory as it can still be in use by the plugin. 971 // The next DecryptAndDecode() call will need to allocate a new shared 972 // memory buffer. 973 audio_input_resource_ = NULL; 974 if (!audio_decode_cb_.is_null()) 975 audio_decode_cb_.ResetAndReturn().Run(Decryptor::kSuccess, 976 Decryptor::AudioBuffers()); 977 break; 978 case Decryptor::kVideo: 979 // Release the shared memory as it can still be in use by the plugin. 980 // The next DecryptAndDecode() call will need to allocate a new shared 981 // memory buffer. 982 video_input_resource_ = NULL; 983 if (!video_decode_cb_.is_null()) 984 video_decode_cb_.ResetAndReturn().Run(Decryptor::kSuccess, NULL); 985 break; 986 default: 987 NOTREACHED(); 988 } 989 } 990 991 bool ContentDecryptorDelegate::MakeMediaBufferResource( 992 Decryptor::StreamType stream_type, 993 const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, 994 scoped_refptr<PPB_Buffer_Impl>* resource) { 995 TRACE_EVENT0("media", "ContentDecryptorDelegate::MakeMediaBufferResource"); 996 997 // End of stream buffers are represented as null resources. 998 if (encrypted_buffer->end_of_stream()) { 999 *resource = NULL; 1000 return true; 1001 } 1002 1003 DCHECK(stream_type == Decryptor::kAudio || stream_type == Decryptor::kVideo); 1004 scoped_refptr<PPB_Buffer_Impl>& media_resource = 1005 (stream_type == Decryptor::kAudio) ? audio_input_resource_ 1006 : video_input_resource_; 1007 1008 const size_t data_size = static_cast<size_t>(encrypted_buffer->data_size()); 1009 if (!media_resource.get() || media_resource->size() < data_size) { 1010 // Either the buffer hasn't been created yet, or we have one that isn't big 1011 // enough to fit |size| bytes. 1012 1013 // Media resource size starts from |kMinimumMediaBufferSize| and grows 1014 // exponentially to avoid frequent re-allocation of PPB_Buffer_Impl, 1015 // which is usually expensive. Since input media buffers are compressed, 1016 // they are usually small (compared to outputs). The over-allocated memory 1017 // should be negligible. 1018 const uint32_t kMinimumMediaBufferSize = 1024; 1019 uint32_t media_resource_size = 1020 media_resource.get() ? media_resource->size() : kMinimumMediaBufferSize; 1021 while (media_resource_size < data_size) 1022 media_resource_size *= 2; 1023 1024 DVLOG(2) << "Size of media buffer for " 1025 << ((stream_type == Decryptor::kAudio) ? "audio" : "video") 1026 << " stream bumped to " << media_resource_size 1027 << " bytes to fit input."; 1028 media_resource = 1029 PPB_Buffer_Impl::CreateResource(pp_instance_, media_resource_size); 1030 if (!media_resource.get()) 1031 return false; 1032 } 1033 1034 BufferAutoMapper mapper(media_resource.get()); 1035 if (!mapper.data() || mapper.size() < data_size) { 1036 media_resource = NULL; 1037 return false; 1038 } 1039 memcpy(mapper.data(), encrypted_buffer->data(), data_size); 1040 1041 *resource = media_resource; 1042 return true; 1043 } 1044 1045 void ContentDecryptorDelegate::FreeBuffer(uint32_t buffer_id) { 1046 if (buffer_id) 1047 free_buffers_.push(buffer_id); 1048 } 1049 1050 void ContentDecryptorDelegate::SetBufferToFreeInTrackingInfo( 1051 PP_DecryptTrackingInfo* tracking_info) { 1052 DCHECK_EQ(tracking_info->buffer_id, 0u); 1053 1054 if (free_buffers_.empty()) 1055 return; 1056 1057 tracking_info->buffer_id = free_buffers_.front(); 1058 free_buffers_.pop(); 1059 } 1060 1061 bool ContentDecryptorDelegate::DeserializeAudioFrames( 1062 PP_Resource audio_frames, 1063 size_t data_size, 1064 media::SampleFormat sample_format, 1065 Decryptor::AudioBuffers* frames) { 1066 DCHECK(frames); 1067 EnterResourceNoLock<PPB_Buffer_API> enter(audio_frames, true); 1068 if (!enter.succeeded()) 1069 return false; 1070 1071 BufferAutoMapper mapper(enter.object()); 1072 if (!mapper.data() || !mapper.size() || 1073 mapper.size() < static_cast<uint32_t>(data_size)) 1074 return false; 1075 1076 // TODO(jrummell): Pass ownership of data() directly to AudioBuffer to avoid 1077 // the copy. Since it is possible to get multiple buffers, it would need to be 1078 // sliced and ref counted appropriately. http://crbug.com/255576. 1079 const uint8* cur = static_cast<uint8*>(mapper.data()); 1080 size_t bytes_left = data_size; 1081 1082 const int audio_bytes_per_frame = 1083 media::SampleFormatToBytesPerChannel(sample_format) * 1084 audio_channel_count_; 1085 if (audio_bytes_per_frame <= 0) 1086 return false; 1087 1088 // Allocate space for the channel pointers given to AudioBuffer. 1089 std::vector<const uint8*> channel_ptrs(audio_channel_count_, 1090 static_cast<const uint8*>(NULL)); 1091 do { 1092 int64 timestamp = 0; 1093 int64 frame_size = -1; 1094 const size_t kHeaderSize = sizeof(timestamp) + sizeof(frame_size); 1095 1096 if (bytes_left < kHeaderSize) 1097 return false; 1098 1099 memcpy(×tamp, cur, sizeof(timestamp)); 1100 cur += sizeof(timestamp); 1101 bytes_left -= sizeof(timestamp); 1102 1103 memcpy(&frame_size, cur, sizeof(frame_size)); 1104 cur += sizeof(frame_size); 1105 bytes_left -= sizeof(frame_size); 1106 1107 // We should *not* have empty frames in the list. 1108 if (frame_size <= 0 || 1109 bytes_left < base::checked_cast<size_t>(frame_size)) { 1110 return false; 1111 } 1112 1113 // Setup channel pointers. AudioBuffer::CopyFrom() will only use the first 1114 // one in the case of interleaved data. 1115 const int size_per_channel = frame_size / audio_channel_count_; 1116 for (int i = 0; i < audio_channel_count_; ++i) 1117 channel_ptrs[i] = cur + i * size_per_channel; 1118 1119 const int frame_count = frame_size / audio_bytes_per_frame; 1120 scoped_refptr<media::AudioBuffer> frame = media::AudioBuffer::CopyFrom( 1121 sample_format, 1122 audio_channel_layout_, 1123 audio_channel_count_, 1124 audio_samples_per_second_, 1125 frame_count, 1126 &channel_ptrs[0], 1127 base::TimeDelta::FromMicroseconds(timestamp)); 1128 frames->push_back(frame); 1129 1130 cur += frame_size; 1131 bytes_left -= frame_size; 1132 } while (bytes_left > 0); 1133 1134 return true; 1135 } 1136 1137 void ContentDecryptorDelegate::SatisfyAllPendingCallbacksOnError() { 1138 if (!audio_decoder_init_cb_.is_null()) 1139 audio_decoder_init_cb_.ResetAndReturn().Run(false); 1140 1141 if (!video_decoder_init_cb_.is_null()) 1142 video_decoder_init_cb_.ResetAndReturn().Run(false); 1143 1144 audio_input_resource_ = NULL; 1145 video_input_resource_ = NULL; 1146 1147 if (!audio_decrypt_cb_.is_null()) 1148 audio_decrypt_cb_.ResetAndReturn().Run(media::Decryptor::kError, NULL); 1149 1150 if (!video_decrypt_cb_.is_null()) 1151 video_decrypt_cb_.ResetAndReturn().Run(media::Decryptor::kError, NULL); 1152 1153 if (!audio_decode_cb_.is_null()) { 1154 const media::Decryptor::AudioBuffers empty_frames; 1155 audio_decode_cb_.ResetAndReturn().Run(media::Decryptor::kError, 1156 empty_frames); 1157 } 1158 1159 if (!video_decode_cb_.is_null()) 1160 video_decode_cb_.ResetAndReturn().Run(media::Decryptor::kError, NULL); 1161 1162 // Reject all outstanding promises. 1163 for (PromiseMap::iterator it = promises_.begin(); it != promises_.end(); 1164 ++it) { 1165 it->second->reject( 1166 media::MediaKeys::UNKNOWN_ERROR, 0, "Failure calling plugin."); 1167 } 1168 promises_.clear(); 1169 } 1170 1171 uint32_t ContentDecryptorDelegate::SavePromise(scoped_ptr<CdmPromise> promise) { 1172 uint32_t promise_id = next_promise_id_++; 1173 promises_.add(promise_id, promise.Pass()); 1174 return promise_id; 1175 } 1176 1177 scoped_ptr<CdmPromise> ContentDecryptorDelegate::TakePromise( 1178 uint32_t promise_id) { 1179 PromiseMap::iterator it = promises_.find(promise_id); 1180 if (it == promises_.end()) 1181 return scoped_ptr<CdmPromise>(); 1182 return promises_.take_and_erase(it); 1183 } 1184 1185 } // namespace content 1186