Commit 4264e566 authored by alph@chromium.org's avatar alph@chromium.org

Revert of Update AudioRenderer API to fire changes in BufferingState....

Revert of Update AudioRenderer API to fire changes in BufferingState. (https://codereview.chromium.org/284763002/)

Reason for revert:
Broke blink tests:
mediasource-redundant-seek.html
mediasource-seek-during-pending-seek.html

http://build.chromium.org/p/chromium.webkit/builders/WebKit%20Mac10.6/builds/28147

Original issue's description:
> Update AudioRenderer API to fire changes in BufferingState.
> 
> As a result, Pipeline now handles prerolling and underflow/rebuffering
> by listening for BUFFERING_HAVE_NOTHING/ENOUGH callbacks. Preroll() is
> renamed StartPlayingFrom() and no longer accepts a completion callback.
> In this new model, AudioRenderers immediately enter and remain in
> the "playing" state and fire buffering state callbacks to let Pipeline
> know when to start/stop the clock.
> 
> BUG=144683
> 
> Committed: https://src.chromium.org/viewvc/chrome?view=rev&revision=272465

TBR=acolwell@chromium.org,scherkus@chromium.org
NOTREECHECKS=true
NOTRY=true
BUG=144683

Review URL: https://codereview.chromium.org/291093013

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272501 0039d316-1c4b-4281-b951-d872f2087c98
parent d6417554
...@@ -8,7 +8,6 @@ ...@@ -8,7 +8,6 @@
#include "base/callback.h" #include "base/callback.h"
#include "base/memory/ref_counted.h" #include "base/memory/ref_counted.h"
#include "base/time/time.h" #include "base/time/time.h"
#include "media/base/buffering_state.h"
#include "media/base/media_export.h" #include "media/base/media_export.h"
#include "media/base/pipeline_status.h" #include "media/base/pipeline_status.h"
...@@ -22,9 +21,6 @@ class MEDIA_EXPORT AudioRenderer { ...@@ -22,9 +21,6 @@ class MEDIA_EXPORT AudioRenderer {
// Second parameter is the maximum time value that the clock cannot exceed. // Second parameter is the maximum time value that the clock cannot exceed.
typedef base::Callback<void(base::TimeDelta, base::TimeDelta)> TimeCB; typedef base::Callback<void(base::TimeDelta, base::TimeDelta)> TimeCB;
// Used to indicate changes in the buffering state of this audio renderer.
typedef base::Callback<void(BufferingState)> BufferingStateCB;
AudioRenderer(); AudioRenderer();
virtual ~AudioRenderer(); virtual ~AudioRenderer();
...@@ -33,10 +29,12 @@ class MEDIA_EXPORT AudioRenderer { ...@@ -33,10 +29,12 @@ class MEDIA_EXPORT AudioRenderer {
// //
// |statistics_cb| is executed periodically with audio rendering stats. // |statistics_cb| is executed periodically with audio rendering stats.
// //
// |time_cb| is executed whenever time has advanced by way of audio rendering. // |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
// condition.
// //
// |buffering_state_cb| is executed when audio rendering has either ran out of // |time_cb| is executed whenever time has advanced by way of audio rendering.
// data or has enough data to continue playback.
// //
// |ended_cb| is executed when audio rendering has reached the end of stream. // |ended_cb| is executed when audio rendering has reached the end of stream.
// //
...@@ -44,8 +42,8 @@ class MEDIA_EXPORT AudioRenderer { ...@@ -44,8 +42,8 @@ class MEDIA_EXPORT AudioRenderer {
virtual void Initialize(DemuxerStream* stream, virtual void Initialize(DemuxerStream* stream,
const PipelineStatusCB& init_cb, const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb, const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb, const TimeCB& time_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb, const base::Closure& ended_cb,
const PipelineStatusCB& error_cb) = 0; const PipelineStatusCB& error_cb) = 0;
...@@ -58,16 +56,14 @@ class MEDIA_EXPORT AudioRenderer { ...@@ -58,16 +56,14 @@ class MEDIA_EXPORT AudioRenderer {
virtual void StopRendering() = 0; virtual void StopRendering() = 0;
// Discard any audio data, executing |callback| when completed. // Discard any audio data, executing |callback| when completed.
//
// Clients should expect |buffering_state_cb| to be called with
// BUFFERING_HAVE_NOTHING while flushing is in progress.
virtual void Flush(const base::Closure& callback) = 0; virtual void Flush(const base::Closure& callback) = 0;
// Starts playback by reading from |stream| and decoding and rendering audio. // Start prerolling audio data for samples starting at |time|, executing
// |timestamp| is the media timestamp playback should start rendering from. // |callback| when completed.
// //
// Only valid to call after a successful Initialize() or Flush(). // Only valid to call after a successful Initialize() or Flush().
virtual void StartPlayingFrom(base::TimeDelta timestamp) = 0; virtual void Preroll(base::TimeDelta time,
const PipelineStatusCB& callback) = 0;
// Stop all operations in preparation for being deleted, executing |callback| // Stop all operations in preparation for being deleted, executing |callback|
// when complete. // when complete.
...@@ -79,6 +75,9 @@ class MEDIA_EXPORT AudioRenderer { ...@@ -79,6 +75,9 @@ class MEDIA_EXPORT AudioRenderer {
// Sets the output volume. // Sets the output volume.
virtual void SetVolume(float volume) = 0; virtual void SetVolume(float volume) = 0;
// Resumes playback after underflow occurs.
virtual void ResumeAfterUnderflow() = 0;
private: private:
DISALLOW_COPY_AND_ASSIGN(AudioRenderer); DISALLOW_COPY_AND_ASSIGN(AudioRenderer);
}; };
......
...@@ -141,8 +141,8 @@ class MockAudioRenderer : public AudioRenderer { ...@@ -141,8 +141,8 @@ class MockAudioRenderer : public AudioRenderer {
MOCK_METHOD7(Initialize, void(DemuxerStream* stream, MOCK_METHOD7(Initialize, void(DemuxerStream* stream,
const PipelineStatusCB& init_cb, const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb, const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb, const TimeCB& time_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb, const base::Closure& ended_cb,
const PipelineStatusCB& error_cb)); const PipelineStatusCB& error_cb));
MOCK_METHOD0(StartRendering, void()); MOCK_METHOD0(StartRendering, void());
...@@ -150,7 +150,7 @@ class MockAudioRenderer : public AudioRenderer { ...@@ -150,7 +150,7 @@ class MockAudioRenderer : public AudioRenderer {
MOCK_METHOD1(Flush, void(const base::Closure& callback)); MOCK_METHOD1(Flush, void(const base::Closure& callback));
MOCK_METHOD1(Stop, void(const base::Closure& callback)); MOCK_METHOD1(Stop, void(const base::Closure& callback));
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate)); MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta time)); MOCK_METHOD2(Preroll, void(base::TimeDelta time, const PipelineStatusCB& cb));
MOCK_METHOD1(SetVolume, void(float volume)); MOCK_METHOD1(SetVolume, void(float volume));
MOCK_METHOD0(ResumeAfterUnderflow, void()); MOCK_METHOD0(ResumeAfterUnderflow, void());
......
...@@ -414,10 +414,6 @@ void Pipeline::StateTransitionTask(PipelineStatus status) { ...@@ -414,10 +414,6 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
PlaybackRateChangedTask(GetPlaybackRate()); PlaybackRateChangedTask(GetPlaybackRate());
VolumeChangedTask(GetVolume()); VolumeChangedTask(GetVolume());
// Handle renderers that immediately signal they have enough data.
if (!WaitingForEnoughData())
StartPlayback();
// We enter this state from either kInitPrerolling or kSeeking. As of now // We enter this state from either kInitPrerolling or kSeeking. As of now
// both those states call Preroll(), which means by time we enter this // both those states call Preroll(), which means by time we enter this
// state we've already buffered enough data. Forcefully update the // state we've already buffered enough data. Forcefully update the
...@@ -426,10 +422,11 @@ void Pipeline::StateTransitionTask(PipelineStatus status) { ...@@ -426,10 +422,11 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
// //
// TODO(scherkus): Remove after renderers are taught to fire buffering // TODO(scherkus): Remove after renderers are taught to fire buffering
// state callbacks http://crbug.com/144683 // state callbacks http://crbug.com/144683
if (video_renderer_) { DCHECK(WaitingForEnoughData());
DCHECK(WaitingForEnoughData()); if (audio_renderer_)
BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH);
if (video_renderer_)
BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH); BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH);
}
return; return;
case kStopping: case kStopping:
...@@ -456,9 +453,9 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { ...@@ -456,9 +453,9 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
// Preroll renderers. // Preroll renderers.
if (audio_renderer_) { if (audio_renderer_) {
bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom, bound_fns.Push(base::Bind(
base::Unretained(audio_renderer_.get()), &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
seek_timestamp)); seek_timestamp));
} }
if (video_renderer_) { if (video_renderer_) {
...@@ -480,14 +477,6 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) { ...@@ -480,14 +477,6 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
} }
#if DCHECK_IS_ON
static void VerifyBufferingStates(BufferingState* audio_buffering_state,
BufferingState* video_buffering_state) {
DCHECK_EQ(*audio_buffering_state, BUFFERING_HAVE_NOTHING);
DCHECK_EQ(*video_buffering_state, BUFFERING_HAVE_NOTHING);
}
#endif
void Pipeline::DoSeek( void Pipeline::DoSeek(
base::TimeDelta seek_timestamp, base::TimeDelta seek_timestamp,
const PipelineStatusCB& done_cb) { const PipelineStatusCB& done_cb) {
...@@ -505,8 +494,14 @@ void Pipeline::DoSeek( ...@@ -505,8 +494,14 @@ void Pipeline::DoSeek(
if (audio_renderer_) { if (audio_renderer_) {
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
&AudioRenderer::Flush, base::Unretained(audio_renderer_.get()))); &AudioRenderer::Flush, base::Unretained(audio_renderer_.get())));
}
// TODO(scherkus): Remove after AudioRenderer is taught to fire buffering
// state callbacks http://crbug.com/144683
bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
base::Unretained(this),
&audio_buffering_state_,
BUFFERING_HAVE_NOTHING));
}
if (video_renderer_) { if (video_renderer_) {
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
&VideoRenderer::Flush, base::Unretained(video_renderer_.get()))); &VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
...@@ -518,14 +513,6 @@ void Pipeline::DoSeek( ...@@ -518,14 +513,6 @@ void Pipeline::DoSeek(
&video_buffering_state_, &video_buffering_state_,
BUFFERING_HAVE_NOTHING)); BUFFERING_HAVE_NOTHING));
} }
#if DCHECK_IS_ON
// Verify renderers reset their buffering states.
bound_fns.Push(base::Bind(&VerifyBufferingStates,
&audio_buffering_state_,
&video_buffering_state_));
#endif
if (text_renderer_) { if (text_renderer_) {
bound_fns.Push(base::Bind( bound_fns.Push(base::Bind(
&TextRenderer::Flush, base::Unretained(text_renderer_.get()))); &TextRenderer::Flush, base::Unretained(text_renderer_.get())));
...@@ -537,9 +524,9 @@ void Pipeline::DoSeek( ...@@ -537,9 +524,9 @@ void Pipeline::DoSeek(
// Preroll renderers. // Preroll renderers.
if (audio_renderer_) { if (audio_renderer_) {
bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom, bound_fns.Push(base::Bind(
base::Unretained(audio_renderer_.get()), &AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
seek_timestamp)); seek_timestamp));
} }
if (video_renderer_) { if (video_renderer_) {
...@@ -869,9 +856,8 @@ void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) { ...@@ -869,9 +856,8 @@ void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
demuxer_->GetStream(DemuxerStream::AUDIO), demuxer_->GetStream(DemuxerStream::AUDIO),
done_cb, done_cb,
base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)), base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
base::Bind(&Pipeline::OnAudioUnderflow, base::Unretained(this)),
base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)), base::Bind(&Pipeline::OnAudioTimeUpdate, base::Unretained(this)),
base::Bind(&Pipeline::BufferingStateChanged, base::Unretained(this),
&audio_buffering_state_),
base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)), base::Bind(&Pipeline::OnAudioRendererEnded, base::Unretained(this)),
base::Bind(&Pipeline::SetError, base::Unretained(this))); base::Bind(&Pipeline::SetError, base::Unretained(this)));
} }
...@@ -892,6 +878,20 @@ void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) { ...@@ -892,6 +878,20 @@ void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this))); base::Bind(&Pipeline::GetMediaDuration, base::Unretained(this)));
} }
void Pipeline::OnAudioUnderflow() {
if (!task_runner_->BelongsToCurrentThread()) {
task_runner_->PostTask(FROM_HERE, base::Bind(
&Pipeline::OnAudioUnderflow, base::Unretained(this)));
return;
}
if (state_ != kPlaying)
return;
if (audio_renderer_)
audio_renderer_->ResumeAfterUnderflow();
}
void Pipeline::BufferingStateChanged(BufferingState* buffering_state, void Pipeline::BufferingStateChanged(BufferingState* buffering_state,
BufferingState new_buffering_state) { BufferingState new_buffering_state) {
DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", " DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
...@@ -903,7 +903,7 @@ void Pipeline::BufferingStateChanged(BufferingState* buffering_state, ...@@ -903,7 +903,7 @@ void Pipeline::BufferingStateChanged(BufferingState* buffering_state,
// Renderer underflowed. // Renderer underflowed.
if (!was_waiting_for_enough_data && WaitingForEnoughData()) { if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
PausePlayback(); StartWaitingForEnoughData();
return; return;
} }
...@@ -925,11 +925,10 @@ bool Pipeline::WaitingForEnoughData() const { ...@@ -925,11 +925,10 @@ bool Pipeline::WaitingForEnoughData() const {
return false; return false;
} }
void Pipeline::PausePlayback() { void Pipeline::StartWaitingForEnoughData() {
DVLOG(1) << __FUNCTION__; DVLOG(1) << __FUNCTION__;
DCHECK_EQ(state_, kPlaying); DCHECK_EQ(state_, kPlaying);
DCHECK(WaitingForEnoughData()); DCHECK(WaitingForEnoughData());
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
PauseClockAndStopRendering_Locked(); PauseClockAndStopRendering_Locked();
...@@ -940,7 +939,6 @@ void Pipeline::StartPlayback() { ...@@ -940,7 +939,6 @@ void Pipeline::StartPlayback() {
DCHECK_EQ(state_, kPlaying); DCHECK_EQ(state_, kPlaying);
DCHECK_EQ(clock_state_, CLOCK_PAUSED); DCHECK_EQ(clock_state_, CLOCK_PAUSED);
DCHECK(!WaitingForEnoughData()); DCHECK(!WaitingForEnoughData());
DCHECK(task_runner_->BelongsToCurrentThread());
if (audio_renderer_) { if (audio_renderer_) {
// We use audio stream to update the clock. So if there is such a // We use audio stream to update the clock. So if there is such a
......
...@@ -309,6 +309,8 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost { ...@@ -309,6 +309,8 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
void DoStop(const PipelineStatusCB& done_cb); void DoStop(const PipelineStatusCB& done_cb);
void OnStopCompleted(PipelineStatus status); void OnStopCompleted(PipelineStatus status);
void OnAudioUnderflow();
// Collection of callback methods and helpers for tracking changes in // Collection of callback methods and helpers for tracking changes in
// buffering state and transition from paused/underflow states and playing // buffering state and transition from paused/underflow states and playing
// states. // states.
...@@ -317,11 +319,11 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost { ...@@ -317,11 +319,11 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
// - A waiting to non-waiting transition indicates preroll has completed // - A waiting to non-waiting transition indicates preroll has completed
// and StartPlayback() should be called // and StartPlayback() should be called
// - A non-waiting to waiting transition indicates underflow has occurred // - A non-waiting to waiting transition indicates underflow has occurred
// and PausePlayback() should be called // and StartWaitingForEnoughData() should be called
void BufferingStateChanged(BufferingState* buffering_state, void BufferingStateChanged(BufferingState* buffering_state,
BufferingState new_buffering_state); BufferingState new_buffering_state);
bool WaitingForEnoughData() const; bool WaitingForEnoughData() const;
void PausePlayback(); void StartWaitingForEnoughData();
void StartPlayback(); void StartPlayback();
void PauseClockAndStopRendering_Locked(); void PauseClockAndStopRendering_Locked();
......
...@@ -51,10 +51,6 @@ ACTION_P2(SetError, pipeline, status) { ...@@ -51,10 +51,6 @@ ACTION_P2(SetError, pipeline, status) {
pipeline->SetErrorForTesting(status); pipeline->SetErrorForTesting(status);
} }
ACTION_P2(SetBufferingState, cb, buffering_state) {
cb->Run(buffering_state);
}
// Used for setting expectations on pipeline callbacks. Using a StrictMock // Used for setting expectations on pipeline callbacks. Using a StrictMock
// also lets us test for missing callbacks. // also lets us test for missing callbacks.
class CallbackHelper { class CallbackHelper {
...@@ -187,8 +183,7 @@ class PipelineTest : public ::testing::Test { ...@@ -187,8 +183,7 @@ class PipelineTest : public ::testing::Test {
// Sets up expectations to allow the audio renderer to initialize. // Sets up expectations to allow the audio renderer to initialize.
void InitializeAudioRenderer(DemuxerStream* stream) { void InitializeAudioRenderer(DemuxerStream* stream) {
EXPECT_CALL(*audio_renderer_, Initialize(stream, _, _, _, _, _, _)) EXPECT_CALL(*audio_renderer_, Initialize(stream, _, _, _, _, _, _))
.WillOnce(DoAll(SaveArg<3>(&audio_time_cb_), .WillOnce(DoAll(SaveArg<4>(&audio_time_cb_),
SaveArg<4>(&audio_buffering_state_cb_),
RunCallback<1>(PIPELINE_OK))); RunCallback<1>(PIPELINE_OK)));
} }
...@@ -210,9 +205,10 @@ class PipelineTest : public ::testing::Test { ...@@ -210,9 +205,10 @@ class PipelineTest : public ::testing::Test {
if (audio_stream_) { if (audio_stream_) {
EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f)); EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f));
EXPECT_CALL(*audio_renderer_, SetVolume(1.0f)); EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(base::TimeDelta()))
.WillOnce(SetBufferingState(&audio_buffering_state_cb_, // Startup sequence.
BUFFERING_HAVE_ENOUGH)); EXPECT_CALL(*audio_renderer_, Preroll(base::TimeDelta(), _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*audio_renderer_, StartRendering()); EXPECT_CALL(*audio_renderer_, StartRendering());
} }
EXPECT_CALL(callbacks_, OnPrerollCompleted()); EXPECT_CALL(callbacks_, OnPrerollCompleted());
...@@ -258,21 +254,17 @@ class PipelineTest : public ::testing::Test { ...@@ -258,21 +254,17 @@ class PipelineTest : public ::testing::Test {
return text_stream_.get(); return text_stream_.get();
} }
void ExpectSeek(const base::TimeDelta& seek_time, bool underflowed) { void ExpectSeek(const base::TimeDelta& seek_time) {
// Every filter should receive a call to Seek(). // Every filter should receive a call to Seek().
EXPECT_CALL(*demuxer_, Seek(seek_time, _)) EXPECT_CALL(*demuxer_, Seek(seek_time, _))
.WillOnce(RunCallback<1>(PIPELINE_OK)); .WillOnce(RunCallback<1>(PIPELINE_OK));
if (audio_stream_) { if (audio_stream_) {
if (!underflowed) EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_)) EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_, .WillOnce(RunClosure<0>());
BUFFERING_HAVE_NOTHING), EXPECT_CALL(*audio_renderer_, Preroll(seek_time, _))
RunClosure<0>())); .WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(seek_time))
.WillOnce(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*audio_renderer_, SetPlaybackRate(_)); EXPECT_CALL(*audio_renderer_, SetPlaybackRate(_));
EXPECT_CALL(*audio_renderer_, SetVolume(_)); EXPECT_CALL(*audio_renderer_, SetVolume(_));
EXPECT_CALL(*audio_renderer_, StartRendering()); EXPECT_CALL(*audio_renderer_, StartRendering());
...@@ -341,7 +333,6 @@ class PipelineTest : public ::testing::Test { ...@@ -341,7 +333,6 @@ class PipelineTest : public ::testing::Test {
scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_; scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_;
scoped_ptr<FakeTextTrackStream> text_stream_; scoped_ptr<FakeTextTrackStream> text_stream_;
AudioRenderer::TimeCB audio_time_cb_; AudioRenderer::TimeCB audio_time_cb_;
AudioRenderer::BufferingStateCB audio_buffering_state_cb_;
VideoDecoderConfig video_decoder_config_; VideoDecoderConfig video_decoder_config_;
PipelineMetadata metadata_; PipelineMetadata metadata_;
...@@ -519,7 +510,7 @@ TEST_F(PipelineTest, Seek) { ...@@ -519,7 +510,7 @@ TEST_F(PipelineTest, Seek) {
// Every filter should receive a call to Seek(). // Every filter should receive a call to Seek().
base::TimeDelta expected = base::TimeDelta::FromSeconds(2000); base::TimeDelta expected = base::TimeDelta::FromSeconds(2000);
ExpectSeek(expected, false); ExpectSeek(expected);
DoSeek(expected); DoSeek(expected);
} }
...@@ -577,7 +568,7 @@ TEST_F(PipelineTest, GetBufferedTimeRanges) { ...@@ -577,7 +568,7 @@ TEST_F(PipelineTest, GetBufferedTimeRanges) {
EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0)); EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0));
base::TimeDelta kSeekTime = kDuration / 2; base::TimeDelta kSeekTime = kDuration / 2;
ExpectSeek(kSeekTime, false); ExpectSeek(kSeekTime);
DoSeek(kSeekTime); DoSeek(kSeekTime);
EXPECT_FALSE(pipeline_->DidLoadingProgress()); EXPECT_FALSE(pipeline_->DidLoadingProgress());
...@@ -679,9 +670,7 @@ TEST_F(PipelineTest, ErrorDuringSeek) { ...@@ -679,9 +670,7 @@ TEST_F(PipelineTest, ErrorDuringSeek) {
// Preroll() isn't called as the demuxer errors out first. // Preroll() isn't called as the demuxer errors out first.
EXPECT_CALL(*audio_renderer_, StopRendering()); EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_)) EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_, .WillOnce(RunClosure<0>());
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
EXPECT_CALL(*audio_renderer_, Stop(_)) EXPECT_CALL(*audio_renderer_, Stop(_))
.WillOnce(RunClosure<0>()); .WillOnce(RunClosure<0>());
...@@ -735,9 +724,7 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) { ...@@ -735,9 +724,7 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
// Seek() isn't called as the demuxer errors out first. // Seek() isn't called as the demuxer errors out first.
EXPECT_CALL(*audio_renderer_, StopRendering()); EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_)) EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_, .WillOnce(RunClosure<0>());
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
EXPECT_CALL(*audio_renderer_, Stop(_)) EXPECT_CALL(*audio_renderer_, Stop(_))
.WillOnce(RunClosure<0>()); .WillOnce(RunClosure<0>());
...@@ -827,12 +814,9 @@ TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) { ...@@ -827,12 +814,9 @@ TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) {
EXPECT_CALL(*audio_renderer_, StopRendering()); EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_)) EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_, .WillOnce(RunClosure<0>());
BUFFERING_HAVE_NOTHING), EXPECT_CALL(*audio_renderer_, Preroll(seek_time, _))
RunClosure<0>())); .WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(seek_time))
.WillOnce(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*audio_renderer_, SetPlaybackRate(_)); EXPECT_CALL(*audio_renderer_, SetPlaybackRate(_));
EXPECT_CALL(*audio_renderer_, SetVolume(_)); EXPECT_CALL(*audio_renderer_, SetVolume(_));
EXPECT_CALL(*audio_renderer_, StartRendering()); EXPECT_CALL(*audio_renderer_, StartRendering());
...@@ -870,28 +854,6 @@ TEST_F(PipelineTest, DeleteAfterStop) { ...@@ -870,28 +854,6 @@ TEST_F(PipelineTest, DeleteAfterStop) {
message_loop_.RunUntilIdle(); message_loop_.RunUntilIdle();
} }
TEST_F(PipelineTest, Underflow) {
CreateAudioStream();
CreateVideoStream();
MockDemuxerStreamVector streams;
streams.push_back(audio_stream());
streams.push_back(video_stream());
InitializeDemuxer(&streams);
InitializeAudioRenderer(audio_stream());
InitializeVideoRenderer(video_stream());
InitializePipeline(PIPELINE_OK);
// Simulate underflow.
EXPECT_CALL(*audio_renderer_, StopRendering());
audio_buffering_state_cb_.Run(BUFFERING_HAVE_NOTHING);
// Seek while underflowed. We shouldn't call StopRendering() again.
base::TimeDelta expected = base::TimeDelta::FromSeconds(5);
ExpectSeek(expected, true);
DoSeek(expected);
}
class PipelineTeardownTest : public PipelineTest { class PipelineTeardownTest : public PipelineTest {
public: public:
enum TeardownState { enum TeardownState {
...@@ -1004,8 +966,7 @@ class PipelineTeardownTest : public PipelineTest { ...@@ -1004,8 +966,7 @@ class PipelineTeardownTest : public PipelineTest {
} }
EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _)) EXPECT_CALL(*audio_renderer_, Initialize(_, _, _, _, _, _, _))
.WillOnce(DoAll(SaveArg<4>(&audio_buffering_state_cb_), .WillOnce(RunCallback<1>(PIPELINE_OK));
RunCallback<1>(PIPELINE_OK)));
if (state == kInitVideoRenderer) { if (state == kInitVideoRenderer) {
if (stop_or_error == kStop) { if (stop_or_error == kStop) {
...@@ -1031,9 +992,8 @@ class PipelineTeardownTest : public PipelineTest { ...@@ -1031,9 +992,8 @@ class PipelineTeardownTest : public PipelineTest {
EXPECT_CALL(callbacks_, OnMetadata(_)); EXPECT_CALL(callbacks_, OnMetadata(_));
// If we get here it's a successful initialization. // If we get here it's a successful initialization.
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(base::TimeDelta())) EXPECT_CALL(*audio_renderer_, Preroll(base::TimeDelta(), _))
.WillOnce(SetBufferingState(&audio_buffering_state_cb_, .WillOnce(RunCallback<1>(PIPELINE_OK));
BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*video_renderer_, Preroll(base::TimeDelta(), _)) EXPECT_CALL(*video_renderer_, Preroll(base::TimeDelta(), _))
.WillOnce(RunCallback<1>(PIPELINE_OK)); .WillOnce(RunCallback<1>(PIPELINE_OK));
...@@ -1080,26 +1040,17 @@ class PipelineTeardownTest : public PipelineTest { ...@@ -1080,26 +1040,17 @@ class PipelineTeardownTest : public PipelineTest {
if (state == kFlushing) { if (state == kFlushing) {
if (stop_or_error == kStop) { if (stop_or_error == kStop) {
EXPECT_CALL(*audio_renderer_, Flush(_)) EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb), RunClosure<0>()));
SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
} else { } else {
status = PIPELINE_ERROR_READ; status = PIPELINE_ERROR_READ;
EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce( EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce(
DoAll(SetError(pipeline_.get(), status), DoAll(SetError(pipeline_.get(), status), RunClosure<0>()));
SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
} }
return status; return status;
} }
EXPECT_CALL(*audio_renderer_, Flush(_)) EXPECT_CALL(*audio_renderer_, Flush(_)).WillOnce(RunClosure<0>());
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
EXPECT_CALL(*video_renderer_, Flush(_)).WillOnce(RunClosure<0>()); EXPECT_CALL(*video_renderer_, Flush(_)).WillOnce(RunClosure<0>());
if (state == kSeeking) { if (state == kSeeking) {
...@@ -1121,18 +1072,20 @@ class PipelineTeardownTest : public PipelineTest { ...@@ -1121,18 +1072,20 @@ class PipelineTeardownTest : public PipelineTest {
if (state == kPrerolling) { if (state == kPrerolling) {
if (stop_or_error == kStop) { if (stop_or_error == kStop) {
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(_)) EXPECT_CALL(*audio_renderer_, Preroll(_, _))
.WillOnce(Stop(pipeline_.get(), stop_cb)); .WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
RunCallback<1>(PIPELINE_OK)));
} else { } else {
status = PIPELINE_ERROR_READ; status = PIPELINE_ERROR_READ;
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(_)) EXPECT_CALL(*audio_renderer_, Preroll(_, _))
.WillOnce(SetError(pipeline_.get(), status)); .WillOnce(RunCallback<1>(status));
} }
return status; return status;
} }
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(_)); EXPECT_CALL(*audio_renderer_, Preroll(_, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*video_renderer_, Preroll(_, _)) EXPECT_CALL(*video_renderer_, Preroll(_, _))
.WillOnce(RunCallback<1>(PIPELINE_OK)); .WillOnce(RunCallback<1>(PIPELINE_OK));
......
...@@ -54,12 +54,12 @@ AudioRendererImpl::AudioRendererImpl( ...@@ -54,12 +54,12 @@ AudioRendererImpl::AudioRendererImpl(
hardware_config_(hardware_config), hardware_config_(hardware_config),
now_cb_(base::Bind(&base::TimeTicks::Now)), now_cb_(base::Bind(&base::TimeTicks::Now)),
state_(kUninitialized), state_(kUninitialized),
buffering_state_(BUFFERING_HAVE_NOTHING),
rendering_(false), rendering_(false),
sink_playing_(false), sink_playing_(false),
pending_read_(false), pending_read_(false),
received_end_of_stream_(false), received_end_of_stream_(false),
rendered_end_of_stream_(false), rendered_end_of_stream_(false),
preroll_aborted_(false),
weak_factory_(this) { weak_factory_(this) {
audio_buffer_stream_.set_splice_observer(base::Bind( audio_buffer_stream_.set_splice_observer(base::Bind(
&AudioRendererImpl::OnNewSpliceBuffer, weak_factory_.GetWeakPtr())); &AudioRendererImpl::OnNewSpliceBuffer, weak_factory_.GetWeakPtr()));
...@@ -92,7 +92,8 @@ void AudioRendererImpl::StartRendering() { ...@@ -92,7 +92,8 @@ void AudioRendererImpl::StartRendering() {
void AudioRendererImpl::StartRendering_Locked() { void AudioRendererImpl::StartRendering_Locked() {
DVLOG(1) << __FUNCTION__; DVLOG(1) << __FUNCTION__;
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPlaying); DCHECK(state_ == kPlaying || state_ == kRebuffering || state_ == kUnderflow)
<< "state_=" << state_;
DCHECK(!sink_playing_); DCHECK(!sink_playing_);
DCHECK_NE(algorithm_->playback_rate(), 0); DCHECK_NE(algorithm_->playback_rate(), 0);
lock_.AssertAcquired(); lock_.AssertAcquired();
...@@ -122,7 +123,8 @@ void AudioRendererImpl::StopRendering() { ...@@ -122,7 +123,8 @@ void AudioRendererImpl::StopRendering() {
void AudioRendererImpl::StopRendering_Locked() { void AudioRendererImpl::StopRendering_Locked() {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK_EQ(state_, kPlaying); DCHECK(state_ == kPlaying || state_ == kRebuffering || state_ == kUnderflow)
<< "state_=" << state_;
DCHECK(sink_playing_); DCHECK(sink_playing_);
lock_.AssertAcquired(); lock_.AssertAcquired();
...@@ -137,14 +139,16 @@ void AudioRendererImpl::Flush(const base::Closure& callback) { ...@@ -137,14 +139,16 @@ void AudioRendererImpl::Flush(const base::Closure& callback) {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
DCHECK_EQ(state_, kPlaying); DCHECK(state_ == kPlaying || state_ == kRebuffering || state_ == kUnderflow)
<< "state_=" << state_;
DCHECK(flush_cb_.is_null()); DCHECK(flush_cb_.is_null());
flush_cb_ = callback; flush_cb_ = callback;
ChangeState_Locked(kFlushing);
if (pending_read_) if (pending_read_) {
ChangeState_Locked(kFlushing);
return; return;
}
ChangeState_Locked(kFlushed); ChangeState_Locked(kFlushed);
DoFlush_Locked(); DoFlush_Locked();
...@@ -174,10 +178,7 @@ void AudioRendererImpl::ResetDecoderDone() { ...@@ -174,10 +178,7 @@ void AudioRendererImpl::ResetDecoderDone() {
audio_clock_.reset(new AudioClock(audio_parameters_.sample_rate())); audio_clock_.reset(new AudioClock(audio_parameters_.sample_rate()));
received_end_of_stream_ = false; received_end_of_stream_ = false;
rendered_end_of_stream_ = false; rendered_end_of_stream_ = false;
preroll_aborted_ = false;
// Flush() may have been called while underflowed/not fully buffered.
if (buffering_state_ != BUFFERING_HAVE_NOTHING)
SetBufferingState_Locked(BUFFERING_HAVE_NOTHING);
earliest_end_time_ = now_cb_.Run(); earliest_end_time_ = now_cb_.Run();
splicer_->Reset(); splicer_->Reset();
...@@ -206,6 +207,7 @@ void AudioRendererImpl::Stop(const base::Closure& callback) { ...@@ -206,6 +207,7 @@ void AudioRendererImpl::Stop(const base::Closure& callback) {
ChangeState_Locked(kStopped); ChangeState_Locked(kStopped);
algorithm_.reset(); algorithm_.reset();
underflow_cb_.Reset();
time_cb_.Reset(); time_cb_.Reset();
flush_cb_.Reset(); flush_cb_.Reset();
} }
...@@ -218,18 +220,20 @@ void AudioRendererImpl::Stop(const base::Closure& callback) { ...@@ -218,18 +220,20 @@ void AudioRendererImpl::Stop(const base::Closure& callback) {
audio_buffer_stream_.Stop(callback); audio_buffer_stream_.Stop(callback);
} }
void AudioRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { void AudioRendererImpl::Preroll(base::TimeDelta time,
DVLOG(1) << __FUNCTION__ << "(" << timestamp.InMicroseconds() << ")"; const PipelineStatusCB& cb) {
DVLOG(1) << __FUNCTION__ << "(" << time.InMicroseconds() << ")";
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
DCHECK(!sink_playing_); DCHECK(!sink_playing_);
DCHECK_EQ(state_, kFlushed); DCHECK_EQ(state_, kFlushed);
DCHECK_EQ(buffering_state_, BUFFERING_HAVE_NOTHING);
DCHECK(!pending_read_) << "Pending read must complete before seeking"; DCHECK(!pending_read_) << "Pending read must complete before seeking";
DCHECK(preroll_cb_.is_null());
ChangeState_Locked(kPlaying); ChangeState_Locked(kPrerolling);
start_timestamp_ = timestamp; preroll_cb_ = cb;
preroll_timestamp_ = time;
AttemptRead_Locked(); AttemptRead_Locked();
} }
...@@ -237,8 +241,8 @@ void AudioRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) { ...@@ -237,8 +241,8 @@ void AudioRendererImpl::StartPlayingFrom(base::TimeDelta timestamp) {
void AudioRendererImpl::Initialize(DemuxerStream* stream, void AudioRendererImpl::Initialize(DemuxerStream* stream,
const PipelineStatusCB& init_cb, const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb, const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb, const TimeCB& time_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb, const base::Closure& ended_cb,
const PipelineStatusCB& error_cb) { const PipelineStatusCB& error_cb) {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
...@@ -246,8 +250,8 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, ...@@ -246,8 +250,8 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream,
DCHECK_EQ(stream->type(), DemuxerStream::AUDIO); DCHECK_EQ(stream->type(), DemuxerStream::AUDIO);
DCHECK(!init_cb.is_null()); DCHECK(!init_cb.is_null());
DCHECK(!statistics_cb.is_null()); DCHECK(!statistics_cb.is_null());
DCHECK(!underflow_cb.is_null());
DCHECK(!time_cb.is_null()); DCHECK(!time_cb.is_null());
DCHECK(!buffering_state_cb.is_null());
DCHECK(!ended_cb.is_null()); DCHECK(!ended_cb.is_null());
DCHECK(!error_cb.is_null()); DCHECK(!error_cb.is_null());
DCHECK_EQ(kUninitialized, state_); DCHECK_EQ(kUninitialized, state_);
...@@ -256,9 +260,8 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream, ...@@ -256,9 +260,8 @@ void AudioRendererImpl::Initialize(DemuxerStream* stream,
state_ = kInitializing; state_ = kInitializing;
init_cb_ = init_cb; init_cb_ = init_cb;
underflow_cb_ = underflow_cb;
time_cb_ = time_cb; time_cb_ = time_cb;
// Callback can be run from audio callback thread in Render().
buffering_state_cb_ = BindToCurrentLoop(buffering_state_cb);
ended_cb_ = ended_cb; ended_cb_ = ended_cb;
error_cb_ = error_cb; error_cb_ = error_cb;
...@@ -348,6 +351,23 @@ void AudioRendererImpl::OnAudioBufferStreamInitialized(bool success) { ...@@ -348,6 +351,23 @@ void AudioRendererImpl::OnAudioBufferStreamInitialized(bool success) {
base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK); base::ResetAndReturn(&init_cb_).Run(PIPELINE_OK);
} }
void AudioRendererImpl::ResumeAfterUnderflow() {
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
if (state_ == kUnderflow) {
// The "!preroll_aborted_" is a hack. If preroll is aborted, then we
// shouldn't even reach the kUnderflow state to begin with. But for now
// we're just making sure that the audio buffer capacity (i.e. the
// number of bytes that need to be buffered for preroll to complete)
// does not increase due to an aborted preroll.
// TODO(vrk): Fix this bug correctly! (crbug.com/151352)
if (!preroll_aborted_)
algorithm_->IncreaseQueueCapacity();
ChangeState_Locked(kRebuffering);
}
}
void AudioRendererImpl::SetVolume(float volume) { void AudioRendererImpl::SetVolume(float volume) {
DCHECK(task_runner_->BelongsToCurrentThread()); DCHECK(task_runner_->BelongsToCurrentThread());
DCHECK(sink_); DCHECK(sink_);
...@@ -409,7 +429,7 @@ void AudioRendererImpl::DecodedAudioReady( ...@@ -409,7 +429,7 @@ void AudioRendererImpl::DecodedAudioReady(
bool need_another_buffer = false; bool need_another_buffer = false;
while (splicer_->HasNextBuffer()) while (splicer_->HasNextBuffer())
need_another_buffer = HandleSplicerBuffer_Locked(splicer_->GetNextBuffer()); need_another_buffer = HandleSplicerBuffer(splicer_->GetNextBuffer());
if (!need_another_buffer && !CanRead_Locked()) if (!need_another_buffer && !CanRead_Locked())
return; return;
...@@ -417,18 +437,23 @@ void AudioRendererImpl::DecodedAudioReady( ...@@ -417,18 +437,23 @@ void AudioRendererImpl::DecodedAudioReady(
AttemptRead_Locked(); AttemptRead_Locked();
} }
bool AudioRendererImpl::HandleSplicerBuffer_Locked( bool AudioRendererImpl::HandleSplicerBuffer(
const scoped_refptr<AudioBuffer>& buffer) { const scoped_refptr<AudioBuffer>& buffer) {
lock_.AssertAcquired();
if (buffer->end_of_stream()) { if (buffer->end_of_stream()) {
received_end_of_stream_ = true; received_end_of_stream_ = true;
// Transition to kPlaying if we are currently handling an underflow since
// no more data will be arriving.
if (state_ == kUnderflow || state_ == kRebuffering)
ChangeState_Locked(kPlaying);
} else { } else {
if (state_ == kPlaying) { if (state_ == kPrerolling) {
if (IsBeforeStartTime(buffer)) if (IsBeforePrerollTime(buffer))
return true; return true;
// Trim off any additional time before the start timestamp. // Trim off any additional time before the preroll timestamp.
const base::TimeDelta trim_time = start_timestamp_ - buffer->timestamp(); const base::TimeDelta trim_time =
preroll_timestamp_ - buffer->timestamp();
if (trim_time > base::TimeDelta()) { if (trim_time > base::TimeDelta()) {
buffer->TrimStart(buffer->frame_count() * buffer->TrimStart(buffer->frame_count() *
(static_cast<double>(trim_time.InMicroseconds()) / (static_cast<double>(trim_time.InMicroseconds()) /
...@@ -454,13 +479,22 @@ bool AudioRendererImpl::HandleSplicerBuffer_Locked( ...@@ -454,13 +479,22 @@ bool AudioRendererImpl::HandleSplicerBuffer_Locked(
DCHECK(!pending_read_); DCHECK(!pending_read_);
return false; return false;
case kPrerolling:
if (!buffer->end_of_stream() && !algorithm_->IsQueueFull())
return true;
ChangeState_Locked(kPlaying);
base::ResetAndReturn(&preroll_cb_).Run(PIPELINE_OK);
return false;
case kPlaying: case kPlaying:
if (buffer->end_of_stream() || algorithm_->IsQueueFull()) { case kUnderflow:
if (buffering_state_ == BUFFERING_HAVE_NOTHING) return false;
SetBufferingState_Locked(BUFFERING_HAVE_ENOUGH);
return false; case kRebuffering:
} if (!algorithm_->IsQueueFull())
return true; return true;
ChangeState_Locked(kPlaying);
return false;
case kStopped: case kStopped:
return false; return false;
...@@ -491,12 +525,15 @@ bool AudioRendererImpl::CanRead_Locked() { ...@@ -491,12 +525,15 @@ bool AudioRendererImpl::CanRead_Locked() {
switch (state_) { switch (state_) {
case kUninitialized: case kUninitialized:
case kInitializing: case kInitializing:
case kFlushing:
case kFlushed: case kFlushed:
case kFlushing:
case kStopped: case kStopped:
return false; return false;
case kPrerolling:
case kPlaying: case kPlaying:
case kUnderflow:
case kRebuffering:
break; break;
} }
...@@ -532,11 +569,11 @@ void AudioRendererImpl::SetPlaybackRate(float playback_rate) { ...@@ -532,11 +569,11 @@ void AudioRendererImpl::SetPlaybackRate(float playback_rate) {
} }
} }
bool AudioRendererImpl::IsBeforeStartTime( bool AudioRendererImpl::IsBeforePrerollTime(
const scoped_refptr<AudioBuffer>& buffer) { const scoped_refptr<AudioBuffer>& buffer) {
DCHECK_EQ(state_, kPlaying); DCHECK_EQ(state_, kPrerolling);
return buffer && !buffer->end_of_stream() && return buffer && !buffer->end_of_stream() &&
(buffer->timestamp() + buffer->duration()) < start_timestamp_; (buffer->timestamp() + buffer->duration()) < preroll_timestamp_;
} }
int AudioRendererImpl::Render(AudioBus* audio_bus, int AudioRendererImpl::Render(AudioBus* audio_bus,
...@@ -548,6 +585,7 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, ...@@ -548,6 +585,7 @@ int AudioRendererImpl::Render(AudioBus* audio_bus,
audio_parameters_.sample_rate()); audio_parameters_.sample_rate());
int frames_written = 0; int frames_written = 0;
base::Closure time_cb; base::Closure time_cb;
base::Closure underflow_cb;
{ {
base::AutoLock auto_lock(lock_); base::AutoLock auto_lock(lock_);
...@@ -601,10 +639,8 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, ...@@ -601,10 +639,8 @@ int AudioRendererImpl::Render(AudioBus* audio_bus,
rendered_end_of_stream_ = true; rendered_end_of_stream_ = true;
ended_cb_.Run(); ended_cb_.Run();
} else if (!received_end_of_stream_ && state_ == kPlaying) { } else if (!received_end_of_stream_ && state_ == kPlaying) {
if (buffering_state_ != BUFFERING_HAVE_NOTHING) { ChangeState_Locked(kUnderflow);
algorithm_->IncreaseQueueCapacity(); underflow_cb = underflow_cb_;
SetBufferingState_Locked(BUFFERING_HAVE_NOTHING);
}
} else { } else {
// We can't write any data this cycle. For example, we may have // We can't write any data this cycle. For example, we may have
// sent all available data to the audio device while not reaching // sent all available data to the audio device while not reaching
...@@ -637,6 +673,9 @@ int AudioRendererImpl::Render(AudioBus* audio_bus, ...@@ -637,6 +673,9 @@ int AudioRendererImpl::Render(AudioBus* audio_bus,
if (!time_cb.is_null()) if (!time_cb.is_null())
time_cb.Run(); time_cb.Run();
if (!underflow_cb.is_null())
underflow_cb.Run();
DCHECK_LE(frames_written, requested_frames); DCHECK_LE(frames_written, requested_frames);
return frames_written; return frames_written;
} }
...@@ -675,6 +714,7 @@ void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) { ...@@ -675,6 +714,7 @@ void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) {
return; return;
case kFlushing: case kFlushing:
ChangeState_Locked(kFlushed); ChangeState_Locked(kFlushed);
if (status == PIPELINE_OK) { if (status == PIPELINE_OK) {
DoFlush_Locked(); DoFlush_Locked();
return; return;
...@@ -683,9 +723,16 @@ void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) { ...@@ -683,9 +723,16 @@ void AudioRendererImpl::HandleAbortedReadOrDecodeError(bool is_decode_error) {
error_cb_.Run(status); error_cb_.Run(status);
base::ResetAndReturn(&flush_cb_).Run(); base::ResetAndReturn(&flush_cb_).Run();
return; return;
case kPrerolling:
// This is a signal for abort if it's not an error.
preroll_aborted_ = !is_decode_error;
ChangeState_Locked(kPlaying);
base::ResetAndReturn(&preroll_cb_).Run(status);
return;
case kFlushed: case kFlushed:
case kPlaying: case kPlaying:
case kUnderflow:
case kRebuffering:
case kStopped: case kStopped:
if (status != PIPELINE_OK) if (status != PIPELINE_OK)
error_cb_.Run(status); error_cb_.Run(status);
...@@ -715,14 +762,4 @@ void AudioRendererImpl::OnConfigChange() { ...@@ -715,14 +762,4 @@ void AudioRendererImpl::OnConfigChange() {
CHECK(splicer_->AddInput(buffer_converter_->GetNextBuffer())); CHECK(splicer_->AddInput(buffer_converter_->GetNextBuffer()));
} }
void AudioRendererImpl::SetBufferingState_Locked(
BufferingState buffering_state) {
DVLOG(1) << __FUNCTION__ << " : " << buffering_state_ << " -> "
<< buffering_state;
DCHECK_NE(buffering_state_, buffering_state);
lock_.AssertAcquired();
buffering_state_ = buffering_state;
buffering_state_cb_.Run(buffering_state_);
}
} // namespace media } // namespace media
...@@ -68,8 +68,8 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -68,8 +68,8 @@ class MEDIA_EXPORT AudioRendererImpl
virtual void Initialize(DemuxerStream* stream, virtual void Initialize(DemuxerStream* stream,
const PipelineStatusCB& init_cb, const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb, const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb, const TimeCB& time_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb, const base::Closure& ended_cb,
const PipelineStatusCB& error_cb) OVERRIDE; const PipelineStatusCB& error_cb) OVERRIDE;
virtual void StartRendering() OVERRIDE; virtual void StartRendering() OVERRIDE;
...@@ -77,7 +77,9 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -77,7 +77,9 @@ class MEDIA_EXPORT AudioRendererImpl
virtual void Flush(const base::Closure& callback) OVERRIDE; virtual void Flush(const base::Closure& callback) OVERRIDE;
virtual void Stop(const base::Closure& callback) OVERRIDE; virtual void Stop(const base::Closure& callback) OVERRIDE;
virtual void SetPlaybackRate(float rate) OVERRIDE; virtual void SetPlaybackRate(float rate) OVERRIDE;
virtual void StartPlayingFrom(base::TimeDelta timestamp) OVERRIDE; virtual void Preroll(base::TimeDelta time,
const PipelineStatusCB& cb) OVERRIDE;
virtual void ResumeAfterUnderflow() OVERRIDE;
virtual void SetVolume(float volume) OVERRIDE; virtual void SetVolume(float volume) OVERRIDE;
// Allows injection of a custom time callback for non-realtime testing. // Allows injection of a custom time callback for non-realtime testing.
...@@ -102,17 +104,25 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -102,17 +104,25 @@ class MEDIA_EXPORT AudioRendererImpl
// | // |
// V Decoders reset // V Decoders reset
// kFlushed <------------------ kFlushing // kFlushed <------------------ kFlushing
// | StartPlayingFrom() ^ // | Preroll() ^
// | | // | |
// | | Flush() // V | Flush()
// `---------> kPlaying --------' // kPrerolling ----------------> kPlaying ---------.
// Enough data buffered ^ | Not enough data
// | | buffered
// Enough data buffered | V
// kRebuffering <--- kUnderflow
// ResumeAfterUnderflow()
enum State { enum State {
kUninitialized, kUninitialized,
kInitializing, kInitializing,
kFlushing, kFlushing,
kFlushed, kFlushed,
kPrerolling,
kPlaying, kPlaying,
kStopped, kStopped,
kUnderflow,
kRebuffering,
}; };
// Callback from the audio decoder delivering decoded audio samples. // Callback from the audio decoder delivering decoded audio samples.
...@@ -121,7 +131,7 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -121,7 +131,7 @@ class MEDIA_EXPORT AudioRendererImpl
// Handles buffers that come out of |splicer_|. // Handles buffers that come out of |splicer_|.
// Returns true if more buffers are needed. // Returns true if more buffers are needed.
bool HandleSplicerBuffer_Locked(const scoped_refptr<AudioBuffer>& buffer); bool HandleSplicerBuffer(const scoped_refptr<AudioBuffer>& buffer);
// Helper functions for AudioDecoder::Status values passed to // Helper functions for AudioDecoder::Status values passed to
// DecodedAudioReady(). // DecodedAudioReady().
...@@ -167,9 +177,10 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -167,9 +177,10 @@ class MEDIA_EXPORT AudioRendererImpl
bool CanRead_Locked(); bool CanRead_Locked();
void ChangeState_Locked(State new_state); void ChangeState_Locked(State new_state);
// Returns true if the data in the buffer is all before |start_timestamp_|. // Returns true if the data in the buffer is all before
// This can only return true while in the kPlaying state. // |preroll_timestamp_|. This can only return true while
bool IsBeforeStartTime(const scoped_refptr<AudioBuffer>& buffer); // in the kPrerolling state.
bool IsBeforePrerollTime(const scoped_refptr<AudioBuffer>& buffer);
// Called upon AudioBufferStream initialization, or failure thereof (indicated // Called upon AudioBufferStream initialization, or failure thereof (indicated
// by the value of |success|). // by the value of |success|).
...@@ -192,9 +203,6 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -192,9 +203,6 @@ class MEDIA_EXPORT AudioRendererImpl
// Called by the AudioBufferStream when a config change occurs. // Called by the AudioBufferStream when a config change occurs.
void OnConfigChange(); void OnConfigChange();
// Updates |buffering_state_| and fires |buffering_state_cb_|.
void SetBufferingState_Locked(BufferingState buffering_state);
scoped_refptr<base::SingleThreadTaskRunner> task_runner_; scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
scoped_ptr<AudioSplicer> splicer_; scoped_ptr<AudioSplicer> splicer_;
...@@ -218,14 +226,17 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -218,14 +226,17 @@ class MEDIA_EXPORT AudioRendererImpl
// Callbacks provided during Initialize(). // Callbacks provided during Initialize().
PipelineStatusCB init_cb_; PipelineStatusCB init_cb_;
base::Closure underflow_cb_;
TimeCB time_cb_; TimeCB time_cb_;
BufferingStateCB buffering_state_cb_;
base::Closure ended_cb_; base::Closure ended_cb_;
PipelineStatusCB error_cb_; PipelineStatusCB error_cb_;
// Callback provided to Flush(). // Callback provided to Flush().
base::Closure flush_cb_; base::Closure flush_cb_;
// Callback provided to Preroll().
PipelineStatusCB preroll_cb_;
// Typically calls base::TimeTicks::Now() but can be overridden by a test. // Typically calls base::TimeTicks::Now() but can be overridden by a test.
NowCB now_cb_; NowCB now_cb_;
...@@ -239,8 +250,6 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -239,8 +250,6 @@ class MEDIA_EXPORT AudioRendererImpl
// Simple state tracking variable. // Simple state tracking variable.
State state_; State state_;
BufferingState buffering_state_;
// Keep track of whether or not the sink is playing and whether we should be // Keep track of whether or not the sink is playing and whether we should be
// rendering. // rendering.
bool rendering_; bool rendering_;
...@@ -255,7 +264,7 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -255,7 +264,7 @@ class MEDIA_EXPORT AudioRendererImpl
scoped_ptr<AudioClock> audio_clock_; scoped_ptr<AudioClock> audio_clock_;
base::TimeDelta start_timestamp_; base::TimeDelta preroll_timestamp_;
// We're supposed to know amount of audio data OS or hardware buffered, but // We're supposed to know amount of audio data OS or hardware buffered, but
// that is not always so -- on my Linux box // that is not always so -- on my Linux box
...@@ -274,6 +283,10 @@ class MEDIA_EXPORT AudioRendererImpl ...@@ -274,6 +283,10 @@ class MEDIA_EXPORT AudioRendererImpl
base::TimeTicks earliest_end_time_; base::TimeTicks earliest_end_time_;
size_t total_frames_filled_; size_t total_frames_filled_;
// True if the renderer receives a buffer with kAborted status during preroll,
// false otherwise. This flag is cleared on the next Preroll() call.
bool preroll_aborted_;
// End variables which must be accessed under |lock_|. ---------------------- // End variables which must be accessed under |lock_|. ----------------------
// NOTE: Weak pointers must be invalidated before all other member variables. // NOTE: Weak pointers must be invalidated before all other member variables.
......
...@@ -117,7 +117,7 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -117,7 +117,7 @@ class AudioRendererImplTest : public ::testing::Test {
} }
MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&)); MOCK_METHOD1(OnStatistics, void(const PipelineStatistics&));
MOCK_METHOD1(OnBufferingStateChange, void(BufferingState)); MOCK_METHOD0(OnUnderflow, void());
MOCK_METHOD1(OnError, void(PipelineStatus)); MOCK_METHOD1(OnError, void(PipelineStatus));
void OnAudioTimeCallback(TimeDelta current_time, TimeDelta max_time) { void OnAudioTimeCallback(TimeDelta current_time, TimeDelta max_time) {
...@@ -131,9 +131,9 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -131,9 +131,9 @@ class AudioRendererImplTest : public ::testing::Test {
pipeline_status_cb, pipeline_status_cb,
base::Bind(&AudioRendererImplTest::OnStatistics, base::Bind(&AudioRendererImplTest::OnStatistics,
base::Unretained(this)), base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnAudioTimeCallback, base::Bind(&AudioRendererImplTest::OnUnderflow,
base::Unretained(this)), base::Unretained(this)),
base::Bind(&AudioRendererImplTest::OnBufferingStateChange, base::Bind(&AudioRendererImplTest::OnAudioTimeCallback,
base::Unretained(this)), base::Unretained(this)),
ended_event_.GetClosure(), ended_event_.GetClosure(),
base::Bind(&AudioRendererImplTest::OnError, base::Bind(&AudioRendererImplTest::OnError,
...@@ -200,7 +200,6 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -200,7 +200,6 @@ class AudioRendererImplTest : public ::testing::Test {
} }
void Flush() { void Flush() {
SCOPED_TRACE("Flush()");
WaitableMessageLoopEvent flush_event; WaitableMessageLoopEvent flush_event;
renderer_->Flush(flush_event.GetClosure()); renderer_->Flush(flush_event.GetClosure());
flush_event.RunAndWait(); flush_event.RunAndWait();
...@@ -219,10 +218,11 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -219,10 +218,11 @@ class AudioRendererImplTest : public ::testing::Test {
next_timestamp_->SetBaseTimestamp(timestamp); next_timestamp_->SetBaseTimestamp(timestamp);
// Fill entire buffer to complete prerolling. // Fill entire buffer to complete prerolling.
renderer_->StartPlayingFrom(timestamp); WaitableMessageLoopEvent event;
renderer_->Preroll(timestamp, event.GetPipelineStatusCB());
WaitForPendingRead(); WaitForPendingRead();
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
DeliverRemainingAudio(); DeliverRemainingAudio();
event.RunAndWaitForStatus(PIPELINE_OK);
// We should have no reads. // We should have no reads.
EXPECT_TRUE(decode_cb_.is_null()); EXPECT_TRUE(decode_cb_.is_null());
...@@ -360,6 +360,10 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -360,6 +360,10 @@ class AudioRendererImplTest : public ::testing::Test {
return buffer_capacity() - frames_buffered(); return buffer_capacity() - frames_buffered();
} }
void CallResumeAfterUnderflow() {
renderer_->ResumeAfterUnderflow();
}
TimeDelta CalculatePlayTime(int frames_filled) { TimeDelta CalculatePlayTime(int frames_filled) {
return TimeDelta::FromMicroseconds( return TimeDelta::FromMicroseconds(
frames_filled * Time::kMicrosecondsPerSecond / frames_filled * Time::kMicrosecondsPerSecond /
...@@ -373,7 +377,6 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -373,7 +377,6 @@ class AudioRendererImplTest : public ::testing::Test {
renderer_->SetPlaybackRate(playback_rate); renderer_->SetPlaybackRate(playback_rate);
// Drain internal buffer, we should have a pending read. // Drain internal buffer, we should have a pending read.
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
int total_frames = frames_buffered(); int total_frames = frames_buffered();
int frames_filled = ConsumeAllBufferedData(); int frames_filled = ConsumeAllBufferedData();
WaitForPendingRead(); WaitForPendingRead();
...@@ -391,7 +394,6 @@ class AudioRendererImplTest : public ::testing::Test { ...@@ -391,7 +394,6 @@ class AudioRendererImplTest : public ::testing::Test {
// Fulfill the read with an end-of-stream packet. We shouldn't report ended // Fulfill the read with an end-of-stream packet. We shouldn't report ended
// nor have a read until we drain the internal buffer. // nor have a read until we drain the internal buffer.
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
DeliverEndOfStream(); DeliverEndOfStream();
// Advance time half way without an ended expectation. // Advance time half way without an ended expectation.
...@@ -537,28 +539,33 @@ TEST_F(AudioRendererImplTest, EndOfStream_SlowerPlaybackSpeed) { ...@@ -537,28 +539,33 @@ TEST_F(AudioRendererImplTest, EndOfStream_SlowerPlaybackSpeed) {
TEST_F(AudioRendererImplTest, Underflow) { TEST_F(AudioRendererImplTest, Underflow) {
Initialize(); Initialize();
Preroll(); Preroll();
int initial_capacity = buffer_capacity();
StartRendering(); StartRendering();
// Drain internal buffer, we should have a pending read. // Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL)); EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead(); WaitForPendingRead();
// Verify the next FillBuffer() call triggers a buffering state change // Verify the next FillBuffer() call triggers the underflow callback
// update. // since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING)); EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL)); EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Verify we're still not getting audio data. renderer_->ResumeAfterUnderflow();
// Verify after resuming that we're still not getting data.
bool muted = false; bool muted = false;
EXPECT_EQ(0, frames_buffered()); EXPECT_EQ(0, frames_buffered());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted)); EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted); EXPECT_TRUE(muted);
// Deliver enough data to have enough for buffering. // Verify that the buffer capacity increased as a result of the underflow.
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH)); EXPECT_GT(buffer_capacity(), initial_capacity);
DeliverRemainingAudio();
// Verify we're getting audio data. // Deliver data, we should get non-muted audio.
DeliverRemainingAudio();
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted)); EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted); EXPECT_FALSE(muted);
} }
...@@ -566,6 +573,9 @@ TEST_F(AudioRendererImplTest, Underflow) { ...@@ -566,6 +573,9 @@ TEST_F(AudioRendererImplTest, Underflow) {
TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) { TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) {
Initialize(); Initialize();
Preroll(); Preroll();
int initial_capacity = buffer_capacity();
StartRendering(); StartRendering();
// Drain internal buffer, we should have a pending read. // Drain internal buffer, we should have a pending read.
...@@ -574,11 +584,13 @@ TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) { ...@@ -574,11 +584,13 @@ TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) {
// Verify the next FillBuffer() call triggers the underflow callback // Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained. // since the decoder hasn't delivered any data after it was drained.
int initial_capacity = buffer_capacity(); EXPECT_CALL(*this, OnUnderflow());
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL)); EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Verify that the buffer capacity increased as a result of underflowing. // Verify that the buffer capacity increased as a result of resuming after
// underflow.
EXPECT_EQ(buffer_capacity(), initial_capacity);
renderer_->ResumeAfterUnderflow();
EXPECT_GT(buffer_capacity(), initial_capacity); EXPECT_GT(buffer_capacity(), initial_capacity);
// Verify that the buffer capacity is restored to the |initial_capacity|. // Verify that the buffer capacity is restored to the |initial_capacity|.
...@@ -587,33 +599,177 @@ TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) { ...@@ -587,33 +599,177 @@ TEST_F(AudioRendererImplTest, Underflow_CapacityResetsAfterFlush) {
EXPECT_EQ(buffer_capacity(), initial_capacity); EXPECT_EQ(buffer_capacity(), initial_capacity);
} }
TEST_F(AudioRendererImplTest, Underflow_Flush) { TEST_F(AudioRendererImplTest, Underflow_FlushWhileUnderflowed) {
Initialize(); Initialize();
Preroll(); Preroll();
StartRendering(); StartRendering();
// Force underflow. // Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL)); EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead(); WaitForPendingRead();
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL)); EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
WaitForPendingRead();
StopRendering();
// We shouldn't expect another buffering state change whenn flushing. // Verify that we can still Flush() before entering the rebuffering state.
AbortPendingRead(); AbortPendingRead();
Flush(); Flush();
} }
TEST_F(AudioRendererImplTest, Underflow_EndOfStream) {
Initialize();
Preroll();
StartRendering();
// Figure out how long until the ended event should fire. Since
// ConsumeBufferedData() doesn't provide audio delay information, the time
// until the ended event fires is equivalent to the longest buffered section,
// which is the initial frames_buffered() read.
TimeDelta time_until_ended = CalculatePlayTime(frames_buffered());
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Deliver a little bit of data.
SatisfyPendingRead(kDataSize);
WaitForPendingRead();
// Verify we're getting muted audio during underflow. Note: Since resampling
// is active, the number of frames_buffered() won't always match kDataSize.
bool muted = false;
const int kInitialFramesBuffered = 1114;
EXPECT_EQ(kInitialFramesBuffered, frames_buffered());
EXPECT_FALSE(ConsumeBufferedData(kInitialFramesBuffered, &muted));
EXPECT_TRUE(muted);
// Now deliver end of stream, we should get our little bit of data back.
DeliverEndOfStream();
const int kNextFramesBuffered = 1408;
EXPECT_EQ(kNextFramesBuffered, frames_buffered());
EXPECT_TRUE(ConsumeBufferedData(kNextFramesBuffered, &muted));
EXPECT_FALSE(muted);
// Attempt to read to make sure we're truly at the end of stream.
AdvanceTime(time_until_ended);
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
WaitForEnded();
}
TEST_F(AudioRendererImplTest, Underflow_ResumeFromCallback) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow())
.WillOnce(Invoke(this, &AudioRendererImplTest::CallResumeAfterUnderflow));
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
// Verify after resuming that we're still not getting data.
bool muted = false;
EXPECT_EQ(0, frames_buffered());
EXPECT_FALSE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_TRUE(muted);
// Deliver data, we should get non-muted audio.
DeliverRemainingAudio();
EXPECT_TRUE(ConsumeBufferedData(kDataSize, &muted));
EXPECT_FALSE(muted);
}
TEST_F(AudioRendererImplTest, Underflow_SetPlaybackRate) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow())
.WillOnce(Invoke(this, &AudioRendererImplTest::CallResumeAfterUnderflow));
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
EXPECT_EQ(0, frames_buffered());
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Simulate playback being paused.
renderer_->SetPlaybackRate(0);
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
// Deliver data to resolve the underflow.
DeliverRemainingAudio();
EXPECT_EQ(FakeAudioRendererSink::kPaused, sink_->state());
// Simulate playback being resumed.
renderer_->SetPlaybackRate(1);
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
}
TEST_F(AudioRendererImplTest, Underflow_PausePlay) {
Initialize();
Preroll();
StartRendering();
// Drain internal buffer, we should have a pending read.
EXPECT_TRUE(ConsumeBufferedData(frames_buffered(), NULL));
WaitForPendingRead();
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Verify the next FillBuffer() call triggers the underflow callback
// since the decoder hasn't delivered any data after it was drained.
EXPECT_CALL(*this, OnUnderflow())
.WillOnce(Invoke(this, &AudioRendererImplTest::CallResumeAfterUnderflow));
EXPECT_FALSE(ConsumeBufferedData(kDataSize, NULL));
EXPECT_EQ(0, frames_buffered());
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
// Simulate playback being paused, and then played again.
renderer_->SetPlaybackRate(0.0);
renderer_->SetPlaybackRate(1.0);
// Deliver data to resolve the underflow.
DeliverRemainingAudio();
// We should have resumed playing now.
EXPECT_EQ(FakeAudioRendererSink::kPlaying, sink_->state());
}
TEST_F(AudioRendererImplTest, AbortPendingRead_Preroll) { TEST_F(AudioRendererImplTest, AbortPendingRead_Preroll) {
Initialize(); Initialize();
// Start prerolling and wait for a read. // Start prerolling and wait for a read.
renderer_->StartPlayingFrom(TimeDelta()); WaitableMessageLoopEvent event;
renderer_->Preroll(TimeDelta(), event.GetPipelineStatusCB());
WaitForPendingRead(); WaitForPendingRead();
// Simulate the decoder aborting the pending read. // Simulate the decoder aborting the pending read.
AbortPendingRead(); AbortPendingRead();
event.RunAndWaitForStatus(PIPELINE_OK);
Flush(); Flush();
// Preroll again to a different timestamp and verify it completed normally. // Preroll again to a different timestamp and verify it completed normally.
...@@ -640,7 +796,6 @@ TEST_F(AudioRendererImplTest, AbortPendingRead_Flush) { ...@@ -640,7 +796,6 @@ TEST_F(AudioRendererImplTest, AbortPendingRead_Flush) {
// Simulate the decoder aborting the pending read. // Simulate the decoder aborting the pending read.
AbortPendingRead(); AbortPendingRead();
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
flush_event.RunAndWait(); flush_event.RunAndWait();
EXPECT_FALSE(IsReadPending()); EXPECT_FALSE(IsReadPending());
...@@ -668,7 +823,7 @@ TEST_F(AudioRendererImplTest, PendingRead_Flush) { ...@@ -668,7 +823,7 @@ TEST_F(AudioRendererImplTest, PendingRead_Flush) {
renderer_->Flush(flush_event.GetClosure()); renderer_->Flush(flush_event.GetClosure());
SatisfyPendingRead(kDataSize); SatisfyPendingRead(kDataSize);
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_NOTHING));
flush_event.RunAndWait(); flush_event.RunAndWait();
EXPECT_FALSE(IsReadPending()); EXPECT_FALSE(IsReadPending());
...@@ -787,10 +942,11 @@ TEST_F(AudioRendererImplTest, ImmediateEndOfStream) { ...@@ -787,10 +942,11 @@ TEST_F(AudioRendererImplTest, ImmediateEndOfStream) {
Initialize(); Initialize();
{ {
SCOPED_TRACE("Preroll()"); SCOPED_TRACE("Preroll()");
renderer_->StartPlayingFrom(base::TimeDelta()); WaitableMessageLoopEvent event;
renderer_->Preroll(base::TimeDelta(), event.GetPipelineStatusCB());
WaitForPendingRead(); WaitForPendingRead();
EXPECT_CALL(*this, OnBufferingStateChange(BUFFERING_HAVE_ENOUGH));
DeliverEndOfStream(); DeliverEndOfStream();
event.RunAndWaitForStatus(PIPELINE_OK);
} }
StartRendering(); StartRendering();
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment