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