| Index: webkit/renderer/media/crypto/ppapi/ffmpeg_cdm_audio_decoder.cc
|
| diff --git a/webkit/renderer/media/crypto/ppapi/ffmpeg_cdm_audio_decoder.cc b/webkit/renderer/media/crypto/ppapi/ffmpeg_cdm_audio_decoder.cc
|
| deleted file mode 100644
|
| index 1391de88a9a28279751ca945e6a47c51c6c71d9d..0000000000000000000000000000000000000000
|
| --- a/webkit/renderer/media/crypto/ppapi/ffmpeg_cdm_audio_decoder.cc
|
| +++ /dev/null
|
| @@ -1,413 +0,0 @@
|
| -// Copyright 2013 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/renderer/media/crypto/ppapi/ffmpeg_cdm_audio_decoder.h"
|
| -
|
| -#include <algorithm>
|
| -
|
| -#include "base/logging.h"
|
| -#include "media/base/audio_bus.h"
|
| -#include "media/base/audio_timestamp_helper.h"
|
| -#include "media/base/buffers.h"
|
| -#include "media/base/data_buffer.h"
|
| -#include "media/base/limits.h"
|
| -#include "webkit/renderer/media/crypto/ppapi/cdm/content_decryption_module.h"
|
| -
|
| -// Include FFmpeg header files.
|
| -extern "C" {
|
| -// Temporarily disable possible loss of data warning.
|
| -MSVC_PUSH_DISABLE_WARNING(4244);
|
| -#include <libavcodec/avcodec.h>
|
| -MSVC_POP_WARNING();
|
| -} // extern "C"
|
| -
|
| -namespace webkit_media {
|
| -
|
| -// Maximum number of channels with defined layout in src/media.
|
| -static const int kMaxChannels = 8;
|
| -
|
| -static AVCodecID CdmAudioCodecToCodecID(
|
| - cdm::AudioDecoderConfig::AudioCodec audio_codec) {
|
| - switch (audio_codec) {
|
| - case cdm::AudioDecoderConfig::kCodecVorbis:
|
| - return AV_CODEC_ID_VORBIS;
|
| - case cdm::AudioDecoderConfig::kCodecAac:
|
| - return AV_CODEC_ID_AAC;
|
| - case cdm::AudioDecoderConfig::kUnknownAudioCodec:
|
| - default:
|
| - NOTREACHED() << "Unsupported cdm::AudioCodec: " << audio_codec;
|
| - return AV_CODEC_ID_NONE;
|
| - }
|
| -}
|
| -
|
| -static void CdmAudioDecoderConfigToAVCodecContext(
|
| - const cdm::AudioDecoderConfig& config,
|
| - AVCodecContext* codec_context) {
|
| - codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
|
| - codec_context->codec_id = CdmAudioCodecToCodecID(config.codec);
|
| -
|
| - switch (config.bits_per_channel) {
|
| - case 8:
|
| - codec_context->sample_fmt = AV_SAMPLE_FMT_U8;
|
| - break;
|
| - case 16:
|
| - codec_context->sample_fmt = AV_SAMPLE_FMT_S16;
|
| - break;
|
| - case 32:
|
| - codec_context->sample_fmt = AV_SAMPLE_FMT_S32;
|
| - break;
|
| - default:
|
| - DVLOG(1) << "CdmAudioDecoderConfigToAVCodecContext() Unsupported bits "
|
| - "per channel: " << config.bits_per_channel;
|
| - codec_context->sample_fmt = AV_SAMPLE_FMT_NONE;
|
| - }
|
| -
|
| - codec_context->channels = config.channel_count;
|
| - codec_context->sample_rate = config.samples_per_second;
|
| -
|
| - if (config.extra_data) {
|
| - codec_context->extradata_size = config.extra_data_size;
|
| - codec_context->extradata = reinterpret_cast<uint8_t*>(
|
| - av_malloc(config.extra_data_size + FF_INPUT_BUFFER_PADDING_SIZE));
|
| - memcpy(codec_context->extradata, config.extra_data,
|
| - config.extra_data_size);
|
| - memset(codec_context->extradata + config.extra_data_size, '\0',
|
| - FF_INPUT_BUFFER_PADDING_SIZE);
|
| - } else {
|
| - codec_context->extradata = NULL;
|
| - codec_context->extradata_size = 0;
|
| - }
|
| -}
|
| -
|
| -FFmpegCdmAudioDecoder::FFmpegCdmAudioDecoder(cdm::Host* host)
|
| - : is_initialized_(false),
|
| - host_(host),
|
| - codec_context_(NULL),
|
| - av_frame_(NULL),
|
| - bits_per_channel_(0),
|
| - samples_per_second_(0),
|
| - channels_(0),
|
| - av_sample_format_(0),
|
| - bytes_per_frame_(0),
|
| - last_input_timestamp_(media::kNoTimestamp()),
|
| - output_bytes_to_drop_(0) {
|
| -}
|
| -
|
| -FFmpegCdmAudioDecoder::~FFmpegCdmAudioDecoder() {
|
| - ReleaseFFmpegResources();
|
| -}
|
| -
|
| -bool FFmpegCdmAudioDecoder::Initialize(const cdm::AudioDecoderConfig& config) {
|
| - DVLOG(1) << "Initialize()";
|
| -
|
| - if (!IsValidConfig(config)) {
|
| - LOG(ERROR) << "Initialize(): invalid audio decoder configuration.";
|
| - return false;
|
| - }
|
| -
|
| - if (is_initialized_) {
|
| - LOG(ERROR) << "Initialize(): Already initialized.";
|
| - return false;
|
| - }
|
| -
|
| - // Initialize AVCodecContext structure.
|
| - codec_context_ = avcodec_alloc_context3(NULL);
|
| - CdmAudioDecoderConfigToAVCodecContext(config, codec_context_);
|
| -
|
| - // MP3 decodes to S16P which we don't support, tell it to use S16 instead.
|
| - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P)
|
| - codec_context_->request_sample_fmt = AV_SAMPLE_FMT_S16;
|
| -
|
| - AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
|
| - if (!codec || avcodec_open2(codec_context_, codec, NULL) < 0) {
|
| - DLOG(ERROR) << "Could not initialize audio decoder: "
|
| - << codec_context_->codec_id;
|
| - return false;
|
| - }
|
| -
|
| - // Ensure avcodec_open2() respected our format request.
|
| - if (codec_context_->sample_fmt == AV_SAMPLE_FMT_S16P) {
|
| - DLOG(ERROR) << "Unable to configure a supported sample format: "
|
| - << codec_context_->sample_fmt;
|
| - 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_ = media::AudioBus::CreateWrapper(channels);
|
| - }
|
| -
|
| - // Success!
|
| - av_frame_ = 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;
|
| - output_timestamp_helper_.reset(
|
| - new media::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.
|
| - channels_ = codec_context_->channels;
|
| - av_sample_format_ = codec_context_->sample_fmt;
|
| -
|
| - return true;
|
| -}
|
| -
|
| -void FFmpegCdmAudioDecoder::Deinitialize() {
|
| - DVLOG(1) << "Deinitialize()";
|
| - ReleaseFFmpegResources();
|
| - is_initialized_ = false;
|
| - ResetTimestampState();
|
| -}
|
| -
|
| -void FFmpegCdmAudioDecoder::Reset() {
|
| - DVLOG(1) << "Reset()";
|
| - avcodec_flush_buffers(codec_context_);
|
| - ResetTimestampState();
|
| -}
|
| -
|
| -// static
|
| -bool FFmpegCdmAudioDecoder::IsValidConfig(
|
| - const cdm::AudioDecoderConfig& config) {
|
| - return config.codec != cdm::AudioDecoderConfig::kUnknownAudioCodec &&
|
| - config.channel_count > 0 &&
|
| - config.channel_count <= kMaxChannels &&
|
| - config.bits_per_channel > 0 &&
|
| - config.bits_per_channel <= media::limits::kMaxBitsPerSample &&
|
| - config.samples_per_second > 0 &&
|
| - config.samples_per_second <= media::limits::kMaxSampleRate;
|
| -}
|
| -
|
| -cdm::Status FFmpegCdmAudioDecoder::DecodeBuffer(
|
| - const uint8_t* compressed_buffer,
|
| - int32_t compressed_buffer_size,
|
| - int64_t input_timestamp,
|
| - cdm::AudioFrames* decoded_frames) {
|
| - DVLOG(1) << "DecodeBuffer()";
|
| - const bool is_end_of_stream = !compressed_buffer;
|
| - base::TimeDelta timestamp =
|
| - base::TimeDelta::FromMicroseconds(input_timestamp);
|
| -
|
| - bool is_vorbis = codec_context_->codec_id == AV_CODEC_ID_VORBIS;
|
| - if (!is_end_of_stream) {
|
| - if (last_input_timestamp_ == media::kNoTimestamp()) {
|
| - if (is_vorbis && timestamp < base::TimeDelta()) {
|
| - // Dropping frames for negative timestamps as outlined in section A.2
|
| - // in the Vorbis spec. http://xiph.org/vorbis/doc/Vorbis_I_spec.html
|
| - int frames_to_drop = floor(
|
| - 0.5 + -timestamp.InSecondsF() * samples_per_second_);
|
| - output_bytes_to_drop_ = bytes_per_frame_ * frames_to_drop;
|
| - } else {
|
| - last_input_timestamp_ = timestamp;
|
| - }
|
| - } else if (timestamp != media::kNoTimestamp()) {
|
| - if (timestamp < last_input_timestamp_) {
|
| - base::TimeDelta diff = timestamp - last_input_timestamp_;
|
| - DVLOG(1) << "Input timestamps are not monotonically increasing! "
|
| - << " ts " << timestamp.InMicroseconds() << " us"
|
| - << " diff " << diff.InMicroseconds() << " us";
|
| - return cdm::kDecodeError;
|
| - }
|
| -
|
| - last_input_timestamp_ = timestamp;
|
| - }
|
| - }
|
| -
|
| - AVPacket packet;
|
| - av_init_packet(&packet);
|
| - packet.data = const_cast<uint8_t*>(compressed_buffer);
|
| - packet.size = compressed_buffer_size;
|
| -
|
| - // 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
|
| - // skipping end of stream packets since they have a size of zero.
|
| - do {
|
| - // Reset frame to default values.
|
| - avcodec_get_frame_defaults(av_frame_);
|
| -
|
| - int frame_decoded = 0;
|
| - int result = avcodec_decode_audio4(
|
| - codec_context_, av_frame_, &frame_decoded, &packet);
|
| -
|
| - if (result < 0) {
|
| - DCHECK(!is_end_of_stream)
|
| - << "End of stream buffer produced an error! "
|
| - << "This is quite possibly a bug in the audio decoder not handling "
|
| - << "end of stream AVPackets correctly.";
|
| -
|
| - DLOG(ERROR)
|
| - << "Error decoding an audio frame with timestamp: "
|
| - << timestamp.InMicroseconds() << " us, duration: "
|
| - << timestamp.InMicroseconds() << " us, packet size: "
|
| - << compressed_buffer_size << " bytes";
|
| -
|
| - return cdm::kDecodeError;
|
| - }
|
| -
|
| - // Update packet size and data pointer in case we need to call the decoder
|
| - // with the remaining bytes from this packet.
|
| - packet.size -= result;
|
| - packet.data += result;
|
| -
|
| - if (output_timestamp_helper_->base_timestamp() == media::kNoTimestamp() &&
|
| - !is_end_of_stream) {
|
| - DCHECK(timestamp != media::kNoTimestamp());
|
| - if (output_bytes_to_drop_ > 0) {
|
| - // Currently Vorbis is the only codec that causes us to drop samples.
|
| - // If we have to drop samples it always means the timeline starts at 0.
|
| - DCHECK_EQ(codec_context_->codec_id, AV_CODEC_ID_VORBIS);
|
| - output_timestamp_helper_->SetBaseTimestamp(base::TimeDelta());
|
| - } else {
|
| - output_timestamp_helper_->SetBaseTimestamp(timestamp);
|
| - }
|
| - }
|
| -
|
| - int decoded_audio_size = 0;
|
| - if (frame_decoded) {
|
| - if (av_frame_->sample_rate != samples_per_second_ ||
|
| - av_frame_->channels != channels_ ||
|
| - av_frame_->format != av_sample_format_) {
|
| - DLOG(ERROR) << "Unsupported midstream configuration change!"
|
| - << " Sample Rate: " << av_frame_->sample_rate << " vs "
|
| - << samples_per_second_
|
| - << ", Channels: " << av_frame_->channels << " vs "
|
| - << channels_
|
| - << ", Sample Format: " << av_frame_->format << " vs "
|
| - << av_sample_format_;
|
| - return cdm::kDecodeError;
|
| - }
|
| -
|
| - 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<media::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;
|
| - }
|
| -
|
| - 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]));
|
| - }
|
| -
|
| - output = new media::DataBuffer(decoded_audio_size);
|
| - output->set_data_size(decoded_audio_size);
|
| -
|
| - 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 = media::DataBuffer::CopyFrom(
|
| - av_frame_->extended_data[0] + start_sample * bytes_per_frame_,
|
| - decoded_audio_size);
|
| - }
|
| -
|
| - base::TimeDelta output_timestamp =
|
| - output_timestamp_helper_->GetTimestamp();
|
| - output_timestamp_helper_->AddFrames(decoded_audio_size /
|
| - bytes_per_frame_);
|
| -
|
| - // Serialize the audio samples into |serialized_audio_frames_|.
|
| - SerializeInt64(output_timestamp.InMicroseconds());
|
| - SerializeInt64(output->data_size());
|
| - serialized_audio_frames_.insert(
|
| - serialized_audio_frames_.end(),
|
| - output->data(),
|
| - output->data() + output->data_size());
|
| - }
|
| - } while (packet.size > 0);
|
| -
|
| - if (!serialized_audio_frames_.empty()) {
|
| - decoded_frames->SetFrameBuffer(
|
| - host_->Allocate(serialized_audio_frames_.size()));
|
| - if (!decoded_frames->FrameBuffer()) {
|
| - LOG(ERROR) << "DecodeBuffer() cdm::Host::Allocate failed.";
|
| - return cdm::kDecodeError;
|
| - }
|
| - memcpy(decoded_frames->FrameBuffer()->Data(),
|
| - &serialized_audio_frames_[0],
|
| - serialized_audio_frames_.size());
|
| - decoded_frames->FrameBuffer()->SetSize(serialized_audio_frames_.size());
|
| - serialized_audio_frames_.clear();
|
| -
|
| - return cdm::kSuccess;
|
| - }
|
| -
|
| - return cdm::kNeedMoreData;
|
| -}
|
| -
|
| -void FFmpegCdmAudioDecoder::ResetTimestampState() {
|
| - output_timestamp_helper_->SetBaseTimestamp(media::kNoTimestamp());
|
| - last_input_timestamp_ = media::kNoTimestamp();
|
| - output_bytes_to_drop_ = 0;
|
| -}
|
| -
|
| -void FFmpegCdmAudioDecoder::ReleaseFFmpegResources() {
|
| - DVLOG(1) << "ReleaseFFmpegResources()";
|
| -
|
| - if (codec_context_) {
|
| - av_free(codec_context_->extradata);
|
| - avcodec_close(codec_context_);
|
| - av_free(codec_context_);
|
| - codec_context_ = NULL;
|
| - }
|
| - if (av_frame_) {
|
| - av_free(av_frame_);
|
| - av_frame_ = NULL;
|
| - }
|
| -}
|
| -
|
| -void FFmpegCdmAudioDecoder::SerializeInt64(int64 value) {
|
| - int previous_size = serialized_audio_frames_.size();
|
| - serialized_audio_frames_.resize(previous_size + sizeof(value));
|
| - memcpy(&serialized_audio_frames_[0] + previous_size, &value, sizeof(value));
|
| -}
|
| -
|
| -} // namespace webkit_media
|
|
|