diff options
-rw-r--r-- | media/base/pipeline_impl.cc | 262 | ||||
-rw-r--r-- | media/base/pipeline_impl.h | 277 | ||||
-rw-r--r-- | media/base/pipeline_impl_unittest.cc | 5 |
3 files changed, 210 insertions, 334 deletions
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc index 7fc4695..07378c0 100644 --- a/media/base/pipeline_impl.cc +++ b/media/base/pipeline_impl.cc @@ -72,60 +72,77 @@ void DecrementCounter(Lock* lock, ConditionVariable* cond_var, int* count) { } // namespace PipelineImpl::PipelineImpl(MessageLoop* message_loop) - : message_loop_(message_loop) { + : message_loop_(message_loop), + state_(kCreated) { ResetState(); } PipelineImpl::~PipelineImpl() { - DCHECK(!pipeline_internal_) - << "Stop() must complete before destroying object"; + AutoLock auto_lock(lock_); + DCHECK(!running_) << "Stop() must complete before destroying object"; } // Creates the PipelineInternal and calls it's start method. - bool PipelineImpl::Start(FilterFactory* factory, +bool PipelineImpl::Start(FilterFactory* factory, const std::string& url, PipelineCallback* start_callback) { - DCHECK(!pipeline_internal_) << "PipelineInternal already exists"; + AutoLock auto_lock(lock_); + DCHECK(factory); scoped_ptr<PipelineCallback> callback(start_callback); - if (pipeline_internal_ || !factory) { + if (running_) { + LOG(INFO) << "Media pipeline is already running"; return false; } - - // Create and start the PipelineInternal. - pipeline_internal_ = new PipelineInternal(this, message_loop_); - if (!pipeline_internal_) { - NOTREACHED() << "Could not create PipelineInternal"; + if (!factory) { return false; } - pipeline_internal_->Start(factory, url, callback.release()); + + // Kick off initialization! + running_ = true; + message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PipelineImpl::StartTask, factory, url, + callback.release())); return true; } -// Stop the PipelineInternal who will NULL our reference to it and reset our -// state to a newly created PipelineImpl object. void PipelineImpl::Stop(PipelineCallback* stop_callback) { + AutoLock auto_lock(lock_); scoped_ptr<PipelineCallback> callback(stop_callback); - if (pipeline_internal_) { - pipeline_internal_->Stop(callback.release()); + if (!running_) { + LOG(INFO) << "Media pipeline has already stopped"; + return; } + + // Stop the pipeline, which will set |running_| to false on behalf. + message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PipelineImpl::StopTask, callback.release())); } void PipelineImpl::Seek(base::TimeDelta time, PipelineCallback* seek_callback) { + AutoLock auto_lock(lock_); scoped_ptr<PipelineCallback> callback(seek_callback); - if (pipeline_internal_) { - pipeline_internal_->Seek(time, callback.release()); + if (!running_) { + LOG(INFO) << "Media pipeline must be running"; + return; } + + message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PipelineImpl::SeekTask, time, + callback.release())); } bool PipelineImpl::IsRunning() const { AutoLock auto_lock(lock_); - return pipeline_internal_ != NULL; + return running_; } bool PipelineImpl::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_|. AutoLock auto_lock(lock_); - return pipeline_internal_ && pipeline_internal_->IsInitialized(); + return state_ == kStarted; } bool PipelineImpl::IsRendered(const std::string& major_mime_type) const { @@ -147,8 +164,10 @@ void PipelineImpl::SetPlaybackRate(float playback_rate) { AutoLock auto_lock(lock_); playback_rate_ = playback_rate; - if (pipeline_internal_) { - pipeline_internal_->PlaybackRateChanged(playback_rate); + if (running_) { + message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PipelineImpl::PlaybackRateChangedTask, + playback_rate)); } } @@ -164,8 +183,10 @@ void PipelineImpl::SetVolume(float volume) { AutoLock auto_lock(lock_); volume_ = volume; - if (pipeline_internal_) { - pipeline_internal_->VolumeChanged(volume); + if (running_) { + message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PipelineImpl::VolumeChangedTask, + volume)); } } @@ -209,7 +230,7 @@ PipelineError PipelineImpl::GetError() const { void PipelineImpl::ResetState() { AutoLock auto_lock(lock_); - pipeline_internal_ = NULL; + running_ = false; duration_ = base::TimeDelta(); buffered_time_ = base::TimeDelta(); buffered_bytes_ = 0; @@ -223,163 +244,101 @@ void PipelineImpl::ResetState() { rendered_mime_types_.clear(); } -bool PipelineImpl::IsPipelineOk() const { - return pipeline_internal_ && PIPELINE_OK == error_; +bool PipelineImpl::IsPipelineOk() { + return PIPELINE_OK == GetError(); +} + +bool PipelineImpl::IsPipelineInitializing() { + DCHECK_EQ(MessageLoop::current(), message_loop_); + return state_ == kInitDataSource || + state_ == kInitDemuxer || + state_ == kInitAudioDecoder || + state_ == kInitAudioRenderer || + state_ == kInitVideoDecoder || + state_ == kInitVideoRenderer; } void PipelineImpl::SetError(PipelineError error) { + DCHECK(IsRunning()); + DCHECK(error != PIPELINE_OK) << "PIPELINE_OK isn't an error!"; + LOG(INFO) << "Media pipeline error: " << error; + AutoLock auto_lock(lock_); error_ = error; + message_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &PipelineImpl::ErrorChangedTask, error)); } base::TimeDelta PipelineImpl::GetTime() const { + DCHECK(IsRunning()); return GetCurrentTime(); } void PipelineImpl::SetTime(base::TimeDelta time) { + DCHECK(IsRunning()); AutoLock auto_lock(lock_); time_ = time; } void PipelineImpl::SetDuration(base::TimeDelta duration) { + DCHECK(IsRunning()); AutoLock auto_lock(lock_); duration_ = duration; } void PipelineImpl::SetBufferedTime(base::TimeDelta buffered_time) { + DCHECK(IsRunning()); AutoLock auto_lock(lock_); buffered_time_ = buffered_time; } void PipelineImpl::SetTotalBytes(int64 total_bytes) { + DCHECK(IsRunning()); AutoLock auto_lock(lock_); total_bytes_ = total_bytes; } void PipelineImpl::SetBufferedBytes(int64 buffered_bytes) { + DCHECK(IsRunning()); AutoLock auto_lock(lock_); buffered_bytes_ = buffered_bytes; } void PipelineImpl::SetVideoSize(size_t width, size_t height) { + DCHECK(IsRunning()); AutoLock auto_lock(lock_); video_width_ = width; video_height_ = height; } void PipelineImpl::InsertRenderedMimeType(const std::string& major_mime_type) { + DCHECK(IsRunning()); AutoLock auto_lock(lock_); rendered_mime_types_.insert(major_mime_type); } - -//----------------------------------------------------------------------------- - -PipelineInternal::PipelineInternal(PipelineImpl* pipeline, - MessageLoop* message_loop) - : pipeline_(pipeline), - message_loop_(message_loop), - state_(kCreated) { -} - -PipelineInternal::~PipelineInternal() { - DCHECK(state_ == kCreated || state_ == kStopped); -} - -// Called on client's thread. -void PipelineInternal::Start(FilterFactory* filter_factory, - const std::string& url, - PipelineCallback* start_callback) { - DCHECK(filter_factory); - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PipelineInternal::StartTask, filter_factory, url, - start_callback)); -} - -// Called on client's thread. -void PipelineInternal::Stop(PipelineCallback* stop_callback) { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PipelineInternal::StopTask, stop_callback)); -} - -// Called on client's thread. -void PipelineInternal::Seek(base::TimeDelta time, - PipelineCallback* seek_callback) { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PipelineInternal::SeekTask, time, - seek_callback)); -} - -// Called on client's thread. -void PipelineInternal::PlaybackRateChanged(float playback_rate) { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PipelineInternal::PlaybackRateChangedTask, - playback_rate)); -} - -// Called on client's thread. -void PipelineInternal::VolumeChanged(float volume) { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PipelineInternal::VolumeChangedTask, volume)); -} - -// Called from any thread. -void PipelineInternal::SetError(PipelineError error) { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PipelineInternal::ErrorTask, error)); -} - -// Called from any thread. -base::TimeDelta PipelineInternal::GetTime() const { - return pipeline_->GetCurrentTime(); -} - -// Called from any thread. -void PipelineInternal::SetTime(base::TimeDelta time) { - pipeline_->SetTime(time); -} - -// Called from any thread. -void PipelineInternal::SetDuration(base::TimeDelta duration) { - pipeline_->SetDuration(duration); -} - -// Called from any thread. -void PipelineInternal::SetBufferedTime(base::TimeDelta buffered_time) { - pipeline_->SetBufferedTime(buffered_time); -} - -// Called from any thread. -void PipelineInternal::SetTotalBytes(int64 total_bytes) { - pipeline_->SetTotalBytes(total_bytes); -} - -// Called from any thread. -void PipelineInternal::SetBufferedBytes(int64 buffered_bytes) { - pipeline_->SetBufferedBytes(buffered_bytes); -} - -// Called from any thread. -void PipelineInternal::SetVideoSize(size_t width, size_t height) { - pipeline_->SetVideoSize(width, height); +bool PipelineImpl::HasRenderedMimeTypes() const { + DCHECK(IsRunning()); + AutoLock auto_lock(lock_); + return !rendered_mime_types_.empty(); } // Called from any thread. -void PipelineInternal::OnFilterInitialize() { +void PipelineImpl::OnFilterInitialize() { // Continue the initialize task by proceeding to the next stage. message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &PipelineInternal::InitializeTask)); + NewRunnableMethod(this, &PipelineImpl::InitializeTask)); } // Called from any thread. -void PipelineInternal::OnFilterSeek() { +void PipelineImpl::OnFilterSeek() { // TODO(scherkus): have PipelineInternal wait to receive replies from every // filter before calling the client's |seek_callback_|. } -void PipelineInternal::StartTask(FilterFactory* filter_factory, - const std::string& url, - PipelineCallback* start_callback) { +void PipelineImpl::StartTask(FilterFactory* filter_factory, + const std::string& url, + PipelineCallback* start_callback) { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK_EQ(kCreated, state_); filter_factory_ = filter_factory; @@ -407,7 +366,7 @@ void PipelineInternal::StartTask(FilterFactory* filter_factory, // 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 PipelineInternal::InitializeTask() { +void PipelineImpl::InitializeTask() { DCHECK_EQ(MessageLoop::current(), message_loop_); // If we have received the stop or error signal, return immediately. @@ -443,7 +402,7 @@ void PipelineInternal::InitializeTask() { state_ = kInitAudioRenderer; // Returns false if there's no audio stream. if (CreateRenderer<AudioDecoder, AudioRenderer>()) { - pipeline_->InsertRenderedMimeType(AudioDecoder::major_mime_type()); + InsertRenderedMimeType(AudioDecoder::major_mime_type()); return; } } @@ -460,20 +419,20 @@ void PipelineInternal::InitializeTask() { if (state_ == kInitVideoDecoder) { state_ = kInitVideoRenderer; if (CreateRenderer<VideoDecoder, VideoRenderer>()) { - pipeline_->InsertRenderedMimeType(VideoDecoder::major_mime_type()); + InsertRenderedMimeType(VideoDecoder::major_mime_type()); return; } } if (state_ == kInitVideoRenderer) { - if (!IsPipelineOk() || pipeline_->rendered_mime_types_.empty()) { + if (!IsPipelineOk() || !HasRenderedMimeTypes()) { SetError(PIPELINE_ERROR_COULD_NOT_RENDER); return; } // Initialization was successful, set the volume and playback rate. - PlaybackRateChangedTask(pipeline_->GetPlaybackRate()); - VolumeChangedTask(pipeline_->GetVolume()); + PlaybackRateChangedTask(GetPlaybackRate()); + VolumeChangedTask(GetVolume()); state_ = kStarted; filter_factory_ = NULL; @@ -492,7 +451,7 @@ void PipelineInternal::InitializeTask() { // 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 PipelineInternal::StopTask(PipelineCallback* stop_callback) { +void PipelineImpl::StopTask(PipelineCallback* stop_callback) { DCHECK_EQ(MessageLoop::current(), message_loop_); stop_callback_.reset(stop_callback); @@ -502,15 +461,13 @@ void PipelineInternal::StopTask(PipelineCallback* stop_callback) { } // Carry out setting the error, notifying the client and destroying filters. - ErrorTask(PIPELINE_STOPPING); + ErrorChangedTask(PIPELINE_STOPPING); // We no longer need to examine our previous state, set it to stopped. state_ = kStopped; - // Reset the pipeline and set our reference to NULL so we don't accidentally - // modify the pipeline. Once remaining tasks execute we will be destroyed. - pipeline_->ResetState(); - pipeline_ = NULL; + // Reset the pipeline. + ResetState(); // Notify the client that stopping has finished. if (stop_callback_.get()) { @@ -519,7 +476,7 @@ void PipelineInternal::StopTask(PipelineCallback* stop_callback) { } } -void PipelineInternal::ErrorTask(PipelineError error) { +void PipelineImpl::ErrorChangedTask(PipelineError error) { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!"; @@ -532,9 +489,6 @@ void PipelineInternal::ErrorTask(PipelineError error) { return; } - // Update our error code first in case we execute the start callback. - pipeline_->SetError(error); - // Notify the client that starting did not complete, if necessary. if (IsPipelineInitializing() && start_callback_.get()) { start_callback_->Run(); @@ -549,7 +503,7 @@ void PipelineInternal::ErrorTask(PipelineError error) { DestroyFilters(); } -void PipelineInternal::PlaybackRateChangedTask(float playback_rate) { +void PipelineImpl::PlaybackRateChangedTask(float playback_rate) { DCHECK_EQ(MessageLoop::current(), message_loop_); for (FilterVector::iterator iter = filters_.begin(); iter != filters_.end(); @@ -558,7 +512,7 @@ void PipelineInternal::PlaybackRateChangedTask(float playback_rate) { } } -void PipelineInternal::VolumeChangedTask(float volume) { +void PipelineImpl::VolumeChangedTask(float volume) { DCHECK_EQ(MessageLoop::current(), message_loop_); scoped_refptr<AudioRenderer> audio_renderer; @@ -568,8 +522,8 @@ void PipelineInternal::VolumeChangedTask(float volume) { } } -void PipelineInternal::SeekTask(base::TimeDelta time, - PipelineCallback* seek_callback) { +void PipelineImpl::SeekTask(base::TimeDelta time, + PipelineCallback* seek_callback) { DCHECK_EQ(MessageLoop::current(), message_loop_); seek_callback_.reset(seek_callback); @@ -581,7 +535,7 @@ void PipelineInternal::SeekTask(base::TimeDelta time, for (FilterVector::iterator iter = filters_.begin(); iter != filters_.end(); ++iter) { - (*iter)->Seek(time, NewCallback(this, &PipelineInternal::OnFilterSeek)); + (*iter)->Seek(time, NewCallback(this, &PipelineImpl::OnFilterSeek)); } // TODO(hclam): we should set the time when the above seek operations were all @@ -599,9 +553,9 @@ void PipelineInternal::SeekTask(base::TimeDelta time, } template <class Filter, class Source> -void PipelineInternal::CreateFilter(FilterFactory* filter_factory, - Source source, - const MediaFormat& media_format) { +void PipelineImpl::CreateFilter(FilterFactory* filter_factory, + Source source, + const MediaFormat& media_format) { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK(IsPipelineOk()); @@ -635,10 +589,10 @@ void PipelineInternal::CreateFilter(FilterFactory* filter_factory, // Now initialize the filter. filter->Initialize(source, - NewCallback(this, &PipelineInternal::OnFilterInitialize)); + NewCallback(this, &PipelineImpl::OnFilterInitialize)); } -void PipelineInternal::CreateDataSource() { +void PipelineImpl::CreateDataSource() { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK(IsPipelineOk()); @@ -648,7 +602,7 @@ void PipelineInternal::CreateDataSource() { CreateFilter<DataSource>(filter_factory_, url_, url_format); } -void PipelineInternal::CreateDemuxer() { +void PipelineImpl::CreateDemuxer() { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK(IsPipelineOk()); @@ -659,7 +613,7 @@ void PipelineInternal::CreateDemuxer() { } template <class Decoder> -bool PipelineInternal::CreateDecoder() { +bool PipelineImpl::CreateDecoder() { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK(IsPipelineOk()); @@ -682,7 +636,7 @@ bool PipelineInternal::CreateDecoder() { } template <class Decoder, class Renderer> -bool PipelineInternal::CreateRenderer() { +bool PipelineImpl::CreateRenderer() { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK(IsPipelineOk()); @@ -699,7 +653,7 @@ bool PipelineInternal::CreateRenderer() { } template <class Filter> -void PipelineInternal::GetFilter(scoped_refptr<Filter>* filter_out) const { +void PipelineImpl::GetFilter(scoped_refptr<Filter>* filter_out) const { DCHECK_EQ(MessageLoop::current(), message_loop_); FilterTypeMap::const_iterator ft = filter_types_.find(Filter::filter_type()); @@ -710,7 +664,7 @@ void PipelineInternal::GetFilter(scoped_refptr<Filter>* filter_out) const { } } -void PipelineInternal::DestroyFilters() { +void PipelineImpl::DestroyFilters() { // Stop every filter. for (FilterVector::iterator iter = filters_.begin(); iter != filters_.end(); diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h index b72e368..1050ef1 100644 --- a/media/base/pipeline_impl.h +++ b/media/base/pipeline_impl.h @@ -20,16 +20,28 @@ namespace media { -class PipelineInternal; - -// Class which implements the Media::Pipeline contract. The majority of the -// actual code for this object lives in the PipelineInternal class, which is -// responsible for actually building and running the pipeline. This object -// is basically a simple container for state information, and is responsible -// for creating and communicating with the PipelineInternal object. -class PipelineImpl : public Pipeline { + +// PipelineImpl runs the media pipeline. Filters are created and called on the +// message loop injected into this object. PipelineImpl works like a state +// machine to perform asynchronous initialization. Initialization is done in +// multiple passes by InitializeTask(). In each pass a different filter is +// created and chained with a previously created filter. +// +// Here's a state diagram that describes the lifetime of this object. +// +// [ *Created ] -> [ InitDataSource ] -> [ InitDemuxer ] -> +// [ InitAudioDecoder ] -> [ InitAudioRenderer ] -> +// [ InitVideoDecoder ] -> [ InitVideoRenderer ] -> [ Started ] +// | | | +// .-> [ Error ] .-> [ Stopped ] <-. +// +// Initialization is a series of state transitions from "Created" to +// "Started". If any error happens during initialization, this object will +// transition to the "Error" state from any state. If Stop() is called during +// initialization, this object will transition to "Stopped" state. +class PipelineImpl : public Pipeline, public FilterHost { public: - PipelineImpl(MessageLoop* message_loop); + explicit PipelineImpl(MessageLoop* message_loop); // Pipeline implementation. virtual bool Start(FilterFactory* filter_factory, @@ -53,142 +65,17 @@ class PipelineImpl : public Pipeline { virtual PipelineError GetError() const; private: - friend class PipelineInternal; virtual ~PipelineImpl(); // Reset the state of the pipeline object to the initial state. This method - // is used by the constructor, and the Stop method. + // is used by the constructor, and the Stop() method. void ResetState(); - // Used internally to make sure that the thread is in a state that is - // acceptable to post a task to. It must exist, be initialized, and there - // must not be an error. - bool IsPipelineOk() const; - - // Methods called by |pipeline_internal_| to update global pipeline data. - // - // Although this is the exact same as the FilterHost interface, we need to - // let |pipeline_internal_| receive the call first so it can post tasks as - // necessary. - void SetError(PipelineError error); - base::TimeDelta GetTime() const; - void SetTime(base::TimeDelta time); - void SetDuration(base::TimeDelta duration); - void SetBufferedTime(base::TimeDelta buffered_time); - void SetTotalBytes(int64 total_bytes); - void SetBufferedBytes(int64 buffered_bytes); - void SetVideoSize(size_t width, size_t height); - - // Method called by the |pipeline_internal_| to insert a mime type into - // the |rendered_mime_types_| set. - void InsertRenderedMimeType(const std::string& major_mime_type); - - // Message loop used to execute pipeline tasks. - MessageLoop* message_loop_; - - // Holds a ref counted reference to the PipelineInternal object associated - // with this pipeline. Prior to the call to the Start() method, this member - // will be NULL, since we are not running. - scoped_refptr<PipelineInternal> pipeline_internal_; - - // After calling Start, if all of the required filters are created and - // initialized, this member will be set to true by the pipeline thread. - bool initialized_; - - // Duration of the media in microseconds. Set by filters. - base::TimeDelta duration_; - - // Amount of available buffered data in microseconds. Set by filters. - base::TimeDelta buffered_time_; - - // Amount of available buffered data. Set by filters. - int64 buffered_bytes_; - - // Total size of the media. Set by filters. - int64 total_bytes_; - - // Lock used to serialize access for getter/setter methods. - mutable Lock lock_; - - // Video width and height. Set by filters. - size_t video_width_; - size_t video_height_; - - // Current volume level (from 0.0f to 1.0f). This value is set immediately - // via SetVolume() and a task is dispatched on the message loop to notify the - // filters. - float volume_; - - // Current playback rate (>= 0.0f). This value is set immediately via - // SetPlaybackRate() and a task is dispatched on the message loop to notify - // the filters. - float playback_rate_; - - // Current playback time. Set by filters. - base::TimeDelta time_; - - // Status of the pipeline. Initialized to PIPELINE_OK which indicates that - // the pipeline is operating correctly. Any other value indicates that the - // pipeline is stopped or is stopping. Clients can call the Stop method to - // reset the pipeline state, and restore this to PIPELINE_OK. - PipelineError error_; - - // Vector of major mime types that have been rendered by this pipeline. - typedef std::set<std::string> RenderedMimeTypesSet; - RenderedMimeTypesSet rendered_mime_types_; - - DISALLOW_COPY_AND_ASSIGN(PipelineImpl); -}; - - -// PipelineInternal contains most of the logic involved with running the -// media pipeline. Filters are created and called on the message loop injected -// into this object. PipelineInternal works like a state machine to perform -// asynchronous initialization. Initialization is done in multiple passes by -// InitializeTask(). In each pass a different filter is created and chained with -// a previously created filter. -// -// Here's a state diagram that describes the lifetime of this object. -// -// [ *Created ] -> [ InitDataSource ] -> [ InitDemuxer ] -> -// [ InitAudioDecoder ] -> [ InitAudioRenderer ] -> -// [ InitVideoDecoder ] -> [ InitVideoRenderer ] -> [ Started ] -// | | | -// .-> [ Error ] .-> [ Stopped ] <-. -// -// Initialization is a series of state transitions from "Created" to -// "Started". If any error happens during initialization, this object will -// transition to the "Error" state from any state. If Stop() is called during -// initialization, this object will transition to "Stopped" state. - -class PipelineInternal : public FilterHost, - public base::RefCountedThreadSafe<PipelineInternal> { - public: - // Methods called by PipelineImpl object on the client's thread. These - // methods post a task to call a corresponding xxxTask() method on the - // message loop. For example, Seek posts a task to call SeekTask. - PipelineInternal(PipelineImpl* pipeline, MessageLoop* message_loop); - - // After Start() is called, a task of StartTask() is posted on the pipeline - // thread to perform initialization. See StartTask() to learn more about - // initialization. - void Start(FilterFactory* filter_factory, - const std::string& url_media_source, - PipelineCallback* start_callback); - void Stop(PipelineCallback* stop_callback); - void Seek(base::TimeDelta time, PipelineCallback* seek_callback); - - // Notifies that the client has changed the playback rate/volume. - void PlaybackRateChanged(float playback_rate); - void VolumeChanged(float volume); - - // Returns true if the pipeline has fully initialized. - bool IsInitialized() { return state_ == kStarted; } + // Simple method used to make sure the pipeline is running normally. + bool IsPipelineOk(); - private: - // Only allow ourselves to be destroyed via ref-counting. - friend class base::RefCountedThreadSafe<PipelineInternal>; - virtual ~PipelineInternal(); + // Helper method to tell whether we are in the state of initializing. + bool IsPipelineInitializing(); // FilterHost implementation. virtual void SetError(PipelineError error); @@ -200,31 +87,12 @@ class PipelineInternal : public FilterHost, virtual void SetBufferedBytes(int64 buffered_bytes); virtual void SetVideoSize(size_t width, size_t height); - enum State { - kCreated, - kInitDataSource, - kInitDemuxer, - kInitAudioDecoder, - kInitAudioRenderer, - kInitVideoDecoder, - kInitVideoRenderer, - kStarted, - kStopped, - kError, - }; - - // Simple method used to make sure the pipeline is running normally. - bool IsPipelineOk() { return PIPELINE_OK == pipeline_->error_; } + // Method called during initialization to insert a mime type into the + // |rendered_mime_types_| set. + void InsertRenderedMimeType(const std::string& major_mime_type); - // Helper method to tell whether we are in the state of initializing. - bool IsPipelineInitializing() { - return state_ == kInitDataSource || - state_ == kInitDemuxer || - state_ == kInitAudioDecoder || - state_ == kInitAudioRenderer || - state_ == kInitVideoDecoder || - state_ == kInitVideoRenderer; - } + // Method called during initialization to determine if we rendered anything. + bool HasRenderedMimeTypes() const; // Callback executed by filters upon completing initialization and seeking. void OnFilterInitialize(); @@ -243,18 +111,18 @@ class PipelineInternal : public FilterHost, // initialization. void InitializeTask(); - // StopTask() and ErrorTask() are similar but serve different purposes: - // - Both destroy the filter chain. - // - Both will execute |start_callback| if the pipeline was initializing. - // - StopTask() resets the pipeline to a fresh state, where as ErrorTask() - // leaves the pipeline as is for client inspection. - // - StopTask() can be scheduled by the client calling Stop(), where as - // ErrorTask() is scheduled as a result of a filter calling SetError(). + // Stops and destroys all filters, placing the pipeline in the kStopped state + // and setting the error code to PIPELINE_STOPPED. void StopTask(PipelineCallback* stop_callback); - void ErrorTask(PipelineError error); - // Carries out notifying filters that the playback rate/volume has changed, + // Carries out stopping and destroying all filters, placing the pipeline in + // the kError state. + void ErrorChangedTask(PipelineError error); + + // Carries out notifying filters that the playback rate has changed. void PlaybackRateChangedTask(float playback_rate); + + // Carries out notifying filters that the volume has changed. void VolumeChangedTask(float volume); // Carries out notifying filters that we are seeking to a new timestamp. @@ -326,13 +194,70 @@ class PipelineInternal : public FilterHost, // references to them. void DestroyFilters(); - // Pointer to the pipeline that owns this PipelineInternal. - PipelineImpl* pipeline_; - // Message loop used to execute pipeline tasks. MessageLoop* message_loop_; + // Lock used to serialize access for the following data members. + mutable Lock lock_; + + // Whether or not the pipeline is running. + bool running_; + + // Duration of the media in microseconds. Set by filters. + base::TimeDelta duration_; + + // Amount of available buffered data in microseconds. Set by filters. + base::TimeDelta buffered_time_; + + // Amount of available buffered data. Set by filters. + int64 buffered_bytes_; + + // Total size of the media. Set by filters. + int64 total_bytes_; + + // Video width and height. Set by filters. + size_t video_width_; + size_t video_height_; + + // Current volume level (from 0.0f to 1.0f). This value is set immediately + // via SetVolume() and a task is dispatched on the message loop to notify the + // filters. + float volume_; + + // Current playback rate (>= 0.0f). This value is set immediately via + // SetPlaybackRate() and a task is dispatched on the message loop to notify + // the filters. + float playback_rate_; + + // Current playback time. Set by filters. + base::TimeDelta time_; + + // Status of the pipeline. Initialized to PIPELINE_OK which indicates that + // the pipeline is operating correctly. Any other value indicates that the + // pipeline is stopped or is stopping. Clients can call the Stop() method to + // reset the pipeline state, and restore this to PIPELINE_OK. + PipelineError error_; + + // Vector of major mime types that have been rendered by this pipeline. + typedef std::set<std::string> RenderedMimeTypesSet; + RenderedMimeTypesSet rendered_mime_types_; + + // The following data members are only accessed by tasks posted to + // |message_loop_|. + // Member that tracks the current state. + enum State { + kCreated, + kInitDataSource, + kInitDemuxer, + kInitAudioDecoder, + kInitAudioRenderer, + kInitVideoDecoder, + kInitVideoRenderer, + kStarted, + kStopped, + kError, + }; State state_; // Filter factory as passed in by Start(). @@ -358,7 +283,7 @@ class PipelineInternal : public FilterHost, typedef std::vector<base::Thread*> FilterThreadVector; FilterThreadVector filter_threads_; - DISALLOW_COPY_AND_ASSIGN(PipelineInternal); + DISALLOW_COPY_AND_ASSIGN(PipelineImpl); }; } // namespace media diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc index 9fe3654..b2ebe54 100644 --- a/media/base/pipeline_impl_unittest.cc +++ b/media/base/pipeline_impl_unittest.cc @@ -153,9 +153,6 @@ TEST_F(PipelineImplTest, NotStarted) { // StrictMock<> will ensure these never get called, and valgrind/purify will // make sure the callbacks are instantly deleted. - pipeline_->Start(NULL, "", - NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_), - &CallbackHelper::OnStart)); pipeline_->Stop(NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_), &CallbackHelper::OnStop)); pipeline_->Seek(kZero, @@ -244,7 +241,7 @@ TEST_F(PipelineImplTest, URLNotFound) { } TEST_F(PipelineImplTest, NoStreams) { - // Manually set these expecations because SetPlaybackRate() is not called if + // Manually set these expectations because SetPlaybackRate() is not called if // we cannot fully initialize the pipeline. EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull())) .WillOnce(Invoke(&RunFilterCallback)); |