Chromium Code Reviews| Index: media/base/pipeline.cc |
| diff --git a/media/base/pipeline.cc b/media/base/pipeline.cc |
| index 24868cfac2c7941c714459e51b99360297d1df99..bd8c08e3836967a4a749b6a83a68001c3fbc8f5f 100644 |
| --- a/media/base/pipeline.cc |
| +++ b/media/base/pipeline.cc |
| @@ -17,6 +17,7 @@ |
| #include "base/synchronization/condition_variable.h" |
| #include "media/base/audio_decoder.h" |
| #include "media/base/audio_renderer.h" |
| +#include "media/base/buffers.h" |
| #include "media/base/callback_util.h" |
| #include "media/base/clock.h" |
| #include "media/base/filter_collection.h" |
| @@ -69,12 +70,21 @@ struct Pipeline::PipelineInitState { |
| Pipeline::Pipeline(MessageLoop* message_loop, MediaLog* media_log) |
| : message_loop_(message_loop->message_loop_proxy()), |
| media_log_(media_log), |
| + running_(false), |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
I believe it is the case that:
running_ == state_
|
| + did_loading_progress_(false), |
| + total_bytes_(0), |
| + volume_(1.0f), |
| + playback_rate_(0.0f), |
| clock_(new Clock(&base::Time::Now)), |
| waiting_for_clock_update_(false), |
| + status_(PIPELINE_OK), |
| + has_audio_(false), |
| + has_video_(false), |
| state_(kCreated), |
| + seek_timestamp_(kNoTimestamp()), |
| + audio_disabled_(false), |
| creation_time_(base::Time::Now()) { |
| media_log_->AddEvent(media_log_->CreatePipelineStateChangedEvent(kCreated)); |
| - ResetState(); |
| media_log_->AddEvent( |
| media_log_->CreateEvent(MediaLogEvent::PIPELINE_CREATED)); |
| } |
| @@ -82,8 +92,9 @@ Pipeline::Pipeline(MessageLoop* message_loop, MediaLog* media_log) |
| Pipeline::~Pipeline() { |
| base::AutoLock auto_lock(lock_); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
If you think you need a lock in a dtor, you're gon
|
| DCHECK(!running_) << "Stop() must complete before destroying object"; |
| - DCHECK(!stop_pending_); |
| - DCHECK(!seek_pending_); |
| + DCHECK(start_cb_.is_null()); |
| + DCHECK(seek_cb_.is_null()); |
| + DCHECK(stop_cb_.is_null()); |
| media_log_->AddEvent( |
| media_log_->CreateEvent(MediaLogEvent::PIPELINE_DESTROYED)); |
| @@ -95,8 +106,8 @@ void Pipeline::Start(scoped_ptr<FilterCollection> collection, |
| const PipelineStatusCB& start_cb) { |
| base::AutoLock auto_lock(lock_); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
replace w/ assertion about running on render threa
|
| CHECK(!running_) << "Media pipeline is already running"; |
| - |
| running_ = true; |
| + |
| message_loop_->PostTask(FROM_HERE, base::Bind( |
| &Pipeline::StartTask, this, base::Passed(&collection), |
| ended_cb, error_cb, start_cb)); |
| @@ -104,9 +115,7 @@ void Pipeline::Start(scoped_ptr<FilterCollection> collection, |
| void Pipeline::Stop(const base::Closure& stop_cb) { |
| base::AutoLock auto_lock(lock_); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
ditto to asserting on render thread (lock is not o
acolwell GONE FROM CHROMIUM
2012/07/30 18:33:10
Most of the GetXXX methods need locks because they
|
| - CHECK(running_) << "Media pipeline isn't running"; |
|
scherkus (not reviewing)
2012/07/28 02:26:07
it's now always valid to call Stop() -- this avoid
|
| - // Stop the pipeline, which will set |running_| to false on our behalf. |
| message_loop_->PostTask(FROM_HERE, base::Bind( |
| &Pipeline::StopTask, this, stop_cb)); |
| } |
| @@ -124,24 +133,6 @@ bool Pipeline::IsRunning() const { |
| return running_; |
| } |
| -bool Pipeline::IsInitialized() const { |
| - // TODO(scherkus): perhaps replace this with a bool that is set/get under the |
| - // lock, because this is breaching the contract that |state_| is only accessed |
| - // on |message_loop_|. |
| - base::AutoLock auto_lock(lock_); |
| - switch (state_) { |
| - case kPausing: |
| - case kFlushing: |
| - case kSeeking: |
| - case kStarting: |
| - case kStarted: |
| - case kEnded: |
| - return true; |
| - default: |
| - return false; |
| - } |
| -} |
| - |
| bool Pipeline::HasAudio() const { |
| base::AutoLock auto_lock(lock_); |
| return has_audio_; |
| @@ -163,7 +154,7 @@ void Pipeline::SetPlaybackRate(float playback_rate) { |
| base::AutoLock auto_lock(lock_); |
| playback_rate_ = playback_rate; |
| - if (running_ && !tearing_down_) { |
| + if (running_) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
couldn't this be an early-return at the top of the
acolwell GONE FROM CHROMIUM
2012/07/30 18:33:10
The running_ check is here because it needs to be
|
| message_loop_->PostTask(FROM_HERE, base::Bind( |
| &Pipeline::PlaybackRateChangedTask, this, playback_rate)); |
| } |
| @@ -180,7 +171,7 @@ void Pipeline::SetVolume(float volume) { |
| base::AutoLock auto_lock(lock_); |
| volume_ = volume; |
| - if (running_ && !tearing_down_) { |
| + if (running_) { |
| message_loop_->PostTask(FROM_HERE, base::Bind( |
| &Pipeline::VolumeChangedTask, this, volume)); |
| } |
| @@ -243,28 +234,8 @@ void Pipeline::SetClockForTesting(Clock* clock) { |
| clock_.reset(clock); |
| } |
| -void Pipeline::ResetState() { |
| - base::AutoLock auto_lock(lock_); |
| - const TimeDelta kZero; |
| - running_ = false; |
| - stop_pending_ = false; |
| - seek_pending_ = false; |
| - tearing_down_ = false; |
| - error_caused_teardown_ = false; |
| - playback_rate_change_pending_ = false; |
| - buffered_byte_ranges_.clear(); |
| - did_loading_progress_ = false; |
| - total_bytes_ = 0; |
| - natural_size_.SetSize(0, 0); |
| - volume_ = 1.0f; |
| - playback_rate_ = 0.0f; |
| - pending_playback_rate_ = 0.0f; |
| - status_ = PIPELINE_OK; |
| - has_audio_ = false; |
| - has_video_ = false; |
| - waiting_for_clock_update_ = false; |
| - audio_disabled_ = false; |
| - clock_->Reset(); |
| +void Pipeline::SetErrorForTesting(PipelineStatus status) { |
| + SetError(status); |
| } |
| void Pipeline::SetState(State next_state) { |
| @@ -283,81 +254,6 @@ bool Pipeline::IsPipelineOk() { |
| return status_ == PIPELINE_OK; |
| } |
| -bool Pipeline::IsPipelineStopped() { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - return state_ == kStopped || state_ == kError; |
| -} |
| - |
| -bool Pipeline::IsPipelineTearingDown() { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - return tearing_down_; |
| -} |
| - |
| -bool Pipeline::IsPipelineStopPending() { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - return stop_pending_; |
| -} |
| - |
| -bool Pipeline::IsPipelineSeeking() { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - if (!seek_pending_) |
| - return false; |
| - DCHECK(kSeeking == state_ || kPausing == state_ || |
| - kFlushing == state_ || kStarting == state_) |
| - << "Current state : " << state_; |
| - return true; |
| -} |
| - |
| -void Pipeline::ReportStatus(const PipelineStatusCB& cb, PipelineStatus status) { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - if (cb.is_null()) |
| - return; |
| - cb.Run(status); |
| - // Prevent double-reporting of errors to clients. |
| - if (status != PIPELINE_OK) |
| - error_cb_.Reset(); |
| -} |
| - |
| -void Pipeline::FinishInitialization() { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - // Execute the seek callback, if present. Note that this might be the |
| - // initial callback passed into Start(). |
| - ReportStatus(seek_cb_, status_); |
| - seek_cb_.Reset(); |
| -} |
| - |
| -// static |
| -bool Pipeline::TransientState(State state) { |
| - return state == kPausing || |
| - state == kFlushing || |
| - state == kSeeking || |
| - state == kStarting || |
| - state == kStopping; |
| -} |
| - |
| -// static |
| -Pipeline::State Pipeline::FindNextState(State current) { |
| - // TODO(scherkus): refactor InitializeTask() to make use of this function. |
| - if (current == kPausing) { |
| - return kFlushing; |
| - } 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() || |
| - IsPipelineTearingDown()); |
| - return IsPipelineSeeking() ? kSeeking : kStopping; |
| - } else if (current == kSeeking) { |
| - return kStarting; |
| - } else if (current == kStarting) { |
| - return kStarted; |
| - } else if (current == kStopping) { |
| - return error_caused_teardown_ ? kError : kStopped; |
| - } else { |
| - return current; |
| - } |
| -} |
| - |
| void Pipeline::OnDemuxerError(PipelineStatus error) { |
| SetError(error); |
| } |
| @@ -453,6 +349,9 @@ TimeDelta Pipeline::TimeForByteOffset_Locked(int64 byte_offset) const { |
| void Pipeline::DoPause(const base::Closure& done_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK_EQ(state_, kPausing); |
| + DCHECK(seek_timestamp_ != kNoTimestamp()); |
| + |
| scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| if (audio_renderer_) |
| @@ -466,6 +365,9 @@ void Pipeline::DoPause(const base::Closure& done_cb) { |
| void Pipeline::DoFlush(const base::Closure& done_cb) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it crazy to still have this around?
Is it not a
|
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK_EQ(state_, kFlushing); |
| + DCHECK(seek_timestamp_ != kNoTimestamp()); |
| + |
| scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| if (audio_renderer_) |
| @@ -479,6 +381,14 @@ void Pipeline::DoFlush(const base::Closure& done_cb) { |
| void Pipeline::DoPlay(const base::Closure& done_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK_EQ(state_, kStarting); |
| + DCHECK(seek_timestamp_ == kNoTimestamp()); |
| + |
| + // Update playback rate and volume in case it changed before we resume |
| + // playback. |
| + PlaybackRateChangedTask(GetPlaybackRate()); |
|
scherkus (not reviewing)
2012/07/28 02:26:07
here's the new method for handling rate/volume cha
|
| + VolumeChangedTask(GetVolume()); |
| + |
| scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| if (audio_renderer_) |
| @@ -492,6 +402,13 @@ void Pipeline::DoPlay(const base::Closure& done_cb) { |
| void Pipeline::DoStop(const base::Closure& done_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK_EQ(state_, kStopping); |
| + |
| + if (video_decoder_) { |
| + video_decoder_->PrepareForShutdownHack(); |
|
scherkus (not reviewing)
2012/07/28 02:26:07
this might actually go away now that we don't Paus
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Yes, it can!
|
| + video_decoder_ = NULL; |
| + } |
| + |
| scoped_ptr<std::queue<ClosureFunc> > closures(new std::queue<ClosureFunc>); |
| if (demuxer_) |
| @@ -538,37 +455,6 @@ void Pipeline::OnRendererEnded() { |
| } |
| // Called from any thread. |
| -void Pipeline::OnFilterInitialize(PipelineStatus status) { |
| - // Continue the initialize task by proceeding to the next stage. |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &Pipeline::InitializeTask, this, status)); |
| -} |
| - |
| -// Called from any thread. |
| -void Pipeline::OnFilterStateTransition() { |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &Pipeline::FilterStateTransitionTask, this)); |
| -} |
| - |
| -// Called from any thread. |
| -// This method makes the PipelineStatusCB behave like a Closure. It |
| -// makes it look like a host()->SetError() call followed by a call to |
| -// OnFilterStateTransition() when errors occur. |
| -// |
| -// TODO: Revisit this code when SetError() is removed from FilterHost and |
| -// all the Closures are converted to PipelineStatusCB. |
| -void Pipeline::OnFilterStateTransitionWithStatus(PipelineStatus status) { |
| - if (status != PIPELINE_OK) |
| - SetError(status); |
| - OnFilterStateTransition(); |
| -} |
| - |
| -void Pipeline::OnTeardownStateTransition() { |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &Pipeline::TeardownStateTransitionTask, this)); |
| -} |
| - |
| -// Called from any thread. |
| void Pipeline::OnUpdateStatistics(const PipelineStatistics& stats) { |
| base::AutoLock auto_lock(lock_); |
| statistics_.audio_bytes_decoded += stats.audio_bytes_decoded; |
| @@ -586,201 +472,259 @@ void Pipeline::StartTask(scoped_ptr<FilterCollection> filter_collection, |
| filter_collection_ = filter_collection.Pass(); |
| ended_cb_ = ended_cb; |
| error_cb_ = error_cb; |
| - seek_cb_ = start_cb; |
| + start_cb_ = start_cb; |
| // Kick off initialization. |
| pipeline_init_state_.reset(new PipelineInitState()); |
| - |
| SetState(kInitDemuxer); |
| - InitializeDemuxer(); |
| -} |
| - |
| -// Main initialization method called on the pipeline thread. This code attempts |
| -// to use the specified filter factory to build a pipeline. |
| -// Initialization step performed in this method depends on current state of this |
| -// object, indicated by |state_|. After each step of initialization, this |
| -// object transits to the next stage. It starts by creating a Demuxer, and then |
| -// connects the Demuxer's audio stream to an AudioDecoder which is then |
| -// connected to an AudioRenderer. If the media has video, then it connects a |
| -// VideoDecoder to the Demuxer's video stream, and then connects the |
| -// VideoDecoder to a VideoRenderer. |
| -// |
| -// When all required filters have been created and have called their |
| -// FilterHost's InitializationComplete() method, the pipeline will update its |
| -// state to kStarted and |init_cb_|, will be executed. |
| -// |
| -// TODO(hclam): InitializeTask() is now starting the pipeline asynchronously. It |
| -// works like a big state change table. If we no longer need to start filters |
| -// in order, we need to get rid of all the state change. |
| -void Pipeline::InitializeTask(PipelineStatus last_stage_status) { |
| + DoInitDemuxer(base::Bind(&Pipeline::DoStateTransition, this)); |
| +} |
| + |
| +bool Pipeline::IsTransitioning() { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| + switch (state_) { |
| + case kCreated: |
| + case kStarted: |
| + case kStopped: |
| + return false; |
| - if (last_stage_status != PIPELINE_OK) { |
| - // Currently only VideoDecoders have a recoverable error code. |
| - if (state_ == kInitVideoDecoder && |
| - last_stage_status == DECODER_ERROR_NOT_SUPPORTED) { |
| - state_ = kInitAudioRenderer; |
| - } else { |
| - SetError(last_stage_status); |
| - } |
| + case kInitDemuxer: |
| + case kInitAudioDecoder: |
| + case kInitAudioRenderer: |
| + case kInitVideoDecoder: |
| + case kInitVideoRenderer: |
| + case kPausing: |
| + case kFlushing: |
| + case kSeeking: |
| + case kStarting: |
| + case kStopping: |
| + return true; |
| + |
| + // Catch any left out states. |
| } |
| + NOTREACHED(); |
| + return false; |
| +} |
| + |
| +void Pipeline::OnStateTransition(PipelineStatus status) { |
| + // Force-post a task on state transitions since to avoid reentrancy between |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
s/since/
|
| + // states. |
| + message_loop_->PostTask(FROM_HERE, base::Bind( |
| + &Pipeline::DoStateTransition, this, status)); |
| +} |
| + |
| +void Pipeline::DoStateTransition(PipelineStatus status) { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| - // If we have received the stop or error signal, return immediately. |
| - if (IsPipelineStopPending() || IsPipelineStopped() || !IsPipelineOk()) |
| + // Preserve existing abnormal status, otherwise update based on the result of |
| + // the previous operation. |
| + status_ = (status_ != PIPELINE_OK ? status_ : status); |
| + |
| + PipelineStatusCB done_cb = base::Bind(&Pipeline::OnStateTransition, this); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Can move south of the error-handling case.
|
| + base::Closure done_cb_no_status = base::Bind( |
|
scherkus (not reviewing)
2012/07/28 02:26:07
now that there's only one entry point for state tr
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
See above; I like the CBing, but I think you can s
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
d_c_no_status is a funny name for a thing that has
|
| + &Pipeline::OnStateTransition, this, status_); |
| + |
| + // Check if we need to stop due to an error or due to |stop_cb_| being set. |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
I'm pretty sure you dropped the VideoDecoder fallb
|
| + if (state_ != kStopping && state_ != kStopped && |
| + (status_ != PIPELINE_OK || !stop_cb_.is_null())) { |
| + SetState(kStopping); |
| + DoStop(done_cb_no_status); |
| return; |
| + } |
| - DCHECK(state_ == kInitDemuxer || |
| - state_ == kInitAudioDecoder || |
| - state_ == kInitAudioRenderer || |
| - state_ == kInitVideoDecoder || |
| - state_ == kInitVideoRenderer); |
| - |
| - // Demuxer created, create audio decoder. |
| - if (state_ == kInitDemuxer) { |
| - SetState(kInitAudioDecoder); |
| - // If this method returns false, then there's no audio stream. |
| - if (InitializeAudioDecoder(demuxer_)) |
| + switch(state_) { |
| + case kCreated: |
| + NOTREACHED() << "kCreated"; |
|
scherkus (not reviewing)
2012/07/28 02:26:07
kCreated, kStarted, kStopped are non transitioning
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
IMO best to keep as-is, and drop the default: case
|
| return; |
| - } |
| - // Assuming audio decoder was created, create audio renderer. |
| - if (state_ == kInitAudioDecoder) { |
| - SetState(kInitAudioRenderer); |
| + case kInitDemuxer: |
| + { |
| + base::AutoLock auto_lock(lock_); |
| + // We do not want to start the clock running. We only want to set the |
| + // base media time so our timestamp calculations will be correct. |
| + clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); |
| + } |
| + SetState(kInitAudioDecoder); |
| + DoInitAudioDecoder(done_cb); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
I'm wondering whether the initialization dance cou
|
| + return; |
| - // Returns false if there's no audio stream. |
| - if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder)) { |
| - base::AutoLock auto_lock(lock_); |
| - has_audio_ = true; |
| + case kInitAudioDecoder: |
| + SetState(kInitAudioRenderer); |
| + DoInitAudioRenderer(done_cb); |
| return; |
| - } |
| - } |
| - // Assuming audio renderer was created, create video decoder. |
| - if (state_ == kInitAudioRenderer) { |
| - // Then perform the stage of initialization, i.e. initialize video decoder. |
| - SetState(kInitVideoDecoder); |
| - if (InitializeVideoDecoder(demuxer_)) |
| + case kInitAudioRenderer: |
| + SetState(kInitVideoDecoder); |
| + DoInitVideoDecoder(done_cb); |
| return; |
| - } |
| - // Assuming video decoder was created, create video renderer. |
| - if (state_ == kInitVideoDecoder) { |
| - SetState(kInitVideoRenderer); |
| - if (InitializeVideoRenderer(pipeline_init_state_->video_decoder)) { |
| - base::AutoLock auto_lock(lock_); |
| - has_video_ = true; |
| + case kInitVideoDecoder: |
| + SetState(kInitVideoRenderer); |
| + DoInitVideoRenderer(done_cb); |
| return; |
| - } |
| - } |
| - if (state_ == kInitVideoRenderer) { |
| - if (!IsPipelineOk() || !(HasAudio() || HasVideo())) { |
| - SetError(PIPELINE_ERROR_COULD_NOT_RENDER); |
| + case kInitVideoRenderer: { |
| + bool success = true; |
| + { |
| + base::AutoLock l(lock_); |
| + has_audio_ = !!audio_renderer_ && !audio_disabled_; |
|
scherkus (not reviewing)
2012/07/28 02:31:47
hmm... this will actually cause us to not render t
|
| + has_video_ = !!video_renderer_; |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it really worthwhile maintaining members for th
|
| + |
| + // We're still successful if we have audio but the sound card is busted. |
| + success = !!audio_renderer_ || !!video_renderer_; |
| + } |
| + if (!success) { |
| + DoStateTransition(PIPELINE_ERROR_COULD_NOT_RENDER); |
| + return; |
| + } |
| + |
| + // Clear initialization state now that we're done. |
| + filter_collection_.reset(); |
| + pipeline_init_state_.reset(); |
| + |
| + // Kick off initial preroll. |
| + seek_timestamp_ = demuxer_->GetStartTime(); |
| + SetState(kSeeking); |
| + DoSeek(true, done_cb); |
| return; |
| } |
| - // Clear initialization state now that we're done. |
| - filter_collection_.reset(); |
| - pipeline_init_state_.reset(); |
| - |
| - // Initialization was successful, we are now considered paused, so it's safe |
| - // to set the initial playback rate and volume. |
| - PlaybackRateChangedTask(GetPlaybackRate()); |
| - VolumeChangedTask(GetVolume()); |
| - |
| - // Fire a seek request to get the renderers to preroll. We can skip a seek |
| - // here as the demuxer should be at the start of the stream. |
| - seek_pending_ = true; |
| - SetState(kSeeking); |
| - seek_timestamp_ = demuxer_->GetStartTime(); |
| - DoSeek(seek_timestamp_, true, |
| - base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this)); |
| + case kPausing: |
| + DCHECK(seek_timestamp_ != kNoTimestamp()); |
| + SetState(kFlushing); |
| + DoFlush(done_cb_no_status); |
| + return; |
| + |
| + case kFlushing: |
| + DCHECK(seek_timestamp_ != kNoTimestamp()); |
| + SetState(kSeeking); |
| + DoSeek(false, done_cb); |
| + return; |
| + |
| + case kSeeking: |
| + DCHECK(seek_timestamp_ != kNoTimestamp()); |
| + seek_timestamp_ = kNoTimestamp(); |
| + |
| + SetState(kStarting); |
| + DoPlay(done_cb_no_status); |
| + return; |
| + |
| + case kStarting: |
| + { |
| + base::AutoLock l(lock_); |
| + // We use audio stream to update the clock. So if there is such a |
| + // stream, we pause the clock until we receive a valid timestamp. |
| + waiting_for_clock_update_ = true; |
| + if (!has_audio_) { |
| + clock_->SetMaxTime(clock_->Duration()); |
| + StartClockIfWaitingForTimeUpdate_Locked(); |
| + } |
| + } |
| + |
| + SetState(kStarted); |
| + if (!start_cb_.is_null()) { |
| + DCHECK_EQ(status_, PIPELINE_OK); |
| + base::ResetAndReturn(&start_cb_).Run(status_); |
| + return; |
| + } |
| + if (!seek_cb_.is_null()) { |
| + DCHECK_EQ(status_, PIPELINE_OK); |
| + base::ResetAndReturn(&seek_cb_).Run(status_); |
| + } |
| + return; |
| + |
| + case kStarted: |
| + NOTREACHED() << "kStarted"; |
| + return; |
| + |
| + case kStopping: |
| + // Release all references. |
| + pipeline_init_state_.reset(); |
| + filter_collection_.reset(); |
| + audio_renderer_ = NULL; |
| + video_renderer_ = NULL; |
| + demuxer_ = NULL; |
| + { |
| + base::AutoLock l(lock_); |
| + running_ = false; |
| + } |
| + SetState(kStopped); |
| + |
| + // Execute any client-initiated pending callbacks if we've got some, |
| + // otherwise use the permanent callback |error_cb_|. |
| + if (!start_cb_.is_null()) { |
|
scherkus (not reviewing)
2012/07/28 02:26:07
put your thinking caps on for this one + look at m
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Can you be more assertive about the possibility of
|
| + base::ResetAndReturn(&start_cb_).Run(status_); |
| + error_cb_.Reset(); |
| + } |
| + if (!seek_cb_.is_null()) { |
| + base::ResetAndReturn(&seek_cb_).Run(status_); |
| + error_cb_.Reset(); |
| + } |
| + if (!stop_cb_.is_null()) { |
| + base::ResetAndReturn(&stop_cb_).Run(); |
| + error_cb_.Reset(); |
| + } |
| + if (!error_cb_.is_null()) { |
| + DCHECK_NE(status_, PIPELINE_OK); |
| + base::ResetAndReturn(&error_cb_).Run(status_); |
| + } |
| + return; |
| + |
| + case kStopped: |
| + NOTREACHED() << "kStopped"; |
| + return; |
| + |
| + default: |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
drop
|
| + NOTREACHED() << "State has no transition: " << state_; |
| } |
| + NOTREACHED() << "You should return, not break!"; |
| } |
| -// This method is called as a result of the client calling Pipeline::Stop() or |
| -// as the result of an error condition. |
| -// We stop the filters in the reverse order. |
| -// |
| -// TODO(scherkus): beware! this can get posted multiple times since we post |
| -// Stop() tasks even if we've already stopped. Perhaps this should no-op for |
| -// additional calls, however most of this logic will be changing. |
| void Pipeline::StopTask(const base::Closure& stop_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK(!IsPipelineStopPending()); |
| - DCHECK_NE(state_, kStopped); |
| + DCHECK(stop_cb_.is_null()); |
| - if (video_decoder_) { |
| - video_decoder_->PrepareForShutdownHack(); |
| - video_decoder_ = NULL; |
| - } |
| - |
| - if (IsPipelineTearingDown() && error_caused_teardown_) { |
| - // If we are stopping due to SetError(), stop normally instead of |
| - // going to error state and calling |error_cb_|. This converts |
| - // the teardown in progress from an error teardown into one that acts |
| - // like the error never occurred. |
| - base::AutoLock auto_lock(lock_); |
| - status_ = PIPELINE_OK; |
| - error_caused_teardown_ = false; |
| + if (state_ == kStopped) { |
| + stop_cb.Run(); |
| + return; |
| } |
| stop_cb_ = stop_cb; |
| - stop_pending_ = true; |
| - if (!IsPipelineSeeking() && !IsPipelineTearingDown()) { |
| - // We will tear down pipeline immediately when there is no seek operation |
| - // pending and no teardown in progress. This should include the case where |
| - // we are partially initialized. |
| - TearDownPipeline(); |
| - } |
| + if (IsTransitioning()) |
| + return; |
| + |
| + DoStateTransition(PIPELINE_OK); |
|
scherkus (not reviewing)
2012/07/28 02:26:07
technically I think I can SetState(kStopping) here
|
| } |
| void Pipeline::ErrorChangedTask(PipelineStatus error) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an 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() || IsPipelineTearingDown()) { |
| + // Preserve the error code. |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
s/the/an existing/
|
| + if (status_ != PIPELINE_OK) |
| return; |
| - } |
| - base::AutoLock auto_lock(lock_); |
| status_ = error; |
| - error_caused_teardown_ = true; |
| + if (IsTransitioning()) |
| + return; |
| - // Posting TearDownPipeline() to message loop so that we can make sure |
| - // it runs after any pending callbacks that are already queued. |
| - // |tearing_down_| is set early here to make sure that pending callbacks |
| - // don't modify the state before TeadDownPipeline() can run. |
| - tearing_down_ = true; |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &Pipeline::TearDownPipeline, this)); |
| + DoStateTransition(status_); |
| } |
| void Pipeline::PlaybackRateChangedTask(float playback_rate) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - if (!running_ || tearing_down_) |
| + // Suppress rate change until we're playing. |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it right to drop this? Or does WMPI already co
|
| + if (state_ != kStarting && state_ != kStarted) |
| return; |
| - // Suppress rate change until after seeking. |
| - if (IsPipelineSeeking()) { |
| - pending_playback_rate_ = playback_rate; |
| - playback_rate_change_pending_ = true; |
| - return; |
| - } |
| - |
| { |
| base::AutoLock auto_lock(lock_); |
| clock_->SetPlaybackRate(playback_rate); |
| } |
| - // These will get set after initialization completes in case playback rate is |
| - // set prior to initialization. |
| if (demuxer_) |
| demuxer_->SetPlaybackRate(playback_rate); |
| if (audio_renderer_) |
| @@ -791,7 +735,9 @@ void Pipeline::PlaybackRateChangedTask(float playback_rate) { |
| void Pipeline::VolumeChangedTask(float volume) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - if (!running_ || tearing_down_) |
| + |
| + // Suppress volume change until we're playing. |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
ditto
|
| + if (state_ != kStarting && state_ != kStarted) |
| return; |
| if (audio_renderer_) |
| @@ -800,10 +746,9 @@ void Pipeline::VolumeChangedTask(float volume) { |
| void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - 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 " |
| @@ -811,17 +756,13 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| return; |
| } |
| - DCHECK(!seek_pending_); |
| - seek_pending_ = true; |
| - |
| // We'll need to pause every filter before seeking. The state transition |
| // is as follows: |
| - // kStarted/kEnded |
| - // kPausing (for each filter) |
| - // kSeeking (for each filter) |
| - // kStarting (for each filter) |
| // kStarted |
| - SetState(kPausing); |
| + // kPausing |
| + // kSeeking |
| + // kStarting |
| + // kStarted |
| seek_timestamp_ = std::max(time, demuxer_->GetStartTime()); |
| seek_cb_ = seek_cb; |
| @@ -831,7 +772,8 @@ void Pipeline::SeekTask(TimeDelta time, const PipelineStatusCB& seek_cb) { |
| if (clock_->IsPlaying()) |
| clock_->Pause(); |
| } |
| - DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| + SetState(kPausing); |
| + DoPause(base::Bind(&Pipeline::OnStateTransition, this, PIPELINE_OK)); |
| } |
| void Pipeline::OnRendererEndedTask() { |
| @@ -861,14 +803,13 @@ void Pipeline::OnRendererEndedTask() { |
| return; |
| } |
| - // Transition to ended, executing the callback if present. |
| - SetState(kEnded); |
| { |
| base::AutoLock auto_lock(lock_); |
| clock_->EndOfStream(); |
| } |
| - ReportStatus(ended_cb_, status_); |
| + DCHECK_EQ(status_, PIPELINE_OK); |
| + ended_cb_.Run(status_); |
| } |
| void Pipeline::AudioDisabledTask() { |
| @@ -887,269 +828,110 @@ void Pipeline::AudioDisabledTask() { |
| StartClockIfWaitingForTimeUpdate_Locked(); |
| } |
| -void Pipeline::FilterStateTransitionTask() { |
| +void Pipeline::DoInitDemuxer(const PipelineStatusCB& done_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK_EQ(state_, kInitDemuxer); |
| - // No reason transitioning if we've errored or have stopped. |
| - if (IsPipelineStopped()) { |
| - return; |
| - } |
| - |
| - // If we are tearing down, don't allow any state changes. Teardown |
| - // state changes will come in via TeardownStateTransitionTask(). |
| - if (IsPipelineTearingDown()) { |
| - return; |
| - } |
| - |
| - if (!TransientState(state_)) { |
| - NOTREACHED() << "Invalid current state: " << state_; |
| - SetError(PIPELINE_ERROR_ABORT); |
| + demuxer_ = filter_collection_->GetDemuxer(); |
| + if (!demuxer_) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
is this something other than programming error? C
|
| + done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| return; |
| } |
| - // Decrement the number of remaining transitions, making sure to transition |
| - // to the next state if needed. |
| - SetState(FindNextState(state_)); |
| - if (state_ == kSeeking) { |
| - base::AutoLock auto_lock(lock_); |
| - clock_->SetTime(seek_timestamp_, seek_timestamp_); |
| - } |
| - |
| - // Carry out the action for the current state. |
| - if (TransientState(state_)) { |
| - if (state_ == kPausing) { |
| - DoPause(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| - } else if (state_ == kFlushing) { |
| - DoFlush(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| - } else if (state_ == kSeeking) { |
| - DoSeek(seek_timestamp_, false, |
| - base::Bind(&Pipeline::OnFilterStateTransitionWithStatus, this)); |
| - } else if (state_ == kStarting) { |
| - DoPlay(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| - } else if (state_ == kStopping) { |
| - DoStop(base::Bind(&Pipeline::OnFilterStateTransition, this)); |
| - } else { |
| - NOTREACHED() << "Unexpected state: " << state_; |
| - } |
| - } else if (state_ == kStarted) { |
| - FinishInitialization(); |
| - |
| - // Finally, complete the seek. |
| - seek_pending_ = false; |
| - |
| - // If a playback rate change was requested during a seek, do it now that |
| - // the seek has compelted. |
| - if (playback_rate_change_pending_) { |
| - playback_rate_change_pending_ = false; |
| - PlaybackRateChangedTask(pending_playback_rate_); |
| - } |
| - |
| - base::AutoLock auto_lock(lock_); |
| - // We use audio stream to update the clock. So if there is such a stream, |
| - // we pause the clock until we receive a valid timestamp. |
| - waiting_for_clock_update_ = true; |
| - if (!has_audio_) { |
| - clock_->SetMaxTime(clock_->Duration()); |
| - StartClockIfWaitingForTimeUpdate_Locked(); |
| - } |
| - |
| - if (IsPipelineStopPending()) { |
| - // We had a pending stop request need to be honored right now. |
| - TearDownPipeline(); |
| - } |
| - } else { |
| - NOTREACHED() << "Unexpected state: " << state_; |
| - } |
| -} |
| - |
| -void Pipeline::TeardownStateTransitionTask() { |
| - DCHECK(IsPipelineTearingDown()); |
| - switch (state_) { |
| - case kStopping: |
| - SetState(error_caused_teardown_ ? kError : kStopped); |
| - FinishDestroyingFiltersTask(); |
| - break; |
| - case kPausing: |
| - SetState(kFlushing); |
| - DoFlush(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| - break; |
| - case kFlushing: |
| - SetState(kStopping); |
| - DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| - break; |
| - |
| - case kCreated: |
| - case kError: |
| - case kInitDemuxer: |
| - case kInitAudioDecoder: |
| - case kInitAudioRenderer: |
| - case kInitVideoDecoder: |
| - case kInitVideoRenderer: |
| - case kSeeking: |
| - 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 |
| - // errors. |
| - }; |
| + demuxer_->Initialize(this, done_cb); |
| } |
| -void Pipeline::FinishDestroyingFiltersTask() { |
| +void Pipeline::DoInitAudioDecoder(const PipelineStatusCB& done_cb) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is it right that Pipeline still initializes the de
|
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK(IsPipelineStopped()); |
| - |
| - audio_renderer_ = NULL; |
| - video_renderer_ = NULL; |
| - demuxer_ = NULL; |
| + DCHECK_EQ(state_, kInitAudioDecoder); |
| - if (error_caused_teardown_ && !IsPipelineOk() && !error_cb_.is_null()) |
| - error_cb_.Run(status_); |
| + scoped_refptr<DemuxerStream> stream = |
| + demuxer_->GetStream(DemuxerStream::AUDIO); |
| - if (stop_pending_) { |
| - stop_pending_ = false; |
| - ResetState(); |
| - // Notify the client that stopping has finished. |
| - base::ResetAndReturn(&stop_cb_).Run(); |
| + if (!stream) { |
| + done_cb.Run(PIPELINE_OK); |
| + return; |
| } |
| - tearing_down_ = false; |
| - error_caused_teardown_ = false; |
| -} |
| - |
| -void Pipeline::InitializeDemuxer() { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK(IsPipelineOk()); |
| - |
| - demuxer_ = filter_collection_->GetDemuxer(); |
| - if (!demuxer_) { |
| - SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| + filter_collection_->SelectAudioDecoder(&pipeline_init_state_->audio_decoder); |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
This SelectFoo() api is funny in that it doesn't j
|
| + if (!pipeline_init_state_->audio_decoder) { |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
ditto (here and below). Promote to DCHECK?
|
| + done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| return; |
| } |
| - demuxer_->Initialize(this, base::Bind(&Pipeline::OnDemuxerInitialized, this)); |
| + pipeline_init_state_->audio_decoder->Initialize( |
| + stream, done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this)); |
| } |
| -void Pipeline::OnDemuxerInitialized(PipelineStatus status) { |
| - if (!message_loop_->BelongsToCurrentThread()) { |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &Pipeline::OnDemuxerInitialized, this, status)); |
| - return; |
| - } |
| +void Pipeline::DoInitAudioRenderer(const PipelineStatusCB& done_cb) { |
| + DCHECK(message_loop_->BelongsToCurrentThread()); |
| + DCHECK_EQ(state_, kInitAudioRenderer); |
| - if (status != PIPELINE_OK) { |
| - SetError(status); |
| + if (!pipeline_init_state_->audio_decoder) { |
| + done_cb.Run(PIPELINE_OK); |
| return; |
| } |
| - { |
| - base::AutoLock auto_lock(lock_); |
| - // We do not want to start the clock running. We only want to set the base |
| - // media time so our timestamp calculations will be correct. |
| - clock_->SetTime(demuxer_->GetStartTime(), demuxer_->GetStartTime()); |
| + filter_collection_->SelectAudioRenderer(&audio_renderer_); |
| + if (!audio_renderer_) { |
| + done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| + return; |
| } |
| - OnFilterInitialize(PIPELINE_OK); |
| + audio_renderer_->Initialize( |
| + pipeline_init_state_->audio_decoder, |
| + done_cb, |
| + base::Bind(&Pipeline::OnAudioUnderflow, this), |
| + base::Bind(&Pipeline::OnAudioTimeUpdate, this), |
| + base::Bind(&Pipeline::OnRendererEnded, this), |
| + base::Bind(&Pipeline::OnAudioDisabled, this), |
| + base::Bind(&Pipeline::SetError, this)); |
| } |
| -bool Pipeline::InitializeAudioDecoder( |
| - const scoped_refptr<Demuxer>& demuxer) { |
| +void Pipeline::DoInitVideoDecoder(const PipelineStatusCB& done_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK(IsPipelineOk()); |
| - DCHECK(demuxer); |
| + DCHECK_EQ(state_, kInitVideoDecoder); |
| scoped_refptr<DemuxerStream> stream = |
| - demuxer->GetStream(DemuxerStream::AUDIO); |
| - |
| - if (!stream) |
| - return false; |
| + demuxer_->GetStream(DemuxerStream::VIDEO); |
| - filter_collection_->SelectAudioDecoder(&pipeline_init_state_->audio_decoder); |
| - |
| - if (!pipeline_init_state_->audio_decoder) { |
| - SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| - return false; |
| + if (!stream) { |
| + done_cb.Run(PIPELINE_OK); |
| + return; |
| } |
| - pipeline_init_state_->audio_decoder->Initialize( |
| - stream, |
| - base::Bind(&Pipeline::OnFilterInitialize, this), |
| - base::Bind(&Pipeline::OnUpdateStatistics, this)); |
| - return true; |
| -} |
| - |
| -bool Pipeline::InitializeVideoDecoder( |
| - const scoped_refptr<Demuxer>& demuxer) { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK(IsPipelineOk()); |
| - DCHECK(demuxer); |
| - |
| - scoped_refptr<DemuxerStream> stream = |
| - demuxer->GetStream(DemuxerStream::VIDEO); |
| - |
| - if (!stream) |
| - return false; |
| - |
| filter_collection_->SelectVideoDecoder(&pipeline_init_state_->video_decoder); |
| - |
| if (!pipeline_init_state_->video_decoder) { |
| - SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| - return false; |
| + done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| + return; |
| } |
| pipeline_init_state_->video_decoder->Initialize( |
| - stream, |
| - base::Bind(&Pipeline::OnFilterInitialize, this), |
| - base::Bind(&Pipeline::OnUpdateStatistics, this)); |
| - |
| - video_decoder_ = pipeline_init_state_->video_decoder; |
| - return true; |
| + stream, done_cb, base::Bind(&Pipeline::OnUpdateStatistics, this)); |
| } |
| -bool Pipeline::InitializeAudioRenderer( |
| - const scoped_refptr<AudioDecoder>& decoder) { |
| +void Pipeline::DoInitVideoRenderer(const PipelineStatusCB& done_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK(IsPipelineOk()); |
| + DCHECK_EQ(state_, kInitVideoRenderer); |
| - if (!decoder) |
| - return false; |
| - |
| - filter_collection_->SelectAudioRenderer(&audio_renderer_); |
| - if (!audio_renderer_) { |
| - SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| - return false; |
| + if (!pipeline_init_state_->video_decoder) { |
| + done_cb.Run(PIPELINE_OK); |
| + return; |
| } |
| - audio_renderer_->Initialize( |
| - decoder, |
| - base::Bind(&Pipeline::OnFilterInitialize, this), |
| - base::Bind(&Pipeline::OnAudioUnderflow, this), |
| - base::Bind(&Pipeline::OnAudioTimeUpdate, this), |
| - base::Bind(&Pipeline::OnRendererEnded, this), |
| - base::Bind(&Pipeline::OnAudioDisabled, this), |
| - base::Bind(&Pipeline::SetError, this)); |
| - return true; |
| -} |
| - |
| -bool Pipeline::InitializeVideoRenderer( |
| - const scoped_refptr<VideoDecoder>& decoder) { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK(IsPipelineOk()); |
| - |
| - if (!decoder) |
| - return false; |
| - |
| filter_collection_->SelectVideoRenderer(&video_renderer_); |
| if (!video_renderer_) { |
| - SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| - return false; |
| + done_cb.Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING); |
| + return; |
| } |
| + // TODO(scherkus): This is for PrepareForShutdownHack(), see |
| + // http://crbug.com/110228 for details. |
| + video_decoder_ = pipeline_init_state_->video_decoder; |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Drop this member.
|
| + |
| video_renderer_->Initialize( |
| - decoder, |
| - base::Bind(&Pipeline::OnFilterInitialize, this), |
| + video_decoder_, |
| + done_cb, |
| base::Bind(&Pipeline::OnUpdateStatistics, this), |
| base::Bind(&Pipeline::OnVideoTimeUpdate, this), |
| base::Bind(&Pipeline::OnNaturalVideoSizeChanged, this), |
| @@ -1157,94 +939,34 @@ bool Pipeline::InitializeVideoRenderer( |
| base::Bind(&Pipeline::SetError, this), |
| base::Bind(&Pipeline::GetMediaTime, this), |
| base::Bind(&Pipeline::GetMediaDuration, this)); |
| - return true; |
| } |
| -void Pipeline::TearDownPipeline() { |
| +void Pipeline::DoSeek(bool skip_demuxer_seek, |
| + const PipelineStatusCB& done_cb) { |
| DCHECK(message_loop_->BelongsToCurrentThread()); |
| - DCHECK_NE(kStopped, state_); |
| - |
| - DCHECK(!tearing_down_ || // Teardown on Stop(). |
| - (tearing_down_ && error_caused_teardown_) || // Teardown on error. |
| - (tearing_down_ && stop_pending_)); // Stop during teardown by error. |
| - |
| - // Mark that we already start tearing down operation. |
| - tearing_down_ = true; |
| - |
| - switch (state_) { |
| - case kCreated: |
| - case kError: |
| - SetState(kStopped); |
| - // Need to put this in the message loop to make sure that it comes |
| - // after any pending callback tasks that are already queued. |
| - message_loop_->PostTask(FROM_HERE, base::Bind( |
| - &Pipeline::FinishDestroyingFiltersTask, this)); |
| - break; |
| - |
| - case kInitDemuxer: |
| - case kInitAudioDecoder: |
| - case kInitAudioRenderer: |
| - case kInitVideoDecoder: |
| - case kInitVideoRenderer: |
| - // Make it look like initialization was successful. |
| - filter_collection_.reset(); |
| - pipeline_init_state_.reset(); |
| - |
| - SetState(kStopping); |
| - DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| + DCHECK(state_ == kSeeking); |
| + DCHECK(seek_timestamp_ != kNoTimestamp()); |
| - FinishInitialization(); |
| - break; |
| - |
| - case kPausing: |
| - case kSeeking: |
| - case kFlushing: |
| - case kStarting: |
| - SetState(kStopping); |
| - DoStop(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| - |
| - if (seek_pending_) { |
| - seek_pending_ = false; |
| - FinishInitialization(); |
| - } |
| - |
| - break; |
| - |
| - case kStarted: |
| - case kEnded: |
| - SetState(kPausing); |
| - DoPause(base::Bind(&Pipeline::OnTeardownStateTransition, this)); |
| - break; |
| - |
| - case kStopping: |
| - case kStopped: |
| - NOTREACHED() << "Unexpected state for teardown: " << state_; |
| - break; |
| - // default: intentionally left out to force new states to cause compiler |
| - // errors. |
| - }; |
| -} |
| + { |
| + base::AutoLock l(lock_); |
| + clock_->SetTime(seek_timestamp_, seek_timestamp_); |
| + } |
| -void Pipeline::DoSeek(base::TimeDelta seek_timestamp, |
| - bool skip_demuxer_seek, |
| - const PipelineStatusCB& done_cb) { |
| - DCHECK(message_loop_->BelongsToCurrentThread()); |
| scoped_ptr<std::queue<PipelineStatusCBFunc> > status_cbs( |
| new std::queue<PipelineStatusCBFunc>()); |
| if (!skip_demuxer_seek) |
|
Ami GONE FROM CHROMIUM
2012/07/30 04:06:21
Is this really important? What happens if we don'
|
| - status_cbs->push(base::Bind(&Demuxer::Seek, demuxer_, seek_timestamp)); |
| + status_cbs->push(base::Bind(&Demuxer::Seek, demuxer_, seek_timestamp_)); |
| if (audio_renderer_) |
| status_cbs->push(base::Bind( |
| - &AudioRenderer::Preroll, audio_renderer_, seek_timestamp)); |
| + &AudioRenderer::Preroll, audio_renderer_, seek_timestamp_)); |
| if (video_renderer_) |
| status_cbs->push(base::Bind( |
| - &VideoRenderer::Preroll, video_renderer_, seek_timestamp)); |
| + &VideoRenderer::Preroll, video_renderer_, seek_timestamp_)); |
| - RunInSeriesWithStatus(status_cbs.Pass(), base::Bind( |
| - &Pipeline::ReportStatus, this, done_cb)); |
| + RunInSeriesWithStatus(status_cbs.Pass(), done_cb); |
| } |
| void Pipeline::OnAudioUnderflow() { |