diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-24 22:33:54 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-24 22:33:54 +0000 |
commit | ecbb97687dd14622163c5ab5861fc6f8392e35c6 (patch) | |
tree | 3a08fb89e0da0801e006141f5740b5d0529930c6 /webkit/plugins | |
parent | 2bd7d9ec294434bc9819ee7cd553545c6eb765a3 (diff) | |
download | chromium_src-ecbb97687dd14622163c5ab5861fc6f8392e35c6.zip chromium_src-ecbb97687dd14622163c5ab5861fc6f8392e35c6.tar.gz chromium_src-ecbb97687dd14622163c5ab5861fc6f8392e35c6.tar.bz2 |
Update PluginInstance for audio support for content decryption.
BUG=123421
TEST=none
Review URL: https://chromiumcodereview.appspot.com/11189082
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@163931 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'webkit/plugins')
-rw-r--r-- | webkit/plugins/ppapi/ppapi_plugin_instance.cc | 292 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppapi_plugin_instance.h | 30 |
2 files changed, 277 insertions, 45 deletions
diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.cc b/webkit/plugins/ppapi/ppapi_plugin_instance.cc index beb44d2..31bae32 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.cc +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.cc @@ -17,6 +17,8 @@ #include "base/utf_string_conversions.h" // TODO(xhwang): Move media specific code out of this class. #include "media/base/audio_decoder_config.h" +#include "media/base/channel_layout.h" +#include "media/base/data_buffer.h" #include "media/base/decoder_buffer.h" #include "media/base/decryptor_client.h" #include "media/base/video_decoder_config.h" @@ -392,6 +394,55 @@ bool MakeEncryptedBlockInfo( return true; } +// Deserializes audio data stored in |audio_frames| into individual audio +// buffers in |frames|. Returns true upon success. +bool DeserializeAudioFrames(PP_Resource audio_frames, + media::Decryptor::AudioBuffers* frames) { + DCHECK(frames); + EnterResourceNoLock<PPB_Buffer_API> enter(audio_frames, true); + if (!enter.succeeded()) + return false; + + BufferAutoMapper mapper(enter.object()); + if (!mapper.data() || !mapper.size()) + return false; + + const uint8* cur = static_cast<uint8*>(mapper.data()); + int bytes_left = mapper.size(); + + do { + int64 timestamp = 0; + int64 frame_size = -1; + const int kHeaderSize = sizeof(timestamp) + sizeof(frame_size); + + if (bytes_left < kHeaderSize) + return false; + + timestamp = *(reinterpret_cast<const int64*>(cur)); + cur += sizeof(timestamp); + bytes_left -= sizeof(timestamp); + + frame_size = *(reinterpret_cast<const int64*>(cur)); + cur += sizeof(frame_size); + bytes_left -= sizeof(frame_size); + + // We should *not* have empty frame in the list. + if (frame_size <= 0 || bytes_left < frame_size) + return false; + + scoped_refptr<media::DataBuffer> frame(new media::DataBuffer(frame_size)); + frame->SetDataSize(frame_size); + memcpy(frame->GetWritableData(), cur, frame_size); + frame->SetTimestamp(base::TimeDelta::FromMicroseconds(timestamp)); + frames->push_back(frame); + + cur += frame_size; + bytes_left -= frame_size; + } while (bytes_left > 0); + + return true; +} + PP_AudioCodec MediaAudioCodecToPpAudioCodec(media::AudioCodec codec) { switch (codec) { case media::kCodecVorbis: @@ -451,6 +502,19 @@ media::Decryptor::Status PpDecryptResultToMediaDecryptorStatus( } } +PP_DecryptorStreamType MediaDecryptorStreamTypeToPpStreamType( + media::Decryptor::StreamType stream_type) { + switch (stream_type) { + case media::Decryptor::kAudio: + return PP_DECRYPTORSTREAMTYPE_AUDIO; + case media::Decryptor::kVideo: + return PP_DECRYPTORSTREAMTYPE_VIDEO; + default: + NOTREACHED(); + return PP_DECRYPTORSTREAMTYPE_VIDEO; + } +} + } // namespace // static @@ -525,8 +589,11 @@ PluginInstance::PluginInstance( flash_impl_(ALLOW_THIS_IN_INITIALIZER_LIST(this)), decryptor_client_(NULL), next_decryption_request_id_(1), + pending_audio_decrypt_request_id_(0), + pending_video_decrypt_request_id_(0), pending_audio_decoder_init_request_id_(0), pending_video_decoder_init_request_id_(0), + pending_audio_decode_request_id_(0), pending_video_decode_request_id_(0) { pp_instance_ = HostGlobals::Get()->AddInstance(this); @@ -1536,7 +1603,6 @@ void PluginInstance::RotateView(WebPlugin::RotationType type) { void PluginInstance::set_decrypt_client( media::DecryptorClient* decryptor_client) { - DCHECK(decryptor_client); decryptor_client_ = decryptor_client; } @@ -1588,10 +1654,13 @@ bool PluginInstance::CancelKeyRequest(const std::string& session_id) { return true; } +// TODO(xhwang): Remove duplication of code in Decrypt(), +// DecryptAndDecodeAudio() and DecryptAndDecodeVideo(). bool PluginInstance::Decrypt( + media::Decryptor::StreamType stream_type, const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, const media::Decryptor::DecryptCB& decrypt_cb) { - DVLOG(3) << "Decrypt()"; + DVLOG(3) << "Decrypt() - stream_type: " << stream_type; if (!LoadContentDecryptorInterface()) return false; @@ -1615,8 +1684,25 @@ bool PluginInstance::Decrypt( return false; } - DCHECK(!ContainsKey(pending_decryption_cbs_, request_id)); - pending_decryption_cbs_.insert(std::make_pair(request_id, decrypt_cb)); + // There is only one pending decrypt request at any time per stream. This is + // enforced by the media pipeline. + switch (stream_type) { + case media::Decryptor::kAudio: + DCHECK_EQ(pending_audio_decrypt_request_id_, 0u); + DCHECK(pending_audio_decrypt_cb_.is_null()); + pending_audio_decrypt_request_id_ = request_id; + pending_audio_decrypt_cb_ = decrypt_cb; + break; + case media::Decryptor::kVideo: + DCHECK_EQ(pending_video_decrypt_request_id_, 0u); + DCHECK(pending_video_decrypt_cb_.is_null()); + pending_video_decrypt_request_id_ = request_id; + pending_video_decrypt_cb_ = decrypt_cb; + break; + default: + NOTREACHED(); + return false; + } plugin_decryption_interface_->Decrypt(pp_instance(), encrypted_resource, @@ -1624,6 +1710,30 @@ bool PluginInstance::Decrypt( return true; } +bool PluginInstance::CancelDecrypt(media::Decryptor::StreamType stream_type) { + DVLOG(3) << "CancelDecrypt() - stream_type: " << stream_type; + + media::Decryptor::DecryptCB decrypt_cb; + switch (stream_type) { + case media::Decryptor::kAudio: + pending_audio_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_audio_decrypt_cb_); + break; + case media::Decryptor::kVideo: + pending_video_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_video_decrypt_cb_); + break; + default: + NOTREACHED(); + return false; + } + + if (!decrypt_cb.is_null()) + decrypt_cb.Run(media::Decryptor::kSuccess, NULL); + + return true; +} + bool PluginInstance::InitializeAudioDecoder( const media::AudioDecoderConfig& decoder_config, const media::Decryptor::DecoderInitCB& init_cb) { @@ -1684,32 +1794,96 @@ bool PluginInstance::InitializeVideoDecoder( return true; } -bool PluginInstance::DeinitializeDecoder() { +// TODO(xhwang): Try to remove duplicate logic here and in CancelDecrypt(). +void PluginInstance::CancelDecode(media::Decryptor::StreamType stream_type) { + switch (stream_type) { + case media::Decryptor::kAudio: + pending_audio_decode_request_id_ = 0; + if (!pending_audio_decode_cb_.is_null()) + base::ResetAndReturn(&pending_audio_decode_cb_).Run( + media::Decryptor::kSuccess, media::Decryptor::AudioBuffers()); + break; + case media::Decryptor::kVideo: + pending_video_decode_request_id_ = 0; + if (!pending_video_decode_cb_.is_null()) + base::ResetAndReturn(&pending_video_decode_cb_).Run( + media::Decryptor::kSuccess, NULL); + break; + default: + NOTREACHED(); + } +} + +bool PluginInstance::DeinitializeDecoder( + media::Decryptor::StreamType stream_type) { if (!LoadContentDecryptorInterface()) return false; + CancelDecode(stream_type); + // TODO(tomfinegan): Add decoder deinitialize request tracking, and get // stream type from media stack. plugin_decryption_interface_->DeinitializeDecoder( - pp_instance(), - PP_DECRYPTORSTREAMTYPE_VIDEO, - 0); + pp_instance(), MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); return true; } -bool PluginInstance::ResetDecoder() { +bool PluginInstance::ResetDecoder(media::Decryptor::StreamType stream_type) { if (!LoadContentDecryptorInterface()) return false; - // TODO(tomfinegan): Add decoder reset request tracking, and get - // stream type from media stack. - plugin_decryption_interface_->ResetDecoder(pp_instance(), - PP_DECRYPTORSTREAMTYPE_VIDEO, - 0); + CancelDecode(stream_type); + + // TODO(tomfinegan): Add decoder reset request tracking. + plugin_decryption_interface_->ResetDecoder( + pp_instance(), MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); return true; } -bool PluginInstance::DecryptAndDecode( +bool PluginInstance::DecryptAndDecodeAudio( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::AudioDecodeCB& audio_decode_cb) { + if (!LoadContentDecryptorInterface()) + return false; + + // If |encrypted_buffer| is end-of-stream buffer, GetData() and GetDataSize() + // return NULL and 0 respectively. In that case, we'll just create a 0 + // resource. + ScopedPPResource encrypted_resource( + ScopedPPResource::PassRef(), + MakeBufferResource(pp_instance(), + encrypted_buffer->GetData(), + encrypted_buffer->GetDataSize())); + if (!encrypted_buffer->IsEndOfStream() && !encrypted_resource.get()) + return false; + + const uint32_t request_id = next_decryption_request_id_++; + DVLOG(2) << "DecryptAndDecodeAudio() - request_id " << request_id; + + PP_EncryptedBlockInfo block_info; + if (!MakeEncryptedBlockInfo( + encrypted_buffer->GetDecryptConfig(), + encrypted_buffer->GetTimestamp().InMicroseconds(), + request_id, + &block_info)) { + return false; + } + + // There is only one pending audio decode request at any time. This is + // enforced by the media pipeline. + DCHECK_EQ(pending_audio_decode_request_id_, 0u); + DCHECK(pending_audio_decode_cb_.is_null()); + pending_audio_decode_request_id_ = request_id; + pending_audio_decode_cb_ = audio_decode_cb; + + plugin_decryption_interface_->DecryptAndDecode(pp_instance(), + PP_DECRYPTORSTREAMTYPE_AUDIO, + encrypted_resource, + &block_info); + return true; +} + +bool PluginInstance::DecryptAndDecodeVideo( const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, const media::Decryptor::VideoDecodeCB& video_decode_cb) { if (!LoadContentDecryptorInterface()) @@ -1727,7 +1901,7 @@ bool PluginInstance::DecryptAndDecode( return false; const uint32_t request_id = next_decryption_request_id_++; - DVLOG(2) << "DecryptAndDecode() - request_id " << request_id; + DVLOG(2) << "DecryptAndDecodeVideo() - request_id " << request_id; PP_EncryptedBlockInfo block_info; if (!MakeEncryptedBlockInfo( @@ -2378,6 +2552,9 @@ void PluginInstance::NeedKey(PP_Instance instance, void PluginInstance::KeyAdded(PP_Instance instance, PP_Var key_system_var, PP_Var session_id_var) { + if (!decryptor_client_) + return; + StringVar* key_system_string = StringVar::FromPPVar(key_system_var); StringVar* session_id_string = StringVar::FromPPVar(session_id_var); if (!key_system_string || !session_id_string) { @@ -2385,7 +2562,6 @@ void PluginInstance::KeyAdded(PP_Instance instance, return; } - DCHECK(decryptor_client_); decryptor_client_->KeyAdded(key_system_string->value(), session_id_string->value()); } @@ -2395,6 +2571,9 @@ void PluginInstance::KeyMessage(PP_Instance instance, PP_Var session_id_var, PP_Resource message_resource, PP_Var default_url_var) { + if (!decryptor_client_) + return; + StringVar* key_system_string = StringVar::FromPPVar(key_system_var); StringVar* session_id_string = StringVar::FromPPVar(session_id_var); StringVar* default_url_string = StringVar::FromPPVar(default_url_var); @@ -2418,7 +2597,6 @@ void PluginInstance::KeyMessage(PP_Instance instance, if (mapper.data() && mapper.size()) memcpy(message_array.get(), mapper.data(), mapper.size()); - DCHECK(decryptor_client_); decryptor_client_->KeyMessage(key_system_string->value(), session_id_string->value(), message_array.Pass(), @@ -2431,6 +2609,9 @@ void PluginInstance::KeyError(PP_Instance instance, PP_Var session_id_var, int32_t media_error, int32_t system_code) { + if (!decryptor_client_) + return; + StringVar* key_system_string = StringVar::FromPPVar(key_system_var); StringVar* session_id_string = StringVar::FromPPVar(session_id_var); if (!key_system_string || !session_id_string) { @@ -2438,7 +2619,6 @@ void PluginInstance::KeyError(PP_Instance instance, return; } - DCHECK(decryptor_client_); decryptor_client_->KeyError( key_system_string->value(), session_id_string->value(), @@ -2489,15 +2669,29 @@ void PluginInstance::DecoderResetDone(PP_Instance instance, void PluginInstance::DeliverBlock(PP_Instance instance, PP_Resource decrypted_block, const PP_DecryptedBlockInfo* block_info) { - DVLOG(2) << "DeliverBlock() - request_id: " - << block_info->tracking_info.request_id; DCHECK(block_info); - DecryptionCBMap::iterator found = pending_decryption_cbs_.find( - block_info->tracking_info.request_id); - if (found == pending_decryption_cbs_.end()) + const uint32_t request_id = block_info->tracking_info.request_id; + DVLOG(2) << "DeliverBlock() - request_id: " << request_id; + + // If the request ID is not valid or does not match what's saved, do nothing. + if (request_id == 0) { + DVLOG(1) << "DeliverBlock() - invalid request_id " << request_id; + return; + } + + media::Decryptor::DecryptCB decrypt_cb; + if (request_id == pending_audio_decrypt_request_id_) { + DCHECK(!pending_audio_decrypt_cb_.is_null()); + pending_audio_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_audio_decrypt_cb_); + } else if (request_id == pending_video_decrypt_request_id_) { + DCHECK(!pending_video_decrypt_cb_.is_null()); + pending_video_decrypt_request_id_ = 0; + decrypt_cb = base::ResetAndReturn(&pending_video_decrypt_cb_); + } else { + DVLOG(1) << "DeliverBlock() - request_id " << request_id << " not found"; return; - media::Decryptor::DecryptCB decrypt_cb = found->second; - pending_decryption_cbs_.erase(found); + } media::Decryptor::Status status = PpDecryptResultToMediaDecryptorStatus(block_info->result); @@ -2530,15 +2724,13 @@ void PluginInstance::DeliverBlock(PP_Instance instance, void PluginInstance::DeliverFrame(PP_Instance instance, PP_Resource decrypted_frame, const PP_DecryptedFrameInfo* frame_info) { - DVLOG(2) << "DeliverFrame() - request_id: " - << frame_info->tracking_info.request_id; DCHECK(frame_info); + const uint32_t request_id = frame_info->tracking_info.request_id; + DVLOG(2) << "DeliverFrame() - request_id: " << request_id; // If the request ID is not valid or does not match what's saved, do nothing. - if (frame_info->tracking_info.request_id == 0 || - frame_info->tracking_info.request_id != - pending_video_decode_request_id_) { - DCHECK(pending_video_decode_cb_.is_null()); + if (request_id == 0 || request_id != pending_video_decode_request_id_) { + DVLOG(1) << "DeliverFrame() - request_id " << request_id << " not found"; return; } @@ -2603,10 +2795,38 @@ void PluginInstance::DeliverFrame(PP_Instance instance, void PluginInstance::DeliverSamples(PP_Instance instance, PP_Resource audio_frames, const PP_DecryptedBlockInfo* block_info) { - DVLOG(2) << "DeliverSamples() - request_id: " - << block_info->tracking_info.request_id; - // TODO(tomfinegan): To be implemented after completion of v0.1 of the - // EME/CDM work. + DCHECK(block_info); + const uint32_t request_id = block_info->tracking_info.request_id; + DVLOG(2) << "DeliverSamples() - request_id: " << request_id; + + // If the request ID is not valid or does not match what's saved, do nothing. + if (request_id == 0 || request_id != pending_audio_decode_request_id_) { + DVLOG(1) << "DeliverSamples() - request_id " << request_id << " not found"; + return; + } + + DCHECK(!pending_audio_decode_cb_.is_null()); + pending_audio_decode_request_id_ = 0; + media::Decryptor::AudioDecodeCB audio_decode_cb = + base::ResetAndReturn(&pending_audio_decode_cb_); + + const media::Decryptor::AudioBuffers empty_frames; + + media::Decryptor::Status status = + PpDecryptResultToMediaDecryptorStatus(block_info->result); + if (status != media::Decryptor::kSuccess) { + audio_decode_cb.Run(status, empty_frames); + return; + } + + media::Decryptor::AudioBuffers audio_frame_list; + if (!DeserializeAudioFrames(audio_frames, &audio_frame_list)) { + NOTREACHED() << "CDM did not serialize the buffer correctly."; + audio_decode_cb.Run(media::Decryptor::kError, empty_frames); + return; + } + + audio_decode_cb.Run(media::Decryptor::kSuccess, audio_frame_list); } void PluginInstance::NumberOfFindResultsChanged(PP_Instance instance, diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.h b/webkit/plugins/ppapi/ppapi_plugin_instance.h index d7346f8..3bd03f4 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.h +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.h @@ -262,8 +262,10 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : const std::string& key, const std::string& init_data); bool CancelKeyRequest(const std::string& session_id); - bool Decrypt(const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + bool Decrypt(media::Decryptor::StreamType stream_type, + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, const media::Decryptor::DecryptCB& decrypt_cb); + bool CancelDecrypt(media::Decryptor::StreamType stream_type); bool InitializeAudioDecoder( const media::AudioDecoderConfig& decoder_config, const media::Decryptor::DecoderInitCB& decoder_init_cb); @@ -272,10 +274,13 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : const media::Decryptor::DecoderInitCB& decoder_init_cb); // TODO(tomfinegan): Add callback args for DeinitializeDecoder() and // ResetDecoder() - bool DeinitializeDecoder(); - bool ResetDecoder(); - // Note: This method can be used with an unencrypted frame. - bool DecryptAndDecode( + bool DeinitializeDecoder(media::Decryptor::StreamType stream_type); + bool ResetDecoder(media::Decryptor::StreamType stream_type); + // Note: These methods can be used with unencrypted data. + bool DecryptAndDecodeAudio( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::AudioDecodeCB& audio_decode_cb); + bool DecryptAndDecodeVideo( const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, const media::Decryptor::VideoDecodeCB& video_decode_cb); @@ -596,6 +601,9 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : void SetSizeAttributesForFullscreen(); void ResetSizeAttributesAfterFullscreen(); + // Cancels the pending decrypt-and-decode callback for |stream_type|. + void CancelDecode(media::Decryptor::StreamType stream_type); + PluginDelegate* delegate_; scoped_refptr<PluginModule> module_; scoped_ptr< ::ppapi::PPP_Instance_Combined> instance_interface_; @@ -792,10 +800,11 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : // of request IDs. uint32_t next_decryption_request_id_; - // TODO(xhwang): Use two separate callbacks for video and audio instead using - // a map here. - typedef std::map<uint32_t, media::Decryptor::DecryptCB> DecryptionCBMap; - DecryptionCBMap pending_decryption_cbs_; + uint32_t pending_audio_decrypt_request_id_; + media::Decryptor::DecryptCB pending_audio_decrypt_cb_; + + uint32_t pending_video_decrypt_request_id_; + media::Decryptor::DecryptCB pending_video_decrypt_cb_; uint32_t pending_audio_decoder_init_request_id_; media::Decryptor::DecoderInitCB pending_audio_decoder_init_cb_; @@ -803,6 +812,9 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : uint32_t pending_video_decoder_init_request_id_; media::Decryptor::DecoderInitCB pending_video_decoder_init_cb_; + uint32_t pending_audio_decode_request_id_; + media::Decryptor::AudioDecodeCB pending_audio_decode_cb_; + uint32_t pending_video_decode_request_id_; media::Decryptor::VideoDecodeCB pending_video_decode_cb_; |