#include "base/test/simple_test_tick_clock.h"
#include "base/threading/simple_thread.h"
#include "base/time/clock.h"
-#include "media/base/clock.h"
#include "media/base/fake_text_track_stream.h"
#include "media/base/gmock_callback_support.h"
#include "media/base/media_log.h"
#include "media/base/test_helpers.h"
#include "media/base/text_renderer.h"
#include "media/base/text_track_config.h"
+#include "media/base/time_delta_interpolator.h"
#include "testing/gtest/include/gtest/gtest.h"
#include "ui/gfx/size.h"
pipeline->SetErrorForTesting(status);
}
+ACTION_P2(SetBufferingState, cb, buffering_state) {
+ cb->Run(buffering_state);
+}
+
// Used for setting expectations on pipeline callbacks. Using a StrictMock
// also lets us test for missing callbacks.
class CallbackHelper {
MOCK_METHOD0(OnEnded, void());
MOCK_METHOD1(OnError, void(PipelineStatus));
MOCK_METHOD1(OnMetadata, void(PipelineMetadata));
- MOCK_METHOD0(OnPrerollCompleted, void());
+ MOCK_METHOD1(OnBufferingStateChange, void(BufferingState));
MOCK_METHOD0(OnDurationChange, void());
private:
: pipeline_(new Pipeline(message_loop_.message_loop_proxy(),
new MediaLog())),
filter_collection_(new FilterCollection()),
- demuxer_(new MockDemuxer()) {
+ demuxer_(new StrictMock<MockDemuxer>()) {
filter_collection_->SetDemuxer(demuxer_.get());
- video_renderer_ = new MockVideoRenderer();
+ video_renderer_ = new StrictMock<MockVideoRenderer>();
scoped_ptr<VideoRenderer> video_renderer(video_renderer_);
filter_collection_->SetVideoRenderer(video_renderer.Pass());
- audio_renderer_ = new MockAudioRenderer();
+ audio_renderer_ = new StrictMock<MockAudioRenderer>();
scoped_ptr<AudioRenderer> audio_renderer(audio_renderer_);
filter_collection_->SetAudioRenderer(audio_renderer.Pass());
scoped_ptr<TextRenderer> text_renderer(text_renderer_);
filter_collection_->SetTextRenderer(text_renderer.Pass());
- // InitializeDemuxer() adds overriding expectations for expected non-NULL
- // streams.
+ // SetDemuxerExpectations() adds overriding expectations for expected
+ // non-NULL streams.
DemuxerStream* null_pointer = NULL;
EXPECT_CALL(*demuxer_, GetStream(_))
.WillRepeatedly(Return(null_pointer));
- EXPECT_CALL(*demuxer_, GetStartTime())
- .WillRepeatedly(Return(base::TimeDelta()));
-
EXPECT_CALL(*demuxer_, GetTimelineOffset())
.WillRepeatedly(Return(base::Time()));
if (!pipeline_ || !pipeline_->IsRunning())
return;
- ExpectStop();
+ ExpectDemuxerStop();
// The mock demuxer doesn't stop the fake text track stream,
// so just stop it manually.
}
// Expect a stop callback if we were started.
- EXPECT_CALL(callbacks_, OnStop());
+ ExpectPipelineStopAndDestroyPipeline();
pipeline_->Stop(base::Bind(&CallbackHelper::OnStop,
base::Unretained(&callbacks_)));
message_loop_.RunUntilIdle();
}
+ void OnDemuxerError() {
+ // Cast because OnDemuxerError is private in Pipeline.
+ static_cast<DemuxerHost*>(pipeline_.get())
+ ->OnDemuxerError(PIPELINE_ERROR_ABORT);
+ }
+
protected:
// Sets up expectations to allow the demuxer to initialize.
typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector;
- void InitializeDemuxer(MockDemuxerStreamVector* streams,
- const base::TimeDelta& duration) {
+ void SetDemuxerExpectations(MockDemuxerStreamVector* streams,
+ const base::TimeDelta& duration) {
EXPECT_CALL(callbacks_, OnDurationChange());
EXPECT_CALL(*demuxer_, Initialize(_, _, _))
.WillOnce(DoAll(SetDemuxerProperties(duration),
}
}
- void InitializeDemuxer(MockDemuxerStreamVector* streams) {
+ void SetDemuxerExpectations(MockDemuxerStreamVector* streams) {
// Initialize with a default non-zero duration.
- InitializeDemuxer(streams, base::TimeDelta::FromSeconds(10));
+ SetDemuxerExpectations(streams, base::TimeDelta::FromSeconds(10));
}
scoped_ptr<StrictMock<MockDemuxerStream> > CreateStream(
}
// Sets up expectations to allow the video renderer to initialize.
- void InitializeVideoRenderer(DemuxerStream* stream) {
- EXPECT_CALL(*video_renderer_, Initialize(stream, _, _, _, _, _, _, _, _))
- .WillOnce(RunCallback<2>(PIPELINE_OK));
- EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
-
- // Startup sequence.
- EXPECT_CALL(*video_renderer_, Preroll(demuxer_->GetStartTime(), _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*video_renderer_, Play(_))
- .WillOnce(RunClosure<0>());
+ 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 InitializeAudioRenderer(DemuxerStream* stream) {
+ void SetAudioRendererExpectations(DemuxerStream* stream) {
EXPECT_CALL(*audio_renderer_, Initialize(stream, _, _, _, _, _, _))
- .WillOnce(DoAll(SaveArg<4>(&audio_time_cb_),
+ .WillOnce(DoAll(SaveArg<3>(&audio_time_cb_),
+ SaveArg<4>(&audio_buffering_state_cb_),
+ SaveArg<5>(&audio_ended_cb_),
RunCallback<1>(PIPELINE_OK)));
}
TextTrackConfig(kTextSubtitles, "", "", ""));
}
- // Sets up expectations on the callback and initializes the pipeline. Called
+ // Sets up expectations on the callback and initializes the pipeline. Called
// after tests have set expectations any filters they wish to use.
- void InitializePipeline(PipelineStatus start_status) {
+ void StartPipeline(PipelineStatus start_status) {
EXPECT_CALL(callbacks_, OnStart(start_status));
if (start_status == PIPELINE_OK) {
EXPECT_CALL(callbacks_, OnMetadata(_)).WillOnce(SaveArg<0>(&metadata_));
if (audio_stream_) {
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f));
+ 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));
+ }
- // Startup sequence.
- EXPECT_CALL(*audio_renderer_, Preroll(base::TimeDelta(), _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*audio_renderer_, StartRendering());
+ if (video_stream_) {
+ EXPECT_CALL(*video_renderer_, StartPlaying())
+ .WillOnce(SetBufferingState(&video_buffering_state_cb_,
+ BUFFERING_HAVE_ENOUGH));
}
- EXPECT_CALL(callbacks_, OnPrerollCompleted());
+
+ EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
}
pipeline_->Start(
base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnMetadata, base::Unretained(&callbacks_)),
- base::Bind(&CallbackHelper::OnPrerollCompleted,
+ base::Bind(&CallbackHelper::OnBufferingStateChange,
base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnDurationChange,
base::Unretained(&callbacks_)));
return text_stream_.get();
}
- void ExpectSeek(const base::TimeDelta& seek_time) {
+ 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));
if (audio_stream_) {
- EXPECT_CALL(*audio_renderer_, StopRendering());
+ if (!underflowed)
+ EXPECT_CALL(time_source_, StopTicking());
EXPECT_CALL(*audio_renderer_, Flush(_))
- .WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Preroll(seek_time, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(_));
+ .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(*audio_renderer_, StartRendering());
}
if (video_stream_) {
EXPECT_CALL(*video_renderer_, Flush(_))
- .WillOnce(RunClosure<0>());
- EXPECT_CALL(*video_renderer_, Preroll(seek_time, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*video_renderer_, SetPlaybackRate(_));
- EXPECT_CALL(*video_renderer_, Play(_))
- .WillOnce(RunClosure<0>());
+ .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));
}
- EXPECT_CALL(callbacks_, OnPrerollCompleted());
-
- // We expect a successful seek callback.
+ // We expect a successful seek callback followed by a buffering update.
EXPECT_CALL(callbacks_, OnSeek(PIPELINE_OK));
+ EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
}
void DoSeek(const base::TimeDelta& seek_time) {
EXPECT_EQ(seek_time, pipeline_->GetMediaTime());
}
- void ExpectStop() {
+ void DestroyPipeline() {
+ // In real code Pipeline could be destroyed on a different thread. All weak
+ // pointers must have been invalidated before the stop callback returns.
+ DCHECK(!pipeline_->HasWeakPtrsForTesting());
+ pipeline_.reset();
+ }
+
+ void ExpectDemuxerStop() {
if (demuxer_)
EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
+ }
- if (audio_stream_)
- EXPECT_CALL(*audio_renderer_, Stop(_)).WillOnce(RunClosure<0>());
-
- if (video_stream_)
- EXPECT_CALL(*video_renderer_, Stop(_)).WillOnce(RunClosure<0>());
+ void ExpectPipelineStopAndDestroyPipeline() {
+ // After the Pipeline is stopped, it could be destroyed any time. Always
+ // destroy the pipeline immediately after OnStop() to test this.
+ EXPECT_CALL(callbacks_, OnStop())
+ .WillOnce(Invoke(this, &PipelineTest::DestroyPipeline));
}
MOCK_METHOD2(OnAddTextTrack, void(const TextTrackConfig&,
scoped_ptr<Pipeline> pipeline_;
scoped_ptr<FilterCollection> filter_collection_;
- scoped_ptr<MockDemuxer> demuxer_;
- MockVideoRenderer* video_renderer_;
- MockAudioRenderer* audio_renderer_;
+ scoped_ptr<StrictMock<MockDemuxer> > demuxer_;
+ 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_;
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_;
base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnMetadata, base::Unretained(&callbacks_)),
- base::Bind(&CallbackHelper::OnPrerollCompleted,
+ base::Bind(&CallbackHelper::OnBufferingStateChange,
base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnDurationChange,
base::Unretained(&callbacks_)));
message_loop_.RunUntilIdle();
-
// Because our callback will get executed when the test tears down, we'll
// verify that nothing has been called, then set our expectation for the call
// made during tear down.
EXPECT_CALL(callbacks_, OnStart(PIPELINE_OK));
}
+TEST_F(PipelineTest, StopWithoutStart) {
+ ExpectPipelineStopAndDestroyPipeline();
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
+ message_loop_.RunUntilIdle();
+}
+
+TEST_F(PipelineTest, StartThenStopImmediately) {
+ EXPECT_CALL(*demuxer_, Initialize(_, _, _))
+ .WillOnce(RunCallback<1>(PIPELINE_OK));
+ EXPECT_CALL(*demuxer_, Stop(_))
+ .WillOnce(RunClosure<0>());
+
+ EXPECT_CALL(callbacks_, OnStart(_));
+
+ pipeline_->Start(
+ filter_collection_.Pass(),
+ base::Bind(&CallbackHelper::OnEnded, base::Unretained(&callbacks_)),
+ base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)),
+ base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)),
+ base::Bind(&CallbackHelper::OnMetadata, base::Unretained(&callbacks_)),
+ base::Bind(&CallbackHelper::OnBufferingStateChange,
+ base::Unretained(&callbacks_)),
+ base::Bind(&CallbackHelper::OnDurationChange,
+ base::Unretained(&callbacks_)));
+
+ // Expect a stop callback if we were started.
+ ExpectPipelineStopAndDestroyPipeline();
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
+ message_loop_.RunUntilIdle();
+}
+
+TEST_F(PipelineTest, DemuxerErrorDuringStop) {
+ CreateAudioStream();
+ MockDemuxerStreamVector streams;
+ streams.push_back(audio_stream());
+
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+
+ StartPipeline(PIPELINE_OK);
+
+ EXPECT_CALL(*demuxer_, Stop(_))
+ .WillOnce(DoAll(InvokeWithoutArgs(this, &PipelineTest::OnDemuxerError),
+ RunClosure<0>()));
+ ExpectPipelineStopAndDestroyPipeline();
+
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
+ message_loop_.RunUntilIdle();
+}
+
TEST_F(PipelineTest, URLNotFound) {
EXPECT_CALL(*demuxer_, Initialize(_, _, _))
.WillOnce(RunCallback<1>(PIPELINE_ERROR_URL_NOT_FOUND));
EXPECT_CALL(*demuxer_, Stop(_))
.WillOnce(RunClosure<0>());
- InitializePipeline(PIPELINE_ERROR_URL_NOT_FOUND);
+ StartPipeline(PIPELINE_ERROR_URL_NOT_FOUND);
}
TEST_F(PipelineTest, NoStreams) {
EXPECT_CALL(*demuxer_, Stop(_))
.WillOnce(RunClosure<0>());
- InitializePipeline(PIPELINE_ERROR_COULD_NOT_RENDER);
+ StartPipeline(PIPELINE_ERROR_COULD_NOT_RENDER);
}
TEST_F(PipelineTest, AudioStream) {
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
EXPECT_TRUE(metadata_.has_audio);
EXPECT_FALSE(metadata_.has_video);
}
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
- InitializeDemuxer(&streams);
- InitializeVideoRenderer(video_stream());
+ SetDemuxerExpectations(&streams);
+ SetVideoRendererExpectations(video_stream());
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
EXPECT_FALSE(metadata_.has_audio);
EXPECT_TRUE(metadata_.has_video);
}
streams.push_back(audio_stream());
streams.push_back(video_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
- InitializeVideoRenderer(video_stream());
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+ SetVideoRendererExpectations(video_stream());
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
EXPECT_TRUE(metadata_.has_audio);
EXPECT_TRUE(metadata_.has_video);
}
MockDemuxerStreamVector streams;
streams.push_back(video_stream());
- InitializeDemuxer(&streams);
- InitializeVideoRenderer(video_stream());
+ SetDemuxerExpectations(&streams);
+ SetVideoRendererExpectations(video_stream());
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
EXPECT_FALSE(metadata_.has_audio);
EXPECT_TRUE(metadata_.has_video);
streams.push_back(video_stream());
streams.push_back(audio_stream());
- InitializeDemuxer(&streams);
- InitializeVideoRenderer(video_stream());
- InitializeAudioRenderer(audio_stream());
+ SetDemuxerExpectations(&streams);
+ SetVideoRendererExpectations(video_stream());
+ SetAudioRendererExpectations(audio_stream());
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
EXPECT_TRUE(metadata_.has_audio);
EXPECT_TRUE(metadata_.has_video);
streams.push_back(audio_stream());
streams.push_back(video_stream());
- InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000));
- InitializeAudioRenderer(audio_stream());
- InitializeVideoRenderer(video_stream());
+ SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000));
+ SetAudioRendererExpectations(audio_stream());
+ SetVideoRendererExpectations(video_stream());
// Initialize then seek!
- InitializePipeline(PIPELINE_OK);
-
- AddTextStream();
- message_loop_.RunUntilIdle();
+ StartPipeline(PIPELINE_OK);
// Every filter should receive a call to Seek().
base::TimeDelta expected = base::TimeDelta::FromSeconds(2000);
- ExpectSeek(expected);
+ ExpectSeek(expected, false);
DoSeek(expected);
}
+TEST_F(PipelineTest, SeekAfterError) {
+ CreateAudioStream();
+ MockDemuxerStreamVector streams;
+ streams.push_back(audio_stream());
+
+ SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000));
+ SetAudioRendererExpectations(audio_stream());
+
+ // Initialize then seek!
+ StartPipeline(PIPELINE_OK);
+
+ EXPECT_CALL(*demuxer_, Stop(_))
+ .WillOnce(RunClosure<0>());
+ EXPECT_CALL(callbacks_, OnError(_));
+
+ static_cast<DemuxerHost*>(pipeline_.get())
+ ->OnDemuxerError(PIPELINE_ERROR_ABORT);
+ message_loop_.RunUntilIdle();
+
+ pipeline_->Seek(
+ base::TimeDelta::FromMilliseconds(100),
+ base::Bind(&CallbackHelper::OnSeek, base::Unretained(&callbacks_)));
+ message_loop_.RunUntilIdle();
+}
+
TEST_F(PipelineTest, SetVolume) {
CreateAudioStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
// The audio renderer should receive a call to SetVolume().
float expected = 0.5f;
EXPECT_CALL(*audio_renderer_, SetVolume(expected));
// Initialize then set volume!
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
pipeline_->SetVolume(expected);
}
streams.push_back(video_stream());
const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
- InitializeDemuxer(&streams, kDuration);
- InitializeVideoRenderer(video_stream());
+ SetDemuxerExpectations(&streams, kDuration);
+ SetVideoRendererExpectations(video_stream());
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
EXPECT_EQ(kDuration.ToInternalValue(),
pipeline_->GetMediaDuration().ToInternalValue());
EXPECT_FALSE(pipeline_->DidLoadingProgress());
streams.push_back(video_stream());
const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
- InitializeDemuxer(&streams, kDuration);
- InitializeVideoRenderer(video_stream());
+ SetDemuxerExpectations(&streams, kDuration);
+ SetVideoRendererExpectations(video_stream());
- InitializePipeline(PIPELINE_OK);
+ StartPipeline(PIPELINE_OK);
EXPECT_EQ(0u, pipeline_->GetBufferedTimeRanges().size());
EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0));
base::TimeDelta kSeekTime = kDuration / 2;
- ExpectSeek(kSeekTime);
+ ExpectSeek(kSeekTime, false);
DoSeek(kSeekTime);
EXPECT_FALSE(pipeline_->DidLoadingProgress());
streams.push_back(audio_stream());
streams.push_back(video_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
- InitializeVideoRenderer(video_stream());
- InitializePipeline(PIPELINE_OK);
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+ SetVideoRendererExpectations(video_stream());
+ StartPipeline(PIPELINE_OK);
AddTextStream();
// The ended callback shouldn't run until all renderers have ended.
- pipeline_->OnAudioRendererEnded();
+ audio_ended_cb_.Run();
message_loop_.RunUntilIdle();
- pipeline_->OnVideoRendererEnded();
+ video_ended_cb_.Run();
message_loop_.RunUntilIdle();
- EXPECT_CALL(*audio_renderer_, StopRendering());
+ EXPECT_CALL(time_source_, StopTicking());
EXPECT_CALL(callbacks_, OnEnded());
text_stream()->SendEosNotification();
message_loop_.RunUntilIdle();
streams.push_back(audio_stream());
streams.push_back(video_stream());
- // Replace the clock so we can simulate wall clock time advancing w/o using
- // Sleep().
- pipeline_->SetClockForTesting(new Clock(&test_tick_clock_));
+ // Replace what's used for interpolating to simulate wall clock time.
+ pipeline_->SetTimeDeltaInterpolatorForTesting(
+ new TimeDeltaInterpolator(&test_tick_clock_));
- InitializeDemuxer(&streams, duration);
- InitializeAudioRenderer(audio_stream());
- InitializeVideoRenderer(video_stream());
- InitializePipeline(PIPELINE_OK);
+ 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(*video_renderer_, SetPlaybackRate(playback_rate));
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate));
+ EXPECT_CALL(time_source_, SetPlaybackRate(playback_rate));
pipeline_->SetPlaybackRate(playback_rate);
message_loop_.RunUntilIdle();
EXPECT_EQ(pipeline_->GetMediaTime().ToInternalValue(), start_time);
// Signal end of audio stream.
- pipeline_->OnAudioRendererEnded();
+ audio_ended_cb_.Run();
message_loop_.RunUntilIdle();
// Verify that the clock advances.
EXPECT_GT(pipeline_->GetMediaTime().ToInternalValue(), start_time);
// Signal end of video stream and make sure OnEnded() callback occurs.
- EXPECT_CALL(*audio_renderer_, StopRendering());
+ EXPECT_CALL(time_source_, StopTicking());
EXPECT_CALL(callbacks_, OnEnded());
- pipeline_->OnVideoRendererEnded();
+ video_ended_cb_.Run();
}
TEST_F(PipelineTest, ErrorDuringSeek) {
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
- InitializePipeline(PIPELINE_OK);
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+ StartPipeline(PIPELINE_OK);
float playback_rate = 1.0f;
- EXPECT_CALL(*audio_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);
// Preroll() isn't called as the demuxer errors out first.
- EXPECT_CALL(*audio_renderer_, StopRendering());
+ EXPECT_CALL(time_source_, StopTicking());
EXPECT_CALL(*audio_renderer_, Flush(_))
- .WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Stop(_))
- .WillOnce(RunClosure<0>());
+ .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
+ BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(*demuxer_, Seek(seek_time, _))
.WillOnce(RunCallback<1>(PIPELINE_ERROR_READ));
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
- InitializePipeline(PIPELINE_OK);
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+ StartPipeline(PIPELINE_OK);
// Trigger additional requests on the pipeline during tear down from error.
base::Callback<void(PipelineStatus)> cb = base::Bind(
base::TimeDelta seek_time = base::TimeDelta::FromSeconds(5);
// Seek() isn't called as the demuxer errors out first.
- EXPECT_CALL(*audio_renderer_, StopRendering());
+ EXPECT_CALL(time_source_, StopTicking());
EXPECT_CALL(*audio_renderer_, Flush(_))
- .WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Stop(_))
- .WillOnce(RunClosure<0>());
+ .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
+ BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
EXPECT_CALL(*demuxer_, Seek(seek_time, _))
.WillOnce(RunCallback<1>(PIPELINE_ERROR_READ));
message_loop_.RunUntilIdle();
}
-TEST_F(PipelineTest, StartTimeIsZero) {
- CreateVideoStream();
- MockDemuxerStreamVector streams;
- streams.push_back(video_stream());
-
- const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
- InitializeDemuxer(&streams, kDuration);
- InitializeVideoRenderer(video_stream());
-
- InitializePipeline(PIPELINE_OK);
- EXPECT_FALSE(metadata_.has_audio);
- EXPECT_TRUE(metadata_.has_video);
-
- EXPECT_EQ(base::TimeDelta(), pipeline_->GetMediaTime());
-}
-
-TEST_F(PipelineTest, StartTimeIsNonZero) {
- const base::TimeDelta kStartTime = base::TimeDelta::FromSeconds(4);
- const base::TimeDelta kDuration = base::TimeDelta::FromSeconds(100);
-
- EXPECT_CALL(*demuxer_, GetStartTime())
- .WillRepeatedly(Return(kStartTime));
-
- CreateVideoStream();
- MockDemuxerStreamVector streams;
- streams.push_back(video_stream());
-
- InitializeDemuxer(&streams, kDuration);
- InitializeVideoRenderer(video_stream());
-
- InitializePipeline(PIPELINE_OK);
- EXPECT_FALSE(metadata_.has_audio);
- EXPECT_TRUE(metadata_.has_video);
-
- EXPECT_EQ(kStartTime, pipeline_->GetMediaTime());
-}
-
static void RunTimeCB(const AudioRenderer::TimeCB& time_cb,
int time_in_ms,
int max_time_in_ms) {
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
- InitializePipeline(PIPELINE_OK);
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+ StartPipeline(PIPELINE_OK);
float playback_rate = 1.0f;
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(playback_rate));
+ EXPECT_CALL(time_source_, SetPlaybackRate(playback_rate));
pipeline_->SetPlaybackRate(playback_rate);
message_loop_.RunUntilIdle();
.WillOnce(DoAll(InvokeWithoutArgs(&closure, &base::Closure::Run),
RunCallback<1>(PIPELINE_OK)));
- EXPECT_CALL(*audio_renderer_, StopRendering());
+ EXPECT_CALL(time_source_, StopTicking());
EXPECT_CALL(*audio_renderer_, Flush(_))
- .WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Preroll(seek_time, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(_));
+ .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(*audio_renderer_, StartRendering());
- EXPECT_CALL(callbacks_, OnPrerollCompleted());
EXPECT_CALL(callbacks_, OnSeek(PIPELINE_OK));
+ EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
DoSeek(seek_time);
EXPECT_EQ(pipeline_->GetMediaTime(), seek_time);
EXPECT_EQ(pipeline_->GetMediaTime(), new_time);
}
-static void DeletePipeline(scoped_ptr<Pipeline> pipeline) {
- // |pipeline| will go out of scope.
+TEST_F(PipelineTest, DestroyAfterStop) {
+ CreateAudioStream();
+ MockDemuxerStreamVector streams;
+ streams.push_back(audio_stream());
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+ StartPipeline(PIPELINE_OK);
+
+ ExpectDemuxerStop();
+
+ ExpectPipelineStopAndDestroyPipeline();
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
+ message_loop_.RunUntilIdle();
+}
+
+TEST_F(PipelineTest, Underflow) {
+ 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);
+
+ // Simulate underflow.
+ 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, DeleteAfterStop) {
+TEST_F(PipelineTest, TimeUpdateAfterStop) {
CreateAudioStream();
+ CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
- InitializeDemuxer(&streams);
- InitializeAudioRenderer(audio_stream());
- InitializePipeline(PIPELINE_OK);
+ streams.push_back(video_stream());
+
+ SetDemuxerExpectations(&streams);
+ SetAudioRendererExpectations(audio_stream());
+ SetVideoRendererExpectations(video_stream());
+ StartPipeline(PIPELINE_OK);
- ExpectStop();
+ // 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_));
- Pipeline* pipeline = pipeline_.get();
- pipeline->Stop(base::Bind(&DeletePipeline, base::Passed(&pipeline_)));
+ EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
+
+ ExpectPipelineStopAndDestroyPipeline();
+ pipeline_->Stop(
+ base::Bind(&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
message_loop_.RunUntilIdle();
}
kInitVideoRenderer,
kFlushing,
kSeeking,
- kPrerolling,
kPlaying,
};
case kFlushing:
case kSeeking:
- case kPrerolling:
DoInitialize(state, stop_or_error);
DoSeek(state, stop_or_error);
break;
base::Bind(&CallbackHelper::OnError, base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnStart, base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnMetadata, base::Unretained(&callbacks_)),
- base::Bind(&CallbackHelper::OnPrerollCompleted,
+ base::Bind(&CallbackHelper::OnBufferingStateChange,
base::Unretained(&callbacks_)),
base::Bind(&CallbackHelper::OnDurationChange,
base::Unretained(&callbacks_)));
EXPECT_CALL(*demuxer_, Initialize(_, _, _))
.WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
RunCallback<1>(PIPELINE_OK)));
- EXPECT_CALL(callbacks_, OnStop());
+ ExpectPipelineStopAndDestroyPipeline();
} else {
status = DEMUXER_ERROR_COULD_NOT_OPEN;
EXPECT_CALL(*demuxer_, Initialize(_, _, _))
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
streams.push_back(video_stream());
- InitializeDemuxer(&streams, base::TimeDelta::FromSeconds(3000));
+ SetDemuxerExpectations(&streams, base::TimeDelta::FromSeconds(3000));
if (state == kInitAudioRenderer) {
if (stop_or_error == kStop) {
EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _))
.WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
RunCallback<1>(PIPELINE_OK)));
- EXPECT_CALL(callbacks_, OnStop());
+ ExpectPipelineStopAndDestroyPipeline();
} else {
status = PIPELINE_ERROR_INITIALIZATION_FAILED;
EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _))
}
EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Stop(_)).WillOnce(RunClosure<0>());
return status;
}
EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
+ .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(_, _, _, _, _, _, _, _, _))
+ EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
.WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
RunCallback<2>(PIPELINE_OK)));
- EXPECT_CALL(callbacks_, OnStop());
+ ExpectPipelineStopAndDestroyPipeline();
} else {
status = PIPELINE_ERROR_INITIALIZATION_FAILED;
- EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _))
+ EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _, _))
.WillOnce(RunCallback<2>(status));
}
EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Stop(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*video_renderer_, Stop(_)).WillOnce(RunClosure<0>());
return status;
}
- EXPECT_CALL(*video_renderer_, Initialize(_, _, _, _, _, _, _, _, _))
- .WillOnce(RunCallback<2>(PIPELINE_OK));
+ 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(*audio_renderer_, Preroll(base::TimeDelta(), _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*video_renderer_, Preroll(base::TimeDelta(), _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
-
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f));
- EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
+ 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_, StartRendering());
- EXPECT_CALL(*video_renderer_, Play(_))
- .WillOnce(RunClosure<0>());
+ 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)
- EXPECT_CALL(callbacks_, OnPrerollCompleted());
+ EXPECT_CALL(callbacks_, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
return status;
}
PipelineStatus status = SetSeekExpectations(state, stop_or_error);
EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Stop(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*video_renderer_, Stop(_)).WillOnce(RunClosure<0>());
EXPECT_CALL(callbacks_, OnSeek(status));
if (status == PIPELINE_OK) {
- EXPECT_CALL(callbacks_, OnStop());
+ ExpectPipelineStopAndDestroyPipeline();
}
pipeline_->Seek(base::TimeDelta::FromSeconds(10), base::Bind(
base::Closure stop_cb = base::Bind(
&CallbackHelper::OnStop, base::Unretained(&callbacks_));
- EXPECT_CALL(*audio_renderer_, StopRendering());
+ EXPECT_CALL(time_source_, StopTicking());
if (state == kFlushing) {
if (stop_or_error == kStop) {
EXPECT_CALL(*audio_renderer_, Flush(_))
- .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), RunClosure<0>()));
+ .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
+ SetBufferingState(&audio_buffering_state_cb_,
+ BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
} else {
status = PIPELINE_ERROR_READ;
EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce(
- DoAll(SetError(pipeline_.get(), status), RunClosure<0>()));
+ DoAll(SetError(pipeline_.get(), status),
+ SetBufferingState(&audio_buffering_state_cb_,
+ BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
}
return status;
}
- EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*video_renderer_, Flush(_)).WillOnce(RunClosure<0>());
+ EXPECT_CALL(*audio_renderer_, Flush(_))
+ .WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
+ BUFFERING_HAVE_NOTHING),
+ RunClosure<0>()));
+ 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) {
return status;
}
- EXPECT_CALL(*demuxer_, Seek(_, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
-
- if (state == kPrerolling) {
- if (stop_or_error == kStop) {
- EXPECT_CALL(*audio_renderer_, Preroll(_, _))
- .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
- RunCallback<1>(PIPELINE_OK)));
- } else {
- status = PIPELINE_ERROR_READ;
- EXPECT_CALL(*audio_renderer_, Preroll(_, _))
- .WillOnce(RunCallback<1>(status));
- }
-
- return status;
- }
-
- EXPECT_CALL(*audio_renderer_, Preroll(_, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
- EXPECT_CALL(*video_renderer_, Preroll(_, _))
- .WillOnce(RunCallback<1>(PIPELINE_OK));
-
- // Playback rate and volume are updated prior to starting.
- EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f));
- EXPECT_CALL(*video_renderer_, SetPlaybackRate(0.0f));
- EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
-
NOTREACHED() << "State not supported: " << state;
return status;
}
InSequence s;
EXPECT_CALL(*demuxer_, Stop(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*audio_renderer_, Stop(_)).WillOnce(RunClosure<0>());
- EXPECT_CALL(*video_renderer_, Stop(_)).WillOnce(RunClosure<0>());
switch (stop_or_error) {
case kStop:
- EXPECT_CALL(callbacks_, OnStop());
+ ExpectPipelineStopAndDestroyPipeline();
pipeline_->Stop(base::Bind(
&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
break;
break;
case kErrorAndStop:
- EXPECT_CALL(callbacks_, OnStop());
+ EXPECT_CALL(callbacks_, OnError(PIPELINE_ERROR_READ));
+ ExpectPipelineStopAndDestroyPipeline();
pipeline_->SetErrorForTesting(PIPELINE_ERROR_READ);
pipeline_->Stop(base::Bind(
&CallbackHelper::OnStop, base::Unretained(&callbacks_)));
INSTANTIATE_TEARDOWN_TEST(Stop, InitVideoRenderer);
INSTANTIATE_TEARDOWN_TEST(Stop, Flushing);
INSTANTIATE_TEARDOWN_TEST(Stop, Seeking);
-INSTANTIATE_TEARDOWN_TEST(Stop, Prerolling);
INSTANTIATE_TEARDOWN_TEST(Stop, Playing);
INSTANTIATE_TEARDOWN_TEST(Error, InitDemuxer);
INSTANTIATE_TEARDOWN_TEST(Error, InitVideoRenderer);
INSTANTIATE_TEARDOWN_TEST(Error, Flushing);
INSTANTIATE_TEARDOWN_TEST(Error, Seeking);
-INSTANTIATE_TEARDOWN_TEST(Error, Prerolling);
INSTANTIATE_TEARDOWN_TEST(Error, Playing);
INSTANTIATE_TEARDOWN_TEST(ErrorAndStop, Playing);