Index: media/base/pipeline_impl.cc |
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc |
index ddbd0cea8e372c56a6be4492630e9bd0883d0b87..fc2b0eb5ba12f8a0030c4e0ca3245259e434ffeb 100644 |
--- a/media/base/pipeline_impl.cc |
+++ b/media/base/pipeline_impl.cc |
@@ -119,11 +119,9 @@ class PipelineImpl::RendererWrapper : public DemuxerHost, |
void CheckPlaybackEnded(); |
// State transition tasks. |
- void DoSeek(base::TimeDelta seek_timestamp, const PipelineStatusCB& done_cb); |
- void DoStop(const base::Closure& done_cb); |
void SetState(State next_state); |
- State GetNextState() const; |
- void StateTransitionTask(PipelineStatus status); |
+ void CompleteSeek(base::TimeDelta seek_time, PipelineStatus status); |
+ void CompleteSuspend(PipelineStatus status); |
void InitializeDemuxer(const PipelineStatusCB& done_cb); |
void InitializeRenderer(const PipelineStatusCB& done_cb); |
void DestroyRenderer(); |
@@ -155,10 +153,6 @@ class PipelineImpl::RendererWrapper : public DemuxerHost, |
// 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_; |
- |
// Whether we've received the audio/video/text ended events. |
bool renderer_ended_; |
bool text_renderer_ended_; |
@@ -195,6 +189,12 @@ PipelineImpl::RendererWrapper::~RendererWrapper() { |
DCHECK(state_ == kCreated || state_ == kStopped); |
} |
+// Note that the usage of base::Unretained() with the renderers is considered |
+// safe as they are owned by |pending_callbacks_| and share the same lifetime. |
+// |
+// That being said, deleting the renderers while keeping |pending_callbacks_| |
+// running on the media thread would result in crashes. |
+ |
void PipelineImpl::RendererWrapper::Start( |
Demuxer* demuxer, |
std::unique_ptr<Renderer> renderer, |
@@ -204,6 +204,8 @@ void PipelineImpl::RendererWrapper::Start( |
DCHECK_EQ(kCreated, state_) << "Received start in unexpected state: " |
<< state_; |
+ SetState(kStarting); |
+ |
DCHECK(!demuxer_); |
DCHECK(!shared_state_.renderer); |
DCHECK(!text_renderer_); |
@@ -222,7 +224,24 @@ void PipelineImpl::RendererWrapper::Start( |
} |
weak_pipeline_ = weak_pipeline; |
- StateTransitionTask(PIPELINE_OK); |
+ // Queue asynchronous actions required to start. |
+ DCHECK(!pending_callbacks_); |
+ SerialRunner::Queue fns; |
+ |
+ // Initialize demuxer. |
+ fns.Push(base::Bind(&RendererWrapper::InitializeDemuxer, weak_this_)); |
+ |
+ // Once the demuxer is initialized successfully, media metadata must be |
+ // available - report the metadata to client. |
+ fns.Push(base::Bind(&RendererWrapper::ReportMetadata, weak_this_)); |
+ |
+ // Initialize renderer. |
+ fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); |
+ |
+ // Run tasks. |
+ pending_callbacks_ = |
+ SerialRunner::Run(fns, base::Bind(&RendererWrapper::CompleteSeek, |
+ weak_this_, base::TimeDelta())); |
} |
void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
@@ -242,7 +261,23 @@ void PipelineImpl::RendererWrapper::Stop(const base::Closure& stop_cb) { |
// tasks. |
pending_callbacks_.reset(); |
- DoStop(stop_cb); |
+ DestroyRenderer(); |
alokp
2016/06/24 06:15:06
DoStop() moved here.
|
+ text_renderer_.reset(); |
+ |
+ if (demuxer_) { |
+ demuxer_->Stop(); |
+ demuxer_ = NULL; |
+ } |
+ |
+ SetState(kStopped); |
+ |
+ // Post the stop callback to enqueue it after the tasks that may have been |
+ // posted by Demuxer and Renderer during stopping. Note that in theory the |
+ // tasks posted by Demuxer/Renderer may post even more tasks that will get |
+ // enqueued after |stop_cb|. This may be problematic because Demuxer may |
+ // get destroyed as soon as |stop_cb| is run. In practice this is not a |
+ // problem, but ideally Demuxer should be destroyed on the media thread. |
+ media_task_runner_->PostTask(FROM_HERE, stop_cb); |
} |
void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
@@ -256,16 +291,40 @@ void PipelineImpl::RendererWrapper::Seek(base::TimeDelta time) { |
return; |
} |
- const base::TimeDelta seek_timestamp = |
- std::max(time, demuxer_->GetStartTime()); |
+ base::TimeDelta seek_timestamp = std::max(time, demuxer_->GetStartTime()); |
SetState(kSeeking); |
renderer_ended_ = false; |
text_renderer_ended_ = false; |
- start_timestamp_ = seek_timestamp; |
- DoSeek(seek_timestamp, |
- base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); |
+ // Queue asynchronous actions required to start. |
alokp
2016/06/24 06:15:07
DoSeek() moved here.
|
+ DCHECK(!pending_callbacks_); |
+ SerialRunner::Queue bound_fns; |
+ |
+ // Pause. |
+ if (text_renderer_) { |
+ bound_fns.Push(base::Bind(&TextRenderer::Pause, |
+ base::Unretained(text_renderer_.get()))); |
+ } |
+ |
+ // Flush. |
+ DCHECK(shared_state_.renderer); |
+ bound_fns.Push(base::Bind(&Renderer::Flush, |
+ base::Unretained(shared_state_.renderer.get()))); |
+ |
+ if (text_renderer_) { |
+ bound_fns.Push(base::Bind(&TextRenderer::Flush, |
+ base::Unretained(text_renderer_.get()))); |
+ } |
+ |
+ // Seek demuxer. |
+ bound_fns.Push( |
+ base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); |
+ |
+ // Run tasks. |
+ pending_callbacks_ = SerialRunner::Run( |
+ bound_fns, |
+ base::Bind(&RendererWrapper::CompleteSeek, weak_this_, seek_timestamp)); |
} |
void PipelineImpl::RendererWrapper::Suspend() { |
@@ -292,9 +351,7 @@ void PipelineImpl::RendererWrapper::Suspend() { |
DCHECK(shared_state_.suspend_timestamp != kNoTimestamp()); |
} |
- // Queue the asynchronous actions required to stop playback. (Matches setup in |
- // DoSeek().) |
- // TODO(sandersd): Share implementation with DoSeek(). |
+ // Queue the asynchronous actions required to stop playback. |
SerialRunner::Queue fns; |
if (text_renderer_) { |
@@ -311,7 +368,7 @@ void PipelineImpl::RendererWrapper::Suspend() { |
} |
pending_callbacks_ = SerialRunner::Run( |
- fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); |
+ fns, base::Bind(&RendererWrapper::CompleteSuspend, weak_this_)); |
} |
void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, |
@@ -335,25 +392,22 @@ void PipelineImpl::RendererWrapper::Resume(std::unique_ptr<Renderer> renderer, |
shared_state_.renderer = std::move(renderer); |
} |
- // 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 start_timestamp = |
+ std::max(timestamp, demuxer_->GetStartTime()); |
- // 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|). |
+ // Queue the asynchronous actions required to start playback. |
SerialRunner::Queue fns; |
fns.Push( |
- base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp_)); |
+ base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), start_timestamp)); |
fns.Push(base::Bind(&RendererWrapper::InitializeRenderer, weak_this_)); |
pending_callbacks_ = SerialRunner::Run( |
- fns, base::Bind(&RendererWrapper::StateTransitionTask, weak_this_)); |
+ fns, |
+ base::Bind(&RendererWrapper::CompleteSeek, weak_this_, start_timestamp)); |
} |
void PipelineImpl::RendererWrapper::SetPlaybackRate(double playback_rate) { |
@@ -619,66 +673,6 @@ void PipelineImpl::RendererWrapper::CheckPlaybackEnded() { |
FROM_HERE, base::Bind(&PipelineImpl::OnEnded, weak_pipeline_)); |
} |
-// Note that the usage of base::Unretained() with the renderers is considered |
-// safe as they are owned by |pending_callbacks_| and share the same lifetime. |
-// |
-// That being said, deleting the renderers while keeping |pending_callbacks_| |
-// running on the media thread would result in crashes. |
-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); |
- SerialRunner::Queue bound_fns; |
- |
- // Pause. |
- if (text_renderer_) { |
- bound_fns.Push(base::Bind(&TextRenderer::Pause, |
- base::Unretained(text_renderer_.get()))); |
- } |
- |
- // Flush. |
- DCHECK(shared_state_.renderer); |
- bound_fns.Push(base::Bind(&Renderer::Flush, |
- base::Unretained(shared_state_.renderer.get()))); |
- |
- if (text_renderer_) { |
- bound_fns.Push(base::Bind(&TextRenderer::Flush, |
- base::Unretained(text_renderer_.get()))); |
- } |
- |
- // Seek demuxer. |
- bound_fns.Push( |
- base::Bind(&Demuxer::Seek, base::Unretained(demuxer_), seek_timestamp)); |
- |
- pending_callbacks_ = SerialRunner::Run(bound_fns, done_cb); |
-} |
- |
-void PipelineImpl::RendererWrapper::DoStop(const base::Closure& done_cb) { |
- DVLOG(2) << __FUNCTION__; |
- DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- DCHECK_EQ(state_, kStopping); |
- DCHECK(!pending_callbacks_.get()); |
- |
- DestroyRenderer(); |
- text_renderer_.reset(); |
- |
- if (demuxer_) { |
- demuxer_->Stop(); |
- demuxer_ = NULL; |
- } |
- |
- SetState(kStopped); |
- |
- // Post the stop callback to enqueue it after the tasks that may have been |
- // posted by Demuxer and Renderer during stopping. Note that in theory the |
- // tasks posted by Demuxer/Renderer may post even more tasks that will get |
- // enqueued after |done_cb|. This may be problematic because Demuxer may |
- // get destroyed as soon as |done_cb| is run. In practice this is not a |
- // problem, but ideally Demuxer should be destroyed on the media thread. |
- media_task_runner_->PostTask(FROM_HERE, done_cb); |
-} |
- |
void PipelineImpl::RendererWrapper::SetState(State next_state) { |
DCHECK(media_task_runner_->BelongsToCurrentThread()); |
DVLOG(1) << PipelineImpl::GetStateString(state_) << " -> " |
@@ -688,119 +682,60 @@ void PipelineImpl::RendererWrapper::SetState(State next_state) { |
media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(next_state)); |
} |
-PipelineImpl::State PipelineImpl::RendererWrapper::GetNextState() const { |
+void PipelineImpl::RendererWrapper::CompleteSeek(base::TimeDelta seek_time, |
+ PipelineStatus status) { |
DCHECK(media_task_runner_->BelongsToCurrentThread()); |
- DCHECK_EQ(status_, PIPELINE_OK) |
- << "State transitions don't happen when there's an error: " << status_; |
- |
- switch (state_) { |
- case kCreated: |
- return kInitDemuxer; |
+ DCHECK(state_ == kStarting || state_ == kSeeking || state_ == kResuming); |
- case kInitDemuxer: |
- return kInitRenderer; |
+ DCHECK(pending_callbacks_); |
+ pending_callbacks_.reset(); |
- case kInitRenderer: |
- case kSeeking: |
- return kPlaying; |
+ if (status != PIPELINE_OK) { |
+ OnPipelineError(status); |
+ return; |
+ } |
- case kSuspending: |
- return kSuspended; |
+ shared_state_.renderer->StartPlayingFrom( |
alokp
2016/06/24 06:15:07
copied from PipelineImpl::RendererWrapper::StateTr
|
+ std::max(seek_time, demuxer_->GetStartTime())); |
+ { |
+ base::AutoLock auto_lock(shared_state_lock_); |
+ shared_state_.suspend_timestamp = kNoTimestamp(); |
+ } |
- case kSuspended: |
- return kResuming; |
+ if (text_renderer_) |
+ text_renderer_->StartPlaying(); |
- case kResuming: |
- return kPlaying; |
+ shared_state_.renderer->SetPlaybackRate(playback_rate_); |
+ shared_state_.renderer->SetVolume(volume_); |
- case kPlaying: |
- case kStopping: |
- case kStopped: |
- break; |
- } |
- NOTREACHED() << "State has no transition: " << state_; |
- return state_; |
+ SetState(kPlaying); |
+ main_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&PipelineImpl::OnSeekDone, weak_pipeline_)); |
} |
-void PipelineImpl::RendererWrapper::StateTransitionTask(PipelineStatus status) { |
- 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; |
- } |
- |
- // 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::CompleteSuspend(PipelineStatus status) { |
+ DCHECK(media_task_runner_->BelongsToCurrentThread()); |
+ DCHECK_EQ(kSuspending, state_); |
+ DCHECK(pending_callbacks_); |
pending_callbacks_.reset(); |
- 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; |
+ // 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. |
+ LOG_IF(WARNING, status != PIPELINE_OK) |
+ << "Encountered pipeline error while suspending: " << status; |
+ |
+ DestroyRenderer(); |
alokp
2016/06/24 06:15:07
copied from PipelineImpl::RendererWrapper::StateTr
|
+ { |
+ base::AutoLock auto_lock(shared_state_lock_); |
+ shared_state_.statistics.audio_memory_usage = 0; |
+ shared_state_.statistics.video_memory_usage = 0; |
} |
+ |
+ SetState(kSuspended); |
+ main_task_runner_->PostTask( |
+ FROM_HERE, base::Bind(&PipelineImpl::OnSuspendDone, weak_pipeline_)); |
} |
void PipelineImpl::RendererWrapper::InitializeDemuxer( |
@@ -1105,8 +1040,7 @@ void PipelineImpl::SetCdm(CdmContext* cdm_context, |
const char* PipelineImpl::GetStateString(State state) { |
switch (state) { |
RETURN_STRING(kCreated); |
- RETURN_STRING(kInitDemuxer); |
- RETURN_STRING(kInitRenderer); |
+ RETURN_STRING(kStarting); |
RETURN_STRING(kSeeking); |
RETURN_STRING(kPlaying); |
RETURN_STRING(kStopping); |
@@ -1218,8 +1152,8 @@ void PipelineImpl::OnVideoOpacityChange(bool opaque) { |
client_->OnVideoOpacityChange(opaque); |
} |
-void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { |
- DVLOG(3) << __FUNCTION__ << "(" << start_time.InMicroseconds() << ")"; |
+void PipelineImpl::OnSeekDone() { |
+ DVLOG(3) << __FUNCTION__; |
DCHECK(thread_checker_.CalledOnValidThread()); |
DCHECK(IsRunning()); |
@@ -1227,8 +1161,8 @@ void PipelineImpl::OnSeekDone(base::TimeDelta start_time) { |
base::ResetAndReturn(&seek_cb_).Run(PIPELINE_OK); |
} |
-void PipelineImpl::OnSuspendDone(base::TimeDelta suspend_time) { |
- DVLOG(3) << __FUNCTION__ << "(" << suspend_time.InMicroseconds() << ")"; |
+void PipelineImpl::OnSuspendDone() { |
+ DVLOG(3) << __FUNCTION__; |
DCHECK(thread_checker_.CalledOnValidThread()); |
DCHECK(IsRunning()); |