Index: media/base/pipeline.cc |
diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc |
index 470f1868e734b483ea57375a7cd4fad05735712b..e99b70afca32a9165d88f57db3af63ef167193e7 100644 |
--- a/media/base/pipeline.cc |
+++ b/media/base/pipeline.cc |
@@ -88,6 +88,8 @@ Pipeline::Pipeline(MessageLoop* message_loop, MediaLog* media_log) |
has_video_(false), |
state_(kCreated), |
seek_timestamp_(kNoTimestamp()), |
+ audio_ended_(false), |
+ video_ended_(false), |
audio_disabled_(false), |
creation_time_(base::Time::Now()) { |
media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
@@ -151,7 +153,6 @@ bool Pipeline::IsInitialized() const { |
case kSeeking: |
case kStarting: |
case kStarted: |
- case kEnded: |
return true; |
default: |
return false; |
@@ -527,11 +528,18 @@ void Pipeline::OnNaturalVideoSizeChanged(const gfx::Size& size) { |
natural_size_ = size; |
} |
-void Pipeline::OnRendererEnded() { |
- DCHECK(IsRunning()); |
+void Pipeline::OnAudioRendererEnded() { |
+ // Force post to process ended messages after current execution frame. |
+ message_loop_->PostTask(FROM_HERE, base::Bind( |
+ &Pipeline::DoAudioRendererEnded, this)); |
+ media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::AUDIO_ENDED)); |
+} |
+ |
+void Pipeline::OnVideoRendererEnded() { |
+ // Force post to process ended messages after current execution frame. |
message_loop_->PostTask(FROM_HERE, base::Bind( |
- &Pipeline::OnRendererEndedTask, this)); |
- media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::ENDED)); |
+ &Pipeline::DoVideoRendererEnded, this)); |
+ media_log_->AddEvent(media_log_->CreateEvent(MediaLogEvent::VIDEO_ENDED)); |
} |
// Called from any thread. |
@@ -796,7 +804,7 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
DCHECK(!IsPipelineStopPending()); |
// Suppress seeking if we're not fully started. |
- if (state_ != kStarted && state_ != kEnded) { |
+ if (state_ != kStarted) { |
// TODO(scherkus): should we run the callback? I'm tempted to say the API |
// will only execute the first Seek() request. |
DVLOG(1) << "Media pipeline has not started, ignoring seek to " |
@@ -809,12 +817,14 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
// We'll need to pause every filter before seeking. The state transition |
// is as follows: |
- // kStarted/kEnded |
+ // kStarted |
// kPausing (for each filter) |
// kSeeking (for each filter) |
// kStarting (for each filter) |
// kStarted |
SetState(kPausing); |
+ audio_ended_ = false; |
+ video_ended_ = false; |
seek_timestamp_ = std::max(time, demuxer_->GetStartTime()); |
seek_cb_ = seek_cb; |
@@ -827,35 +837,46 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
} |
-void Pipeline::OnRendererEndedTask() { |
+void Pipeline::DoAudioRendererEnded() { |
DCHECK(message_loop_->BelongsToCurrentThread()); |
- // We can only end if we were actually playing. |
- if (state_ != kStarted) { |
+ if (state_ != kStarted) |
return; |
- } |
- DCHECK(audio_renderer_ || video_renderer_); |
+ DCHECK(!audio_ended_); |
+ audio_ended_ = true; |
- // Make sure every extant renderer has ended. |
- if (audio_renderer_ && !audio_disabled_) { |
- if (!audio_renderer_->HasEnded()) { |
- return; |
- } |
- |
- // Start clock since there is no more audio to |
- // trigger clock updates. |
+ // Start clock since there is no more audio to trigger clock updates. |
+ if (!audio_disabled_) { |
base::AutoLock auto_lock(lock_); |
clock_->SetMaxTime(clock_->Duration()); |
StartClockIfWaitingForTimeUpdate_Locked(); |
} |
- if (video_renderer_ && !video_renderer_->HasEnded()) { |
+ RunEndedCallbackIfNeeded(); |
+} |
+ |
+void Pipeline::DoVideoRendererEnded() { |
+ DCHECK(message_loop_->BelongsToCurrentThread()); |
+ |
+ if (state_ != kStarted) |
+ return; |
+ |
+ DCHECK(!video_ended_); |
+ video_ended_ = true; |
+ |
+ RunEndedCallbackIfNeeded(); |
+} |
+ |
+void Pipeline::RunEndedCallbackIfNeeded() { |
+ DCHECK(message_loop_->BelongsToCurrentThread()); |
+ |
+ if (audio_renderer_ && !audio_ended_ && !audio_disabled_) |
+ return; |
+ |
+ if (video_renderer_ && !video_ended_) |
return; |
- } |
- // Transition to ended, executing the callback if present. |
- SetState(kEnded); |
{ |
base::AutoLock auto_lock(lock_); |
clock_->EndOfStream(); |
@@ -990,7 +1011,6 @@ void Pipeline::TeardownStateTransitionTask() { |
case kStarting: |
case kStopped: |
case kStarted: |
- case kEnded: |
NOTREACHED() << "Unexpected state for teardown: " << state_; |
break; |
// default: intentionally left out to force new states to cause compiler |
@@ -1132,7 +1152,7 @@ bool Pipeline::InitializeAudioRenderer( |
base::Bind(&Pipeline::OnFilterInitialize, this), |
base::Bind(&Pipeline::OnAudioUnderflow, this), |
base::Bind(&Pipeline::OnAudioTimeUpdate, this), |
- base::Bind(&Pipeline::OnRendererEnded, this), |
+ base::Bind(&Pipeline::OnAudioRendererEnded, this), |
base::Bind(&Pipeline::OnAudioDisabled, this), |
base::Bind(&Pipeline::SetError, this)); |
return true; |
@@ -1158,7 +1178,7 @@ bool Pipeline::InitializeVideoRenderer( |
base::Bind(&Pipeline::OnUpdateStatistics, this), |
base::Bind(&Pipeline::OnVideoTimeUpdate, this), |
base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this), |
- base::Bind(&Pipeline::OnRendererEnded, this), |
+ base::Bind(&Pipeline::OnVideoRendererEnded, this), |
base::Bind(&Pipeline::SetError, this), |
base::Bind(&Pipeline::GetMediaTime, this), |
base::Bind(&Pipeline::GetMediaDuration, this)); |
@@ -1219,7 +1239,6 @@ void Pipeline::TearDownPipeline() { |
break; |
case kStarted: |
- case kEnded: |
SetState(kPausing); |
DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
break; |