| Index: webkit/plugins/ppapi/content_decryptor_delegate.cc
|
| ===================================================================
|
| --- webkit/plugins/ppapi/content_decryptor_delegate.cc (revision 213561)
|
| +++ webkit/plugins/ppapi/content_decryptor_delegate.cc (working copy)
|
| @@ -1,1048 +0,0 @@
|
| -// 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 "base/message_loop/message_loop_proxy.h"
|
| -#include "base/safe_numerics.h"
|
| -#include "media/base/audio_buffer.h"
|
| -#include "media/base/audio_decoder_config.h"
|
| -#include "media/base/bind_to_loop.h"
|
| -#include "media/base/channel_layout.h"
|
| -#include "media/base/data_buffer.h"
|
| -#include "media/base/decoder_buffer.h"
|
| -#include "media/base/decrypt_config.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 "ui/gfx/rect.h"
|
| -#include "webkit/plugins/ppapi/ppb_buffer_impl.h"
|
| -
|
| -using ppapi::ArrayBufferVar;
|
| -using ppapi::PpapiGlobals;
|
| -using ppapi::ScopedPPResource;
|
| -using ppapi::StringVar;
|
| -using ppapi::thunk::EnterResourceNoLock;
|
| -using ppapi::thunk::PPB_Buffer_API;
|
| -
|
| -namespace webkit {
|
| -namespace ppapi {
|
| -
|
| -namespace {
|
| -
|
| -// Fills |resource| with a PPB_Buffer_Impl and copies |data| into the buffer
|
| -// resource. The |*resource|, if valid, will be in the ResourceTracker with a
|
| -// reference-count of 0. If |data| is NULL, sets |*resource| to NULL. Returns
|
| -// true upon success and false if any error happened.
|
| -bool MakeBufferResource(PP_Instance instance,
|
| - const uint8* data, uint32_t size,
|
| - scoped_refptr<PPB_Buffer_Impl>* resource) {
|
| - TRACE_EVENT0("eme", "ContentDecryptorDelegate - MakeBufferResource");
|
| - DCHECK(resource);
|
| -
|
| - if (!data || !size) {
|
| - DCHECK(!data && !size);
|
| - resource = NULL;
|
| - return true;
|
| - }
|
| -
|
| - scoped_refptr<PPB_Buffer_Impl> buffer(
|
| - PPB_Buffer_Impl::CreateResource(instance, size));
|
| - if (!buffer.get())
|
| - return false;
|
| -
|
| - BufferAutoMapper mapper(buffer.get());
|
| - if (!mapper.data() || mapper.size() < size)
|
| - return false;
|
| - memcpy(mapper.data(), data, size);
|
| -
|
| - *resource = buffer;
|
| - return true;
|
| -}
|
| -
|
| -// 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 |encrypted_buffer|.
|
| -//
|
| -// Returns true if |block_info| is successfully filled. Returns false
|
| -// otherwise.
|
| -static bool MakeEncryptedBlockInfo(
|
| - const scoped_refptr<media::DecoderBuffer>& encrypted_buffer,
|
| - uint32_t request_id,
|
| - PP_EncryptedBlockInfo* 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;
|
| -
|
| - // EOS buffers need a request ID and nothing more.
|
| - if (encrypted_buffer->end_of_stream())
|
| - return true;
|
| -
|
| - DCHECK(encrypted_buffer->data_size())
|
| - << "DecryptConfig is set on an empty buffer";
|
| -
|
| - block_info->tracking_info.timestamp =
|
| - encrypted_buffer->timestamp().InMicroseconds();
|
| - block_info->data_size = encrypted_buffer->data_size();
|
| -
|
| - const media::DecryptConfig* decrypt_config =
|
| - encrypted_buffer->decrypt_config();
|
| - 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;
|
| -}
|
| -
|
| -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),
|
| - 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),
|
| - weak_ptr_factory_(this),
|
| - weak_this_(weak_ptr_factory_.GetWeakPtr()),
|
| - audio_sample_format_(media::kUnknownSampleFormat),
|
| - audio_samples_per_second_(0),
|
| - audio_channel_count_(0),
|
| - audio_bytes_per_frame_(0) {
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::Initialize(const std::string& key_system) {
|
| - // TODO(ddorwin): Add an Initialize method to PPP_ContentDecryptor_Private.
|
| - DCHECK(!key_system.empty());
|
| - key_system_ = key_system;
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::SetKeyEventCallbacks(
|
| - const media::KeyAddedCB& key_added_cb,
|
| - const media::KeyErrorCB& key_error_cb,
|
| - const media::KeyMessageCB& key_message_cb) {
|
| - key_added_cb_ = key_added_cb;
|
| - key_error_cb_ = key_error_cb;
|
| - key_message_cb_ = key_message_cb;
|
| -}
|
| -
|
| -bool ContentDecryptorDelegate::GenerateKeyRequest(const std::string& type,
|
| - const uint8* init_data,
|
| - int init_data_length) {
|
| - PP_Var init_data_array =
|
| - PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
|
| - init_data_length, init_data);
|
| -
|
| - plugin_decryption_interface_->GenerateKeyRequest(
|
| - pp_instance_,
|
| - StringVar::StringToPPVar(key_system_), // TODO(ddorwin): Remove.
|
| - StringVar::StringToPPVar(type),
|
| - init_data_array);
|
| - return true;
|
| -}
|
| -
|
| -bool ContentDecryptorDelegate::AddKey(const std::string& session_id,
|
| - const uint8* key,
|
| - int key_length,
|
| - const uint8* init_data,
|
| - int init_data_length) {
|
| - PP_Var key_array =
|
| - PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(key_length,
|
| - key);
|
| - PP_Var init_data_array =
|
| - PpapiGlobals::Get()->GetVarTracker()->MakeArrayBufferPPVar(
|
| - init_data_length, init_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;
|
| - // |{audio|video}_input_resource_| is not being used by the plugin
|
| - // now because there is only one pending audio/video decrypt request at any
|
| - // time. This is enforced by the media pipeline.
|
| - scoped_refptr<PPB_Buffer_Impl> encrypted_resource;
|
| - if (!MakeMediaBufferResource(
|
| - stream_type, encrypted_buffer, &encrypted_resource) ||
|
| - !encrypted_resource.get()) {
|
| - return false;
|
| - }
|
| - ScopedPPResource pp_resource(encrypted_resource.get());
|
| -
|
| - const uint32_t request_id = next_decryption_request_id_++;
|
| - DVLOG(2) << "Decrypt() - request_id " << request_id;
|
| -
|
| - PP_EncryptedBlockInfo block_info = {};
|
| - DCHECK(encrypted_buffer->decrypt_config());
|
| - if (!MakeEncryptedBlockInfo(encrypted_buffer, 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;
|
| - }
|
| -
|
| - SetBufferToFreeInTrackingInfo(&block_info.tracking_info);
|
| -
|
| - plugin_decryption_interface_->Decrypt(pp_instance_,
|
| - pp_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:
|
| - // Release the shared memory as it can still be in use by the plugin.
|
| - // The next Decrypt() call will need to allocate a new shared memory
|
| - // buffer.
|
| - audio_input_resource_ = NULL;
|
| - pending_audio_decrypt_request_id_ = 0;
|
| - decrypt_cb = base::ResetAndReturn(&pending_audio_decrypt_cb_);
|
| - break;
|
| - case media::Decryptor::kVideo:
|
| - // Release the shared memory as it can still be in use by the plugin.
|
| - // The next Decrypt() call will need to allocate a new shared memory
|
| - // buffer.
|
| - video_input_resource_ = NULL;
|
| - 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_++;
|
| -
|
| - audio_sample_format_ = decoder_config.sample_format();
|
| - audio_samples_per_second_ = pp_decoder_config.samples_per_second;
|
| - audio_channel_count_ = pp_decoder_config.channel_count;
|
| - audio_bytes_per_frame_ = decoder_config.bytes_per_frame();
|
| -
|
| - scoped_refptr<PPB_Buffer_Impl> extra_data_resource;
|
| - if (!MakeBufferResource(pp_instance_,
|
| - decoder_config.extra_data(),
|
| - decoder_config.extra_data_size(),
|
| - &extra_data_resource)) {
|
| - return false;
|
| - }
|
| - ScopedPPResource pp_resource(extra_data_resource.get());
|
| -
|
| - 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,
|
| - pp_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_++;
|
| -
|
| - scoped_refptr<PPB_Buffer_Impl> extra_data_resource;
|
| - if (!MakeBufferResource(pp_instance_,
|
| - decoder_config.extra_data(),
|
| - decoder_config.extra_data_size(),
|
| - &extra_data_resource)) {
|
| - return false;
|
| - }
|
| - ScopedPPResource pp_resource(extra_data_resource.get());
|
| -
|
| - 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;
|
| -
|
| - natural_size_ = decoder_config.natural_size();
|
| -
|
| - plugin_decryption_interface_->InitializeVideoDecoder(pp_instance_,
|
| - &pp_decoder_config,
|
| - pp_resource);
|
| - return true;
|
| -}
|
| -
|
| -bool ContentDecryptorDelegate::DeinitializeDecoder(
|
| - media::Decryptor::StreamType stream_type) {
|
| - CancelDecode(stream_type);
|
| -
|
| - natural_size_ = gfx::Size();
|
| -
|
| - // 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) {
|
| - // |audio_input_resource_| is not being used by the plugin now
|
| - // because there is only one pending audio decode request at any time.
|
| - // This is enforced by the media pipeline.
|
| - scoped_refptr<PPB_Buffer_Impl> encrypted_resource;
|
| - if (!MakeMediaBufferResource(media::Decryptor::kAudio,
|
| - encrypted_buffer,
|
| - &encrypted_resource)) {
|
| - return false;
|
| - }
|
| -
|
| - // The resource should not be NULL for non-EOS buffer.
|
| - if (!encrypted_buffer->end_of_stream() && !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, request_id, &block_info)) {
|
| - return false;
|
| - }
|
| -
|
| - SetBufferToFreeInTrackingInfo(&block_info.tracking_info);
|
| -
|
| - // There is only one pending audio decode request at any time. This is
|
| - // enforced by the media pipeline. If this DCHECK is violated, our buffer
|
| - // reuse policy is not valid, and we may have race problems for the shared
|
| - // buffer.
|
| - 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;
|
| -
|
| - ScopedPPResource pp_resource(encrypted_resource.get());
|
| - plugin_decryption_interface_->DecryptAndDecode(pp_instance_,
|
| - PP_DECRYPTORSTREAMTYPE_AUDIO,
|
| - pp_resource,
|
| - &block_info);
|
| - return true;
|
| -}
|
| -
|
| -bool ContentDecryptorDelegate::DecryptAndDecodeVideo(
|
| - const scoped_refptr<media::DecoderBuffer>& encrypted_buffer,
|
| - const media::Decryptor::VideoDecodeCB& video_decode_cb) {
|
| - // |video_input_resource_| is not being used by the plugin now
|
| - // because there is only one pending video decode request at any time.
|
| - // This is enforced by the media pipeline.
|
| - scoped_refptr<PPB_Buffer_Impl> encrypted_resource;
|
| - if (!MakeMediaBufferResource(media::Decryptor::kVideo,
|
| - encrypted_buffer,
|
| - &encrypted_resource)) {
|
| - return false;
|
| - }
|
| -
|
| - // The resource should not be 0 for non-EOS buffer.
|
| - if (!encrypted_buffer->end_of_stream() && !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", "ContentDecryptorDelegate::DecryptAndDecodeVideo", request_id);
|
| -
|
| - PP_EncryptedBlockInfo block_info = {};
|
| - if (!MakeEncryptedBlockInfo(encrypted_buffer, request_id, &block_info)) {
|
| - return false;
|
| - }
|
| -
|
| - SetBufferToFreeInTrackingInfo(&block_info.tracking_info);
|
| -
|
| - // Only one pending video decode request at any time. This is enforced by the
|
| - // media pipeline. If this DCHECK is violated, our buffer
|
| - // reuse policy is not valid, and we may have race problems for the shared
|
| - // buffer.
|
| - 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.
|
| - ScopedPPResource pp_resource(encrypted_resource.get());
|
| - plugin_decryption_interface_->DecryptAndDecode(pp_instance_,
|
| - PP_DECRYPTORSTREAMTYPE_VIDEO,
|
| - pp_resource,
|
| - &block_info);
|
| - return true;
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::NeedKey(PP_Var key_system_var,
|
| - PP_Var session_id_var,
|
| - PP_Var init_data_var) {
|
| - // TODO(ddorwin): Remove from PPB_ContentDecryptor_Private.
|
| - NOTREACHED();
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::KeyAdded(PP_Var key_system_var,
|
| - PP_Var session_id_var) {
|
| - if (key_added_cb_.is_null())
|
| - return;
|
| -
|
| - StringVar* session_id_string = StringVar::FromPPVar(session_id_var);
|
| - if (!session_id_string) {
|
| - key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0);
|
| - return;
|
| - }
|
| -
|
| - key_added_cb_.Run(session_id_string->value());
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::KeyMessage(PP_Var key_system_var,
|
| - PP_Var session_id_var,
|
| - PP_Var message_var,
|
| - PP_Var default_url_var) {
|
| - if (key_message_cb_.is_null())
|
| - return;
|
| -
|
| - StringVar* session_id_string = StringVar::FromPPVar(session_id_var);
|
| -
|
| - ArrayBufferVar* message_array_buffer =
|
| - ArrayBufferVar::FromPPVar(message_var);
|
| -
|
| - std::vector<uint8> message;
|
| - if (message_array_buffer) {
|
| - const uint8* data = static_cast<const uint8*>(message_array_buffer->Map());
|
| - message.assign(data, data + message_array_buffer->ByteLength());
|
| - }
|
| -
|
| - StringVar* default_url_string = StringVar::FromPPVar(default_url_var);
|
| -
|
| - if (!session_id_string || !default_url_string) {
|
| - key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0);
|
| - return;
|
| - }
|
| -
|
| - key_message_cb_.Run(session_id_string->value(),
|
| - message,
|
| - 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 (key_error_cb_.is_null())
|
| - return;
|
| -
|
| - StringVar* session_id_string = StringVar::FromPPVar(session_id_var);
|
| - if (!session_id_string) {
|
| - key_error_cb_.Run(std::string(), media::MediaKeys::kUnknownError, 0);
|
| - return;
|
| - }
|
| -
|
| - key_error_cb_.Run(session_id_string->value(),
|
| - static_cast<media::MediaKeys::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;
|
| -
|
| - if (!success)
|
| - natural_size_ = gfx::Size();
|
| -
|
| - 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);
|
| -
|
| - FreeBuffer(block_info->tracking_info.buffer_id);
|
| -
|
| - 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() ||
|
| - mapper.size() < block_info->data_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()), block_info->data_size));
|
| - decrypted_buffer->set_timestamp(base::TimeDelta::FromMicroseconds(
|
| - block_info->tracking_info.timestamp));
|
| - decrypt_cb.Run(media::Decryptor::kSuccess, decrypted_buffer);
|
| -}
|
| -
|
| -// Use a non-class-member function here so that if for some reason
|
| -// ContentDecryptorDelegate is destroyed before VideoFrame calls this callback,
|
| -// we can still get the shared memory unmapped.
|
| -static void BufferNoLongerNeeded(
|
| - const scoped_refptr<PPB_Buffer_Impl>& ppb_buffer,
|
| - base::Closure buffer_no_longer_needed_cb) {
|
| - ppb_buffer->Unmap();
|
| - buffer_no_longer_needed_cb.Run();
|
| -}
|
| -
|
| -// Enters |resource|, maps shared memory and returns pointer of mapped data.
|
| -// Returns NULL if any error occurs.
|
| -static uint8* GetMappedBuffer(PP_Resource resource,
|
| - scoped_refptr<PPB_Buffer_Impl>* ppb_buffer) {
|
| - EnterResourceNoLock<PPB_Buffer_API> enter(resource, true);
|
| - if (!enter.succeeded())
|
| - return NULL;
|
| -
|
| - uint8* mapped_data = static_cast<uint8*>(enter.object()->Map());
|
| - if (!enter.object()->IsMapped() || !mapped_data)
|
| - return NULL;
|
| -
|
| - uint32_t mapped_size = 0;
|
| - if (!enter.object()->Describe(&mapped_size) || !mapped_size) {
|
| - enter.object()->Unmap();
|
| - return NULL;
|
| - }
|
| -
|
| - *ppb_buffer = static_cast<PPB_Buffer_Impl*>(enter.object());
|
| -
|
| - return mapped_data;
|
| -}
|
| -
|
| -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";
|
| - FreeBuffer(frame_info->tracking_info.buffer_id);
|
| - return;
|
| - }
|
| -
|
| - TRACE_EVENT_ASYNC_END0(
|
| - "eme", "ContentDecryptorDelegate::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) {
|
| - DCHECK(!frame_info->tracking_info.buffer_id);
|
| - video_decode_cb.Run(status, NULL);
|
| - return;
|
| - }
|
| -
|
| - scoped_refptr<PPB_Buffer_Impl> ppb_buffer;
|
| - uint8* frame_data = GetMappedBuffer(decrypted_frame, &ppb_buffer);
|
| - if (!frame_data) {
|
| - FreeBuffer(frame_info->tracking_info.buffer_id);
|
| - video_decode_cb.Run(media::Decryptor::kError, NULL);
|
| - return;
|
| - }
|
| -
|
| - gfx::Size frame_size(frame_info->width, frame_info->height);
|
| - DCHECK_EQ(frame_info->format, PP_DECRYPTEDFRAMEFORMAT_YV12);
|
| -
|
| - scoped_refptr<media::VideoFrame> decoded_frame =
|
| - media::VideoFrame::WrapExternalYuvData(
|
| - media::VideoFrame::YV12,
|
| - frame_size, gfx::Rect(frame_size), natural_size_,
|
| - frame_info->strides[PP_DECRYPTEDFRAMEPLANES_Y],
|
| - frame_info->strides[PP_DECRYPTEDFRAMEPLANES_U],
|
| - frame_info->strides[PP_DECRYPTEDFRAMEPLANES_V],
|
| - frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_Y],
|
| - frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_U],
|
| - frame_data + frame_info->plane_offsets[PP_DECRYPTEDFRAMEPLANES_V],
|
| - base::TimeDelta::FromMicroseconds(
|
| - frame_info->tracking_info.timestamp),
|
| - ppb_buffer->shared_memory()->handle(),
|
| - media::BindToLoop(
|
| - base::MessageLoopProxy::current(),
|
| - base::Bind(&BufferNoLongerNeeded, ppb_buffer,
|
| - base::Bind(&ContentDecryptorDelegate::FreeBuffer,
|
| - weak_this_,
|
| - frame_info->tracking_info.buffer_id))));
|
| -
|
| - video_decode_cb.Run(media::Decryptor::kSuccess, decoded_frame);
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::DeliverSamples(
|
| - PP_Resource audio_frames,
|
| - const PP_DecryptedBlockInfo* block_info) {
|
| - DCHECK(block_info);
|
| -
|
| - FreeBuffer(block_info->tracking_info.buffer_id);
|
| -
|
| - 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,
|
| - block_info->data_size,
|
| - &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:
|
| - // Release the shared memory as it can still be in use by the plugin.
|
| - // The next DecryptAndDecode() call will need to allocate a new shared
|
| - // memory buffer.
|
| - audio_input_resource_ = NULL;
|
| - 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:
|
| - // Release the shared memory as it can still be in use by the plugin.
|
| - // The next DecryptAndDecode() call will need to allocate a new shared
|
| - // memory buffer.
|
| - video_input_resource_ = NULL;
|
| - 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 ContentDecryptorDelegate::MakeMediaBufferResource(
|
| - media::Decryptor::StreamType stream_type,
|
| - const scoped_refptr<media::DecoderBuffer>& encrypted_buffer,
|
| - scoped_refptr<PPB_Buffer_Impl>* resource) {
|
| - TRACE_EVENT0("eme", "ContentDecryptorDelegate::MakeMediaBufferResource");
|
| -
|
| - // End of stream buffers are represented as null resources.
|
| - if (encrypted_buffer->end_of_stream()) {
|
| - *resource = NULL;
|
| - return true;
|
| - }
|
| -
|
| - DCHECK(stream_type == media::Decryptor::kAudio ||
|
| - stream_type == media::Decryptor::kVideo);
|
| - scoped_refptr<PPB_Buffer_Impl>& media_resource =
|
| - (stream_type == media::Decryptor::kAudio) ? audio_input_resource_ :
|
| - video_input_resource_;
|
| -
|
| - const size_t data_size = static_cast<size_t>(encrypted_buffer->data_size());
|
| - if (!media_resource.get() || media_resource->size() < data_size) {
|
| - // Either the buffer hasn't been created yet, or we have one that isn't big
|
| - // enough to fit |size| bytes.
|
| -
|
| - // Media resource size starts from |kMinimumMediaBufferSize| and grows
|
| - // exponentially to avoid frequent re-allocation of PPB_Buffer_Impl,
|
| - // which is usually expensive. Since input media buffers are compressed,
|
| - // they are usually small (compared to outputs). The over-allocated memory
|
| - // should be negligible.
|
| - const uint32_t kMinimumMediaBufferSize = 1024;
|
| - uint32_t media_resource_size =
|
| - media_resource.get() ? media_resource->size() : kMinimumMediaBufferSize;
|
| - while (media_resource_size < data_size)
|
| - media_resource_size *= 2;
|
| -
|
| - DVLOG(2) << "Size of media buffer for "
|
| - << ((stream_type == media::Decryptor::kAudio) ? "audio" : "video")
|
| - << " stream bumped to " << media_resource_size
|
| - << " bytes to fit input.";
|
| - media_resource = PPB_Buffer_Impl::CreateResource(pp_instance_,
|
| - media_resource_size);
|
| - if (!media_resource.get())
|
| - return false;
|
| - }
|
| -
|
| - BufferAutoMapper mapper(media_resource.get());
|
| - if (!mapper.data() || mapper.size() < data_size) {
|
| - media_resource = NULL;
|
| - return false;
|
| - }
|
| - memcpy(mapper.data(), encrypted_buffer->data(), data_size);
|
| -
|
| - *resource = media_resource;
|
| - return true;
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::FreeBuffer(uint32_t buffer_id) {
|
| - if (buffer_id)
|
| - free_buffers_.push(buffer_id);
|
| -}
|
| -
|
| -void ContentDecryptorDelegate::SetBufferToFreeInTrackingInfo(
|
| - PP_DecryptTrackingInfo* tracking_info) {
|
| - DCHECK_EQ(tracking_info->buffer_id, 0u);
|
| -
|
| - if (free_buffers_.empty())
|
| - return;
|
| -
|
| - tracking_info->buffer_id = free_buffers_.front();
|
| - free_buffers_.pop();
|
| -}
|
| -
|
| -bool ContentDecryptorDelegate::DeserializeAudioFrames(
|
| - PP_Resource audio_frames,
|
| - size_t data_size,
|
| - 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() ||
|
| - mapper.size() < static_cast<uint32_t>(data_size))
|
| - return false;
|
| -
|
| - // TODO(jrummell): Pass ownership of data() directly to AudioBuffer to avoid
|
| - // the copy. Since it is possible to get multiple buffers, it would need to be
|
| - // sliced and ref counted appropriately. http://crbug.com/255576.
|
| - const uint8* cur = static_cast<uint8*>(mapper.data());
|
| - size_t bytes_left = data_size;
|
| -
|
| - do {
|
| - int64 timestamp = 0;
|
| - int64 frame_size = -1;
|
| - const size_t 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 frames in the list.
|
| - if (frame_size <= 0 ||
|
| - bytes_left < base::checked_numeric_cast<size_t>(frame_size)) {
|
| - return false;
|
| - }
|
| -
|
| - const uint8* data[] = {cur};
|
| - int frame_count = frame_size / audio_bytes_per_frame_;
|
| - scoped_refptr<media::AudioBuffer> frame = media::AudioBuffer::CopyFrom(
|
| - audio_sample_format_,
|
| - audio_channel_count_,
|
| - frame_count,
|
| - data,
|
| - base::TimeDelta::FromMicroseconds(timestamp),
|
| - base::TimeDelta::FromMicroseconds(audio_samples_per_second_ /
|
| - frame_count));
|
| - frames->push_back(frame);
|
| -
|
| - cur += frame_size;
|
| - bytes_left -= frame_size;
|
| - } while (bytes_left > 0);
|
| -
|
| - return true;
|
| -}
|
| -
|
| -} // namespace ppapi
|
| -} // namespace webkit
|
|
|