diff options
author | dalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-22 23:19:39 +0000 |
---|---|---|
committer | dalecurtis@chromium.org <dalecurtis@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2013-10-22 23:19:39 +0000 |
commit | 455e7d8424f4c82bf22f70bf9894d55c8df95592 (patch) | |
tree | 8ec76ccb5ba54c691895bf8d689fb2812c843240 /media/cdm | |
parent | a42ac2783669b171f34e074d652bcd6a0195c1cd (diff) | |
download | chromium_src-455e7d8424f4c82bf22f70bf9894d55c8df95592.zip chromium_src-455e7d8424f4c82bf22f70bf9894d55c8df95592.tar.gz chromium_src-455e7d8424f4c82bf22f70bf9894d55c8df95592.tar.bz2 |
Plumb support for audio sample formats.
- Introduces a new PP_DecryptedSampleInfo structure which
contains the AudioFormat for use when delivering samples.
- Plumbs the structure through to ContentDecryptorDelegate.
- Extends the ContentDecryptorDelegate code to handle planar
data.
- Cleans up FFmpegCdmAudioDecoder to remove 3 memcpy's in the
common case (1 decode per packet) and 2 in the uncommon (> 1
decode per packet).
BUG=169105
TEST=eme browsertests
Review URL: https://codereview.chromium.org/26956002
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@230250 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/cdm')
-rw-r--r-- | media/cdm/ppapi/cdm_adapter.cc | 44 | ||||
-rw-r--r-- | media/cdm/ppapi/cdm_wrapper.h | 8 | ||||
-rw-r--r-- | media/cdm/ppapi/clear_key_cdm.cc | 16 | ||||
-rw-r--r-- | media/cdm/ppapi/clear_key_cdm.h | 8 | ||||
-rw-r--r-- | media/cdm/ppapi/ffmpeg_cdm_audio_decoder.cc | 162 | ||||
-rw-r--r-- | media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h | 7 |
6 files changed, 151 insertions, 94 deletions
diff --git a/media/cdm/ppapi/cdm_adapter.cc b/media/cdm/ppapi/cdm_adapter.cc index 6814a5e..92bb326 100644 --- a/media/cdm/ppapi/cdm_adapter.cc +++ b/media/cdm/ppapi/cdm_adapter.cc @@ -107,6 +107,26 @@ PP_DecryptedFrameFormat CdmVideoFormatToPpDecryptedFrameFormat( } } +PP_DecryptedSampleFormat CdmAudioFormatToPpDecryptedSampleFormat( + cdm::AudioFormat format) { + switch (format) { + case cdm::kAudioFormatU8: + return PP_DECRYPTEDSAMPLEFORMAT_U8; + case cdm::kAudioFormatS16: + return PP_DECRYPTEDSAMPLEFORMAT_S16; + case cdm::kAudioFormatS32: + return PP_DECRYPTEDSAMPLEFORMAT_S32; + case cdm::kAudioFormatF32: + return PP_DECRYPTEDSAMPLEFORMAT_F32; + case cdm::kAudioFormatPlanarS16: + return PP_DECRYPTEDSAMPLEFORMAT_PLANAR_S16; + case cdm::kAudioFormatPlanarF32: + return PP_DECRYPTEDSAMPLEFORMAT_PLANAR_F32; + default: + return PP_DECRYPTEDSAMPLEFORMAT_UNKNOWN; + } +} + cdm::AudioDecoderConfig::AudioCodec PpAudioCodecToCdmAudioCodec( PP_AudioCodec codec) { switch (codec) { @@ -675,30 +695,32 @@ void CdmAdapter::DeliverSamples(int32_t result, const PP_DecryptTrackingInfo& tracking_info) { PP_DCHECK(result == PP_OK); - PP_DecryptedBlockInfo decrypted_block_info; - decrypted_block_info.tracking_info = tracking_info; - decrypted_block_info.tracking_info.timestamp = 0; - decrypted_block_info.tracking_info.buffer_id = 0; - decrypted_block_info.data_size = 0; - decrypted_block_info.result = CdmStatusToPpDecryptResult(status); + PP_DecryptedSampleInfo decrypted_sample_info; + decrypted_sample_info.tracking_info = tracking_info; + decrypted_sample_info.tracking_info.timestamp = 0; + decrypted_sample_info.tracking_info.buffer_id = 0; + decrypted_sample_info.data_size = 0; + decrypted_sample_info.result = CdmStatusToPpDecryptResult(status); pp::Buffer_Dev buffer; - if (decrypted_block_info.result == PP_DECRYPTRESULT_SUCCESS) { + if (decrypted_sample_info.result == PP_DECRYPTRESULT_SUCCESS) { PP_DCHECK(audio_frames.get() && audio_frames->FrameBuffer()); if (!audio_frames.get() || !audio_frames->FrameBuffer()) { PP_NOTREACHED(); - decrypted_block_info.result = PP_DECRYPTRESULT_DECRYPT_ERROR; + decrypted_sample_info.result = PP_DECRYPTRESULT_DECRYPT_ERROR; } else { PpbBuffer* ppb_buffer = static_cast<PpbBuffer*>(audio_frames->FrameBuffer()); buffer = ppb_buffer->buffer_dev(); - decrypted_block_info.tracking_info.buffer_id = ppb_buffer->buffer_id(); - decrypted_block_info.data_size = ppb_buffer->Size(); + decrypted_sample_info.tracking_info.buffer_id = ppb_buffer->buffer_id(); + decrypted_sample_info.data_size = ppb_buffer->Size(); + decrypted_sample_info.format = + CdmAudioFormatToPpDecryptedSampleFormat(audio_frames->Format()); } } - pp::ContentDecryptor_Private::DeliverSamples(buffer, decrypted_block_info); + pp::ContentDecryptor_Private::DeliverSamples(buffer, decrypted_sample_info); } bool CdmAdapter::IsValidVideoFrame(const LinkedVideoFrame& video_frame) { diff --git a/media/cdm/ppapi/cdm_wrapper.h b/media/cdm/ppapi/cdm_wrapper.h index e660c25..370eecc 100644 --- a/media/cdm/ppapi/cdm_wrapper.h +++ b/media/cdm/ppapi/cdm_wrapper.h @@ -229,10 +229,10 @@ CdmWrapper* CdmWrapper::Create(const char* key_system, } // When updating the CdmAdapter, ensure you've updated the CdmWrapper to contain -// stub implementations for new or modified methods which the older CDM -// interface does not have. -COMPILE_ASSERT(cdm::ContentDecryptionModule_2::kVersion == - cdm::ContentDecryptionModule::kVersion, +// stub implementations for new or modified methods that the older CDM interface +// does not have. +COMPILE_ASSERT(cdm::ContentDecryptionModule::kVersion == + cdm::ContentDecryptionModule_2::kVersion, ensure_cdm_wrapper_templates_have_old_version_support); } // namespace media diff --git a/media/cdm/ppapi/clear_key_cdm.cc b/media/cdm/ppapi/clear_key_cdm.cc index d732500..071b2b3 100644 --- a/media/cdm/ppapi/clear_key_cdm.cc +++ b/media/cdm/ppapi/clear_key_cdm.cc @@ -136,11 +136,11 @@ void* CreateCdmInstance( return NULL; } - if (cdm_interface_version != cdm::ContentDecryptionModule_1::kVersion) + if (cdm_interface_version != cdm::ContentDecryptionModule_2::kVersion) return NULL; cdm::Host* host = static_cast<cdm::Host*>( - get_cdm_host_func(cdm::ContentDecryptionModule_1::kVersion, user_data)); + get_cdm_host_func(cdm::ContentDecryptionModule_2::kVersion, user_data)); if (!host) return NULL; @@ -417,7 +417,7 @@ cdm::Status ClearKeyCdm::DecryptAndDecodeFrame( cdm::Status ClearKeyCdm::DecryptAndDecodeSamples( const cdm::InputBuffer& encrypted_buffer, - cdm::AudioFrames_1* audio_frames) { + cdm::AudioFrames* audio_frames) { DVLOG(1) << "DecryptAndDecodeSamples()"; scoped_refptr<media::DecoderBuffer> buffer; @@ -500,6 +500,16 @@ cdm::Status ClearKeyCdm::DecryptToMediaDecoderBuffer( return cdm::kSuccess; } +void ClearKeyCdm::OnPlatformChallengeResponse( + const cdm::PlatformChallengeResponse& response) { + NOTIMPLEMENTED(); +} + +void ClearKeyCdm::OnQueryOutputProtectionStatus( + uint32_t link_mask, uint32_t output_protection_mask) { + NOTIMPLEMENTED(); +}; + #if defined(CLEAR_KEY_CDM_USE_FAKE_AUDIO_DECODER) int64 ClearKeyCdm::CurrentTimeStampInMicroseconds() const { return output_timestamp_base_in_microseconds_ + diff --git a/media/cdm/ppapi/clear_key_cdm.h b/media/cdm/ppapi/clear_key_cdm.h index c77e4b4..a0d951c 100644 --- a/media/cdm/ppapi/clear_key_cdm.h +++ b/media/cdm/ppapi/clear_key_cdm.h @@ -28,7 +28,7 @@ class DecoderBuffer; class FFmpegCdmAudioDecoder; // Clear key implementation of the cdm::ContentDecryptionModule interface. -class ClearKeyCdm : public cdm::ContentDecryptionModule_1 { +class ClearKeyCdm : public cdm::ContentDecryptionModule_2 { public: explicit ClearKeyCdm(cdm::Host* host); virtual ~ClearKeyCdm(); @@ -56,8 +56,12 @@ class ClearKeyCdm : public cdm::ContentDecryptionModule_1 { cdm::VideoFrame* video_frame) OVERRIDE; virtual cdm::Status DecryptAndDecodeSamples( const cdm::InputBuffer& encrypted_buffer, - cdm::AudioFrames_1* audio_frames) OVERRIDE; + cdm::AudioFrames* audio_frames) OVERRIDE; virtual void Destroy() OVERRIDE; + virtual void OnPlatformChallengeResponse( + const cdm::PlatformChallengeResponse& response) OVERRIDE; + virtual void OnQueryOutputProtectionStatus( + uint32_t link_mask, uint32_t output_protection_mask) OVERRIDE; private: // TODO(xhwang): After we removed DecryptorClient. We probably can also remove diff --git a/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.cc b/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.cc index 3e2ca65..97d04e8 100644 --- a/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.cc +++ b/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.cc @@ -80,10 +80,59 @@ static void CdmAudioDecoderConfigToAVCodecContext( } } +static cdm::AudioFormat AVSampleFormatToCdmAudioFormat( + AVSampleFormat sample_format) { + switch (sample_format) { + case AV_SAMPLE_FMT_U8: + return cdm::kAudioFormatU8; + case AV_SAMPLE_FMT_S16: + return cdm::kAudioFormatS16; + case AV_SAMPLE_FMT_S32: + return cdm::kAudioFormatS32; + case AV_SAMPLE_FMT_FLT: + return cdm::kAudioFormatF32; + case AV_SAMPLE_FMT_S16P: + return cdm::kAudioFormatPlanarS16; + case AV_SAMPLE_FMT_FLTP: + return cdm::kAudioFormatPlanarF32; + default: + DVLOG(1) << "Unknown AVSampleFormat: " << sample_format; + } + return cdm::kUnknownAudioFormat; +} + +static void CopySamples(cdm::AudioFormat cdm_format, + int decoded_audio_size, + const AVFrame& av_frame, + uint8_t* output_buffer) { + switch (cdm_format) { + case cdm::kAudioFormatU8: + case cdm::kAudioFormatS16: + case cdm::kAudioFormatS32: + case cdm::kAudioFormatF32: + memcpy(output_buffer, av_frame.data[0], decoded_audio_size); + break; + case cdm::kAudioFormatPlanarS16: + case cdm::kAudioFormatPlanarF32: { + const int decoded_size_per_channel = + decoded_audio_size / av_frame.channels; + for (int i = 0; i < av_frame.channels; ++i) { + memcpy(output_buffer, + av_frame.extended_data[i], + decoded_size_per_channel); + output_buffer += decoded_size_per_channel; + } + break; + } + default: + NOTREACHED() << "Unsupported CDM Audio Format!"; + memset(output_buffer, 0, decoded_audio_size); + } +} + FFmpegCdmAudioDecoder::FFmpegCdmAudioDecoder(cdm::Host* host) : is_initialized_(false), host_(host), - bits_per_channel_(0), samples_per_second_(0), channels_(0), av_sample_format_(0), @@ -98,7 +147,6 @@ FFmpegCdmAudioDecoder::~FFmpegCdmAudioDecoder() { bool FFmpegCdmAudioDecoder::Initialize(const cdm::AudioDecoderConfig& config) { DVLOG(1) << "Initialize()"; - if (!IsValidConfig(config)) { LOG(ERROR) << "Initialize(): invalid audio decoder configuration."; return false; @@ -131,27 +179,12 @@ bool FFmpegCdmAudioDecoder::Initialize(const cdm::AudioDecoderConfig& config) { return false; } - // Some codecs will only output float data, so we need to convert to integer - // before returning the decoded buffer. - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP || - codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { - // Preallocate the AudioBus for float conversions. We can treat interleaved - // float data as a single planar channel since our output is expected in an - // interleaved format anyways. - int channels = codec_context_->channels; - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) - channels = 1; - converter_bus_ = AudioBus::CreateWrapper(channels); - } - // Success! av_frame_.reset(avcodec_alloc_frame()); - bits_per_channel_ = config.bits_per_channel; samples_per_second_ = config.samples_per_second; - bytes_per_frame_ = codec_context_->channels * bits_per_channel_ / 8; + bytes_per_frame_ = codec_context_->channels * config.bits_per_channel / 8; output_timestamp_helper_.reset( new AudioTimestampHelper(config.samples_per_second)); - serialized_audio_frames_.reserve(bytes_per_frame_ * samples_per_second_); is_initialized_ = true; // Store initial values to guard against midstream configuration changes. @@ -190,7 +223,7 @@ cdm::Status FFmpegCdmAudioDecoder::DecodeBuffer( const uint8_t* compressed_buffer, int32_t compressed_buffer_size, int64_t input_timestamp, - cdm::AudioFrames_1* decoded_frames) { + cdm::AudioFrames* decoded_frames) { DVLOG(1) << "DecodeBuffer()"; const bool is_end_of_stream = !compressed_buffer; base::TimeDelta timestamp = @@ -226,6 +259,12 @@ cdm::Status FFmpegCdmAudioDecoder::DecodeBuffer( packet.data = const_cast<uint8_t*>(compressed_buffer); packet.size = compressed_buffer_size; + // Tell the CDM what AudioFormat we're using. + const cdm::AudioFormat cdm_format = AVSampleFormatToCdmAudioFormat( + static_cast<AVSampleFormat>(av_sample_format_)); + DCHECK_NE(cdm_format, cdm::kUnknownAudioFormat); + decoded_frames->SetFormat(cdm_format); + // Each audio packet may contain several frames, so we must call the decoder // until we've exhausted the packet. Regardless of the packet size we always // want to hand it to the decoder at least once, otherwise we would end up @@ -289,76 +328,63 @@ cdm::Status FFmpegCdmAudioDecoder::DecodeBuffer( decoded_audio_size = av_samples_get_buffer_size( NULL, codec_context_->channels, av_frame_->nb_samples, codec_context_->sample_fmt, 1); - // If we're decoding into float, adjust audio size. - if (converter_bus_ && bits_per_channel_ / 8 != sizeof(float)) { - DCHECK(codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT || - codec_context_->sample_fmt == AV_SAMPLE_FMT_FLTP); - decoded_audio_size *= - static_cast<float>(bits_per_channel_ / 8) / sizeof(float); - } } - int start_sample = 0; if (decoded_audio_size > 0 && output_bytes_to_drop_ > 0) { DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) << "Decoder didn't output full frames"; int dropped_size = std::min(decoded_audio_size, output_bytes_to_drop_); - start_sample = dropped_size / bytes_per_frame_; decoded_audio_size -= dropped_size; output_bytes_to_drop_ -= dropped_size; } - scoped_refptr<DataBuffer> output; if (decoded_audio_size > 0) { DCHECK_EQ(decoded_audio_size % bytes_per_frame_, 0) << "Decoder didn't output full frames"; - // Convert float data using an AudioBus. - if (converter_bus_) { - // Setup the AudioBus as a wrapper of the AVFrame data and then use - // AudioBus::ToInterleaved() to convert the data as necessary. - int skip_frames = start_sample; - int total_frames = av_frame_->nb_samples; - int frames_to_interleave = decoded_audio_size / bytes_per_frame_; - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_FLT) { - DCHECK_EQ(converter_bus_->channels(), 1); - total_frames *= codec_context_->channels; - skip_frames *= codec_context_->channels; - frames_to_interleave *= codec_context_->channels; - } + base::TimeDelta output_timestamp = + output_timestamp_helper_->GetTimestamp(); + output_timestamp_helper_->AddFrames(decoded_audio_size / + bytes_per_frame_); - converter_bus_->set_frames(total_frames); - for (int i = 0; i < converter_bus_->channels(); ++i) { - converter_bus_->SetChannelData(i, reinterpret_cast<float*>( - av_frame_->extended_data[i])); + // If we've exhausted the packet in the first decode we can write directly + // into the frame buffer instead of a multistep serialization approach. + if (serialized_audio_frames_.empty() && !packet.size) { + const uint32_t buffer_size = decoded_audio_size + sizeof(int64) * 2; + decoded_frames->SetFrameBuffer(host_->Allocate(buffer_size)); + if (!decoded_frames->FrameBuffer()) { + LOG(ERROR) << "DecodeBuffer() cdm::Host::Allocate failed."; + return cdm::kDecodeError; } + decoded_frames->FrameBuffer()->SetSize(buffer_size); + uint8_t* output_buffer = decoded_frames->FrameBuffer()->Data(); - output = new DataBuffer(decoded_audio_size); - output->set_data_size(decoded_audio_size); + const int64 timestamp = output_timestamp.InMicroseconds(); + memcpy(output_buffer, ×tamp, sizeof(timestamp)); + output_buffer += sizeof(timestamp); - DCHECK_EQ(frames_to_interleave, converter_bus_->frames() - skip_frames); - converter_bus_->ToInterleavedPartial( - skip_frames, frames_to_interleave, bits_per_channel_ / 8, - output->writable_data()); - } else { - output = DataBuffer::CopyFrom( - av_frame_->extended_data[0] + start_sample * bytes_per_frame_, - decoded_audio_size); - } + const int64 output_size = decoded_audio_size; + memcpy(output_buffer, &output_size, sizeof(output_size)); + output_buffer += sizeof(output_size); - base::TimeDelta output_timestamp = - output_timestamp_helper_->GetTimestamp(); - output_timestamp_helper_->AddFrames(decoded_audio_size / - bytes_per_frame_); + // Copy the samples and return success. + CopySamples( + cdm_format, decoded_audio_size, *av_frame_, output_buffer); + return cdm::kSuccess; + } - // Serialize the audio samples into |serialized_audio_frames_|. + // There are still more frames to decode, so we need to serialize them in + // a secondary buffer since we don't know their sizes ahead of time (which + // is required to allocate the FrameBuffer object). SerializeInt64(output_timestamp.InMicroseconds()); - SerializeInt64(output->data_size()); - serialized_audio_frames_.insert( - serialized_audio_frames_.end(), - output->data(), - output->data() + output->data_size()); + SerializeInt64(decoded_audio_size); + + const size_t previous_size = serialized_audio_frames_.size(); + serialized_audio_frames_.resize(previous_size + decoded_audio_size); + uint8_t* output_buffer = &serialized_audio_frames_[0] + previous_size; + CopySamples( + cdm_format, decoded_audio_size, *av_frame_, output_buffer); } } while (packet.size > 0); @@ -395,7 +421,7 @@ void FFmpegCdmAudioDecoder::ReleaseFFmpegResources() { } void FFmpegCdmAudioDecoder::SerializeInt64(int64 value) { - int previous_size = serialized_audio_frames_.size(); + const size_t previous_size = serialized_audio_frames_.size(); serialized_audio_frames_.resize(previous_size + sizeof(value)); memcpy(&serialized_audio_frames_[0] + previous_size, &value, sizeof(value)); } diff --git a/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h b/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h index 35a01df..6e170ad 100644 --- a/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h +++ b/media/cdm/ppapi/ffmpeg_cdm_audio_decoder.h @@ -49,7 +49,7 @@ class FFmpegCdmAudioDecoder { cdm::Status DecodeBuffer(const uint8_t* compressed_buffer, int32_t compressed_buffer_size, int64_t timestamp, - cdm::AudioFrames_1* decoded_frames); + cdm::AudioFrames* decoded_frames); private: void ResetTimestampState(); @@ -68,7 +68,6 @@ class FFmpegCdmAudioDecoder { scoped_ptr_malloc<AVFrame, ScopedPtrAVFreeFrame> av_frame_; // Audio format. - int bits_per_channel_; int samples_per_second_; int channels_; @@ -80,10 +79,6 @@ class FFmpegCdmAudioDecoder { int bytes_per_frame_; base::TimeDelta last_input_timestamp_; - // We may need to convert the audio data coming out of FFmpeg from planar - // float to integer. - scoped_ptr<AudioBus> converter_bus_; - // Number of output sample bytes to drop before generating output buffers. // This is required for handling negative timestamps when decoding Vorbis // audio, for example. |