summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-21 22:44:04 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-09-21 22:44:04 +0000
commit1ab1c24bda1b30b57e040b3b077a001d095fcab3 (patch)
tree59e01bd62cc44596a5d8df6c7e63e6414846b4a8 /media
parent735586ec444f701f94219fbb59ff01f11b064071 (diff)
downloadchromium_src-1ab1c24bda1b30b57e040b3b077a001d095fcab3.zip
chromium_src-1ab1c24bda1b30b57e040b3b077a001d095fcab3.tar.gz
chromium_src-1ab1c24bda1b30b57e040b3b077a001d095fcab3.tar.bz2
Introduce AudioDecoderConfig to migrate away from GetAVStream().
Instead add DemuxerStream::audio_decoder_config() to break FFmpegAudioDecoder's dependency on the AVCodecContext object maintained by FFmpegDemuxer. Review URL: http://codereview.chromium.org/7867051 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@102183 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/audio_decoder_config.cc87
-rw-r--r--media/base/audio_decoder_config.h75
-rw-r--r--media/base/demuxer_stream.h5
-rw-r--r--media/base/filters.h2
-rw-r--r--media/base/media_log.cc2
-rw-r--r--media/base/mock_filters.h4
-rw-r--r--media/base/pipeline_status.h2
-rw-r--r--media/base/video_decoder_config.h2
-rw-r--r--media/ffmpeg/ffmpeg_common.cc111
-rw-r--r--media/ffmpeg/ffmpeg_common.h8
-rw-r--r--media/filters/adaptive_demuxer.cc4
-rw-r--r--media/filters/adaptive_demuxer.h1
-rw-r--r--media/filters/audio_renderer_base.cc2
-rw-r--r--media/filters/audio_renderer_base_unittest.cc2
-rw-r--r--media/filters/chunk_demuxer.cc10
-rw-r--r--media/filters/chunk_demuxer_unittest.cc32
-rw-r--r--media/filters/dummy_demuxer.cc5
-rw-r--r--media/filters/dummy_demuxer.h3
-rw-r--r--media/filters/ffmpeg_audio_decoder.cc58
-rw-r--r--media/filters/ffmpeg_audio_decoder.h4
-rw-r--r--media/filters/ffmpeg_audio_decoder_unittest.cc43
-rw-r--r--media/filters/ffmpeg_demuxer.cc6
-rw-r--r--media/filters/ffmpeg_demuxer.h22
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc13
-rw-r--r--media/media.gyp2
-rw-r--r--media/video/ffmpeg_video_decode_engine_unittest.cc6
26 files changed, 426 insertions, 85 deletions
diff --git a/media/base/audio_decoder_config.cc b/media/base/audio_decoder_config.cc
new file mode 100644
index 0000000..80b23e2
--- /dev/null
+++ b/media/base/audio_decoder_config.cc
@@ -0,0 +1,87 @@
+// Copyright (c) 2011 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 "media/base/audio_decoder_config.h"
+
+#include "base/logging.h"
+#include "media/base/limits.h"
+
+namespace media {
+
+AudioDecoderConfig::AudioDecoderConfig()
+ : codec_(kUnknownAudioCodec),
+ bits_per_channel_(0),
+ channel_layout_(CHANNEL_LAYOUT_UNSUPPORTED),
+ samples_per_second_(0),
+ extra_data_size_(0) {
+}
+
+AudioDecoderConfig::AudioDecoderConfig(AudioCodec codec,
+ int bits_per_channel,
+ ChannelLayout channel_layout,
+ int samples_per_second,
+ const uint8* extra_data,
+ size_t extra_data_size) {
+ Initialize(codec, bits_per_channel, channel_layout, samples_per_second,
+ extra_data, extra_data_size);
+}
+
+void AudioDecoderConfig::Initialize(AudioCodec codec,
+ int bits_per_channel,
+ ChannelLayout channel_layout,
+ int samples_per_second,
+ const uint8* extra_data,
+ size_t extra_data_size) {
+ CHECK((extra_data_size != 0) == (extra_data != NULL));
+
+ codec_ = codec;
+ bits_per_channel_ = bits_per_channel;
+ channel_layout_ = channel_layout;
+ samples_per_second_ = samples_per_second;
+ extra_data_size_ = extra_data_size;
+
+ if (extra_data_size_ > 0) {
+ extra_data_.reset(new uint8[extra_data_size_]);
+ memcpy(extra_data_.get(), extra_data, extra_data_size_);
+ } else {
+ extra_data_.reset();
+ }
+}
+
+AudioDecoderConfig::~AudioDecoderConfig() {}
+
+bool AudioDecoderConfig::IsValidConfig() const {
+ return codec_ != kUnknownAudioCodec &&
+ channel_layout_ != CHANNEL_LAYOUT_UNSUPPORTED &&
+ bits_per_channel_ > 0 &&
+ bits_per_channel_ <= Limits::kMaxBitsPerSample &&
+ samples_per_second_ > 0 &&
+ samples_per_second_ <= Limits::kMaxSampleRate;
+}
+
+AudioCodec AudioDecoderConfig::codec() const {
+ return codec_;
+}
+
+int AudioDecoderConfig::bits_per_channel() const {
+ return bits_per_channel_;
+}
+
+ChannelLayout AudioDecoderConfig::channel_layout() const {
+ return channel_layout_;
+}
+
+int AudioDecoderConfig::samples_per_second() const {
+ return samples_per_second_;
+}
+
+uint8* AudioDecoderConfig::extra_data() const {
+ return extra_data_.get();
+}
+
+size_t AudioDecoderConfig::extra_data_size() const {
+ return extra_data_size_;
+}
+
+} // namespace media
diff --git a/media/base/audio_decoder_config.h b/media/base/audio_decoder_config.h
new file mode 100644
index 0000000..0245f42
--- /dev/null
+++ b/media/base/audio_decoder_config.h
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 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 MEDIA_BASE_AUDIO_DECODER_CONFIG_H_
+#define MEDIA_BASE_AUDIO_DECODER_CONFIG_H_
+
+#include "base/basictypes.h"
+#include "base/memory/scoped_ptr.h"
+#include "media/base/channel_layout.h"
+#include "media/base/media_export.h"
+
+namespace media {
+
+enum AudioCodec {
+ kUnknownAudioCodec,
+ kCodecAAC,
+ kCodecMP3,
+ kCodecPCM,
+ kCodecVorbis,
+
+ // DO NOT ADD RANDOM AUDIO CODECS!
+ //
+ // The only acceptable time to add a new codec is if there is production code
+ // that uses said codec in the same CL.
+};
+
+class MEDIA_EXPORT AudioDecoderConfig {
+ public:
+ // Constructs an uninitialized object. Clients should call Initialize() with
+ // appropriate values before using.
+ AudioDecoderConfig();
+
+ // Constructs an initialized object. It is acceptable to pass in NULL for
+ // |extra_data|, otherwise the memory is copied.
+ AudioDecoderConfig(AudioCodec codec, int bits_per_channel,
+ ChannelLayout channel_layout, int samples_per_second,
+ const uint8* extra_data, size_t extra_data_size);
+
+ ~AudioDecoderConfig();
+
+ // Resets the internal state of this object.
+ void Initialize(AudioCodec codec, int bits_per_channel,
+ ChannelLayout channel_layout, int samples_per_second,
+ const uint8* extra_data, size_t extra_data_size);
+
+ // Returns true if this object has appropriate configuration values, false
+ // otherwise.
+ bool IsValidConfig() const;
+
+ AudioCodec codec() const;
+ int bits_per_channel() const;
+ ChannelLayout channel_layout() const;
+ int samples_per_second() const;
+
+ // Optional byte data required to initialize audio decoders such as Vorbis
+ // codebooks.
+ uint8* extra_data() const;
+ size_t extra_data_size() const;
+
+ private:
+ AudioCodec codec_;
+ int bits_per_channel_;
+ ChannelLayout channel_layout_;
+ int samples_per_second_;
+
+ scoped_array<uint8> extra_data_;
+ size_t extra_data_size_;
+
+ DISALLOW_COPY_AND_ASSIGN(AudioDecoderConfig);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_AUDIO_DECODER_CONFIG_H_
diff --git a/media/base/demuxer_stream.h b/media/base/demuxer_stream.h
index 5256115..e81423e 100644
--- a/media/base/demuxer_stream.h
+++ b/media/base/demuxer_stream.h
@@ -13,6 +13,7 @@ struct AVStream;
namespace media {
+class AudioDecoderConfig;
class Buffer;
class MEDIA_EXPORT DemuxerStream
@@ -34,6 +35,10 @@ class MEDIA_EXPORT DemuxerStream
// Returns an |AVStream*| if supported, or NULL.
virtual AVStream* GetAVStream();
+ // Returns the audio decoder configuration. It is an error to call this method
+ // if type() != AUDIO.
+ virtual const AudioDecoderConfig& audio_decoder_config() = 0;
+
// Returns the type of stream.
virtual Type type() = 0;
diff --git a/media/base/filters.h b/media/base/filters.h
index a379a58..c11c943 100644
--- a/media/base/filters.h
+++ b/media/base/filters.h
@@ -231,7 +231,7 @@ class MEDIA_EXPORT AudioDecoder : public Filter {
// Returns various information about the decoded audio format.
virtual int bits_per_channel() = 0;
virtual ChannelLayout channel_layout() = 0;
- virtual int sample_rate() = 0;
+ virtual int samples_per_second() = 0;
protected:
AudioDecoder();
diff --git a/media/base/media_log.cc b/media/base/media_log.cc
index 62f509e..af205dd 100644
--- a/media/base/media_log.cc
+++ b/media/base/media_log.cc
@@ -137,6 +137,8 @@ const char* MediaLog::PipelineStatusToString(PipelineStatus status) {
return "demuxer: no supported streams";
case DEMUXER_ERROR_COULD_NOT_CREATE_THREAD:
return "demuxer: could not create thread";
+ case DECODER_ERROR_NOT_SUPPORTED:
+ return "decoder: not supported";
case DATASOURCE_ERROR_URL_NOT_SUPPORTED:
return "data source: url not supported";
}
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index a836f0b..9b16ff5 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -15,6 +15,7 @@
#include <string>
+#include "media/base/audio_decoder_config.h"
#include "media/base/demuxer.h"
#include "media/base/filters.h"
#include "media/base/filter_collection.h"
@@ -160,6 +161,7 @@ class MockDemuxerStream : public DemuxerStream {
MOCK_METHOD0(type, Type());
MOCK_METHOD1(Read, void(const ReadCallback& read_callback));
MOCK_METHOD0(GetAVStream, AVStream*());
+ MOCK_METHOD0(audio_decoder_config, const AudioDecoderConfig&());
MOCK_METHOD0(EnableBitstreamConverter, void());
protected:
@@ -214,7 +216,7 @@ class MockAudioDecoder : public AudioDecoder {
MOCK_METHOD1(ProduceAudioSamples, void(scoped_refptr<Buffer>));
MOCK_METHOD0(bits_per_channel, int(void));
MOCK_METHOD0(channel_layout, ChannelLayout(void));
- MOCK_METHOD0(sample_rate, int(void));
+ MOCK_METHOD0(samples_per_second, int(void));
void ConsumeAudioSamplesForTest(scoped_refptr<Buffer> buffer) {
AudioDecoder::ConsumeAudioSamples(buffer);
diff --git a/media/base/pipeline_status.h b/media/base/pipeline_status.h
index dcbda09..508d2d6 100644
--- a/media/base/pipeline_status.h
+++ b/media/base/pipeline_status.h
@@ -30,6 +30,8 @@ enum PipelineStatus {
DEMUXER_ERROR_COULD_NOT_PARSE,
DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
DEMUXER_ERROR_COULD_NOT_CREATE_THREAD,
+ // Decoder related errors.
+ DECODER_ERROR_NOT_SUPPORTED,
// DataSourceFactory errors.
DATASOURCE_ERROR_URL_NOT_SUPPORTED,
};
diff --git a/media/base/video_decoder_config.h b/media/base/video_decoder_config.h
index fcfd3e7..2c6bd45 100644
--- a/media/base/video_decoder_config.h
+++ b/media/base/video_decoder_config.h
@@ -14,7 +14,7 @@
namespace media {
enum VideoCodec {
- kUnknown,
+ kUnknownVideoCodec,
kCodecH264,
kCodecVC1,
kCodecMPEG2,
diff --git a/media/ffmpeg/ffmpeg_common.cc b/media/ffmpeg/ffmpeg_common.cc
index e0e40f3..72323c7 100644
--- a/media/ffmpeg/ffmpeg_common.cc
+++ b/media/ffmpeg/ffmpeg_common.cc
@@ -21,6 +21,113 @@ int64 ConvertToTimeBase(const AVRational& time_base,
return av_rescale_q(timestamp.InMicroseconds(), kMicrosBase, time_base);
}
+static AudioCodec CodecIDToAudioCodec(CodecID codec_id) {
+ switch (codec_id) {
+ case CODEC_ID_AAC:
+ return kCodecAAC;
+ case CODEC_ID_MP3:
+ return kCodecMP3;
+ case CODEC_ID_VORBIS:
+ return kCodecVorbis;
+ case CODEC_ID_PCM_U8:
+ case CODEC_ID_PCM_S16LE:
+ case CODEC_ID_PCM_S32LE:
+ return kCodecPCM;
+ default:
+ NOTREACHED();
+ }
+ return kUnknownAudioCodec;
+}
+
+static CodecID AudioCodecToCodecID(AudioCodec audio_codec,
+ int bits_per_channel) {
+ switch (audio_codec) {
+ case kUnknownAudioCodec:
+ return CODEC_ID_NONE;
+ case kCodecAAC:
+ return CODEC_ID_AAC;
+ case kCodecMP3:
+ return CODEC_ID_MP3;
+ case kCodecPCM:
+ switch (bits_per_channel) {
+ case 8:
+ return CODEC_ID_PCM_U8;
+ case 16:
+ return CODEC_ID_PCM_S16LE;
+ case 32:
+ return CODEC_ID_PCM_S32LE;
+ default:
+ NOTREACHED() << "Unsupported bits_per_channel: " << bits_per_channel;
+ }
+ case kCodecVorbis:
+ return CODEC_ID_VORBIS;
+ default:
+ NOTREACHED();
+ }
+ return CODEC_ID_NONE;
+}
+
+void AVCodecContextToAudioDecoderConfig(
+ const AVCodecContext* codec_context,
+ AudioDecoderConfig* config) {
+ DCHECK_EQ(codec_context->codec_type, AVMEDIA_TYPE_AUDIO);
+
+ AudioCodec codec = CodecIDToAudioCodec(codec_context->codec_id);
+ int bits_per_channel = av_get_bits_per_sample_fmt(codec_context->sample_fmt);
+ ChannelLayout channel_layout =
+ ChannelLayoutToChromeChannelLayout(codec_context->channel_layout,
+ codec_context->channels);
+ int samples_per_second = codec_context->sample_rate;
+
+ config->Initialize(codec,
+ bits_per_channel,
+ channel_layout,
+ samples_per_second,
+ codec_context->extradata,
+ codec_context->extradata_size);
+}
+
+void AudioDecoderConfigToAVCodecContext(const AudioDecoderConfig& config,
+ AVCodecContext* codec_context) {
+ codec_context->codec_type = AVMEDIA_TYPE_AUDIO;
+ codec_context->codec_id = AudioCodecToCodecID(config.codec(),
+ config.bits_per_channel());
+
+ 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:
+ NOTIMPLEMENTED() << "TODO(scherkus): DO SOMETHING BETTER HERE?";
+ codec_context->sample_fmt = AV_SAMPLE_FMT_NONE;
+ }
+
+ // TODO(scherkus): should we set |channel_layout|? I'm not sure if FFmpeg uses
+ // said information to decode.
+ codec_context->channels =
+ ChannelLayoutToChannelCount(config.channel_layout());
+ 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;
+ }
+}
+
VideoCodec CodecIDToVideoCodec(CodecID codec_id) {
switch (codec_id) {
case CODEC_ID_VC1:
@@ -38,12 +145,12 @@ VideoCodec CodecIDToVideoCodec(CodecID codec_id) {
default:
NOTREACHED();
}
- return kUnknown;
+ return kUnknownVideoCodec;
}
CodecID VideoCodecToCodecID(VideoCodec video_codec) {
switch (video_codec) {
- case kUnknown:
+ case kUnknownVideoCodec:
return CODEC_ID_NONE;
case kCodecVC1:
return CODEC_ID_VC1;
diff --git a/media/ffmpeg/ffmpeg_common.h b/media/ffmpeg/ffmpeg_common.h
index 6f04e07..dbdbc6e 100644
--- a/media/ffmpeg/ffmpeg_common.h
+++ b/media/ffmpeg/ffmpeg_common.h
@@ -11,6 +11,7 @@
#include "base/compiler_specific.h"
#include "base/memory/singleton.h"
#include "base/time.h"
+#include "media/base/audio_decoder_config.h"
#include "media/base/channel_layout.h"
#include "media/base/media_export.h"
#include "media/video/video_decode_engine.h"
@@ -65,6 +66,13 @@ MEDIA_EXPORT base::TimeDelta ConvertFromTimeBase(const AVRational& time_base,
MEDIA_EXPORT int64 ConvertToTimeBase(const AVRational& time_base,
const base::TimeDelta& timestamp);
+void AVCodecContextToAudioDecoderConfig(
+ const AVCodecContext* codec_context,
+ AudioDecoderConfig* config);
+void AudioDecoderConfigToAVCodecContext(
+ const AudioDecoderConfig& config,
+ AVCodecContext* codec_context);
+
VideoCodec CodecIDToVideoCodec(CodecID codec_id);
CodecID VideoCodecToCodecID(VideoCodec video_codec);
diff --git a/media/filters/adaptive_demuxer.cc b/media/filters/adaptive_demuxer.cc
index 84a84c8..de980f9 100644
--- a/media/filters/adaptive_demuxer.cc
+++ b/media/filters/adaptive_demuxer.cc
@@ -217,6 +217,10 @@ AVStream* AdaptiveDemuxerStream::GetAVStream() {
return current_stream()->GetAVStream();
}
+const AudioDecoderConfig& AdaptiveDemuxerStream::audio_decoder_config() {
+ return current_stream()->audio_decoder_config();
+}
+
DemuxerStream::Type AdaptiveDemuxerStream::type() {
return current_stream()->type();
}
diff --git a/media/filters/adaptive_demuxer.h b/media/filters/adaptive_demuxer.h
index 1bc83f6..4fd7e27 100644
--- a/media/filters/adaptive_demuxer.h
+++ b/media/filters/adaptive_demuxer.h
@@ -70,6 +70,7 @@ class AdaptiveDemuxerStream : public DemuxerStream {
virtual Type type();
virtual void EnableBitstreamConverter();
virtual AVStream* GetAVStream();
+ virtual const AudioDecoderConfig& audio_decoder_config();
private:
// Returns a pointer to the current stream.
diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc
index f64f13f..10ad5ea 100644
--- a/media/filters/audio_renderer_base.cc
+++ b/media/filters/audio_renderer_base.cc
@@ -107,7 +107,7 @@ void AudioRendererBase::Initialize(AudioDecoder* decoder,
ChannelLayout channel_layout = decoder_->channel_layout();
int channels = ChannelLayoutToChannelCount(channel_layout);
int bits_per_channel = decoder_->bits_per_channel();
- int sample_rate = decoder_->sample_rate();
+ int sample_rate = decoder_->samples_per_second();
algorithm_->Initialize(channels, sample_rate, bits_per_channel, 0.0f, cb);
// Give the subclass an opportunity to initialize itself.
diff --git a/media/filters/audio_renderer_base_unittest.cc b/media/filters/audio_renderer_base_unittest.cc
index 87faab3..7c1e6c9 100644
--- a/media/filters/audio_renderer_base_unittest.cc
+++ b/media/filters/audio_renderer_base_unittest.cc
@@ -61,7 +61,7 @@ class AudioRendererBaseTest : public ::testing::Test {
.WillByDefault(Return(16));
ON_CALL(*decoder_, channel_layout())
.WillByDefault(Return(CHANNEL_LAYOUT_MONO));
- ON_CALL(*decoder_, sample_rate())
+ ON_CALL(*decoder_, samples_per_second())
.WillByDefault(Return(44100));
}
diff --git a/media/filters/chunk_demuxer.cc b/media/filters/chunk_demuxer.cc
index 0a90e5c..ef4f792 100644
--- a/media/filters/chunk_demuxer.cc
+++ b/media/filters/chunk_demuxer.cc
@@ -80,12 +80,14 @@ class ChunkDemuxerStream : public DemuxerStream {
virtual Type type();
virtual void EnableBitstreamConverter();
virtual AVStream* GetAVStream();
+ virtual const AudioDecoderConfig& audio_decoder_config();
private:
static void RunCallback(ReadCallback cb, scoped_refptr<Buffer> buffer);
Type type_;
AVStream* av_stream_;
+ AudioDecoderConfig audio_config_;
mutable base::Lock lock_;
ReadCBQueue read_cbs_;
@@ -107,6 +109,9 @@ ChunkDemuxerStream::ChunkDemuxerStream(Type type, AVStream* stream)
shutdown_called_(false),
received_end_of_stream_(false),
last_buffer_timestamp_(kNoTimestamp) {
+ if (type_ == AUDIO) {
+ AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_);
+ }
}
ChunkDemuxerStream::~ChunkDemuxerStream() {}
@@ -281,6 +286,11 @@ void ChunkDemuxerStream::EnableBitstreamConverter() {}
AVStream* ChunkDemuxerStream::GetAVStream() { return av_stream_; }
+const AudioDecoderConfig& ChunkDemuxerStream::audio_decoder_config() {
+ CHECK_EQ(type_, AUDIO);
+ return audio_config_;
+}
+
ChunkDemuxer::ChunkDemuxer(ChunkDemuxerClient* client)
: state_(WAITING_FOR_INIT),
client_(client),
diff --git a/media/filters/chunk_demuxer_unittest.cc b/media/filters/chunk_demuxer_unittest.cc
index 8217514..6b0ee3b 100644
--- a/media/filters/chunk_demuxer_unittest.cc
+++ b/media/filters/chunk_demuxer_unittest.cc
@@ -3,6 +3,7 @@
// found in the LICENSE file.
#include "base/bind.h"
+#include "media/base/audio_decoder_config.h"
#include "media/base/mock_callback.h"
#include "media/base/mock_filter_host.h"
#include "media/base/test_data_util.h"
@@ -185,16 +186,37 @@ TEST_F(ChunkDemuxerTest, TestInit) {
client_.reset(new MockChunkDemuxerClient());
demuxer_ = new ChunkDemuxer(client_.get());
InitDemuxer(has_audio, has_video);
- EXPECT_EQ(demuxer_->GetStream(DemuxerStream::AUDIO).get() != NULL,
- has_audio);
- EXPECT_EQ(demuxer_->GetStream(DemuxerStream::VIDEO).get() != NULL,
- has_video);
+
+ scoped_refptr<DemuxerStream> audio_stream =
+ demuxer_->GetStream(DemuxerStream::AUDIO);
+ if (has_audio) {
+ EXPECT_TRUE(audio_stream);
+
+ const AudioDecoderConfig& config = audio_stream->audio_decoder_config();
+ EXPECT_EQ(kCodecVorbis, config.codec());
+ EXPECT_EQ(16, config.bits_per_channel());
+ EXPECT_EQ(CHANNEL_LAYOUT_STEREO, config.channel_layout());
+ EXPECT_EQ(44100, config.samples_per_second());
+ EXPECT_TRUE(config.extra_data());
+ EXPECT_GT(config.extra_data_size(), 0u);
+ } else {
+ EXPECT_FALSE(audio_stream);
+ }
+
+ scoped_refptr<DemuxerStream> video_stream =
+ demuxer_->GetStream(DemuxerStream::VIDEO);
+ if (has_video) {
+ EXPECT_TRUE(video_stream);
+ } else {
+ EXPECT_FALSE(video_stream);
+ }
+
ShutdownDemuxer();
demuxer_ = NULL;
}
}
-// Makes sure that Seek() reports an error if Shutdown()
+// Makes sure that Seek() reports an error if Shutdown()
// is called before the first cluster is passed to the demuxer.
TEST_F(ChunkDemuxerTest, TestShutdownBeforeFirstSeekCompletes) {
InitDemuxer(true, true);
diff --git a/media/filters/dummy_demuxer.cc b/media/filters/dummy_demuxer.cc
index 91aa40d..a9afbfc 100644
--- a/media/filters/dummy_demuxer.cc
+++ b/media/filters/dummy_demuxer.cc
@@ -18,6 +18,11 @@ DemuxerStream::Type DummyDemuxerStream::type() {
return type_;
}
+const AudioDecoderConfig& DummyDemuxerStream::audio_decoder_config() {
+ CHECK_EQ(type_, AUDIO);
+ return audio_config_;
+}
+
void DummyDemuxerStream::Read(const ReadCallback& read_callback) {}
void DummyDemuxerStream::EnableBitstreamConverter() {}
diff --git a/media/filters/dummy_demuxer.h b/media/filters/dummy_demuxer.h
index 272d1e4..1b74d01 100644
--- a/media/filters/dummy_demuxer.h
+++ b/media/filters/dummy_demuxer.h
@@ -11,6 +11,7 @@
#include <vector>
+#include "media/base/audio_decoder_config.h"
#include "media/base/demuxer.h"
namespace media {
@@ -22,12 +23,14 @@ class DummyDemuxerStream : public DemuxerStream {
// DemuxerStream implementation.
virtual void Read(const ReadCallback& read_callback) OVERRIDE;
virtual Type type() OVERRIDE;
+ virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
virtual void EnableBitstreamConverter() OVERRIDE;
private:
virtual ~DummyDemuxerStream();
Type type_;
+ AudioDecoderConfig audio_config_;
DISALLOW_COPY_AND_ASSIGN(DummyDemuxerStream);
};
diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc
index cb3f846..d2086a8 100644
--- a/media/filters/ffmpeg_audio_decoder.cc
+++ b/media/filters/ffmpeg_audio_decoder.cc
@@ -8,7 +8,6 @@
#include "media/base/data_buffer.h"
#include "media/base/demuxer.h"
#include "media/base/filter_host.h"
-#include "media/base/limits.h"
#include "media/ffmpeg/ffmpeg_common.h"
namespace media {
@@ -50,7 +49,7 @@ FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop)
codec_context_(NULL),
bits_per_channel_(0),
channel_layout_(CHANNEL_LAYOUT_NONE),
- sample_rate_(0),
+ samples_per_second_(0),
decoded_audio_size_(AVCODEC_MAX_AUDIO_FRAME_SIZE),
decoded_audio_(static_cast<uint8*>(av_malloc(decoded_audio_size_))),
pending_reads_(0) {
@@ -58,6 +57,14 @@ FFmpegAudioDecoder::FFmpegAudioDecoder(MessageLoop* message_loop)
FFmpegAudioDecoder::~FFmpegAudioDecoder() {
av_free(decoded_audio_);
+
+ // XXX: should we require Stop() to be called? this might end up getting
+ // called on a random thread due to refcounting.
+ if (codec_context_) {
+ av_free(codec_context_->extradata);
+ avcodec_close(codec_context_);
+ av_free(codec_context_);
+ }
}
void FFmpegAudioDecoder::Flush(FilterCallback* callback) {
@@ -94,8 +101,8 @@ ChannelLayout FFmpegAudioDecoder::channel_layout() {
return channel_layout_;
}
-int FFmpegAudioDecoder::sample_rate() {
- return sample_rate_;
+int FFmpegAudioDecoder::samples_per_second() {
+ return samples_per_second_;
}
void FFmpegAudioDecoder::DoInitialize(
@@ -105,48 +112,41 @@ void FFmpegAudioDecoder::DoInitialize(
scoped_ptr<FilterCallback> c(callback);
demuxer_stream_ = stream;
- AVStream* av_stream = demuxer_stream_->GetAVStream();
- CHECK(av_stream);
-
+ const AudioDecoderConfig& config = stream->audio_decoder_config();
stats_callback_.reset(stats_callback);
- // Grab the AVStream's codec context and make sure we have sensible values.
- codec_context_ = av_stream->codec;
- int bps = av_get_bits_per_sample_fmt(codec_context_->sample_fmt);
- if (codec_context_->channels <= 0 ||
- codec_context_->channels > Limits::kMaxChannels ||
- (codec_context_->channel_layout == 0 && codec_context_->channels > 2) ||
- bps <= 0 || bps > Limits::kMaxBitsPerSample ||
- codec_context_->sample_rate <= 0 ||
- codec_context_->sample_rate > Limits::kMaxSampleRate) {
+ // TODO(scherkus): this check should go in PipelineImpl prior to creating
+ // decoder objects.
+ if (!config.IsValidConfig()) {
DLOG(ERROR) << "Invalid audio stream -"
- << " channels: " << codec_context_->channels
- << " channel layout:" << codec_context_->channel_layout
- << " bps: " << bps
- << " sample rate: " << codec_context_->sample_rate;
+ << " codec: " << config.codec()
+ << " channel layout: " << config.channel_layout()
+ << " bits per channel: " << config.bits_per_channel()
+ << " samples per second: " << config.samples_per_second();
- host()->SetError(PIPELINE_ERROR_DECODE);
+ host()->SetError(DECODER_ERROR_NOT_SUPPORTED);
callback->Run();
return;
}
- // Serialize calls to avcodec_open().
+ // Initialize AVCodecContext structure.
+ codec_context_ = avcodec_alloc_context();
+ AudioDecoderConfigToAVCodecContext(config, codec_context_);
+
AVCodec* codec = avcodec_find_decoder(codec_context_->codec_id);
if (!codec || avcodec_open(codec_context_, codec) < 0) {
DLOG(ERROR) << "Could not initialize audio decoder: "
<< codec_context_->codec_id;
- host()->SetError(PIPELINE_ERROR_DECODE);
+ host()->SetError(DECODER_ERROR_NOT_SUPPORTED);
callback->Run();
return;
}
// Success!
- bits_per_channel_ = av_get_bits_per_sample_fmt(codec_context_->sample_fmt);
- channel_layout_ =
- ChannelLayoutToChromeChannelLayout(codec_context_->channel_layout,
- codec_context_->channels);
- sample_rate_ = codec_context_->sample_rate;
+ bits_per_channel_ = config.bits_per_channel();
+ channel_layout_ = config.channel_layout();
+ samples_per_second_ = config.samples_per_second();
callback->Run();
}
@@ -286,7 +286,7 @@ void FFmpegAudioDecoder::UpdateDurationAndTimestamp(
base::TimeDelta FFmpegAudioDecoder::CalculateDuration(int size) {
int64 denominator = ChannelLayoutToChannelCount(channel_layout_) *
- bits_per_channel_ / 8 * sample_rate_;
+ bits_per_channel_ / 8 * samples_per_second_;
double microseconds = size /
(denominator / static_cast<double>(base::Time::kMicrosecondsPerSecond));
return base::TimeDelta::FromMicroseconds(static_cast<int64>(microseconds));
diff --git a/media/filters/ffmpeg_audio_decoder.h b/media/filters/ffmpeg_audio_decoder.h
index 999e37c..65f499b 100644
--- a/media/filters/ffmpeg_audio_decoder.h
+++ b/media/filters/ffmpeg_audio_decoder.h
@@ -30,7 +30,7 @@ class MEDIA_EXPORT FFmpegAudioDecoder : public AudioDecoder {
virtual void ProduceAudioSamples(scoped_refptr<Buffer> output) OVERRIDE;
virtual int bits_per_channel() OVERRIDE;
virtual ChannelLayout channel_layout() OVERRIDE;
- virtual int sample_rate() OVERRIDE;
+ virtual int samples_per_second() OVERRIDE;
private:
// Methods running on decoder thread.
@@ -62,7 +62,7 @@ class MEDIA_EXPORT FFmpegAudioDecoder : public AudioDecoder {
// Decoded audio format.
int bits_per_channel_;
ChannelLayout channel_layout_;
- int sample_rate_;
+ int samples_per_second_;
base::TimeDelta estimated_next_timestamp_;
diff --git a/media/filters/ffmpeg_audio_decoder_unittest.cc b/media/filters/ffmpeg_audio_decoder_unittest.cc
index 9edc85a..d9919427 100644
--- a/media/filters/ffmpeg_audio_decoder_unittest.cc
+++ b/media/filters/ffmpeg_audio_decoder_unittest.cc
@@ -16,7 +16,8 @@
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::_;
-using ::testing::Return;
+using ::testing::ReturnRef;
+using ::testing::StrictMock;
namespace media {
@@ -28,8 +29,7 @@ class FFmpegAudioDecoderTest : public testing::Test {
public:
FFmpegAudioDecoderTest()
: decoder_(new FFmpegAudioDecoder(&message_loop_)),
- demuxer_(new MockDemuxerStream()),
- codec_context_(avcodec_alloc_context()) {
+ demuxer_(new StrictMock<MockDemuxerStream>()) {
CHECK(FFmpegGlue::GetInstance());
ReadTestDataFile("vorbis-extradata",
@@ -58,31 +58,19 @@ class FFmpegAudioDecoderTest : public testing::Test {
base::Bind(&FFmpegAudioDecoderTest::DecodeFinished,
base::Unretained(this)));
- memset(&stream_, 0, sizeof(stream_));
-
- stream_.codec = codec_context_;
- codec_context_->codec_id = CODEC_ID_VORBIS;
- codec_context_->codec_type = AVMEDIA_TYPE_AUDIO;
- codec_context_->channels = 2;
- codec_context_->channel_layout = AV_CH_LAYOUT_STEREO;
- codec_context_->sample_fmt = AV_SAMPLE_FMT_S16;
- codec_context_->sample_rate = 44100;
- codec_context_->extradata = vorbis_extradata_.get();
- codec_context_->extradata_size = vorbis_extradata_size_;
+ config_.Initialize(kCodecVorbis,
+ 16,
+ CHANNEL_LAYOUT_STEREO,
+ 44100,
+ vorbis_extradata_.get(),
+ vorbis_extradata_size_);
}
- virtual ~FFmpegAudioDecoderTest() {
- // TODO(scherkus): currently FFmpegAudioDecoder assumes FFmpegDemuxer will
- // call avcodec_close().
- if (codec_context_->codec) {
- avcodec_close(codec_context_);
- }
- av_free(codec_context_);
- }
+ virtual ~FFmpegAudioDecoderTest() {}
void Initialize() {
- EXPECT_CALL(*demuxer_, GetAVStream())
- .WillOnce(Return(&stream_));
+ EXPECT_CALL(*demuxer_, audio_decoder_config())
+ .WillOnce(ReturnRef(config_));
decoder_->Initialize(demuxer_,
NewExpectedCallback(),
@@ -125,7 +113,7 @@ class FFmpegAudioDecoderTest : public testing::Test {
MessageLoop message_loop_;
scoped_refptr<FFmpegAudioDecoder> decoder_;
- scoped_refptr<MockDemuxerStream> demuxer_;
+ scoped_refptr<StrictMock<MockDemuxerStream> > demuxer_;
MockStatisticsCallback statistics_callback_;
scoped_array<uint8> vorbis_extradata_;
@@ -134,8 +122,7 @@ class FFmpegAudioDecoderTest : public testing::Test {
std::deque<scoped_refptr<Buffer> > encoded_audio_;
std::deque<scoped_refptr<Buffer> > decoded_audio_;
- AVStream stream_;
- AVCodecContext* codec_context_; // Allocated via avcodec_alloc_context().
+ AudioDecoderConfig config_;
};
TEST_F(FFmpegAudioDecoderTest, Initialize) {
@@ -143,7 +130,7 @@ TEST_F(FFmpegAudioDecoderTest, Initialize) {
EXPECT_EQ(16, decoder_->bits_per_channel());
EXPECT_EQ(CHANNEL_LAYOUT_STEREO, decoder_->channel_layout());
- EXPECT_EQ(44100, decoder_->sample_rate());
+ EXPECT_EQ(44100, decoder_->samples_per_second());
Stop();
}
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index e4e21af..3d7bb12 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -67,6 +67,7 @@ FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer,
switch (stream->codec->codec_type) {
case AVMEDIA_TYPE_AUDIO:
type_ = AUDIO;
+ AVCodecContextToAudioDecoderConfig(stream->codec, &audio_config_);
break;
case AVMEDIA_TYPE_VIDEO:
type_ = VIDEO;
@@ -241,6 +242,11 @@ AVStream* FFmpegDemuxerStream::GetAVStream() {
return stream_;
}
+const AudioDecoderConfig& FFmpegDemuxerStream::audio_decoder_config() {
+ CHECK_EQ(type_, AUDIO);
+ return audio_config_;
+}
+
// static
base::TimeDelta FFmpegDemuxerStream::ConvertStreamTimestamp(
const AVRational& time_base, int64 timestamp) {
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index ed59c87..e9bd127 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -28,6 +28,7 @@
#include "base/callback.h"
#include "base/gtest_prod_util.h"
#include "base/synchronization/waitable_event.h"
+#include "media/base/audio_decoder_config.h"
#include "media/base/buffers.h"
#include "media/base/demuxer.h"
#include "media/base/pipeline.h"
@@ -55,22 +56,22 @@ class FFmpegDemuxerStream : public DemuxerStream {
// Returns true is this stream has pending reads, false otherwise.
//
// Safe to call on any thread.
- virtual bool HasPendingReads();
+ bool HasPendingReads();
// Enqueues and takes ownership over the given AVPacket.
- virtual void EnqueuePacket(AVPacket* packet);
+ void EnqueuePacket(AVPacket* packet);
// Signals to empty the buffer queue and mark next packet as discontinuous.
- virtual void FlushBuffers();
+ void FlushBuffers();
// Empties the queues and ignores any additional calls to Read().
- virtual void Stop();
+ void Stop();
// Returns the duration of this stream.
- virtual base::TimeDelta duration();
+ base::TimeDelta duration();
// DemuxerStream implementation.
- virtual Type type();
+ virtual Type type() OVERRIDE;
// If |buffer_queue_| is not empty will execute on caller's thread, otherwise
// will post ReadTask to execute on demuxer's thread. Read will acquire
@@ -78,10 +79,10 @@ class FFmpegDemuxerStream : public DemuxerStream {
// not make calls into FFmpegDemuxerStream directly or that may cause a
// deadlock. |read_callback| should execute as quickly as possible because
// |lock_| is held throughout the life of the callback.
- virtual void Read(const ReadCallback& read_callback);
- // Bitstream converter to convert input packet.
- virtual void EnableBitstreamConverter();
- virtual AVStream* GetAVStream();
+ virtual void Read(const ReadCallback& read_callback) OVERRIDE;
+ virtual void EnableBitstreamConverter() OVERRIDE;
+ virtual AVStream* GetAVStream() OVERRIDE;
+ virtual const AudioDecoderConfig& audio_decoder_config() OVERRIDE;
private:
virtual ~FFmpegDemuxerStream();
@@ -100,6 +101,7 @@ class FFmpegDemuxerStream : public DemuxerStream {
FFmpegDemuxer* demuxer_;
AVStream* stream_;
+ AudioDecoderConfig audio_config_;
Type type_;
base::TimeDelta duration_;
bool discontinuous_;
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index e636849..d5c76ae 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -170,18 +170,27 @@ TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) {
TEST_F(FFmpegDemuxerTest, Initialize_Successful) {
InitializeDemuxer(CreateDataSource("bear-320x240.webm"));
- // First stream should be video and support the FFmpegDemuxerStream interface.
+ // Video stream should be present.
scoped_refptr<DemuxerStream> stream =
demuxer_->GetStream(DemuxerStream::VIDEO);
ASSERT_TRUE(stream);
EXPECT_EQ(DemuxerStream::VIDEO, stream->type());
ASSERT_TRUE(stream->GetAVStream());
- // Other stream should be audio and support the FFmpegDemuxerStream interface.
+ // Audio stream should be present.
stream = demuxer_->GetStream(DemuxerStream::AUDIO);
ASSERT_TRUE(stream);
EXPECT_EQ(DemuxerStream::AUDIO, stream->type());
ASSERT_TRUE(stream->GetAVStream());
+
+ // FFmpegDemuxer's audio streams support AudioDecoderConfig structs.
+ const AudioDecoderConfig& config = stream->audio_decoder_config();
+ EXPECT_EQ(kCodecVorbis, config.codec());
+ EXPECT_EQ(16, config.bits_per_channel());
+ EXPECT_EQ(CHANNEL_LAYOUT_STEREO, config.channel_layout());
+ EXPECT_EQ(44100, config.samples_per_second());
+ EXPECT_TRUE(config.extra_data());
+ EXPECT_GT(config.extra_data_size(), 0u);
}
TEST_F(FFmpegDemuxerTest, Read_Audio) {
diff --git a/media/media.gyp b/media/media.gyp
index 603f5a0..cdc1495 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -84,6 +84,8 @@
'audio/win/waveout_output_win.h',
'base/async_filter_factory_base.cc',
'base/async_filter_factory_base.h',
+ 'base/audio_decoder_config.cc',
+ 'base/audio_decoder_config.h',
'base/bitstream_buffer.h',
'base/buffers.cc',
'base/buffers.h',
diff --git a/media/video/ffmpeg_video_decode_engine_unittest.cc b/media/video/ffmpeg_video_decode_engine_unittest.cc
index e8238ff..8e171e1 100644
--- a/media/video/ffmpeg_video_decode_engine_unittest.cc
+++ b/media/video/ffmpeg_video_decode_engine_unittest.cc
@@ -149,8 +149,10 @@ TEST_F(FFmpegVideoDecodeEngineTest, Initialize_Normal) {
}
TEST_F(FFmpegVideoDecodeEngineTest, Initialize_FindDecoderFails) {
- VideoDecoderConfig config(kUnknown, kCodedSize, kVisibleRect, kNaturalSize,
- kFrameRate.num, kFrameRate.den, NULL, 0);
+ VideoDecoderConfig config(kUnknownVideoCodec, kCodedSize, kVisibleRect,
+ kNaturalSize, kFrameRate.num, kFrameRate.den,
+ NULL, 0);
+
// Test avcodec_find_decoder() returning NULL.
VideoCodecInfo info;
EXPECT_CALL(*this, OnInitializeComplete(_))