summaryrefslogtreecommitdiffstats
path: root/media/base/pipeline.cc
diff options
context:
space:
mode:
Diffstat (limited to 'media/base/pipeline.cc')
-rw-r--r--media/base/pipeline.cc191
1 files changed, 128 insertions, 63 deletions
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc
index 3cfe296..841a49b 100644
--- a/media/base/pipeline.cc
+++ b/media/base/pipeline.cc
@@ -48,6 +48,8 @@ Pipeline::Pipeline(
audio_ended_(false),
video_ended_(false),
text_ended_(false),
+ audio_buffering_state_(BUFFERING_HAVE_NOTHING),
+ video_buffering_state_(BUFFERING_HAVE_NOTHING),
demuxer_(NULL),
creation_time_(default_tick_clock_.NowTicks()) {
media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
@@ -188,7 +190,7 @@ void Pipeline::SetErrorForTesting(PipelineStatus status) {
}
void Pipeline::SetState(State next_state) {
- if (state_ != kStarted && next_state == kStarted &&
+ if (state_ != kPlaying && next_state == kPlaying &&
!creation_time_.is_null()) {
UMA_HISTOGRAM_TIMES("Media.TimeToPipelineStarted",
default_tick_clock_.NowTicks() - creation_time_);
@@ -211,8 +213,7 @@ const char* Pipeline::GetStateString(State state) {
RETURN_STRING(kInitVideoRenderer);
RETURN_STRING(kInitPrerolling);
RETURN_STRING(kSeeking);
- RETURN_STRING(kStarting);
- RETURN_STRING(kStarted);
+ RETURN_STRING(kPlaying);
RETURN_STRING(kStopping);
RETURN_STRING(kStopped);
}
@@ -249,15 +250,12 @@ Pipeline::State Pipeline::GetNextState() const {
return kInitPrerolling;
case kInitPrerolling:
- return kStarting;
+ return kPlaying;
case kSeeking:
- return kStarting;
+ return kPlaying;
- case kStarting:
- return kStarted;
-
- case kStarted:
+ case kPlaying:
case kStopping:
case kStopped:
break;
@@ -364,11 +362,9 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
// Guard against accidentally clearing |pending_callbacks_| for states that
// use it as well as states that should not be using it.
- //
- // TODO(scherkus): Make every state transition use |pending_callbacks_|.
DCHECK_EQ(pending_callbacks_.get() != NULL,
- (state_ == kInitPrerolling || state_ == kStarting ||
- state_ == kSeeking));
+ (state_ == kInitPrerolling || state_ == kSeeking));
+
pending_callbacks_.reset();
PipelineStatusCB done_cb = base::Bind(
@@ -412,29 +408,24 @@ void Pipeline::StateTransitionTask(PipelineStatus status) {
return DoInitialPreroll(done_cb);
- case kStarting:
- return DoPlay(done_cb);
-
- case kStarted:
- {
- base::AutoLock l(lock_);
- // We use audio stream to update the clock. So if there is such a
- // stream, we pause the clock until we receive a valid timestamp.
- waiting_for_clock_update_ = true;
- if (!audio_renderer_) {
- clock_->SetMaxTime(clock_->Duration());
- StartClockIfWaitingForTimeUpdate_Locked();
- }
- }
-
- DCHECK(!seek_cb_.is_null());
- DCHECK_EQ(status_, PIPELINE_OK);
-
- // Fire canplaythrough immediately after playback begins because of
- // crbug.com/106480.
- // TODO(vrk): set ready state to HaveFutureData when bug above is fixed.
- preroll_completed_cb_.Run();
- return base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
+ case kPlaying:
+ PlaybackRateChangedTask(GetPlaybackRate());
+ VolumeChangedTask(GetVolume());
+
+ // 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
+ // buffering state, which start the clock and renderers and transition
+ // into kPlaying state.
+ //
+ // 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_)
+ BufferingStateChanged(&video_buffering_state_, BUFFERING_HAVE_ENOUGH);
+ return;
case kStopping:
case kStopped:
@@ -469,6 +460,16 @@ void Pipeline::DoInitialPreroll(const PipelineStatusCB& done_cb) {
bound_fns.Push(base::Bind(
&VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
seek_timestamp));
+
+ // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
+ // state callbacks http://crbug.com/144683
+ bound_fns.Push(base::Bind(&VideoRenderer::Play,
+ base::Unretained(video_renderer_.get())));
+ }
+
+ if (text_renderer_) {
+ bound_fns.Push(base::Bind(
+ &TextRenderer::Play, base::Unretained(text_renderer_.get())));
}
pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
@@ -495,10 +496,24 @@ 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())));
+
+ // TODO(scherkus): Remove after VideoRenderer is taught to fire buffering
+ // state callbacks http://crbug.com/144683
+ bound_fns.Push(base::Bind(&Pipeline::BufferingStateChanged,
+ base::Unretained(this),
+ &video_buffering_state_,
+ BUFFERING_HAVE_NOTHING));
}
if (text_renderer_) {
bound_fns.Push(base::Bind(
@@ -520,27 +535,11 @@ void Pipeline::DoSeek(
bound_fns.Push(base::Bind(
&VideoRenderer::Preroll, base::Unretained(video_renderer_.get()),
seek_timestamp));
- }
-
- pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb);
-}
-
-void Pipeline::DoPlay(const PipelineStatusCB& done_cb) {
- DCHECK(task_runner_->BelongsToCurrentThread());
- DCHECK(!pending_callbacks_.get());
- SerialRunner::Queue bound_fns;
-
- PlaybackRateChangedTask(GetPlaybackRate());
- VolumeChangedTask(GetVolume());
-
- if (audio_renderer_) {
- bound_fns.Push(base::Bind(
- &AudioRenderer::Play, base::Unretained(audio_renderer_.get())));
- }
- if (video_renderer_) {
- bound_fns.Push(base::Bind(
- &VideoRenderer::Play, base::Unretained(video_renderer_.get())));
+ // TODO(scherkus): Remove after renderers are taught to fire buffering
+ // state callbacks http://crbug.com/144683
+ bound_fns.Push(base::Bind(&VideoRenderer::Play,
+ base::Unretained(video_renderer_.get())));
}
if (text_renderer_) {
@@ -706,7 +705,7 @@ void Pipeline::PlaybackRateChangedTask(float playback_rate) {
DCHECK(task_runner_->BelongsToCurrentThread());
// Playback rate changes are only carried out while playing.
- if (state_ != kStarting && state_ != kStarted)
+ if (state_ != kPlaying)
return;
{
@@ -724,7 +723,7 @@ void Pipeline::VolumeChangedTask(float volume) {
DCHECK(task_runner_->BelongsToCurrentThread());
// Volume changes are only carried out while playing.
- if (state_ != kStarting && state_ != kStarted)
+ if (state_ != kPlaying)
return;
if (audio_renderer_)
@@ -736,7 +735,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
DCHECK(stop_cb_.is_null());
// Suppress seeking if we're not fully started.
- if (state_ != kStarted) {
+ if (state_ != kPlaying) {
DCHECK(state_ == kStopping || state_ == kStopped)
<< "Receive extra seek in unexpected state: " << state_;
@@ -770,7 +769,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) {
void Pipeline::DoAudioRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread());
- if (state_ != kStarted)
+ if (state_ != kPlaying)
return;
DCHECK(!audio_ended_);
@@ -789,7 +788,7 @@ void Pipeline::DoAudioRendererEnded() {
void Pipeline::DoVideoRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread());
- if (state_ != kStarted)
+ if (state_ != kPlaying)
return;
DCHECK(!video_ended_);
@@ -801,7 +800,7 @@ void Pipeline::DoVideoRendererEnded() {
void Pipeline::DoTextRendererEnded() {
DCHECK(task_runner_->BelongsToCurrentThread());
- if (state_ != kStarted)
+ if (state_ != kPlaying)
return;
DCHECK(!text_ended_);
@@ -888,13 +887,79 @@ void Pipeline::OnAudioUnderflow() {
return;
}
- if (state_ != kStarted)
+ 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 << ", "
+ << " " << new_buffering_state << ") "
+ << (buffering_state == &audio_buffering_state_ ? "audio" : "video");
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ bool was_waiting_for_enough_data = WaitingForEnoughData();
+ *buffering_state = new_buffering_state;
+
+ // Renderer underflowed.
+ if (!was_waiting_for_enough_data && WaitingForEnoughData()) {
+ StartWaitingForEnoughData();
+ return;
+ }
+
+ // Renderer prerolled.
+ if (was_waiting_for_enough_data && !WaitingForEnoughData()) {
+ StartPlayback();
+ return;
+ }
+}
+
+bool Pipeline::WaitingForEnoughData() const {
+ DCHECK(task_runner_->BelongsToCurrentThread());
+ if (state_ != kPlaying)
+ return false;
+ if (audio_renderer_ && audio_buffering_state_ != BUFFERING_HAVE_ENOUGH)
+ return true;
+ if (video_renderer_ && video_buffering_state_ != BUFFERING_HAVE_ENOUGH)
+ return true;
+ return false;
+}
+
+void Pipeline::StartWaitingForEnoughData() {
+ DCHECK_EQ(state_, kPlaying);
+ DCHECK(WaitingForEnoughData());
+
+ if (audio_renderer_)
+ audio_renderer_->Pause();
+
+ base::AutoLock auto_lock(lock_);
+ clock_->Pause();
+}
+
+void Pipeline::StartPlayback() {
+ DCHECK_EQ(state_, kPlaying);
+ DCHECK(!WaitingForEnoughData());
+
+ if (audio_renderer_) {
+ audio_renderer_->Play();
+
+ base::AutoLock auto_lock(lock_);
+ // We use audio stream to update the clock. So if there is such a
+ // stream, we pause the clock until we receive a valid timestamp.
+ waiting_for_clock_update_ = true;
+ } else {
+ base::AutoLock auto_lock(lock_);
+ clock_->SetMaxTime(clock_->Duration());
+ clock_->Play();
+ }
+
+ preroll_completed_cb_.Run();
+ if (!seek_cb_.is_null())
+ base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK);
+}
+
void Pipeline::StartClockIfWaitingForTimeUpdate_Locked() {
lock_.AssertAcquired();
if (!waiting_for_clock_update_)