| Index: media/base/pipeline_unittest.cc
|
| diff --git a/media/base/pipeline_unittest.cc b/media/base/pipeline_unittest.cc
|
| index c064df4c3c130635380f33bd05fccbfa86ef25e0..4eb84f62c3f98eaa88a37c0dca1a1674527e546a 100644
|
| --- a/media/base/pipeline_unittest.cc
|
| +++ b/media/base/pipeline_unittest.cc
|
| @@ -55,6 +55,26 @@
|
| cb->Run(buffering_state);
|
| }
|
|
|
| +// Used for setting expectations on pipeline callbacks. Using a StrictMock
|
| +// also lets us test for missing callbacks.
|
| +class CallbackHelper {
|
| + public:
|
| + CallbackHelper() {}
|
| + virtual ~CallbackHelper() {}
|
| +
|
| + MOCK_METHOD1(OnStart, void(PipelineStatus));
|
| + MOCK_METHOD1(OnSeek, void(PipelineStatus));
|
| + MOCK_METHOD0(OnStop, void());
|
| + MOCK_METHOD0(OnEnded, void());
|
| + MOCK_METHOD1(OnError, void(PipelineStatus));
|
| + MOCK_METHOD1(OnMetadata, void(PipelineMetadata));
|
| + MOCK_METHOD1(OnBufferingStateChange, void(BufferingState));
|
| + MOCK_METHOD0(OnDurationChange, void());
|
| +
|
| + private:
|
| + DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
|
| +};
|
| +
|
| // TODO(scherkus): even though some filters are initialized on separate
|
| // threads these test aren't flaky... why? It's because filters' Initialize()
|
| // is executed on |message_loop_| and the mock filters instantly call
|
| @@ -63,26 +83,6 @@
|
| // initialization is moved to a separate thread this test will become flaky.
|
| class PipelineTest : public ::testing::Test {
|
| public:
|
| - // Used for setting expectations on pipeline callbacks. Using a StrictMock
|
| - // also lets us test for missing callbacks.
|
| - class CallbackHelper {
|
| - public:
|
| - CallbackHelper() {}
|
| - virtual ~CallbackHelper() {}
|
| -
|
| - MOCK_METHOD1(OnStart, void(PipelineStatus));
|
| - MOCK_METHOD1(OnSeek, void(PipelineStatus));
|
| - MOCK_METHOD0(OnStop, void());
|
| - MOCK_METHOD0(OnEnded, void());
|
| - MOCK_METHOD1(OnError, void(PipelineStatus));
|
| - MOCK_METHOD1(OnMetadata, void(PipelineMetadata));
|
| - MOCK_METHOD1(OnBufferingStateChange, void(BufferingState));
|
| - MOCK_METHOD0(OnDurationChange, void());
|
| -
|
| - private:
|
| - DISALLOW_COPY_AND_ASSIGN(CallbackHelper);
|
| - };
|
| -
|
| PipelineTest()
|
| : pipeline_(new Pipeline(message_loop_.message_loop_proxy(),
|
| new MediaLog())),
|
| @@ -90,9 +90,13 @@
|
| demuxer_(new StrictMock<MockDemuxer>()) {
|
| filter_collection_->SetDemuxer(demuxer_.get());
|
|
|
| - renderer_ = new StrictMock<MockRenderer>();
|
| - scoped_ptr<Renderer> renderer(renderer_);
|
| - filter_collection_->SetRenderer(renderer.Pass());
|
| + video_renderer_ = new StrictMock<MockVideoRenderer>();
|
| + scoped_ptr<VideoRenderer> video_renderer(video_renderer_);
|
| + filter_collection_->SetVideoRenderer(video_renderer.Pass());
|
| +
|
| + audio_renderer_ = new StrictMock<MockAudioRenderer>();
|
| + scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_);
|
| + filter_collection_->SetAudioRenderer(audio_renderer.Pass());
|
|
|
| text_renderer_ = new TextRenderer(
|
| message_loop_.message_loop_proxy(),
|
| @@ -112,9 +116,6 @@
|
|
|
| EXPECT_CALL(*demuxer_, GetLiveness())
|
| .WillRepeatedly(Return(Demuxer::LIVENESS_UNKNOWN));
|
| -
|
| - EXPECT_CALL(*renderer_, GetMediaTime())
|
| - .WillRepeatedly(Return(base::TimeDelta()));
|
| }
|
|
|
| virtual ~PipelineTest() {
|
| @@ -174,13 +175,20 @@
|
| }
|
|
|
| // Sets up expectations to allow the video renderer to initialize.
|
| - void SetRendererExpectations() {
|
| - EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
|
| - .WillOnce(DoAll(SaveArg<2>(&ended_cb_),
|
| - SaveArg<4>(&buffering_state_cb_),
|
| - RunCallback<0>(PIPELINE_OK)));
|
| - EXPECT_CALL(*renderer_, HasAudio()).WillRepeatedly(Return(audio_stream()));
|
| - EXPECT_CALL(*renderer_, HasVideo()).WillRepeatedly(Return(video_stream()));
|
| + void SetVideoRendererExpectations(DemuxerStream* stream) {
|
| + EXPECT_CALL(*video_renderer_, Initialize(stream, _, _, _, _, _, _, _, _, _))
|
| + .WillOnce(DoAll(SaveArg<5>(&video_buffering_state_cb_),
|
| + SaveArg<6>(&video_ended_cb_),
|
| + RunCallback<2>(PIPELINE_OK)));
|
| + }
|
| +
|
| + // Sets up expectations to allow the audio renderer to initialize.
|
| + void SetAudioRendererExpectations(DemuxerStream* stream) {
|
| + EXPECT_CALL(*audio_renderer_, Initialize(stream, _, _, _, _, _, _))
|
| + .WillOnce(DoAll(SaveArg<3>(&audio_time_cb_),
|
| + SaveArg<4>(&audio_buffering_state_cb_),
|
| + SaveArg<5>(&audio_ended_cb_),
|
| + RunCallback<1>(PIPELINE_OK)));
|
| }
|
|
|
| void AddTextStream() {
|
| @@ -188,7 +196,6 @@
|
| .WillOnce(Invoke(this, &PipelineTest::DoOnAddTextTrack));
|
| static_cast<DemuxerHost*>(pipeline_.get())->AddTextStream(text_stream(),
|
| TextTrackConfig(kTextSubtitles, "", "", ""));
|
| - message_loop_.RunUntilIdle();
|
| }
|
|
|
| // Sets up expectations on the callback and initializes the pipeline. Called
|
| @@ -198,11 +205,25 @@
|
|
|
| if (start_status == PIPELINE_OK) {
|
| EXPECT_CALL(callbacks_, OnMetadata(_)).WillOnce(SaveArg<0>(&metadata_));
|
| - EXPECT_CALL(*renderer_, SetPlaybackRate(0.0f));
|
| - EXPECT_CALL(*renderer_, SetVolume(1.0f));
|
| - EXPECT_CALL(*renderer_, StartPlayingFrom(base::TimeDelta()))
|
| - .WillOnce(SetBufferingState(&buffering_state_cb_,
|
| - BUFFERING_HAVE_ENOUGH));
|
| +
|
| + if (audio_stream_) {
|
| + EXPECT_CALL(*audio_renderer_, GetTimeSource())
|
| + .WillOnce(Return(&time_source_));
|
| + EXPECT_CALL(time_source_, SetPlaybackRate(0.0f));
|
| + EXPECT_CALL(time_source_, SetMediaTime(base::TimeDelta()));
|
| + EXPECT_CALL(time_source_, StartTicking());
|
| + EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
|
| + EXPECT_CALL(*audio_renderer_, StartPlaying())
|
| + .WillOnce(SetBufferingState(&audio_buffering_state_cb_,
|
| + BUFFERING_HAVE_ENOUGH));
|
| + }
|
| +
|
| + if (video_stream_) {
|
| + EXPECT_CALL(*video_renderer_, StartPlaying())
|
| + .WillOnce(SetBufferingState(&video_buffering_state_cb_,
|
| + BUFFERING_HAVE_ENOUGH));
|
| + }
|
| +
|
| EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
|
| }
|
|
|
| @@ -247,19 +268,35 @@
|
| }
|
|
|
| void ExpectSeek(const base::TimeDelta& seek_time, bool underflowed) {
|
| + // Every filter should receive a call to Seek().
|
| EXPECT_CALL(*demuxer_, Seek(seek_time, _))
|
| .WillOnce(RunCallback<1>(PIPELINE_OK));
|
|
|
| - EXPECT_CALL(*renderer_, Flush(_))
|
| - .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
|
| - BUFFERING_HAVE_NOTHING),
|
| - RunClosure<0>()));
|
| - EXPECT_CALL(*renderer_, SetPlaybackRate(_));
|
| - EXPECT_CALL(*renderer_, SetVolume(_));
|
| - EXPECT_CALL(*renderer_, StartPlayingFrom(seek_time))
|
| - .WillOnce(SetBufferingState(&buffering_state_cb_,
|
| - BUFFERING_HAVE_ENOUGH));
|
| - EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| + if (audio_stream_) {
|
| + if (!underflowed)
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| + EXPECT_CALL(*audio_renderer_, Flush(_))
|
| + .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
|
| + BUFFERING_HAVE_NOTHING),
|
| + RunClosure<0>()));
|
| + EXPECT_CALL(time_source_, SetMediaTime(seek_time));
|
| + EXPECT_CALL(time_source_, SetPlaybackRate(_));
|
| + EXPECT_CALL(time_source_, StartTicking());
|
| + EXPECT_CALL(*audio_renderer_, StartPlaying())
|
| + .WillOnce(SetBufferingState(&audio_buffering_state_cb_,
|
| + BUFFERING_HAVE_ENOUGH));
|
| + EXPECT_CALL(*audio_renderer_, SetVolume(_));
|
| + }
|
| +
|
| + if (video_stream_) {
|
| + EXPECT_CALL(*video_renderer_, Flush(_))
|
| + .WillOnce(DoAll(SetBufferingState(&video_buffering_state_cb_,
|
| + BUFFERING_HAVE_NOTHING),
|
| + RunClosure<0>()));
|
| + EXPECT_CALL(*video_renderer_, StartPlaying())
|
| + .WillOnce(SetBufferingState(&video_buffering_state_cb_,
|
| + BUFFERING_HAVE_ENOUGH));
|
| + }
|
|
|
| // We expect a successful seek callback followed by a buffering update.
|
| EXPECT_CALL(callbacks_, OnSeek(PIPELINE_OK));
|
| @@ -270,7 +307,11 @@
|
| pipeline_->Seek(seek_time,
|
| base::Bind(&CallbackHelper::OnSeek,
|
| base::Unretained(&callbacks_)));
|
| +
|
| + // We expect the time to be updated only after the seek has completed.
|
| + EXPECT_NE(seek_time, pipeline_->GetMediaTime());
|
| message_loop_.RunUntilIdle();
|
| + EXPECT_EQ(seek_time, pipeline_->GetMediaTime());
|
| }
|
|
|
| void DestroyPipeline() {
|
| @@ -309,14 +350,19 @@
|
|
|
| scoped_ptr<FilterCollection> filter_collection_;
|
| scoped_ptr<StrictMock<MockDemuxer> > demuxer_;
|
| - StrictMock<MockRenderer>* renderer_;
|
| + StrictMock<MockVideoRenderer>* video_renderer_;
|
| + StrictMock<MockAudioRenderer>* audio_renderer_;
|
| + StrictMock<MockTimeSource> time_source_;
|
| StrictMock<CallbackHelper> text_renderer_callbacks_;
|
| TextRenderer* text_renderer_;
|
| scoped_ptr<StrictMock<MockDemuxerStream> > audio_stream_;
|
| scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_;
|
| scoped_ptr<FakeTextTrackStream> text_stream_;
|
| - BufferingStateCB buffering_state_cb_;
|
| - base::Closure ended_cb_;
|
| + AudioRenderer::TimeCB audio_time_cb_;
|
| + BufferingStateCB audio_buffering_state_cb_;
|
| + BufferingStateCB video_buffering_state_cb_;
|
| + base::Closure audio_ended_cb_;
|
| + base::Closure video_ended_cb_;
|
| VideoDecoderConfig video_decoder_config_;
|
| PipelineMetadata metadata_;
|
|
|
| @@ -415,7 +461,7 @@
|
| streams.push_back(audio_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
|
|
| @@ -453,7 +499,7 @@
|
| streams.push_back(audio_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
| EXPECT_TRUE(metadata_.has_audio);
|
| @@ -466,7 +512,7 @@
|
| streams.push_back(video_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetVideoRendererExpectations(video_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
| EXPECT_FALSE(metadata_.has_audio);
|
| @@ -481,7 +527,8 @@
|
| streams.push_back(video_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
| + SetVideoRendererExpectations(video_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
| EXPECT_TRUE(metadata_.has_audio);
|
| @@ -495,13 +542,14 @@
|
| streams.push_back(video_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetVideoRendererExpectations(video_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
| EXPECT_FALSE(metadata_.has_audio);
|
| EXPECT_TRUE(metadata_.has_video);
|
|
|
| AddTextStream();
|
| + message_loop_.RunUntilIdle();
|
| }
|
|
|
| TEST_F(PipelineTest, VideoAudioTextStream) {
|
| @@ -513,13 +561,15 @@
|
| streams.push_back(audio_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetVideoRendererExpectations(video_stream());
|
| + SetAudioRendererExpectations(audio_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
| EXPECT_TRUE(metadata_.has_audio);
|
| EXPECT_TRUE(metadata_.has_video);
|
|
|
| AddTextStream();
|
| + message_loop_.RunUntilIdle();
|
| }
|
|
|
| TEST_F(PipelineTest, Seek) {
|
| @@ -531,7 +581,8 @@
|
| streams.push_back(video_stream());
|
|
|
| SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000));
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
| + SetVideoRendererExpectations(video_stream());
|
|
|
| // Initialize then seek!
|
| StartPipeline(PIPELINE_OK);
|
| @@ -548,7 +599,7 @@
|
| streams.push_back(audio_stream());
|
|
|
| SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000));
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
|
|
| // Initialize then seek!
|
| StartPipeline(PIPELINE_OK);
|
| @@ -573,11 +624,11 @@
|
| streams.push_back(audio_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
|
|
| // The audio renderer should receive a call to SetVolume().
|
| float expected = 0.5f;
|
| - EXPECT_CALL(*renderer_, SetVolume(expected));
|
| + EXPECT_CALL(*audio_renderer_, SetVolume(expected));
|
|
|
| // Initialize then set volume!
|
| StartPipeline(PIPELINE_OK);
|
| @@ -591,7 +642,7 @@
|
|
|
| const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
|
| SetDemuxerExpectations(&streams, kDuration);
|
| - SetRendererExpectations();
|
| + SetVideoRendererExpectations(video_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
| EXPECT_EQ(kDuration.ToInternalValue(),
|
| @@ -606,7 +657,7 @@
|
|
|
| const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
|
| SetDemuxerExpectations(&streams, kDuration);
|
| - SetRendererExpectations();
|
| + SetVideoRendererExpectations(video_stream());
|
|
|
| StartPipeline(PIPELINE_OK);
|
|
|
| @@ -636,39 +687,93 @@
|
| streams.push_back(video_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
| + SetVideoRendererExpectations(video_stream());
|
| StartPipeline(PIPELINE_OK);
|
|
|
| AddTextStream();
|
|
|
| // The ended callback shouldn't run until all renderers have ended.
|
| - ended_cb_.Run();
|
| - message_loop_.RunUntilIdle();
|
| -
|
| + audio_ended_cb_.Run();
|
| + message_loop_.RunUntilIdle();
|
| +
|
| + video_ended_cb_.Run();
|
| + message_loop_.RunUntilIdle();
|
| +
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| EXPECT_CALL(callbacks_, OnEnded());
|
| text_stream()->SendEosNotification();
|
| message_loop_.RunUntilIdle();
|
| }
|
|
|
| +TEST_F(PipelineTest, AudioStreamShorterThanVideo) {
|
| + base::TimeDelta duration = base::TimeDelta::FromSeconds(10);
|
| +
|
| + CreateAudioStream();
|
| + CreateVideoStream();
|
| + MockDemuxerStreamVector streams;
|
| + streams.push_back(audio_stream());
|
| + streams.push_back(video_stream());
|
| +
|
| + // Replace what's used for interpolating to simulate wall clock time.
|
| + pipeline_->SetTimeDeltaInterpolatorForTesting(
|
| + new TimeDeltaInterpolator(&test_tick_clock_));
|
| +
|
| + SetDemuxerExpectations(&streams, duration);
|
| + SetAudioRendererExpectations(audio_stream());
|
| + SetVideoRendererExpectations(video_stream());
|
| + StartPipeline(PIPELINE_OK);
|
| +
|
| + EXPECT_EQ(0, pipeline_->GetMediaTime().ToInternalValue());
|
| +
|
| + float playback_rate = 1.0f;
|
| + EXPECT_CALL(time_source_, SetPlaybackRate(playback_rate));
|
| + pipeline_->SetPlaybackRate(playback_rate);
|
| + message_loop_.RunUntilIdle();
|
| +
|
| + InSequence s;
|
| +
|
| + // Verify that the clock doesn't advance since it hasn't been started by
|
| + // a time update from the audio stream.
|
| + int64 start_time = pipeline_->GetMediaTime().ToInternalValue();
|
| + test_tick_clock_.Advance(base::TimeDelta::FromMilliseconds(100));
|
| + EXPECT_EQ(pipeline_->GetMediaTime().ToInternalValue(), start_time);
|
| +
|
| + // Signal end of audio stream.
|
| + audio_ended_cb_.Run();
|
| + message_loop_.RunUntilIdle();
|
| +
|
| + // Verify that the clock advances.
|
| + start_time = pipeline_->GetMediaTime().ToInternalValue();
|
| + test_tick_clock_.Advance(base::TimeDelta::FromMilliseconds(100));
|
| + EXPECT_GT(pipeline_->GetMediaTime().ToInternalValue(), start_time);
|
| +
|
| + // Signal end of video stream and make sure OnEnded() callback occurs.
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| + EXPECT_CALL(callbacks_, OnEnded());
|
| + video_ended_cb_.Run();
|
| +}
|
| +
|
| TEST_F(PipelineTest, ErrorDuringSeek) {
|
| CreateAudioStream();
|
| MockDemuxerStreamVector streams;
|
| streams.push_back(audio_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
| StartPipeline(PIPELINE_OK);
|
|
|
| float playback_rate = 1.0f;
|
| - EXPECT_CALL(*renderer_, SetPlaybackRate(playback_rate));
|
| + EXPECT_CALL(time_source_, SetPlaybackRate(playback_rate));
|
| pipeline_->SetPlaybackRate(playback_rate);
|
| message_loop_.RunUntilIdle();
|
|
|
| base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5);
|
|
|
| - EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| - EXPECT_CALL(*renderer_, Flush(_))
|
| - .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
|
| + // Preroll() isn't called as the demuxer errors out first.
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| + EXPECT_CALL(*audio_renderer_, Flush(_))
|
| + .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
|
| BUFFERING_HAVE_NOTHING),
|
| RunClosure<0>()));
|
|
|
| @@ -708,7 +813,7 @@
|
| streams.push_back(audio_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
| StartPipeline(PIPELINE_OK);
|
|
|
| // Trigger additional requests on the pipeline during tear down from error.
|
| @@ -720,11 +825,11 @@
|
| base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5);
|
|
|
| // Seek() isn't called as the demuxer errors out first.
|
| - EXPECT_CALL(*renderer_, Flush(_))
|
| - .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| + EXPECT_CALL(*audio_renderer_, Flush(_))
|
| + .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
|
| BUFFERING_HAVE_NOTHING),
|
| RunClosure<0>()));
|
| - EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
|
|
| EXPECT_CALL(*demuxer_, Seek(seek_time, _))
|
| .WillOnce(RunCallback<1>(PIPELINE_ERROR_READ));
|
| @@ -737,12 +842,75 @@
|
| message_loop_.RunUntilIdle();
|
| }
|
|
|
| +static void RunTimeCB(const AudioRenderer::TimeCB& time_cb,
|
| + int time_in_ms,
|
| + int max_time_in_ms) {
|
| + time_cb.Run(base::TimeDelta::FromMilliseconds(time_in_ms),
|
| + base::TimeDelta::FromMilliseconds(max_time_in_ms));
|
| +}
|
| +
|
| +TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) {
|
| + CreateAudioStream();
|
| + MockDemuxerStreamVector streams;
|
| + streams.push_back(audio_stream());
|
| +
|
| + SetDemuxerExpectations(&streams);
|
| + SetAudioRendererExpectations(audio_stream());
|
| + StartPipeline(PIPELINE_OK);
|
| +
|
| + float playback_rate = 1.0f;
|
| + EXPECT_CALL(time_source_, SetPlaybackRate(playback_rate));
|
| + pipeline_->SetPlaybackRate(playback_rate);
|
| + message_loop_.RunUntilIdle();
|
| +
|
| + // Provide an initial time update so that the pipeline transitions out of the
|
| + // "waiting for time update" state.
|
| + audio_time_cb_.Run(base::TimeDelta::FromMilliseconds(100),
|
| + base::TimeDelta::FromMilliseconds(500));
|
| +
|
| + base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5);
|
| +
|
| + // Arrange to trigger a time update while the demuxer is in the middle of
|
| + // seeking. This update should be ignored by the pipeline and the clock should
|
| + // not get updated.
|
| + base::Closure closure = base::Bind(&RunTimeCB, audio_time_cb_, 300, 700);
|
| + EXPECT_CALL(*demuxer_, Seek(seek_time, _))
|
| + .WillOnce(DoAll(InvokeWithoutArgs(&closure, &base::Closure::Run),
|
| + RunCallback<1>(PIPELINE_OK)));
|
| +
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| + EXPECT_CALL(*audio_renderer_, Flush(_))
|
| + .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
|
| + BUFFERING_HAVE_NOTHING),
|
| + RunClosure<0>()));
|
| + EXPECT_CALL(time_source_, SetMediaTime(seek_time));
|
| + EXPECT_CALL(time_source_, SetPlaybackRate(_));
|
| + EXPECT_CALL(time_source_, StartTicking());
|
| + EXPECT_CALL(*audio_renderer_, StartPlaying())
|
| + .WillOnce(SetBufferingState(&audio_buffering_state_cb_,
|
| + BUFFERING_HAVE_ENOUGH));
|
| + EXPECT_CALL(*audio_renderer_, SetVolume(_));
|
| +
|
| + EXPECT_CALL(callbacks_, OnSeek(PIPELINE_OK));
|
| + EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
|
| + DoSeek(seek_time);
|
| +
|
| + EXPECT_EQ(pipeline_->GetMediaTime(), seek_time);
|
| +
|
| + // Now that the seek is complete, verify that time updates advance the current
|
| + // time.
|
| + base::TimeDelta new_time = seek_time + base::TimeDelta::FromMilliseconds(100);
|
| + audio_time_cb_.Run(new_time, new_time);
|
| +
|
| + EXPECT_EQ(pipeline_->GetMediaTime(), new_time);
|
| +}
|
| +
|
| TEST_F(PipelineTest, DestroyAfterStop) {
|
| CreateAudioStream();
|
| MockDemuxerStreamVector streams;
|
| streams.push_back(audio_stream());
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
| StartPipeline(PIPELINE_OK);
|
|
|
| ExpectDemuxerStop();
|
| @@ -761,24 +929,59 @@
|
| streams.push_back(video_stream());
|
|
|
| SetDemuxerExpectations(&streams);
|
| - SetRendererExpectations();
|
| + SetAudioRendererExpectations(audio_stream());
|
| + SetVideoRendererExpectations(video_stream());
|
| StartPipeline(PIPELINE_OK);
|
|
|
| // Simulate underflow.
|
| - EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| - buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
|
| -
|
| - // Seek while underflowed.
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| + audio_buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
|
| +
|
| + // Seek while underflowed. We shouldn't call StopTicking() again.
|
| base::TimeDelta expected = base::TimeDelta::FromSeconds(5);
|
| ExpectSeek(expected, true);
|
| DoSeek(expected);
|
| +}
|
| +
|
| +static void PostTimeCB(base::MessageLoop* message_loop,
|
| + const AudioRenderer::TimeCB& time_cb) {
|
| + base::TimeDelta new_time = base::TimeDelta::FromMilliseconds(100);
|
| + message_loop->PostTask(FROM_HERE, base::Bind(time_cb, new_time, new_time));
|
| +}
|
| +
|
| +TEST_F(PipelineTest, TimeUpdateAfterStop) {
|
| + CreateAudioStream();
|
| + CreateVideoStream();
|
| + MockDemuxerStreamVector streams;
|
| + streams.push_back(audio_stream());
|
| + streams.push_back(video_stream());
|
| +
|
| + SetDemuxerExpectations(&streams);
|
| + SetAudioRendererExpectations(audio_stream());
|
| + SetVideoRendererExpectations(video_stream());
|
| + StartPipeline(PIPELINE_OK);
|
| +
|
| + // Double post here! This is a hack to simulate the case where TimeCB is
|
| + // posted during ~AudioRenderer(), which is triggered in Pipeline::DoStop.
|
| + // Since we can't EXPECT_CALL the dtor and Pipeline::DoStop() is posted
|
| + // as well, we need to post twice here.
|
| + message_loop_.PostTask(
|
| + FROM_HERE, base::Bind(&PostTimeCB, &message_loop_, audio_time_cb_));
|
| +
|
| + EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
|
| +
|
| + ExpectPipelineStopAndDestroyPipeline();
|
| + pipeline_->Stop(
|
| + base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
|
| + message_loop_.RunUntilIdle();
|
| }
|
|
|
| class PipelineTeardownTest : public PipelineTest {
|
| public:
|
| enum TeardownState {
|
| kInitDemuxer,
|
| - kInitRenderer,
|
| + kInitAudioRenderer,
|
| + kInitVideoRenderer,
|
| kFlushing,
|
| kSeeking,
|
| kPlaying,
|
| @@ -796,7 +999,8 @@
|
| void RunTest(TeardownState state, StopOrError stop_or_error) {
|
| switch (state) {
|
| case kInitDemuxer:
|
| - case kInitRenderer:
|
| + case kInitAudioRenderer:
|
| + case kInitVideoRenderer:
|
| DoInitialize(state, stop_or_error);
|
| break;
|
|
|
| @@ -864,36 +1068,60 @@
|
| streams.push_back(video_stream());
|
| SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000));
|
|
|
| - EXPECT_CALL(*renderer_, HasAudio()).WillRepeatedly(Return(true));
|
| - EXPECT_CALL(*renderer_, HasVideo()).WillRepeatedly(Return(true));
|
| -
|
| - if (state == kInitRenderer) {
|
| + if (state == kInitAudioRenderer) {
|
| if (stop_or_error == kStop) {
|
| - EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
|
| + EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _))
|
| .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
|
| - RunCallback<0>(PIPELINE_OK)));
|
| + RunCallback<1>(PIPELINE_OK)));
|
| ExpectPipelineStopAndDestroyPipeline();
|
| } else {
|
| status = PIPELINE_ERROR_INITIALIZATION_FAILED;
|
| - EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
|
| - .WillOnce(RunCallback<0>(status));
|
| + EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _))
|
| + .WillOnce(RunCallback<1>(status));
|
| }
|
|
|
| EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
|
| return status;
|
| }
|
|
|
| - EXPECT_CALL(*renderer_, Initialize(_, _, _, _, _, _))
|
| - .WillOnce(DoAll(SaveArg<4>(&buffering_state_cb_),
|
| - RunCallback<0>(PIPELINE_OK)));
|
| + EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _))
|
| + .WillOnce(DoAll(SaveArg<4>(&audio_buffering_state_cb_),
|
| + RunCallback<1>(PIPELINE_OK)));
|
| +
|
| + if (state == kInitVideoRenderer) {
|
| + if (stop_or_error == kStop) {
|
| + EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
|
| + .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
|
| + RunCallback<2>(PIPELINE_OK)));
|
| + ExpectPipelineStopAndDestroyPipeline();
|
| + } else {
|
| + status = PIPELINE_ERROR_INITIALIZATION_FAILED;
|
| + EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
|
| + .WillOnce(RunCallback<2>(status));
|
| + }
|
| +
|
| + EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
|
| + return status;
|
| + }
|
| +
|
| + EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
|
| + .WillOnce(DoAll(SaveArg<5>(&video_buffering_state_cb_),
|
| + RunCallback<2>(PIPELINE_OK)));
|
|
|
| EXPECT_CALL(callbacks_, OnMetadata(_));
|
|
|
| // If we get here it's a successful initialization.
|
| - EXPECT_CALL(*renderer_, SetPlaybackRate(0.0f));
|
| - EXPECT_CALL(*renderer_, SetVolume(1.0f));
|
| - EXPECT_CALL(*renderer_, StartPlayingFrom(base::TimeDelta()))
|
| - .WillOnce(SetBufferingState(&buffering_state_cb_,
|
| + EXPECT_CALL(*audio_renderer_, GetTimeSource())
|
| + .WillOnce(Return(&time_source_));
|
| + EXPECT_CALL(time_source_, SetMediaTime(base::TimeDelta()));
|
| + EXPECT_CALL(time_source_, SetPlaybackRate(0.0f));
|
| + EXPECT_CALL(time_source_, StartTicking());
|
| + EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
|
| + EXPECT_CALL(*audio_renderer_, StartPlaying())
|
| + .WillOnce(SetBufferingState(&audio_buffering_state_cb_,
|
| + BUFFERING_HAVE_ENOUGH));
|
| + EXPECT_CALL(*video_renderer_, StartPlaying())
|
| + .WillOnce(SetBufferingState(&video_buffering_state_cb_,
|
| BUFFERING_HAVE_ENOUGH));
|
|
|
| if (status == PIPELINE_OK)
|
| @@ -924,32 +1152,35 @@
|
| base::Closure stop_cb = base::Bind(
|
| &CallbackHelper::OnStop, base::Unretained(&callbacks_));
|
|
|
| + EXPECT_CALL(time_source_, StopTicking());
|
| +
|
| if (state == kFlushing) {
|
| if (stop_or_error == kStop) {
|
| - EXPECT_CALL(*renderer_, Flush(_))
|
| + EXPECT_CALL(*audio_renderer_, Flush(_))
|
| .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
|
| - SetBufferingState(&buffering_state_cb_,
|
| + SetBufferingState(&audio_buffering_state_cb_,
|
| BUFFERING_HAVE_NOTHING),
|
| RunClosure<0>()));
|
| - EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| } else {
|
| status = PIPELINE_ERROR_READ;
|
| - EXPECT_CALL(*renderer_, Flush(_))
|
| - .WillOnce(DoAll(SetError(pipeline_.get(), status),
|
| - SetBufferingState(&buffering_state_cb_,
|
| - BUFFERING_HAVE_NOTHING),
|
| - RunClosure<0>()));
|
| - EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| + EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce(
|
| + DoAll(SetError(pipeline_.get(), status),
|
| + SetBufferingState(&audio_buffering_state_cb_,
|
| + BUFFERING_HAVE_NOTHING),
|
| + RunClosure<0>()));
|
| }
|
|
|
| return status;
|
| }
|
|
|
| - EXPECT_CALL(*renderer_, Flush(_))
|
| - .WillOnce(DoAll(SetBufferingState(&buffering_state_cb_,
|
| + EXPECT_CALL(*audio_renderer_, Flush(_))
|
| + .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
|
| BUFFERING_HAVE_NOTHING),
|
| RunClosure<0>()));
|
| - EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
|
| + EXPECT_CALL(*video_renderer_, Flush(_))
|
| + .WillOnce(DoAll(SetBufferingState(&video_buffering_state_cb_,
|
| + BUFFERING_HAVE_NOTHING),
|
| + RunClosure<0>()));
|
|
|
| if (state == kSeeking) {
|
| if (stop_or_error == kStop) {
|
| @@ -1007,13 +1238,15 @@
|
| }
|
|
|
| INSTANTIATE_TEARDOWN_TEST(Stop, InitDemuxer);
|
| -INSTANTIATE_TEARDOWN_TEST(Stop, InitRenderer);
|
| +INSTANTIATE_TEARDOWN_TEST(Stop, InitAudioRenderer);
|
| +INSTANTIATE_TEARDOWN_TEST(Stop, InitVideoRenderer);
|
| INSTANTIATE_TEARDOWN_TEST(Stop, Flushing);
|
| INSTANTIATE_TEARDOWN_TEST(Stop, Seeking);
|
| INSTANTIATE_TEARDOWN_TEST(Stop, Playing);
|
|
|
| INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer);
|
| -INSTANTIATE_TEARDOWN_TEST(Error, InitRenderer);
|
| +INSTANTIATE_TEARDOWN_TEST(Error, InitAudioRenderer);
|
| +INSTANTIATE_TEARDOWN_TEST(Error, InitVideoRenderer);
|
| INSTANTIATE_TEARDOWN_TEST(Error, Flushing);
|
| INSTANTIATE_TEARDOWN_TEST(Error, Seeking);
|
| INSTANTIATE_TEARDOWN_TEST(Error, Playing);
|
|
|