summaryrefslogtreecommitdiffstats
path: root/media/base/pipeline_impl.cc
diff options
context:
space:
mode:
authorjiesun@google.com <jiesun@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-26 19:45:25 +0000
committerjiesun@google.com <jiesun@google.com@0039d316-1c4b-4281-b951-d872f2087c98>2010-08-26 19:45:25 +0000
commit7f537a10140277911002830f0c991a45f31ad0a5 (patch)
tree75aa5d7bdf48d776039848d4419cbcafd81be9d9 /media/base/pipeline_impl.cc
parente1ddda0ea4dd580bb767c07264bd26ad761af8b3 (diff)
downloadchromium_src-7f537a10140277911002830f0c991a45f31ad0a5.zip
chromium_src-7f537a10140277911002830f0c991a45f31ad0a5.tar.gz
chromium_src-7f537a10140277911002830f0c991a45f31ad0a5.tar.bz2
media: add flush stage before stop().
to make stop more clean without race condiction, we need to flush therefore when stop happen, there are no buffer exchange. 1. add seek_pending_ to track a seek operation in transition. 2. add tearing_down_ to track a stop operation in transition. 3. add stop_pending_ to track a stop that could be delayed by a seek. 4. an error while initialization will trigger a short teardown. ( stopping => stopped ) 5. an error after initialization will trigger a full tear down. ( pausing => flushing =>stopping => stopped. ). Review URL: http://codereview.chromium.org/3192008 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@57564 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/base/pipeline_impl.cc')
-rw-r--r--media/base/pipeline_impl.cc120
1 files changed, 90 insertions, 30 deletions
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index d8b3192..73af164 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -72,6 +72,8 @@ PipelineImpl::PipelineImpl(MessageLoop* message_loop)
PipelineImpl::~PipelineImpl() {
AutoLock auto_lock(lock_);
DCHECK(!running_) << "Stop() must complete before destroying object";
+ DCHECK(!stop_pending_);
+ DCHECK(!seek_pending_);
}
// Creates the PipelineInternal and calls it's start method.
@@ -321,6 +323,9 @@ void PipelineImpl::ResetState() {
AutoLock auto_lock(lock_);
const base::TimeDelta kZero;
running_ = false;
+ stop_pending_ = false;
+ seek_pending_ = false;
+ tearing_down_ = false;
duration_ = kZero;
buffered_time_ = kZero;
buffered_bytes_ = 0;
@@ -356,6 +361,25 @@ bool PipelineImpl::IsPipelineStopped() {
return state_ == kStopped || state_ == kError;
}
+bool PipelineImpl::IsPipelineTearingDown() {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return tearing_down_;
+}
+
+bool PipelineImpl::IsPipelineStopPending() {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ return stop_pending_;
+}
+
+bool PipelineImpl::IsPipelineSeeking() {
+ DCHECK_EQ(MessageLoop::current(), message_loop_);
+ if (!seek_pending_)
+ return false;
+ DCHECK(kSeeking == state_ || kPausing == state_ ||
+ kFlushing == state_ || kStarting == state_);
+ return true;
+}
+
void PipelineImpl::FinishInitialization() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
// Execute the seek callback, if present. Note that this might be the
@@ -379,17 +403,22 @@ bool PipelineImpl::TransientState(State state) {
// static
PipelineImpl::State PipelineImpl::FindNextState(State current) {
// TODO(scherkus): refactor InitializeTask() to make use of this function.
- if (current == kPausing)
+ if (current == kPausing) {
return kFlushing;
- if (current == kFlushing)
- return kSeeking;
- if (current == kSeeking)
+ } else if (current == kFlushing) {
+ // We will always honor Seek() before Stop(). This is based on the
+ // assumption that we never accept Seek() after Stop().
+ DCHECK(IsPipelineSeeking() || IsPipelineStopPending());
+ return IsPipelineSeeking() ? kSeeking : kStopping;
+ } else if (current == kSeeking) {
return kStarting;
- if (current == kStarting)
+ } else if (current == kStarting) {
return kStarted;
- if (current == kStopping)
+ } else if (current == kStopping) {
return kStopped;
- return current;
+ } else {
+ return current;
+ }
}
void PipelineImpl::SetError(PipelineError error) {
@@ -559,8 +588,11 @@ void PipelineImpl::InitializeTask() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
// If we have received the stop or error signal, return immediately.
- if (state_ == kStopping || IsPipelineStopped())
+ if (IsPipelineStopPending() ||
+ IsPipelineStopped() ||
+ PIPELINE_OK != GetError()) {
return;
+ }
DCHECK(state_ == kCreated || IsPipelineInitializing());
@@ -625,6 +657,7 @@ void PipelineImpl::InitializeTask() {
VolumeChangedTask(GetVolume());
// Fire the initial seek request to get the filters to preroll.
+ seek_pending_ = true;
state_ = kSeeking;
remaining_transitions_ = filters_.size();
seek_timestamp_ = base::TimeDelta();
@@ -644,12 +677,12 @@ void PipelineImpl::StopTask(PipelineCallback* stop_callback) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
PipelineError error = GetError();
- if (state_ == kStopped || (state_ == kStopping && error == PIPELINE_OK)) {
+ if (state_ == kStopped || (IsPipelineStopPending() && error == PIPELINE_OK)) {
// If we are already stopped or stopping normally, return immediately.
delete stop_callback;
return;
} else if (state_ == kError ||
- (state_ == kStopping && error != PIPELINE_OK)) {
+ (IsPipelineStopPending() && error != PIPELINE_OK)) {
// If we are stopping due to SetError(), stop normally instead of
// going to error state.
AutoLock auto_lock(lock_);
@@ -658,11 +691,14 @@ void PipelineImpl::StopTask(PipelineCallback* stop_callback) {
stop_callback_.reset(stop_callback);
- if (IsPipelineInitializing()) {
- FinishInitialization();
+ stop_pending_ = true;
+ if (!IsPipelineSeeking()) {
+ // We will tear down pipeline immediately when there is no seek operation
+ // pending. This should include the case where we are partially initialized.
+ // Ideally this case should use SetError() rather than Stop() to tear down.
+ DCHECK(!IsPipelineTearingDown());
+ TearDownPipeline();
}
-
- StartDestroyingFilters();
}
void PipelineImpl::ErrorChangedTask(PipelineError error) {
@@ -672,19 +708,14 @@ void PipelineImpl::ErrorChangedTask(PipelineError error) {
// Suppress executing additional error logic. Note that if we are currently
// performing a normal stop, then we return immediately and continue the
// normal stop.
- if (IsPipelineStopped() || state_ == kStopping) {
+ if (IsPipelineStopped() || IsPipelineTearingDown()) {
return;
}
AutoLock auto_lock(lock_);
error_ = error;
- // Notify the client that starting did not complete, if necessary.
- if (IsPipelineInitializing()) {
- FinishInitialization();
- }
-
- StartDestroyingFilters();
+ TearDownPipeline();
}
void PipelineImpl::PlaybackRateChangedTask(float playback_rate) {
@@ -713,6 +744,7 @@ void PipelineImpl::VolumeChangedTask(float volume) {
void PipelineImpl::SeekTask(base::TimeDelta time,
PipelineCallback* seek_callback) {
DCHECK_EQ(MessageLoop::current(), message_loop_);
+ DCHECK(!IsPipelineStopPending());
// Suppress seeking if we're not fully started.
if (state_ != kStarted && state_ != kEnded) {
@@ -724,6 +756,9 @@ void PipelineImpl::SeekTask(base::TimeDelta time,
return;
}
+ DCHECK(!seek_pending_);
+ seek_pending_ = true;
+
// We'll need to pause every filter before seeking. The state transition
// is as follows:
// kStarted/kEnded
@@ -814,8 +849,8 @@ void PipelineImpl::FilterStateTransitionTask() {
// Decrement the number of remaining transitions, making sure to transition
// to the next state if needed.
- CHECK(remaining_transitions_ <= filters_.size());
- CHECK(remaining_transitions_ > 0u);
+ DCHECK(remaining_transitions_ <= filters_.size());
+ DCHECK(remaining_transitions_ > 0u);
if (--remaining_transitions_ == 0) {
state_ = FindNextState(state_);
if (state_ == kSeeking) {
@@ -834,7 +869,13 @@ void PipelineImpl::FilterStateTransitionTask() {
if (state_ == kPausing) {
filter->Pause(NewCallback(this, &PipelineImpl::OnFilterStateTransition));
} else if (state_ == kFlushing) {
- filter->Flush(NewCallback(this, &PipelineImpl::OnFilterStateTransition));
+ // We had to use parallel flushing all filters.
+ if (remaining_transitions_ == filters_.size()) {
+ for (size_t i = 0; i < filters_.size(); i++) {
+ filters_[i]->Flush(
+ NewCallback(this, &PipelineImpl::OnFilterStateTransition));
+ }
+ }
} else if (state_ == kSeeking) {
filter->Seek(seek_timestamp_,
NewCallback(this, &PipelineImpl::OnFilterStateTransition));
@@ -850,6 +891,7 @@ void PipelineImpl::FilterStateTransitionTask() {
// Finally, reset our seeking timestamp back to zero.
seek_timestamp_ = base::TimeDelta();
+ seek_pending_ = false;
AutoLock auto_lock(lock_);
// We use audio stream to update the clock. So if there is such a stream,
@@ -859,6 +901,11 @@ void PipelineImpl::FilterStateTransitionTask() {
rendered_mime_types_.end();
if (!waiting_for_clock_update_)
clock_.Play();
+
+ if (IsPipelineStopPending()) {
+ // We had a pending stop request need to be honored right now.
+ TearDownPipeline();
+ }
} else if (IsPipelineStopped()) {
FinishDestroyingFiltersTask();
} else {
@@ -886,6 +933,9 @@ void PipelineImpl::FinishDestroyingFiltersTask() {
filter_types_.clear();
STLDeleteElements(&filter_threads_);
+ stop_pending_ = false;
+ tearing_down_ = false;
+
if (PIPELINE_OK == GetError()) {
// Destroying filters due to Stop().
ResetState();
@@ -1017,19 +1067,29 @@ void PipelineImpl::GetFilter(scoped_refptr<Filter>* filter_out) const {
}
}
-void PipelineImpl::StartDestroyingFilters() {
+void PipelineImpl::TearDownPipeline() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
DCHECK_NE(kStopped, state_);
- if (state_ == kStopping) {
- return; // Do not call Stop() on filters twice.
+ // Mark that we already start tearing down operation.
+ tearing_down_ = true;
+
+ if (IsPipelineInitializing()) {
+ // Notify the client that starting did not complete, if necessary.
+ FinishInitialization();
}
remaining_transitions_ = filters_.size();
if (remaining_transitions_ > 0) {
- state_ = kStopping;
- filters_.front()->Stop(NewCallback(
- this, &PipelineImpl::OnFilterStateTransition));
+ if (IsPipelineInitializing()) {
+ state_ = kStopping;
+ filters_.front()->Stop(NewCallback(
+ this, &PipelineImpl::OnFilterStateTransition));
+ } else {
+ state_ = kPausing;
+ filters_.front()->Pause(NewCallback(
+ this, &PipelineImpl::OnFilterStateTransition));
+ }
} else {
state_ = kStopped;
message_loop_->PostTask(FROM_HERE,