Commit e27f0305 authored by scherkus@chromium.org's avatar scherkus@chromium.org

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

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@272465 0039d316-1c4b-4281-b951-d872f2087c98
parent 5f66681f
......@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/memory/ref_counted.h"
#include "base/time/time.h"
#include "media/base/buffering_state.h"
#include "media/base/media_export.h"
#include "media/base/pipeline_status.h"
......@@ -21,6 +22,9 @@ class MEDIA_EXPORT AudioRenderer {
// Second parameter is the maximum time value that the clock cannot exceed.
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();
virtual ~AudioRenderer();
......@@ -29,21 +33,19 @@ class MEDIA_EXPORT AudioRenderer {
//
// |statistics_cb| is executed periodically with audio rendering stats.
//
// |underflow_cb| is executed when the renderer runs out of data to pass to
// the audio card during playback. ResumeAfterUnderflow() must be called
// to resume playback. Pause(), Preroll(), or Stop() cancels the underflow
// condition.
//
// |time_cb| is executed whenever time has advanced by way of audio rendering.
//
// |buffering_state_cb| is executed when audio rendering has either ran out of
// data or has enough data to continue playback.
//
// |ended_cb| is executed when audio rendering has reached the end of stream.
//
// |error_cb| is executed if an error was encountered.
virtual void Initialize(DemuxerStream* stream,
const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
const PipelineStatusCB& error_cb) = 0;
......@@ -56,14 +58,16 @@ class MEDIA_EXPORT AudioRenderer {
virtual void StopRendering() = 0;
// 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;
// Start prerolling audio data for samples starting at |time|, executing
// |callback| when completed.
// Starts playback by reading from |stream| and decoding and rendering audio.
// |timestamp| is the media timestamp playback should start rendering from.
//
// Only valid to call after a successful Initialize() or Flush().
virtual void Preroll(base::TimeDelta time,
const PipelineStatusCB& callback) = 0;
virtual void StartPlayingFrom(base::TimeDelta timestamp) = 0;
// Stop all operations in preparation for being deleted, executing |callback|
// when complete.
......@@ -75,9 +79,6 @@ class MEDIA_EXPORT AudioRenderer {
// Sets the output volume.
virtual void SetVolume(float volume) = 0;
// Resumes playback after underflow occurs.
virtual void ResumeAfterUnderflow() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(AudioRenderer);
};
......
......@@ -141,8 +141,8 @@ class MockAudioRenderer : public AudioRenderer {
MOCK_METHOD7(Initialize, void(DemuxerStream* stream,
const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
const PipelineStatusCB& error_cb));
MOCK_METHOD0(StartRendering, void());
......@@ -150,7 +150,7 @@ class MockAudioRenderer : public AudioRenderer {
MOCK_METHOD1(Flush, void(const base::Closure& callback));
MOCK_METHOD1(Stop, void(const base::Closure& callback));
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
MOCK_METHOD2(Preroll, void(base::TimeDelta time, const PipelineStatusCB& cb));
MOCK_METHOD1(StartPlayingFrom, void(base::TimeDelta time));
MOCK_METHOD1(SetVolume, void(float volume));
MOCK_METHOD0(ResumeAfterUnderflow, void());
......
......@@ -414,6 +414,10 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
PlaybackRateChangedTask(GetPlaybackRate());
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
// both those states call Preroll(), which means by time we enter this
// state we've already buffered enough data. Forcefully update the
......@@ -422,11 +426,10 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
//
// TODO(scherkus): Remove after renderers are taught to fire buffering
// state callbacks http://crbug.com/144683
DCHECK(WaitingForEnoughData());
if (audio_renderer_)
BufferingStateChanged(&audio_buffering_state_, BUFFERING_HAVE_ENOUGH);
if (video_renderer_)
if (video_renderer_) {
DCHECK(WaitingForEnoughData());
BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH);
}
return;
case kStopping:
......@@ -453,9 +456,9 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
// Preroll renderers.
if (audio_renderer_) {
bound_fns.Push(base::Bind(
&AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
seek_timestamp));
bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom,
base::Unretained(audio_renderer_.get()),
seek_timestamp));
}
if (video_renderer_) {
......@@ -477,6 +480,14 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& 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(
base::TimeDelta seek_timestamp,
const PipelineStatusCB& done_cb) {
......@@ -494,14 +505,8 @@ void Pipeline::DoSeek(
if (audio_renderer_) {
bound_fns.Push(base::Bind(
&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_) {
bound_fns.Push(base::Bind(
&VideoRenderer::Flush, base::Unretained(video_renderer_.get())));
......@@ -513,6 +518,14 @@ void Pipeline::DoSeek(
&video_buffering_state_,
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_) {
bound_fns.Push(base::Bind(
&TextRenderer::Flush, base::Unretained(text_renderer_.get())));
......@@ -524,9 +537,9 @@ void Pipeline::DoSeek(
// Preroll renderers.
if (audio_renderer_) {
bound_fns.Push(base::Bind(
&AudioRenderer::Preroll, base::Unretained(audio_renderer_.get()),
seek_timestamp));
bound_fns.Push(base::Bind(&AudioRenderer::StartPlayingFrom,
base::Unretained(audio_renderer_.get()),
seek_timestamp));
}
if (video_renderer_) {
......@@ -856,8 +869,9 @@ void Pipeline::InitializeAudioRenderer(const PipelineStatusCB& done_cb) {
demuxer_->GetStream(DemuxerStream::AUDIO),
done_cb,
base::Bind(&Pipeline::OnUpdateStatistics, base::Unretained(this)),
base::Bind(&Pipeline::OnAudioUnderflow, 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::SetError, base::Unretained(this)));
}
......@@ -878,20 +892,6 @@ void Pipeline::InitializeVideoRenderer(const PipelineStatusCB& done_cb) {
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,
BufferingState new_buffering_state) {
DVLOG(1) << __FUNCTION__ << "(" << *buffering_state << ", "
......@@ -903,7 +903,7 @@ void Pipeline::BufferingStateChanged(BufferingState* buffering_state,
// Renderer underflowed.
if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
StartWaitingForEnoughData();
PausePlayback();
return;
}
......@@ -925,10 +925,11 @@ bool Pipeline::WaitingForEnoughData() const {
return false;
}
void Pipeline::StartWaitingForEnoughData() {
void Pipeline::PausePlayback() {
DVLOG(1) << __FUNCTION__;
DCHECK_EQ(state_, kPlaying);
DCHECK(WaitingForEnoughData());
DCHECK(task_runner_->BelongsToCurrentThread());
base::AutoLock auto_lock(lock_);
PauseClockAndStopRendering_Locked();
......@@ -939,6 +940,7 @@ void Pipeline::StartPlayback() {
DCHECK_EQ(state_, kPlaying);
DCHECK_EQ(clock_state_, CLOCK_PAUSED);
DCHECK(!WaitingForEnoughData());
DCHECK(task_runner_->BelongsToCurrentThread());
if (audio_renderer_) {
// We use audio stream to update the clock. So if there is such a
......
......@@ -309,8 +309,6 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
void DoStop(const PipelineStatusCB& done_cb);
void OnStopCompleted(PipelineStatus status);
void OnAudioUnderflow();
// Collection of callback methods and helpers for tracking changes in
// buffering state and transition from paused/underflow states and playing
// states.
......@@ -319,11 +317,11 @@ class MEDIA_EXPORT Pipeline : public DemuxerHost {
// - A waiting to non-waiting transition indicates preroll has completed
// and StartPlayback() should be called
// - A non-waiting to waiting transition indicates underflow has occurred
// and StartWaitingForEnoughData() should be called
// and PausePlayback() should be called
void BufferingStateChanged(BufferingState* buffering_state,
BufferingState new_buffering_state);
bool WaitingForEnoughData() const;
void StartWaitingForEnoughData();
void PausePlayback();
void StartPlayback();
void PauseClockAndStopRendering_Locked();
......
......@@ -51,6 +51,10 @@ ACTION_P2(SetError, pipeline, status) {
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 {
......@@ -183,7 +187,8 @@ class PipelineTest : public ::testing::Test {
// Sets up expectations to allow the audio renderer to initialize.
void InitializeAudioRenderer(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_),
RunCallback<1>(PIPELINE_OK)));
}
......@@ -205,10 +210,9 @@ class PipelineTest : public ::testing::Test {
if (audio_stream_) {
EXPECT_CALL(*audio_renderer_, SetPlaybackRate(0.0f));
EXPECT_CALL(*audio_renderer_, SetVolume(1.0f));
// Startup sequence.
EXPECT_CALL(*audio_renderer_, Preroll(base::TimeDelta(), _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(base::TimeDelta()))
.WillOnce(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*audio_renderer_, StartRendering());
}
EXPECT_CALL(callbacks_, OnPrerollCompleted());
......@@ -254,17 +258,21 @@ class PipelineTest : public ::testing::Test {
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(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(RunClosure<0>());
EXPECT_CALL(*audio_renderer_, Preroll(seek_time, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
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_, SetVolume(_));
EXPECT_CALL(*audio_renderer_, StartRendering());
......@@ -333,6 +341,7 @@ class PipelineTest : public ::testing::Test {
scoped_ptr<StrictMock<MockDemuxerStream> > video_stream_;
scoped_ptr<FakeTextTrackStream> text_stream_;
AudioRenderer::TimeCB audio_time_cb_;
AudioRenderer::BufferingStateCB audio_buffering_state_cb_;
VideoDecoderConfig video_decoder_config_;
PipelineMetadata metadata_;
......@@ -510,7 +519,7 @@ TEST_F(PipelineTest, Seek) {
// Every filter should receive a call to Seek().
base::TimeDelta expected = base::TimeDelta::FromSeconds(2000);
ExpectSeek(expected);
ExpectSeek(expected, false);
DoSeek(expected);
}
......@@ -568,7 +577,7 @@ TEST_F(PipelineTest, GetBufferedTimeRanges) {
EXPECT_EQ(kDuration / 8, pipeline_->GetBufferedTimeRanges().end(0));
base::TimeDelta kSeekTime = kDuration / 2;
ExpectSeek(kSeekTime);
ExpectSeek(kSeekTime, false);
DoSeek(kSeekTime);
EXPECT_FALSE(pipeline_->DidLoadingProgress());
......@@ -670,7 +679,9 @@ TEST_F(PipelineTest, ErrorDuringSeek) {
// Preroll() isn't called as the demuxer errors out first.
EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(RunClosure<0>());
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
EXPECT_CALL(*audio_renderer_, Stop(_))
.WillOnce(RunClosure<0>());
......@@ -724,7 +735,9 @@ TEST_F(PipelineTest, NoMessageDuringTearDownFromError) {
// Seek() isn't called as the demuxer errors out first.
EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(RunClosure<0>());
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
EXPECT_CALL(*audio_renderer_, Stop(_))
.WillOnce(RunClosure<0>());
......@@ -814,9 +827,12 @@ TEST_F(PipelineTest, AudioTimeUpdateDuringSeek) {
EXPECT_CALL(*audio_renderer_, StopRendering());
EXPECT_CALL(*audio_renderer_, Flush(_))
.WillOnce(RunClosure<0>());
EXPECT_CALL(*audio_renderer_, Preroll(seek_time, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
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_, SetVolume(_));
EXPECT_CALL(*audio_renderer_, StartRendering());
......@@ -854,6 +870,28 @@ TEST_F(PipelineTest, DeleteAfterStop) {
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 {
public:
enum TeardownState {
......@@ -966,7 +1004,8 @@ class PipelineTeardownTest : public PipelineTest {
}
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) {
......@@ -992,8 +1031,9 @@ class PipelineTeardownTest : public PipelineTest {
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(*audio_renderer_, StartPlayingFrom(base::TimeDelta()))
.WillOnce(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_ENOUGH));
EXPECT_CALL(*video_renderer_, Preroll(base::TimeDelta(), _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
......@@ -1040,17 +1080,26 @@ class PipelineTeardownTest : public PipelineTest {
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(*audio_renderer_, Flush(_))
.WillOnce(DoAll(SetBufferingState(&audio_buffering_state_cb_,
BUFFERING_HAVE_NOTHING),
RunClosure<0>()));
EXPECT_CALL(*video_renderer_, Flush(_)).WillOnce(RunClosure<0>());
if (state == kSeeking) {
......@@ -1072,20 +1121,18 @@ class PipelineTeardownTest : public PipelineTest {
if (state == kPrerolling) {
if (stop_or_error == kStop) {
EXPECT_CALL(*audio_renderer_, Preroll(_, _))
.WillOnce(DoAll(Stop(pipeline_.get(), stop_cb),
RunCallback<1>(PIPELINE_OK)));
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(_))
.WillOnce(Stop(pipeline_.get(), stop_cb));
} else {
status = PIPELINE_ERROR_READ;
EXPECT_CALL(*audio_renderer_, Preroll(_, _))
.WillOnce(RunCallback<1>(status));
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(_))
.WillOnce(SetError(pipeline_.get(), status));
}
return status;
}
EXPECT_CALL(*audio_renderer_, Preroll(_, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
EXPECT_CALL(*audio_renderer_, StartPlayingFrom(_));
EXPECT_CALL(*video_renderer_, Preroll(_, _))
.WillOnce(RunCallback<1>(PIPELINE_OK));
......
This diff is collapsed.
......@@ -68,8 +68,8 @@ class MEDIA_EXPORT AudioRendererImpl
virtual void Initialize(DemuxerStream* stream,
const PipelineStatusCB& init_cb,
const StatisticsCB& statistics_cb,
const base::Closure& underflow_cb,
const TimeCB& time_cb,
const BufferingStateCB& buffering_state_cb,
const base::Closure& ended_cb,
const PipelineStatusCB& error_cb) OVERRIDE;
virtual void StartRendering() OVERRIDE;
......@@ -77,9 +77,7 @@ class MEDIA_EXPORT AudioRendererImpl
virtual void Flush(const base::Closure& callback) OVERRIDE;
virtual void Stop(const base::Closure& callback) OVERRIDE;
virtual void SetPlaybackRate(float rate) OVERRIDE;
virtual void Preroll(base::TimeDelta time,
const PipelineStatusCB& cb) OVERRIDE;
virtual void ResumeAfterUnderflow() OVERRIDE;
virtual void StartPlayingFrom(base::TimeDelta timestamp) OVERRIDE;
virtual void SetVolume(float volume) OVERRIDE;
// Allows injection of a custom time callback for non-realtime testing.
......@@ -104,25 +102,17 @@ class MEDIA_EXPORT AudioRendererImpl
// |
// V Decoders reset
// kFlushed <------------------ kFlushing
// | Preroll() ^
// | StartPlayingFrom() ^
// | |
// V | Flush()
// kPrerolling ----------------> kPlaying ---------.
// Enough data buffered ^ | Not enough data
// | | buffered
// Enough data buffered | V
// kRebuffering <--- kUnderflow
// ResumeAfterUnderflow()
// | | Flush()
// `---------> kPlaying --------'
enum State {
kUninitialized,
kInitializing,
kFlushing,
kFlushed,
kPrerolling,
kPlaying,
kStopped,
kUnderflow,
kRebuffering,
};
// Callback from the audio decoder delivering decoded audio samples.
......@@ -131,7 +121,7 @@ class MEDIA_EXPORT AudioRendererImpl
// Handles buffers that come out of |splicer_|.
// Returns true if more buffers are needed.
bool HandleSplicerBuffer(const scoped_refptr<AudioBuffer>& buffer);
bool HandleSplicerBuffer_Locked(const scoped_refptr<AudioBuffer>& buffer);
// Helper functions for AudioDecoder::Status values passed to
// DecodedAudioReady().
......@@ -177,10 +167,9 @@ class MEDIA_EXPORT AudioRendererImpl
bool CanRead_Locked();
void ChangeState_Locked(State new_state);
// Returns true if the data in the buffer is all before
// |preroll_timestamp_|. This can only return true while
// in the kPrerolling state.
bool IsBeforePrerollTime(const scoped_refptr<AudioBuffer>& buffer);
// Returns true if the data in the buffer is all before |start_timestamp_|.
// This can only return true while in the kPlaying state.
bool IsBeforeStartTime(const scoped_refptr<AudioBuffer>& buffer);
// Called upon AudioBufferStream initialization, or failure thereof (indicated
// by the value of |success|).
......@@ -203,6 +192,9 @@ class MEDIA_EXPORT AudioRendererImpl
// Called by the AudioBufferStream when a config change occurs.
void OnConfigChange();
// Updates |buffering_state_| and fires |buffering_state_cb_|.
void SetBufferingState_Locked(BufferingState buffering_state);
scoped_refptr<base::SingleThreadTaskRunner> task_runner_;
scoped_ptr<AudioSplicer> splicer_;
......@@ -226,17 +218,14 @@ class MEDIA_EXPORT AudioRendererImpl
// Callbacks provided during Initialize().
PipelineStatusCB init_cb_;
base::Closure underflow_cb_;
TimeCB time_cb_;
BufferingStateCB buffering_state_cb_;
base::Closure ended_cb_;
PipelineStatusCB error_cb_;
// Callback provided to Flush().
base::Closure flush_cb_;
// Callback provided to Preroll().
PipelineStatusCB preroll_cb_;
// Typically calls base::TimeTicks::Now() but can be overridden by a test.
NowCB now_cb_;
......@@ -250,6 +239,8 @@ class MEDIA_EXPORT AudioRendererImpl
// Simple state tracking variable.
State state_;
BufferingState buffering_state_;
// Keep track of whether or not the sink is playing and whether we should be
// rendering.
bool rendering_;
......@@ -264,7 +255,7 @@ class MEDIA_EXPORT AudioRendererImpl
scoped_ptr<AudioClock> audio_clock_;
base::TimeDelta preroll_timestamp_;
base::TimeDelta start_timestamp_;
// We're supposed to know amount of audio data OS or hardware buffered, but
// that is not always so -- on my Linux box
......@@ -283,10 +274,6 @@ class MEDIA_EXPORT AudioRendererImpl
base::TimeTicks earliest_end_time_;
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_|. ----------------------
// NOTE: Weak pointers must be invalidated before all other member variables.
......
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