| Index: media/renderers/renderer_impl.cc
|
| diff --git a/media/renderers/renderer_impl.cc b/media/renderers/renderer_impl.cc
|
| index cc1504529eee3f0dbef0eef70fb56a63306f9e05..eb5710d2eea5f40c4f634d063a62e29f9547c162 100644
|
| --- a/media/renderers/renderer_impl.cc
|
| +++ b/media/renderers/renderer_impl.cc
|
| @@ -80,6 +80,8 @@ RendererImpl::RendererImpl(
|
| task_runner_(task_runner),
|
| audio_renderer_(std::move(audio_renderer)),
|
| video_renderer_(std::move(video_renderer)),
|
| + current_audio_stream_(nullptr),
|
| + current_video_stream_(nullptr),
|
| time_source_(NULL),
|
| time_ticking_(false),
|
| playback_rate_(0.0),
|
| @@ -197,10 +199,19 @@ void RendererImpl::Flush(const base::Closure& flush_cb) {
|
| flush_cb_ = flush_cb;
|
| state_ = STATE_FLUSHING;
|
|
|
| - if (time_ticking_)
|
| - PausePlayback();
|
| + // If we are currently handling a media stream status change, then postpone
|
| + // Flush until after that's done (because stream status changes also flush
|
| + // audio_renderer_/video_renderer_ and they need to be restarted before they
|
| + // can be flushed again). OnStreamRestartCompleted will resume Flush
|
| + // processing after audio/video restart has completed and there are no other
|
| + // pending stream status changes.
|
| + if (restarting_audio_ || restarting_video_) {
|
| + pending_actions_.push_back(
|
| + base::Bind(&RendererImpl::FlushInternal, weak_this_));
|
| + return;
|
| + }
|
|
|
| - FlushAudioRenderer();
|
| + FlushInternal();
|
| }
|
|
|
| void RendererImpl::StartPlayingFrom(base::TimeDelta time) {
|
| @@ -221,64 +232,6 @@ void RendererImpl::StartPlayingFrom(base::TimeDelta time) {
|
| video_renderer_->StartPlayingFrom(time);
|
| }
|
|
|
| -void RendererImpl::OnStreamStatusChanged(DemuxerStream* stream,
|
| - bool enabled,
|
| - base::TimeDelta time) {
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - DCHECK(stream);
|
| - bool video = (stream->type() == DemuxerStream::VIDEO);
|
| - DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream
|
| - << " enabled=" << enabled << " time=" << time.InSecondsF();
|
| - if ((state_ != STATE_PLAYING) || (audio_ended_ && video_ended_))
|
| - return;
|
| - if (restarting_audio_ || restarting_video_) {
|
| - DVLOG(3) << __func__ << ": postponed stream " << stream
|
| - << " status change handling.";
|
| - pending_stream_status_notifications_.push_back(
|
| - base::Bind(&RendererImpl::OnStreamStatusChanged, weak_this_, stream,
|
| - enabled, time));
|
| - return;
|
| - }
|
| - if (stream->type() == DemuxerStream::VIDEO) {
|
| - DCHECK(video_renderer_);
|
| - restarting_video_ = true;
|
| - video_renderer_->Flush(
|
| - base::Bind(&RendererImpl::RestartVideoRenderer, weak_this_, time));
|
| - } else if (stream->type() == DemuxerStream::AUDIO) {
|
| - DCHECK(audio_renderer_);
|
| - DCHECK(time_source_);
|
| - restarting_audio_ = true;
|
| - // Stop ticking (transition into paused state) in audio renderer before
|
| - // calling Flush, since after Flush we are going to restart playback by
|
| - // calling audio renderer StartPlaying which would fail in playing state.
|
| - if (time_ticking_) {
|
| - time_ticking_ = false;
|
| - time_source_->StopTicking();
|
| - }
|
| - audio_renderer_->Flush(
|
| - base::Bind(&RendererImpl::RestartAudioRenderer, weak_this_, time));
|
| - }
|
| -}
|
| -
|
| -void RendererImpl::RestartVideoRenderer(base::TimeDelta time) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - DCHECK(video_renderer_);
|
| - DCHECK_EQ(state_, STATE_PLAYING);
|
| - video_ended_ = false;
|
| - video_renderer_->StartPlayingFrom(time);
|
| -}
|
| -
|
| -void RendererImpl::RestartAudioRenderer(base::TimeDelta time) {
|
| - DVLOG(3) << __func__;
|
| - DCHECK(task_runner_->BelongsToCurrentThread());
|
| - DCHECK_EQ(state_, STATE_PLAYING);
|
| - DCHECK(time_source_);
|
| - DCHECK(audio_renderer_);
|
| - audio_ended_ = false;
|
| - audio_renderer_->StartPlaying();
|
| -}
|
| -
|
| void RendererImpl::SetPlaybackRate(double playback_rate) {
|
| DVLOG(1) << __func__ << "(" << playback_rate << ")";
|
| DCHECK(task_runner_->BelongsToCurrentThread());
|
| @@ -400,6 +353,8 @@ void RendererImpl::InitializeAudioRenderer() {
|
| return;
|
| }
|
|
|
| + current_audio_stream_ = audio_stream;
|
| +
|
| audio_renderer_client_.reset(
|
| new RendererClientInternal(DemuxerStream::AUDIO, this));
|
| // Note: After the initialization of a renderer, error events from it may
|
| @@ -448,6 +403,8 @@ void RendererImpl::InitializeVideoRenderer() {
|
| return;
|
| }
|
|
|
| + current_video_stream_ = video_stream;
|
| +
|
| video_renderer_client_.reset(
|
| new RendererClientInternal(DemuxerStream::VIDEO, this));
|
| video_renderer_->Initialize(
|
| @@ -493,6 +450,18 @@ void RendererImpl::OnVideoRendererInitializeDone(PipelineStatus status) {
|
| FinishInitialization(PIPELINE_OK);
|
| }
|
|
|
| +void RendererImpl::FlushInternal() {
|
| + DVLOG(1) << __func__;
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK_EQ(state_, STATE_FLUSHING);
|
| + DCHECK(!flush_cb_.is_null());
|
| +
|
| + if (time_ticking_)
|
| + PausePlayback();
|
| +
|
| + FlushAudioRenderer();
|
| +}
|
| +
|
| void RendererImpl::FlushAudioRenderer() {
|
| DVLOG(1) << __func__;
|
| DCHECK(task_runner_->BelongsToCurrentThread());
|
| @@ -560,6 +529,168 @@ void RendererImpl::OnVideoRendererFlushDone() {
|
| video_ended_ = false;
|
| state_ = STATE_FLUSHED;
|
| base::ResetAndReturn(&flush_cb_).Run();
|
| +
|
| + if (!pending_actions_.empty()) {
|
| + base::Closure closure = pending_actions_.front();
|
| + pending_actions_.pop_front();
|
| + closure.Run();
|
| + }
|
| +}
|
| +
|
| +void RendererImpl::OnStreamStatusChanged(DemuxerStream* stream,
|
| + bool enabled,
|
| + base::TimeDelta time) {
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK(stream);
|
| + bool video = (stream->type() == DemuxerStream::VIDEO);
|
| + DVLOG(1) << __func__ << (video ? " video" : " audio") << " stream=" << stream
|
| + << " enabled=" << enabled << " time=" << time.InSecondsF();
|
| +
|
| + if ((state_ != STATE_PLAYING && state_ != STATE_FLUSHING &&
|
| + state_ != STATE_FLUSHED) ||
|
| + (audio_ended_ && video_ended_))
|
| + return;
|
| +
|
| + if (restarting_audio_ || restarting_video_ || state_ == STATE_FLUSHING) {
|
| + DVLOG(3) << __func__ << ": postponed stream " << stream
|
| + << " status change handling.";
|
| + pending_actions_.push_back(base::Bind(&RendererImpl::OnStreamStatusChanged,
|
| + weak_this_, stream, enabled, time));
|
| + return;
|
| + }
|
| +
|
| + DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHED);
|
| + if (stream->type() == DemuxerStream::VIDEO) {
|
| + DCHECK(video_renderer_);
|
| + restarting_video_ = true;
|
| + base::Closure handle_track_status_cb =
|
| + base::Bind(stream == current_video_stream_
|
| + ? &RendererImpl::RestartVideoRenderer
|
| + : &RendererImpl::ReinitializeVideoRenderer,
|
| + weak_this_, stream, time);
|
| + if (state_ == STATE_FLUSHED)
|
| + handle_track_status_cb.Run();
|
| + else
|
| + video_renderer_->Flush(handle_track_status_cb);
|
| + } else if (stream->type() == DemuxerStream::AUDIO) {
|
| + DCHECK(audio_renderer_);
|
| + DCHECK(time_source_);
|
| + restarting_audio_ = true;
|
| + base::Closure handle_track_status_cb =
|
| + base::Bind(stream == current_audio_stream_
|
| + ? &RendererImpl::RestartAudioRenderer
|
| + : &RendererImpl::ReinitializeAudioRenderer,
|
| + weak_this_, stream, time);
|
| + if (state_ == STATE_FLUSHED) {
|
| + handle_track_status_cb.Run();
|
| + return;
|
| + }
|
| + // Stop ticking (transition into paused state) in audio renderer before
|
| + // calling Flush, since after Flush we are going to restart playback by
|
| + // calling audio renderer StartPlaying which would fail in playing state.
|
| + if (time_ticking_) {
|
| + time_ticking_ = false;
|
| + time_source_->StopTicking();
|
| + }
|
| + audio_renderer_->Flush(handle_track_status_cb);
|
| + }
|
| +}
|
| +
|
| +void RendererImpl::ReinitializeAudioRenderer(DemuxerStream* stream,
|
| + base::TimeDelta time) {
|
| + DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK_NE(stream, current_audio_stream_);
|
| +
|
| + current_audio_stream_ = stream;
|
| + audio_renderer_->Initialize(
|
| + stream, cdm_context_, audio_renderer_client_.get(),
|
| + base::Bind(&RendererImpl::OnAudioRendererReinitialized, weak_this_,
|
| + stream, time));
|
| +}
|
| +
|
| +void RendererImpl::OnAudioRendererReinitialized(DemuxerStream* stream,
|
| + base::TimeDelta time,
|
| + PipelineStatus status) {
|
| + DVLOG(2) << __func__ << ": status=" << status;
|
| + DCHECK_EQ(stream, current_audio_stream_);
|
| +
|
| + if (status != PIPELINE_OK) {
|
| + OnError(status);
|
| + return;
|
| + }
|
| + RestartAudioRenderer(stream, time);
|
| +}
|
| +
|
| +void RendererImpl::ReinitializeVideoRenderer(DemuxerStream* stream,
|
| + base::TimeDelta time) {
|
| + DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK_NE(stream, current_video_stream_);
|
| +
|
| + current_video_stream_ = stream;
|
| + video_renderer_->OnTimeStopped();
|
| + video_renderer_->Initialize(
|
| + stream, cdm_context_, video_renderer_client_.get(),
|
| + base::Bind(&RendererImpl::GetWallClockTimes, base::Unretained(this)),
|
| + base::Bind(&RendererImpl::OnVideoRendererReinitialized, weak_this_,
|
| + stream, time));
|
| +}
|
| +
|
| +void RendererImpl::OnVideoRendererReinitialized(DemuxerStream* stream,
|
| + base::TimeDelta time,
|
| + PipelineStatus status) {
|
| + DVLOG(2) << __func__ << ": status=" << status;
|
| + DCHECK_EQ(stream, current_video_stream_);
|
| +
|
| + if (status != PIPELINE_OK) {
|
| + OnError(status);
|
| + return;
|
| + }
|
| + RestartVideoRenderer(stream, time);
|
| +}
|
| +
|
| +void RendererImpl::RestartAudioRenderer(DemuxerStream* stream,
|
| + base::TimeDelta time) {
|
| + DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHED ||
|
| + state_ == STATE_FLUSHING);
|
| + DCHECK(time_source_);
|
| + DCHECK(audio_renderer_);
|
| + DCHECK_EQ(stream, current_audio_stream_);
|
| +
|
| + audio_ended_ = false;
|
| + if (state_ == STATE_FLUSHED) {
|
| + // If we are in the FLUSHED state, then we are done. The audio renderer will
|
| + // be restarted by a subsequent RendererImpl::StartPlayingFrom call.
|
| + OnStreamRestartCompleted();
|
| + } else {
|
| + // Stream restart will be completed when the audio renderer decodes enough
|
| + // data and reports HAVE_ENOUGH to HandleRestartedStreamBufferingChanges.
|
| + audio_renderer_->StartPlaying();
|
| + }
|
| +}
|
| +
|
| +void RendererImpl::RestartVideoRenderer(DemuxerStream* stream,
|
| + base::TimeDelta time) {
|
| + DVLOG(2) << __func__ << " stream=" << stream << " time=" << time.InSecondsF();
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| + DCHECK(video_renderer_);
|
| + DCHECK(state_ == STATE_PLAYING || state_ == STATE_FLUSHED ||
|
| + state_ == STATE_FLUSHING);
|
| + DCHECK_EQ(stream, current_video_stream_);
|
| +
|
| + video_ended_ = false;
|
| + if (state_ == STATE_FLUSHED) {
|
| + // If we are in the FLUSHED state, then we are done. The video renderer will
|
| + // be restarted by a subsequent RendererImpl::StartPlayingFrom call.
|
| + OnStreamRestartCompleted();
|
| + } else {
|
| + // Stream restart will be completed when the video renderer decodes enough
|
| + // data and reports HAVE_ENOUGH to HandleRestartedStreamBufferingChanges.
|
| + video_renderer_->StartPlayingFrom(time);
|
| + }
|
| }
|
|
|
| void RendererImpl::OnStatisticsUpdate(const PipelineStatistics& stats) {
|
| @@ -634,12 +765,16 @@ bool RendererImpl::HandleRestartedStreamBufferingChanges(
|
| }
|
|
|
| void RendererImpl::OnStreamRestartCompleted() {
|
| + DVLOG(3) << __func__ << " restarting_audio_=" << restarting_audio_
|
| + << " restarting_video_=" << restarting_video_;
|
| + DCHECK(task_runner_->BelongsToCurrentThread());
|
| DCHECK(restarting_audio_ || restarting_video_);
|
| restarting_audio_ = false;
|
| restarting_video_ = false;
|
| - if (!pending_stream_status_notifications_.empty()) {
|
| - pending_stream_status_notifications_.front().Run();
|
| - pending_stream_status_notifications_.pop_front();
|
| + if (!pending_actions_.empty()) {
|
| + base::Closure closure = pending_actions_.front();
|
| + pending_actions_.pop_front();
|
| + closure.Run();
|
| }
|
| }
|
|
|
|
|