diff options
-rw-r--r-- | webkit/glue/webkit_glue.gypi | 2 | ||||
-rw-r--r-- | webkit/media/crypto/ppapi_decryptor.cc | 58 | ||||
-rw-r--r-- | webkit/media/crypto/ppapi_decryptor.h | 14 | ||||
-rw-r--r-- | webkit/media/crypto/proxy_decryptor.cc | 2 | ||||
-rw-r--r-- | webkit/plugins/ppapi/content_decryptor_delegate.cc | 871 | ||||
-rw-r--r-- | webkit/plugins/ppapi/content_decryptor_delegate.h | 132 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppapi_plugin_instance.cc | 859 | ||||
-rw-r--r-- | webkit/plugins/ppapi/ppapi_plugin_instance.h | 84 |
8 files changed, 1100 insertions, 922 deletions
diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi index 4f39a61..c8e36f8 100644 --- a/webkit/glue/webkit_glue.gypi +++ b/webkit/glue/webkit_glue.gypi @@ -164,6 +164,8 @@ '../plugins/ppapi/audio_helper.cc', '../plugins/ppapi/audio_helper.h', '../plugins/ppapi/common.h', + '../plugins/ppapi/content_decryptor_delegate.cc', + '../plugins/ppapi/content_decryptor_delegate.h', '../plugins/ppapi/event_conversion.cc', '../plugins/ppapi/event_conversion.h', '../plugins/ppapi/file_callbacks.cc', diff --git a/webkit/media/crypto/ppapi_decryptor.cc b/webkit/media/crypto/ppapi_decryptor.cc index 5da0800..836f71d 100644 --- a/webkit/media/crypto/ppapi_decryptor.cc +++ b/webkit/media/crypto/ppapi_decryptor.cc @@ -18,25 +18,29 @@ #include "media/base/video_decoder_config.h" #include "media/base/video_frame.h" #include "webkit/media/crypto/key_systems.h" +#include "webkit/plugins/ppapi/content_decryptor_delegate.h" #include "webkit/plugins/ppapi/ppapi_plugin_instance.h" namespace webkit_media { PpapiDecryptor::PpapiDecryptor( - media::DecryptorClient* client, - const scoped_refptr<webkit::ppapi::PluginInstance>& plugin_instance) + media::DecryptorClient* client, + const scoped_refptr<webkit::ppapi::PluginInstance>& plugin_instance) : client_(client), - cdm_plugin_(plugin_instance), + plugin_instance_(plugin_instance), + plugin_cdm_delegate_(NULL), render_loop_proxy_(base::MessageLoopProxy::current()), weak_ptr_factory_(this), weak_this_(weak_ptr_factory_.GetWeakPtr()) { DCHECK(client_); - DCHECK(cdm_plugin_); - cdm_plugin_->set_decrypt_client(client); + DCHECK(plugin_instance_); } PpapiDecryptor::~PpapiDecryptor() { - cdm_plugin_->set_decrypt_client(NULL); + if (plugin_cdm_delegate_) + plugin_cdm_delegate_->set_decrypt_client(NULL); + plugin_cdm_delegate_ = NULL; + plugin_instance_ = NULL; } bool PpapiDecryptor::GenerateKeyRequest(const std::string& key_system, @@ -45,11 +49,19 @@ bool PpapiDecryptor::GenerateKeyRequest(const std::string& key_system, int init_data_length) { DVLOG(2) << "GenerateKeyRequest()"; DCHECK(render_loop_proxy_->BelongsToCurrentThread()); - DCHECK(cdm_plugin_); + + if (!plugin_cdm_delegate_) { + plugin_cdm_delegate_ = plugin_instance_->GetContentDecryptorDelegate(); + if (!plugin_cdm_delegate_) { + DVLOG(1) << "PpapiDecryptor: plugin cdm delegate creation failed."; + return false; + } + plugin_cdm_delegate_->set_decrypt_client(client_); + } // TODO(xhwang): Finalize the data type for |init_data| to avoid unnecessary // data type conversions. - if (!cdm_plugin_->GenerateKeyRequest( + if (!plugin_cdm_delegate_->GenerateKeyRequest( key_system, type, std::string(reinterpret_cast<const char*>(init_data), @@ -69,13 +81,12 @@ void PpapiDecryptor::AddKey(const std::string& key_system, const std::string& session_id) { DVLOG(2) << "AddKey()"; DCHECK(render_loop_proxy_->BelongsToCurrentThread()); - DCHECK(cdm_plugin_); - if (!cdm_plugin_->AddKey(session_id, - std::string(reinterpret_cast<const char*>(key), - key_length), - std::string(reinterpret_cast<const char*>(init_data), - init_data_length))) { + if (!plugin_cdm_delegate_->AddKey( + session_id, + std::string(reinterpret_cast<const char*>(key), key_length), + std::string(reinterpret_cast<const char*>(init_data), + init_data_length))) { ReportFailureToCallPlugin(key_system, session_id); } @@ -90,9 +101,8 @@ void PpapiDecryptor::CancelKeyRequest(const std::string& key_system, const std::string& session_id) { DVLOG(2) << "CancelKeyRequest()"; DCHECK(render_loop_proxy_->BelongsToCurrentThread()); - DCHECK(cdm_plugin_); - if (!cdm_plugin_->CancelKeyRequest(session_id)) + if (!plugin_cdm_delegate_->CancelKeyRequest(session_id)) ReportFailureToCallPlugin(key_system, session_id); } @@ -122,13 +132,13 @@ void PpapiDecryptor::Decrypt( } DVLOG(3) << "Decrypt() - stream_type: " << stream_type; - if (!cdm_plugin_->Decrypt(stream_type, encrypted, decrypt_cb)) + if (!plugin_cdm_delegate_->Decrypt(stream_type, encrypted, decrypt_cb)) decrypt_cb.Run(kError, NULL); } void PpapiDecryptor::CancelDecrypt(StreamType stream_type) { DVLOG(1) << "CancelDecrypt() - stream_type: " << stream_type; - cdm_plugin_->CancelDecrypt(stream_type); + plugin_cdm_delegate_->CancelDecrypt(stream_type); } void PpapiDecryptor::InitializeAudioDecoder( @@ -146,7 +156,7 @@ void PpapiDecryptor::InitializeAudioDecoder( DCHECK(config->IsValidConfig()); audio_decoder_init_cb_ = init_cb; - if (!cdm_plugin_->InitializeAudioDecoder(*config, base::Bind( + if (!plugin_cdm_delegate_->InitializeAudioDecoder(*config, base::Bind( &PpapiDecryptor::OnDecoderInitialized, weak_this_, kAudio))) { base::ResetAndReturn(&audio_decoder_init_cb_).Run(false); return; @@ -168,7 +178,7 @@ void PpapiDecryptor::InitializeVideoDecoder( DCHECK(config->IsValidConfig()); video_decoder_init_cb_ = init_cb; - if (!cdm_plugin_->InitializeVideoDecoder(*config, base::Bind( + if (!plugin_cdm_delegate_->InitializeVideoDecoder(*config, base::Bind( &PpapiDecryptor::OnDecoderInitialized, weak_this_, kVideo))) { base::ResetAndReturn(&video_decoder_init_cb_).Run(false); return; @@ -186,7 +196,7 @@ void PpapiDecryptor::DecryptAndDecodeAudio( } DVLOG(1) << "DecryptAndDecodeAudio()"; - if (!cdm_plugin_->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) + if (!plugin_cdm_delegate_->DecryptAndDecodeAudio(encrypted, audio_decode_cb)) audio_decode_cb.Run(kError, AudioBuffers()); } @@ -201,7 +211,7 @@ void PpapiDecryptor::DecryptAndDecodeVideo( } DVLOG(3) << "DecryptAndDecodeVideo()"; - if (!cdm_plugin_->DecryptAndDecodeVideo(encrypted, video_decode_cb)) + if (!plugin_cdm_delegate_->DecryptAndDecodeVideo(encrypted, video_decode_cb)) video_decode_cb.Run(kError, NULL); } @@ -213,7 +223,7 @@ void PpapiDecryptor::ResetDecoder(StreamType stream_type) { } DVLOG(2) << "ResetDecoder() - stream_type: " << stream_type; - cdm_plugin_->ResetDecoder(stream_type); + plugin_cdm_delegate_->ResetDecoder(stream_type); } void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) { @@ -224,7 +234,7 @@ void PpapiDecryptor::DeinitializeDecoder(StreamType stream_type) { } DVLOG(2) << "DeinitializeDecoder() - stream_type: " << stream_type; - cdm_plugin_->DeinitializeDecoder(stream_type); + plugin_cdm_delegate_->DeinitializeDecoder(stream_type); } void PpapiDecryptor::ReportFailureToCallPlugin(const std::string& key_system, diff --git a/webkit/media/crypto/ppapi_decryptor.h b/webkit/media/crypto/ppapi_decryptor.h index 4ac5c1e..63b536b 100644 --- a/webkit/media/crypto/ppapi_decryptor.h +++ b/webkit/media/crypto/ppapi_decryptor.h @@ -22,6 +22,7 @@ class DecryptorClient; namespace webkit { namespace ppapi { +class ContentDecryptorDelegate; class PluginInstance; } } @@ -34,8 +35,8 @@ namespace webkit_media { class PpapiDecryptor : public media::Decryptor { public: PpapiDecryptor( - media::DecryptorClient* client, - const scoped_refptr<webkit::ppapi::PluginInstance>& plugin_instance); + media::DecryptorClient* client, + const scoped_refptr<webkit::ppapi::PluginInstance>& plugin_instance); virtual ~PpapiDecryptor(); // media::Decryptor implementation. @@ -79,7 +80,14 @@ class PpapiDecryptor : public media::Decryptor { void OnDecoderInitialized(StreamType stream_type, bool success); media::DecryptorClient* client_; - scoped_refptr<webkit::ppapi::PluginInstance> cdm_plugin_; + + // Hold a reference of the plugin instance to make sure the plugin outlives + // the |plugin_cdm_delegate_|. This is needed because |plugin_cdm_delegate_| + // is owned by the |plugin_instance_|. + scoped_refptr<webkit::ppapi::PluginInstance> plugin_instance_; + + webkit::ppapi::ContentDecryptorDelegate* plugin_cdm_delegate_; + scoped_refptr<base::MessageLoopProxy> render_loop_proxy_; DecoderInitCB audio_decoder_init_cb_; diff --git a/webkit/media/crypto/proxy_decryptor.cc b/webkit/media/crypto/proxy_decryptor.cc index 12dd3b6..ddf186a 100644 --- a/webkit/media/crypto/proxy_decryptor.cc +++ b/webkit/media/crypto/proxy_decryptor.cc @@ -257,7 +257,7 @@ scoped_ptr<media::Decryptor> ProxyDecryptor::CreatePpapiDecryptor( const scoped_refptr<webkit::ppapi::PluginInstance>& plugin_instance = CreatePluginInstance(plugin_type, web_media_player_client_, web_frame_); if (!plugin_instance) { - DVLOG(1) << "PpapiDecryptor: plugin instance creation failed."; + DVLOG(1) << "ProxyDecryptor: plugin instance creation failed."; return scoped_ptr<media::Decryptor>(); } diff --git a/webkit/plugins/ppapi/content_decryptor_delegate.cc b/webkit/plugins/ppapi/content_decryptor_delegate.cc new file mode 100644 index 0000000..3c00865 --- /dev/null +++ b/webkit/plugins/ppapi/content_decryptor_delegate.cc @@ -0,0 +1,871 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "webkit/plugins/ppapi/content_decryptor_delegate.h" + +#include "base/callback_helpers.h" +#include "base/debug/trace_event.h" +#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" +#include "media/base/video_frame.h" +#include "media/base/video_util.h" +#include "ppapi/shared_impl/scoped_pp_resource.h" +#include "ppapi/shared_impl/var.h" +#include "ppapi/shared_impl/var_tracker.h" +#include "ppapi/thunk/enter.h" +#include "ppapi/thunk/ppb_buffer_api.h" +#include "webkit/plugins/ppapi/ppb_buffer_impl.h" + +using ppapi::PpapiGlobals; +using ppapi::ScopedPPResource; +using ppapi::StringVar; +using ppapi::thunk::EnterResourceNoLock; +using ppapi::thunk::PPB_Buffer_API; + +namespace webkit { +namespace ppapi { + +namespace { + +// Creates a PP_Resource containing a PPB_Buffer_Impl, copies |data| into the +// buffer resource, and returns it. Returns a an invalid PP_Resource with an ID +// of 0 on failure. Upon success, the returned Buffer resource has a reference +// count of 1. +PP_Resource MakeBufferResource(PP_Instance instance, + const uint8* data, int size) { + if (!data || !size) + return 0; + + ScopedPPResource resource(PPB_Buffer_Impl::Create(instance, size)); + if (!resource.get()) + return 0; + + EnterResourceNoLock<PPB_Buffer_API> enter(resource, true); + if (enter.failed()) + return 0; + + BufferAutoMapper mapper(enter.object()); + if (!mapper.data() || mapper.size() < static_cast<size_t>(size)) + return 0; + + memcpy(mapper.data(), data, size); + return resource.get(); +} + +// Copies the content of |str| into |array|. +// Returns true if copy succeeded. Returns false if copy failed, e.g. if the +// |array_size| is smaller than the |str| length. +template <uint32_t array_size> +bool CopyStringToArray(const std::string& str, uint8 (&array)[array_size]) { + if (array_size < str.size()) + return false; + + memcpy(array, str.data(), str.size()); + return true; +} + +// Fills the |block_info| with information from |decrypt_config|, |timestamp| +// and |request_id|. |decrypt_config| can be NULL if the block is not encrypted. +// This is useful for end-of-stream blocks. +// Returns true if |block_info| is successfully filled. Returns false +// otherwise. +bool MakeEncryptedBlockInfo( + const media::DecryptConfig* decrypt_config, + int64_t timestamp, + uint32_t request_id, + PP_EncryptedBlockInfo* block_info) { + DCHECK(block_info); + + // TODO(xhwang): Fix initialization of PP_EncryptedBlockInfo here and + // anywhere else. + memset(block_info, 0, sizeof(*block_info)); + + block_info->tracking_info.request_id = request_id; + block_info->tracking_info.timestamp = timestamp; + + if (!decrypt_config) + return true; + + block_info->data_offset = decrypt_config->data_offset(); + + if (!CopyStringToArray(decrypt_config->key_id(), block_info->key_id) || + !CopyStringToArray(decrypt_config->iv(), block_info->iv)) + return false; + + block_info->key_id_size = decrypt_config->key_id().size(); + block_info->iv_size = decrypt_config->iv().size(); + + if (decrypt_config->subsamples().size() > arraysize(block_info->subsamples)) + return false; + + block_info->num_subsamples = decrypt_config->subsamples().size(); + for (uint32_t i = 0; i < block_info->num_subsamples; ++i) { + block_info->subsamples[i].clear_bytes = + decrypt_config->subsamples()[i].clear_bytes; + block_info->subsamples[i].cipher_bytes = + decrypt_config->subsamples()[i].cypher_bytes; + } + + 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; + + memcpy(×tamp, cur, sizeof(timestamp)); + cur += sizeof(timestamp); + bytes_left -= sizeof(timestamp); + + memcpy(&frame_size, cur, sizeof(frame_size)); + 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(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: + return PP_AUDIOCODEC_VORBIS; + case media::kCodecAAC: + return PP_AUDIOCODEC_AAC; + default: + return PP_AUDIOCODEC_UNKNOWN; + } +} + +PP_VideoCodec MediaVideoCodecToPpVideoCodec(media::VideoCodec codec) { + switch (codec) { + case media::kCodecVP8: + return PP_VIDEOCODEC_VP8; + case media::kCodecH264: + return PP_VIDEOCODEC_H264; + default: + return PP_VIDEOCODEC_UNKNOWN; + } +} + +PP_VideoCodecProfile MediaVideoCodecProfileToPpVideoCodecProfile( + media::VideoCodecProfile profile) { + switch (profile) { + case media::VP8PROFILE_MAIN: + return PP_VIDEOCODECPROFILE_VP8_MAIN; + case media::H264PROFILE_BASELINE: + return PP_VIDEOCODECPROFILE_H264_BASELINE; + case media::H264PROFILE_MAIN: + return PP_VIDEOCODECPROFILE_H264_MAIN; + case media::H264PROFILE_EXTENDED: + return PP_VIDEOCODECPROFILE_H264_EXTENDED; + case media::H264PROFILE_HIGH: + return PP_VIDEOCODECPROFILE_H264_HIGH; + case media::H264PROFILE_HIGH10PROFILE: + return PP_VIDEOCODECPROFILE_H264_HIGH_10; + case media::H264PROFILE_HIGH422PROFILE: + return PP_VIDEOCODECPROFILE_H264_HIGH_422; + case media::H264PROFILE_HIGH444PREDICTIVEPROFILE: + return PP_VIDEOCODECPROFILE_H264_HIGH_444_PREDICTIVE; + default: + return PP_VIDEOCODECPROFILE_UNKNOWN; + } +} + +PP_DecryptedFrameFormat MediaVideoFormatToPpDecryptedFrameFormat( + media::VideoFrame::Format format) { + switch (format) { + case media::VideoFrame::YV12: + return PP_DECRYPTEDFRAMEFORMAT_YV12; + case media::VideoFrame::I420: + return PP_DECRYPTEDFRAMEFORMAT_I420; + default: + return PP_DECRYPTEDFRAMEFORMAT_UNKNOWN; + } +} + +media::Decryptor::Status PpDecryptResultToMediaDecryptorStatus( + PP_DecryptResult result) { + switch (result) { + case PP_DECRYPTRESULT_SUCCESS: + return media::Decryptor::kSuccess; + case PP_DECRYPTRESULT_DECRYPT_NOKEY: + return media::Decryptor::kNoKey; + case PP_DECRYPTRESULT_NEEDMOREDATA: + return media::Decryptor::kNeedMoreData; + case PP_DECRYPTRESULT_DECRYPT_ERROR: + return media::Decryptor::kError; + case PP_DECRYPTRESULT_DECODE_ERROR: + return media::Decryptor::kError; + default: + NOTREACHED(); + return media::Decryptor::kError; + } +} + +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 + +ContentDecryptorDelegate::ContentDecryptorDelegate( + PP_Instance pp_instance, + const PPP_ContentDecryptor_Private* plugin_decryption_interface) + : pp_instance_(pp_instance), + plugin_decryption_interface_(plugin_decryption_interface), + 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) { +} + +void ContentDecryptorDelegate::set_decrypt_client( + media::DecryptorClient* decryptor_client) { + decryptor_client_ = decryptor_client; +} + +bool ContentDecryptorDelegate::GenerateKeyRequest( + const std::string& key_system, + const std::string& type, + const std::string& init_data) { + if (key_system.empty()) + return false; + + PP_Var init_data_array = + PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( + init_data.size(), init_data.data()); + + plugin_decryption_interface_->GenerateKeyRequest( + pp_instance_, + StringVar::StringToPPVar(key_system), + StringVar::StringToPPVar(type), + init_data_array); + return true; +} + +bool ContentDecryptorDelegate::AddKey(const std::string& session_id, + const std::string& key, + const std::string& init_data) { + PP_Var key_array = + PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(key.size(), + key.data()); + PP_Var init_data_array = + PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( + init_data.size(), + init_data.data()); + + plugin_decryption_interface_->AddKey( + pp_instance_, + StringVar::StringToPPVar(session_id), + key_array, + init_data_array); + return true; +} + +bool ContentDecryptorDelegate::CancelKeyRequest(const std::string& session_id) { + plugin_decryption_interface_->CancelKeyRequest( + pp_instance_, + StringVar::StringToPPVar(session_id)); + return true; +} + +// TODO(xhwang): Remove duplication of code in Decrypt(), +// DecryptAndDecodeAudio() and DecryptAndDecodeVideo(). +bool ContentDecryptorDelegate::Decrypt( + media::Decryptor::StreamType stream_type, + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::DecryptCB& decrypt_cb) { + DVLOG(3) << "Decrypt() - stream_type: " << stream_type; + ScopedPPResource encrypted_resource( + ScopedPPResource::PassRef(), + MakeBufferResource(pp_instance_, + encrypted_buffer->GetData(), + encrypted_buffer->GetDataSize())); + if (!encrypted_resource.get()) + return false; + + const uint32_t request_id = next_decryption_request_id_++; + DVLOG(2) << "Decrypt() - request_id " << request_id; + + PP_EncryptedBlockInfo block_info; + DCHECK(encrypted_buffer->GetDecryptConfig()); + if (!MakeEncryptedBlockInfo(encrypted_buffer->GetDecryptConfig(), + encrypted_buffer->GetTimestamp().InMicroseconds(), + request_id, + &block_info)) { + return false; + } + + // 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, + &block_info); + return true; +} + +bool ContentDecryptorDelegate::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 ContentDecryptorDelegate::InitializeAudioDecoder( + const media::AudioDecoderConfig& decoder_config, + const media::Decryptor::DecoderInitCB& init_cb) { + PP_AudioDecoderConfig pp_decoder_config; + pp_decoder_config.codec = + MediaAudioCodecToPpAudioCodec(decoder_config.codec()); + pp_decoder_config.channel_count = + media::ChannelLayoutToChannelCount(decoder_config.channel_layout()); + pp_decoder_config.bits_per_channel = decoder_config.bits_per_channel(); + pp_decoder_config.samples_per_second = decoder_config.samples_per_second(); + pp_decoder_config.request_id = next_decryption_request_id_++; + + ScopedPPResource extra_data_resource( + ScopedPPResource::PassRef(), + MakeBufferResource(pp_instance_, + decoder_config.extra_data(), + decoder_config.extra_data_size())); + + DCHECK_EQ(pending_audio_decoder_init_request_id_, 0u); + DCHECK(pending_audio_decoder_init_cb_.is_null()); + pending_audio_decoder_init_request_id_ = pp_decoder_config.request_id; + pending_audio_decoder_init_cb_ = init_cb; + + plugin_decryption_interface_->InitializeAudioDecoder(pp_instance_, + &pp_decoder_config, + extra_data_resource); + return true; +} + +bool ContentDecryptorDelegate::InitializeVideoDecoder( + const media::VideoDecoderConfig& decoder_config, + const media::Decryptor::DecoderInitCB& init_cb) { + PP_VideoDecoderConfig pp_decoder_config; + pp_decoder_config.codec = + MediaVideoCodecToPpVideoCodec(decoder_config.codec()); + pp_decoder_config.profile = + MediaVideoCodecProfileToPpVideoCodecProfile(decoder_config.profile()); + pp_decoder_config.format = + MediaVideoFormatToPpDecryptedFrameFormat(decoder_config.format()); + pp_decoder_config.width = decoder_config.coded_size().width(); + pp_decoder_config.height = decoder_config.coded_size().height(); + pp_decoder_config.request_id = next_decryption_request_id_++; + + ScopedPPResource extra_data_resource( + ScopedPPResource::PassRef(), + MakeBufferResource(pp_instance_, + decoder_config.extra_data(), + decoder_config.extra_data_size())); + + DCHECK_EQ(pending_video_decoder_init_request_id_, 0u); + DCHECK(pending_video_decoder_init_cb_.is_null()); + pending_video_decoder_init_request_id_ = pp_decoder_config.request_id; + pending_video_decoder_init_cb_ = init_cb; + + plugin_decryption_interface_->InitializeVideoDecoder(pp_instance_, + &pp_decoder_config, + extra_data_resource); + return true; +} + +bool ContentDecryptorDelegate::DeinitializeDecoder( + media::Decryptor::StreamType stream_type) { + CancelDecode(stream_type); + + // TODO(tomfinegan): Add decoder deinitialize request tracking, and get + // stream type from media stack. + plugin_decryption_interface_->DeinitializeDecoder( + pp_instance_, MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); + return true; +} + +bool ContentDecryptorDelegate::ResetDecoder( + media::Decryptor::StreamType stream_type) { + CancelDecode(stream_type); + + // TODO(tomfinegan): Add decoder reset request tracking. + plugin_decryption_interface_->ResetDecoder( + pp_instance_, MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); + return true; +} + +bool ContentDecryptorDelegate::DecryptAndDecodeAudio( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::AudioDecodeCB& audio_decode_cb) { + // 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 ContentDecryptorDelegate::DecryptAndDecodeVideo( + const scoped_refptr<media::DecoderBuffer>& encrypted_buffer, + const media::Decryptor::VideoDecodeCB& video_decode_cb) { + // 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) << "DecryptAndDecodeVideo() - request_id " << request_id; + TRACE_EVENT_ASYNC_BEGIN0( + "eme", "PluginInstance::DecryptAndDecodeVideo", request_id); + + PP_EncryptedBlockInfo block_info; + if (!MakeEncryptedBlockInfo( + encrypted_buffer->GetDecryptConfig(), + encrypted_buffer->GetTimestamp().InMicroseconds(), + request_id, + &block_info)) { + return false; + } + + // Only one pending video decode request at any time. This is enforced by the + // media pipeline. + DCHECK_EQ(pending_video_decode_request_id_, 0u); + DCHECK(pending_video_decode_cb_.is_null()); + pending_video_decode_request_id_ = request_id; + pending_video_decode_cb_ = video_decode_cb; + + // TODO(tomfinegan): Need to get stream type from media stack. + plugin_decryption_interface_->DecryptAndDecode(pp_instance_, + PP_DECRYPTORSTREAMTYPE_VIDEO, + encrypted_resource, + &block_info); + return true; +} + +void ContentDecryptorDelegate::NeedKey(PP_Var key_system_var, + PP_Var session_id_var, + PP_Var init_data_var) { + // TODO(tomfinegan): send the data to media stack. +} + +void ContentDecryptorDelegate::KeyAdded(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) { + decryptor_client_->KeyError("", "", media::Decryptor::kUnknownError, 0); + return; + } + + decryptor_client_->KeyAdded(key_system_string->value(), + session_id_string->value()); +} + +void ContentDecryptorDelegate::KeyMessage(PP_Var key_system_var, + 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); + + if (!key_system_string || !session_id_string || !default_url_string) { + decryptor_client_->KeyError("", "", media::Decryptor::kUnknownError, 0); + return; + } + + EnterResourceNoLock<PPB_Buffer_API> enter(message_resource, true); + if (!enter.succeeded()) { + decryptor_client_->KeyError(key_system_string->value(), + session_id_string->value(), + media::Decryptor::kUnknownError, + 0); + return; + } + + BufferAutoMapper mapper(enter.object()); + scoped_array<uint8> message_array(new uint8[mapper.size()]); + if (mapper.data() && mapper.size()) + memcpy(message_array.get(), mapper.data(), mapper.size()); + + decryptor_client_->KeyMessage(key_system_string->value(), + session_id_string->value(), + message_array.Pass(), + mapper.size(), + default_url_string->value()); +} + +void ContentDecryptorDelegate::KeyError(PP_Var key_system_var, + 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) { + decryptor_client_->KeyError("", "", media::Decryptor::kUnknownError, 0); + return; + } + + decryptor_client_->KeyError( + key_system_string->value(), + session_id_string->value(), + static_cast<media::Decryptor::KeyError>(media_error), + system_code); +} + +void ContentDecryptorDelegate::DecoderInitializeDone( + PP_DecryptorStreamType decoder_type, + uint32_t request_id, + PP_Bool success) { + if (decoder_type == PP_DECRYPTORSTREAMTYPE_AUDIO) { + // If the request ID is not valid or does not match what's saved, do + // nothing. + if (request_id == 0 || + request_id != pending_audio_decoder_init_request_id_) + return; + + DCHECK(!pending_audio_decoder_init_cb_.is_null()); + pending_audio_decoder_init_request_id_ = 0; + base::ResetAndReturn( + &pending_audio_decoder_init_cb_).Run(PP_ToBool(success)); + } else { + if (request_id == 0 || + request_id != pending_video_decoder_init_request_id_) + return; + + DCHECK(!pending_video_decoder_init_cb_.is_null()); + pending_video_decoder_init_request_id_ = 0; + base::ResetAndReturn( + &pending_video_decoder_init_cb_).Run(PP_ToBool(success)); + } +} + +void ContentDecryptorDelegate::DecoderDeinitializeDone( + PP_DecryptorStreamType decoder_type, + uint32_t request_id) { + // TODO(tomfinegan): Add decoder stop completion handling. +} + +void ContentDecryptorDelegate::DecoderResetDone( + PP_DecryptorStreamType decoder_type, + uint32_t request_id) { + // TODO(tomfinegan): Add decoder reset completion handling. +} + +void ContentDecryptorDelegate::DeliverBlock( + PP_Resource decrypted_block, + const PP_DecryptedBlockInfo* block_info) { + DCHECK(block_info); + 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::Status status = + PpDecryptResultToMediaDecryptorStatus(block_info->result); + if (status != media::Decryptor::kSuccess) { + decrypt_cb.Run(status, NULL); + return; + } + + EnterResourceNoLock<PPB_Buffer_API> enter(decrypted_block, true); + if (!enter.succeeded()) { + decrypt_cb.Run(media::Decryptor::kError, NULL); + return; + } + BufferAutoMapper mapper(enter.object()); + if (!mapper.data() || !mapper.size()) { + decrypt_cb.Run(media::Decryptor::kError, NULL); + return; + } + + // TODO(tomfinegan): Find a way to take ownership of the shared memory + // managed by the PPB_Buffer_Dev, and avoid the extra copy. + scoped_refptr<media::DecoderBuffer> decrypted_buffer( + media::DecoderBuffer::CopyFrom( + static_cast<uint8*>(mapper.data()), mapper.size())); + decrypted_buffer->SetTimestamp(base::TimeDelta::FromMicroseconds( + block_info->tracking_info.timestamp)); + decrypt_cb.Run(media::Decryptor::kSuccess, decrypted_buffer); +} + +void ContentDecryptorDelegate::DeliverFrame( + PP_Resource decrypted_frame, + const PP_DecryptedFrameInfo* frame_info) { + 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 (request_id == 0 || request_id != pending_video_decode_request_id_) { + DVLOG(1) << "DeliverFrame() - request_id " << request_id << " not found"; + return; + } + + TRACE_EVENT_ASYNC_END0( + "eme", "PluginInstance::DecryptAndDecodeVideo", request_id); + + DCHECK(!pending_video_decode_cb_.is_null()); + pending_video_decode_request_id_ = 0; + media::Decryptor::VideoDecodeCB video_decode_cb = + base::ResetAndReturn(&pending_video_decode_cb_); + + media::Decryptor::Status status = + PpDecryptResultToMediaDecryptorStatus(frame_info->result); + if (status != media::Decryptor::kSuccess) { + video_decode_cb.Run(status, NULL); + return; + } + + EnterResourceNoLock<PPB_Buffer_API> enter(decrypted_frame, true); + if (!enter.succeeded()) { + video_decode_cb.Run(media::Decryptor::kError, NULL); + return; + } + BufferAutoMapper mapper(enter.object()); + if (!mapper.data() || !mapper.size()) { + video_decode_cb.Run(media::Decryptor::kError, NULL); + return; + } + + const uint8* frame_data = static_cast<uint8*>(mapper.data()); + gfx::Size frame_size(frame_info->width, frame_info->height); + + DCHECK(frame_info->format == PP_DECRYPTEDFRAMEFORMAT_YV12); + const media::VideoFrame::Format format = media::VideoFrame::YV12; + + // TODO(tomfinegan): Find a way to take ownership of the shared memory + // managed by the PPB_Buffer_Dev, and avoid the extra copy. + scoped_refptr<media::VideoFrame> decoded_frame( + media::VideoFrame::CreateFrame( + format, frame_size, frame_size, + base::TimeDelta::FromMicroseconds( + frame_info->tracking_info.timestamp))); + + media::CopyYPlane( + frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_Y], + frame_info->strides[PP_DECRYPTEDFRAMEPLANES_Y], + frame_info->height, + decoded_frame.get()); + + media::CopyUPlane( + frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_U], + frame_info->strides[PP_DECRYPTEDFRAMEPLANES_U], + frame_info->height, + decoded_frame.get()); + + media::CopyVPlane( + frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_V], + frame_info->strides[PP_DECRYPTEDFRAMEPLANES_V], + frame_info->height, + decoded_frame.get()); + + video_decode_cb.Run(media::Decryptor::kSuccess, decoded_frame); +} + +void ContentDecryptorDelegate::DeliverSamples( + PP_Resource audio_frames, + const PP_DecryptedBlockInfo* block_info) { + 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); +} + +// TODO(xhwang): Try to remove duplicate logic here and in CancelDecrypt(). +void ContentDecryptorDelegate::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(); + } +} + +} // namespace ppapi +} // namespace webkit diff --git a/webkit/plugins/ppapi/content_decryptor_delegate.h b/webkit/plugins/ppapi/content_decryptor_delegate.h new file mode 100644 index 0000000..9d2d331 --- /dev/null +++ b/webkit/plugins/ppapi/content_decryptor_delegate.h @@ -0,0 +1,132 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef WEBKIT_PLUGINS_PPAPI_CONTENT_DECRYPTOR_DELEGATE_H_ +#define WEBKIT_PLUGINS_PPAPI_CONTENT_DECRYPTOR_DELEGATE_H_ + +#include <string> + +#include "base/basictypes.h" +#include "base/compiler_specific.h" +#include "base/memory/ref_counted.h" +#include "media/base/decryptor.h" +#include "ppapi/c/private/pp_content_decryptor.h" +#include "ppapi/c/private/ppp_content_decryptor_private.h" +#include "webkit/plugins/webkit_plugins_export.h" + +namespace media { +class AudioDecoderConfig; +class DecoderBuffer; +class DecryptorClient; +class VideoDecoderConfig; +} + +namespace webkit { +namespace ppapi { + +class WEBKIT_PLUGINS_EXPORT ContentDecryptorDelegate { + public: + // ContentDecryptorDelegate does not take ownership of + // |plugin_decryption_interface|. Therefore |plugin_decryption_interface| + // must outlive this object. + ContentDecryptorDelegate( + PP_Instance pp_instance, + const PPP_ContentDecryptor_Private* plugin_decryption_interface); + + // Provides access to PPP_ContentDecryptor_Private. + void set_decrypt_client(media::DecryptorClient* decryptor_client); + bool GenerateKeyRequest(const std::string& key_system, + const std::string& type, + const std::string& init_data); + bool AddKey(const std::string& session_id, + const std::string& key, + const std::string& init_data); + bool CancelKeyRequest(const std::string& session_id); + 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); + bool InitializeVideoDecoder( + const media::VideoDecoderConfig& decoder_config, + const media::Decryptor::DecoderInitCB& decoder_init_cb); + // TODO(tomfinegan): Add callback args for DeinitializeDecoder() and + // ResetDecoder() + 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); + + // PPB_ContentDecryptor_Private dispatching methods. + void NeedKey(PP_Var key_system, PP_Var session_id, PP_Var init_data); + void KeyAdded(PP_Var key_system, PP_Var session_id); + void KeyMessage(PP_Var key_system, + PP_Var session_id, + PP_Resource message, + PP_Var default_url); + void KeyError(PP_Var key_system, + PP_Var session_id, + int32_t media_error, + int32_t system_code); + void DeliverBlock(PP_Resource decrypted_block, + const PP_DecryptedBlockInfo* block_info); + void DecoderInitializeDone(PP_DecryptorStreamType decoder_type, + uint32_t request_id, + PP_Bool success); + void DecoderDeinitializeDone(PP_DecryptorStreamType decoder_type, + uint32_t request_id); + void DecoderResetDone(PP_DecryptorStreamType decoder_type, + uint32_t request_id); + void DeliverFrame(PP_Resource decrypted_frame, + const PP_DecryptedFrameInfo* frame_info); + void DeliverSamples(PP_Resource audio_frames, + const PP_DecryptedBlockInfo* block_info); + + private: + // Cancels the pending decrypt-and-decode callback for |stream_type|. + void CancelDecode(media::Decryptor::StreamType stream_type); + + const PP_Instance pp_instance_; + const PPP_ContentDecryptor_Private* const plugin_decryption_interface_; + + media::DecryptorClient* decryptor_client_; + + // Request ID for tracking pending content decryption callbacks. + // Note that zero indicates an invalid request ID. + // TODO(xhwang): Add completion callbacks for Reset/Stop and remove the use + // of request IDs. + uint32_t next_decryption_request_id_; + + 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_; + + 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_; + + DISALLOW_COPY_AND_ASSIGN(ContentDecryptorDelegate); +}; + +} // namespace ppapi +} // namespace webkit + +#endif // WEBKIT_PLUGINS_PPAPI_CONTENT_DECRYPTOR_DELEGATE_H_ diff --git a/webkit/plugins/ppapi/ppapi_plugin_instance.cc b/webkit/plugins/ppapi/ppapi_plugin_instance.cc index 795a00e..4aa3f99 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.cc +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.cc @@ -15,15 +15,6 @@ #include "base/time.h" #include "base/utf_offset_string_conversions.h" #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" -#include "media/base/video_frame.h" -#include "media/base/video_util.h" #include "ppapi/c/dev/ppb_find_dev.h" #include "ppapi/c/dev/ppb_zoom_dev.h" #include "ppapi/c/dev/ppp_find_dev.h" @@ -38,7 +29,6 @@ #include "ppapi/c/ppp_instance.h" #include "ppapi/c/ppp_messaging.h" #include "ppapi/c/ppp_mouse_lock.h" -#include "ppapi/c/private/pp_content_decryptor.h" #include "ppapi/c/private/ppp_instance_private.h" #include "ppapi/shared_impl/ppapi_preferences.h" #include "ppapi/shared_impl/ppb_gamepad_shared.h" @@ -77,6 +67,7 @@ #include "ui/gfx/rect_conversions.h" #include "webkit/plugins/plugin_constants.h" #include "webkit/plugins/ppapi/common.h" +#include "webkit/plugins/ppapi/content_decryptor_delegate.h" #include "webkit/plugins/ppapi/event_conversion.h" #include "webkit/plugins/ppapi/fullscreen_container.h" #include "webkit/plugins/ppapi/gfx_conversion.h" @@ -90,8 +81,8 @@ #include "webkit/plugins/ppapi/ppb_graphics_3d_impl.h" #include "webkit/plugins/ppapi/ppb_image_data_impl.h" #include "webkit/plugins/ppapi/ppb_url_loader_impl.h" -#include "webkit/plugins/ppapi/url_request_info_util.h" #include "webkit/plugins/ppapi/ppp_pdf.h" +#include "webkit/plugins/ppapi/url_request_info_util.h" #include "webkit/plugins/sad_plugin.h" #if defined(OS_MACOSX) @@ -312,226 +303,6 @@ scoped_array<const char*> StringVectorToArgArray( return array.Pass(); } -// Creates a PP_Resource containing a PPB_Buffer_Impl, copies |data| into the -// buffer resource, and returns it. Returns a an invalid PP_Resource with an ID -// of 0 on failure. Upon success, the returned Buffer resource has a reference -// count of 1. -PP_Resource MakeBufferResource(PP_Instance instance, - const uint8* data, int size) { - if (!data || !size) - return 0; - - ScopedPPResource resource(PPB_Buffer_Impl::Create(instance, size)); - if (!resource.get()) - return 0; - - EnterResourceNoLock<PPB_Buffer_API> enter(resource, true); - if (enter.failed()) - return 0; - - BufferAutoMapper mapper(enter.object()); - if (!mapper.data() || mapper.size() < static_cast<size_t>(size)) - return 0; - - memcpy(mapper.data(), data, size); - return resource.get(); -} - -// Copies the content of |str| into |array|. -// Returns true if copy succeeded. Returns false if copy failed, e.g. if the -// |array_size| is smaller than the |str| length. -template <uint32_t array_size> -bool CopyStringToArray(const std::string& str, uint8 (&array)[array_size]) { - if (array_size < str.size()) - return false; - - memcpy(array, str.data(), str.size()); - return true; -} - -// Fills the |block_info| with information from |decrypt_config|, |timestamp| -// and |request_id|. |decrypt_config| can be NULL if the block is not encrypted. -// This is useful for end-of-stream blocks. -// Returns true if |block_info| is successfully filled. Returns false -// otherwise. -bool MakeEncryptedBlockInfo( - const media::DecryptConfig* decrypt_config, - int64_t timestamp, - uint32_t request_id, - PP_EncryptedBlockInfo* block_info) { - DCHECK(block_info); - - // TODO(xhwang): Fix initialization of PP_EncryptedBlockInfo here and - // anywhere else. - memset(block_info, 0, sizeof(*block_info)); - - block_info->tracking_info.request_id = request_id; - block_info->tracking_info.timestamp = timestamp; - - if (!decrypt_config) - return true; - - block_info->data_offset = decrypt_config->data_offset(); - - if (!CopyStringToArray(decrypt_config->key_id(), block_info->key_id) || - !CopyStringToArray(decrypt_config->iv(), block_info->iv)) - return false; - - block_info->key_id_size = decrypt_config->key_id().size(); - block_info->iv_size = decrypt_config->iv().size(); - - if (decrypt_config->subsamples().size() > arraysize(block_info->subsamples)) - return false; - - block_info->num_subsamples = decrypt_config->subsamples().size(); - for (uint32_t i = 0; i < block_info->num_subsamples; ++i) { - block_info->subsamples[i].clear_bytes = - decrypt_config->subsamples()[i].clear_bytes; - block_info->subsamples[i].cipher_bytes = - decrypt_config->subsamples()[i].cypher_bytes; - } - - 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; - - memcpy(×tamp, cur, sizeof(timestamp)); - cur += sizeof(timestamp); - bytes_left -= sizeof(timestamp); - - memcpy(&frame_size, cur, sizeof(frame_size)); - 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(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: - return PP_AUDIOCODEC_VORBIS; - case media::kCodecAAC: - return PP_AUDIOCODEC_AAC; - default: - return PP_AUDIOCODEC_UNKNOWN; - } -} - -PP_VideoCodec MediaVideoCodecToPpVideoCodec(media::VideoCodec codec) { - switch (codec) { - case media::kCodecVP8: - return PP_VIDEOCODEC_VP8; - case media::kCodecH264: - return PP_VIDEOCODEC_H264; - default: - return PP_VIDEOCODEC_UNKNOWN; - } -} - -PP_VideoCodecProfile MediaVideoCodecProfileToPpVideoCodecProfile( - media::VideoCodecProfile profile) { - switch (profile) { - case media::VP8PROFILE_MAIN: - return PP_VIDEOCODECPROFILE_VP8_MAIN; - case media::H264PROFILE_BASELINE: - return PP_VIDEOCODECPROFILE_H264_BASELINE; - case media::H264PROFILE_MAIN: - return PP_VIDEOCODECPROFILE_H264_MAIN; - case media::H264PROFILE_EXTENDED: - return PP_VIDEOCODECPROFILE_H264_EXTENDED; - case media::H264PROFILE_HIGH: - return PP_VIDEOCODECPROFILE_H264_HIGH; - case media::H264PROFILE_HIGH10PROFILE: - return PP_VIDEOCODECPROFILE_H264_HIGH_10; - case media::H264PROFILE_HIGH422PROFILE: - return PP_VIDEOCODECPROFILE_H264_HIGH_422; - case media::H264PROFILE_HIGH444PREDICTIVEPROFILE: - return PP_VIDEOCODECPROFILE_H264_HIGH_444_PREDICTIVE; - default: - return PP_VIDEOCODECPROFILE_UNKNOWN; - } -} - -PP_DecryptedFrameFormat MediaVideoFormatToPpDecryptedFrameFormat( - media::VideoFrame::Format format) { - switch (format) { - case media::VideoFrame::YV12: - return PP_DECRYPTEDFRAMEFORMAT_YV12; - case media::VideoFrame::I420: - return PP_DECRYPTEDFRAMEFORMAT_I420; - default: - return PP_DECRYPTEDFRAMEFORMAT_UNKNOWN; - } -} - -media::Decryptor::Status PpDecryptResultToMediaDecryptorStatus( - PP_DecryptResult result) { - switch (result) { - case PP_DECRYPTRESULT_SUCCESS: - return media::Decryptor::kSuccess; - case PP_DECRYPTRESULT_DECRYPT_NOKEY: - return media::Decryptor::kNoKey; - case PP_DECRYPTRESULT_NEEDMOREDATA: - return media::Decryptor::kNeedMoreData; - case PP_DECRYPTRESULT_DECRYPT_ERROR: - return media::Decryptor::kError; - case PP_DECRYPTRESULT_DECODE_ERROR: - return media::Decryptor::kError; - default: - NOTREACHED(); - return media::Decryptor::kError; - } -} - -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 @@ -572,7 +343,6 @@ PluginInstance::PluginInstance( has_webkit_focus_(false), has_content_area_focus_(false), find_identifier_(-1), - plugin_decryption_interface_(NULL), plugin_find_interface_(NULL), plugin_input_event_interface_(NULL), plugin_messaging_interface_(NULL), @@ -603,15 +373,7 @@ PluginInstance::PluginInstance( selection_caret_(0), selection_anchor_(0), pending_user_gesture_(0.0), - 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) { + flash_impl_(ALLOW_THIS_IN_INITIALIZER_LIST(this)) { pp_instance_ = HostGlobals::Get()->AddInstance(this); memset(¤t_print_settings_, 0, sizeof(current_print_settings_)); @@ -1295,16 +1057,6 @@ void PluginInstance::StopFind() { plugin_find_interface_->StopFind(pp_instance()); } -bool PluginInstance::LoadContentDecryptorInterface() { - if (!plugin_decryption_interface_) { - plugin_decryption_interface_ = - static_cast<const PPP_ContentDecryptor_Private*>( - module_->GetPluginInterface( - PPP_CONTENTDECRYPTOR_PRIVATE_INTERFACE)); - } - return !!plugin_decryption_interface_; -} - bool PluginInstance::LoadFindInterface() { if (!plugin_find_interface_) { plugin_find_interface_ = @@ -1623,336 +1375,6 @@ void PluginInstance::RotateView(WebPlugin::RotationType type) { // NOTE: plugin instance may have been deleted. } -void PluginInstance::set_decrypt_client( - media::DecryptorClient* decryptor_client) { - decryptor_client_ = decryptor_client; -} - -bool PluginInstance::GenerateKeyRequest(const std::string& key_system, - const std::string& type, - const std::string& init_data) { - if (!LoadContentDecryptorInterface()) - return false; - if (key_system.empty()) - return false; - - PP_Var init_data_array = - PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( - init_data.size(), init_data.data()); - - plugin_decryption_interface_->GenerateKeyRequest( - pp_instance(), - StringVar::StringToPPVar(key_system), - StringVar::StringToPPVar(type), - init_data_array); - return true; -} - -bool PluginInstance::AddKey(const std::string& session_id, - const std::string& key, - const std::string& init_data) { - if (!LoadContentDecryptorInterface()) - return false; - PP_Var key_array = - PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(key.size(), - key.data()); - PP_Var init_data_array = - PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar( - init_data.size(), - init_data.data()); - - plugin_decryption_interface_->AddKey( - pp_instance(), - StringVar::StringToPPVar(session_id), - key_array, - init_data_array); - return true; -} - -bool PluginInstance::CancelKeyRequest(const std::string& session_id) { - if (!LoadContentDecryptorInterface()) - return false; - plugin_decryption_interface_->CancelKeyRequest( - pp_instance(), - StringVar::StringToPPVar(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() - stream_type: " << stream_type; - if (!LoadContentDecryptorInterface()) - return false; - - ScopedPPResource encrypted_resource( - ScopedPPResource::PassRef(), - MakeBufferResource(pp_instance(), - encrypted_buffer->GetData(), - encrypted_buffer->GetDataSize())); - if (!encrypted_resource.get()) - return false; - - const uint32_t request_id = next_decryption_request_id_++; - DVLOG(2) << "Decrypt() - request_id " << request_id; - - PP_EncryptedBlockInfo block_info; - DCHECK(encrypted_buffer->GetDecryptConfig()); - if (!MakeEncryptedBlockInfo(encrypted_buffer->GetDecryptConfig(), - encrypted_buffer->GetTimestamp().InMicroseconds(), - request_id, - &block_info)) { - return false; - } - - // 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, - &block_info); - 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) { - PP_AudioDecoderConfig pp_decoder_config; - pp_decoder_config.codec = - MediaAudioCodecToPpAudioCodec(decoder_config.codec()); - pp_decoder_config.channel_count = - media::ChannelLayoutToChannelCount(decoder_config.channel_layout()); - pp_decoder_config.bits_per_channel = decoder_config.bits_per_channel(); - pp_decoder_config.samples_per_second = decoder_config.samples_per_second(); - pp_decoder_config.request_id = next_decryption_request_id_++; - - ScopedPPResource extra_data_resource( - ScopedPPResource::PassRef(), - MakeBufferResource(pp_instance(), - decoder_config.extra_data(), - decoder_config.extra_data_size())); - - DCHECK_EQ(pending_audio_decoder_init_request_id_, 0u); - DCHECK(pending_audio_decoder_init_cb_.is_null()); - pending_audio_decoder_init_request_id_ = pp_decoder_config.request_id; - pending_audio_decoder_init_cb_ = init_cb; - - plugin_decryption_interface_->InitializeAudioDecoder(pp_instance(), - &pp_decoder_config, - extra_data_resource); - return true; -} - -bool PluginInstance::InitializeVideoDecoder( - const media::VideoDecoderConfig& decoder_config, - const media::Decryptor::DecoderInitCB& init_cb) { - PP_VideoDecoderConfig pp_decoder_config; - pp_decoder_config.codec = - MediaVideoCodecToPpVideoCodec(decoder_config.codec()); - pp_decoder_config.profile = - MediaVideoCodecProfileToPpVideoCodecProfile(decoder_config.profile()); - pp_decoder_config.format = - MediaVideoFormatToPpDecryptedFrameFormat(decoder_config.format()); - pp_decoder_config.width = decoder_config.coded_size().width(); - pp_decoder_config.height = decoder_config.coded_size().height(); - pp_decoder_config.request_id = next_decryption_request_id_++; - - ScopedPPResource extra_data_resource( - ScopedPPResource::PassRef(), - MakeBufferResource(pp_instance(), - decoder_config.extra_data(), - decoder_config.extra_data_size())); - - DCHECK_EQ(pending_video_decoder_init_request_id_, 0u); - DCHECK(pending_video_decoder_init_cb_.is_null()); - pending_video_decoder_init_request_id_ = pp_decoder_config.request_id; - pending_video_decoder_init_cb_ = init_cb; - - plugin_decryption_interface_->InitializeVideoDecoder(pp_instance(), - &pp_decoder_config, - extra_data_resource); - return true; -} - -// 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(), MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); - return true; -} - -bool PluginInstance::ResetDecoder(media::Decryptor::StreamType stream_type) { - if (!LoadContentDecryptorInterface()) - return false; - - CancelDecode(stream_type); - - // TODO(tomfinegan): Add decoder reset request tracking. - plugin_decryption_interface_->ResetDecoder( - pp_instance(), MediaDecryptorStreamTypeToPpStreamType(stream_type), 0); - return true; -} - -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()) - 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) << "DecryptAndDecodeVideo() - request_id " << request_id; - TRACE_EVENT_ASYNC_BEGIN0( - "eme", "PluginInstance::DecryptAndDecodeVideo", request_id); - - PP_EncryptedBlockInfo block_info; - if (!MakeEncryptedBlockInfo( - encrypted_buffer->GetDecryptConfig(), - encrypted_buffer->GetTimestamp().InMicroseconds(), - request_id, - &block_info)) { - return false; - } - - // Only one pending video decode request at any time. This is enforced by the - // media pipeline. - DCHECK_EQ(pending_video_decode_request_id_, 0u); - DCHECK(pending_video_decode_cb_.is_null()); - pending_video_decode_request_id_ = request_id; - pending_video_decode_cb_ = video_decode_cb; - - // TODO(tomfinegan): Need to get stream type from media stack. - plugin_decryption_interface_->DecryptAndDecode(pp_instance(), - PP_DECRYPTORSTREAMTYPE_VIDEO, - encrypted_resource, - &block_info); - return true; -} - bool PluginInstance::FlashIsFullscreenOrPending() { return fullscreen_container_ != NULL; } @@ -2400,6 +1822,22 @@ void PluginInstance::SimulateImeSetCompositionEvent( utf16_text, underlines, offsets[0], offsets[1]); } +ContentDecryptorDelegate* PluginInstance::GetContentDecryptorDelegate() { + if (content_decryptor_delegate_) + return content_decryptor_delegate_.get(); + + const PPP_ContentDecryptor_Private* plugin_decryption_interface = + static_cast<const PPP_ContentDecryptor_Private*>( + module_->GetPluginInterface( + PPP_CONTENTDECRYPTOR_PRIVATE_INTERFACE)); + if (!plugin_decryption_interface) + return NULL; + + content_decryptor_delegate_.reset( + new ContentDecryptorDelegate(pp_instance_, plugin_decryption_interface)); + return content_decryptor_delegate_.get(); +} + void PluginInstance::ClosePendingUserGesture(PP_Instance instance, PP_TimeTicks timestamp) { // Do nothing so that the pending user gesture will stay open for @@ -2566,28 +2004,22 @@ PP_Var PluginInstance::GetFontFamilies(PP_Instance instance) { return PP_MakeUndefined(); } +// These PPB_ContentDecryptor_Private calls are responses to +// PPP_ContentDecryptor_Private calls made on |content_decryptor_delegate_|. +// Therefore, |content_decryptor_delegate_| must have been initialized when +// the following methods are called. void PluginInstance::NeedKey(PP_Instance instance, PP_Var key_system_var, PP_Var session_id_var, PP_Var init_data_var) { - // TODO(tomfinegan): send the data to media stack. + content_decryptor_delegate_->NeedKey( + key_system_var, session_id_var, init_data_var); } 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) { - decryptor_client_->KeyError("", "", media::Decryptor::kUnknownError, 0); - return; - } - - decryptor_client_->KeyAdded(key_system_string->value(), - session_id_string->value()); + content_decryptor_delegate_->KeyAdded(key_system_var, session_id_var); } void PluginInstance::KeyMessage(PP_Instance instance, @@ -2595,37 +2027,8 @@ 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); - - if (!key_system_string || !session_id_string || !default_url_string) { - decryptor_client_->KeyError("", "", media::Decryptor::kUnknownError, 0); - return; - } - - EnterResourceNoLock<PPB_Buffer_API> enter(message_resource, true); - if (!enter.succeeded()) { - decryptor_client_->KeyError(key_system_string->value(), - session_id_string->value(), - media::Decryptor::kUnknownError, - 0); - return; - } - - BufferAutoMapper mapper(enter.object()); - scoped_array<uint8> message_array(new uint8[mapper.size()]); - if (mapper.data() && mapper.size()) - memcpy(message_array.get(), mapper.data(), mapper.size()); - - decryptor_client_->KeyMessage(key_system_string->value(), - session_id_string->value(), - message_array.Pass(), - mapper.size(), - default_url_string->value()); + content_decryptor_delegate_->KeyMessage( + key_system_var, session_id_var, message_resource, default_url_var); } void PluginInstance::KeyError(PP_Instance instance, @@ -2633,227 +2036,49 @@ 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) { - decryptor_client_->KeyError("", "", media::Decryptor::kUnknownError, 0); - return; - } + content_decryptor_delegate_->KeyError( + key_system_var, session_id_var, media_error, system_code); +} - decryptor_client_->KeyError( - key_system_string->value(), - session_id_string->value(), - static_cast<media::Decryptor::KeyError>(media_error), - system_code); +void PluginInstance::DeliverBlock(PP_Instance instance, + PP_Resource decrypted_block, + const PP_DecryptedBlockInfo* block_info) { + content_decryptor_delegate_->DeliverBlock(decrypted_block, block_info); } void PluginInstance::DecoderInitializeDone(PP_Instance instance, PP_DecryptorStreamType decoder_type, uint32_t request_id, PP_Bool success) { - if (decoder_type == PP_DECRYPTORSTREAMTYPE_AUDIO) { - // If the request ID is not valid or does not match what's saved, do - // nothing. - if (request_id == 0 || - request_id != pending_audio_decoder_init_request_id_) - return; - - DCHECK(!pending_audio_decoder_init_cb_.is_null()); - pending_audio_decoder_init_request_id_ = 0; - base::ResetAndReturn( - &pending_audio_decoder_init_cb_).Run(PP_ToBool(success)); - } else { - if (request_id == 0 || - request_id != pending_video_decoder_init_request_id_) - return; - - DCHECK(!pending_video_decoder_init_cb_.is_null()); - pending_video_decoder_init_request_id_ = 0; - base::ResetAndReturn( - &pending_video_decoder_init_cb_).Run(PP_ToBool(success)); - } + content_decryptor_delegate_->DecoderInitializeDone( + decoder_type, request_id, success); } void PluginInstance::DecoderDeinitializeDone( PP_Instance instance, PP_DecryptorStreamType decoder_type, uint32_t request_id) { - // TODO(tomfinegan): Add decoder stop completion handling. + content_decryptor_delegate_->DecoderDeinitializeDone(decoder_type, + request_id); } void PluginInstance::DecoderResetDone(PP_Instance instance, PP_DecryptorStreamType decoder_type, uint32_t request_id) { - // TODO(tomfinegan): Add decoder reset completion handling. + content_decryptor_delegate_->DecoderResetDone(decoder_type, request_id); } -void PluginInstance::DeliverBlock(PP_Instance instance, - PP_Resource decrypted_block, - const PP_DecryptedBlockInfo* block_info) { - DCHECK(block_info); - 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::Status status = - PpDecryptResultToMediaDecryptorStatus(block_info->result); - if (status != media::Decryptor::kSuccess) { - decrypt_cb.Run(status, NULL); - return; - } - - EnterResourceNoLock<PPB_Buffer_API> enter(decrypted_block, true); - if (!enter.succeeded()) { - decrypt_cb.Run(media::Decryptor::kError, NULL); - return; - } - BufferAutoMapper mapper(enter.object()); - if (!mapper.data() || !mapper.size()) { - decrypt_cb.Run(media::Decryptor::kError, NULL); - return; - } - - // TODO(tomfinegan): Find a way to take ownership of the shared memory - // managed by the PPB_Buffer_Dev, and avoid the extra copy. - scoped_refptr<media::DecoderBuffer> decrypted_buffer( - media::DecoderBuffer::CopyFrom( - static_cast<uint8*>(mapper.data()), mapper.size())); - decrypted_buffer->SetTimestamp(base::TimeDelta::FromMicroseconds( - block_info->tracking_info.timestamp)); - decrypt_cb.Run(media::Decryptor::kSuccess, decrypted_buffer); -} void PluginInstance::DeliverFrame(PP_Instance instance, PP_Resource decrypted_frame, const PP_DecryptedFrameInfo* frame_info) { - 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 (request_id == 0 || request_id != pending_video_decode_request_id_) { - DVLOG(1) << "DeliverFrame() - request_id " << request_id << " not found"; - return; - } - - TRACE_EVENT_ASYNC_END0( - "eme", "PluginInstance::DecryptAndDecodeVideo", request_id); - - DCHECK(!pending_video_decode_cb_.is_null()); - pending_video_decode_request_id_ = 0; - media::Decryptor::VideoDecodeCB video_decode_cb = - base::ResetAndReturn(&pending_video_decode_cb_); - - media::Decryptor::Status status = - PpDecryptResultToMediaDecryptorStatus(frame_info->result); - if (status != media::Decryptor::kSuccess) { - video_decode_cb.Run(status, NULL); - return; - } - - EnterResourceNoLock<PPB_Buffer_API> enter(decrypted_frame, true); - if (!enter.succeeded()) { - video_decode_cb.Run(media::Decryptor::kError, NULL); - return; - } - BufferAutoMapper mapper(enter.object()); - if (!mapper.data() || !mapper.size()) { - video_decode_cb.Run(media::Decryptor::kError, NULL); - return; - } - - const uint8* frame_data = static_cast<uint8*>(mapper.data()); - gfx::Size frame_size(frame_info->width, frame_info->height); - - DCHECK(frame_info->format == PP_DECRYPTEDFRAMEFORMAT_YV12); - const media::VideoFrame::Format format = media::VideoFrame::YV12; - - // TODO(tomfinegan): Find a way to take ownership of the shared memory - // managed by the PPB_Buffer_Dev, and avoid the extra copy. - scoped_refptr<media::VideoFrame> decoded_frame( - media::VideoFrame::CreateFrame( - format, frame_size, frame_size, - base::TimeDelta::FromMicroseconds( - frame_info->tracking_info.timestamp))); - - media::CopyYPlane( - frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_Y], - frame_info->strides[PP_DECRYPTEDFRAMEPLANES_Y], - frame_info->height, - decoded_frame.get()); - - media::CopyUPlane( - frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_U], - frame_info->strides[PP_DECRYPTEDFRAMEPLANES_U], - frame_info->height, - decoded_frame.get()); - - media::CopyVPlane( - frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_V], - frame_info->strides[PP_DECRYPTEDFRAMEPLANES_V], - frame_info->height, - decoded_frame.get()); - - video_decode_cb.Run(media::Decryptor::kSuccess, decoded_frame); + content_decryptor_delegate_->DeliverFrame(decrypted_frame, frame_info); } void PluginInstance::DeliverSamples(PP_Instance instance, PP_Resource audio_frames, const PP_DecryptedBlockInfo* block_info) { - 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); + content_decryptor_delegate_->DeliverSamples(audio_frames, block_info); } 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 426106e..762510a 100644 --- a/webkit/plugins/ppapi/ppapi_plugin_instance.h +++ b/webkit/plugins/ppapi/ppapi_plugin_instance.h @@ -5,20 +5,17 @@ #ifndef WEBKIT_PLUGINS_PPAPI_PPAPI_PLUGIN_INSTANCE_H_ #define WEBKIT_PLUGINS_PPAPI_PPAPI_PLUGIN_INSTANCE_H_ -#include <map> #include <set> #include <string> #include <vector> #include "base/basictypes.h" -#include "base/callback.h" #include "base/compiler_specific.h" #include "base/memory/ref_counted.h" #include "base/memory/scoped_ptr.h" #include "base/memory/weak_ptr.h" #include "base/string16.h" #include "googleurl/src/gurl.h" -#include "media/base/decryptor.h" #include "ppapi/c/dev/pp_cursor_type_dev.h" #include "ppapi/c/dev/ppp_printing_dev.h" #include "ppapi/c/dev/ppp_find_dev.h" @@ -38,7 +35,6 @@ #include "ppapi/c/ppp_messaging.h" #include "ppapi/c/ppp_mouse_lock.h" #include "ppapi/c/private/ppb_content_decryptor_private.h" -#include "ppapi/c/private/ppp_content_decryptor_private.h" #include "ppapi/c/private/ppp_instance_private.h" #include "ppapi/shared_impl/ppb_instance_shared.h" #include "ppapi/shared_impl/ppb_view_shared.h" @@ -58,7 +54,6 @@ #include "webkit/plugins/ppapi/ppp_pdf.h" #include "webkit/plugins/webkit_plugins_export.h" -struct PP_DecryptedBlockInfo; struct PP_Point; class SkBitmap; @@ -73,13 +68,6 @@ struct WebCursorInfo; struct WebPrintParams; } -namespace media { -class AudioDecoderConfig; -class DecoderBuffer; -class DecryptorClient; -class VideoDecoderConfig; -} - namespace ppapi { struct InputEventData; struct PPP_Instance_Combined; @@ -94,6 +82,7 @@ class Range; namespace webkit { namespace ppapi { +class ContentDecryptorDelegate; class FullscreenContainer; class MessageChannel; class PluginDelegate; @@ -254,38 +243,6 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : void Graphics3DContextLost(); - // Provides access to PPP_ContentDecryptor_Private. - // TODO(tomfinegan): Move decryptor methods to delegate class. - void set_decrypt_client(media::DecryptorClient* client); - bool GenerateKeyRequest(const std::string& key_system, - const std::string& type, - const std::string& init_data); - bool AddKey(const std::string& session_id, - const std::string& key, - const std::string& init_data); - bool CancelKeyRequest(const std::string& session_id); - 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); - bool InitializeVideoDecoder( - const media::VideoDecoderConfig& decoder_config, - const media::Decryptor::DecoderInitCB& decoder_init_cb); - // TODO(tomfinegan): Add callback args for DeinitializeDecoder() and - // ResetDecoder() - 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); - // There are 2 implementations of the fullscreen interface // PPB_FlashFullscreen is used by Pepper Flash. // PPB_Fullscreen is intended for other applications including NaCl. @@ -381,6 +338,8 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : void SimulateImeSetCompositionEvent( const ::ppapi::InputEventData& input_event); + ContentDecryptorDelegate* GetContentDecryptorDelegate(); + // PPB_Instance_API implementation. virtual PP_Bool BindGraphics(PP_Instance instance, PP_Resource device) OVERRIDE; @@ -458,9 +417,7 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : PP_Instance instance, PP_URLComponents_Dev* components) OVERRIDE; - // PPB_ContentDecryptor_Private - // TODO(tomfinegan): Move the PPB_ContentDecryptor_Private methods to a - // delegate class. + // PPB_ContentDecryptor_Private implementation. virtual void NeedKey(PP_Instance instance, PP_Var key_system, PP_Var session_id, @@ -525,7 +482,6 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : PluginModule* module, ::ppapi::PPP_Instance_Combined* instance_interface); - bool LoadContentDecryptorInterface(); bool LoadFindInterface(); bool LoadInputEventInterface(); bool LoadMessagingInterface(); @@ -605,9 +561,6 @@ 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_; @@ -662,7 +615,6 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : // The plugin-provided interfaces. // When adding PPP interfaces, make sure to reset them in ResetAsProxied. - const PPP_ContentDecryptor_Private* plugin_decryption_interface_; const PPP_Find_Dev* plugin_find_interface_; const PPP_InputEvent* plugin_input_event_interface_; const PPP_Messaging* plugin_messaging_interface_; @@ -796,31 +748,9 @@ class WEBKIT_PLUGINS_EXPORT PluginInstance : // the pointer so we can re-send it later if we are reset to talk to NaCl. scoped_refptr<PPB_URLLoader_Impl> document_loader_; - media::DecryptorClient* decryptor_client_; - - // Request ID for tracking pending content decryption callbacks. - // Note that zero indicates an invalid request ID. - // TODO(xhwang): Add completion callbacks for Reset/Stop and remove the use - // of request IDs. - uint32_t next_decryption_request_id_; - - 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_; - - 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_; + // The ContentDecryptorDelegate forwards PPP_ContentDecryptor_Private + // calls and handles PPB_ContentDecryptor_Private calls. + scoped_ptr<ContentDecryptorDelegate> content_decryptor_delegate_; DISALLOW_COPY_AND_ASSIGN(PluginInstance); }; |