diff options
Diffstat (limited to 'media/base/pipeline.cc')
-rw-r--r-- | media/base/pipeline.cc | 191 |
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_) |