| Index: media/base/pipeline_impl.cc
|
| diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
|
| index 38a835b17af3248a968cb8650ca0b88ea908389c..bb23fd26e1e515ffa5e3dc06116138d8258eac83 100644
|
| --- a/media/base/pipeline_impl.cc
|
| +++ b/media/base/pipeline_impl.cc
|
| @@ -29,9 +29,21 @@
|
| #include "media/base/timestamp_constants.h"
|
| #include "media/base/video_decoder_config.h"
|
|
|
| +namespace {
|
| static const double kDefaultPlaybackRate = 0.0;
|
| static const float kDefaultVolume = 1.0f;
|
|
|
| +base::TimeDelta CurrentMediaTime(base::TimeDelta min_time,
|
| + base::TimeDelta max_time,
|
| + base::TimeTicks ref_time,
|
| + base::TimeTicks curr_time,
|
| + double playback_rate) {
|
| + DCHECK(!ref_time.is_null());
|
| + return std::min(min_time + (curr_time - ref_time) * playback_rate, max_time);
|
| +}
|
| +
|
| +} // namespace
|
| +
|
| namespace media {
|
|
|
| class PipelineImpl::RendererWrapper : public DemuxerHost,
|
| @@ -51,7 +63,9 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
|
| void Resume(std::unique_ptr<Renderer> renderer, base::TimeDelta time);
|
| void SetPlaybackRate(double playback_rate);
|
| void SetVolume(float volume);
|
| - base::TimeDelta GetMediaTime() const;
|
| + void GetMediaTimeExtents(base::TimeDelta* time_min,
|
| + base::TimeDelta* time_max,
|
| + base::TimeTicks* time_reference) const;
|
| Ranges<base::TimeDelta> GetBufferedTimeRanges() const;
|
| bool DidLoadingProgress();
|
| PipelineStatistics GetStatistics() const;
|
| @@ -78,10 +92,6 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
|
| // frequently than needed. Posting all those notifications to the main thread
|
| // causes performance issues: crbug.com/619975.
|
| struct SharedState {
|
| - // TODO(scherkus): Enforce that Renderer is only called on a single thread,
|
| - // even for accessing media time http://crbug.com/370634
|
| - std::unique_ptr<Renderer> renderer;
|
| -
|
| // True when OnBufferedTimeRangesChanged() has been called more recently
|
| // than DidLoadingProgress().
|
| bool did_loading_progress = false;
|
| @@ -92,9 +102,10 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
|
| // Accumulated statistics reported by the renderer.
|
| PipelineStatistics statistics;
|
|
|
| - // The media timestamp to return while the pipeline is suspended.
|
| - // Otherwise set to kNoTimestamp.
|
| - base::TimeDelta suspend_timestamp = kNoTimestamp;
|
| + // Media time extents reported by the renderer.
|
| + base::TimeDelta media_time_min;
|
| + base::TimeDelta media_time_max;
|
| + base::TimeTicks media_time_reference;
|
| };
|
|
|
| // DemuxerHost implementaion.
|
| @@ -113,6 +124,9 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
|
| void OnWaitingForDecryptionKey() final;
|
| void OnVideoNaturalSizeChange(const gfx::Size& size) final;
|
| void OnVideoOpacityChange(bool opaque) final;
|
| + void OnTimeUpdate(base::TimeDelta curr_time,
|
| + base::TimeDelta max_time,
|
| + base::TimeTicks capture_time) final;
|
|
|
| // TextRenderer tasks and notifications.
|
| void OnTextRendererEnded();
|
| @@ -133,7 +147,6 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
|
| void CompleteSuspend(PipelineStatus status);
|
| void InitializeDemuxer(const PipelineStatusCB& done_cb);
|
| void InitializeRenderer(const PipelineStatusCB& done_cb);
|
| - void DestroyRenderer();
|
| void ReportMetadata();
|
|
|
| const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_;
|
| @@ -142,6 +155,7 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
|
|
|
| base::WeakPtr<PipelineImpl> weak_pipeline_;
|
| Demuxer* demuxer_;
|
| + std::unique_ptr<Renderer> renderer_;
|
| std::unique_ptr<TextRenderer> text_renderer_;
|
| double playback_rate_;
|
| float volume_;
|
| @@ -216,16 +230,13 @@ void PipelineImpl::RendererWrapper::Start(
|
| SetState(kStarting);
|
|
|
| DCHECK(!demuxer_);
|
| - DCHECK(!shared_state_.renderer);
|
| + DCHECK(!renderer_);
|
| DCHECK(!text_renderer_);
|
| DCHECK(!renderer_ended_);
|
| DCHECK(!text_renderer_ended_);
|
| DCHECK(!weak_pipeline_);
|
| demuxer_ = demuxer;
|
| - {
|
| - base::AutoLock auto_lock(shared_state_lock_);
|
| - shared_state_.renderer = std::move(renderer);
|
| - }
|
| + renderer_ = std::move(renderer);
|
| text_renderer_ = std::move(text_renderer);
|
| if (text_renderer_) {
|
| text_renderer_->Initialize(
|
| @@ -270,9 +281,8 @@ void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) {
|
| // tasks.
|
| pending_callbacks_.reset();
|
|
|
| - DestroyRenderer();
|
| + renderer_.reset();
|
| text_renderer_.reset();
|
| -
|
| if (demuxer_) {
|
| demuxer_->Stop();
|
| demuxer_ = NULL;
|
| @@ -300,8 +310,6 @@ void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
|
| return;
|
| }
|
|
|
| - base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime());
|
| -
|
| SetState(kSeeking);
|
| renderer_ended_ = false;
|
| text_renderer_ended_ = false;
|
| @@ -317,9 +325,9 @@ void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
|
| }
|
|
|
| // Flush.
|
| - DCHECK(shared_state_.renderer);
|
| - bound_fns.Push(base::Bind(&Renderer::Flush,
|
| - base::Unretained(shared_state_.renderer.get())));
|
| + DCHECK(renderer_);
|
| + bound_fns.Push(
|
| + base::Bind(&Renderer::Flush, base::Unretained(renderer_.get())));
|
|
|
| if (text_renderer_) {
|
| bound_fns.Push(base::Bind(&TextRenderer::Flush,
|
| @@ -327,13 +335,14 @@ void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
|
| }
|
|
|
| // Seek demuxer.
|
| + base::TimeDelta seek_time = std::max(time, demuxer_->GetStartTime());
|
| bound_fns.Push(
|
| - base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp));
|
| + base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_time));
|
|
|
| // Run tasks.
|
| pending_callbacks_ = SerialRunner::Run(
|
| bound_fns,
|
| - base::Bind(&RendererWrapper::CompleteSeek, weak_this_, seek_timestamp));
|
| + base::Bind(&RendererWrapper::CompleteSeek, weak_this_, seek_time));
|
| }
|
|
|
| void PipelineImpl::RendererWrapper::Suspend() {
|
| @@ -346,18 +355,20 @@ void PipelineImpl::RendererWrapper::Suspend() {
|
| OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
|
| return;
|
| }
|
| - DCHECK(shared_state_.renderer);
|
| + DCHECK(renderer_);
|
| DCHECK(!pending_callbacks_.get());
|
|
|
| SetState(kSuspending);
|
|
|
| // Freeze playback and record the media time before flushing. (Flushing clears
|
| // the value.)
|
| - shared_state_.renderer->SetPlaybackRate(0.0);
|
| + renderer_->SetPlaybackRate(0.0);
|
| + base::TimeDelta media_time = renderer_->GetMediaTime();
|
| {
|
| base::AutoLock auto_lock(shared_state_lock_);
|
| - shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime();
|
| - DCHECK(shared_state_.suspend_timestamp != kNoTimestamp);
|
| + shared_state_.media_time_min = media_time;
|
| + shared_state_.media_time_max = media_time;
|
| + shared_state_.media_time_reference = base::TimeTicks();
|
| }
|
|
|
| // Queue the asynchronous actions required to stop playback.
|
| @@ -368,8 +379,7 @@ void PipelineImpl::RendererWrapper::Suspend() {
|
| base::Unretained(text_renderer_.get())));
|
| }
|
|
|
| - fns.Push(base::Bind(&Renderer::Flush,
|
| - base::Unretained(shared_state_.renderer.get())));
|
| + fns.Push(base::Bind(&Renderer::Flush, base::Unretained(renderer_.get())));
|
|
|
| if (text_renderer_) {
|
| fns.Push(base::Bind(&TextRenderer::Flush,
|
| @@ -391,16 +401,12 @@ void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer,
|
| OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
|
| return;
|
| }
|
| - DCHECK(!shared_state_.renderer);
|
| + DCHECK(!renderer_);
|
| DCHECK(!pending_callbacks_.get());
|
|
|
| SetState(kResuming);
|
|
|
| - {
|
| - base::AutoLock auto_lock(shared_state_lock_);
|
| - shared_state_.renderer = std::move(renderer);
|
| - }
|
| -
|
| + renderer_ = std::move(renderer);
|
| renderer_ended_ = false;
|
| text_renderer_ended_ = false;
|
| base::TimeDelta start_timestamp =
|
| @@ -422,9 +428,18 @@ void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer,
|
| void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) {
|
| DCHECK(media_task_runner_->BelongsToCurrentThread());
|
|
|
| + if (!shared_state_.media_time_reference.is_null()) {
|
| + base::AutoLock auto_lock(shared_state_lock_);
|
| + base::TimeTicks now_time = base::TimeTicks::Now();
|
| + shared_state_.media_time_min = CurrentMediaTime(
|
| + shared_state_.media_time_min, shared_state_.media_time_max,
|
| + shared_state_.media_time_reference, now_time, playback_rate_);
|
| + shared_state_.media_time_reference = now_time;
|
| + }
|
| +
|
| playback_rate_ = playback_rate;
|
| if (state_ == kPlaying)
|
| - shared_state_.renderer->SetPlaybackRate(playback_rate_);
|
| + renderer_->SetPlaybackRate(playback_rate_);
|
| }
|
|
|
| void PipelineImpl::RendererWrapper::SetVolume(float volume) {
|
| @@ -432,17 +447,18 @@ void PipelineImpl::RendererWrapper::SetVolume(float volume) {
|
|
|
| volume_ = volume;
|
| if (state_ == kPlaying)
|
| - shared_state_.renderer->SetVolume(volume_);
|
| + renderer_->SetVolume(volume_);
|
| }
|
|
|
| -base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() const {
|
| - DCHECK(main_task_runner_->BelongsToCurrentThread());
|
| -
|
| +void PipelineImpl::RendererWrapper::GetMediaTimeExtents(
|
| + base::TimeDelta* time_min,
|
| + base::TimeDelta* time_max,
|
| + base::TimeTicks* time_reference) const {
|
| base::AutoLock auto_lock(shared_state_lock_);
|
| - if (shared_state_.suspend_timestamp != kNoTimestamp)
|
| - return shared_state_.suspend_timestamp;
|
| - return shared_state_.renderer ? shared_state_.renderer->GetMediaTime()
|
| - : base::TimeDelta();
|
| +
|
| + *time_min = shared_state_.media_time_min;
|
| + *time_max = shared_state_.media_time_max;
|
| + *time_reference = shared_state_.media_time_reference;
|
| }
|
|
|
| Ranges<base::TimeDelta> PipelineImpl::RendererWrapper::GetBufferedTimeRanges()
|
| @@ -474,15 +490,15 @@ void PipelineImpl::RendererWrapper::SetCdm(
|
| const CdmAttachedCB& cdm_attached_cb) {
|
| DCHECK(media_task_runner_->BelongsToCurrentThread());
|
|
|
| - if (!shared_state_.renderer) {
|
| + if (!renderer_) {
|
| cdm_context_ = cdm_context;
|
| cdm_attached_cb.Run(true);
|
| return;
|
| }
|
|
|
| - shared_state_.renderer->SetCdm(
|
| - cdm_context, base::Bind(&RendererWrapper::OnCdmAttached, weak_this_,
|
| - cdm_attached_cb, cdm_context));
|
| + renderer_->SetCdm(cdm_context,
|
| + base::Bind(&RendererWrapper::OnCdmAttached, weak_this_,
|
| + cdm_attached_cb, cdm_context));
|
| }
|
|
|
| void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged(
|
| @@ -582,11 +598,10 @@ void PipelineImpl::RendererWrapper::OnEnabledAudioTracksChanged(
|
| }
|
|
|
| DCHECK(demuxer_);
|
| - DCHECK(shared_state_.renderer);
|
| + DCHECK(renderer_);
|
|
|
| - base::TimeDelta currTime = (state_ == kPlaying)
|
| - ? shared_state_.renderer->GetMediaTime()
|
| - : demuxer_->GetStartTime();
|
| + base::TimeDelta currTime = (state_ == kPlaying) ? renderer_->GetMediaTime()
|
| + : demuxer_->GetStartTime();
|
| demuxer_->OnEnabledAudioTracksChanged(enabledTrackIds, currTime);
|
| }
|
|
|
| @@ -601,11 +616,10 @@ void PipelineImpl::RendererWrapper::OnSelectedVideoTrackChanged(
|
| }
|
|
|
| DCHECK(demuxer_);
|
| - DCHECK(shared_state_.renderer);
|
| + DCHECK(renderer_);
|
|
|
| - base::TimeDelta currTime = (state_ == kPlaying)
|
| - ? shared_state_.renderer->GetMediaTime()
|
| - : demuxer_->GetStartTime();
|
| + base::TimeDelta currTime = (state_ == kPlaying) ? renderer_->GetMediaTime()
|
| + : demuxer_->GetStartTime();
|
| demuxer_->OnSelectedVideoTrackChanged(selectedTrackId, currTime);
|
| }
|
|
|
| @@ -658,6 +672,19 @@ void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) {
|
| base::Bind(&PipelineImpl::OnVideoOpacityChange, weak_pipeline_, opaque));
|
| }
|
|
|
| +void PipelineImpl::RendererWrapper::OnTimeUpdate(base::TimeDelta curr_time,
|
| + base::TimeDelta max_time,
|
| + base::TimeTicks capture_time) {
|
| + DVLOG(4) << __func__ << "(" << curr_time.InMicroseconds() << ", "
|
| + << max_time.InMicroseconds() << ", " << capture_time << ")";
|
| + DCHECK(media_task_runner_->BelongsToCurrentThread());
|
| +
|
| + base::AutoLock auto_lock(shared_state_lock_);
|
| + shared_state_.media_time_min = curr_time;
|
| + shared_state_.media_time_max = max_time;
|
| + shared_state_.media_time_reference = capture_time;
|
| +}
|
| +
|
| void PipelineImpl::RendererWrapper::OnTextRendererEnded() {
|
| DCHECK(media_task_runner_->BelongsToCurrentThread());
|
| media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
|
| @@ -727,7 +754,7 @@ void PipelineImpl::RendererWrapper::OnCdmAttached(
|
| void PipelineImpl::RendererWrapper::CheckPlaybackEnded() {
|
| DCHECK(media_task_runner_->BelongsToCurrentThread());
|
|
|
| - if (shared_state_.renderer && !renderer_ended_)
|
| + if (renderer_ && !renderer_ended_)
|
| return;
|
|
|
| if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
|
| @@ -760,18 +787,20 @@ void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time,
|
| return;
|
| }
|
|
|
| - shared_state_.renderer->StartPlayingFrom(
|
| - std::max(seek_time, demuxer_->GetStartTime()));
|
| - {
|
| - base::AutoLock auto_lock(shared_state_lock_);
|
| - shared_state_.suspend_timestamp = kNoTimestamp;
|
| - }
|
| + seek_time = std::max(seek_time, demuxer_->GetStartTime());
|
| + renderer_->StartPlayingFrom(seek_time);
|
| + renderer_->SetPlaybackRate(playback_rate_);
|
| + renderer_->SetVolume(volume_);
|
|
|
| if (text_renderer_)
|
| text_renderer_->StartPlaying();
|
|
|
| - shared_state_.renderer->SetPlaybackRate(playback_rate_);
|
| - shared_state_.renderer->SetVolume(volume_);
|
| + {
|
| + base::AutoLock auto_lock(shared_state_lock_);
|
| + shared_state_.media_time_min = seek_time;
|
| + shared_state_.media_time_max = seek_time;
|
| + shared_state_.media_time_reference = base::TimeTicks();
|
| + }
|
|
|
| SetState(kPlaying);
|
| main_task_runner_->PostTask(
|
| @@ -791,7 +820,7 @@ void PipelineImpl::RendererWrapper::CompleteSuspend(PipelineStatus status) {
|
| LOG_IF(WARNING, status != PIPELINE_OK)
|
| << "Encountered pipeline error while suspending: " << status;
|
|
|
| - DestroyRenderer();
|
| + renderer_.reset();
|
| {
|
| base::AutoLock auto_lock(shared_state_lock_);
|
| shared_state_.statistics.audio_memory_usage = 0;
|
| @@ -821,22 +850,9 @@ void PipelineImpl::RendererWrapper::InitializeRenderer(
|
| }
|
|
|
| if (cdm_context_)
|
| - shared_state_.renderer->SetCdm(cdm_context_,
|
| - base::Bind(&IgnoreCdmAttached));
|
| + renderer_->SetCdm(cdm_context_, base::Bind(&IgnoreCdmAttached));
|
|
|
| - shared_state_.renderer->Initialize(demuxer_, this, done_cb);
|
| -}
|
| -
|
| -void PipelineImpl::RendererWrapper::DestroyRenderer() {
|
| - DCHECK(media_task_runner_->BelongsToCurrentThread());
|
| -
|
| - // Destroy the renderer outside the lock scope to avoid holding the lock
|
| - // while renderer is being destroyed (in case Renderer destructor is costly).
|
| - std::unique_ptr<Renderer> renderer;
|
| - {
|
| - base::AutoLock auto_lock(shared_state_lock_);
|
| - renderer.swap(shared_state_.renderer);
|
| - }
|
| + renderer_->Initialize(demuxer_, this, done_cb);
|
| }
|
|
|
| void PipelineImpl::RendererWrapper::ReportMetadata() {
|
| @@ -979,6 +995,8 @@ void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) {
|
|
|
| DCHECK(seek_cb_.is_null());
|
| seek_cb_ = seek_cb;
|
| + media_time_last_ = base::TimeDelta();
|
| +
|
| media_task_runner_->PostTask(
|
| FROM_HERE, base::Bind(&RendererWrapper::Seek,
|
| base::Unretained(renderer_wrapper_.get()), time));
|
| @@ -1008,6 +1026,7 @@ void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer,
|
| DCHECK(IsRunning());
|
| DCHECK(seek_cb_.is_null());
|
| seek_cb_ = seek_cb;
|
| + media_time_last_ = base::TimeDelta();
|
|
|
| media_task_runner_->PostTask(
|
| FROM_HERE, base::Bind(&RendererWrapper::Resume,
|
| @@ -1060,7 +1079,34 @@ void PipelineImpl::SetVolume(float volume) {
|
|
|
| base::TimeDelta PipelineImpl::GetMediaTime() const {
|
| DCHECK(thread_checker_.CalledOnValidThread());
|
| - return renderer_wrapper_->GetMediaTime();
|
| +
|
| + base::TimeDelta time_min, time_max;
|
| + base::TimeTicks time_ref;
|
| + renderer_wrapper_->GetMediaTimeExtents(&time_min, &time_max, &time_ref);
|
| +
|
| + // Return the current time based on the known extents of media data plus an
|
| + // estimate based on the last time those values were calculated.
|
| + base::TimeDelta media_time =
|
| + time_ref.is_null()
|
| + ? time_min
|
| + : CurrentMediaTime(time_min, time_max, time_ref,
|
| + base::TimeTicks::Now(), playback_rate_);
|
| +
|
| + // Clamp current media time to the last reported value, this prevents higher
|
| + // level clients from seeing time go backwards based on inaccurate or spurious
|
| + // delay values reported to the AudioClock.
|
| + //
|
| + // It is expected that such events are transient and will be recovered as
|
| + // rendering continues over time.
|
| + if (media_time < media_time_last_) {
|
| + DVLOG(2) << __func__ << ": " << media_time_last_
|
| + << " (clamped), actual: " << media_time;
|
| + return media_time_last_;
|
| + }
|
| +
|
| + DVLOG(2) << __func__ << ": " << media_time;
|
| + media_time_last_ = media_time;
|
| + return media_time;
|
| }
|
|
|
| Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const {
|
|
|