diff options
author | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 07:02:42 +0000 |
---|---|---|
committer | xhwang@chromium.org <xhwang@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-10-18 07:02:42 +0000 |
commit | 8a8f4d1a570302b00c7c2a64c699a108621893ec (patch) | |
tree | c291166134e75e4baceb798b689e0dd4d70efe1e /media | |
parent | 741c28f9db1521cb02c6b89fe2966f67f4329dff (diff) | |
download | chromium_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.h | 11 | ||||
-rw-r--r-- | media/base/filter_collection.cc | 17 | ||||
-rw-r--r-- | media/base/filter_collection.h | 5 | ||||
-rw-r--r-- | media/base/filter_collection_unittest.cc | 48 | ||||
-rw-r--r-- | media/base/mock_filters.cc | 2 | ||||
-rw-r--r-- | media/base/mock_filters.h | 4 | ||||
-rw-r--r-- | media/base/pipeline.cc | 26 | ||||
-rw-r--r-- | media/base/pipeline.h | 9 | ||||
-rw-r--r-- | media/base/pipeline_unittest.cc | 101 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.cc | 76 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl.h | 19 | ||||
-rw-r--r-- | media/filters/audio_renderer_impl_unittest.cc | 47 | ||||
-rw-r--r-- | media/filters/pipeline_integration_test_base.cc | 9 | ||||
-rw-r--r-- | media/tools/player_wtl/movie.cc | 2 | ||||
-rw-r--r-- | media/tools/player_x11/player_x11.cc | 2 |
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))); |