Chromium Code Reviews| Index: media/base/pipeline_impl.cc |
| diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc |
| index ac39834374cad409c77ca023419fc83ee8b5a7ed..860f5f29c6b4930b3fb2a0520363e4e14aeae5b5 100644 |
| --- a/media/base/pipeline_impl.cc |
| +++ b/media/base/pipeline_impl.cc |
| @@ -5,260 +5,387 @@ |
| #include "media/base/pipeline_impl.h" |
| #include <algorithm> |
| -#include <utility> |
| #include "base/bind.h" |
| #include "base/bind_helpers.h" |
| #include "base/callback.h" |
| #include "base/callback_helpers.h" |
| #include "base/command_line.h" |
| -#include "base/compiler_specific.h" |
| #include "base/location.h" |
| -#include "base/memory/ptr_util.h" |
| #include "base/metrics/histogram.h" |
| #include "base/single_thread_task_runner.h" |
| -#include "base/stl_util.h" |
| -#include "base/strings/string_number_conversions.h" |
| -#include "base/strings/string_util.h" |
| +#include "base/synchronization/lock.h" |
| #include "base/synchronization/waitable_event.h" |
| #include "base/threading/thread_task_runner_handle.h" |
| #include "media/base/bind_to_current_loop.h" |
| +#include "media/base/demuxer.h" |
| #include "media/base/media_log.h" |
| #include "media/base/media_switches.h" |
| #include "media/base/renderer.h" |
| +#include "media/base/renderer_client.h" |
| +#include "media/base/serial_runner.h" |
| #include "media/base/text_renderer.h" |
| #include "media/base/text_track_config.h" |
| #include "media/base/timestamp_constants.h" |
| #include "media/base/video_decoder_config.h" |
| -using base::TimeDelta; |
| +namespace { |
| + |
| +const double kDefaultPlaybackRate = 0.0; |
| +const float kDefaultVolume = 1.0f; |
| + |
| +bool TextTracksEnabled() { |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
Is this worth the indirection, given that it's cal
alokp
2016/06/10 00:06:02
May be not but it should not hurt. Your call.
sandersd (OOO until July 31)
2016/06/10 18:22:49
I think I would prefer to not extract this.
alokp
2016/06/10 21:25:37
Do you want to move the entire definition back to
sandersd (OOO until July 31)
2016/06/10 22:02:06
The entire definition.
alokp
2016/06/10 22:35:03
Done.
|
| + static bool enabled = base::CommandLine::ForCurrentProcess()->HasSwitch( |
| + switches::kEnableInbandTextTracks); |
| + return enabled; |
| +} |
| + |
| +} // namespace |
| namespace media { |
| +class PipelineImpl::RendererWrapper : public DemuxerHost, |
| + public RendererClient { |
| + public: |
| + RendererWrapper(base::WeakPtr<PipelineImpl> weak_pipeline, |
| + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, |
| + scoped_refptr<MediaLog> media_log); |
| + ~RendererWrapper() final; |
| + |
| + void Start(Demuxer* demuxer, |
| + std::unique_ptr<Renderer> renderer, |
| + std::unique_ptr<TextRenderer> text_renderer); |
| + void Stop(const base::Closure& stop_cb); |
| + void Seek(base::TimeDelta time); |
| + void Suspend(); |
| + void Resume(base::TimeDelta time, std::unique_ptr<Renderer> renderer); |
| + void SetPlaybackRate(double playback_rate); |
| + void SetVolume(float volume); |
| + base::TimeDelta GetMediaTime(); |
| + void SetCdm(CdmContext* cdm_context, const CdmAttachedCB& cdm_attached_cb); |
| + |
| + private: |
| + // DemuxerHost implementaion. |
| + void OnBufferedTimeRangesChanged(const Ranges<base::TimeDelta>& ranges) final; |
| + void SetDuration(base::TimeDelta duration) final; |
| + void OnDemuxerError(PipelineStatus error) final; |
| + void AddTextStream(DemuxerStream* text_stream, |
| + const TextTrackConfig& config) final; |
| + void RemoveTextStream(DemuxerStream* text_stream) final; |
| + |
| + // RendererClient implementation. |
| + void OnError(PipelineStatus error) final; |
| + void OnEnded() final; |
| + void OnStatisticsUpdate(const PipelineStatistics& stats) final; |
| + void OnBufferingStateChange(BufferingState state) final; |
| + void OnWaitingForDecryptionKey() final; |
| + void OnVideoNaturalSizeChange(const gfx::Size& size) final; |
| + void OnVideoOpacityChange(bool opaque) final; |
| + |
| + void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); |
| + void DoStop(const base::Closure& done_cb); |
| + void OnPipelineError(PipelineStatus error); |
| + void OnTextRendererEnded(); |
| + void RunEndedCallbackIfNeeded(); |
| + void SetState(State next_state); |
| + State GetNextState() const; |
| + void StateTransitionTask(PipelineStatus status); |
| + void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
| + CdmContext* cdm_context, |
| + bool success); |
| + void AddTextStreamTask(DemuxerStream* text_stream, |
| + const TextTrackConfig& config); |
| + void RemoveTextStreamTask(DemuxerStream* text_stream); |
| + void InitializeDemuxer(const PipelineStatusCB& done_cb); |
| + void InitializeRenderer(const PipelineStatusCB& done_cb); |
| + void ReportMetadata(); |
| + |
| + base::WeakPtr<PipelineImpl> weak_pipeline_; |
| + const scoped_refptr<base::SingleThreadTaskRunner> media_task_runner_; |
| + const scoped_refptr<base::SingleThreadTaskRunner> main_task_runner_; |
| + const scoped_refptr<MediaLog> media_log_; |
| + |
| + Demuxer* demuxer_; |
| + std::unique_ptr<Renderer> renderer_; |
| + std::unique_ptr<TextRenderer> text_renderer_; |
| + double playback_rate_; |
| + float volume_; |
| + CdmContext* cdm_context_; |
| + |
| + // Lock used to serialize access for |renderer_|. |
| + mutable base::Lock renderer_lock_; |
| + |
| + // Current state of the pipeline. |
| + State state_; |
| + |
| + // Status of the pipeline. Initialized to PIPELINE_OK which indicates that |
| + // the pipeline is operating correctly. Any other value indicates that the |
| + // pipeline is stopped or is stopping. Clients can call the Stop() method to |
| + // reset the pipeline state, and restore this to PIPELINE_OK. |
| + PipelineStatus status_; |
| + |
| + // The timestamp to start playback from after starting/seeking/resuming has |
| + // completed. |
| + base::TimeDelta start_timestamp_; |
| + |
| + // The media timestamp to return while the pipeline is suspended. |
| + // Otherwise set to kNoTimestamp(). |
| + base::TimeDelta suspend_timestamp_; |
| + |
| + // Whether we've received the audio/video/text ended events. |
| + bool renderer_ended_; |
| + bool text_renderer_ended_; |
| + |
| + // Series of tasks to Start(), Seek(), and Resume(). |
| + std::unique_ptr<SerialRunner> pending_callbacks_; |
| + |
| + base::WeakPtrFactory<RendererWrapper> weak_factory_; |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
There seem to be weak pointers being created from
alokp
2016/06/10 00:06:02
Done.
|
| + DISALLOW_COPY_AND_ASSIGN(RendererWrapper); |
| +}; |
| + |
| PipelineImpl::PipelineImpl( |
| const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner, |
| MediaLog* media_log) |
| - : main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| - media_task_runner_(media_task_runner), |
| + : media_task_runner_(media_task_runner), |
| media_log_(media_log), |
| - running_(false), |
| + client_(nullptr), |
| + playback_rate_(kDefaultPlaybackRate), |
| + volume_(kDefaultVolume), |
| + suspend_time_(kNoTimestamp()), |
| did_loading_progress_(false), |
| - volume_(1.0f), |
| - playback_rate_(0.0), |
| - status_(PIPELINE_OK), |
| - state_(kCreated), |
| - suspend_timestamp_(kNoTimestamp()), |
| - renderer_ended_(false), |
| - text_renderer_ended_(false), |
| - demuxer_(NULL), |
| - cdm_context_(nullptr), |
| weak_factory_(this) { |
| - weak_this_ = weak_factory_.GetWeakPtr(); |
| - media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| + DVLOG(2) << __FUNCTION__; |
| + renderer_wrapper_.reset(new RendererWrapper(weak_factory_.GetWeakPtr(), |
| + media_task_runner_, media_log_)); |
| } |
| PipelineImpl::~PipelineImpl() { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()) |
| - << "Pipeline must be destroyed on same thread that created it"; |
| - DCHECK(!running_) << "Stop() must complete before destroying object"; |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(!client_) << "Stop() must complete before destroying object"; |
| DCHECK(seek_cb_.is_null()); |
| + DCHECK(suspend_cb_.is_null()); |
| + |
| + // Invalidate self weak pointers effectively canceling all pending |
| + // notifications in the message queue. |
| + weak_factory_.InvalidateWeakPtrs(); |
|
sandersd (OOO until July 31)
2016/06/09 19:30:25
Is there a specific reason you do not want to rely
alokp
2016/06/10 00:06:02
Yes - we pass a weak_ptr for PipelineImpl to Rende
|
| + |
| + // RendererWrapper is deleted on the media thread. |
| + media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release()); |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
Should we be taking any specific precautions to en
alokp
2016/06/10 00:06:02
I am draining the message loop in PipelineIntegrat
|
| } |
| void PipelineImpl::Start(Demuxer* demuxer, |
| std::unique_ptr<Renderer> renderer, |
| Client* client, |
| const PipelineStatusCB& seek_cb) { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(demuxer); |
| + DCHECK(renderer); |
| DCHECK(client); |
| DCHECK(!seek_cb.is_null()); |
| - base::AutoLock auto_lock(lock_); |
| - CHECK(!running_) << "Media pipeline is already running"; |
| - running_ = true; |
| + DCHECK(!client_); |
| + DCHECK(seek_cb_.is_null()); |
| + client_ = client; |
| + seek_cb_ = seek_cb; |
| + |
| + std::unique_ptr<TextRenderer> text_renderer; |
| + if (TextTracksEnabled()) { |
| + text_renderer.reset(new TextRenderer( |
| + media_task_runner_, |
| + BindToCurrentLoop(base::Bind(&PipelineImpl::OnAddTextTrack, |
| + weak_factory_.GetWeakPtr())))); |
| + } |
| - demuxer_ = demuxer; |
| - renderer_ = std::move(renderer); |
| - client_weak_factory_.reset(new base::WeakPtrFactory<Client>(client)); |
| - weak_client_ = client_weak_factory_->GetWeakPtr(); |
| - seek_cb_ = media::BindToCurrentLoop(seek_cb); |
| media_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&PipelineImpl::StartTask, weak_this_)); |
| + FROM_HERE, |
| + base::Bind(&RendererWrapper::Start, |
| + base::Unretained(renderer_wrapper_.get()), demuxer, |
| + base::Passed(&renderer), base::Passed(&text_renderer))); |
| } |
| void PipelineImpl::Stop() { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (!IsRunning()) { |
| + DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()"; |
| + return; |
| + } |
| - if (media_task_runner_ != main_task_runner_) { |
| + if (media_task_runner_->BelongsToCurrentThread()) { |
| + // This path is executed by unittests that share media and main threads. |
| + base::Closure stop_cb = base::Bind(&base::DoNothing); |
| + media_task_runner_->PostTask( |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
If this task is posted, it's not obvious why the r
alokp
2016/06/10 00:06:02
That could cause RendererWrapper::Stop to run befo
|
| + FROM_HERE, |
| + base::Bind(&RendererWrapper::Stop, |
| + base::Unretained(renderer_wrapper_.get()), stop_cb)); |
| + } else { |
| // This path is executed by production code where the two task runners - |
| // main and media - live on different threads. |
| - // TODO(alokp): It may be possible to not have to wait for StopTask by |
| - // moving the members accessed on media thread into a class/struct and |
| - // DeleteSoon the instance on the media thread. |
| + // |
| + // TODO(alokp): We should not have to wait for the RendererWrapper::Stop. |
| + // RendererWrapper holds a raw reference to Demuxer, which in turn holds a |
| + // raw reference to DataSource. Both Demuxer and DataSource need to live |
| + // until RendererWrapper is stopped. If RendererWrapper owned Demuxer and |
| + // Demuxer owned DataSource, we could simply let RendererWrapper get lazily |
| + // destroyed on the media thread. |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
Perhaps this is a case where ref counting would be
|
| base::WaitableEvent waiter(base::WaitableEvent::ResetPolicy::AUTOMATIC, |
| base::WaitableEvent::InitialState::NOT_SIGNALED); |
| base::Closure stop_cb = |
| base::Bind(&base::WaitableEvent::Signal, base::Unretained(&waiter)); |
| - // If posting the task fails or the posted task fails to run, |
| - // we will wait here forever. So add a CHECK to make sure we do not run |
| - // into those situations. |
| - CHECK(weak_factory_.HasWeakPtrs()); |
| - CHECK(media_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&PipelineImpl::StopTask, weak_this_, stop_cb))); |
| + media_task_runner_->PostTask( |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
We should still verify that the task is successful
alokp
2016/06/10 00:06:02
Done.
|
| + FROM_HERE, |
| + base::Bind(&RendererWrapper::Stop, |
| + base::Unretained(renderer_wrapper_.get()), stop_cb)); |
| waiter.Wait(); |
| - } else { |
| - // This path is executed by unittests that share media and main threads. |
| - StopTask(base::Bind(&base::DoNothing)); |
| } |
| - // Invalidate client weak pointer effectively canceling all pending client |
| - // notifications in the message queue. |
| - client_weak_factory_.reset(); |
| + |
| + // Once the pipeline is stopped, nothing is reported back to the client. |
| + // Reset all callbacks and client handle. |
| + seek_cb_.Reset(); |
| + suspend_cb_.Reset(); |
| + client_ = nullptr; |
| } |
| -void PipelineImpl::Seek(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| +void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(!seek_cb.is_null()); |
| if (!IsRunning()) { |
| DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek()."; |
| return; |
| } |
| + DCHECK(seek_cb_.is_null()); |
| + seek_cb_ = seek_cb; |
| media_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&PipelineImpl::SeekTask, weak_this_, time, |
| - media::BindToCurrentLoop(seek_cb))); |
| + FROM_HERE, base::Bind(&RendererWrapper::Seek, |
| + base::Unretained(renderer_wrapper_.get()), time)); |
| } |
| bool PipelineImpl::IsRunning() const { |
| - // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| - // media thread. |
| - base::AutoLock auto_lock(lock_); |
| - return running_; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + return !!client_; |
| } |
| double PipelineImpl::GetPlaybackRate() const { |
| - // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| - // media thread. |
| - base::AutoLock auto_lock(lock_); |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| return playback_rate_; |
| } |
| void PipelineImpl::SetPlaybackRate(double playback_rate) { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| if (playback_rate < 0.0) |
| return; |
| - base::AutoLock auto_lock(lock_); |
| playback_rate_ = playback_rate; |
| - if (running_) { |
| - media_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&PipelineImpl::PlaybackRateChangedTask, |
| - weak_this_, playback_rate)); |
| - } |
| + media_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&RendererWrapper::SetPlaybackRate, |
| + base::Unretained(renderer_wrapper_.get()), playback_rate_)); |
| } |
| void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(!suspend_cb.is_null()); |
| + |
| + DCHECK(IsRunning()); |
| + DCHECK(suspend_cb_.is_null()); |
| + suspend_cb_ = suspend_cb; |
| media_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&PipelineImpl::SuspendTask, weak_this_, |
| - media::BindToCurrentLoop(suspend_cb))); |
| + FROM_HERE, base::Bind(&RendererWrapper::Suspend, |
| + base::Unretained(renderer_wrapper_.get()))); |
| } |
| void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer, |
| - base::TimeDelta timestamp, |
| + base::TimeDelta time, |
| const PipelineStatusCB& seek_cb) { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(renderer); |
| + DCHECK(!seek_cb.is_null()); |
| + |
| + DCHECK(IsRunning()); |
| + DCHECK(seek_cb_.is_null()); |
| + seek_cb_ = seek_cb; |
| media_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&PipelineImpl::ResumeTask, weak_this_, base::Passed(&renderer), |
| - timestamp, media::BindToCurrentLoop(seek_cb))); |
| + FROM_HERE, base::Bind(&RendererWrapper::Resume, |
| + base::Unretained(renderer_wrapper_.get()), time, |
| + base::Passed(&renderer))); |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
Why swap the parameter order here?
alokp
2016/06/10 00:06:02
did not mean to. reverted.
|
| } |
| float PipelineImpl::GetVolume() const { |
| - // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| - // media thread. |
| - base::AutoLock auto_lock(lock_); |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| return volume_; |
| } |
| void PipelineImpl::SetVolume(float volume) { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__ << "(" << volume << ")"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| if (volume < 0.0f || volume > 1.0f) |
| return; |
| - base::AutoLock auto_lock(lock_); |
| volume_ = volume; |
| - if (running_) { |
| - media_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&PipelineImpl::VolumeChangedTask, weak_this_, volume)); |
| - } |
| + media_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&RendererWrapper::SetVolume, |
| + base::Unretained(renderer_wrapper_.get()), volume)); |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
I see that some of these wrappers are passing the
alokp
2016/06/10 00:06:02
used member value everywhere.
|
| } |
| -TimeDelta PipelineImpl::GetMediaTime() const { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| +base::TimeDelta PipelineImpl::GetMediaTime() const { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| - base::AutoLock auto_lock(lock_); |
| - if (suspend_timestamp_ != kNoTimestamp()) |
| - return suspend_timestamp_; |
| - return renderer_ ? std::min(renderer_->GetMediaTime(), duration_) |
| - : TimeDelta(); |
| + return suspend_time_ != kNoTimestamp() ? suspend_time_ |
| + : renderer_wrapper_->GetMediaTime(); |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
There is a window here between RendererWrapper::Su
alokp
2016/06/10 00:06:02
Good catch! Done.
|
| } |
| -Ranges<TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| - |
| - base::AutoLock auto_lock(lock_); |
| +Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| return buffered_time_ranges_; |
| } |
| -TimeDelta PipelineImpl::GetMediaDuration() const { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| - |
| - base::AutoLock auto_lock(lock_); |
| +base::TimeDelta PipelineImpl::GetMediaDuration() const { |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| return duration_; |
| } |
| bool PipelineImpl::DidLoadingProgress() { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| - |
| - base::AutoLock auto_lock(lock_); |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| bool ret = did_loading_progress_; |
| did_loading_progress_ = false; |
| return ret; |
| } |
| PipelineStatistics PipelineImpl::GetStatistics() const { |
| - // TODO(alokp): Add thread DCHECK after removing the internal usage on |
| - // media thread. |
| - base::AutoLock auto_lock(lock_); |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| return statistics_; |
| } |
| void PipelineImpl::SetCdm(CdmContext* cdm_context, |
| const CdmAttachedCB& cdm_attached_cb) { |
| - DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK(cdm_context); |
| + DCHECK(!cdm_attached_cb.is_null()); |
| media_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&PipelineImpl::SetCdmTask, weak_this_, cdm_context, |
| - cdm_attached_cb)); |
| -} |
| - |
| -void PipelineImpl::SetErrorForTesting(PipelineStatus status) { |
| - OnError(status); |
| -} |
| - |
| -bool PipelineImpl::HasWeakPtrsForTesting() const { |
| - DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - return weak_factory_.HasWeakPtrs(); |
| + FROM_HERE, |
| + base::Bind(&RendererWrapper::SetCdm, |
| + base::Unretained(renderer_wrapper_.get()), cdm_context, |
| + media::BindToCurrentLoop(cdm_attached_cb))); |
| } |
| -void PipelineImpl::SetState(State next_state) { |
| +void PipelineImpl::RendererWrapper::SetState(State next_state) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - DVLOG(1) << GetStateString(state_) << " -> " << GetStateString(next_state); |
| + DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " |
| + << PipelineImpl::GetStateString(next_state); |
| state_ = next_state; |
| media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
| @@ -268,6 +395,7 @@ void PipelineImpl::SetState(State next_state) { |
| case state: \ |
| return #state; |
| +// static |
| const char* PipelineImpl::GetStateString(State state) { |
| switch (state) { |
| RETURN_STRING(kCreated); |
| @@ -287,9 +415,8 @@ const char* PipelineImpl::GetStateString(State state) { |
| #undef RETURN_STRING |
| -PipelineImpl::State PipelineImpl::GetNextState() const { |
| +PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - DCHECK(stop_cb_.is_null()) << "State transitions don't happen when stopping"; |
| DCHECK_EQ(status_, PIPELINE_OK) |
| << "State transitions don't happen when there's an error: " << status_; |
| @@ -322,43 +449,42 @@ PipelineImpl::State PipelineImpl::GetNextState() const { |
| return state_; |
| } |
| -void PipelineImpl::OnDemuxerError(PipelineStatus error) { |
| +void PipelineImpl::RendererWrapper::OnDemuxerError(PipelineStatus error) { |
| // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| // implementations call DemuxerHost on the media thread. |
| - media_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); |
| + media_task_runner_->PostTask(FROM_HERE, |
| + base::Bind(&RendererWrapper::OnPipelineError, |
| + weak_factory_.GetWeakPtr(), error)); |
| } |
| -void PipelineImpl::AddTextStream(DemuxerStream* text_stream, |
| - const TextTrackConfig& config) { |
| +void PipelineImpl::RendererWrapper::AddTextStream( |
| + DemuxerStream* text_stream, |
| + const TextTrackConfig& config) { |
| // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| // implementations call DemuxerHost on the media thread. |
| media_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&PipelineImpl::AddTextStreamTask, weak_this_, |
| - text_stream, config)); |
| + FROM_HERE, base::Bind(&RendererWrapper::AddTextStreamTask, |
| + weak_factory_.GetWeakPtr(), text_stream, config)); |
| } |
| -void PipelineImpl::RemoveTextStream(DemuxerStream* text_stream) { |
| +void PipelineImpl::RendererWrapper::RemoveTextStream( |
| + DemuxerStream* text_stream) { |
| // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| // implementations call DemuxerHost on the media thread. |
| media_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&PipelineImpl::RemoveTextStreamTask, weak_this_, text_stream)); |
| + FROM_HERE, base::Bind(&RendererWrapper::RemoveTextStreamTask, |
| + weak_factory_.GetWeakPtr(), text_stream)); |
| } |
| -void PipelineImpl::OnError(PipelineStatus error) { |
| +void PipelineImpl::RendererWrapper::OnError(PipelineStatus error) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - DCHECK(IsRunning()); |
| - DCHECK_NE(PIPELINE_OK, error); |
| - VLOG(1) << "Media pipeline error: " << error; |
| - media_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&PipelineImpl::ErrorChangedTask, weak_this_, error)); |
| + media_task_runner_->PostTask(FROM_HERE, |
| + base::Bind(&RendererWrapper::OnPipelineError, |
| + weak_factory_.GetWeakPtr(), error)); |
| } |
| -void PipelineImpl::OnEnded() { |
| +void PipelineImpl::RendererWrapper::OnEnded() { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); |
| @@ -367,70 +493,66 @@ void PipelineImpl::OnEnded() { |
| DCHECK(!renderer_ended_); |
| renderer_ended_ = true; |
| - |
| RunEndedCallbackIfNeeded(); |
| } |
| -void PipelineImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { |
| +void PipelineImpl::RendererWrapper::OnStatisticsUpdate( |
| + const PipelineStatistics& stats) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - base::AutoLock auto_lock(lock_); |
| - statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
| - statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
| - statistics_.video_frames_decoded += stats.video_frames_decoded; |
| - statistics_.video_frames_dropped += stats.video_frames_dropped; |
| - statistics_.audio_memory_usage += stats.audio_memory_usage; |
| - statistics_.video_memory_usage += stats.video_memory_usage; |
| + main_task_runner_->PostTask( |
| + FROM_HERE, |
| + base::Bind(&PipelineImpl::OnStatisticsUpdate, weak_pipeline_, stats)); |
| } |
| -void PipelineImpl::OnBufferingStateChange(BufferingState state) { |
| - DVLOG(1) << __FUNCTION__ << "(" << state << ") "; |
| +void PipelineImpl::RendererWrapper::OnBufferingStateChange( |
| + BufferingState state) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + DVLOG(2) << __FUNCTION__ << "(" << state << ") "; |
| main_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&Pipeline::Client::OnBufferingStateChange, |
| - weak_client_, state)); |
| + FROM_HERE, |
| + base::Bind(&PipelineImpl::OnBufferingStateChange, weak_pipeline_, state)); |
| } |
| -void PipelineImpl::OnWaitingForDecryptionKey() { |
| +void PipelineImpl::RendererWrapper::OnWaitingForDecryptionKey() { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| main_task_runner_->PostTask( |
| FROM_HERE, |
| - base::Bind(&Pipeline::Client::OnWaitingForDecryptionKey, weak_client_)); |
| + base::Bind(&PipelineImpl::OnWaitingForDecryptionKey, weak_pipeline_)); |
| } |
| -void PipelineImpl::OnVideoNaturalSizeChange(const gfx::Size& size) { |
| +void PipelineImpl::RendererWrapper::OnVideoNaturalSizeChange( |
| + const gfx::Size& size) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| main_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&Pipeline::Client::OnVideoNaturalSizeChange, |
| - weak_client_, size)); |
| + FROM_HERE, base::Bind(&PipelineImpl::OnVideoNaturalSizeChange, |
| + weak_pipeline_, size)); |
| } |
| -void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
| +void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| main_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&Pipeline::Client::OnVideoOpacityChange, |
| - weak_client_, opaque)); |
| + FROM_HERE, |
| + base::Bind(&PipelineImpl::OnVideoOpacityChange, weak_pipeline_, opaque)); |
| } |
| -void PipelineImpl::SetDuration(TimeDelta duration) { |
| +void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) { |
| // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| // implementations call DemuxerHost on the media thread. |
| - DCHECK(IsRunning()); |
| media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET, |
| "duration", duration)); |
| UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration); |
| - base::AutoLock auto_lock(lock_); |
| - duration_ = duration; |
| main_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&Pipeline::Client::OnDurationChange, weak_client_)); |
| + FROM_HERE, |
| + base::Bind(&PipelineImpl::OnDurationChange, weak_pipeline_, duration)); |
| } |
| -void PipelineImpl::StateTransitionTask(PipelineStatus status) { |
| +void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| // No-op any state transitions if we're stopping. |
| @@ -439,7 +561,7 @@ void PipelineImpl::StateTransitionTask(PipelineStatus status) { |
| // Report error from the previous operation. |
| if (status != PIPELINE_OK) { |
| - ErrorChangedTask(status); |
| + OnPipelineError(status); |
| return; |
| } |
| @@ -450,8 +572,8 @@ void PipelineImpl::StateTransitionTask(PipelineStatus status) { |
| pending_callbacks_.reset(); |
| - PipelineStatusCB done_cb = |
| - base::Bind(&PipelineImpl::StateTransitionTask, weak_this_); |
| + PipelineStatusCB done_cb = base::Bind(&RendererWrapper::StateTransitionTask, |
| + weak_factory_.GetWeakPtr()); |
| // Switch states, performing any entrance actions for the new state as well. |
| SetState(GetNextState()); |
| @@ -473,28 +595,23 @@ void PipelineImpl::StateTransitionTask(PipelineStatus status) { |
| case kPlaying: |
| DCHECK(start_timestamp_ >= base::TimeDelta()); |
| renderer_->StartPlayingFrom(start_timestamp_); |
| - { |
| - base::AutoLock auto_lock(lock_); |
| - suspend_timestamp_ = kNoTimestamp(); |
| - } |
| if (text_renderer_) |
| text_renderer_->StartPlaying(); |
| - base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
| + renderer_->SetPlaybackRate(playback_rate_); |
| + renderer_->SetVolume(volume_); |
| - PlaybackRateChangedTask(GetPlaybackRate()); |
| - VolumeChangedTask(GetVolume()); |
| + main_task_runner_->PostTask( |
|
sandersd (OOO until July 31)
2016/06/09 19:30:25
Is there a particular reason the order was changed
alokp
2016/06/10 00:06:02
not really - it seemed more natural but not necess
|
| + FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_, |
| + start_timestamp_)); |
| return; |
| case kSuspended: |
| renderer_.reset(); |
| - { |
| - base::AutoLock auto_lock(lock_); |
| - statistics_.audio_memory_usage = 0; |
| - statistics_.video_memory_usage = 0; |
| - } |
| - base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); |
| + main_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_, |
| + suspend_timestamp_)); |
| return; |
| case kStopping: |
| @@ -513,8 +630,8 @@ void PipelineImpl::StateTransitionTask(PipelineStatus status) { |
| // |
| // That being said, deleting the renderers while keeping |pending_callbacks_| |
| // running on the media thread would result in crashes. |
| -void PipelineImpl::DoSeek(TimeDelta seek_timestamp, |
| - const PipelineStatusCB& done_cb) { |
| +void PipelineImpl::RendererWrapper::DoSeek(base::TimeDelta seek_timestamp, |
| + const PipelineStatusCB& done_cb) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| DCHECK(!pending_callbacks_.get()); |
| DCHECK_EQ(state_, kSeeking); |
| @@ -543,7 +660,7 @@ void PipelineImpl::DoSeek(TimeDelta seek_timestamp, |
| pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
| } |
| -void PipelineImpl::DoStop() { |
| +void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) { |
| DVLOG(2) << __FUNCTION__; |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| DCHECK_EQ(state_, kStopping); |
| @@ -553,7 +670,7 @@ void PipelineImpl::DoStop() { |
| // even for accessing media time http://crbug.com/370634 |
| std::unique_ptr<Renderer> renderer; |
| { |
| - base::AutoLock auto_lock(lock_); |
| + base::AutoLock auto_lock(renderer_lock_); |
| renderer.swap(renderer_); |
| } |
| renderer.reset(); |
| @@ -564,91 +681,65 @@ void PipelineImpl::DoStop() { |
| demuxer_ = NULL; |
| } |
| - { |
| - base::AutoLock auto_lock(lock_); |
| - running_ = false; |
| - } |
| SetState(kStopped); |
| - // If we stop during initialization/seeking/suspending we don't want to leave |
| - // outstanding callbacks around. The callbacks also do not get run if the |
| - // pipeline is stopped before it had a chance to complete outstanding tasks. |
| - seek_cb_.Reset(); |
| - suspend_cb_.Reset(); |
| - |
| - if (!stop_cb_.is_null()) { |
| - // Invalid all weak pointers so it's safe to destroy |this| on the render |
| - // main thread. |
| - weak_factory_.InvalidateWeakPtrs(); |
| - |
| - // Post the stop callback to enqueue it after the tasks that may have been |
| - // Demuxer and Renderer during stopping. |
| - media_task_runner_->PostTask(FROM_HERE, base::ResetAndReturn(&stop_cb_)); |
| - } |
| + // Post the stop callback to enqueue it after the tasks that may have been |
| + // Demuxer and Renderer during stopping. |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
?
(Also, we can't truly rely on that ordering, si
alokp
2016/06/10 00:06:02
you are right - no need to post it.
sandersd (OOO until July 31)
2016/06/10 18:22:50
Actually I just couldn't parse the comment, but th
alokp
2016/06/10 21:25:36
Acknowledged.
|
| + media_task_runner_->PostTask(FROM_HERE, done_cb); |
| } |
| -void PipelineImpl::OnBufferedTimeRangesChanged( |
| +void PipelineImpl::RendererWrapper::OnBufferedTimeRangesChanged( |
| const Ranges<base::TimeDelta>& ranges) { |
| // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer |
| // implementations call DemuxerHost on the media thread. |
| - base::AutoLock auto_lock(lock_); |
| - buffered_time_ranges_ = ranges; |
| - did_loading_progress_ = true; |
| + main_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&PipelineImpl::OnBufferedTimeRangesChange, |
| + weak_pipeline_, ranges)); |
| } |
| -void PipelineImpl::StartTask() { |
| +void PipelineImpl::RendererWrapper::Start( |
| + Demuxer* demuxer, |
| + std::unique_ptr<Renderer> renderer, |
| + std::unique_ptr<TextRenderer> text_renderer) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
| + << state_; |
| - CHECK_EQ(kCreated, state_) |
| - << "Media pipeline cannot be started more than once"; |
| - |
| - text_renderer_ = CreateTextRenderer(); |
| + DCHECK(!demuxer_); |
| + DCHECK(!renderer_); |
| + DCHECK(!text_renderer_); |
| + DCHECK(!renderer_ended_); |
| + DCHECK(!text_renderer_ended_); |
| + demuxer_ = demuxer; |
| + { |
| + base::AutoLock auto_lock(renderer_lock_); |
| + renderer_ = std::move(renderer); |
| + } |
| + text_renderer_ = std::move(text_renderer); |
| if (text_renderer_) { |
| - text_renderer_->Initialize( |
| - base::Bind(&PipelineImpl::OnTextRendererEnded, weak_this_)); |
| + text_renderer_->Initialize(base::Bind(&RendererWrapper::OnTextRendererEnded, |
| + weak_factory_.GetWeakPtr())); |
| } |
| StateTransitionTask(PIPELINE_OK); |
| } |
| -void PipelineImpl::StopTask(const base::Closure& stop_cb) { |
| +void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - DCHECK(stop_cb_.is_null()); |
| - |
| - if (state_ == kStopped) { |
| - // Invalid all weak pointers so it's safe to destroy |this| on the render |
| - // main thread. |
| - weak_factory_.InvalidateWeakPtrs(); |
| - |
| - // NOTE: pipeline may be deleted at this point in time as a result of |
| - // executing |stop_cb|. |
| - stop_cb.Run(); |
| - |
| - return; |
| - } |
| - |
| - stop_cb_ = stop_cb; |
| - |
| - // We may already be stopping due to a runtime error. |
| - if (state_ == kStopping) |
| - return; |
| - |
| - // Do not report statistics if the pipeline is not fully initialized. |
| - if (state_ == kSeeking || state_ == kPlaying || state_ == kSuspending || |
| - state_ == kSuspended || state_ == kResuming) { |
| - PipelineStatistics stats = GetStatistics(); |
| - if (stats.video_frames_decoded > 0) { |
| - UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount", |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
This UMA report seems to have disappeared entirely
alokp
2016/06/10 00:06:02
Do you think it is important to check for state_ h
sandersd (OOO until July 31)
2016/06/10 18:22:50
The goal is to only report a number when at least
alokp
2016/06/10 21:25:36
Then we probably do not need to look at the curren
sandersd (OOO until July 31)
2016/06/10 22:02:06
I guess it is, I can't actually think of any speci
alokp
2016/06/10 22:35:03
Acknowledged.
|
| - stats.video_frames_dropped); |
| - } |
| - } |
| + DCHECK(state_ != kStopping && state_ != kStopped); |
| SetState(kStopping); |
| + |
| + // If we stop during starting/seeking/suspending/resuming we don't want to |
| + // leave outstanding callbacks around. The callbacks also do not get run if |
| + // the pipeline is stopped before it had a chance to complete outstanding |
| + // tasks. |
| pending_callbacks_.reset(); |
| - DoStop(); |
| + |
| + DoStop(stop_cb); |
| } |
| -void PipelineImpl::ErrorChangedTask(PipelineStatus error) { |
| +void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
| @@ -656,113 +747,81 @@ void PipelineImpl::ErrorChangedTask(PipelineStatus error) { |
| if (status_ != PIPELINE_OK) |
| return; |
| - // Don't report pipeline error events to the media log here. The embedder will |
| - // log this when Client::OnError is called. If the pipeline is already stopped |
| - // or stopping we also don't want to log any event. In case we are suspending |
| - // or suspended, the error may be recoverable, so don't propagate it now, |
| - // instead let the subsequent seek during resume propagate it if it's |
| - // unrecoverable. |
| + // Don't report pipeline error events to the media log here. The embedder |
| + // will log this when Client::OnError is called. If the pipeline is already |
| + // stopped or stopping we also don't want to log any event. In case we are |
| + // suspending or suspended, the error may be recoverable, so don't propagate |
| + // it now, instead let the subsequent seek during resume propagate it if |
| + // it's unrecoverable. |
| if (state_ == kStopping || state_ == kStopped || state_ == kSuspending || |
| state_ == kSuspended) { |
| return; |
| } |
| - // Once we enter |kStopping| state, nothing is reported back to the client. |
| - // If we encounter an error during initialization/seeking/suspending, |
| - // report the error using the completion callbacks for those tasks. |
| status_ = error; |
| - bool error_reported = false; |
| - if (!seek_cb_.is_null()) { |
| - base::ResetAndReturn(&seek_cb_).Run(status_); |
| - error_reported = true; |
| - } |
| - if (!suspend_cb_.is_null()) { |
| - base::ResetAndReturn(&suspend_cb_).Run(status_); |
| - error_reported = true; |
| - } |
| - if (!error_reported) { |
| - DCHECK_NE(status_, PIPELINE_OK); |
| - main_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&Pipeline::Client::OnError, weak_client_, status_)); |
| - } |
| - |
| - SetState(kStopping); |
| - pending_callbacks_.reset(); |
| - DoStop(); |
| + main_task_runner_->PostTask( |
| + FROM_HERE, base::Bind(&PipelineImpl::OnError, weak_pipeline_, error)); |
| } |
| -void PipelineImpl::PlaybackRateChangedTask(double playback_rate) { |
| +void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - // Playback rate changes are only carried out while playing. |
| - if (state_ != kPlaying) |
| - return; |
| - |
| - renderer_->SetPlaybackRate(playback_rate); |
| + playback_rate_ = playback_rate; |
| + if (state_ == kPlaying) |
| + renderer_->SetPlaybackRate(playback_rate_); |
| } |
| -void PipelineImpl::VolumeChangedTask(float volume) { |
| +void PipelineImpl::RendererWrapper::SetVolume(float volume) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - // Volume changes are only carried out while playing. |
| - if (state_ != kPlaying) |
| - return; |
| - |
| - renderer_->SetVolume(volume); |
| + volume_ = volume; |
| + if (state_ == kPlaying) |
| + renderer_->SetVolume(volume_); |
| } |
| -void PipelineImpl::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| +void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - DCHECK(stop_cb_.is_null()); |
| // Suppress seeking if we're not fully started. |
| if (state_ != kPlaying) { |
| DCHECK(state_ == kStopping || state_ == kStopped) |
| << "Receive seek in unexpected state: " << state_; |
| - seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); |
| + OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| return; |
| } |
| - DCHECK(seek_cb_.is_null()); |
| - |
| const base::TimeDelta seek_timestamp = |
| std::max(time, demuxer_->GetStartTime()); |
| SetState(kSeeking); |
| - seek_cb_ = seek_cb; |
| renderer_ended_ = false; |
| text_renderer_ended_ = false; |
| start_timestamp_ = seek_timestamp; |
| - DoSeek(seek_timestamp, |
| - base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); |
| + DoSeek(seek_timestamp, base::Bind(&RendererWrapper::StateTransitionTask, |
| + weak_factory_.GetWeakPtr())); |
| } |
| -void PipelineImpl::SuspendTask(const PipelineStatusCB& suspend_cb) { |
| +void PipelineImpl::RendererWrapper::Suspend() { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| // Suppress suspending if we're not playing. |
| if (state_ != kPlaying) { |
| DCHECK(state_ == kStopping || state_ == kStopped) |
| << "Receive suspend in unexpected state: " << state_; |
| - suspend_cb.Run(PIPELINE_ERROR_INVALID_STATE); |
| + OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| return; |
| } |
| DCHECK(renderer_); |
| DCHECK(!pending_callbacks_.get()); |
| SetState(kSuspending); |
| - suspend_cb_ = suspend_cb; |
| // Freeze playback and record the media time before flushing. (Flushing clears |
| // the value.) |
| renderer_->SetPlaybackRate(0.0); |
| - { |
| - base::AutoLock auto_lock(lock_); |
| - suspend_timestamp_ = renderer_->GetMediaTime(); |
| - DCHECK(suspend_timestamp_ != kNoTimestamp()); |
| - } |
| + suspend_timestamp_ = renderer_->GetMediaTime(); |
| + DCHECK(suspend_timestamp_ != kNoTimestamp()); |
| // Queue the asynchronous actions required to stop playback. (Matches setup in |
| // DoSeek().) |
| @@ -781,31 +840,34 @@ void PipelineImpl::SuspendTask(const PipelineStatusCB& suspend_cb) { |
| base::Unretained(text_renderer_.get()))); |
| } |
| - pending_callbacks_ = SerialRunner::Run( |
| - fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); |
| + pending_callbacks_ = |
| + SerialRunner::Run(fns, base::Bind(&RendererWrapper::StateTransitionTask, |
| + weak_factory_.GetWeakPtr())); |
| } |
| -void PipelineImpl::ResumeTask(std::unique_ptr<Renderer> renderer, |
| - base::TimeDelta timestamp, |
| - const PipelineStatusCB& seek_cb) { |
| +void PipelineImpl::RendererWrapper::Resume(base::TimeDelta timestamp, |
| + std::unique_ptr<Renderer> renderer) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| // Suppress resuming if we're not suspended. |
| if (state_ != kSuspended) { |
| DCHECK(state_ == kStopping || state_ == kStopped) |
| << "Receive resume in unexpected state: " << state_; |
| - seek_cb.Run(PIPELINE_ERROR_INVALID_STATE); |
| + OnPipelineError(PIPELINE_ERROR_INVALID_STATE); |
| return; |
| } |
| DCHECK(!renderer_); |
| DCHECK(!pending_callbacks_.get()); |
| SetState(kResuming); |
| - renderer_ = std::move(renderer); |
| + |
| + { |
| + base::AutoLock auto_lock(renderer_lock_); |
| + renderer_ = std::move(renderer); |
| + } |
| // Set up for a seek. (Matches setup in SeekTask().) |
| // TODO(sandersd): Share implementation with SeekTask(). |
| - seek_cb_ = seek_cb; |
| renderer_ended_ = false; |
| text_renderer_ended_ = false; |
| start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime()); |
| @@ -819,36 +881,42 @@ void PipelineImpl::ResumeTask(std::unique_ptr<Renderer> renderer, |
| fns.Push( |
| base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); |
| - fns.Push(base::Bind(&PipelineImpl::InitializeRenderer, weak_this_)); |
| + fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, |
| + weak_factory_.GetWeakPtr())); |
| - pending_callbacks_ = SerialRunner::Run( |
| - fns, base::Bind(&PipelineImpl::StateTransitionTask, weak_this_)); |
| + pending_callbacks_ = |
| + SerialRunner::Run(fns, base::Bind(&RendererWrapper::StateTransitionTask, |
| + weak_factory_.GetWeakPtr())); |
| } |
| -void PipelineImpl::SetCdmTask(CdmContext* cdm_context, |
| - const CdmAttachedCB& cdm_attached_cb) { |
| - base::AutoLock auto_lock(lock_); |
| +void PipelineImpl::RendererWrapper::SetCdm( |
| + CdmContext* cdm_context, |
| + const CdmAttachedCB& cdm_attached_cb) { |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| if (!renderer_) { |
| cdm_context_ = cdm_context; |
| cdm_attached_cb.Run(true); |
| return; |
| } |
| - renderer_->SetCdm(cdm_context, |
| - base::Bind(&PipelineImpl::OnCdmAttached, weak_this_, |
| - cdm_attached_cb, cdm_context)); |
| + renderer_->SetCdm(cdm_context, base::Bind(&RendererWrapper::OnCdmAttached, |
| + weak_factory_.GetWeakPtr(), |
| + cdm_attached_cb, cdm_context)); |
| } |
| -void PipelineImpl::OnCdmAttached(const CdmAttachedCB& cdm_attached_cb, |
| - CdmContext* cdm_context, |
| - bool success) { |
| +void PipelineImpl::RendererWrapper::OnCdmAttached( |
| + const CdmAttachedCB& cdm_attached_cb, |
| + CdmContext* cdm_context, |
| + bool success) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + |
| if (success) |
| cdm_context_ = cdm_context; |
| cdm_attached_cb.Run(success); |
| } |
| -void PipelineImpl::OnTextRendererEnded() { |
| +void PipelineImpl::RendererWrapper::OnTextRendererEnded() { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED)); |
| @@ -861,7 +929,7 @@ void PipelineImpl::OnTextRendererEnded() { |
| RunEndedCallbackIfNeeded(); |
| } |
| -void PipelineImpl::RunEndedCallbackIfNeeded() { |
| +void PipelineImpl::RendererWrapper::RunEndedCallbackIfNeeded() { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| if (renderer_ && !renderer_ended_) |
| @@ -872,62 +940,42 @@ void PipelineImpl::RunEndedCallbackIfNeeded() { |
| DCHECK_EQ(status_, PIPELINE_OK); |
| main_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&Pipeline::Client::OnEnded, weak_client_)); |
| + FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
| } |
| -std::unique_ptr<TextRenderer> PipelineImpl::CreateTextRenderer() { |
| +void PipelineImpl::RendererWrapper::AddTextStreamTask( |
| + DemuxerStream* text_stream, |
| + const TextTrackConfig& config) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - const base::CommandLine* cmd_line = base::CommandLine::ForCurrentProcess(); |
| - if (!cmd_line->HasSwitch(switches::kEnableInbandTextTracks)) |
| - return nullptr; |
| - |
| - return base::WrapUnique(new media::TextRenderer( |
| - media_task_runner_, |
| - base::Bind(&PipelineImpl::OnAddTextTrack, weak_this_))); |
| -} |
| - |
| -void PipelineImpl::AddTextStreamTask(DemuxerStream* text_stream, |
| - const TextTrackConfig& config) { |
| - DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| // TODO(matthewjheaney): fix up text_ended_ when text stream |
| // is added (http://crbug.com/321446). |
| if (text_renderer_) |
| text_renderer_->AddTextStream(text_stream, config); |
| } |
| -void PipelineImpl::RemoveTextStreamTask(DemuxerStream* text_stream) { |
| +void PipelineImpl::RendererWrapper::RemoveTextStreamTask( |
| + DemuxerStream* text_stream) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| if (text_renderer_) |
| text_renderer_->RemoveTextStream(text_stream); |
| } |
| -void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, |
| - const AddTextTrackDoneCB& done_cb) { |
| - DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| - |
| - main_task_runner_->PostTask( |
| - FROM_HERE, base::Bind(&Pipeline::Client::OnAddTextTrack, weak_client_, |
| - config, done_cb)); |
| -} |
| - |
| -void PipelineImpl::InitializeDemuxer(const PipelineStatusCB& done_cb) { |
| +void PipelineImpl::RendererWrapper::InitializeDemuxer( |
| + const PipelineStatusCB& done_cb) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| demuxer_->Initialize(this, done_cb, !!text_renderer_); |
| } |
| -void PipelineImpl::InitializeRenderer(const PipelineStatusCB& done_cb) { |
| +void PipelineImpl::RendererWrapper::InitializeRenderer( |
| + const PipelineStatusCB& done_cb) { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| if (!demuxer_->GetStream(DemuxerStream::AUDIO) && |
| !demuxer_->GetStream(DemuxerStream::VIDEO)) { |
| - { |
| - base::AutoLock auto_lock(lock_); |
| - renderer_.reset(); |
| - } |
| - OnError(PIPELINE_ERROR_COULD_NOT_RENDER); |
| + done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER); |
| return; |
| } |
| @@ -937,7 +985,7 @@ void PipelineImpl::InitializeRenderer(const PipelineStatusCB& done_cb) { |
| renderer_->Initialize(demuxer_, this, done_cb); |
| } |
| -void PipelineImpl::ReportMetadata() { |
| +void PipelineImpl::RendererWrapper::ReportMetadata() { |
| DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| PipelineMetadata metadata; |
| @@ -952,9 +1000,208 @@ void PipelineImpl::ReportMetadata() { |
| metadata.has_audio = true; |
| } |
| - main_task_runner_->PostTask( |
| - FROM_HERE, |
| - base::Bind(&Pipeline::Client::OnMetadata, weak_client_, metadata)); |
| + main_task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::OnMetadata, |
| + weak_pipeline_, metadata)); |
| +} |
| + |
| +void PipelineImpl::OnError(PipelineStatus error) { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; |
| + |
| + if (!IsRunning()) |
| + return; |
| + |
| + // If the error happens during starting/seeking/suspending/resuming, |
| + // report the error via the completion callback for those tasks. |
| + // Else report error via the client interface. |
| + if (!seek_cb_.is_null()) { |
| + base::ResetAndReturn(&seek_cb_).Run(error); |
| + } else if (!suspend_cb_.is_null()) { |
| + base::ResetAndReturn(&suspend_cb_).Run(error); |
| + } else { |
| + DCHECK(client_); |
| + client_->OnError(error); |
| + } |
| + |
| + // Any kind of error stops the pipeline. |
| + Stop(); |
| +} |
| + |
| +void PipelineImpl::OnEnded() { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnEnded(); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnMetadata(PipelineMetadata metadata) { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnMetadata(metadata); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnBufferingStateChange(BufferingState state) { |
| + DVLOG(2) << __FUNCTION__ << "(" << state << ")"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnBufferingStateChange(state); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnDurationChange(base::TimeDelta duration) { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + duration_ = duration; |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnDurationChange(); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnAddTextTrack(const TextTrackConfig& config, |
| + const AddTextTrackDoneCB& done_cb) { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnAddTextTrack(config, done_cb); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnWaitingForDecryptionKey() { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnWaitingForDecryptionKey(); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnVideoNaturalSizeChange(const gfx::Size& size) { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnVideoNaturalSizeChange(size); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
| + DVLOG(2) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + if (IsRunning()) { |
| + DCHECK(client_); |
| + client_->OnVideoOpacityChange(opaque); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnBufferedTimeRangesChange( |
| + const Ranges<base::TimeDelta>& ranges) { |
| + DVLOG(3) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + buffered_time_ranges_ = ranges; |
| + did_loading_progress_ = true; |
| +} |
| + |
| +void PipelineImpl::OnStatisticsUpdate(const PipelineStatistics& stats) { |
| + DVLOG(3) << __FUNCTION__; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
| + statistics_.video_bytes_decoded += stats.video_bytes_decoded; |
| + statistics_.video_frames_decoded += stats.video_frames_decoded; |
| + statistics_.video_frames_dropped += stats.video_frames_dropped; |
| + statistics_.audio_memory_usage += stats.audio_memory_usage; |
| + statistics_.video_memory_usage += stats.video_memory_usage; |
| +} |
| + |
| +void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { |
| + DVLOG(3) << __FUNCTION__ << "(" << start_time.InMicroseconds() << ")"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // Reset the suspend_time now that the pipeline is playing. |
| + // Media time will now be reported by renderer. |
| + suspend_time_ = kNoTimestamp(); |
| + |
| + if (IsRunning()) { |
| + DCHECK(!seek_cb_.is_null()); |
| + base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
| + } |
| +} |
| + |
| +void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { |
| + DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")"; |
| + DCHECK(thread_checker_.CalledOnValidThread()); |
| + |
| + // Cache the time at which pipeline was suspended. |
| + // It will be used to report media time while the pipeline is suspended. |
| + suspend_time_ = suspend_time; |
| + |
| + // Reset audio-video memory usage since renderer has been destroyed. |
| + statistics_.audio_memory_usage = 0; |
| + statistics_.video_memory_usage = 0; |
| + |
| + if (IsRunning()) { |
| + DCHECK(!suspend_cb_.is_null()); |
| + base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK); |
| + } |
| +} |
| + |
| +// Note that the usage of base::Unretained() with the renderers and demuxer |
| +// is safe as they are owned by |pending_callbacks_| and share the same |
| +// lifetime. That said, deleting the renderers while keeping |
| +// |pending_callbacks_| running on the media thread would result in crashes. |
|
sandersd (OOO until July 31)
2016/06/09 19:30:26
This comment should be moved to where base::Unreta
alokp
2016/06/10 00:06:02
Done.
|
| + |
| +PipelineImpl::RendererWrapper::RendererWrapper( |
| + base::WeakPtr<PipelineImpl> weak_pipeline, |
| + scoped_refptr<base::SingleThreadTaskRunner> media_task_runner, |
| + scoped_refptr<MediaLog> media_log) |
| + : weak_pipeline_(weak_pipeline), |
| + media_task_runner_(std::move(media_task_runner)), |
| + main_task_runner_(base::ThreadTaskRunnerHandle::Get()), |
| + media_log_(std::move(media_log)), |
| + demuxer_(nullptr), |
| + playback_rate_(kDefaultPlaybackRate), |
| + volume_(kDefaultVolume), |
| + cdm_context_(nullptr), |
| + state_(kCreated), |
| + status_(PIPELINE_OK), |
| + renderer_ended_(false), |
| + text_renderer_ended_(false), |
| + weak_factory_(this) { |
| + media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| +} |
| + |
| +PipelineImpl::RendererWrapper::~RendererWrapper() { |
| + DCHECK(media_task_runner_->BelongsToCurrentThread()); |
| + DCHECK(state_ == kCreated || state_ == kStopped); |
| +} |
| + |
| +base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() { |
| + // This is the only member function that gets called on the main thread. |
| + // TODO(alokp): Enforce that Renderer is only called on a single thread, |
| + // even for accessing media time http://crbug.com/370634. |
| + DCHECK(main_task_runner_->BelongsToCurrentThread()); |
| + |
| + base::AutoLock auto_lock(renderer_lock_); |
| + return renderer_ ? renderer_->GetMediaTime() : base::TimeDelta(); |
| } |
| } // namespace media |