Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(372)

Unified Diff: media/base/pipeline_impl.cc

Issue 2097623002: Rearrange function definitions in the same order as declared. (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@master
Patch Set: Created 4 years, 6 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « no previous file | no next file » | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: media/base/pipeline_impl.cc
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 36747bd48b3cd7ba73421aa5d2de35966c903170..ddbd0cea8e372c56a6be4492630e9bd0883d0b87 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -105,20 +105,25 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
void OnVideoNaturalSizeChange(const gfx::Size& size) final;
void OnVideoOpacityChange(bool opaque) final;
+ // TextRenderer tasks and notifications.
+ void OnTextRendererEnded();
+ void AddTextStreamTask(DemuxerStream* text_stream,
+ const TextTrackConfig& config);
+ void RemoveTextStreamTask(DemuxerStream* text_stream);
+
+ // Common handlers for notifications from renderers and demuxer.
+ void OnPipelineError(PipelineStatus error);
+ void OnCdmAttached(const CdmAttachedCB& cdm_attached_cb,
+ CdmContext* cdm_context,
+ bool success);
+ void CheckPlaybackEnded();
+
+ // State transition tasks.
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 DestroyRenderer();
@@ -166,310 +171,276 @@ class PipelineImpl::RendererWrapper : public DemuxerHost,
DISALLOW_COPY_AND_ASSIGN(RendererWrapper);
};
-PipelineImpl::PipelineImpl(
- const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
- MediaLog* media_log)
- : media_task_runner_(media_task_runner),
- media_log_(media_log),
- client_(nullptr),
+PipelineImpl::RendererWrapper::RendererWrapper(
+ scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
+ scoped_refptr<MediaLog> media_log)
+ : 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) {
- DVLOG(2) << __FUNCTION__;
- renderer_wrapper_.reset(new RendererWrapper(media_task_runner_, media_log_));
+ weak_this_ = weak_factory_.GetWeakPtr();
+ media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated));
}
-PipelineImpl::~PipelineImpl() {
- 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());
- DCHECK(!weak_factory_.HasWeakPtrs());
-
- // RendererWrapper is deleted on the media thread.
- media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release());
+PipelineImpl::RendererWrapper::~RendererWrapper() {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
+ DCHECK(state_ == kCreated || state_ == kStopped);
}
-void PipelineImpl::Start(Demuxer* demuxer,
- std::unique_ptr<Renderer> renderer,
- Client* client,
- const PipelineStatusCB& seek_cb) {
- DVLOG(2) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(demuxer);
- DCHECK(renderer);
- DCHECK(client);
- DCHECK(!seek_cb.is_null());
-
- DCHECK(!client_);
- DCHECK(seek_cb_.is_null());
- client_ = client;
- seek_cb_ = seek_cb;
+void PipelineImpl::RendererWrapper::Start(
+ Demuxer* demuxer,
+ std::unique_ptr<Renderer> renderer,
+ std::unique_ptr<TextRenderer> text_renderer,
+ base::WeakPtr<PipelineImpl> weak_pipeline) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
+ DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: "
+ << state_;
- std::unique_ptr<TextRenderer> text_renderer;
- if (base::CommandLine::ForCurrentProcess()->HasSwitch(
- switches::kEnableInbandTextTracks)) {
- text_renderer.reset(new TextRenderer(
- media_task_runner_,
- BindToCurrentLoop(base::Bind(&PipelineImpl::OnAddTextTrack,
- weak_factory_.GetWeakPtr()))));
+ DCHECK(!demuxer_);
+ DCHECK(!shared_state_.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);
+ }
+ text_renderer_ = std::move(text_renderer);
+ if (text_renderer_) {
+ text_renderer_->Initialize(
+ base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_));
}
+ weak_pipeline_ = weak_pipeline;
- media_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&RendererWrapper::Start,
- base::Unretained(renderer_wrapper_.get()), demuxer,
- base::Passed(&renderer), base::Passed(&text_renderer),
- weak_factory_.GetWeakPtr()));
+ StateTransitionTask(PIPELINE_OK);
}
-void PipelineImpl::Stop() {
- DVLOG(2) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
+void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
+ DCHECK(state_ != kStopping && state_ != kStopped);
- if (!IsRunning()) {
- DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()";
- return;
- }
+ SetState(kStopping);
- 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(
- 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): 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.
- 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(media_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&RendererWrapper::Stop,
- base::Unretained(renderer_wrapper_.get()), stop_cb)));
- waiter.Wait();
+ if (shared_state_.statistics.video_frames_decoded > 0) {
+ UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount",
+ shared_state_.statistics.video_frames_dropped);
}
- // 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;
+ // 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();
- // Invalidate self weak pointers effectively canceling all pending
- // notifications in the message queue.
- weak_factory_.InvalidateWeakPtrs();
+ DoStop(stop_cb);
}
-void PipelineImpl::Seek(base::TimeDelta time, const PipelineStatusCB& seek_cb) {
- DVLOG(2) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(!seek_cb.is_null());
+void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- if (!IsRunning()) {
- DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
+ // Suppress seeking if we're not fully started.
+ if (state_ != kPlaying) {
+ DCHECK(state_ == kStopping || state_ == kStopped)
+ << "Receive seek in unexpected state: " << state_;
+ OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
return;
}
- DCHECK(seek_cb_.is_null());
- seek_cb_ = seek_cb;
- media_task_runner_->PostTask(
- FROM_HERE, base::Bind(&RendererWrapper::Seek,
- base::Unretained(renderer_wrapper_.get()), time));
-}
+ const base::TimeDelta seek_timestamp =
+ std::max(time, demuxer_->GetStartTime());
-bool PipelineImpl::IsRunning() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return !!client_;
-}
+ SetState(kSeeking);
+ renderer_ended_ = false;
+ text_renderer_ended_ = false;
+ start_timestamp_ = seek_timestamp;
-double PipelineImpl::GetPlaybackRate() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return playback_rate_;
+ DoSeek(seek_timestamp,
+ base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
}
-void PipelineImpl::SetPlaybackRate(double playback_rate) {
- DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")";
- DCHECK(thread_checker_.CalledOnValidThread());
+void PipelineImpl::RendererWrapper::Suspend() {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- if (playback_rate < 0.0)
+ // Suppress suspending if we're not playing.
+ if (state_ != kPlaying) {
+ DCHECK(state_ == kStopping || state_ == kStopped)
+ << "Receive suspend in unexpected state: " << state_;
+ OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
return;
+ }
+ DCHECK(shared_state_.renderer);
+ DCHECK(!pending_callbacks_.get());
- playback_rate_ = playback_rate;
- media_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&RendererWrapper::SetPlaybackRate,
- base::Unretained(renderer_wrapper_.get()), playback_rate_));
-}
+ SetState(kSuspending);
-void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) {
- DVLOG(2) << __FUNCTION__;
- DCHECK(!suspend_cb.is_null());
+ // Freeze playback and record the media time before flushing. (Flushing clears
+ // the value.)
+ shared_state_.renderer->SetPlaybackRate(0.0);
+ {
+ base::AutoLock auto_lock(shared_state_lock_);
+ shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime();
+ DCHECK(shared_state_.suspend_timestamp != kNoTimestamp());
+ }
- DCHECK(IsRunning());
- DCHECK(suspend_cb_.is_null());
- suspend_cb_ = suspend_cb;
+ // Queue the asynchronous actions required to stop playback. (Matches setup in
+ // DoSeek().)
+ // TODO(sandersd): Share implementation with DoSeek().
+ SerialRunner::Queue fns;
- media_task_runner_->PostTask(
- FROM_HERE, base::Bind(&RendererWrapper::Suspend,
- base::Unretained(renderer_wrapper_.get())));
-}
+ if (text_renderer_) {
+ fns.Push(base::Bind(&TextRenderer::Pause,
+ base::Unretained(text_renderer_.get())));
+ }
-void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer,
- base::TimeDelta time,
- const PipelineStatusCB& seek_cb) {
- DVLOG(2) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(renderer);
- DCHECK(!seek_cb.is_null());
+ fns.Push(base::Bind(&Renderer::Flush,
+ base::Unretained(shared_state_.renderer.get())));
- DCHECK(IsRunning());
- DCHECK(seek_cb_.is_null());
- seek_cb_ = seek_cb;
-
- media_task_runner_->PostTask(
- FROM_HERE, base::Bind(&RendererWrapper::Resume,
- base::Unretained(renderer_wrapper_.get()),
- base::Passed(&renderer), time));
-}
+ if (text_renderer_) {
+ fns.Push(base::Bind(&TextRenderer::Flush,
+ base::Unretained(text_renderer_.get())));
+ }
-float PipelineImpl::GetVolume() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return volume_;
+ pending_callbacks_ = SerialRunner::Run(
+ fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
}
-void PipelineImpl::SetVolume(float volume) {
- DVLOG(2) << __FUNCTION__ << "(" << volume << ")";
- DCHECK(thread_checker_.CalledOnValidThread());
+void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer,
+ base::TimeDelta timestamp) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- if (volume < 0.0f || volume > 1.0f)
+ // Suppress resuming if we're not suspended.
+ if (state_ != kSuspended) {
+ DCHECK(state_ == kStopping || state_ == kStopped)
+ << "Receive resume in unexpected state: " << state_;
+ OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
return;
+ }
+ DCHECK(!shared_state_.renderer);
+ DCHECK(!pending_callbacks_.get());
- volume_ = volume;
- media_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&RendererWrapper::SetVolume,
- base::Unretained(renderer_wrapper_.get()), volume_));
-}
+ SetState(kResuming);
-base::TimeDelta PipelineImpl::GetMediaTime() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return renderer_wrapper_->GetMediaTime();
-}
+ {
+ base::AutoLock auto_lock(shared_state_lock_);
+ shared_state_.renderer = std::move(renderer);
+ }
-Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return renderer_wrapper_->GetBufferedTimeRanges();
-}
+ // Set up for a seek. (Matches setup in SeekTask().)
+ // TODO(sandersd): Share implementation with SeekTask().
+ renderer_ended_ = false;
+ text_renderer_ended_ = false;
+ start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime());
-base::TimeDelta PipelineImpl::GetMediaDuration() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return duration_;
-}
+ // Queue the asynchronous actions required to start playback. Unlike DoSeek(),
+ // we need to initialize the renderer ourselves (we don't want to enter state
+ // kInitDemuxer, and even if we did the current code would seek to the start
+ // instead of |timestamp|).
+ SerialRunner::Queue fns;
-bool PipelineImpl::DidLoadingProgress() {
- DCHECK(thread_checker_.CalledOnValidThread());
- return renderer_wrapper_->DidLoadingProgress();
-}
+ fns.Push(
+ base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_));
-PipelineStatistics PipelineImpl::GetStatistics() const {
- DCHECK(thread_checker_.CalledOnValidThread());
- return renderer_wrapper_->GetStatistics();
+ fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_));
+
+ pending_callbacks_ = SerialRunner::Run(
+ fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
}
-void PipelineImpl::SetCdm(CdmContext* cdm_context,
- const CdmAttachedCB& cdm_attached_cb) {
- DVLOG(2) << __FUNCTION__;
- DCHECK(thread_checker_.CalledOnValidThread());
- DCHECK(cdm_context);
- DCHECK(!cdm_attached_cb.is_null());
+void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- media_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&RendererWrapper::SetCdm,
- base::Unretained(renderer_wrapper_.get()), cdm_context,
- media::BindToCurrentLoop(cdm_attached_cb)));
+ playback_rate_ = playback_rate;
+ if (state_ == kPlaying)
+ shared_state_.renderer->SetPlaybackRate(playback_rate_);
}
-void PipelineImpl::RendererWrapper::SetState(State next_state) {
+void PipelineImpl::RendererWrapper::SetVolume(float volume) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
- DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> "
- << PipelineImpl::GetStateString(next_state);
- state_ = next_state;
- media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
+ volume_ = volume;
+ if (state_ == kPlaying)
+ shared_state_.renderer->SetVolume(volume_);
}
-#define RETURN_STRING(state) \
- case state: \
- return #state;
+base::TimeDelta PipelineImpl::RendererWrapper::GetMediaTime() const {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
-// static
-const char* PipelineImpl::GetStateString(State state) {
- switch (state) {
- RETURN_STRING(kCreated);
- RETURN_STRING(kInitDemuxer);
- RETURN_STRING(kInitRenderer);
- RETURN_STRING(kSeeking);
- RETURN_STRING(kPlaying);
- RETURN_STRING(kStopping);
- RETURN_STRING(kStopped);
- RETURN_STRING(kSuspending);
- RETURN_STRING(kSuspended);
- RETURN_STRING(kResuming);
- }
- NOTREACHED();
- return "INVALID";
+ 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();
}
-#undef RETURN_STRING
-
-PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
- DCHECK_EQ(status_, PIPELINE_OK)
- << "State transitions don't happen when there's an error: " << status_;
+Ranges<base::TimeDelta> PipelineImpl::RendererWrapper::GetBufferedTimeRanges()
+ const {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
- switch (state_) {
- case kCreated:
- return kInitDemuxer;
+ base::AutoLock auto_lock(shared_state_lock_);
+ return shared_state_.buffered_time_ranges;
+}
- case kInitDemuxer:
- return kInitRenderer;
+bool PipelineImpl::RendererWrapper::DidLoadingProgress() {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
- case kInitRenderer:
- case kSeeking:
- return kPlaying;
+ base::AutoLock auto_lock(shared_state_lock_);
+ bool did_progress = shared_state_.did_loading_progress;
+ shared_state_.did_loading_progress = false;
+ return did_progress;
+}
- case kSuspending:
- return kSuspended;
+PipelineStatistics PipelineImpl::RendererWrapper::GetStatistics() const {
+ DCHECK(main_task_runner_->BelongsToCurrentThread());
- case kSuspended:
- return kResuming;
+ base::AutoLock auto_lock(shared_state_lock_);
+ return shared_state_.statistics;
+}
- case kResuming:
- return kPlaying;
+void PipelineImpl::RendererWrapper::SetCdm(
+ CdmContext* cdm_context,
+ const CdmAttachedCB& cdm_attached_cb) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- case kPlaying:
- case kStopping:
- case kStopped:
- break;
+ if (!shared_state_.renderer) {
+ cdm_context_ = cdm_context;
+ cdm_attached_cb.Run(true);
+ return;
}
- NOTREACHED() << "State has no transition: " << state_;
- return state_;
+
+ shared_state_.renderer->SetCdm(
+ cdm_context, base::Bind(&RendererWrapper::OnCdmAttached, weak_this_,
+ cdm_attached_cb, cdm_context));
+}
+
+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(shared_state_lock_);
+ shared_state_.did_loading_progress = true;
+ shared_state_.buffered_time_ranges = ranges;
+}
+
+void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) {
+ // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
+ // implementations call DemuxerHost on the media thread.
+ media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET,
+ "duration", duration));
+ UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
+
+ main_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&PipelineImpl::OnDurationChange, weak_pipeline_, duration));
}
void PipelineImpl::RendererWrapper::OnDemuxerError(PipelineStatus error) {
@@ -516,7 +487,7 @@ void PipelineImpl::RendererWrapper::OnEnded() {
DCHECK(!renderer_ended_);
renderer_ended_ = true;
- RunEndedCallbackIfNeeded();
+ CheckPlaybackEnded();
}
void PipelineImpl::RendererWrapper::OnStatisticsUpdate(
@@ -568,98 +539,84 @@ void PipelineImpl::RendererWrapper::OnVideoOpacityChange(bool opaque) {
base::Bind(&PipelineImpl::OnVideoOpacityChange, weak_pipeline_, opaque));
}
-void PipelineImpl::RendererWrapper::SetDuration(base::TimeDelta duration) {
- // TODO(alokp): Add thread DCHECK after ensuring that all Demuxer
- // implementations call DemuxerHost on the media thread.
- media_log_->AddEvent(media_log_->CreateTimeEvent(MediaLogEvent::DURATION_SET,
- "duration", duration));
- UMA_HISTOGRAM_LONG_TIMES("Media.Duration", duration);
+void PipelineImpl::RendererWrapper::OnTextRendererEnded() {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
+ media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
- main_task_runner_->PostTask(
- FROM_HERE,
- base::Bind(&PipelineImpl::OnDurationChange, weak_pipeline_, duration));
+ if (state_ != kPlaying)
+ return;
+
+ DCHECK(!text_renderer_ended_);
+ text_renderer_ended_ = true;
+ CheckPlaybackEnded();
}
-void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) {
+void PipelineImpl::RendererWrapper::AddTextStreamTask(
+ DemuxerStream* text_stream,
+ const TextTrackConfig& config) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
- // No-op any state transitions if we're stopping or already encountered error.
- if (state_ == kStopping || state_ == kStopped || status_ != PIPELINE_OK)
- return;
-
- // Report error from the previous operation.
- if (status != PIPELINE_OK) {
- OnPipelineError(status);
- return;
- }
+ // TODO(matthewjheaney): fix up text_ended_ when text stream
+ // is added (http://crbug.com/321446).
+ if (text_renderer_)
+ text_renderer_->AddTextStream(text_stream, config);
+}
- // Guard against accidentally clearing |pending_callbacks_| for states that
- // use it as well as states that should not be using it.
- DCHECK_EQ(pending_callbacks_.get() != NULL,
- state_ == kSeeking || state_ == kSuspending || state_ == kResuming);
+void PipelineImpl::RendererWrapper::RemoveTextStreamTask(
+ DemuxerStream* text_stream) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- pending_callbacks_.reset();
+ if (text_renderer_)
+ text_renderer_->RemoveTextStream(text_stream);
+}
- PipelineStatusCB done_cb =
- base::Bind(&RendererWrapper::StateTransitionTask, weak_this_);
+void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
+ DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
- // Switch states, performing any entrance actions for the new state as well.
- SetState(GetNextState());
- switch (state_) {
- case kInitDemuxer:
- return InitializeDemuxer(done_cb);
+ // Preserve existing abnormal status.
+ if (status_ != PIPELINE_OK)
+ return;
- case kInitRenderer:
- // When the state_ transfers to kInitRenderer, it means the demuxer has
- // finished parsing the init info. It should call ReportMetadata in case
- // meeting 'decode' error when passing media segment but WebMediaPlayer's
- // ready_state_ is still ReadyStateHaveNothing. In that case, it will
- // treat it as NetworkStateFormatError not NetworkStateDecodeError.
- ReportMetadata();
- start_timestamp_ = demuxer_->GetStartTime();
+ // 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;
+ }
- return InitializeRenderer(done_cb);
+ status_ = error;
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PipelineImpl::OnError, weak_pipeline_, error));
+}
- case kPlaying:
- DCHECK(start_timestamp_ >= base::TimeDelta());
- shared_state_.renderer->StartPlayingFrom(start_timestamp_);
- {
- base::AutoLock auto_lock(shared_state_lock_);
- shared_state_.suspend_timestamp = kNoTimestamp();
- }
+void PipelineImpl::RendererWrapper::OnCdmAttached(
+ const CdmAttachedCB& cdm_attached_cb,
+ CdmContext* cdm_context,
+ bool success) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- if (text_renderer_)
- text_renderer_->StartPlaying();
+ if (success)
+ cdm_context_ = cdm_context;
+ cdm_attached_cb.Run(success);
+}
- main_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_,
- start_timestamp_));
+void PipelineImpl::RendererWrapper::CheckPlaybackEnded() {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- shared_state_.renderer->SetPlaybackRate(playback_rate_);
- shared_state_.renderer->SetVolume(volume_);
- return;
+ if (shared_state_.renderer && !renderer_ended_)
+ return;
- case kSuspended:
- DestroyRenderer();
- {
- base::AutoLock auto_lock(shared_state_lock_);
- shared_state_.statistics.audio_memory_usage = 0;
- shared_state_.statistics.video_memory_usage = 0;
- }
- main_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_,
- shared_state_.suspend_timestamp));
- return;
+ if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
+ return;
- case kStopping:
- case kStopped:
- case kCreated:
- case kSeeking:
- case kSuspending:
- case kResuming:
- NOTREACHED() << "State has no transition: " << state_;
- return;
- }
+ DCHECK_EQ(status_, PIPELINE_OK);
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_));
}
// Note that the usage of base::Unretained() with the renderers is considered
@@ -722,368 +679,448 @@ void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) {
media_task_runner_->PostTask(FROM_HERE, done_cb);
}
-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(shared_state_lock_);
- shared_state_.did_loading_progress = true;
- shared_state_.buffered_time_ranges = ranges;
+void PipelineImpl::RendererWrapper::SetState(State next_state) {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
+ DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> "
+ << PipelineImpl::GetStateString(next_state);
+
+ state_ = next_state;
+ media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state));
}
-void PipelineImpl::RendererWrapper::Start(
- Demuxer* demuxer,
- std::unique_ptr<Renderer> renderer,
- std::unique_ptr<TextRenderer> text_renderer,
- base::WeakPtr<PipelineImpl> weak_pipeline) {
+PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const {
DCHECK(media_task_runner_->BelongsToCurrentThread());
- DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: "
- << state_;
+ DCHECK_EQ(status_, PIPELINE_OK)
+ << "State transitions don't happen when there's an error: " << status_;
- DCHECK(!demuxer_);
- DCHECK(!shared_state_.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);
- }
- text_renderer_ = std::move(text_renderer);
- if (text_renderer_) {
- text_renderer_->Initialize(
- base::Bind(&RendererWrapper::OnTextRendererEnded, weak_this_));
- }
- weak_pipeline_ = weak_pipeline;
+ switch (state_) {
+ case kCreated:
+ return kInitDemuxer;
- StateTransitionTask(PIPELINE_OK);
-}
+ case kInitDemuxer:
+ return kInitRenderer;
-void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
- DCHECK(state_ != kStopping && state_ != kStopped);
+ case kInitRenderer:
+ case kSeeking:
+ return kPlaying;
- SetState(kStopping);
+ case kSuspending:
+ return kSuspended;
- if (shared_state_.statistics.video_frames_decoded > 0) {
- UMA_HISTOGRAM_COUNTS("Media.DroppedFrameCount",
- shared_state_.statistics.video_frames_dropped);
- }
+ case kSuspended:
+ return kResuming;
- // 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();
+ case kResuming:
+ return kPlaying;
- DoStop(stop_cb);
+ case kPlaying:
+ case kStopping:
+ case kStopped:
+ break;
+ }
+ NOTREACHED() << "State has no transition: " << state_;
+ return state_;
}
-
-void PipelineImpl::RendererWrapper::OnPipelineError(PipelineStatus error) {
+void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
- DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
- // Preserve existing abnormal status.
- if (status_ != PIPELINE_OK)
+ // No-op any state transitions if we're stopping or already encountered error.
+ if (state_ == kStopping || state_ == kStopped || 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.
- if (state_ == kStopping || state_ == kStopped || state_ == kSuspending ||
- state_ == kSuspended) {
+ // Report error from the previous operation.
+ if (status != PIPELINE_OK) {
+ OnPipelineError(status);
return;
}
- status_ = error;
- main_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PipelineImpl::OnError, weak_pipeline_, error));
-}
+ // Guard against accidentally clearing |pending_callbacks_| for states that
+ // use it as well as states that should not be using it.
+ DCHECK_EQ(pending_callbacks_.get() != NULL,
+ state_ == kSeeking || state_ == kSuspending || state_ == kResuming);
-void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+ pending_callbacks_.reset();
- playback_rate_ = playback_rate;
- if (state_ == kPlaying)
- shared_state_.renderer->SetPlaybackRate(playback_rate_);
+ PipelineStatusCB done_cb =
+ base::Bind(&RendererWrapper::StateTransitionTask, weak_this_);
+
+ // Switch states, performing any entrance actions for the new state as well.
+ SetState(GetNextState());
+ switch (state_) {
+ case kInitDemuxer:
+ return InitializeDemuxer(done_cb);
+
+ case kInitRenderer:
+ // When the state_ transfers to kInitRenderer, it means the demuxer has
+ // finished parsing the init info. It should call ReportMetadata in case
+ // meeting 'decode' error when passing media segment but WebMediaPlayer's
+ // ready_state_ is still ReadyStateHaveNothing. In that case, it will
+ // treat it as NetworkStateFormatError not NetworkStateDecodeError.
+ ReportMetadata();
+ start_timestamp_ = demuxer_->GetStartTime();
+
+ return InitializeRenderer(done_cb);
+
+ case kPlaying:
+ DCHECK(start_timestamp_ >= base::TimeDelta());
+ shared_state_.renderer->StartPlayingFrom(start_timestamp_);
+ {
+ base::AutoLock auto_lock(shared_state_lock_);
+ shared_state_.suspend_timestamp = kNoTimestamp();
+ }
+
+ if (text_renderer_)
+ text_renderer_->StartPlaying();
+
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_,
+ start_timestamp_));
+
+ shared_state_.renderer->SetPlaybackRate(playback_rate_);
+ shared_state_.renderer->SetVolume(volume_);
+ return;
+
+ case kSuspended:
+ DestroyRenderer();
+ {
+ base::AutoLock auto_lock(shared_state_lock_);
+ shared_state_.statistics.audio_memory_usage = 0;
+ shared_state_.statistics.video_memory_usage = 0;
+ }
+ main_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_,
+ shared_state_.suspend_timestamp));
+ return;
+
+ case kStopping:
+ case kStopped:
+ case kCreated:
+ case kSeeking:
+ case kSuspending:
+ case kResuming:
+ NOTREACHED() << "State has no transition: " << state_;
+ return;
+ }
}
-void PipelineImpl::RendererWrapper::SetVolume(float volume) {
+void PipelineImpl::RendererWrapper::InitializeDemuxer(
+ const PipelineStatusCB& done_cb) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
- volume_ = volume;
- if (state_ == kPlaying)
- shared_state_.renderer->SetVolume(volume_);
+ demuxer_->Initialize(this, done_cb, !!text_renderer_);
}
-void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) {
+void PipelineImpl::RendererWrapper::InitializeRenderer(
+ const PipelineStatusCB& done_cb) {
DCHECK(media_task_runner_->BelongsToCurrentThread());
- // Suppress seeking if we're not fully started.
- if (state_ != kPlaying) {
- DCHECK(state_ == kStopping || state_ == kStopped)
- << "Receive seek in unexpected state: " << state_;
- OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
+ if (!demuxer_->GetStream(DemuxerStream::AUDIO) &&
+ !demuxer_->GetStream(DemuxerStream::VIDEO)) {
+ done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
return;
}
- const base::TimeDelta seek_timestamp =
- std::max(time, demuxer_->GetStartTime());
-
- SetState(kSeeking);
- renderer_ended_ = false;
- text_renderer_ended_ = false;
- start_timestamp_ = seek_timestamp;
+ if (cdm_context_)
+ shared_state_.renderer->SetCdm(cdm_context_,
+ base::Bind(&IgnoreCdmAttached));
- DoSeek(seek_timestamp,
- base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
+ shared_state_.renderer->Initialize(demuxer_, this, done_cb);
}
-void PipelineImpl::RendererWrapper::Suspend() {
+void PipelineImpl::RendererWrapper::DestroyRenderer() {
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_;
- OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
- return;
- }
- DCHECK(shared_state_.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);
+ // 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_);
- shared_state_.suspend_timestamp = shared_state_.renderer->GetMediaTime();
- DCHECK(shared_state_.suspend_timestamp != kNoTimestamp());
+ renderer.swap(shared_state_.renderer);
}
+}
- // Queue the asynchronous actions required to stop playback. (Matches setup in
- // DoSeek().)
- // TODO(sandersd): Share implementation with DoSeek().
- SerialRunner::Queue fns;
+void PipelineImpl::RendererWrapper::ReportMetadata() {
+ DCHECK(media_task_runner_->BelongsToCurrentThread());
- if (text_renderer_) {
- fns.Push(base::Bind(&TextRenderer::Pause,
- base::Unretained(text_renderer_.get())));
+ PipelineMetadata metadata;
+ metadata.timeline_offset = demuxer_->GetTimelineOffset();
+ DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
+ if (stream) {
+ metadata.has_video = true;
+ metadata.natural_size = stream->video_decoder_config().natural_size();
+ metadata.video_rotation = stream->video_rotation();
}
-
- fns.Push(base::Bind(&Renderer::Flush,
- base::Unretained(shared_state_.renderer.get())));
-
- if (text_renderer_) {
- fns.Push(base::Bind(&TextRenderer::Flush,
- base::Unretained(text_renderer_.get())));
+ if (demuxer_->GetStream(DemuxerStream::AUDIO)) {
+ metadata.has_audio = true;
}
- pending_callbacks_ = SerialRunner::Run(
- fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
+ main_task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::OnMetadata,
+ weak_pipeline_, metadata));
}
-void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer,
- base::TimeDelta timestamp) {
- 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_;
- OnPipelineError(PIPELINE_ERROR_INVALID_STATE);
- return;
- }
- DCHECK(!shared_state_.renderer);
- DCHECK(!pending_callbacks_.get());
-
- SetState(kResuming);
+PipelineImpl::PipelineImpl(
+ const scoped_refptr<base::SingleThreadTaskRunner>& media_task_runner,
+ MediaLog* media_log)
+ : media_task_runner_(media_task_runner),
+ media_log_(media_log),
+ client_(nullptr),
+ playback_rate_(kDefaultPlaybackRate),
+ volume_(kDefaultVolume),
+ weak_factory_(this) {
+ DVLOG(2) << __FUNCTION__;
+ renderer_wrapper_.reset(new RendererWrapper(media_task_runner_, media_log_));
+}
- {
- base::AutoLock auto_lock(shared_state_lock_);
- shared_state_.renderer = std::move(renderer);
- }
+PipelineImpl::~PipelineImpl() {
+ 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());
+ DCHECK(!weak_factory_.HasWeakPtrs());
- // Set up for a seek. (Matches setup in SeekTask().)
- // TODO(sandersd): Share implementation with SeekTask().
- renderer_ended_ = false;
- text_renderer_ended_ = false;
- start_timestamp_ = std::max(timestamp, demuxer_->GetStartTime());
+ // RendererWrapper is deleted on the media thread.
+ media_task_runner_->DeleteSoon(FROM_HERE, renderer_wrapper_.release());
+}
- // Queue the asynchronous actions required to start playback. Unlike DoSeek(),
- // we need to initialize the renderer ourselves (we don't want to enter state
- // kInitDemuxer, and even if we did the current code would seek to the start
- // instead of |timestamp|).
- SerialRunner::Queue fns;
+void PipelineImpl::Start(Demuxer* demuxer,
+ std::unique_ptr<Renderer> renderer,
+ Client* client,
+ const PipelineStatusCB& seek_cb) {
+ DVLOG(2) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(demuxer);
+ DCHECK(renderer);
+ DCHECK(client);
+ DCHECK(!seek_cb.is_null());
- fns.Push(
- base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_));
+ DCHECK(!client_);
+ DCHECK(seek_cb_.is_null());
+ client_ = client;
+ seek_cb_ = seek_cb;
- fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_));
+ std::unique_ptr<TextRenderer> text_renderer;
+ if (base::CommandLine::ForCurrentProcess()->HasSwitch(
+ switches::kEnableInbandTextTracks)) {
+ text_renderer.reset(new TextRenderer(
+ media_task_runner_,
+ BindToCurrentLoop(base::Bind(&PipelineImpl::OnAddTextTrack,
+ weak_factory_.GetWeakPtr()))));
+ }
- pending_callbacks_ = SerialRunner::Run(
- fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_));
+ media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&RendererWrapper::Start,
+ base::Unretained(renderer_wrapper_.get()), demuxer,
+ base::Passed(&renderer), base::Passed(&text_renderer),
+ weak_factory_.GetWeakPtr()));
}
-Ranges<base::TimeDelta> PipelineImpl::RendererWrapper::GetBufferedTimeRanges()
- const {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- base::AutoLock auto_lock(shared_state_lock_);
- return shared_state_.buffered_time_ranges;
-}
+void PipelineImpl::Stop() {
+ DVLOG(2) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
-bool PipelineImpl::RendererWrapper::DidLoadingProgress() {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+ if (!IsRunning()) {
+ DVLOG(2) << "Media pipeline isn't running. Ignoring Stop()";
+ return;
+ }
- base::AutoLock auto_lock(shared_state_lock_);
- bool did_progress = shared_state_.did_loading_progress;
- shared_state_.did_loading_progress = false;
- return did_progress;
-}
+ 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(
+ 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): 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.
+ 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(media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&RendererWrapper::Stop,
+ base::Unretained(renderer_wrapper_.get()), stop_cb)));
+ waiter.Wait();
+ }
-PipelineStatistics PipelineImpl::RendererWrapper::GetStatistics() const {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
+ // 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;
- base::AutoLock auto_lock(shared_state_lock_);
- return shared_state_.statistics;
+ // Invalidate self weak pointers effectively canceling all pending
+ // notifications in the message queue.
+ weak_factory_.InvalidateWeakPtrs();
}
-void PipelineImpl::RendererWrapper::SetCdm(
- CdmContext* cdm_context,
- const CdmAttachedCB& cdm_attached_cb) {
- DCHECK(media_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 (!shared_state_.renderer) {
- cdm_context_ = cdm_context;
- cdm_attached_cb.Run(true);
+ if (!IsRunning()) {
+ DLOG(ERROR) << "Media pipeline isn't running. Ignoring Seek().";
return;
}
- shared_state_.renderer->SetCdm(
- cdm_context, base::Bind(&RendererWrapper::OnCdmAttached, weak_this_,
- cdm_attached_cb, cdm_context));
+ DCHECK(seek_cb_.is_null());
+ seek_cb_ = seek_cb;
+ media_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&RendererWrapper::Seek,
+ base::Unretained(renderer_wrapper_.get()), time));
}
-void PipelineImpl::RendererWrapper::OnCdmAttached(
- const CdmAttachedCB& cdm_attached_cb,
- CdmContext* cdm_context,
- bool success) {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+void PipelineImpl::Suspend(const PipelineStatusCB& suspend_cb) {
+ DVLOG(2) << __FUNCTION__;
+ DCHECK(!suspend_cb.is_null());
- if (success)
- cdm_context_ = cdm_context;
- cdm_attached_cb.Run(success);
+ DCHECK(IsRunning());
+ DCHECK(suspend_cb_.is_null());
+ suspend_cb_ = suspend_cb;
+
+ media_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&RendererWrapper::Suspend,
+ base::Unretained(renderer_wrapper_.get())));
}
-void PipelineImpl::RendererWrapper::OnTextRendererEnded() {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
- media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::TEXT_ENDED));
+void PipelineImpl::Resume(std::unique_ptr<Renderer> renderer,
+ base::TimeDelta time,
+ const PipelineStatusCB& seek_cb) {
+ DVLOG(2) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(renderer);
+ DCHECK(!seek_cb.is_null());
- if (state_ != kPlaying)
- return;
+ DCHECK(IsRunning());
+ DCHECK(seek_cb_.is_null());
+ seek_cb_ = seek_cb;
- DCHECK(!text_renderer_ended_);
- text_renderer_ended_ = true;
+ media_task_runner_->PostTask(
+ FROM_HERE, base::Bind(&RendererWrapper::Resume,
+ base::Unretained(renderer_wrapper_.get()),
+ base::Passed(&renderer), time));
+}
- RunEndedCallbackIfNeeded();
+bool PipelineImpl::IsRunning() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return !!client_;
}
-void PipelineImpl::RendererWrapper::RunEndedCallbackIfNeeded() {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+double PipelineImpl::GetPlaybackRate() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return playback_rate_;
+}
- if (shared_state_.renderer && !renderer_ended_)
- return;
+void PipelineImpl::SetPlaybackRate(double playback_rate) {
+ DVLOG(2) << __FUNCTION__ << "(" << playback_rate << ")";
+ DCHECK(thread_checker_.CalledOnValidThread());
- if (text_renderer_ && text_renderer_->HasTracks() && !text_renderer_ended_)
+ if (playback_rate < 0.0)
return;
- DCHECK_EQ(status_, PIPELINE_OK);
- main_task_runner_->PostTask(
- FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_));
+ playback_rate_ = playback_rate;
+ media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&RendererWrapper::SetPlaybackRate,
+ base::Unretained(renderer_wrapper_.get()), playback_rate_));
}
-void PipelineImpl::RendererWrapper::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);
+float PipelineImpl::GetVolume() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return volume_;
}
-void PipelineImpl::RendererWrapper::RemoveTextStreamTask(
- DemuxerStream* text_stream) {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+void PipelineImpl::SetVolume(float volume) {
+ DVLOG(2) << __FUNCTION__ << "(" << volume << ")";
+ DCHECK(thread_checker_.CalledOnValidThread());
- if (text_renderer_)
- text_renderer_->RemoveTextStream(text_stream);
-}
+ if (volume < 0.0f || volume > 1.0f)
+ return;
-void PipelineImpl::RendererWrapper::InitializeDemuxer(
- const PipelineStatusCB& done_cb) {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+ volume_ = volume;
+ media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&RendererWrapper::SetVolume,
+ base::Unretained(renderer_wrapper_.get()), volume_));
+}
- demuxer_->Initialize(this, done_cb, !!text_renderer_);
+base::TimeDelta PipelineImpl::GetMediaTime() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return renderer_wrapper_->GetMediaTime();
}
-void PipelineImpl::RendererWrapper::InitializeRenderer(
- const PipelineStatusCB& done_cb) {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+Ranges<base::TimeDelta> PipelineImpl::GetBufferedTimeRanges() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return renderer_wrapper_->GetBufferedTimeRanges();
+}
- if (!demuxer_->GetStream(DemuxerStream::AUDIO) &&
- !demuxer_->GetStream(DemuxerStream::VIDEO)) {
- done_cb.Run(PIPELINE_ERROR_COULD_NOT_RENDER);
- return;
- }
+base::TimeDelta PipelineImpl::GetMediaDuration() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return duration_;
+}
- if (cdm_context_)
- shared_state_.renderer->SetCdm(cdm_context_,
- base::Bind(&IgnoreCdmAttached));
+bool PipelineImpl::DidLoadingProgress() {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return renderer_wrapper_->DidLoadingProgress();
+}
- shared_state_.renderer->Initialize(demuxer_, this, done_cb);
+PipelineStatistics PipelineImpl::GetStatistics() const {
+ DCHECK(thread_checker_.CalledOnValidThread());
+ return renderer_wrapper_->GetStatistics();
}
-void PipelineImpl::RendererWrapper::DestroyRenderer() {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+void PipelineImpl::SetCdm(CdmContext* cdm_context,
+ const CdmAttachedCB& cdm_attached_cb) {
+ DVLOG(2) << __FUNCTION__;
+ DCHECK(thread_checker_.CalledOnValidThread());
+ DCHECK(cdm_context);
+ DCHECK(!cdm_attached_cb.is_null());
- // 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);
- }
+ media_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&RendererWrapper::SetCdm,
+ base::Unretained(renderer_wrapper_.get()), cdm_context,
+ media::BindToCurrentLoop(cdm_attached_cb)));
}
-void PipelineImpl::RendererWrapper::ReportMetadata() {
- DCHECK(media_task_runner_->BelongsToCurrentThread());
+#define RETURN_STRING(state) \
+ case state: \
+ return #state;
- PipelineMetadata metadata;
- metadata.timeline_offset = demuxer_->GetTimelineOffset();
- DemuxerStream* stream = demuxer_->GetStream(DemuxerStream::VIDEO);
- if (stream) {
- metadata.has_video = true;
- metadata.natural_size = stream->video_decoder_config().natural_size();
- metadata.video_rotation = stream->video_rotation();
- }
- if (demuxer_->GetStream(DemuxerStream::AUDIO)) {
- metadata.has_audio = true;
+// static
+const char* PipelineImpl::GetStateString(State state) {
+ switch (state) {
+ RETURN_STRING(kCreated);
+ RETURN_STRING(kInitDemuxer);
+ RETURN_STRING(kInitRenderer);
+ RETURN_STRING(kSeeking);
+ RETURN_STRING(kPlaying);
+ RETURN_STRING(kStopping);
+ RETURN_STRING(kStopped);
+ RETURN_STRING(kSuspending);
+ RETURN_STRING(kSuspended);
+ RETURN_STRING(kResuming);
}
-
- main_task_runner_->PostTask(FROM_HERE, base::Bind(&PipelineImpl::OnMetadata,
- weak_pipeline_, metadata));
+ NOTREACHED();
+ return "INVALID";
}
+#undef RETURN_STRING
+
void PipelineImpl::OnError(PipelineStatus error) {
DVLOG(2) << __FUNCTION__;
DCHECK(thread_checker_.CalledOnValidThread());
@@ -1199,38 +1236,4 @@ void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) {
base::ResetAndReturn(&suspend_cb_).Run(PIPELINE_OK);
}
-PipelineImpl::RendererWrapper::RendererWrapper(
- scoped_refptr<base::SingleThreadTaskRunner> media_task_runner,
- scoped_refptr<MediaLog> media_log)
- : 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) {
- weak_this_ = weak_factory_.GetWeakPtr();
- 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() const {
- DCHECK(main_task_runner_->BelongsToCurrentThread());
-
- 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();
-}
-
} // namespace media
« no previous file with comments | « no previous file | no next file » | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698