diff options
-rw-r--r-- | chrome/browser/media_uitest.cc | 2 | ||||
-rw-r--r-- | media/base/composite_filter.cc | 603 | ||||
-rw-r--r-- | media/base/composite_filter.h | 165 | ||||
-rw-r--r-- | media/base/composite_filter_unittest.cc | 809 | ||||
-rw-r--r-- | media/base/mock_filters.cc | 19 | ||||
-rw-r--r-- | media/base/mock_filters.h | 46 | ||||
-rw-r--r-- | media/base/pipeline.h | 2 | ||||
-rw-r--r-- | media/base/pipeline_impl.cc | 159 | ||||
-rw-r--r-- | media/base/pipeline_impl.h | 20 | ||||
-rw-r--r-- | media/media.gyp | 3 | ||||
-rw-r--r-- | webkit/glue/webmediaplayer_impl.cc | 2 |
11 files changed, 121 insertions, 1709 deletions
diff --git a/chrome/browser/media_uitest.cc b/chrome/browser/media_uitest.cc index 18c73eb..61861e0 100644 --- a/chrome/browser/media_uitest.cc +++ b/chrome/browser/media_uitest.cc @@ -65,7 +65,7 @@ class MediaTest : public UITest { } }; -#if defined(OS_LINUX) || defined(OS_WIN) +#if defined(OS_LINUX) // Test appears to be fine on linux, but let's first change to flaky and // see how that goes. // http://crbug.com/56364 diff --git a/media/base/composite_filter.cc b/media/base/composite_filter.cc deleted file mode 100644 index 87ff799..0000000 --- a/media/base/composite_filter.cc +++ /dev/null @@ -1,603 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/base/composite_filter.h" - -#include "base/stl_util-inl.h" -#include "media/base/callback.h" - -namespace media { - -class CompositeFilter::FilterHostImpl : public FilterHost { - public: - FilterHostImpl(CompositeFilter* parent, FilterHost* host); - - FilterHost* host(); - - // media::FilterHost methods. - virtual void SetError(PipelineError error); - virtual base::TimeDelta GetTime() const; - virtual base::TimeDelta GetDuration() const; - virtual void SetTime(base::TimeDelta time); - virtual void SetDuration(base::TimeDelta duration); - virtual void SetBufferedTime(base::TimeDelta buffered_time); - virtual void SetTotalBytes(int64 total_bytes); - virtual void SetBufferedBytes(int64 buffered_bytes); - virtual void SetVideoSize(size_t width, size_t height); - virtual void SetStreaming(bool streaming); - virtual void NotifyEnded(); - virtual void SetLoaded(bool loaded); - virtual void SetNetworkActivity(bool network_activity); - virtual void DisableAudioRenderer(); - virtual void SetCurrentReadPosition(int64 offset); - virtual int64 GetCurrentReadPosition(); - - private: - CompositeFilter* parent_; - FilterHost* host_; - - DISALLOW_COPY_AND_ASSIGN(FilterHostImpl); -}; - -CompositeFilter::CompositeFilter(MessageLoop* message_loop) { - Init(message_loop, NULL); -} - -CompositeFilter::CompositeFilter(MessageLoop* message_loop, - ThreadFactoryFunction thread_factory) { - DCHECK(thread_factory); - Init(message_loop, thread_factory); -} - -void CompositeFilter::Init(MessageLoop* message_loop, - ThreadFactoryFunction thread_factory) { - DCHECK(message_loop); - message_loop_ = message_loop; - thread_factory_ = thread_factory; - - if (!thread_factory_) { - thread_factory_ = &CompositeFilter::DefaultThreadFactory; - } - - state_ = kCreated; - sequence_index_ = 0; - error_ = PIPELINE_OK; -} - -CompositeFilter::~CompositeFilter() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(state_ == kCreated || state_ == kStopped); - - // Stop every running filter thread. - for (FilterThreadVector::iterator iter = filter_threads_.begin(); - iter != filter_threads_.end(); - ++iter) { - (*iter)->Stop(); - } - - // Reset the pipeline, which will decrement a reference to this object. - // We will get destroyed as soon as the remaining tasks finish executing. - // To be safe, we'll set our pipeline reference to NULL. - filters_.clear(); - STLDeleteElements(&filter_threads_); -} - -bool CompositeFilter::AddFilter(scoped_refptr<Filter> filter) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - if (!filter.get() || state_ != kCreated || !host()) - return false; - - // Create a dedicated thread for this filter if applicable. - if (filter->requires_message_loop()) { - scoped_ptr<base::Thread> thread( - thread_factory_(filter->message_loop_name())); - - if (!thread.get() || !thread->Start()) { - return false; - } - - filter->set_message_loop(thread->message_loop()); - filter_threads_.push_back(thread.release()); - } - - // Register ourselves as the filter's host. - filter->set_host(host_impl_.get()); - filters_.push_back(make_scoped_refptr(filter.get())); - return true; -} - -const char* CompositeFilter::major_mime_type() const { - return ""; -} - -void CompositeFilter::set_host(FilterHost* host) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - DCHECK(host); - DCHECK(!host_impl_.get()); - host_impl_.reset(new FilterHostImpl(this, host)); -} - -FilterHost* CompositeFilter::host() { - return host_impl_.get() ? host_impl_->host() : NULL; -} - -bool CompositeFilter::requires_message_loop() const { - return false; -} - -const char* CompositeFilter::message_loop_name() const { - return "CompositeFilter"; -} - -void CompositeFilter::set_message_loop(MessageLoop* message_loop) { - NOTREACHED() << "Message loop should not be set."; -} - -MessageLoop* CompositeFilter::message_loop() { - return NULL; -} - -void CompositeFilter::Play(FilterCallback* play_callback) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - scoped_ptr<FilterCallback> callback(play_callback); - if (callback_.get()) { - SendErrorToHost(PIPELINE_ERROR_OPERATION_PENDING); - callback->Run(); - return; - } else if (state_ == kPlaying) { - callback->Run(); - return; - } else if (!host() || (state_ != kPaused && state_ != kCreated)) { - SendErrorToHost(PIPELINE_ERROR_INVALID_STATE); - callback->Run(); - return; - } - - ChangeState(kPlayPending); - callback_.reset(callback.release()); - StartSerialCallSequence(); -} - -void CompositeFilter::Pause(FilterCallback* pause_callback) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - scoped_ptr<FilterCallback> callback(pause_callback); - if (callback_.get()) { - SendErrorToHost(PIPELINE_ERROR_OPERATION_PENDING); - callback->Run(); - return; - } else if (state_ == kPaused) { - callback->Run(); - return; - } else if (!host() || state_ != kPlaying) { - SendErrorToHost(PIPELINE_ERROR_INVALID_STATE); - callback->Run(); - return; - } - - ChangeState(kPausePending); - callback_.reset(callback.release()); - StartSerialCallSequence(); -} - -void CompositeFilter::Flush(FilterCallback* flush_callback) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - scoped_ptr<FilterCallback> callback(flush_callback); - if (callback_.get()) { - SendErrorToHost(PIPELINE_ERROR_OPERATION_PENDING); - callback->Run(); - return; - } else if (!host() || (state_ != kCreated && state_ != kPaused)) { - SendErrorToHost(PIPELINE_ERROR_INVALID_STATE); - callback->Run(); - return; - } - - ChangeState(kFlushPending); - callback_.reset(callback.release()); - StartParallelCallSequence(); -} - -void CompositeFilter::Stop(FilterCallback* stop_callback) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - scoped_ptr<FilterCallback> callback(stop_callback); - if (!host()) { - SendErrorToHost(PIPELINE_ERROR_INVALID_STATE); - callback->Run(); - return; - } else if (state_ == kStopped) { - callback->Run(); - return; - } - - switch(state_) { - case kError: - case kCreated: - case kPaused: - case kPlaying: - ChangeState(kStopPending); - break; - case kPlayPending: - ChangeState(kStopWhilePlayPending); - break; - case kPausePending: - ChangeState(kStopWhilePausePending); - break; - case kFlushPending: - ChangeState(kStopWhileFlushPending); - break; - case kSeekPending: - ChangeState(kStopWhileSeekPending); - break; - default: - SendErrorToHost(PIPELINE_ERROR_INVALID_STATE); - callback->Run(); - return; - } - - callback_.reset(callback.release()); - if (state_ == kStopPending) { - StartSerialCallSequence(); - } -} - -void CompositeFilter::SetPlaybackRate(float playback_rate) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - for (FilterVector::iterator iter = filters_.begin(); - iter != filters_.end(); - ++iter) { - (*iter)->SetPlaybackRate(playback_rate); - } -} - -void CompositeFilter::Seek(base::TimeDelta time, - FilterCallback* seek_callback) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - scoped_ptr<FilterCallback> callback(seek_callback); - if (callback_.get()) { - SendErrorToHost(PIPELINE_ERROR_OPERATION_PENDING); - callback->Run(); - return; - } else if (!host() || (state_ != kPaused && state_ != kCreated)) { - SendErrorToHost(PIPELINE_ERROR_INVALID_STATE); - callback->Run(); - return; - } - - ChangeState(kSeekPending); - callback_.reset(callback.release()); - pending_seek_time_ = time; - StartSerialCallSequence(); -} - -void CompositeFilter::OnAudioRendererDisabled() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - for (FilterVector::iterator iter = filters_.begin(); - iter != filters_.end(); - ++iter) { - (*iter)->OnAudioRendererDisabled(); - } -} - -base::Thread* CompositeFilter::DefaultThreadFactory( - const char* thread_name) { - return new base::Thread(thread_name); -} - -void CompositeFilter::ChangeState(State new_state) { - DCHECK_EQ(message_loop_, MessageLoop::current()); - state_ = new_state; -} - -void CompositeFilter::StartSerialCallSequence() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - error_ = PIPELINE_OK; - - if (filters_.size() > 0) { - sequence_index_ = 0; - CallFilter(filters_[sequence_index_], - NewThreadSafeCallback(&CompositeFilter::SerialCallback)); - } else { - sequence_index_ = 0; - SerialCallback(); - } -} - -void CompositeFilter::StartParallelCallSequence() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - error_ = PIPELINE_OK; - - if (filters_.size() > 0) { - sequence_index_ = 0; - for (size_t i = 0; i < filters_.size(); i++) { - CallFilter(filters_[i], - NewThreadSafeCallback(&CompositeFilter::ParallelCallback)); - } - } else { - sequence_index_ = 0; - ParallelCallback(); - } -} - -void CompositeFilter::CallFilter(scoped_refptr<Filter>& filter, - FilterCallback* callback) { - switch(state_) { - case kPlayPending: - filter->Play(callback); - break; - case kPausePending: - filter->Pause(callback); - break; - case kFlushPending: - filter->Flush(callback); - break; - case kStopPending: - filter->Stop(callback); - break; - case kSeekPending: - filter->Seek(pending_seek_time_, callback); - break; - default: - delete callback; - ChangeState(kError); - HandleError(PIPELINE_ERROR_INVALID_STATE); - } -} - -void CompositeFilter::DispatchPendingCallback() { - if (callback_.get()) { - scoped_ptr<FilterCallback> callback(callback_.release()); - callback->Run(); - } -} - -CompositeFilter::State CompositeFilter::GetNextState(State state) const { - State ret = kInvalid; - switch (state) { - case kPlayPending: - ret = kPlaying; - break; - case kPausePending: - ret = kPaused; - case kFlushPending: - ret = kPaused; - break; - case kStopPending: - ret = kStopped; - break; - case kSeekPending: - ret = kPaused; - break; - case kStopWhilePlayPending: - case kStopWhilePausePending: - case kStopWhileFlushPending: - case kStopWhileSeekPending: - ret = kStopPending; - break; - - case kInvalid: - case kCreated: - case kPlaying: - case kPaused: - case kStopped: - case kError: - ret = kInvalid; - break; - - // default: intentionally left out to catch missing states. - } - - return ret; -} - -void CompositeFilter::SerialCallback() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - if (error_ != PIPELINE_OK) { - // We encountered an error. Terminate the sequence now. - ChangeState(kError); - HandleError(error_); - return; - } - - if (filters_.size() > 0) - sequence_index_++; - - if (sequence_index_ == filters_.size()) { - // All filters have been successfully called without error. - OnCallSequenceDone(); - } else if (GetNextState(state_) == kStopPending) { - // Abort sequence early and start issuing Stop() calls. - ChangeState(kStopPending); - StartSerialCallSequence(); - } else { - // We aren't done with the sequence. Call the next filter. - CallFilter(filters_[sequence_index_], - NewThreadSafeCallback(&CompositeFilter::SerialCallback)); - } -} - -void CompositeFilter::ParallelCallback() { - DCHECK_EQ(message_loop_, MessageLoop::current()); - - if (filters_.size() > 0) - sequence_index_++; - - if (sequence_index_ == filters_.size()) { - if (error_ != PIPELINE_OK) { - // We encountered an error. - ChangeState(kError); - HandleError(error_); - return; - } - - OnCallSequenceDone(); - } -} - -void CompositeFilter::OnCallSequenceDone() { - State next_state = GetNextState(state_); - - if (next_state == kInvalid) { - // We somehow got into an unexpected state. - ChangeState(kError); - HandleError(PIPELINE_ERROR_INVALID_STATE); - } - - ChangeState(next_state); - - if (state_ == kStopPending) { - // Handle a deferred Stop(). - StartSerialCallSequence(); - } else { - // Call the callback to indicate that the operation has completed. - DispatchPendingCallback(); - } -} - -void CompositeFilter::SendErrorToHost(PipelineError error) { - if (host_impl_.get()) - host_impl_.get()->host()->SetError(error); -} - -void CompositeFilter::HandleError(PipelineError error) { - if (error != PIPELINE_OK) { - SendErrorToHost(error); - } - - DispatchPendingCallback(); -} - -FilterCallback* CompositeFilter::NewThreadSafeCallback( - void (CompositeFilter::*method)()) { - return TaskToCallbackAdapter::NewCallback( - NewRunnableMethod(this, - &CompositeFilter::OnCallback, - message_loop_, - method)); -} - -void CompositeFilter::OnCallback(MessageLoop* message_loop, - void (CompositeFilter::*method)()) { - if (MessageLoop::current() != message_loop) { - // Posting callback to the proper thread. - message_loop->PostTask(FROM_HERE, NewRunnableMethod(this, method)); - return; - } - - (this->*method)(); -} - -bool CompositeFilter::CanForwardError() { - return (state_ == kCreated) || (state_ == kPlaying) || (state_ == kPaused); -} - -void CompositeFilter::SetError(PipelineError error) { - // TODO(acolwell): Temporary hack to handle errors that occur - // during filter initialization. In this case we just forward - // the error to the host even if it is on the wrong thread. We - // have to do this because if we defer the call, we can't be - // sure the host will get the error before the "init done" callback - // is executed. This will be cleaned up when filter init is refactored. - if (state_ == kCreated) { - SendErrorToHost(error); - return; - } - - if (message_loop_ != MessageLoop::current()) { - message_loop_->PostTask(FROM_HERE, - NewRunnableMethod(this, &CompositeFilter::SetError, error)); - return; - } - - DCHECK_EQ(message_loop_, MessageLoop::current()); - - // Drop errors recieved while stopping or stopped. - // This shields the owner of this object from having - // to deal with errors it can't do anything about. - if (state_ == kStopPending || state_ == kStopped) - return; - - error_ = error; - if (CanForwardError()) - SendErrorToHost(error); -} - -CompositeFilter::FilterHostImpl::FilterHostImpl(CompositeFilter* parent, - FilterHost* host) : - parent_(parent), - host_(host) { -} - -FilterHost* CompositeFilter::FilterHostImpl::host() { - return host_; -} - -// media::FilterHost methods. -void CompositeFilter::FilterHostImpl::SetError(PipelineError error) { - parent_->SetError(error); -} - -base::TimeDelta CompositeFilter::FilterHostImpl::GetTime() const { - return host_->GetTime(); -} - -base::TimeDelta CompositeFilter::FilterHostImpl::GetDuration() const { - return host_->GetDuration(); -} - -void CompositeFilter::FilterHostImpl::SetTime(base::TimeDelta time) { - host_->SetTime(time); -} - -void CompositeFilter::FilterHostImpl::SetDuration(base::TimeDelta duration) { - host_->SetDuration(duration); -} - -void CompositeFilter::FilterHostImpl::SetBufferedTime( - base::TimeDelta buffered_time) { - host_->SetBufferedTime(buffered_time); -} - -void CompositeFilter::FilterHostImpl::SetTotalBytes(int64 total_bytes) { - host_->SetTotalBytes(total_bytes); -} - -void CompositeFilter::FilterHostImpl::SetBufferedBytes(int64 buffered_bytes) { - host_->SetBufferedBytes(buffered_bytes); -} - -void CompositeFilter::FilterHostImpl::SetVideoSize(size_t width, - size_t height) { - host_->SetVideoSize(width, height); -} - -void CompositeFilter::FilterHostImpl::SetStreaming(bool streaming) { - host_->SetStreaming(streaming); -} - -void CompositeFilter::FilterHostImpl::NotifyEnded() { - host_->NotifyEnded(); -} - -void CompositeFilter::FilterHostImpl::SetLoaded(bool loaded) { - host_->SetLoaded(loaded); -} - -void CompositeFilter::FilterHostImpl::SetNetworkActivity( - bool network_activity) { - host_->SetNetworkActivity(network_activity); -} - -void CompositeFilter::FilterHostImpl::DisableAudioRenderer() { - host_->DisableAudioRenderer(); -} - -void CompositeFilter::FilterHostImpl::SetCurrentReadPosition(int64 offset) { - host_->SetCurrentReadPosition(offset); -} - -int64 CompositeFilter::FilterHostImpl::GetCurrentReadPosition() { - return host_->GetCurrentReadPosition(); -} - -} // namespace media diff --git a/media/base/composite_filter.h b/media/base/composite_filter.h deleted file mode 100644 index f8bcb12..0000000 --- a/media/base/composite_filter.h +++ /dev/null @@ -1,165 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#ifndef MEDIA_BASE_COMPOSITE_FILTER_H_ -#define MEDIA_BASE_COMPOSITE_FILTER_H_ - -#include "base/thread.h" -#include "media/base/filter_host.h" -#include "media/base/filters.h" - -namespace media { - -class CompositeFilter : public Filter { - public: - typedef base::Thread* (*ThreadFactoryFunction)(const char* thread_name); - - CompositeFilter(MessageLoop* message_loop); - - // Constructor that allows the default thread creation strategy to be - // overridden. - CompositeFilter(MessageLoop* message_loop, - ThreadFactoryFunction thread_factory); - - // Adds a filter to the composite. This is only allowed after set_host() - // is called and before the first state changing operation such as Play(), - // Flush(), Stop(), or Seek(). True is returned if the filter was successfully - // added to the composite. False is returned if the filter couldn't be added - // because the composite is in the wrong state or the filter needed a thread - // and the composite was unable to create one. - bool AddFilter(scoped_refptr<Filter> filter); - - // media::Filter methods. - virtual const char* major_mime_type() const; - virtual void set_host(FilterHost* host); - virtual FilterHost* host(); - virtual bool requires_message_loop() const; - virtual const char* message_loop_name() const; - virtual void set_message_loop(MessageLoop* message_loop); - virtual MessageLoop* message_loop(); - virtual void Play(FilterCallback* play_callback); - virtual void Pause(FilterCallback* pause_callback); - virtual void Flush(FilterCallback* flush_callback); - virtual void Stop(FilterCallback* stop_callback); - virtual void SetPlaybackRate(float playback_rate); - virtual void Seek(base::TimeDelta time, FilterCallback* seek_callback); - virtual void OnAudioRendererDisabled(); - - protected: - virtual ~CompositeFilter(); - - /// Default thread factory strategy. - static base::Thread* DefaultThreadFactory(const char* thread_name); - - void SetError(PipelineError error); - - private: - class FilterHostImpl; - - enum State { - kInvalid, - kCreated, - kPaused, - kPlayPending, - kStopWhilePlayPending, - kPlaying, - kPausePending, - kStopWhilePausePending, - kFlushPending, - kStopWhileFlushPending, - kSeekPending, - kStopWhileSeekPending, - kStopPending, - kStopped, - kError - }; - - // Initialization method called by constructors. - void Init(MessageLoop* message_loop, ThreadFactoryFunction thread_factory); - - // Transition to a new state. - void ChangeState(State new_state); - - // Start calling filters in a sequence. - void StartSerialCallSequence(); - - // Call filters in parallel. - void StartParallelCallSequence(); - - // Call the filter based on the current value of state_. - void CallFilter(scoped_refptr<Filter>& filter, FilterCallback* callback); - - // Calls |callback_| and then clears the reference. - void DispatchPendingCallback(); - - // Gets the state to transition to given |state|. - State GetNextState(State state) const; - - // Filter callback for a serial sequence. - void SerialCallback(); - - // Filter callback for a parallel sequence. - void ParallelCallback(); - - // Called when a parallel or serial call sequence completes. - void OnCallSequenceDone(); - - // Helper function for sending an error to the FilterHost. - void SendErrorToHost(PipelineError error); - - // Helper function for handling errors during call sequences. - void HandleError(PipelineError error); - - // Creates a callback that can be called from any thread, but is guaranteed - // to call the specified method on the thread associated with this filter. - FilterCallback* NewThreadSafeCallback(void (CompositeFilter::*method)()); - - // Helper function used by NewThreadSafeCallback() to make sure the - // method gets called on the right thread. - void OnCallback(MessageLoop* message_loop, - void (CompositeFilter::*method)()); - - // Helper function that indicates whether SetError() calls can be forwarded - // to the host of this filter. - bool CanForwardError(); - - // Vector of threads owned by the composite and used by filters in |filters_|. - typedef std::vector<base::Thread*> FilterThreadVector; - FilterThreadVector filter_threads_; - - // Vector of the filters added to the composite. - typedef std::vector<scoped_refptr<Filter> > FilterVector; - FilterVector filters_; - - // Factory function used to create filter threads. - ThreadFactoryFunction thread_factory_; - - // Callback for the pending request. - scoped_ptr<FilterCallback> callback_; - - // Time parameter for the pending Seek() request. - base::TimeDelta pending_seek_time_; - - // Current state of this filter. - State state_; - - // The index of the filter currently processing a request. - unsigned int sequence_index_; - - // Message loop passed into the constructor. - MessageLoop* message_loop_; - - // FilterHost implementation passed to Filters owned by this - // object. - scoped_ptr<FilterHostImpl> host_impl_; - - // Error passed in the last SetError() call. - PipelineError error_; - - DISALLOW_COPY_AND_ASSIGN(CompositeFilter); -}; - -} // namespace media - -#endif // MEDIA_BASE_COMPOSITE_FILTER_H_ diff --git a/media/base/composite_filter_unittest.cc b/media/base/composite_filter_unittest.cc deleted file mode 100644 index 5a529f5..0000000 --- a/media/base/composite_filter_unittest.cc +++ /dev/null @@ -1,809 +0,0 @@ -// Copyright (c) 2010 The Chromium Authors. All rights reserved. -// Use of this source code is governed by a BSD-style license that can be -// found in the LICENSE file. - -#include "media/base/composite_filter.h" -#include "media/base/mock_filter_host.h" -#include "media/base/mock_filters.h" -#include "testing/gtest/include/gtest/gtest.h" - -using ::testing::_; -using ::testing::InSequence; -using ::testing::Return; -using ::testing::SaveArg; -using ::testing::StrictMock; - -namespace media { - -class CompositeFilterTest : public testing::Test { - public: - CompositeFilterTest(); - virtual ~CompositeFilterTest(); - - // Sets up a new CompositeFilter in |composite_|, creates |filter_1_| and - // |filter_2_|, and adds these filters to |composite_|. - void SetupAndAdd2Filters(); - - // Helper enum that indicates what filter method to call. - enum MethodToCall { - PLAY, - PAUSE, - FLUSH, - STOP, - SEEK, - }; - - // Helper method that adds a filter method call expectation based on the value - // of |method_to_call|. - // - // |method_to_call| - Indicates which method we expect a call for. - // |filter| - The MockFilter to add the expectation to. - // |seek_time| - The time to pass to the Seek() call if |method_to_call| - // equals SEEK. - void ExpectFilterCall(MethodToCall method_to_call, MockFilter* filter, - base::TimeDelta seek_time); - - // Helper method that calls a filter method based on the value of - // |method_to_call|. - // - // |method_to_call| - Indicates which method to call. - // |filter| - The Filter to make the method call on. - // |seek_time| - The time to pass to the Seek() call if |method_to_call| - // equals SEEK. - // |callback| - The callback object to pass to the method. - void DoFilterCall(MethodToCall method_to_call, Filter* filter, - base::TimeDelta seek_time, - FilterCallback* callback); - - // Creates an expectation sequence based on the value of method_to_call. - // - // |method_to_call| - Indicates which method we want a success sequence for. - // |seek_time| - The time to pass in the Seek() call if |method_to_call| - // equals SEEK. - void ExpectSuccess(MethodToCall method_to_call, - base::TimeDelta seek_time = base::TimeDelta()); - - // Issue a Play(), Pause(), Flush(), Stop(), or Seek() on the composite and - // verify all the expected calls on the filters. - void DoPlay(); - void DoPause(); - void DoFlush(); - void DoStop(); - void DoSeek(base::TimeDelta time); - - // Issue a Play(), Pause(), Flush(), or Seek() and expect the calls to fail - // with a PIPELINE_ERROR_INVALID_STATE error. - // - // |method_to_call| - Indicates whick method to call. - // |seek_time| - The time to pass to the Seek() call if |method_to_call| - // equals SEEK. - void ExpectInvalidStateFail(MethodToCall method_to_call, - base::TimeDelta seek_time = base::TimeDelta()); - - // Run the callback stored in |filter_1_callback_|. - void RunFilter1Callback(); - - // Run the callback stored in |filter_2_callback_|. - void RunFilter2Callback(); - - protected: - MessageLoop message_loop_; - - // The composite object being tested. - scoped_refptr<CompositeFilter> composite_; - - // First filter added to the composite. - scoped_refptr<StrictMock<MockFilter> > filter_1_; - - // Callback passed to |filter_1_| during last Play(), Pause(), Flush(), - // Stop(), or Seek() call. - FilterCallback* filter_1_callback_; - - // Second filter added to the composite. - scoped_refptr<StrictMock<MockFilter> > filter_2_; - - // Callback passed to |filter_2_| during last Play(), Pause(), Flush(), - // Stop(), or Seek() call. - FilterCallback* filter_2_callback_; - - // FilterHost implementation passed to |composite_| via set_host(). - scoped_ptr<StrictMock<MockFilterHost> > mock_filter_host_; - - DISALLOW_COPY_AND_ASSIGN(CompositeFilterTest); -}; - -CompositeFilterTest::CompositeFilterTest() : - composite_(new CompositeFilter(&message_loop_)), - filter_1_callback_(NULL), - filter_2_callback_(NULL), - mock_filter_host_(new StrictMock<MockFilterHost>()) { -} - -CompositeFilterTest::~CompositeFilterTest() {} - -void CompositeFilterTest::SetupAndAdd2Filters() { - mock_filter_host_.reset(new StrictMock<MockFilterHost>()); - composite_ = new CompositeFilter(&message_loop_); - composite_->set_host(mock_filter_host_.get()); - - // Setup |filter_1_| and arrange for methods to set - // |filter_1_callback_| when they are called. - filter_1_ = new StrictMock<MockFilter>(); - filter_1_callback_ = NULL; - ON_CALL(*filter_1_, Play(_)) - .WillByDefault(SaveArg<0>(&filter_1_callback_)); - ON_CALL(*filter_1_, Pause(_)) - .WillByDefault(SaveArg<0>(&filter_1_callback_)); - ON_CALL(*filter_1_, Flush(_)) - .WillByDefault(SaveArg<0>(&filter_1_callback_)); - ON_CALL(*filter_1_, Stop(_)) - .WillByDefault(SaveArg<0>(&filter_1_callback_)); - ON_CALL(*filter_1_, Seek(_,_)) - .WillByDefault(SaveArg<1>(&filter_1_callback_)); - - // Setup |filter_2_| and arrange for methods to set - // |filter_2_callback_| when they are called. - filter_2_ = new StrictMock<MockFilter>(); - filter_2_callback_ = NULL; - ON_CALL(*filter_2_, Play(_)) - .WillByDefault(SaveArg<0>(&filter_2_callback_)); - ON_CALL(*filter_2_, Pause(_)) - .WillByDefault(SaveArg<0>(&filter_2_callback_)); - ON_CALL(*filter_2_, Flush(_)) - .WillByDefault(SaveArg<0>(&filter_2_callback_)); - ON_CALL(*filter_2_, Stop(_)) - .WillByDefault(SaveArg<0>(&filter_2_callback_)); - ON_CALL(*filter_2_, Seek(_,_)) - .WillByDefault(SaveArg<1>(&filter_2_callback_)); - - composite_->AddFilter(filter_1_); - composite_->AddFilter(filter_2_); -} - -void CompositeFilterTest::ExpectFilterCall(MethodToCall method_to_call, - MockFilter* filter, - base::TimeDelta seek_time) { - switch(method_to_call) { - case PLAY: - EXPECT_CALL(*filter, Play(_)); - break; - case PAUSE: - EXPECT_CALL(*filter, Pause(_)); - break; - case FLUSH: - EXPECT_CALL(*filter, Flush(_)); - break; - case STOP: - EXPECT_CALL(*filter, Stop(_)); - break; - case SEEK: - EXPECT_CALL(*filter, Seek(seek_time, _)); - break; - }; -} - -void CompositeFilterTest::DoFilterCall(MethodToCall method_to_call, - Filter* filter, - base::TimeDelta seek_time, - FilterCallback* callback) { - switch(method_to_call) { - case PLAY: - filter->Play(callback); - break; - case PAUSE: - filter->Pause(callback); - break; - case FLUSH: - filter->Flush(callback); - break; - case STOP: - filter->Stop(callback); - break; - case SEEK: - filter->Seek(seek_time, callback); - break; - }; -} - -void CompositeFilterTest::ExpectSuccess(MethodToCall method_to_call, - base::TimeDelta seek_time) { - InSequence seq; - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - bool is_parallel_call = (method_to_call == FLUSH); - - ExpectFilterCall(method_to_call, filter_1_.get(), seek_time); - - if (is_parallel_call) { - ExpectFilterCall(method_to_call, filter_2_.get(), seek_time); - } - - // Make method call on the composite. - DoFilterCall(method_to_call, composite_.get(), seek_time, - mock_callback->NewCallback()); - - if (is_parallel_call) { - // Make sure both filters have their callbacks set. - EXPECT_TRUE(filter_1_callback_ != NULL); - EXPECT_TRUE(filter_2_callback_ != NULL); - - RunFilter1Callback(); - } else { - // Make sure that only |filter_1_| has its callback set. - EXPECT_TRUE(filter_1_callback_ != NULL); - EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); - - ExpectFilterCall(method_to_call, filter_2_.get(), seek_time); - - RunFilter1Callback(); - - // Verify that |filter_2_| was called by checking the callback pointer. - EXPECT_TRUE(filter_2_callback_ != NULL); - } - - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - RunFilter2Callback(); -} - -void CompositeFilterTest::DoPlay() { - ExpectSuccess(PLAY); -} - -void CompositeFilterTest::DoPause() { - ExpectSuccess(PAUSE); -} - -void CompositeFilterTest::DoFlush() { - ExpectSuccess(FLUSH); -} - -void CompositeFilterTest::DoStop() { - ExpectSuccess(STOP); -} - -void CompositeFilterTest::DoSeek(base::TimeDelta time) { - ExpectSuccess(SEEK, time); -} - -void CompositeFilterTest::ExpectInvalidStateFail(MethodToCall method_to_call, - base::TimeDelta seek_time) { - InSequence seq; - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_INVALID_STATE)) - .WillOnce(Return()); - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - DoFilterCall(method_to_call, composite_, seek_time, - mock_callback->NewCallback()); - - // Make sure that neither of the filters were called by - // verifying that the callback pointers weren't set. - EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); - EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); -} - -void CompositeFilterTest::RunFilter1Callback() { - EXPECT_TRUE(filter_1_callback_ != NULL); - FilterCallback* callback = filter_1_callback_; - filter_1_callback_ = NULL; - callback->Run(); - delete callback; -} - -void CompositeFilterTest::RunFilter2Callback() { - EXPECT_TRUE(filter_2_callback_ != NULL); - FilterCallback* callback = filter_2_callback_; - filter_2_callback_ = NULL; - callback->Run(); - delete callback; -} - -static base::Thread* NullThreadFactory(const char* thread_name) { - return NULL; -} - -// Test AddFilter() failure cases. -TEST_F(CompositeFilterTest, TestAddFilterFailCases) { - // Test adding a null pointer. - EXPECT_FALSE(composite_->AddFilter(NULL)); - - scoped_refptr<StrictMock<MockFilter> > filter = - new StrictMock<MockFilter>(true); - EXPECT_EQ(NULL, filter->host()); - EXPECT_EQ(NULL, filter->message_loop()); - - // Test failing because set_host() hasn't been called yet. - EXPECT_FALSE(composite_->AddFilter(filter)); - - // Test thread creation failure. - composite_ = new CompositeFilter(&message_loop_, &NullThreadFactory); - composite_->set_host(mock_filter_host_.get()); - EXPECT_FALSE(composite_->AddFilter(filter)); - EXPECT_EQ(NULL, filter->host()); - EXPECT_EQ(NULL, filter->message_loop()); -} - -// Test successful AddFilter() cases. -TEST_F(CompositeFilterTest, TestAddFilter) { - composite_->set_host(mock_filter_host_.get()); - - // Add a filter that doesn't require a message loop. - scoped_refptr<StrictMock<MockFilter> > filter = new StrictMock<MockFilter>(); - EXPECT_EQ(NULL, filter->host()); - EXPECT_EQ(NULL, filter->message_loop()); - - EXPECT_TRUE(composite_->AddFilter(filter)); - - EXPECT_TRUE(filter->host() != NULL); - EXPECT_EQ(NULL, filter->message_loop()); - - // Add a filter that requires a message loop. - scoped_refptr<StrictMock<MockFilter> > filter_2 = - new StrictMock<MockFilter>(true); - - EXPECT_EQ(NULL, filter_2->host()); - EXPECT_EQ(NULL, filter_2->message_loop()); - - EXPECT_TRUE(composite_->AddFilter(filter_2)); - - EXPECT_TRUE(filter_2->host() != NULL); - EXPECT_TRUE(filter_2->message_loop() != NULL); -} - -static bool g_thread_cleanup_called_ = false; -class CompositeFilterThread : public base::Thread { - public: - CompositeFilterThread(const char* name) : base::Thread(name) {} - virtual void CleanUp() { - g_thread_cleanup_called_ = true; - base::Thread::CleanUp(); - } -}; - -TEST_F(CompositeFilterTest, TestPlay) { - InSequence sequence; - - SetupAndAdd2Filters(); - - // Verify successful call to Play(). - DoPlay(); - - // At this point we are now in the kPlaying state. - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - // Try calling Play() again to make sure that we simply get a callback. - // We are already in the Play() state so there is no point calling the - // filters. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - composite_->Play(mock_callback->NewCallback()); - - // Verify that neither of the filter callbacks were set. - EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); - EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); - - // Stop playback. - DoStop(); - - // At this point we should be in the kStopped state. - - // Try calling Stop() again to make sure neither filter is called. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - composite_->Stop(mock_callback->NewCallback()); - - // Verify that neither of the filter callbacks were set. - EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); - EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); - - // Try calling Play() again to make sure we get an error. - ExpectInvalidStateFail(PLAY); -} - -// Test errors in the middle of a serial call sequence like Play(). -TEST_F(CompositeFilterTest, TestPlayErrors) { - InSequence sequence; - - SetupAndAdd2Filters(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - EXPECT_CALL(*filter_1_, Play(_)); - - // Call Play() on the composite. - composite_->Play(mock_callback->NewCallback()); - - EXPECT_CALL(*filter_2_, Play(_)); - - // Run callback to indicate that |filter_1_|'s Play() has completed. - RunFilter1Callback(); - - // At this point Play() has been called on |filter_2_|. Simulate an - // error by calling SetError() on its FilterHost interface. - filter_2_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); - - // Expect error to be reported and "play done" callback to be called. - EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - // Run callback to indicate that |filter_2_|'s Play() has completed. - RunFilter2Callback(); - - // Verify that Play/Pause/Flush/Seek fail now that an error occured. - ExpectInvalidStateFail(PLAY); - ExpectInvalidStateFail(PAUSE); - ExpectInvalidStateFail(FLUSH); - ExpectInvalidStateFail(SEEK); - - // Make sure you can still Stop(). - DoStop(); -} - -TEST_F(CompositeFilterTest, TestPause) { - InSequence sequence; - - SetupAndAdd2Filters(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - // Try calling Pause() to make sure we get an error because we aren't in - // the playing state. - ExpectInvalidStateFail(PAUSE); - - // Transition to playing state. - DoPlay(); - - // Issue a successful Pause(). - DoPause(); - - // At this point we are paused. - - // Try calling Pause() again to make sure that the filters aren't called - // because we are already in the paused state. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - composite_->Pause(mock_callback->NewCallback()); - - // Verify that neither of the filter callbacks were set. - EXPECT_EQ((FilterCallback*)NULL, filter_1_callback_); - EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); - - // Verify that we can transition pack to the play state. - DoPlay(); - - // Go back to the pause state. - DoPause(); - - // Transition to the stop state. - DoStop(); - - // Try calling Pause() to make sure we get an error because we aren't in - // the playing state. - ExpectInvalidStateFail(PAUSE); -} - -// Test errors in the middle of a serial call sequence like Pause(). -TEST_F(CompositeFilterTest, TestPauseErrors) { - InSequence sequence; - - SetupAndAdd2Filters(); - - DoPlay(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - EXPECT_CALL(*filter_1_, Pause(_)); - - // Call Pause() on the composite. - composite_->Pause(mock_callback->NewCallback()); - - // Simulate an error by calling SetError() on |filter_1_|'s FilterHost - // interface. - filter_1_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); - - // Expect error to be reported and "pause done" callback to be called. - EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - RunFilter1Callback(); - - // Make sure |filter_2_callback_| was not set. - EXPECT_EQ((FilterCallback*)NULL, filter_2_callback_); - - // Verify that Play/Pause/Flush/Seek fail now that an error occured. - ExpectInvalidStateFail(PLAY); - ExpectInvalidStateFail(PAUSE); - ExpectInvalidStateFail(FLUSH); - ExpectInvalidStateFail(SEEK); - - // Make sure you can still Stop(). - DoStop(); -} - -TEST_F(CompositeFilterTest, TestFlush) { - InSequence sequence; - - SetupAndAdd2Filters(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - // Make sure Flush() works before calling Play(). - DoFlush(); - - // Transition to playing state. - DoPlay(); - - // Call Flush() to make sure we get an error because we are in - // the playing state. - ExpectInvalidStateFail(FLUSH); - - // Issue a successful Pause(). - DoPause(); - - // Make sure Flush() works after pausing. - DoFlush(); - - // Verify that we can transition back to the play state. - DoPlay(); - - // Transition to the stop state. - DoStop(); - - // Try calling Flush() to make sure we get an error because we are stopped. - ExpectInvalidStateFail(FLUSH); -} - -// Test errors in the middle of a parallel call sequence like Flush(). -TEST_F(CompositeFilterTest, TestFlushErrors) { - InSequence sequence; - - SetupAndAdd2Filters(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - EXPECT_CALL(*filter_1_, Flush(_)); - EXPECT_CALL(*filter_2_, Flush(_)); - - // Call Flush() on the composite. - composite_->Flush(mock_callback->NewCallback()); - - // Simulate an error by calling SetError() on |filter_1_|'s FilterHost - // interface. - filter_1_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); - - RunFilter1Callback(); - - // Expect error to be reported and "pause done" callback to be called. - EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - RunFilter2Callback(); - - // Verify that Play/Pause/Flush/Seek fail now that an error occured. - ExpectInvalidStateFail(PLAY); - ExpectInvalidStateFail(PAUSE); - ExpectInvalidStateFail(FLUSH); - ExpectInvalidStateFail(SEEK); - - // Make sure you can still Stop(). - DoStop(); -} - -TEST_F(CompositeFilterTest, TestSeek) { - InSequence sequence; - - SetupAndAdd2Filters(); - - // Verify that seek is allowed to be called before a Play() call. - DoSeek(base::TimeDelta::FromSeconds(5)); - - // Verify we can issue a Play() after the Seek(). - DoPlay(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - // Try calling Seek() while playing to make sure we get an error. - ExpectInvalidStateFail(SEEK); - - // Transition to paused state. - DoPause(); - - // Verify that seek is allowed after pausing. - DoSeek(base::TimeDelta::FromSeconds(5)); - - // Verify we can still play again. - DoPlay(); - - // Stop playback. - DoStop(); - - // Try calling Seek() to make sure we get an error. - ExpectInvalidStateFail(SEEK); -} - -TEST_F(CompositeFilterTest, TestStop) { - InSequence sequence; - - // Test Stop() before any other call. - SetupAndAdd2Filters(); - DoStop(); - - // Test error during Stop() sequence. - SetupAndAdd2Filters(); - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - EXPECT_CALL(*filter_1_, Stop(_)); - - composite_->Stop(mock_callback->NewCallback()); - - // Have |filter_1_| signal an error. - filter_1_->host()->SetError(PIPELINE_ERROR_READ); - - EXPECT_CALL(*filter_2_, Stop(_)); - - RunFilter1Callback(); - - EXPECT_CALL(*mock_callback, OnFilterCallback()); - - RunFilter2Callback(); -} - -// Test stopping in the middle of a serial call sequence. -TEST_F(CompositeFilterTest, TestStopWhilePlayPending) { - InSequence sequence; - - SetupAndAdd2Filters(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>()); - - EXPECT_CALL(*filter_1_, Play(_)); - - composite_->Play(mock_callback->NewCallback()); - - // Note: Play() is pending on |filter_1_| right now. - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback_2( - new StrictMock<MockFilterCallback>(false)); - - EXPECT_CALL(*mock_callback, OnCallbackDestroyed()); - - composite_->Stop(mock_callback_2->NewCallback()); - - EXPECT_CALL(*filter_1_, Stop(_)); - - // Run |filter_1_|'s callback again to indicate Play() has completed. - RunFilter1Callback(); - - EXPECT_CALL(*filter_2_, Stop(_)); - - // Run |filter_1_|'s callback again to indicate Stop() has completed. - RunFilter1Callback(); - - EXPECT_CALL(*mock_callback_2, OnFilterCallback()); - - // Run |filter_2_|'s callback to indicate Stop() has completed. - RunFilter2Callback(); -} - -// Test stopping in the middle of a parallel call sequence. -TEST_F(CompositeFilterTest, TestStopWhileFlushPending) { - InSequence sequence; - - SetupAndAdd2Filters(); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>()); - - EXPECT_CALL(*filter_1_, Flush(_)); - EXPECT_CALL(*filter_2_, Flush(_)); - - composite_->Flush(mock_callback->NewCallback()); - - // Note: |filter_1_| and |filter_2_| have pending Flush() calls at this point. - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback_2( - new StrictMock<MockFilterCallback>(false)); - - EXPECT_CALL(*mock_callback, OnCallbackDestroyed()); - - composite_->Stop(mock_callback_2->NewCallback()); - - // Run callback to indicate that |filter_1_|'s Flush() has completed. - RunFilter1Callback(); - - EXPECT_CALL(*filter_1_, Stop(_)); - - // Run callback to indicate that |filter_2_|'s Flush() has completed. - RunFilter2Callback(); - - EXPECT_CALL(*filter_2_, Stop(_)); - - // Run callback to indicate that |filter_1_|'s Stop() has completed. - RunFilter1Callback(); - - EXPECT_CALL(*mock_callback_2, OnFilterCallback()); - - // Run callback to indicate that |filter_2_|'s Stop() has completed. - RunFilter2Callback(); -} - -TEST_F(CompositeFilterTest, TestErrorWhilePlaying) { - InSequence sequence; - - SetupAndAdd2Filters(); - - // Simulate an error on |filter_2_| while in kCreated state. This - // can happen if an error occurs during filter initialization. - EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); - filter_2_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); - - DoPlay(); - - // Simulate an error on |filter_2_| while playing. - EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_OUT_OF_MEMORY)); - filter_2_->host()->SetError(PIPELINE_ERROR_OUT_OF_MEMORY); - - DoPause(); - - // Simulate an error on |filter_2_| while paused. - EXPECT_CALL(*mock_filter_host_, SetError(PIPELINE_ERROR_NETWORK)); - filter_2_->host()->SetError(PIPELINE_ERROR_NETWORK); - - DoStop(); - - // Verify that errors are not passed to |mock_filter_host_| - // after Stop() has been called. - filter_2_->host()->SetError(PIPELINE_ERROR_NETWORK); -} - -// Make sure that state transitions act as expected even -// if the composite doesn't contain any filters. -TEST_F(CompositeFilterTest, TestEmptyComposite) { - InSequence sequence; - - composite_->set_host(mock_filter_host_.get()); - - scoped_ptr<StrictMock<MockFilterCallback> > mock_callback( - new StrictMock<MockFilterCallback>(false)); - - // Issue a Play() and expect no errors. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - composite_->Play(mock_callback->NewCallback()); - - // Issue a Pause() and expect no errors. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - composite_->Pause(mock_callback->NewCallback()); - - // Issue a Flush() and expect no errors. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - composite_->Flush(mock_callback->NewCallback()); - - // Issue a Seek() and expect no errors. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - composite_->Seek(base::TimeDelta::FromSeconds(5), - mock_callback->NewCallback()); - - // Issue a Play() and expect no errors. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - composite_->Play(mock_callback->NewCallback()); - - // Issue a Stop() and expect no errors. - EXPECT_CALL(*mock_callback, OnFilterCallback()); - composite_->Stop(mock_callback->NewCallback()); -} - -} // namespace media diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc index e2d6ad9..f2110ff 100644 --- a/media/base/mock_filters.cc +++ b/media/base/mock_filters.cc @@ -20,23 +20,4 @@ void RunStopFilterCallback(FilterCallback* callback) { delete callback; } -MockFilter::MockFilter() : - requires_message_loop_(false) { -} - -MockFilter::MockFilter(bool requires_message_loop) : - requires_message_loop_(requires_message_loop) { -} - -MockFilter::~MockFilter() {} - -bool MockFilter::requires_message_loop() const { - return requires_message_loop_; -} - -const char* MockFilter::message_loop_name() const { - return "MockFilter"; -} - - } // namespace media diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h index 36b676f..3b1deb3 100644 --- a/media/base/mock_filters.h +++ b/media/base/mock_filters.h @@ -50,12 +50,7 @@ class Destroyable : public MockClass { // gmock will track the number of times the methods are executed. class MockFilterCallback { public: - MockFilterCallback() : run_destroy_callback_(true) { - } - - MockFilterCallback(bool run_destroy_callback) : - run_destroy_callback_(run_destroy_callback) { - } + MockFilterCallback() {} virtual ~MockFilterCallback() {} MOCK_METHOD0(OnCallbackDestroyed, void()); @@ -66,7 +61,7 @@ class MockFilterCallback { // destroyed. Clients should use NiceMock<> or StrictMock<> depending on the // test. FilterCallback* NewCallback() { - return new CallbackImpl(this, run_destroy_callback_); + return new CallbackImpl(this); } private: @@ -74,15 +69,12 @@ class MockFilterCallback { // MockFilterCallback. class CallbackImpl : public CallbackRunner<Tuple0> { public: - explicit CallbackImpl(MockFilterCallback* mock_callback, - bool run_destroy_callback) - : mock_callback_(mock_callback), - run_destroy_callback_(run_destroy_callback) { + explicit CallbackImpl(MockFilterCallback* mock_callback) + : mock_callback_(mock_callback) { } virtual ~CallbackImpl() { - if (run_destroy_callback_) - mock_callback_->OnCallbackDestroyed(); + mock_callback_->OnCallbackDestroyed(); } virtual void RunWithParams(const Tuple0& params) { @@ -91,41 +83,13 @@ class MockFilterCallback { private: MockFilterCallback* mock_callback_; - bool run_destroy_callback_; DISALLOW_COPY_AND_ASSIGN(CallbackImpl); }; - bool run_destroy_callback_; DISALLOW_COPY_AND_ASSIGN(MockFilterCallback); }; -class MockFilter : public Filter { - public: - MockFilter(); - MockFilter(bool requires_message_loop); - - // Filter implementation. - virtual bool requires_message_loop() const; - virtual const char* message_loop_name() const; - - MOCK_METHOD1(Play, void(FilterCallback* callback)); - MOCK_METHOD1(Pause, void(FilterCallback* callback)); - MOCK_METHOD1(Flush, void(FilterCallback* callback)); - MOCK_METHOD1(Stop, void(FilterCallback* callback)); - MOCK_METHOD1(SetPlaybackRate, void(float playback_rate)); - MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback)); - MOCK_METHOD0(OnAudioRendererDisabled, void()); - - protected: - virtual ~MockFilter(); - - private: - bool requires_message_loop_; - - DISALLOW_COPY_AND_ASSIGN(MockFilter); -}; - class MockDataSource : public DataSource { public: MockDataSource() {} diff --git a/media/base/pipeline.h b/media/base/pipeline.h index 61a1110..50158d2 100644 --- a/media/base/pipeline.h +++ b/media/base/pipeline.h @@ -33,8 +33,6 @@ enum PipelineError { PIPELINE_ERROR_COULD_NOT_RENDER, PIPELINE_ERROR_READ, PIPELINE_ERROR_AUDIO_HARDWARE, - PIPELINE_ERROR_OPERATION_PENDING, - PIPELINE_ERROR_INVALID_STATE, // Demuxer related errors. DEMUXER_ERROR_COULD_NOT_OPEN, DEMUXER_ERROR_COULD_NOT_PARSE, diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc index 3a68374..3b27943 100644 --- a/media/base/pipeline_impl.cc +++ b/media/base/pipeline_impl.cc @@ -22,7 +22,6 @@ class PipelineImpl::PipelineInitState { scoped_refptr<Demuxer> demuxer_; scoped_refptr<AudioDecoder> audio_decoder_; scoped_refptr<VideoDecoder> video_decoder_; - scoped_refptr<CompositeFilter> composite_; }; PipelineImpl::PipelineImpl(MessageLoop* message_loop) @@ -30,6 +29,7 @@ PipelineImpl::PipelineImpl(MessageLoop* message_loop) clock_(new ClockImpl(&base::Time::Now)), waiting_for_clock_update_(false), state_(kCreated), + remaining_transitions_(0), current_bytes_(0) { ResetState(); } @@ -317,10 +317,6 @@ void PipelineImpl::ResetState() { rendered_mime_types_.clear(); } -void PipelineImpl::set_state(State next_state) { - state_ = next_state; -} - bool PipelineImpl::IsPipelineOk() { return PIPELINE_OK == GetError(); } @@ -576,25 +572,22 @@ void PipelineImpl::InitializeTask() { // Just created, create data source. if (state_ == kCreated) { - set_state(kInitDataSource); + state_ = kInitDataSource; pipeline_init_state_.reset(new PipelineInitState()); - pipeline_init_state_->composite_ = new CompositeFilter(message_loop_); - pipeline_init_state_->composite_->set_host(this); - InitializeDataSource(); return; } // Data source created, create demuxer. if (state_ == kInitDataSource) { - set_state(kInitDemuxer); + state_ = kInitDemuxer; InitializeDemuxer(pipeline_init_state_->data_source_); return; } // Demuxer created, create audio decoder. if (state_ == kInitDemuxer) { - set_state(kInitAudioDecoder); + state_ = kInitAudioDecoder; // If this method returns false, then there's no audio stream. if (InitializeAudioDecoder(pipeline_init_state_->demuxer_)) return; @@ -602,7 +595,7 @@ void PipelineImpl::InitializeTask() { // Assuming audio decoder was created, create audio renderer. if (state_ == kInitAudioDecoder) { - set_state(kInitAudioRenderer); + state_ = kInitAudioRenderer; // Returns false if there's no audio stream. if (InitializeAudioRenderer(pipeline_init_state_->audio_decoder_)) { InsertRenderedMimeType(mime_type::kMajorTypeAudio); @@ -613,14 +606,14 @@ void PipelineImpl::InitializeTask() { // Assuming audio renderer was created, create video decoder. if (state_ == kInitAudioRenderer) { // Then perform the stage of initialization, i.e. initialize video decoder. - set_state(kInitVideoDecoder); + state_ = kInitVideoDecoder; if (InitializeVideoDecoder(pipeline_init_state_->demuxer_)) return; } // Assuming video decoder was created, create video renderer. if (state_ == kInitVideoDecoder) { - set_state(kInitVideoRenderer); + state_ = kInitVideoRenderer; if (InitializeVideoRenderer(pipeline_init_state_->video_decoder_)) { InsertRenderedMimeType(mime_type::kMajorTypeVideo); return; @@ -636,8 +629,6 @@ void PipelineImpl::InitializeTask() { // Clear the collection of filters. filter_collection_->Clear(); - pipeline_filter_ = pipeline_init_state_->composite_; - // Clear init state since we're done initializing. pipeline_init_state_.reset(); @@ -646,11 +637,12 @@ void PipelineImpl::InitializeTask() { PlaybackRateChangedTask(GetPlaybackRate()); VolumeChangedTask(GetVolume()); - // Fire the seek request to get the filters to preroll. + // Fire the initial seek request to get the filters to preroll. seek_pending_ = true; - set_state(kSeeking); + state_ = kSeeking; + remaining_transitions_ = filters_.size(); seek_timestamp_ = base::TimeDelta(); - pipeline_filter_->Seek(seek_timestamp_, + filters_.front()->Seek(seek_timestamp_, NewCallback(this, &PipelineImpl::OnFilterStateTransition)); } } @@ -713,7 +705,11 @@ void PipelineImpl::PlaybackRateChangedTask(float playback_rate) { AutoLock auto_lock(lock_); clock_->SetPlaybackRate(playback_rate); } - pipeline_filter_->SetPlaybackRate(playback_rate); + for (FilterVector::iterator iter = filters_.begin(); + iter != filters_.end(); + ++iter) { + (*iter)->SetPlaybackRate(playback_rate); + } } void PipelineImpl::VolumeChangedTask(float volume) { @@ -749,9 +745,10 @@ void PipelineImpl::SeekTask(base::TimeDelta time, // kSeeking (for each filter) // kStarting (for each filter) // kStarted - set_state(kPausing); + state_ = kPausing; seek_timestamp_ = time; seek_callback_.reset(seek_callback); + remaining_transitions_ = filters_.size(); // Kick off seeking! { @@ -760,7 +757,7 @@ void PipelineImpl::SeekTask(base::TimeDelta time, if (!waiting_for_clock_update_) clock_->Pause(); } - pipeline_filter_->Pause( + filters_.front()->Pause( NewCallback(this, &PipelineImpl::OnFilterStateTransition)); } @@ -793,7 +790,7 @@ void PipelineImpl::NotifyEndedTask() { } // Transition to ended, executing the callback if present. - set_state(kEnded); + state_ = kEnded; if (ended_callback_.get()) { ended_callback_->Run(); } @@ -817,7 +814,11 @@ void PipelineImpl::DisableAudioRendererTask() { audio_disabled_ = true; // Notify all filters of disabled audio renderer. - pipeline_filter_->OnAudioRendererDisabled(); + for (FilterVector::iterator iter = filters_.begin(); + iter != filters_.end(); + ++iter) { + (*iter)->OnAudioRendererDisabled(); + } } void PipelineImpl::FilterStateTransitionTask() { @@ -836,31 +837,42 @@ void PipelineImpl::FilterStateTransitionTask() { // Decrement the number of remaining transitions, making sure to transition // to the next state if needed. - set_state(FindNextState(state_)); - if (state_ == kSeeking) { - AutoLock auto_lock(lock_); - clock_->SetTime(seek_timestamp_); + DCHECK(remaining_transitions_ <= filters_.size()); + DCHECK(remaining_transitions_ > 0u); + if (--remaining_transitions_ == 0) { + state_ = FindNextState(state_); + if (state_ == kSeeking) { + AutoLock auto_lock(lock_); + clock_->SetTime(seek_timestamp_); + } + + if (TransientState(state_)) { + remaining_transitions_ = filters_.size(); + } } // Carry out the action for the current state. if (TransientState(state_)) { + Filter* filter = filters_[filters_.size() - remaining_transitions_]; if (state_ == kPausing) { - pipeline_filter_->Pause( - NewCallback(this, &PipelineImpl::OnFilterStateTransition)); + filter->Pause(NewCallback(this, &PipelineImpl::OnFilterStateTransition)); } else if (state_ == kFlushing) { - pipeline_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) { - pipeline_filter_->Seek(seek_timestamp_, + filter->Seek(seek_timestamp_, NewCallback(this, &PipelineImpl::OnFilterStateTransition)); } else if (state_ == kStarting) { - pipeline_filter_->Play( - NewCallback(this,&PipelineImpl::OnFilterStateTransition)); + filter->Play(NewCallback(this, &PipelineImpl::OnFilterStateTransition)); } else if (state_ == kStopping) { - pipeline_filter_->Stop( - NewCallback(this, &PipelineImpl::OnFilterStateTransition)); + filter->Stop(NewCallback(this, &PipelineImpl::OnFilterStateTransition)); } else { - NOTREACHED() << "Unexpected state: " << state_; + NOTREACHED(); } } else if (state_ == kStarted) { FinishInitialization(); @@ -885,7 +897,7 @@ void PipelineImpl::FilterStateTransitionTask() { } else if (IsPipelineStopped()) { FinishDestroyingFiltersTask(); } else { - NOTREACHED() << "Unexpected state: " << state_; + NOTREACHED(); } } @@ -893,11 +905,24 @@ void PipelineImpl::FinishDestroyingFiltersTask() { DCHECK_EQ(MessageLoop::current(), message_loop_); DCHECK(IsPipelineStopped()); + // Stop every running filter thread. + // + // TODO(scherkus): can we watchdog this section to detect wedged threads? + for (FilterThreadVector::iterator iter = filter_threads_.begin(); + iter != filter_threads_.end(); + ++iter) { + (*iter)->Stop(); + } + // Clear renderer references. audio_renderer_ = NULL; video_renderer_ = NULL; - pipeline_filter_ = NULL; + // Reset the pipeline, which will decrement a reference to this object. + // We will get destroyed as soon as the remaining tasks finish executing. + // To be safe, we'll set our pipeline reference to NULL. + filters_.clear(); + STLDeleteElements(&filter_threads_); stop_pending_ = false; tearing_down_ = false; @@ -913,7 +938,7 @@ void PipelineImpl::FinishDestroyingFiltersTask() { } } else { // Destroying filters due to SetError(). - set_state(kError); + state_ = kError; // If our owner has requested to be notified of an error. if (error_callback_.get()) { error_callback_->Run(); @@ -922,12 +947,28 @@ void PipelineImpl::FinishDestroyingFiltersTask() { } bool PipelineImpl::PrepareFilter(scoped_refptr<Filter> filter) { - bool ret = pipeline_init_state_->composite_->AddFilter(filter.get()); + DCHECK_EQ(MessageLoop::current(), message_loop_); + DCHECK(IsPipelineOk()); + + // Create a dedicated thread for this filter if applicable. + if (filter->requires_message_loop()) { + scoped_ptr<base::Thread> thread( + new base::Thread(filter->message_loop_name())); + if (!thread.get() || !thread->Start()) { + NOTREACHED() << "Could not start filter thread"; + SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); + return false; + } - if (!ret) { - SetError(PIPELINE_ERROR_INITIALIZATION_FAILED); + filter->set_message_loop(thread->message_loop()); + filter_threads_.push_back(thread.release()); } - return ret; + + // Register ourselves as the filter's host. + DCHECK(IsPipelineOk()); + filter->set_host(this); + filters_.push_back(make_scoped_refptr(filter.get())); + return true; } void PipelineImpl::InitializeDataSource() { @@ -1104,21 +1145,23 @@ void PipelineImpl::TearDownPipeline() { tearing_down_ = true; if (IsPipelineInitializing()) { - // Make it look like initialization was successful. - pipeline_filter_ = pipeline_init_state_->composite_; - pipeline_init_state_.reset(); - - set_state(kStopping); - pipeline_filter_->Stop(NewCallback( - this, &PipelineImpl::OnFilterStateTransition)); - + // Notify the client that starting did not complete, if necessary. FinishInitialization(); - } else if (pipeline_filter_.get()) { - set_state(kPausing); - pipeline_filter_->Pause(NewCallback( - this, &PipelineImpl::OnFilterStateTransition)); + } + + remaining_transitions_ = filters_.size(); + if (remaining_transitions_ > 0) { + if (IsPipelineInitializing()) { + state_ = kStopping; + filters_.front()->Stop(NewCallback( + this, &PipelineImpl::OnFilterStateTransition)); + } else { + state_ = kPausing; + filters_.front()->Pause(NewCallback( + this, &PipelineImpl::OnFilterStateTransition)); + } } else { - set_state(kStopped); + state_ = kStopped; message_loop_->PostTask(FROM_HERE, NewRunnableMethod(this, &PipelineImpl::FinishDestroyingFiltersTask)); } diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h index fbc2b3d..aa99f73 100644 --- a/media/base/pipeline_impl.h +++ b/media/base/pipeline_impl.h @@ -18,7 +18,6 @@ #include "base/thread.h" #include "base/time.h" #include "media/base/clock.h" -#include "media/base/composite_filter.h" #include "media/base/filter_host.h" #include "media/base/pipeline.h" @@ -120,9 +119,6 @@ class PipelineImpl : public Pipeline, public FilterHost { // is used by the constructor, and the Stop() method. void ResetState(); - // Updates |state_|. All state transitions should use this call. - void set_state(State next_state); - // Simple method used to make sure the pipeline is running normally. bool IsPipelineOk(); @@ -357,6 +353,12 @@ class PipelineImpl : public Pipeline, public FilterHost { // Member that tracks the current state. State state_; + // For kPausing, kSeeking and kStarting, we need to track how many filters + // have completed transitioning to the destination state. When + // |remaining_transitions_| reaches 0 the pipeline can transition out + // of the current state. + size_t remaining_transitions_; + // For kSeeking we need to remember where we're seeking between filter // replies. base::TimeDelta seek_timestamp_; @@ -386,14 +388,20 @@ class PipelineImpl : public Pipeline, public FilterHost { scoped_ptr<PipelineCallback> error_callback_; scoped_ptr<PipelineCallback> network_callback_; - // Reference to the filter(s) that constitute the pipeline. - scoped_refptr<Filter> pipeline_filter_; + // Vector of our filters and map maintaining the relationship between the + // FilterType and the filter itself. + typedef std::vector<scoped_refptr<Filter> > FilterVector; + FilterVector filters_; // Renderer references used for setting the volume and determining // when playback has finished. scoped_refptr<AudioRenderer> audio_renderer_; scoped_refptr<VideoRenderer> video_renderer_; + // Vector of threads owned by the pipeline and being used by filters. + typedef std::vector<base::Thread*> FilterThreadVector; + FilterThreadVector filter_threads_; + // Helper class that stores filter references during pipeline // initialization. class PipelineInitState; diff --git a/media/media.gyp b/media/media.gyp index 0331a77..6fe8e83 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -76,8 +76,6 @@ 'base/clock.h', 'base/clock_impl.cc', 'base/clock_impl.h', - 'base/composite_filter.cc', - 'base/composite_filter.h', 'base/data_buffer.cc', 'base/data_buffer.h', 'base/djb2.cc', @@ -271,7 +269,6 @@ 'audio/mac/audio_output_mac_unittest.cc', 'audio/simple_sources_unittest.cc', 'audio/win/audio_output_win_unittest.cc', - 'base/composite_filter_unittest.cc', 'base/clock_impl_unittest.cc', 'base/data_buffer_unittest.cc', 'base/djb2_unittest.cc', diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc index 005c7cf..4a64b0f 100644 --- a/webkit/glue/webmediaplayer_impl.cc +++ b/webkit/glue/webmediaplayer_impl.cc @@ -737,8 +737,6 @@ void WebMediaPlayerImpl::OnPipelineError() { case media::PIPELINE_ERROR_ABORT: case media::PIPELINE_ERROR_OUT_OF_MEMORY: case media::PIPELINE_ERROR_AUDIO_HARDWARE: - case media::PIPELINE_ERROR_OPERATION_PENDING: - case media::PIPELINE_ERROR_INVALID_STATE: // Decode error. SetNetworkState(WebMediaPlayer::DecodeError); break; |