summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 07:02:42 +0000
committerxhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2012-10-18 07:02:42 +0000
commit8a8f4d1a570302b00c7c2a64c699a108621893ec (patch)
treec291166134e75e4baceb798b689e0dd4d70efe1e /media
parent741c28f9db1521cb02c6b89fe2966f67f4329dff (diff)
downloadchromium_src-8a8f4d1a570302b00c7c2a64c699a108621893ec.zip
chromium_src-8a8f4d1a570302b00c7c2a64c699a108621893ec.tar.gz
chromium_src-8a8f4d1a570302b00c7c2a64c699a108621893ec.tar.bz2
Move audio decoder initialization to AudioRendererImpl.
Also add support to multiple audio decoder initializaiton. BUG=145635 TEST=media_unittests Review URL: https://chromiumcodereview.appspot.com/11148011 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@162658 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/audio_renderer.h11
-rw-r--r--media/base/filter_collection.cc17
-rw-r--r--media/base/filter_collection.h5
-rw-r--r--media/base/filter_collection_unittest.cc48
-rw-r--r--media/base/mock_filters.cc2
-rw-r--r--media/base/mock_filters.h4
-rw-r--r--media/base/pipeline.cc26
-rw-r--r--media/base/pipeline.h9
-rw-r--r--media/base/pipeline_unittest.cc101
-rw-r--r--media/filters/audio_renderer_impl.cc76
-rw-r--r--media/filters/audio_renderer_impl.h19
-rw-r--r--media/filters/audio_renderer_impl_unittest.cc47
-rw-r--r--media/filters/pipeline_integration_test_base.cc9
-rw-r--r--media/tools/player_wtl/movie.cc2
-rw-r--r--media/tools/player_x11/player_x11.cc2
15 files changed, 221 insertions, 157 deletions
diff --git a/media/base/audio_renderer.h b/media/base/audio_renderer.h
index b3a3315..19459ac 100644
--- a/media/base/audio_renderer.h
+++ b/media/base/audio_renderer.h
@@ -5,6 +5,8 @@
#ifndef MEDIA_BASE_AUDIO_RENDERER_H_
#define MEDIA_BASE_AUDIO_RENDERER_H_
+#include <list>
+
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/time.h"
@@ -14,10 +16,13 @@
namespace media {
class AudioDecoder;
+class DemuxerStream;
class MEDIA_EXPORT AudioRenderer
: public base::RefCountedThreadSafe<AudioRenderer> {
public:
+ typedef std::list<scoped_refptr<AudioDecoder> > AudioDecoderList;
+
// First parameter is the current time that has been rendered.
// Second parameter is the maximum time value that the clock cannot exceed.
typedef base::Callback<void(base::TimeDelta, base::TimeDelta)> TimeCB;
@@ -25,6 +30,8 @@ class MEDIA_EXPORT AudioRenderer
// Initialize a AudioRenderer with the given AudioDecoder, executing the
// |init_cb| upon completion.
//
+ // |statistics_cb| is executed periodically with audio rendering stats.
+ //
// |underflow_cb| is executed when the renderer runs out of data to pass to
// the audio card during playback. ResumeAfterUnderflow() must be called
// to resume playback. Pause(), Preroll(), or Stop() cancels the underflow
@@ -39,8 +46,10 @@ class MEDIA_EXPORT AudioRenderer
// executed.
//
// |error_cb| is executed if an error was encountered.
- virtual void Initialize(const scoped_refptr<AudioDecoder>& decoder,
+ virtual void Initialize(const scoped_refptr<DemuxerStream>& stream,
+ const AudioDecoderList& decoders,
const PipelineStatusCB& init_cb,
+ const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const base::Closure& ended_cb,
diff --git a/media/base/filter_collection.cc b/media/base/filter_collection.cc
index ff277ba..f82a61b 100644
--- a/media/base/filter_collection.cc
+++ b/media/base/filter_collection.cc
@@ -25,10 +25,6 @@ const scoped_refptr<Demuxer>& FilterCollection::GetDemuxer() {
return demuxer_;
}
-void FilterCollection::AddAudioDecoder(AudioDecoder* audio_decoder) {
- audio_decoders_.push_back(audio_decoder);
-}
-
void FilterCollection::AddAudioRenderer(AudioRenderer* audio_renderer) {
audio_renderers_.push_back(audio_renderer);
}
@@ -44,15 +40,6 @@ void FilterCollection::Clear() {
video_renderers_.clear();
}
-void FilterCollection::SelectAudioDecoder(scoped_refptr<AudioDecoder>* out) {
- if (audio_decoders_.empty()) {
- *out = NULL;
- return;
- }
- *out = audio_decoders_.front();
- audio_decoders_.pop_front();
-}
-
void FilterCollection::SelectAudioRenderer(scoped_refptr<AudioRenderer>* out) {
if (audio_renderers_.empty()) {
*out = NULL;
@@ -71,6 +58,10 @@ void FilterCollection::SelectVideoRenderer(scoped_refptr<VideoRenderer>* out) {
video_renderers_.pop_front();
}
+FilterCollection::AudioDecoderList* FilterCollection::GetAudioDecoders() {
+ return &audio_decoders_;
+}
+
FilterCollection::VideoDecoderList* FilterCollection::GetVideoDecoders() {
return &video_decoders_;
}
diff --git a/media/base/filter_collection.h b/media/base/filter_collection.h
index 08354a7..e9f2be5 100644
--- a/media/base/filter_collection.h
+++ b/media/base/filter_collection.h
@@ -25,6 +25,7 @@ class VideoRenderer;
// http://crbug.com/110800
class MEDIA_EXPORT FilterCollection {
public:
+ typedef std::list<scoped_refptr<AudioDecoder> > AudioDecoderList;
typedef std::list<scoped_refptr<VideoDecoder> > VideoDecoderList;
FilterCollection();
@@ -46,15 +47,15 @@ class MEDIA_EXPORT FilterCollection {
// If the required filter cannot be found, NULL is returned.
// If a filter is returned it is removed from the collection.
// Filters are selected in FIFO order.
- void SelectAudioDecoder(scoped_refptr<AudioDecoder>* out);
void SelectAudioRenderer(scoped_refptr<AudioRenderer>* out);
void SelectVideoRenderer(scoped_refptr<VideoRenderer>* out);
+ AudioDecoderList* GetAudioDecoders();
VideoDecoderList* GetVideoDecoders();
private:
scoped_refptr<Demuxer> demuxer_;
- std::list<scoped_refptr<AudioDecoder> > audio_decoders_;
+ AudioDecoderList audio_decoders_;
VideoDecoderList video_decoders_;
std::list<scoped_refptr<AudioRenderer> > audio_renderers_;
std::list<scoped_refptr<VideoRenderer> > video_renderers_;
diff --git a/media/base/filter_collection_unittest.cc b/media/base/filter_collection_unittest.cc
index 514d96c..a8fdd70 100644
--- a/media/base/filter_collection_unittest.cc
+++ b/media/base/filter_collection_unittest.cc
@@ -21,45 +21,45 @@ class FilterCollectionTest : public ::testing::Test {
};
TEST_F(FilterCollectionTest, SelectXXXMethods) {
- scoped_refptr<AudioDecoder> audio_decoder;
+ scoped_refptr<AudioRenderer> audio_renderer;
- collection_.SelectAudioDecoder(&audio_decoder);
- EXPECT_FALSE(audio_decoder);
+ collection_.SelectAudioRenderer(&audio_renderer);
+ EXPECT_FALSE(audio_renderer);
// Add an audio decoder.
- collection_.AddAudioDecoder(mock_filters_.audio_decoder());
+ collection_.AddAudioRenderer(mock_filters_.audio_renderer());
// Verify that we can select the audio decoder.
- collection_.SelectAudioDecoder(&audio_decoder);
- EXPECT_TRUE(audio_decoder);
+ collection_.SelectAudioRenderer(&audio_renderer);
+ EXPECT_TRUE(audio_renderer);
// Verify that we can't select it again since only one has been added.
- collection_.SelectAudioDecoder(&audio_decoder);
- EXPECT_FALSE(audio_decoder);
+ collection_.SelectAudioRenderer(&audio_renderer);
+ EXPECT_FALSE(audio_renderer);
}
TEST_F(FilterCollectionTest, MultipleFiltersOfSameType) {
- scoped_refptr<AudioDecoder> audio_decoder_a(new MockAudioDecoder());
- scoped_refptr<AudioDecoder> audio_decoder_b(new MockAudioDecoder());
+ scoped_refptr<AudioRenderer> audio_renderer_a(new MockAudioRenderer());
+ scoped_refptr<AudioRenderer> audio_renderer_b(new MockAudioRenderer());
- scoped_refptr<AudioDecoder> audio_decoder;
+ scoped_refptr<AudioRenderer> audio_renderer;
- collection_.AddAudioDecoder(audio_decoder_a.get());
- collection_.AddAudioDecoder(audio_decoder_b.get());
+ collection_.AddAudioRenderer(audio_renderer_a.get());
+ collection_.AddAudioRenderer(audio_renderer_b.get());
- // Verify that first SelectAudioDecoder() returns audio_decoder_a.
- collection_.SelectAudioDecoder(&audio_decoder);
- EXPECT_TRUE(audio_decoder);
- EXPECT_EQ(audio_decoder, audio_decoder_a);
+ // Verify that first SelectAudioRenderer() returns audio_renderer_a.
+ collection_.SelectAudioRenderer(&audio_renderer);
+ EXPECT_TRUE(audio_renderer);
+ EXPECT_EQ(audio_renderer, audio_renderer_a);
- // Verify that second SelectAudioDecoder() returns audio_decoder_b.
- collection_.SelectAudioDecoder(&audio_decoder);
- EXPECT_TRUE(audio_decoder);
- EXPECT_EQ(audio_decoder, audio_decoder_b);
+ // Verify that second SelectAudioRenderer() returns audio_renderer_b.
+ collection_.SelectAudioRenderer(&audio_renderer);
+ EXPECT_TRUE(audio_renderer);
+ EXPECT_EQ(audio_renderer, audio_renderer_b);
- // Verify that third SelectAudioDecoder() returns nothing.
- collection_.SelectAudioDecoder(&audio_decoder);
- EXPECT_FALSE(audio_decoder);
+ // Verify that third SelectAudioRenderer() returns nothing.
+ collection_.SelectAudioRenderer(&audio_renderer);
+ EXPECT_FALSE(audio_renderer);
}
} // namespace media
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index 5607fe7..9f02cb3 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -85,7 +85,7 @@ scoped_ptr<FilterCollection> MockFilterCollection::Create() {
scoped_ptr<FilterCollection> collection(new FilterCollection());
collection->SetDemuxer(demuxer_);
collection->GetVideoDecoders()->push_back(video_decoder_);
- collection->AddAudioDecoder(audio_decoder_);
+ collection->GetAudioDecoders()->push_back(audio_decoder_);
collection->AddVideoRenderer(video_renderer_);
collection->AddAudioRenderer(audio_renderer_);
return collection.Pass();
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index d60e954..d5541a7 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -166,8 +166,10 @@ class MockAudioRenderer : public AudioRenderer {
MockAudioRenderer();
// AudioRenderer implementation.
- MOCK_METHOD7(Initialize, void(const scoped_refptr<AudioDecoder>& decoder,
+ MOCK_METHOD9(Initialize, void(const scoped_refptr<DemuxerStream>& stream,
+ const AudioDecoderList& decoders,
const PipelineStatusCB& init_cb,
+ const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const base::Closure& ended_cb,
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc
index 2cb87f0..fd8b902 100644
--- a/media/base/pipeline.cc
+++ b/media/base/pipeline.cc
@@ -264,7 +264,6 @@ const char* Pipeline::GetStateString(State state) {
switch (state) {
RETURN_STRING(kCreated);
RETURN_STRING(kInitDemuxer);
- RETURN_STRING(kInitAudioDecoder);
RETURN_STRING(kInitAudioRenderer);
RETURN_STRING(kInitVideoRenderer);
RETURN_STRING(kInitPrerolling);
@@ -293,14 +292,11 @@ Pipeline::State Pipeline::GetNextState() const {
case kInitDemuxer:
if (demuxer_->GetStream(DemuxerStream::AUDIO))
- return kInitAudioDecoder;
+ return kInitAudioRenderer;
if (demuxer_->GetStream(DemuxerStream::VIDEO))
return kInitVideoRenderer;
return kInitPrerolling;
- case kInitAudioDecoder:
- return kInitAudioRenderer;
-
case kInitAudioRenderer:
if (demuxer_->GetStream(DemuxerStream::VIDEO))
return kInitVideoRenderer;
@@ -464,9 +460,6 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
case kInitDemuxer:
return InitializeDemuxer(done_cb);
- case kInitAudioDecoder:
- return InitializeAudioDecoder(done_cb);
-
case kInitAudioRenderer:
return InitializeAudioRenderer(done_cb);
@@ -633,7 +626,6 @@ void Pipeline::OnStopCompleted(PipelineStatus status) {
SetState(kStopped);
pending_callbacks_.reset();
filter_collection_.reset();
- audio_decoder_ = NULL;
audio_renderer_ = NULL;
video_renderer_ = NULL;
demuxer_ = NULL;
@@ -886,31 +878,25 @@ void Pipeline::InitializeDemuxer(const PipelineStatusCB& done_cb) {
demuxer_->Initialize(this, done_cb);
}
-void Pipeline::InitializeAudioDecoder(const PipelineStatusCB& done_cb) {
+void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
DCHECK(message_loop_->BelongsToCurrentThread());
scoped_refptr<DemuxerStream> stream =
demuxer_->GetStream(DemuxerStream::AUDIO);
DCHECK(stream);
- filter_collection_->SelectAudioDecoder(&audio_decoder_);
- audio_decoder_->Initialize(
- stream, done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this));
-}
-
-void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
- DCHECK(message_loop_->BelongsToCurrentThread());
- DCHECK(audio_decoder_);
-
filter_collection_->SelectAudioRenderer(&audio_renderer_);
audio_renderer_->Initialize(
- audio_decoder_,
+ stream,
+ *filter_collection_->GetAudioDecoders(),
done_cb,
+ base::Bind(&Pipeline::OnUpdateStatistics, this),
base::Bind(&Pipeline::OnAudioUnderflow, this),
base::Bind(&Pipeline::OnAudioTimeUpdate, this),
base::Bind(&Pipeline::OnAudioRendererEnded, this),
base::Bind(&Pipeline::OnAudioDisabled, this),
base::Bind(&Pipeline::SetError, this));
+ filter_collection_->GetAudioDecoders()->clear();
}
void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index b90f4c4..12371b4 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -28,7 +28,6 @@ class TimeDelta;
namespace media {
-class AudioDecoder;
class Clock;
class FilterCollection;
class MediaLog;
@@ -232,7 +231,6 @@ class MEDIA_EXPORT Pipeline
enum State {
kCreated,
kInitDemuxer,
- kInitAudioDecoder,
kInitAudioRenderer,
kInitVideoRenderer,
kInitPrerolling,
@@ -323,7 +321,6 @@ class MEDIA_EXPORT Pipeline
// Kicks off initialization for each media object, executing |done_cb| with
// the result when completed.
void InitializeDemuxer(const PipelineStatusCB& done_cb);
- void InitializeAudioDecoder(const PipelineStatusCB& done_cb);
void InitializeAudioRenderer(const PipelineStatusCB& done_cb);
void InitializeVideoRenderer(const PipelineStatusCB& done_cb);
@@ -460,12 +457,6 @@ class MEDIA_EXPORT Pipeline
// Demuxer reference used for setting the preload value.
scoped_refptr<Demuxer> demuxer_;
- // Audio decoder reference used during initialization.
- //
- // TODO(scherkus): Remove after renderers do initialization, see
- // http://crbug.com/145635
- scoped_refptr<AudioDecoder> audio_decoder_;
-
PipelineStatistics statistics_;
// Time of pipeline creation; is non-zero only until the pipeline first
diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc
index ca43938..ad2e2fe 100644
--- a/media/base/pipeline_unittest.cc
+++ b/media/base/pipeline_unittest.cc
@@ -160,16 +160,10 @@ class PipelineTest : public ::testing::Test {
return stream;
}
- // Sets up expectations to allow the audio decoder to initialize.
- void InitializeAudioDecoder(const scoped_refptr<DemuxerStream>& stream) {
- EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream, _, _))
- .WillOnce(RunPipelineStatusCB());
- }
-
// Sets up expectations to allow the video renderer to initialize.
void InitializeVideoRenderer(const scoped_refptr<DemuxerStream>& stream) {
- EXPECT_CALL(*mocks_->video_renderer(), Initialize(
- stream, _, _, _, _, _, _, _, _, _))
+ EXPECT_CALL(*mocks_->video_renderer(),
+ Initialize(stream, _, _, _, _, _, _, _, _, _))
.WillOnce(RunPipelineStatusCB2());
EXPECT_CALL(*mocks_->video_renderer(), SetPlaybackRate(0.0f));
@@ -182,19 +176,18 @@ class PipelineTest : public ::testing::Test {
}
// Sets up expectations to allow the audio renderer to initialize.
- void InitializeAudioRenderer(bool disable_after_init_cb = false) {
+ void InitializeAudioRenderer(const scoped_refptr<DemuxerStream>& stream,
+ bool disable_after_init_cb) {
if (disable_after_init_cb) {
- EXPECT_CALL(*mocks_->audio_renderer(), Initialize(
- scoped_refptr<AudioDecoder>(mocks_->audio_decoder()),
- _, _, _, _, _, _))
- .WillOnce(DoAll(RunPipelineStatusCB(),
- WithArg<5>(RunClosure()))); // |disabled_cb|.
+ EXPECT_CALL(*mocks_->audio_renderer(),
+ Initialize(stream, _, _, _, _, _, _, _, _))
+ .WillOnce(DoAll(RunPipelineStatusCB2(),
+ WithArg<7>(RunClosure()))); // |disabled_cb|.
} else {
- EXPECT_CALL(*mocks_->audio_renderer(), Initialize(
- scoped_refptr<AudioDecoder>(mocks_->audio_decoder()),
- _, _, _, _, _, _))
- .WillOnce(DoAll(SaveArg<3>(&audio_time_cb_),
- RunPipelineStatusCB()));
+ EXPECT_CALL(*mocks_->audio_renderer(),
+ Initialize(stream, _, _, _, _, _, _, _, _))
+ .WillOnce(DoAll(SaveArg<5>(&audio_time_cb_),
+ RunPipelineStatusCB2()));
}
}
@@ -394,8 +387,7 @@ TEST_F(PipelineTest, AudioStream) {
streams.push_back(audio_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializePipeline(PIPELINE_OK);
EXPECT_TRUE(pipeline_->HasAudio());
@@ -423,8 +415,7 @@ TEST_F(PipelineTest, AudioVideoStream) {
streams.push_back(video_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializeVideoRenderer(video_stream());
InitializePipeline(PIPELINE_OK);
@@ -440,8 +431,7 @@ TEST_F(PipelineTest, Seek) {
streams.push_back(video_stream());
InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000));
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializeVideoRenderer(video_stream());
// Initialize then seek!
@@ -459,8 +449,7 @@ TEST_F(PipelineTest, SetVolume) {
streams.push_back(audio_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
// The audio renderer should receive a call to SetVolume().
float expected = 0.5f;
@@ -546,8 +535,7 @@ TEST_F(PipelineTest, DisableAudioRenderer) {
streams.push_back(video_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializeVideoRenderer(video_stream());
InitializePipeline(PIPELINE_OK);
@@ -570,8 +558,7 @@ TEST_F(PipelineTest, DisableAudioRendererDuringInit) {
streams.push_back(video_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer(true);
+ InitializeAudioRenderer(audio_stream(), true);
InitializeVideoRenderer(video_stream());
EXPECT_CALL(*mocks_->demuxer(),
@@ -594,8 +581,7 @@ TEST_F(PipelineTest, EndedCallback) {
streams.push_back(video_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializeVideoRenderer(video_stream());
InitializePipeline(PIPELINE_OK);
@@ -628,8 +614,7 @@ TEST_F(PipelineTest, AudioStreamShorterThanVideo) {
pipeline_->SetClockForTesting(new Clock(&StaticClockFunction));
InitializeDemuxer(&streams, duration);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializeVideoRenderer(video_stream());
InitializePipeline(PIPELINE_OK);
@@ -672,8 +657,7 @@ TEST_F(PipelineTest, ErrorDuringSeek) {
streams.push_back(audio_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializePipeline(PIPELINE_OK);
float playback_rate = 1.0f;
@@ -728,8 +712,7 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
streams.push_back(audio_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializePipeline(PIPELINE_OK);
// Trigger additional requests on the pipeline during tear down from error.
@@ -809,8 +792,7 @@ TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) {
streams.push_back(audio_stream());
InitializeDemuxer(&streams);
- InitializeAudioDecoder(audio_stream());
- InitializeAudioRenderer();
+ InitializeAudioRenderer(audio_stream(), false);
InitializePipeline(PIPELINE_OK);
float playback_rate = 1.0f;
@@ -917,7 +899,6 @@ class PipelineTeardownTest : public PipelineTest {
public:
enum TeardownState {
kInitDemuxer,
- kInitAudioDecoder,
kInitAudioRenderer,
kInitVideoRenderer,
kPausing,
@@ -939,7 +920,6 @@ class PipelineTeardownTest : public PipelineTest {
void RunTest(TeardownState state, StopOrError stop_or_error) {
switch (state) {
case kInitDemuxer:
- case kInitAudioDecoder:
case kInitAudioRenderer:
case kInitVideoRenderer:
DoInitialize(state, stop_or_error);
@@ -1008,33 +988,17 @@ class PipelineTeardownTest : public PipelineTest {
streams.push_back(video_stream());
InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000));
- if (state == kInitAudioDecoder) {
- if (stop_or_error == kStop) {
- EXPECT_CALL(*mocks_->audio_decoder(), Initialize(_, _, _))
- .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunPipelineStatusCB()));
- EXPECT_CALL(callbacks_, OnStop());
- } else {
- status = PIPELINE_ERROR_DECODE;
- EXPECT_CALL(*mocks_->audio_decoder(), Initialize(_, _, _))
- .WillOnce(RunPipelineStatusCBWithStatus(status));
- }
-
- EXPECT_CALL(*mocks_->demuxer(), Stop(_)).WillOnce(RunClosure());
- return status;
- }
-
- EXPECT_CALL(*mocks_->audio_decoder(), Initialize(_, _, _))
- .WillOnce(RunPipelineStatusCB());
-
if (state == kInitAudioRenderer) {
if (stop_or_error == kStop) {
- EXPECT_CALL(*mocks_->audio_renderer(), Initialize(_, _, _, _, _, _, _))
- .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunPipelineStatusCB()));
+ EXPECT_CALL(*mocks_->audio_renderer(),
+ Initialize(_, _, _, _, _, _, _, _, _))
+ .WillOnce(DoAll(Stop(pipeline_, stop_cb), RunPipelineStatusCB2()));
EXPECT_CALL(callbacks_, OnStop());
} else {
status = PIPELINE_ERROR_INITIALIZATION_FAILED;
- EXPECT_CALL(*mocks_->audio_renderer(), Initialize(_, _, _, _, _, _, _))
- .WillOnce(RunPipelineStatusCBWithStatus(status));
+ EXPECT_CALL(*mocks_->audio_renderer(),
+ Initialize(_, _, _, _, _, _, _, _, _))
+ .WillOnce(RunPipelineStatusCB2WithStatus(status));
}
EXPECT_CALL(*mocks_->demuxer(), Stop(_)).WillOnce(RunClosure());
@@ -1042,8 +1006,9 @@ class PipelineTeardownTest : public PipelineTest {
return status;
}
- EXPECT_CALL(*mocks_->audio_renderer(), Initialize(_, _, _, _, _, _, _))
- .WillOnce(RunPipelineStatusCB());
+ EXPECT_CALL(*mocks_->audio_renderer(),
+ Initialize(_, _, _, _, _, _, _, _, _))
+ .WillOnce(RunPipelineStatusCB2());
if (state == kInitVideoRenderer) {
if (stop_or_error == kStop) {
@@ -1232,7 +1197,6 @@ class PipelineTeardownTest : public PipelineTest {
}
INSTANTIATE_TEARDOWN_TEST(Stop, InitDemuxer);
-INSTANTIATE_TEARDOWN_TEST(Stop, InitAudioDecoder);
INSTANTIATE_TEARDOWN_TEST(Stop, InitAudioRenderer);
INSTANTIATE_TEARDOWN_TEST(Stop, InitVideoRenderer);
INSTANTIATE_TEARDOWN_TEST(Stop, Pausing);
@@ -1243,7 +1207,6 @@ INSTANTIATE_TEARDOWN_TEST(Stop, Starting);
INSTANTIATE_TEARDOWN_TEST(Stop, Playing);
INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer);
-INSTANTIATE_TEARDOWN_TEST(Error, InitAudioDecoder);
INSTANTIATE_TEARDOWN_TEST(Error, InitAudioRenderer);
INSTANTIATE_TEARDOWN_TEST(Error, InitVideoRenderer);
INSTANTIATE_TEARDOWN_TEST(Error, Pausing);
diff --git a/media/filters/audio_renderer_impl.cc b/media/filters/audio_renderer_impl.cc
index 820cf6e..20dffd6 100644
--- a/media/filters/audio_renderer_impl.cc
+++ b/media/filters/audio_renderer_impl.cc
@@ -6,11 +6,14 @@
#include <math.h>
+#include <algorithm>
+
#include "base/bind.h"
#include "base/callback.h"
#include "base/callback_helpers.h"
#include "base/logging.h"
#include "media/audio/audio_util.h"
+#include "media/base/demuxer_stream.h"
namespace media {
@@ -83,6 +86,8 @@ void AudioRendererImpl::Flush(const base::Closure& callback) {
}
void AudioRendererImpl::Stop(const base::Closure& callback) {
+ DCHECK(!callback.is_null());
+
if (!stopped_) {
DCHECK(sink_.get());
sink_->Stop();
@@ -93,12 +98,12 @@ void AudioRendererImpl::Stop(const base::Closure& callback) {
base::AutoLock auto_lock(lock_);
state_ = kStopped;
algorithm_.reset(NULL);
- time_cb_.Reset();
+ init_cb_.Reset();
underflow_cb_.Reset();
+ time_cb_.Reset();
}
- if (!callback.is_null()) {
- callback.Run();
- }
+
+ callback.Run();
}
void AudioRendererImpl::Preroll(base::TimeDelta time,
@@ -130,14 +135,19 @@ void AudioRendererImpl::Preroll(base::TimeDelta time,
sink_->Pause(true);
}
-void AudioRendererImpl::Initialize(const scoped_refptr<AudioDecoder>& decoder,
+void AudioRendererImpl::Initialize(const scoped_refptr<DemuxerStream>& stream,
+ const AudioDecoderList& decoders,
const PipelineStatusCB& init_cb,
+ const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const base::Closure& ended_cb,
const base::Closure& disabled_cb,
const PipelineStatusCB& error_cb) {
- DCHECK(decoder);
+ base::AutoLock auto_lock(lock_);
+ DCHECK(stream);
+ DCHECK(!decoders.empty());
+ DCHECK_EQ(stream->type(), DemuxerStream::AUDIO);
DCHECK(!init_cb.is_null());
DCHECK(!underflow_cb.is_null());
DCHECK(!time_cb.is_null());
@@ -145,13 +155,61 @@ void AudioRendererImpl::Initialize(const scoped_refptr<AudioDecoder>& decoder,
DCHECK(!disabled_cb.is_null());
DCHECK(!error_cb.is_null());
DCHECK_EQ(kUninitialized, state_);
- decoder_ = decoder;
+
+ init_cb_ = init_cb;
+ statistics_cb_ = statistics_cb;
underflow_cb_ = underflow_cb;
time_cb_ = time_cb;
ended_cb_ = ended_cb;
disabled_cb_ = disabled_cb;
error_cb_ = error_cb;
+ scoped_ptr<AudioDecoderList> decoder_list(new AudioDecoderList(decoders));
+ InitializeNextDecoder(stream, decoder_list.Pass());
+}
+
+void AudioRendererImpl::InitializeNextDecoder(
+ const scoped_refptr<DemuxerStream>& demuxer_stream,
+ scoped_ptr<AudioDecoderList> decoders) {
+ lock_.AssertAcquired();
+ DCHECK(!decoders->empty());
+
+ scoped_refptr<AudioDecoder> decoder = decoders->front();
+ decoders->pop_front();
+
+ DCHECK(decoder);
+ decoder_ = decoder;
+
+ base::AutoUnlock auto_unlock(lock_);
+ decoder->Initialize(
+ demuxer_stream,
+ base::Bind(&AudioRendererImpl::OnDecoderInitDone, this,
+ demuxer_stream,
+ base::Passed(&decoders)),
+ statistics_cb_);
+}
+
+void AudioRendererImpl::OnDecoderInitDone(
+ const scoped_refptr<DemuxerStream>& demuxer_stream,
+ scoped_ptr<AudioDecoderList> decoders,
+ PipelineStatus status) {
+ base::AutoLock auto_lock(lock_);
+
+ if (state_ == kStopped) {
+ DCHECK(stopped_);
+ return;
+ }
+
+ if (!decoders->empty() && status == DECODER_ERROR_NOT_SUPPORTED) {
+ InitializeNextDecoder(demuxer_stream, decoders.Pass());
+ return;
+ }
+
+ if (status != PIPELINE_OK) {
+ base::ResetAndReturn(&init_cb_).Run(status);
+ return;
+ }
+
// Create a callback so our algorithm can request more reads.
base::Closure cb = base::Bind(&AudioRendererImpl::ScheduleRead_Locked, this);
@@ -170,7 +228,7 @@ void AudioRendererImpl::Initialize(const scoped_refptr<AudioDecoder>& decoder,
bool config_ok = algorithm_->ValidateConfig(channels, sample_rate,
bits_per_channel);
if (!config_ok || is_initialized_) {
- init_cb.Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ base::ResetAndReturn(&init_cb_).Run(PIPELINE_ERROR_INITIALIZATION_FAILED);
return;
}
@@ -197,7 +255,7 @@ void AudioRendererImpl::Initialize(const scoped_refptr<AudioDecoder>& decoder,
// Finally, execute the start callback.
state_ = kPaused;
- init_cb.Run(PIPELINE_OK);
+ base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
}
void AudioRendererImpl::ResumeAfterUnderflow(bool buffer_more_audio) {
diff --git a/media/filters/audio_renderer_impl.h b/media/filters/audio_renderer_impl.h
index 998bdb5..b154acf 100644
--- a/media/filters/audio_renderer_impl.h
+++ b/media/filters/audio_renderer_impl.h
@@ -41,8 +41,10 @@ class MEDIA_EXPORT AudioRendererImpl
// Methods called on pipeline thread ----------------------------------------
// AudioRenderer implementation.
- virtual void Initialize(const scoped_refptr<AudioDecoder>& decoder,
+ virtual void Initialize(const scoped_refptr<DemuxerStream>& stream,
+ const AudioDecoderList& decoders,
const PipelineStatusCB& init_cb,
+ const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const base::Closure& ended_cb,
@@ -134,6 +136,18 @@ class MEDIA_EXPORT AudioRendererImpl
// in the kPrerolling state.
bool IsBeforePrerollTime(const scoped_refptr<Buffer>& buffer);
+ // Pops the front of |decoders|, assigns it to |decoder_| and then
+ // calls initialize on the new decoder.
+ void InitializeNextDecoder(const scoped_refptr<DemuxerStream>& demuxer_stream,
+ scoped_ptr<AudioDecoderList> decoders);
+
+ // Called when |decoder_| initialization completes.
+ // |demuxer_stream| & |decoders| are used if initialization failed and
+ // InitializeNextDecoder() needs to be called again.
+ void OnDecoderInitDone(const scoped_refptr<DemuxerStream>& demuxer_stream,
+ scoped_ptr<AudioDecoderList> decoders,
+ PipelineStatus status);
+
// Audio decoder.
scoped_refptr<AudioDecoder> decoder_;
@@ -166,6 +180,9 @@ class MEDIA_EXPORT AudioRendererImpl
base::TimeDelta audio_time_buffered_;
base::TimeDelta current_time_;
+ PipelineStatusCB init_cb_;
+ StatisticsCB statistics_cb_;
+
// Filter callbacks.
base::Closure pause_cb_;
PipelineStatusCB preroll_cb_;
diff --git a/media/filters/audio_renderer_impl_unittest.cc b/media/filters/audio_renderer_impl_unittest.cc
index 89c870b..fe901f0 100644
--- a/media/filters/audio_renderer_impl_unittest.cc
+++ b/media/filters/audio_renderer_impl_unittest.cc
@@ -20,6 +20,10 @@ using ::testing::Return;
using ::testing::NiceMock;
using ::testing::StrictMock;
+ACTION_P(RunPipelineStatusCB1, status) {
+ arg1.Run(status);
+}
+
namespace media {
// Constants for distinguishing between muted audio and playing audio when using
@@ -32,7 +36,10 @@ class AudioRendererImplTest : public ::testing::Test {
// Give the decoder some non-garbage media properties.
AudioRendererImplTest()
: renderer_(new AudioRendererImpl(new NiceMock<MockAudioRendererSink>())),
+ demuxer_stream_(new MockDemuxerStream()),
decoder_(new MockAudioDecoder()) {
+ EXPECT_CALL(*demuxer_stream_, type())
+ .WillRepeatedly(Return(DemuxerStream::AUDIO));
// Queue all reads from the decoder by default.
ON_CALL(*decoder_, Read(_))
@@ -46,6 +53,8 @@ class AudioRendererImplTest : public ::testing::Test {
.Times(AnyNumber());
EXPECT_CALL(*decoder_, samples_per_second())
.Times(AnyNumber());
+
+ decoders_.push_back(decoder_);
}
virtual ~AudioRendererImplTest() {
@@ -76,6 +85,7 @@ class AudioRendererImplTest : public ::testing::Test {
base::Unretained(this));
}
+ MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&));
MOCK_METHOD0(OnUnderflow, void());
MOCK_METHOD0(OnEnded, void());
MOCK_METHOD0(OnDisabled, void());
@@ -87,12 +97,19 @@ class AudioRendererImplTest : public ::testing::Test {
}
void Initialize() {
+ EXPECT_CALL(*decoder_, Initialize(_, _, _))
+ .WillOnce(RunPipelineStatusCB1(PIPELINE_OK));
+
InitializeWithStatus(PIPELINE_OK);
}
void InitializeWithStatus(PipelineStatus expected) {
renderer_->Initialize(
- decoder_, NewExpectedStatusCB(expected),
+ demuxer_stream_,
+ decoders_,
+ NewExpectedStatusCB(expected),
+ base::Bind(&AudioRendererImplTest::OnStatistics,
+ base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnUnderflow,
base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnAudioTimeCallback,
@@ -207,7 +224,9 @@ class AudioRendererImplTest : public ::testing::Test {
// Fixture members.
scoped_refptr<AudioRendererImpl> renderer_;
+ scoped_refptr<MockDemuxerStream> demuxer_stream_;
scoped_refptr<MockAudioDecoder> decoder_;
+ AudioRendererImpl::AudioDecoderList decoders_;
AudioDecoder::ReadCB read_cb_;
base::TimeDelta next_timestamp_;
@@ -221,7 +240,10 @@ class AudioRendererImplTest : public ::testing::Test {
};
TEST_F(AudioRendererImplTest, Initialize_Failed) {
+ EXPECT_CALL(*decoder_, Initialize(_, _, _))
+ .WillOnce(RunPipelineStatusCB1(PIPELINE_OK));
SetUnsupportedAudioDecoderProperties();
+
InitializeWithStatus(PIPELINE_ERROR_INITIALIZATION_FAILED);
// We should have no reads.
@@ -235,6 +257,29 @@ TEST_F(AudioRendererImplTest, Initialize_Successful) {
EXPECT_TRUE(read_cb_.is_null());
}
+TEST_F(AudioRendererImplTest, Initialize_DecoderInitFailure) {
+ EXPECT_CALL(*decoder_, Initialize(_, _, _))
+ .WillOnce(RunPipelineStatusCB1(PIPELINE_ERROR_DECODE));
+ InitializeWithStatus(PIPELINE_ERROR_DECODE);
+
+ // We should have no reads.
+ EXPECT_TRUE(read_cb_.is_null());
+}
+
+TEST_F(AudioRendererImplTest, Initialize_MultipleDecoders) {
+ scoped_refptr<MockAudioDecoder> decoder1 = new MockAudioDecoder();
+ // Insert |decoder1| as the first decoder in the list.
+ decoders_.push_front(decoder1);
+ EXPECT_CALL(*decoder1, Initialize(_, _, _))
+ .WillOnce(RunPipelineStatusCB1(DECODER_ERROR_NOT_SUPPORTED));
+ EXPECT_CALL(*decoder_, Initialize(_, _, _))
+ .WillOnce(RunPipelineStatusCB1(PIPELINE_OK));
+ InitializeWithStatus(PIPELINE_OK);
+
+ // We should have no reads.
+ EXPECT_TRUE(read_cb_.is_null());
+}
+
TEST_F(AudioRendererImplTest, Preroll) {
Initialize();
Preroll();
diff --git a/media/filters/pipeline_integration_test_base.cc b/media/filters/pipeline_integration_test_base.cc
index 7c1cc5d..9e3c6a9 100644
--- a/media/filters/pipeline_integration_test_base.cc
+++ b/media/filters/pipeline_integration_test_base.cc
@@ -197,16 +197,17 @@ PipelineIntegrationTestBase::CreateFilterCollection(
Decryptor* decryptor) {
scoped_ptr<FilterCollection> collection(new FilterCollection());
collection->SetDemuxer(demuxer);
- collection->AddAudioDecoder(new FFmpegAudioDecoder(
+ scoped_refptr<AudioDecoder> audio_decoder = new FFmpegAudioDecoder(
base::Bind(&MessageLoopFactory::GetMessageLoop,
base::Unretained(message_loop_factory_.get()),
- media::MessageLoopFactory::kDecoder)));
- scoped_refptr<VideoDecoder> decoder = new FFmpegVideoDecoder(
+ media::MessageLoopFactory::kDecoder));
+ scoped_refptr<VideoDecoder> video_decoder = new FFmpegVideoDecoder(
base::Bind(&MessageLoopFactory::GetMessageLoop,
base::Unretained(message_loop_factory_.get()),
media::MessageLoopFactory::kDecoder),
decryptor);
- collection->GetVideoDecoders()->push_back(decoder);
+ collection->GetAudioDecoders()->push_back(audio_decoder);
+ collection->GetVideoDecoders()->push_back(video_decoder);
// Disable frame dropping if hashing is enabled.
renderer_ = new VideoRendererBase(
diff --git a/media/tools/player_wtl/movie.cc b/media/tools/player_wtl/movie.cc
index ed162ce..3029da9 100644
--- a/media/tools/player_wtl/movie.cc
+++ b/media/tools/player_wtl/movie.cc
@@ -76,7 +76,7 @@ bool Movie::Open(const wchar_t* url, VideoRendererBase* video_renderer) {
// Create filter collection.
scoped_ptr<FilterCollection> collection(new FilterCollection());
collection->SetDemuxer(new FFmpegDemuxer(pipeline_loop, data_source));
- collection->AddAudioDecoder(new FFmpegAudioDecoder(
+ collection->GetAudioDecoders()->push_back(new FFmpegAudioDecoder(
base::Bind(&MessageLoopFactory::GetMessageLoop,
base::Unretained(message_loop_factory_.get()),
media::MessageLoopFactory::kDecoder)));
diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc
index fd30b9a..b1c814e 100644
--- a/media/tools/player_x11/player_x11.cc
+++ b/media/tools/player_x11/player_x11.cc
@@ -117,7 +117,7 @@ bool InitPipeline(const scoped_refptr<base::MessageLoopProxy>& message_loop,
scoped_ptr<media::FilterCollection> collection(
new media::FilterCollection());
collection->SetDemuxer(new media::FFmpegDemuxer(message_loop, data_source));
- collection->AddAudioDecoder(new media::FFmpegAudioDecoder(
+ collection->GetAudioDecoders()->push_back(new media::FFmpegAudioDecoder(
base::Bind(&media::MessageLoopFactory::GetMessageLoop,
base::Unretained(message_loop_factory),
media::MessageLoopFactory::kDecoder)));