summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-14 15:56:29 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-03-14 15:56:29 +0000
commitb6c2955681cc0959a9754c02cab67b317fc59e8a (patch)
tree65bd81c3fe1ec4d0bde03510bcad2d4b222e1c21
parent42d2ebcb5bd1b2efc9cd32baf8656c859dff8313 (diff)
downloadchromium_src-b6c2955681cc0959a9754c02cab67b317fc59e8a.zip
chromium_src-b6c2955681cc0959a9754c02cab67b317fc59e8a.tar.gz
chromium_src-b6c2955681cc0959a9754c02cab67b317fc59e8a.tar.bz2
Refactoring code to use factories to create DataSource objects.
BUG=72485 TEST=None for now. Existing unit tests cover this code. Review URL: http://codereview.chromium.org/6480050 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@78033 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/base/async_filter_factory_base.cc112
-rw-r--r--media/base/async_filter_factory_base.h129
-rw-r--r--media/base/composite_data_source_factory.cc108
-rw-r--r--media/base/composite_data_source_factory.h44
-rw-r--r--media/base/filter_collection.cc18
-rw-r--r--media/base/filter_collection.h12
-rw-r--r--media/base/filter_collection_unittest.cc56
-rw-r--r--media/base/filter_factories.cc11
-rw-r--r--media/base/filter_factories.h35
-rw-r--r--media/base/filters.cc4
-rw-r--r--media/base/filters.h6
-rw-r--r--media/base/mock_callback.cc25
-rw-r--r--media/base/mock_callback.h24
-rw-r--r--media/base/mock_filters.cc91
-rw-r--r--media/base/mock_filters.h50
-rw-r--r--media/base/pipeline.h24
-rw-r--r--media/base/pipeline_impl.cc60
-rw-r--r--media/base/pipeline_impl.h2
-rw-r--r--media/base/pipeline_impl_unittest.cc44
-rw-r--r--media/base/pipeline_status.h41
-rw-r--r--media/filters/file_data_source.cc26
-rw-r--r--media/filters/file_data_source.h6
-rw-r--r--media/filters/file_data_source_factory.cc40
-rw-r--r--media/filters/file_data_source_factory.h27
-rw-r--r--media/filters/file_data_source_unittest.cc4
-rw-r--r--media/media.gyp9
-rw-r--r--media/tools/player_wtl/movie.cc8
-rw-r--r--media/tools/player_x11/player_x11.cc4
-rw-r--r--webkit/glue/media/buffered_data_source.cc191
-rw-r--r--webkit/glue/media/buffered_data_source.h23
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc6
-rw-r--r--webkit/glue/media/buffered_resource_loader.cc2
-rw-r--r--webkit/glue/media/simple_data_source.cc238
-rw-r--r--webkit/glue/media/simple_data_source.h20
-rw-r--r--webkit/glue/media/simple_data_source_unittest.cc42
-rw-r--r--webkit/glue/media/web_data_source.h24
-rw-r--r--webkit/glue/media/web_data_source_factory.cc102
-rw-r--r--webkit/glue/media/web_data_source_factory.h51
-rw-r--r--webkit/glue/webkit_glue.gypi2
-rw-r--r--webkit/glue/webmediaplayer_impl.cc39
-rw-r--r--webkit/glue/webmediaplayer_impl.h10
41 files changed, 1397 insertions, 373 deletions
diff --git a/media/base/async_filter_factory_base.cc b/media/base/async_filter_factory_base.cc
new file mode 100644
index 0000000..bbc5160
--- /dev/null
+++ b/media/base/async_filter_factory_base.cc
@@ -0,0 +1,112 @@
+// Copyright (c) 2011 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/async_filter_factory_base.h"
+
+#include "base/logging.h"
+#include "base/stl_util-inl.h"
+
+namespace media {
+
+AsyncDataSourceFactoryBase::AsyncDataSourceFactoryBase() {}
+
+AsyncDataSourceFactoryBase::~AsyncDataSourceFactoryBase() {
+ base::AutoLock auto_lock(lock_);
+ STLDeleteElements(&outstanding_requests_);
+}
+
+void AsyncDataSourceFactoryBase::Build(const std::string& url,
+ BuildCallback* callback) {
+ DCHECK(callback);
+ BuildRequest* request = NULL;
+ {
+ base::AutoLock auto_lock(lock_);
+
+ if (url.empty()) {
+ RunAndDestroyCallback(PIPELINE_ERROR_URL_NOT_FOUND, callback);
+ return;
+ }
+
+ if (!AllowRequests()) {
+ RunAndDestroyCallback(DATASOURCE_ERROR_URL_NOT_SUPPORTED, callback);
+ return;
+ }
+
+ request = CreateRequest(url, callback);
+
+ if (!request) {
+ RunAndDestroyCallback(DATASOURCE_ERROR_URL_NOT_SUPPORTED, callback);
+ return;
+ }
+
+ outstanding_requests_.insert(request);
+ }
+
+ request->Start(NewCallback(this,
+ &AsyncDataSourceFactoryBase::BuildRequestDone));
+}
+
+void AsyncDataSourceFactoryBase::RunAndDestroyCallback(
+ PipelineError error,
+ BuildCallback* callback) const {
+ DCHECK_NE(error, PIPELINE_OK);
+ DCHECK(callback);
+
+ callback->Run(error, static_cast<DataSource*>(NULL));
+ delete callback;
+}
+
+void AsyncDataSourceFactoryBase::BuildRequestDone(BuildRequest* request) {
+ base::AutoLock auto_lock(lock_);
+ outstanding_requests_.erase(request);
+ delete request;
+}
+
+AsyncDataSourceFactoryBase::BuildRequest::BuildRequest(const std::string& url,
+ BuildCallback* callback)
+ : url_(url),
+ callback_(callback) {
+}
+
+AsyncDataSourceFactoryBase::BuildRequest::~BuildRequest() {}
+
+void AsyncDataSourceFactoryBase::BuildRequest::Start(
+ RequestDoneCallback* done_callback) {
+ DCHECK(done_callback);
+ DCHECK(!done_callback_.get());
+
+ done_callback_.reset(done_callback);
+ DoStart();
+ // Don't do anything after this line since the object could
+ // have been deleted at this point if the request was completed
+ // inside the call.
+}
+
+void AsyncDataSourceFactoryBase::BuildRequest::RequestComplete(
+ PipelineError error,
+ DataSource* data_source) {
+ DCHECK(callback_.get());
+ DCHECK(done_callback_.get());
+
+ // Transfer ownership to local variables just in case the
+ // request object gets deleted by one of the callbacks.
+ scoped_ptr<RequestDoneCallback> done_callback(done_callback_.release());
+ scoped_ptr<BuildCallback> callback(callback_.release());
+
+ // Notify factory that this request has completed. We do this before
+ // calling |callback| so the factory doesn't consider this request
+ // pending if |callback| happens to destroy the factory.
+ //
+ // NOTE: This BuildRequest object is destroyed inside this callback so
+ // no modifications should be made to this object after this call.
+ done_callback->Run(this);
+
+ callback->Run(error, data_source);
+}
+
+const std::string& AsyncDataSourceFactoryBase::BuildRequest::url() const {
+ return url_;
+}
+
+} // namespace media
diff --git a/media/base/async_filter_factory_base.h b/media/base/async_filter_factory_base.h
new file mode 100644
index 0000000..75617f4
--- /dev/null
+++ b/media/base/async_filter_factory_base.h
@@ -0,0 +1,129 @@
+// Copyright (c) 2011 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_ASYNC_FILTER_FACTORY_BASE_H_
+#define MEDIA_BASE_ASYNC_FILTER_FACTORY_BASE_H_
+
+#include <set>
+
+#include "base/scoped_ptr.h"
+#include "base/synchronization/lock.h"
+#include "media/base/filter_factories.h"
+
+namespace media {
+
+// This is a helper base class for DataSourceFactory implementation that
+// actually require asynchronous operations to build a data source. It
+// provides a common framework for dealing with an asychronous build.
+// Factories are expected to derive from this object and implement
+// AllowRequests(), CreateRequest(), and an custom implementation that derives
+// from BuildRequest.
+//
+// AllowRequests() just checks to see if the factory is in a state where it can
+// accept Build() requests. If it returns false, this base class will signal an
+// error through the BuildCallback and not call any of the other code. If
+// AllowRequests() returns true then this class will continue with the build
+// process by calling CreateRequest().
+//
+// CreateRequest() allows the derived implementation to create an instance of
+// their custom BuildRequest implementation that is specific to its asynchronous
+// build process. The custom BuildRequest should contain all of the state that
+// needs to be maintained for a particular build request. This state could
+// consist of partially initialized objects that require asynchronous operations
+// to complete before the build completes. This implementation MUST derive from
+// AsyncDataSourceFactoryBase::BuildRequest.
+//
+// Once CreateRequest() returns a BuildRequest implementation, this class adds
+// the object to its request list and then calls Start() on the BuildRequest
+// instance. BuildRequest::Start() manages storing the |done_callback| passed to
+// it and then call DoStart() on the derived object. DoStart() is where this
+// framework expects any neccesary asynchronous operations to be initiated.
+//
+// Once all asynchronous operations are completed and a fully initialized
+// DataSource object has been created, the BuildRequest instance should call
+// the RequestComplete() method. This call signals the end of the request and
+// the BuildRequest should be in a state where it can be deleted from inside
+// this call. If an error occurs during the build process, RequestComplete()
+// can also be called to signal the error.
+class AsyncDataSourceFactoryBase : public DataSourceFactory {
+ public:
+ AsyncDataSourceFactoryBase();
+ virtual ~AsyncDataSourceFactoryBase();
+
+ // DataSourceFactory method.
+ // Derived classes should not overload this Build() method. AllowRequests() &
+ // CreateRequest() should be implemented instead.
+ virtual void Build(const std::string& url, BuildCallback* callback);
+
+ // DataSourceFactory method.
+ // Clone() must be implemented by derived classes.
+ // NOTE: Nothing in this base class needs to be cloned because this class
+ // only keeps track of pending requests, which are not part of the cloning
+ // process.
+ virtual DataSourceFactory* Clone() const = 0;
+
+ protected:
+ class BuildRequest {
+ public:
+ BuildRequest(const std::string& url, BuildCallback* callback);
+ virtual ~BuildRequest();
+
+ typedef Callback1<BuildRequest*>::Type RequestDoneCallback;
+ // Starts the build request.
+ void Start(RequestDoneCallback* done_callback);
+
+ // Derived objects call this method to indicate that the build request
+ // has completed. If the build was successful |error| should be set to
+ // PIPELINE_OK and |data_source| should contain the DataSource object
+ // that was built by this request. Ownership of |data_source| is being
+ // passed in this call. If an error occurs during the build process, this
+ // method should be called with |error| set to an appropriate status code
+ // and |data_source| set to NULL.
+ //
+ // The derived object should be in a state where it can be deleted from
+ // within this call. This class as well AsyncDataSourceFactoryBase use this
+ // method to cleanup state associated with this request.
+ void RequestComplete(media::PipelineError error, DataSource* data_source);
+
+ protected:
+ // Implemented by the derived object to start the build. Called by Start().
+ virtual void DoStart() = 0;
+
+ // Gets the requested URL.
+ const std::string& url() const;
+
+ private:
+ std::string url_;
+ scoped_ptr<BuildCallback> callback_;
+ scoped_ptr<RequestDoneCallback> done_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuildRequest);
+ };
+
+ // Implemented by derived class. Called by Build() to check if the
+ // factory is in a state where it can accept requests.
+ virtual bool AllowRequests() const = 0;
+
+ // Implemented by derived class. Called by Build() to allow derived objects
+ // to create their own custom BuildRequest implementations.
+ virtual BuildRequest* CreateRequest(const std::string& url,
+ BuildCallback* callback) = 0;
+
+ private:
+ void RunAndDestroyCallback(PipelineError error,
+ BuildCallback* callback) const;
+
+ typedef Callback1<BuildRequest*>::Type RequestDoneCallback;
+ void BuildRequestDone(BuildRequest* request);
+
+ base::Lock lock_;
+ typedef std::set<BuildRequest*> RequestSet;
+ RequestSet outstanding_requests_;
+
+ DISALLOW_COPY_AND_ASSIGN(AsyncDataSourceFactoryBase);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_ASYNC_FILTER_FACTORY_BASE_H_
diff --git a/media/base/composite_data_source_factory.cc b/media/base/composite_data_source_factory.cc
new file mode 100644
index 0000000..84b5259
--- /dev/null
+++ b/media/base/composite_data_source_factory.cc
@@ -0,0 +1,108 @@
+// Copyright (c) 2011 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_data_source_factory.h"
+
+#include "base/callback.h"
+#include "base/logging.h"
+#include "base/scoped_ptr.h"
+#include "base/stl_util-inl.h"
+
+namespace media {
+
+class CompositeDataSourceFactory::BuildRequest
+ : public AsyncDataSourceFactoryBase::BuildRequest {
+ public:
+ BuildRequest(const std::string& url, BuildCallback* callback,
+ const FactoryList& factories);
+ ~BuildRequest();
+
+ protected:
+ // AsyncDataSourceFactoryBase::BuildRequest method.
+ virtual void DoStart();
+
+ private:
+ void CallNextFactory();
+ void OnBuildDone(PipelineError error, DataSource* data_source);
+
+ FactoryList factories_;
+};
+
+CompositeDataSourceFactory::CompositeDataSourceFactory() {}
+
+CompositeDataSourceFactory::~CompositeDataSourceFactory() {
+ STLDeleteElements(&factories_);
+}
+
+void CompositeDataSourceFactory::AddFactory(DataSourceFactory* factory) {
+ DCHECK(factory);
+ factories_.push_back(factory);
+}
+
+DataSourceFactory* CompositeDataSourceFactory::Clone() const {
+ CompositeDataSourceFactory* new_factory = new CompositeDataSourceFactory();
+
+ for (FactoryList::const_iterator itr = factories_.begin();
+ itr != factories_.end();
+ ++itr) {
+ new_factory->AddFactory((*itr)->Clone());
+ }
+
+ return new_factory;
+}
+
+bool CompositeDataSourceFactory::AllowRequests() const {
+ return !factories_.empty();
+}
+
+AsyncDataSourceFactoryBase::BuildRequest*
+CompositeDataSourceFactory::CreateRequest(const std::string& url,
+ BuildCallback* callback) {
+ return new BuildRequest(url, callback, factories_);
+}
+
+CompositeDataSourceFactory::BuildRequest::BuildRequest(
+ const std::string& url,
+ BuildCallback* callback,
+ const FactoryList& factories)
+ : AsyncDataSourceFactoryBase::BuildRequest(url, callback),
+ factories_(factories){
+ DCHECK(!factories.empty());
+}
+
+CompositeDataSourceFactory::BuildRequest::~BuildRequest() {}
+
+void CompositeDataSourceFactory::BuildRequest::DoStart() {
+ CallNextFactory();
+}
+
+void CompositeDataSourceFactory::BuildRequest::CallNextFactory() {
+ DCHECK(!factories_.empty());
+
+ DataSourceFactory* factory = factories_.front();
+ factories_.pop_front();
+
+ factory->Build(url(), NewCallback(this, &BuildRequest::OnBuildDone));
+}
+
+void CompositeDataSourceFactory::BuildRequest::OnBuildDone(
+ PipelineError error,
+ DataSource* data_source) {
+
+ if (error == PIPELINE_OK) {
+ DCHECK(data_source);
+ RequestComplete(error, data_source);
+ return;
+ }
+
+ DCHECK(!data_source);
+ if ((error == DATASOURCE_ERROR_URL_NOT_SUPPORTED) && !factories_.empty()) {
+ CallNextFactory();
+ return;
+ }
+
+ RequestComplete(error, data_source);
+}
+
+} // namespace media
diff --git a/media/base/composite_data_source_factory.h b/media/base/composite_data_source_factory.h
new file mode 100644
index 0000000..799960d
--- /dev/null
+++ b/media/base/composite_data_source_factory.h
@@ -0,0 +1,44 @@
+// Copyright (c) 2011 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_DATA_SOURCE_FACTORY_H_
+#define MEDIA_BASE_COMPOSITE_DATA_SOURCE_FACTORY_H_
+
+#include <list>
+#include <set>
+
+#include "base/synchronization/lock.h"
+#include "media/base/async_filter_factory_base.h"
+
+namespace media {
+
+class CompositeDataSourceFactory : public AsyncDataSourceFactoryBase {
+ public:
+ CompositeDataSourceFactory();
+ virtual ~CompositeDataSourceFactory();
+
+ // Add factory to this composite. Ownership is transferred here.
+ void AddFactory(DataSourceFactory* factory);
+
+ // DataSourceFactory method.
+ virtual DataSourceFactory* Clone() const;
+
+ protected:
+ // AsyncDataSourceFactoryBase methods.
+ virtual bool AllowRequests() const;
+ virtual AsyncDataSourceFactoryBase::BuildRequest* CreateRequest(
+ const std::string& url, BuildCallback* callback);
+
+ private:
+ class BuildRequest;
+
+ typedef std::list<DataSourceFactory*> FactoryList;
+ FactoryList factories_;
+
+ DISALLOW_COPY_AND_ASSIGN(CompositeDataSourceFactory);
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_COMPOSITE_DATA_SOURCE_FACTORY_H_
diff --git a/media/base/filter_collection.cc b/media/base/filter_collection.cc
index b236b02..c25fa7a 100644
--- a/media/base/filter_collection.cc
+++ b/media/base/filter_collection.cc
@@ -1,17 +1,24 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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/filter_collection.h"
+#include "base/logging.h"
+
namespace media {
FilterCollection::FilterCollection() {}
FilterCollection::~FilterCollection() {}
-void FilterCollection::AddDataSource(DataSource* filter) {
- AddFilter(DATA_SOURCE, filter);
+void FilterCollection::SetDataSourceFactory(DataSourceFactory* factory) {
+ DCHECK(factory);
+ data_source_factory_.reset(factory);
+}
+
+DataSourceFactory* FilterCollection::GetDataSourceFactory() {
+ return data_source_factory_.get();
}
void FilterCollection::AddDemuxer(Demuxer* filter) {
@@ -42,11 +49,6 @@ void FilterCollection::Clear() {
filters_.clear();
}
-void FilterCollection::SelectDataSource(
- scoped_refptr<DataSource>* filter_out) {
- SelectFilter<DATA_SOURCE>(filter_out);
-}
-
void FilterCollection::SelectDemuxer(scoped_refptr<Demuxer>* filter_out) {
SelectFilter<DEMUXER>(filter_out);
}
diff --git a/media/base/filter_collection.h b/media/base/filter_collection.h
index 8dd5b04..6fc445d 100644
--- a/media/base/filter_collection.h
+++ b/media/base/filter_collection.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -9,6 +9,7 @@
#include "base/ref_counted.h"
#include "media/base/filters.h"
+#include "media/base/filter_factories.h"
namespace media {
@@ -19,8 +20,12 @@ class FilterCollection {
FilterCollection();
~FilterCollection();
+ // DataSourceFactory accessor methods.
+ // FilterCollection takes ownership of the factory here.
+ void SetDataSourceFactory(DataSourceFactory* factory);
+ DataSourceFactory* GetDataSourceFactory();
+
// Adds a filter to the collection.
- void AddDataSource(DataSource* filter);
void AddDemuxer(Demuxer* filter);
void AddVideoDecoder(VideoDecoder* filter);
void AddAudioDecoder(AudioDecoder* filter);
@@ -36,7 +41,6 @@ class FilterCollection {
// Selects a filter of the specified type from the collection.
// If the required filter cannot be found, NULL is returned.
// If a filter is returned it is removed from the collection.
- void SelectDataSource(scoped_refptr<DataSource>* filter_out);
void SelectDemuxer(scoped_refptr<Demuxer>* filter_out);
void SelectVideoDecoder(scoped_refptr<VideoDecoder>* filter_out);
void SelectAudioDecoder(scoped_refptr<AudioDecoder>* filter_out);
@@ -48,7 +52,6 @@ class FilterCollection {
// the following types. This is used to mark, identify, and support
// downcasting of different filter types stored in the filters_ list.
enum FilterType {
- DATA_SOURCE,
DEMUXER,
AUDIO_DECODER,
VIDEO_DECODER,
@@ -60,6 +63,7 @@ class FilterCollection {
typedef std::pair<FilterType, scoped_refptr<Filter> > FilterListElement;
typedef std::list<FilterListElement> FilterList;
FilterList filters_;
+ scoped_ptr<DataSourceFactory> data_source_factory_;
// Helper function that adds a filter to the filter list.
void AddFilter(FilterType filter_type, Filter* filter);
diff --git a/media/base/filter_collection_unittest.cc b/media/base/filter_collection_unittest.cc
index fef7e7f7..70624a1 100644
--- a/media/base/filter_collection_unittest.cc
+++ b/media/base/filter_collection_unittest.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -23,7 +23,7 @@ class FilterCollectionTest : public ::testing::Test {
TEST_F(FilterCollectionTest, TestIsEmptyAndClear) {
EXPECT_TRUE(collection_.IsEmpty());
- collection_.AddDataSource(mock_filters_.data_source());
+ collection_.AddAudioDecoder(mock_filters_.audio_decoder());
EXPECT_FALSE(collection_.IsEmpty());
@@ -34,25 +34,25 @@ TEST_F(FilterCollectionTest, TestIsEmptyAndClear) {
TEST_F(FilterCollectionTest, SelectXXXMethods) {
scoped_refptr<AudioDecoder> audio_decoder;
- scoped_refptr<DataSource> data_source;
+ scoped_refptr<VideoDecoder> video_decoder;
- collection_.AddDataSource(mock_filters_.data_source());
+ collection_.AddVideoDecoder(mock_filters_.video_decoder());
EXPECT_FALSE(collection_.IsEmpty());
- // Verify that the data source will not be returned if we
+ // Verify that the video decoder will not be returned if we
// ask for a different type.
collection_.SelectAudioDecoder(&audio_decoder);
EXPECT_FALSE(audio_decoder);
EXPECT_FALSE(collection_.IsEmpty());
- // Verify that we can actually retrieve the data source
+ // Verify that we can actually retrieve the video decoder
// and that it is removed from the collection.
- collection_.SelectDataSource(&data_source);
- EXPECT_TRUE(data_source);
+ collection_.SelectVideoDecoder(&video_decoder);
+ EXPECT_TRUE(video_decoder);
EXPECT_TRUE(collection_.IsEmpty());
- // Add a data source and audio decoder.
- collection_.AddDataSource(mock_filters_.data_source());
+ // Add a video decoder and audio decoder.
+ collection_.AddVideoDecoder(mock_filters_.video_decoder());
collection_.AddAudioDecoder(mock_filters_.audio_decoder());
// Verify that we can select the audio decoder.
@@ -64,36 +64,36 @@ TEST_F(FilterCollectionTest, SelectXXXMethods) {
collection_.SelectAudioDecoder(&audio_decoder);
EXPECT_FALSE(audio_decoder);
- // Verify that we can select the data source and that doing so will
+ // Verify that we can select the video decoder and that doing so will
// empty the collection again.
- collection_.SelectDataSource(&data_source);
+ collection_.SelectVideoDecoder(&video_decoder);
EXPECT_TRUE(collection_.IsEmpty());
}
TEST_F(FilterCollectionTest, MultipleFiltersOfSameType) {
- scoped_refptr<DataSource> data_source_a(new MockDataSource());
- scoped_refptr<DataSource> data_source_b(new MockDataSource());
+ scoped_refptr<AudioDecoder> audio_decoder_a(new MockAudioDecoder());
+ scoped_refptr<AudioDecoder> audio_decoder_b(new MockAudioDecoder());
- scoped_refptr<DataSource> data_source;
+ scoped_refptr<AudioDecoder> audio_decoder;
- collection_.AddDataSource(data_source_a.get());
- collection_.AddDataSource(data_source_b.get());
+ collection_.AddAudioDecoder(audio_decoder_a.get());
+ collection_.AddAudioDecoder(audio_decoder_b.get());
- // Verify that first SelectDataSource() returns data_source_a.
- collection_.SelectDataSource(&data_source);
- EXPECT_TRUE(data_source);
- EXPECT_EQ(data_source, data_source_a);
+ // Verify that first SelectAudioDecoder() returns audio_decoder_a.
+ collection_.SelectAudioDecoder(&audio_decoder);
+ EXPECT_TRUE(audio_decoder);
+ EXPECT_EQ(audio_decoder, audio_decoder_a);
EXPECT_FALSE(collection_.IsEmpty());
- // Verify that second SelectDataSource() returns data_source_b.
- collection_.SelectDataSource(&data_source);
- EXPECT_TRUE(data_source);
- EXPECT_EQ(data_source, data_source_b);
+ // Verify that second SelectAudioDecoder() returns audio_decoder_b.
+ collection_.SelectAudioDecoder(&audio_decoder);
+ EXPECT_TRUE(audio_decoder);
+ EXPECT_EQ(audio_decoder, audio_decoder_b);
EXPECT_TRUE(collection_.IsEmpty());
- // Verify that third SelectDataSource() returns nothing.
- collection_.SelectDataSource(&data_source);
- EXPECT_FALSE(data_source);
+ // Verify that third SelectAudioDecoder() returns nothing.
+ collection_.SelectAudioDecoder(&audio_decoder);
+ EXPECT_FALSE(audio_decoder);
}
} // namespace media
diff --git a/media/base/filter_factories.cc b/media/base/filter_factories.cc
new file mode 100644
index 0000000..bb5df5e
--- /dev/null
+++ b/media/base/filter_factories.cc
@@ -0,0 +1,11 @@
+// Copyright (c) 2011 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/filter_factories.h"
+
+namespace media {
+
+DataSourceFactory::~DataSourceFactory() {}
+
+} // namespace media
diff --git a/media/base/filter_factories.h b/media/base/filter_factories.h
new file mode 100644
index 0000000..c00adef
--- /dev/null
+++ b/media/base/filter_factories.h
@@ -0,0 +1,35 @@
+// Copyright (c) 2011 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_FILTER_FACTORIES_H_
+#define MEDIA_BASE_FILTER_FACTORIES_H_
+
+#include<string>
+
+#include "base/callback.h"
+#include "media/base/pipeline_status.h"
+
+namespace media {
+
+class DataSource;
+
+// Asynchronous factory interface for building DataSource objects.
+class DataSourceFactory {
+ public:
+ // Ownership of the DataSource is transferred through this callback.
+ typedef Callback2<PipelineError, DataSource*>::Type BuildCallback;
+
+ virtual ~DataSourceFactory();
+
+ // Builds a DataSource for |url| and returns it via |callback|.
+ virtual void Build(const std::string& url, BuildCallback* callback) = 0;
+
+ // Makes a copy of this factory.
+ // NOTE: Pending requests are not cloned.
+ virtual DataSourceFactory* Clone() const = 0;
+};
+
+} // namespace media
+
+#endif // MEDIA_BASE_FILTER_FACTORIES_H_
diff --git a/media/base/filters.cc b/media/base/filters.cc
index 1328dd1..fc5f537 100644
--- a/media/base/filters.cc
+++ b/media/base/filters.cc
@@ -66,10 +66,6 @@ void Filter::Seek(base::TimeDelta time, FilterCallback* callback) {
void Filter::OnAudioRendererDisabled() {
}
-bool DataSource::IsUrlSupported(const std::string& url) {
- return true;
-}
-
void* DemuxerStream::QueryInterface(const char* interface_id) {
return NULL;
}
diff --git a/media/base/filters.h b/media/base/filters.h
index 11bf526..89a7584 100644
--- a/media/base/filters.h
+++ b/media/base/filters.h
@@ -112,12 +112,6 @@ class DataSource : public Filter {
typedef Callback1<size_t>::Type ReadCallback;
static const size_t kReadError = static_cast<size_t>(-1);
- virtual bool IsUrlSupported(const std::string& url);
-
- // Initialize a DataSource for the given URL, executing the callback upon
- // completion.
- virtual void Initialize(const std::string& url, FilterCallback* callback) = 0;
-
// Reads |size| bytes from |position| into |data|. And when the read is done
// or failed, |read_callback| is called with the number of bytes read or
// kReadError in case of error.
diff --git a/media/base/mock_callback.cc b/media/base/mock_callback.cc
index e948368..03ed896 100644
--- a/media/base/mock_callback.cc
+++ b/media/base/mock_callback.cc
@@ -20,10 +20,35 @@ void MockCallback::ExpectRunAndDelete() {
EXPECT_CALL(*this, Destructor());
}
+MockStatusCallback::MockStatusCallback() {}
+
+MockStatusCallback::~MockStatusCallback() {
+ Destructor();
+}
+
+// Required by GMock to allow the RunWithParams() expectation
+// in ExpectRunAndDelete() to compile.
+bool operator==(const Tuple1<PipelineError>& lhs,
+ const Tuple1<PipelineError>& rhs) {
+ return lhs.a == rhs.a;
+}
+
+void MockStatusCallback::ExpectRunAndDelete(PipelineError error) {
+ EXPECT_CALL(*this, RunWithParams(Tuple1<PipelineError>(error)));
+ EXPECT_CALL(*this, Destructor());
+}
+
MockCallback* NewExpectedCallback() {
StrictMock<MockCallback>* callback = new StrictMock<MockCallback>();
callback->ExpectRunAndDelete();
return callback;
}
+MockStatusCallback* NewExpectedStatusCallback(PipelineError error) {
+ StrictMock<MockStatusCallback>* callback =
+ new StrictMock<MockStatusCallback>();
+ callback->ExpectRunAndDelete(error);
+ return callback;
+}
+
} // namespace media
diff --git a/media/base/mock_callback.h b/media/base/mock_callback.h
index 2aef84a..ddf1bef 100644
--- a/media/base/mock_callback.h
+++ b/media/base/mock_callback.h
@@ -6,6 +6,7 @@
#define MEDIA_BASE_MOCK_CALLBACK_H_
#include "base/callback.h"
+#include "media/base/pipeline_status.h"
#include "testing/gmock/include/gmock/gmock.h"
namespace media {
@@ -48,9 +49,30 @@ class MockCallback : public CallbackRunner<Tuple0> {
DISALLOW_COPY_AND_ASSIGN(MockCallback);
};
-// Convenience function that automatically creates and sets an expectation for
+// Helper class similar to MockCallback but is used where a
+// PipelineStatusCallback is needed.
+class MockStatusCallback : public CallbackRunner<Tuple1<PipelineError> > {
+ public:
+ MockStatusCallback();
+ virtual ~MockStatusCallback();
+
+ MOCK_METHOD1(RunWithParams, void(const Tuple1<PipelineError>& params));
+
+ // Can be used to verify the object is destroyed.
+ MOCK_METHOD0(Destructor, void());
+
+ // Convenience function to set expectations for the callback to execute and
+ // deleted.
+ void ExpectRunAndDelete(PipelineError error);
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockStatusCallback);
+};
+
+// Convenience functions that automatically create and set an expectation for
// the callback to run.
MockCallback* NewExpectedCallback();
+MockStatusCallback* NewExpectedStatusCallback(PipelineError error);
} // namespace media
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
index 1f8a78b..68e344f 100644
--- a/media/base/mock_filters.cc
+++ b/media/base/mock_filters.cc
@@ -4,12 +4,78 @@
#include "media/base/mock_filters.h"
+#include "media/base/filter_host.h"
+
+using ::testing::_;
+using ::testing::Invoke;
+using ::testing::NotNull;
+
namespace media {
-MockDataSource::MockDataSource() {}
+MockDataSource::MockDataSource()
+ : total_bytes_(-1),
+ buffered_bytes_(-1) {
+}
MockDataSource::~MockDataSource() {}
+void MockDataSource::set_host(FilterHost* filter_host) {
+ Filter::set_host(filter_host);
+
+ if (total_bytes_ > 0)
+ host()->SetTotalBytes(total_bytes_);
+
+ if (buffered_bytes_ > 0)
+ host()->SetBufferedBytes(buffered_bytes_);
+}
+
+void MockDataSource::SetTotalAndBufferedBytes(int64 total_bytes,
+ int64 buffered_bytes) {
+ total_bytes_ = total_bytes;
+ buffered_bytes_ = buffered_bytes;
+}
+
+MockDataSourceFactory::MockDataSourceFactory(MockDataSource* data_source)
+ : data_source_(data_source),
+ error_(PIPELINE_OK) {
+}
+
+MockDataSourceFactory::~MockDataSourceFactory() {}
+
+void MockDataSourceFactory::SetError(PipelineError error) {
+ error_ = error;
+}
+
+void MockDataSourceFactory::RunBuildCallback(const std::string& url,
+ BuildCallback* callback) {
+ scoped_ptr<BuildCallback> cb(callback);
+
+ if (!data_source_.get()) {
+ cb->Run(PIPELINE_ERROR_REQUIRED_FILTER_MISSING,
+ static_cast<DataSource*>(NULL));
+ return;
+ }
+
+ scoped_refptr<MockDataSource> data_source = data_source_;
+ data_source_ = NULL;
+
+ if (error_ == PIPELINE_OK) {
+ cb->Run(PIPELINE_OK, data_source.get());
+ return;
+ }
+
+ cb->Run(error_, static_cast<DataSource*>(NULL));
+}
+
+void MockDataSourceFactory::DestroyBuildCallback(const std::string& url,
+ BuildCallback* callback) {
+ delete callback;
+}
+
+DataSourceFactory* MockDataSourceFactory::Clone() const {
+ return new MockDataSourceFactory(data_source_.get());
+}
+
MockDemuxer::MockDemuxer() {}
MockDemuxer::~MockDemuxer() {}
@@ -46,12 +112,29 @@ MockFilterCollection::MockFilterCollection()
MockFilterCollection::~MockFilterCollection() {}
FilterCollection* MockFilterCollection::filter_collection(
- bool include_data_source) const {
+ bool include_data_source,
+ bool run_build_callback,
+ PipelineError build_error) const {
FilterCollection* collection = new FilterCollection();
- if (include_data_source) {
- collection->AddDataSource(data_source_);
+ MockDataSourceFactory* data_source_factory =
+ new MockDataSourceFactory(include_data_source ? data_source_ : NULL);
+
+ if (build_error != PIPELINE_OK)
+ data_source_factory->SetError(build_error);
+
+ if (run_build_callback) {
+ ON_CALL(*data_source_factory, Build(_, NotNull()))
+ .WillByDefault(Invoke(data_source_factory,
+ &MockDataSourceFactory::RunBuildCallback));
+ } else {
+ ON_CALL(*data_source_factory, Build(_, NotNull()))
+ .WillByDefault(Invoke(data_source_factory,
+ &MockDataSourceFactory::DestroyBuildCallback));
}
+ EXPECT_CALL(*data_source_factory, Build(_, NotNull()));
+
+ collection->SetDataSourceFactory(data_source_factory);
collection->AddDemuxer(demuxer_);
collection->AddVideoDecoder(video_decoder_);
collection->AddAudioDecoder(audio_decoder_);
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index dd391ac..cc121ba 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -69,28 +69,54 @@ class MockDataSource : public DataSource {
MockDataSource();
// Filter implementation.
+ virtual void set_host(FilterHost* host);
+
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());
// DataSource implementation.
- MOCK_METHOD1(IsUrlSupported, bool(const std::string& url));
- MOCK_METHOD2(Initialize, void(const std::string& url,
- FilterCallback* callback));
MOCK_METHOD0(media_format, const MediaFormat&());
MOCK_METHOD4(Read, void(int64 position, size_t size, uint8* data,
DataSource::ReadCallback* callback));
MOCK_METHOD1(GetSize, bool(int64* size_out));
MOCK_METHOD0(IsStreaming, bool());
+ // Sets the TotalBytes & BufferedBytes values to be sent to host() when
+ // the set_host() is called.
+ void SetTotalAndBufferedBytes(int64 total_bytes, int64 buffered_bytes);
+
protected:
virtual ~MockDataSource();
private:
+ int64 total_bytes_;
+ int64 buffered_bytes_;
+
DISALLOW_COPY_AND_ASSIGN(MockDataSource);
};
+class MockDataSourceFactory : public DataSourceFactory {
+ public:
+ explicit MockDataSourceFactory(MockDataSource* data_source);
+ virtual ~MockDataSourceFactory();
+
+ void SetError(PipelineError error);
+ void RunBuildCallback(const std::string& url, BuildCallback* callback);
+ void DestroyBuildCallback(const std::string& url, BuildCallback* callback);
+
+ // DataSourceFactory methods.
+ MOCK_METHOD2(Build, void(const std::string& url, BuildCallback* callback));
+ virtual DataSourceFactory* Clone() const;
+
+ private:
+ scoped_refptr<MockDataSource> data_source_;
+ PipelineError error_;
+
+ DISALLOW_COPY_AND_ASSIGN(MockDataSourceFactory);
+};
+
class MockDemuxer : public Demuxer {
public:
MockDemuxer();
@@ -256,10 +282,12 @@ class MockFilterCollection {
MockAudioRenderer* audio_renderer() const { return audio_renderer_; }
FilterCollection* filter_collection() const {
- return filter_collection(true);
+ return filter_collection(true, true, PIPELINE_OK);
}
- FilterCollection* filter_collection(bool include_data_source) const;
+ FilterCollection* filter_collection(bool include_data_source,
+ bool run_build_callback,
+ PipelineError build_error) const;
private:
scoped_refptr<MockDataSource> data_source_;
@@ -300,18 +328,6 @@ ACTION_P2(SetDuration, filter, duration) {
filter->host()->SetDuration(duration);
}
-// Helper gmock action that calls SetTotalBytes() on behalf of the provided
-// filter.
-ACTION_P2(SetTotalBytes, filter, bytes) {
- filter->host()->SetTotalBytes(bytes);
-}
-
-// Helper gmock action that calls SetBufferedBytes() on behalf of the provided
-// filter.
-ACTION_P2(SetBufferedBytes, filter, bytes) {
- filter->host()->SetBufferedBytes(bytes);
-}
-
// Helper gmock action that calls DisableAudioRenderer() on behalf of the
// provided filter.
ACTION_P(DisableAudioRenderer, filter) {
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index 8e7a8db..802903d 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -12,6 +12,7 @@
#include <string>
#include "base/callback.h"
+#include "media/base/pipeline_status.h"
namespace base {
class TimeDelta;
@@ -19,29 +20,6 @@ class TimeDelta;
namespace media {
-// Error definitions for pipeline. All codes indicate an error except:
-// PIPELINE_OK indicates the pipeline is running normally.
-enum PipelineError {
- PIPELINE_OK,
- PIPELINE_ERROR_URL_NOT_FOUND,
- PIPELINE_ERROR_NETWORK,
- PIPELINE_ERROR_DECODE,
- PIPELINE_ERROR_ABORT,
- PIPELINE_ERROR_INITIALIZATION_FAILED,
- PIPELINE_ERROR_REQUIRED_FILTER_MISSING,
- PIPELINE_ERROR_OUT_OF_MEMORY,
- 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,
- DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
- DEMUXER_ERROR_COULD_NOT_CREATE_THREAD,
-};
-
struct PipelineStatistics {
PipelineStatistics() :
audio_bytes_decoded(0),
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index d4009c8..4f19fcc 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -18,7 +18,6 @@ namespace media {
class PipelineImpl::PipelineInitState {
public:
- scoped_refptr<DataSource> data_source_;
scoped_refptr<Demuxer> demuxer_;
scoped_refptr<AudioDecoder> audio_decoder_;
scoped_refptr<VideoDecoder> video_decoder_;
@@ -550,7 +549,12 @@ void PipelineImpl::StartTask(FilterCollection* filter_collection,
seek_callback_.reset(start_callback);
// Kick off initialization.
- InitializeTask();
+ set_state(kInitDataSource);
+ pipeline_init_state_.reset(new PipelineInitState());
+ pipeline_init_state_->composite_ = new CompositeFilter(message_loop_);
+ pipeline_init_state_->composite_->set_host(this);
+
+ InitializeDataSource();
}
// Main initialization method called on the pipeline thread. This code attempts
@@ -580,31 +584,12 @@ void PipelineImpl::InitializeTask() {
return;
}
- DCHECK(state_ == kCreated ||
- state_ == kInitDataSource ||
- state_ == kInitDemuxer ||
+ DCHECK(state_ == kInitDemuxer ||
state_ == kInitAudioDecoder ||
state_ == kInitAudioRenderer ||
state_ == kInitVideoDecoder ||
state_ == kInitVideoRenderer);
- // Just created, create data source.
- if (state_ == kCreated) {
- set_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);
- InitializeDemuxer(pipeline_init_state_->data_source_);
- return;
- }
// Demuxer created, create audio decoder.
if (state_ == kInitDemuxer) {
@@ -1013,24 +998,30 @@ void PipelineImpl::InitializeDataSource() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
DCHECK(IsPipelineOk());
- scoped_refptr<DataSource> data_source;
- while (true) {
- filter_collection_->SelectDataSource(&data_source);
- if (!data_source || data_source->IsUrlSupported(url_))
- break;
- }
+ filter_collection_->GetDataSourceFactory()->Build(url_,
+ NewCallback(this, &PipelineImpl::OnDataSourceBuilt));
+}
- if (!data_source) {
- SetError(PIPELINE_ERROR_REQUIRED_FILTER_MISSING);
+void PipelineImpl::OnDataSourceBuilt(PipelineError error,
+ DataSource* data_source) {
+ if (MessageLoop::current() != message_loop_) {
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this,
+ &PipelineImpl::OnDataSourceBuilt,
+ error,
+ make_scoped_refptr(data_source)));
return;
}
- if (!PrepareFilter(data_source))
+ if (error != PIPELINE_OK) {
+ SetError(error);
return;
+ }
+
+ PrepareFilter(data_source);
- pipeline_init_state_->data_source_ = data_source;
- data_source->Initialize(
- url_, NewCallback(this, &PipelineImpl::OnFilterInitialize));
+ set_state(kInitDemuxer);
+ InitializeDemuxer(data_source);
}
void PipelineImpl::InitializeDemuxer(
@@ -1207,6 +1198,7 @@ void PipelineImpl::TearDownPipeline() {
// Make it look like initialization was successful.
pipeline_filter_ = pipeline_init_state_->composite_;
pipeline_init_state_.reset();
+ filter_collection_.reset();
set_state(kStopping);
pipeline_filter_->Stop(
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index fa4b5d1..0fe8c09 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -246,6 +246,8 @@ class PipelineImpl : public Pipeline, public FilterHost {
// The following initialize methods are used to select a specific type of
// Filter object from FilterCollection and initialize it asynchronously.
void InitializeDataSource();
+ void OnDataSourceBuilt(PipelineError error, DataSource* data_source);
+
void InitializeDemuxer(const scoped_refptr<DataSource>& data_source);
// Returns true if the asynchronous action of creating decoder has started.
diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc
index b583879..486b5e0 100644
--- a/media/base/pipeline_impl_unittest.cc
+++ b/media/base/pipeline_impl_unittest.cc
@@ -85,13 +85,10 @@ class PipelineImplTest : public ::testing::Test {
protected:
// Sets up expectations to allow the data source to initialize.
void InitializeDataSource() {
- EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
- .WillOnce(DoAll(SetTotalBytes(mocks_->data_source(), kTotalBytes),
- SetBufferedBytes(mocks_->data_source(), kBufferedBytes),
- Invoke(&RunFilterCallback)));
+ mocks_->data_source()->SetTotalAndBufferedBytes(kTotalBytes,
+ kBufferedBytes);
+
EXPECT_CALL(*mocks_->data_source(), SetPlaybackRate(0.0f));
- EXPECT_CALL(*mocks_->data_source(), IsUrlSupported(_))
- .WillOnce(Return(true));
EXPECT_CALL(*mocks_->data_source(), Seek(base::TimeDelta(), NotNull()))
.WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
@@ -189,9 +186,14 @@ class PipelineImplTest : public ::testing::Test {
// Sets up expectations on the callback and initializes the pipeline. Called
// after tests have set expectations any filters they wish to use.
void InitializePipeline() {
+ InitializePipeline(PIPELINE_OK);
+ }
+
+ void InitializePipeline(PipelineError factory_error) {
// Expect an initialization callback.
EXPECT_CALL(callbacks_, OnStart());
- pipeline_->Start(mocks_->filter_collection(), "",
+ pipeline_->Start(mocks_->filter_collection(true, true, factory_error),
+ "",
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnStart));
message_loop_.RunAllPending();
@@ -312,17 +314,10 @@ TEST_F(PipelineImplTest, NotStarted) {
}
TEST_F(PipelineImplTest, NeverInitializes) {
- EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
- .WillOnce(Invoke(&DestroyFilterCallback));
- EXPECT_CALL(*mocks_->data_source(), IsUrlSupported(_))
- .WillOnce(Return(true));
- EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
- .WillOnce(Invoke(&RunStopFilterCallback));
-
// This test hangs during initialization by never calling
// InitializationComplete(). StrictMock<> will ensure that the callback is
// never executed.
- pipeline_->Start(mocks_->filter_collection(), "",
+ pipeline_->Start(mocks_->filter_collection(false, false, PIPELINE_OK), "",
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnStart));
message_loop_.RunAllPending();
@@ -346,7 +341,9 @@ TEST_F(PipelineImplTest, RequiredFilterMissing) {
EXPECT_CALL(callbacks_, OnStart());
// Create a filter collection with missing filter.
- FilterCollection* collection = mocks_->filter_collection(false);
+ FilterCollection* collection =
+ mocks_->filter_collection(false, true,
+ PIPELINE_ERROR_REQUIRED_FILTER_MISSING);
pipeline_->Start(collection, "",
NewCallback(reinterpret_cast<CallbackHelper*>(&callbacks_),
&CallbackHelper::OnStart));
@@ -358,17 +355,10 @@ TEST_F(PipelineImplTest, RequiredFilterMissing) {
}
TEST_F(PipelineImplTest, URLNotFound) {
- EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
- .WillOnce(DoAll(SetError(mocks_->data_source(),
- PIPELINE_ERROR_URL_NOT_FOUND),
- Invoke(&RunFilterCallback)));
- EXPECT_CALL(*mocks_->data_source(), IsUrlSupported(_))
- .WillOnce(Return(true));
+
EXPECT_CALL(callbacks_, OnError());
- EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
- .WillOnce(Invoke(&RunStopFilterCallback));
- InitializePipeline();
+ InitializePipeline(PIPELINE_ERROR_URL_NOT_FOUND);
EXPECT_FALSE(pipeline_->IsInitialized());
EXPECT_EQ(PIPELINE_ERROR_URL_NOT_FOUND, pipeline_->GetError());
}
@@ -376,10 +366,6 @@ TEST_F(PipelineImplTest, URLNotFound) {
TEST_F(PipelineImplTest, NoStreams) {
// 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));
- EXPECT_CALL(*mocks_->data_source(), IsUrlSupported(_))
- .WillOnce(Return(true));
EXPECT_CALL(*mocks_->data_source(), Stop(NotNull()))
.WillOnce(Invoke(&RunStopFilterCallback));
diff --git a/media/base/pipeline_status.h b/media/base/pipeline_status.h
new file mode 100644
index 0000000..787708e
--- /dev/null
+++ b/media/base/pipeline_status.h
@@ -0,0 +1,41 @@
+// Copyright (c) 2011 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_PIPELINE_STATUS_H_
+#define MEDIA_BASE_PIPELINE_STATUS_H_
+
+#include "base/callback.h"
+
+namespace media {
+
+// Error definitions for pipeline. All codes indicate an error except:
+// PIPELINE_OK indicates the pipeline is running normally.
+enum PipelineError {
+ PIPELINE_OK,
+ PIPELINE_ERROR_URL_NOT_FOUND,
+ PIPELINE_ERROR_NETWORK,
+ PIPELINE_ERROR_DECODE,
+ PIPELINE_ERROR_ABORT,
+ PIPELINE_ERROR_INITIALIZATION_FAILED,
+ PIPELINE_ERROR_REQUIRED_FILTER_MISSING,
+ PIPELINE_ERROR_OUT_OF_MEMORY,
+ 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,
+ DEMUXER_ERROR_NO_SUPPORTED_STREAMS,
+ DEMUXER_ERROR_COULD_NOT_CREATE_THREAD,
+ // DataSourceFactory errors.
+ DATASOURCE_ERROR_URL_NOT_SUPPORTED,
+};
+
+typedef Callback1<media::PipelineError>::Type PipelineStatusCallback;
+
+} // namespace media
+
+#endif // MEDIA_BASE_PIPELINE_STATUS_H_
diff --git a/media/filters/file_data_source.cc b/media/filters/file_data_source.cc
index 9fd93a5..8a1f055 100644
--- a/media/filters/file_data_source.cc
+++ b/media/filters/file_data_source.cc
@@ -23,10 +23,8 @@ FileDataSource::~FileDataSource() {
DCHECK(!file_);
}
-void FileDataSource::Initialize(const std::string& url,
- FilterCallback* callback) {
+PipelineError FileDataSource::Initialize(const std::string& url) {
DCHECK(!file_);
- scoped_ptr<FilterCallback> c(callback);
#if defined(OS_WIN)
FilePath file_path(UTF8ToWide(url));
#else
@@ -37,14 +35,24 @@ void FileDataSource::Initialize(const std::string& url,
}
if (!file_) {
file_size_ = 0;
- host()->SetError(PIPELINE_ERROR_URL_NOT_FOUND);
- callback->Run();
- return;
+ return PIPELINE_ERROR_URL_NOT_FOUND;
}
media_format_.SetAsString(MediaFormat::kURL, url);
- host()->SetTotalBytes(file_size_);
- host()->SetBufferedBytes(file_size_);
- callback->Run();
+
+ if (host()) {
+ host()->SetTotalBytes(file_size_);
+ host()->SetBufferedBytes(file_size_);
+ }
+
+ return PIPELINE_OK;
+}
+
+void FileDataSource::set_host(FilterHost* filter_host) {
+ DataSource::set_host(filter_host);
+ if (file_) {
+ host()->SetTotalBytes(file_size_);
+ host()->SetBufferedBytes(file_size_);
+ }
}
void FileDataSource::Stop(FilterCallback* callback) {
diff --git a/media/filters/file_data_source.h b/media/filters/file_data_source.h
index 7be9a5a..5ae3956 100644
--- a/media/filters/file_data_source.h
+++ b/media/filters/file_data_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -20,11 +20,13 @@ class FileDataSource : public DataSource {
FileDataSource();
virtual ~FileDataSource();
+ PipelineError Initialize(const std::string& url);
+
// Implementation of Filter.
+ virtual void set_host(FilterHost* filter_host);
virtual void Stop(FilterCallback* callback);
// Implementation of DataSource.
- virtual void Initialize(const std::string& url, FilterCallback* callback);
virtual const MediaFormat& media_format();
virtual void Read(int64 position, size_t size, uint8* data,
ReadCallback* read_callback);
diff --git a/media/filters/file_data_source_factory.cc b/media/filters/file_data_source_factory.cc
new file mode 100644
index 0000000..80642fb
--- /dev/null
+++ b/media/filters/file_data_source_factory.cc
@@ -0,0 +1,40 @@
+// Copyright (c) 2011 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/filters/file_data_source_factory.h"
+
+#include "base/logging.h"
+#include "media/filters/file_data_source.h"
+
+namespace media {
+
+FileDataSourceFactory::FileDataSourceFactory() {}
+
+FileDataSourceFactory::~FileDataSourceFactory() {}
+
+void FileDataSourceFactory::Build(const std::string& url,
+ BuildCallback* callback) {
+ DCHECK(callback);
+
+ if (url.empty()) {
+ callback->Run(media::PIPELINE_ERROR_URL_NOT_FOUND,
+ static_cast<media::DataSource*>(NULL));
+ delete callback;
+ return;
+ }
+
+ scoped_refptr<FileDataSource> file_data_source = new FileDataSource();
+
+ PipelineError error = file_data_source->Initialize(url);
+ DataSource* data_source =
+ (error == PIPELINE_OK) ? file_data_source.get() : NULL;
+ callback->Run(error, data_source);
+ delete callback;
+}
+
+DataSourceFactory* FileDataSourceFactory::Clone() const {
+ return new FileDataSourceFactory();
+}
+
+} // namespace media
diff --git a/media/filters/file_data_source_factory.h b/media/filters/file_data_source_factory.h
new file mode 100644
index 0000000..18c0587
--- /dev/null
+++ b/media/filters/file_data_source_factory.h
@@ -0,0 +1,27 @@
+// Copyright (c) 2011 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_FILTERS_FILE_DATA_SOURCE_FACTORY_H_
+#define MEDIA_FILTERS_FILE_DATA_SOURCE_FACTORY_H_
+
+#include "media/base/filter_factories.h"
+
+namespace media {
+
+class FileDataSourceFactory : public DataSourceFactory {
+ public:
+ FileDataSourceFactory();
+ virtual ~FileDataSourceFactory();
+
+ // DataSourceFactory methods.
+ virtual void Build(const std::string& url, BuildCallback* callback);
+ virtual DataSourceFactory* Clone() const;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FileDataSourceFactory);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_FILE_DATA_SOURCE_FACTORY_H_
diff --git a/media/filters/file_data_source_unittest.cc b/media/filters/file_data_source_unittest.cc
index 1770ac4..1ae111e 100644
--- a/media/filters/file_data_source_unittest.cc
+++ b/media/filters/file_data_source_unittest.cc
@@ -56,7 +56,7 @@ TEST(FileDataSourceTest, OpenFile) {
scoped_refptr<FileDataSource> filter(new FileDataSource());
filter->set_host(&host);
- filter->Initialize(TestFileURL(), NewExpectedCallback());
+ EXPECT_EQ(PIPELINE_OK, filter->Initialize(TestFileURL()));
filter->Stop(NewExpectedCallback());
}
@@ -71,7 +71,7 @@ TEST(FileDataSourceTest, ReadData) {
scoped_refptr<FileDataSource> filter(new FileDataSource());
filter->set_host(&host);
- filter->Initialize(TestFileURL(), NewExpectedCallback());
+ EXPECT_EQ(PIPELINE_OK, filter->Initialize(TestFileURL()));
EXPECT_TRUE(filter->GetSize(&size));
EXPECT_EQ(10, size);
diff --git a/media/media.gyp b/media/media.gyp
index ebccbf2..933f513 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -72,12 +72,16 @@
'audio/win/wavein_input_win.h',
'audio/win/waveout_output_win.cc',
'audio/win/waveout_output_win.h',
+ 'base/async_filter_factory_base.cc',
+ 'base/async_filter_factory_base.h',
'base/buffers.cc',
'base/buffers.h',
'base/callback.cc',
'base/callback.h',
'base/clock.cc',
'base/clock.h',
+ 'base/composite_data_source_factory.cc',
+ 'base/composite_data_source_factory.h',
'base/composite_filter.cc',
'base/composite_filter.h',
'base/data_buffer.cc',
@@ -86,6 +90,8 @@
'base/djb2.h',
'base/filter_collection.cc',
'base/filter_collection.h',
+ 'base/filter_factories.cc',
+ 'base/filter_factories.h',
'base/filter_host.h',
'base/filters.cc',
'base/filters.h',
@@ -105,6 +111,7 @@
'base/pipeline.h',
'base/pipeline_impl.cc',
'base/pipeline_impl.h',
+ 'base/pipeline_status.h',
'base/pts_heap.cc',
'base/pts_heap.h',
'base/seekable_buffer.cc',
@@ -146,6 +153,8 @@
'filters/ffmpeg_video_decoder.h',
'filters/file_data_source.cc',
'filters/file_data_source.h',
+ 'filters/file_data_source_factory.cc',
+ 'filters/file_data_source_factory.h',
'filters/null_audio_renderer.cc',
'filters/null_audio_renderer.h',
'filters/null_video_renderer.cc',
diff --git a/media/tools/player_wtl/movie.cc b/media/tools/player_wtl/movie.cc
index fe73490..371d178 100644
--- a/media/tools/player_wtl/movie.cc
+++ b/media/tools/player_wtl/movie.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -14,7 +14,7 @@
#include "media/filters/ffmpeg_audio_decoder.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"
-#include "media/filters/file_data_source.h"
+#include "media/filters/file_data_source_factory.h"
#include "media/filters/null_audio_renderer.h"
#include "media/tools/player_wtl/wtl_renderer.h"
@@ -22,7 +22,7 @@ using media::AudioRendererImpl;
using media::FFmpegAudioDecoder;
using media::FFmpegDemuxer;
using media::FFmpegVideoDecoder;
-using media::FileDataSource;
+using media::FileDataSourceFactory;
using media::FilterCollection;
using media::PipelineImpl;
@@ -65,7 +65,7 @@ bool Movie::Open(const wchar_t* url, WtlVideoRenderer* video_renderer) {
// Create filter collection.
scoped_ptr<FilterCollection> collection(new FilterCollection());
- collection->AddDataSource(new FileDataSource());
+ collection->SetDataSourceFactory(new FileDataSourceFactory());
collection->AddAudioDecoder(new FFmpegAudioDecoder(
message_loop_factory_->GetMessageLoop("AudioDecoderThread")));
collection->AddDemuxer(new FFmpegDemuxer(
diff --git a/media/tools/player_x11/player_x11.cc b/media/tools/player_x11/player_x11.cc
index 39504ff..68c56ae 100644
--- a/media/tools/player_x11/player_x11.cc
+++ b/media/tools/player_x11/player_x11.cc
@@ -23,7 +23,7 @@
#include "media/filters/ffmpeg_audio_decoder.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"
-#include "media/filters/file_data_source.h"
+#include "media/filters/file_data_source_factory.h"
#include "media/filters/null_audio_renderer.h"
#include "media/filters/omx_video_decoder.h"
@@ -103,7 +103,7 @@ bool InitPipeline(MessageLoop* message_loop,
// Create our filter factories.
scoped_ptr<media::FilterCollection> collection(
new media::FilterCollection());
- collection->AddDataSource(new media::FileDataSource());
+ collection->SetDataSourceFactory(new media::FileDataSourceFactory());
collection->AddDemuxer(new media::FFmpegDemuxer(
message_loop_factory->GetMessageLoop("DemuxThread")));
collection->AddAudioDecoder(new media::FFmpegAudioDecoder(
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index 9cb0284..135c49e 100644
--- a/webkit/glue/media/buffered_data_source.cc
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -6,6 +6,7 @@
#include "media/base/filter_host.h"
#include "net/base/net_errors.h"
+#include "webkit/glue/media/web_data_source_factory.h"
#include "webkit/glue/webkit_glue.h"
using WebKit::WebFrame;
@@ -27,10 +28,25 @@ static const int kReadTrials = 3;
// of FFmpeg.
static const int kInitialReadBufferSize = 32768;
+static WebDataSource* NewBufferedDataSource(MessageLoop* render_loop,
+ WebKit::WebFrame* frame) {
+ return new BufferedDataSource(render_loop, frame);
+}
+
+// static
+media::DataSourceFactory* BufferedDataSource::CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer) {
+ return new WebDataSourceFactory(render_loop, frame, &NewBufferedDataSource,
+ build_observer);
+}
+
BufferedDataSource::BufferedDataSource(
MessageLoop* render_loop,
WebFrame* frame)
: total_bytes_(kPositionNotSpecified),
+ buffered_bytes_(0),
loaded_(false),
streaming_(false),
frame_(frame),
@@ -73,18 +89,25 @@ base::TimeDelta BufferedDataSource::GetTimeoutMilliseconds() {
return base::TimeDelta::FromMilliseconds(kTimeoutMilliseconds);
}
-/////////////////////////////////////////////////////////////////////////////
-// media::Filter implementation.
+void BufferedDataSource::set_host(media::FilterHost* host) {
+ DataSource::set_host(host);
+
+ if (loader_.get())
+ UpdateHostState();
+}
+
void BufferedDataSource::Initialize(const std::string& url,
- media::FilterCallback* callback) {
+ media::PipelineStatusCallback* callback) {
// Saves the url.
url_ = GURL(url);
- if (!IsProtocolSupportedForMedia(url_)) {
- // This method is called on the thread where host() lives so it is safe
- // to make this call.
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
- callback->Run();
+ // This data source doesn't support data:// protocol so reject it.
+ if (url_.SchemeIs(kDataScheme)) {
+ callback->Run(media::DATASOURCE_ERROR_URL_NOT_SUPPORTED);
+ delete callback;
+ return;
+ } else if (!IsProtocolSupportedForMedia(url_)) {
+ callback->Run(media::PIPELINE_ERROR_NETWORK);
delete callback;
return;
}
@@ -99,13 +122,18 @@ void BufferedDataSource::Initialize(const std::string& url,
NewRunnableMethod(this, &BufferedDataSource::InitializeTask));
}
-bool BufferedDataSource::IsUrlSupported(const std::string& url) {
- GURL gurl(url);
+void BufferedDataSource::CancelInitialize() {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(initialize_callback_.get());
- // This data source doesn't support data:// protocol so reject it.
- return IsProtocolSupportedForMedia(gurl) && !gurl.SchemeIs(kDataScheme);
+ initialize_callback_.reset();
+
+ render_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &BufferedDataSource::CleanupTask));
}
+/////////////////////////////////////////////////////////////////////////////
+// media::Filter implementation.
void BufferedDataSource::Stop(media::FilterCallback* callback) {
{
base::AutoLock auto_lock(lock_);
@@ -190,7 +218,7 @@ void BufferedDataSource::Abort() {
void BufferedDataSource::InitializeTask() {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(!loader_.get());
- if (stopped_on_render_loop_)
+ if (stopped_on_render_loop_ || !initialize_callback_.get())
return;
// Kick starts the watch dog task that will handle connection timeout.
@@ -384,13 +412,14 @@ void BufferedDataSource::DoneRead_Locked(int error) {
read_buffer_ = 0;
}
-void BufferedDataSource::DoneInitialization_Locked() {
+void BufferedDataSource::DoneInitialization_Locked(media::PipelineError error) {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(initialize_callback_.get());
lock_.AssertAcquired();
- initialize_callback_->Run();
- initialize_callback_.reset();
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback(
+ initialize_callback_.release());
+ initialize_callback->Run(error);
}
/////////////////////////////////////////////////////////////////////////////
@@ -403,6 +432,11 @@ void BufferedDataSource::HttpInitialStartCallback(int error) {
bool partial_response = loader_->partial_response();
bool success = error == net::OK;
+ if (!initialize_callback_.get()) {
+ loader_->Stop();
+ return;
+ }
+
if (success) {
// TODO(hclam): Needs more thinking about supporting servers without range
// request or their partial response is not complete.
@@ -427,74 +461,82 @@ void BufferedDataSource::HttpInitialStartCallback(int error) {
return;
}
- // We need to prevent calling to filter host and running the callback if
- // we have received the stop signal. We need to lock down the whole callback
- // method to prevent bad things from happening. The reason behind this is
- // that we cannot guarantee tasks on render thread have completely stopped
- // when we receive the Stop() method call. The only way to solve this is to
- // let tasks on render thread to run but make sure they don't call outside
- // this object when Stop() method is ever called. Locking this method is safe
- // because |lock_| is only acquired in tasks on render thread.
- base::AutoLock auto_lock(lock_);
- if (stop_signal_received_)
- return;
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ // TODO: Review locking in this class and figure out a way to run the callback
+ // w/o the lock.
+ scoped_refptr<BufferedDataSource> destruction_guard(this);
+ {
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. The only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is
+ // safe because |lock_| is only acquired in tasks on render thread.
+ base::AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
- if (!success) {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
- DoneInitialization_Locked();
- return;
- }
+ if (!success) {
+ DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
+ return;
+ }
- if (streaming_) {
- // If the server didn't reply with an instance size, it is likely this
- // is a streaming response.
- host()->SetStreaming(true);
- } else {
- // This value governs the range that we can seek to.
- // TODO(hclam): Report the correct value of buffered bytes.
- host()->SetTotalBytes(total_bytes_);
- host()->SetBufferedBytes(0);
+ UpdateHostState();
+ DoneInitialization_Locked(media::PIPELINE_OK);
}
-
- // Currently, only files can be used reliably w/o a network.
- host()->SetLoaded(false);
- DoneInitialization_Locked();
}
void BufferedDataSource::NonHttpInitialStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(loader_.get());
+ if (!initialize_callback_.get()) {
+ loader_->Stop();
+ return;
+ }
+
int64 instance_size = loader_->instance_size();
bool success = error == net::OK && instance_size != kPositionNotSpecified;
if (success) {
total_bytes_ = instance_size;
+ buffered_bytes_ = total_bytes_;
loaded_ = true;
} else {
loader_->Stop();
}
- // We need to prevent calling to filter host and running the callback if
- // we have received the stop signal. We need to lock down the whole callback
- // method to prevent bad things from happening. The reason behind this is
- // that we cannot guarantee tasks on render thread have completely stopped
- // when we receive the Stop() method call. The only way to solve this is to
- // let tasks on render thread to run but make sure they don't call outside
- // this object when Stop() method is ever called. Locking this method is safe
- // because |lock_| is only acquired in tasks on render thread.
- base::AutoLock auto_lock(lock_);
- if (stop_signal_received_)
- return;
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ // TODO: Review locking in this class and figure out a way to run the callback
+ // w/o the lock.
+ scoped_refptr<BufferedDataSource> destruction_guard(this);
+ {
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. The only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is
+ // safe because |lock_| is only acquired in tasks on render thread.
+ base::AutoLock auto_lock(lock_);
+ if (stop_signal_received_ || !initialize_callback_.get())
+ return;
- if (success) {
- host()->SetTotalBytes(total_bytes_);
- host()->SetBufferedBytes(total_bytes_);
- host()->SetLoaded(loaded_);
- } else {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
+ if (!success) {
+ DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
+ return;
+ }
+
+ UpdateHostState();
+ DoneInitialization_Locked(media::PIPELINE_OK);
}
- DoneInitialization_Locked();
}
void BufferedDataSource::PartialReadStartCallback(int error) {
@@ -594,9 +636,28 @@ void BufferedDataSource::NetworkEventCallback() {
if (network_activity != network_activity_) {
network_activity_ = network_activity;
- host()->SetNetworkActivity(network_activity);
+ if (host())
+ host()->SetNetworkActivity(network_activity);
+ }
+
+ buffered_bytes_ = buffered_position + 1;
+ if (host())
+ host()->SetBufferedBytes(buffered_bytes_);
+}
+
+void BufferedDataSource::UpdateHostState() {
+ media::FilterHost* filter_host = host();
+ if (!filter_host)
+ return;
+
+ filter_host->SetLoaded(loaded_);
+
+ if (streaming_) {
+ filter_host->SetStreaming(true);
+ } else {
+ filter_host->SetTotalBytes(total_bytes_);
+ filter_host->SetBufferedBytes(buffered_bytes_);
}
- host()->SetBufferedBytes(buffered_position + 1);
}
} // namespace webkit_glue
diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h
index 895b259..9e043b0 100644
--- a/webkit/glue/media/buffered_data_source.h
+++ b/webkit/glue/media/buffered_data_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -10,21 +10,26 @@
#include "base/callback.h"
#include "base/scoped_ptr.h"
#include "base/synchronization/lock.h"
+#include "media/base/filter_factories.h"
#include "webkit/glue/media/buffered_resource_loader.h"
namespace webkit_glue {
class BufferedDataSource : public WebDataSource {
public:
+ // Creates a DataSourceFactory for building BufferedDataSource objects.
+ static media::DataSourceFactory* CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer);
+
BufferedDataSource(MessageLoop* render_loop,
WebKit::WebFrame* frame);
virtual ~BufferedDataSource();
// media::Filter implementation.
- virtual void Initialize(const std::string& url,
- media::FilterCallback* callback);
- virtual bool IsUrlSupported(const std::string& url);
+ virtual void set_host(media::FilterHost* host);
virtual void Stop(media::FilterCallback* callback);
virtual void SetPlaybackRate(float playback_rate);
@@ -41,6 +46,9 @@ class BufferedDataSource : public WebDataSource {
}
// webkit_glue::WebDataSource implementation.
+ virtual void Initialize(const std::string& url,
+ media::PipelineStatusCallback* callback);
+ virtual void CancelInitialize();
virtual bool HasSingleOrigin();
virtual void Abort();
@@ -90,7 +98,7 @@ class BufferedDataSource : public WebDataSource {
void DoneRead_Locked(int error);
// Calls |initialize_callback_| and reset it.
- void DoneInitialization_Locked();
+ void DoneInitialization_Locked(media::PipelineError error);
// Callback method for |loader_| if URL for the resource requested is using
// HTTP protocol. This method is called when response for initial request is
@@ -116,6 +124,8 @@ class BufferedDataSource : public WebDataSource {
// Callback method when a network event is received.
void NetworkEventCallback();
+ void UpdateHostState();
+
media::MediaFormat media_format_;
// URL of the resource requested.
@@ -126,6 +136,7 @@ class BufferedDataSource : public WebDataSource {
// member is guaranteed to happen after it is first written, so we don't
// need to protect it.
int64 total_bytes_;
+ int64 buffered_bytes_;
// True if this data source is considered loaded.
bool loaded_;
@@ -144,7 +155,7 @@ class BufferedDataSource : public WebDataSource {
bool network_activity_;
// Callback method from the pipeline for initialization.
- scoped_ptr<media::FilterCallback> initialize_callback_;
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback_;
// Read parameters received from the Read() method call.
scoped_ptr<media::DataSource::ReadCallback> read_callback_;
diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc
index 41f37d6..9763678 100644
--- a/webkit/glue/media/buffered_data_source_unittest.cc
+++ b/webkit/glue/media/buffered_data_source_unittest.cc
@@ -168,6 +168,7 @@ class BufferedDataSourceTest : public testing::Test {
.WillByDefault(Return(partial_response));
ON_CALL(*loader_, url())
.WillByDefault(ReturnRef(gurl_));
+ media::PipelineError expected_init_error = media::PIPELINE_OK;
if (initialized_ok) {
// Expected loaded or not.
EXPECT_CALL(host_, SetLoaded(loaded));
@@ -183,12 +184,13 @@ class BufferedDataSourceTest : public testing::Test {
EXPECT_CALL(host_, SetStreaming(true));
}
} else {
- EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
+ expected_init_error = media::PIPELINE_ERROR_NETWORK;
EXPECT_CALL(*loader_, Stop());
}
// Actual initialization of the data source.
- data_source_->Initialize(url, media::NewExpectedCallback());
+ data_source_->Initialize(url,
+ media::NewExpectedStatusCallback(expected_init_error));
message_loop_->RunAllPending();
if (initialized_ok) {
diff --git a/webkit/glue/media/buffered_resource_loader.cc b/webkit/glue/media/buffered_resource_loader.cc
index 4a3ae8b..c6295bf 100644
--- a/webkit/glue/media/buffered_resource_loader.cc
+++ b/webkit/glue/media/buffered_resource_loader.cc
@@ -244,7 +244,7 @@ void BufferedResourceLoader::willSendRequest(
return;
}
- // Only allow |single_origin_| if we haven't seen a different origin yet.
+ // Only allow |single_origin_| if we haven't seen a different origin yet.
if (single_origin_)
single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
diff --git a/webkit/glue/media/simple_data_source.cc b/webkit/glue/media/simple_data_source.cc
index f1564d9..ca258d4 100644
--- a/webkit/glue/media/simple_data_source.cc
+++ b/webkit/glue/media/simple_data_source.cc
@@ -12,12 +12,27 @@
#include "net/url_request/url_request_status.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKitClient.h"
+#include "webkit/glue/media/web_data_source_factory.h"
#include "webkit/glue/webkit_glue.h"
namespace webkit_glue {
static const char kDataScheme[] = "data";
+static WebDataSource* NewSimpleDataSource(MessageLoop* render_loop,
+ WebKit::WebFrame* frame) {
+ return new SimpleDataSource(render_loop, frame);
+}
+
+// static
+media::DataSourceFactory* SimpleDataSource::CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer) {
+ return new WebDataSourceFactory(render_loop, frame, &NewSimpleDataSource,
+ build_observer);
+}
+
SimpleDataSource::SimpleDataSource(
MessageLoop* render_loop,
WebKit::WebFrame* frame)
@@ -35,6 +50,15 @@ SimpleDataSource::~SimpleDataSource() {
DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
}
+void SimpleDataSource::set_host(media::FilterHost* host) {
+ DataSource::set_host(host);
+
+ base::AutoLock auto_lock(lock_);
+ if (state_ == INITIALIZED) {
+ UpdateHostState();
+ }
+}
+
void SimpleDataSource::Stop(media::FilterCallback* callback) {
base::AutoLock auto_lock(lock_);
state_ = STOPPED;
@@ -48,26 +72,42 @@ void SimpleDataSource::Stop(media::FilterCallback* callback) {
NewRunnableMethod(this, &SimpleDataSource::CancelTask));
}
-void SimpleDataSource::Initialize(const std::string& url,
- media::FilterCallback* callback) {
- base::AutoLock auto_lock(lock_);
- DCHECK_EQ(state_, UNINITIALIZED);
- DCHECK(callback);
- state_ = INITIALIZING;
- initialize_callback_.reset(callback);
-
- // Validate the URL.
- SetURL(GURL(url));
- if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
- initialize_callback_->Run();
- initialize_callback_.reset();
- return;
+void SimpleDataSource::Initialize(
+ const std::string& url,
+ media::PipelineStatusCallback* callback) {
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+ DCHECK_EQ(state_, UNINITIALIZED);
+ DCHECK(callback);
+ state_ = INITIALIZING;
+ initialize_callback_.reset(callback);
+
+ // Validate the URL.
+ SetURL(GURL(url));
+ if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
+ DoneInitialization_Locked(false);
+ return;
+ }
+
+ // Post a task to the render thread to start loading the resource.
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SimpleDataSource::StartTask));
}
+}
- // Post a task to the render thread to start loading the resource.
+void SimpleDataSource::CancelInitialize() {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(initialize_callback_.get());
+ state_ = STOPPED;
+ initialize_callback_.reset();
+
+ // Post a task to the render thread to cancel loading the resource.
render_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this, &SimpleDataSource::StartTask));
+ NewRunnableMethod(this, &SimpleDataSource::CancelTask));
}
const media::MediaFormat& SimpleDataSource::media_format() {
@@ -157,44 +197,56 @@ void SimpleDataSource::didFinishLoading(
WebKit::WebURLLoader* loader,
double finishTime) {
DCHECK(MessageLoop::current() == render_loop_);
- base::AutoLock auto_lock(lock_);
- // It's possible this gets called after Stop(), in which case |host_| is no
- // longer valid.
- if (state_ == STOPPED)
- return;
-
- // Otherwise we should be initializing and have created a WebURLLoader.
- DCHECK_EQ(state_, INITIALIZING);
-
- // If we don't get a content length or the request has failed, report it
- // as a network error.
- if (size_ == -1)
- size_ = data_.length();
- DCHECK(static_cast<size_t>(size_) == data_.length());
-
- DoneInitialization_Locked(true);
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+ // It's possible this gets called after Stop(), in which case |host_| is no
+ // longer valid.
+ if (state_ == STOPPED)
+ return;
+
+ // Otherwise we should be initializing and have created a WebURLLoader.
+ DCHECK_EQ(state_, INITIALIZING);
+
+ // If we don't get a content length or the request has failed, report it
+ // as a network error.
+ if (size_ == -1)
+ size_ = data_.length();
+ DCHECK(static_cast<size_t>(size_) == data_.length());
+
+ DoneInitialization_Locked(true);
+ }
}
void SimpleDataSource::didFail(
WebKit::WebURLLoader* loader,
const WebKit::WebURLError& error) {
DCHECK(MessageLoop::current() == render_loop_);
- base::AutoLock auto_lock(lock_);
- // It's possible this gets called after Stop(), in which case |host_| is no
- // longer valid.
- if (state_ == STOPPED)
- return;
-
- // Otherwise we should be initializing and have created a WebURLLoader.
- DCHECK_EQ(state_, INITIALIZING);
-
- // If we don't get a content length or the request has failed, report it
- // as a network error.
- if (size_ == -1)
- size_ = data_.length();
- DCHECK(static_cast<size_t>(size_) == data_.length());
-
- DoneInitialization_Locked(false);
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+ // It's possible this gets called after Stop(), in which case |host_| is no
+ // longer valid.
+ if (state_ == STOPPED)
+ return;
+
+ // Otherwise we should be initializing and have created a WebURLLoader.
+ DCHECK_EQ(state_, INITIALIZING);
+
+ // If we don't get a content length or the request has failed, report it
+ // as a network error.
+ if (size_ == -1)
+ size_ = data_.length();
+ DCHECK(static_cast<size_t>(size_) == data_.length());
+
+ DoneInitialization_Locked(false);
+ }
}
bool SimpleDataSource::HasSingleOrigin() {
@@ -215,37 +267,43 @@ void SimpleDataSource::SetURL(const GURL& url) {
void SimpleDataSource::StartTask() {
DCHECK(MessageLoop::current() == render_loop_);
- base::AutoLock auto_lock(lock_);
-
- // We may have stopped.
- if (state_ == STOPPED)
- return;
-
- CHECK(frame_);
-
- DCHECK_EQ(state_, INITIALIZING);
-
- if (url_.SchemeIs(kDataScheme)) {
- // If this using data protocol, we just need to decode it.
- std::string mime_type, charset;
- bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
-
- // Don't care about the mime-type just proceed if decoding was successful.
- size_ = data_.length();
- DoneInitialization_Locked(success);
- } else {
- // Prepare the request.
- WebKit::WebURLRequest request(url_);
- request.setTargetType(WebKit::WebURLRequest::TargetIsMedia);
-
- frame_->setReferrerForRequest(request, WebKit::WebURL());
-
- // This flag is for unittests as we don't want to reset |url_loader|
- if (!keep_test_loader_)
- url_loader_.reset(frame_->createAssociatedURLLoader());
-
- // Start the resource loading.
- url_loader_->loadAsynchronously(request, this);
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+
+ // We may have stopped.
+ if (state_ == STOPPED)
+ return;
+
+ CHECK(frame_);
+
+ DCHECK_EQ(state_, INITIALIZING);
+
+ if (url_.SchemeIs(kDataScheme)) {
+ // If this using data protocol, we just need to decode it.
+ std::string mime_type, charset;
+ bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
+
+ // Don't care about the mime-type just proceed if decoding was successful.
+ size_ = data_.length();
+ DoneInitialization_Locked(success);
+ } else {
+ // Prepare the request.
+ WebKit::WebURLRequest request(url_);
+ request.setTargetType(WebKit::WebURLRequest::TargetIsMedia);
+
+ frame_->setReferrerForRequest(request, WebKit::WebURL());
+
+ // This flag is for unittests as we don't want to reset |url_loader|
+ if (!keep_test_loader_)
+ url_loader_.reset(frame_->createAssociatedURLLoader());
+
+ // Start the resource loading.
+ url_loader_->loadAsynchronously(request, this);
+ }
}
}
@@ -263,17 +321,29 @@ void SimpleDataSource::CancelTask() {
void SimpleDataSource::DoneInitialization_Locked(bool success) {
lock_.AssertAcquired();
+ media::PipelineError error = media::PIPELINE_ERROR_NETWORK;
if (success) {
state_ = INITIALIZED;
+
+ UpdateHostState();
+ error = media::PIPELINE_OK;
+ } else {
+ state_ = UNINITIALIZED;
+ url_loader_.reset();
+ }
+
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback(
+ initialize_callback_.release());
+ initialize_callback->Run(error);
+}
+
+void SimpleDataSource::UpdateHostState() {
+ if (host()) {
host()->SetTotalBytes(size_);
host()->SetBufferedBytes(size_);
// If scheme is file or data, say we are loaded.
host()->SetLoaded(url_.SchemeIsFile() || url_.SchemeIs(kDataScheme));
- } else {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
}
- initialize_callback_->Run();
- initialize_callback_.reset();
}
} // namespace webkit_glue
diff --git a/webkit/glue/media/simple_data_source.h b/webkit/glue/media/simple_data_source.h
index 10d93ff..5e59ead 100644
--- a/webkit/glue/media/simple_data_source.h
+++ b/webkit/glue/media/simple_data_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -15,6 +15,7 @@
#include "base/message_loop.h"
#include "base/scoped_ptr.h"
+#include "media/base/filter_factories.h"
#include "media/base/filters.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoader.h"
@@ -31,15 +32,20 @@ namespace webkit_glue {
class SimpleDataSource : public WebDataSource,
public WebKit::WebURLLoaderClient {
public:
+ // Creates a DataSourceFactory for building SimpleDataSource objects.
+ static media::DataSourceFactory* CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer);
+
SimpleDataSource(MessageLoop* render_loop, WebKit::WebFrame* frame);
virtual ~SimpleDataSource();
// media::Filter implementation.
+ virtual void set_host(media::FilterHost* host);
virtual void Stop(media::FilterCallback* callback);
// media::DataSource implementation.
- virtual void Initialize(const std::string& url,
- media::FilterCallback* callback);
virtual const media::MediaFormat& media_format();
virtual void Read(int64 position, size_t size,
uint8* data, ReadCallback* read_callback);
@@ -79,6 +85,9 @@ class SimpleDataSource : public WebDataSource,
const WebKit::WebURLError&);
// webkit_glue::WebDataSource implementation.
+ virtual void Initialize(const std::string& url,
+ media::PipelineStatusCallback* callback);
+ virtual void CancelInitialize();
virtual bool HasSingleOrigin();
virtual void Abort();
@@ -95,6 +104,9 @@ class SimpleDataSource : public WebDataSource,
// Perform initialization completion tasks under a lock.
void DoneInitialization_Locked(bool success);
+ // Update host() stats like total bytes & buffered bytes.
+ void UpdateHostState();
+
// Primarily used for asserting the bridge is loading on the render thread.
MessageLoop* render_loop_;
@@ -123,7 +135,7 @@ class SimpleDataSource : public WebDataSource,
base::Lock lock_;
// Filter callbacks.
- scoped_ptr<media::FilterCallback> initialize_callback_;
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback_;
// Used to ensure mocks for unittests are used instead of reset in Start().
bool keep_test_loader_;
diff --git a/webkit/glue/media/simple_data_source_unittest.cc b/webkit/glue/media/simple_data_source_unittest.cc
index 977807b..d3177ae 100644
--- a/webkit/glue/media/simple_data_source_unittest.cc
+++ b/webkit/glue/media/simple_data_source_unittest.cc
@@ -59,7 +59,7 @@ class SimpleDataSourceTest : public testing::Test {
}
void InitializeDataSource(const char* url,
- media::MockCallback* callback) {
+ media::MockStatusCallback* callback) {
gurl_ = GURL(url);
frame_.reset(new NiceMock<MockWebFrame>());
@@ -103,7 +103,6 @@ class SimpleDataSourceTest : public testing::Test {
void RequestFailed() {
InSequence s;
- EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
WebURLError error;
error.reason = net::ERR_FAILED;
@@ -158,19 +157,22 @@ class SimpleDataSourceTest : public testing::Test {
};
TEST_F(SimpleDataSourceTest, InitializeHTTP) {
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(false);
DestroyDataSource();
}
TEST_F(SimpleDataSourceTest, InitializeHTTPS) {
- InitializeDataSource(kHttpsUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpsUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(false);
DestroyDataSource();
}
TEST_F(SimpleDataSourceTest, InitializeFile) {
- InitializeDataSource(kFileUrl, media::NewExpectedCallback());
+ InitializeDataSource(kFileUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(true);
DestroyDataSource();
}
@@ -181,8 +183,6 @@ TEST_F(SimpleDataSourceTest, InitializeData) {
data_source_ = new SimpleDataSource(MessageLoop::current(),
frame_.get());
- EXPECT_TRUE(data_source_->IsUrlSupported(kDataUrl));
-
// There is no need to provide a message loop to data source.
data_source_->set_host(&host_);
data_source_->SetURLLoaderForTest(url_loader_);
@@ -191,14 +191,16 @@ TEST_F(SimpleDataSourceTest, InitializeData) {
EXPECT_CALL(host_, SetTotalBytes(sizeof(kDataUrlDecoded)));
EXPECT_CALL(host_, SetBufferedBytes(sizeof(kDataUrlDecoded)));
- data_source_->Initialize(kDataUrl, media::NewExpectedCallback());
+ data_source_->Initialize(kDataUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
MessageLoop::current()->RunAllPending();
DestroyDataSource();
}
TEST_F(SimpleDataSourceTest, RequestFailed) {
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_ERROR_NETWORK));
RequestFailed();
DestroyDataSource();
}
@@ -206,8 +208,8 @@ TEST_F(SimpleDataSourceTest, RequestFailed) {
TEST_F(SimpleDataSourceTest, StopWhenDownloading) {
// The callback should be deleted, but not executed.
// TODO(scherkus): should this really be the behaviour? Seems strange...
- StrictMock<media::MockCallback>* callback =
- new StrictMock<media::MockCallback>();
+ StrictMock<media::MockStatusCallback>* callback =
+ new StrictMock<media::MockStatusCallback>();
EXPECT_CALL(*callback, Destructor());
InitializeDataSource(kHttpUrl, callback);
@@ -217,7 +219,8 @@ TEST_F(SimpleDataSourceTest, StopWhenDownloading) {
}
TEST_F(SimpleDataSourceTest, AsyncRead) {
- InitializeDataSource(kFileUrl, media::NewExpectedCallback());
+ InitializeDataSource(kFileUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(true);
AsyncRead();
DestroyDataSource();
@@ -228,20 +231,23 @@ TEST_F(SimpleDataSourceTest, AsyncRead) {
// is fixed.
TEST_F(SimpleDataSourceTest, HasSingleOrigin) {
// Make sure no redirect case works as expected.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(false);
EXPECT_TRUE(data_source_->HasSingleOrigin());
DestroyDataSource();
// Test redirect to the same domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToSameDomainUrl1);
RequestSucceeded(false);
EXPECT_TRUE(data_source_->HasSingleOrigin());
DestroyDataSource();
// Test redirect twice to the same domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToSameDomainUrl1);
Redirect(kHttpRedirectToSameDomainUrl2);
RequestSucceeded(false);
@@ -249,14 +255,16 @@ TEST_F(SimpleDataSourceTest, HasSingleOrigin) {
DestroyDataSource();
// Test redirect to a different domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToDifferentDomainUrl1);
RequestSucceeded(false);
EXPECT_FALSE(data_source_->HasSingleOrigin());
DestroyDataSource();
// Test redirect to the same domain and then to a different domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToSameDomainUrl1);
Redirect(kHttpRedirectToDifferentDomainUrl1);
RequestSucceeded(false);
diff --git a/webkit/glue/media/web_data_source.h b/webkit/glue/media/web_data_source.h
index 956ac9e..25f1258 100644
--- a/webkit/glue/media/web_data_source.h
+++ b/webkit/glue/media/web_data_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -6,6 +6,7 @@
#define WEBKIT_GLUE_MEDIA_WEB_DATA_SOURCE_H_
#include "media/base/filters.h"
+#include "media/base/pipeline_status.h"
namespace webkit_glue {
@@ -16,6 +17,18 @@ class WebDataSource : public media::DataSource {
WebDataSource();
virtual ~WebDataSource();
+ // Initialize this object using |url|. This object calls |callback| when
+ // initialization has completed.
+ virtual void Initialize(const std::string& url,
+ media::PipelineStatusCallback* callback) = 0;
+
+ // Called to cancel initialization. The callback passed in Initialize() will
+ // be destroyed and will not be called after this method returns. Once this
+ // method returns, the object will be in an uninitialized state and
+ // Initialize() cannot be called again. The caller is expected to release
+ // its handle to this object and never call it again.
+ virtual void CancelInitialize() = 0;
+
// Returns true if the media resource has a single origin, false otherwise.
//
// Method called on the render thread.
@@ -31,6 +44,15 @@ class WebDataSource : public media::DataSource {
DISALLOW_COPY_AND_ASSIGN(WebDataSource);
};
+// Temporary hack to allow WebMediaPlayerImpl::Proxy::AddDataSource() to
+// be called when WebDataSource objects are created. This can be removed
+// once WebMediaPlayerImpl::Proxy is fixed so it doesn't have to track
+// WebDataSources. Proxy only has to track WebDataSources so it can call Abort()
+// on them at shutdown. Once cancellation is added to DataSource and pause
+// support in Demuxers cancel pending reads, Proxy shouldn't have to keep
+// a WebDataSource list or call Abort().
+typedef Callback1<WebDataSource*>::Type WebDataSourceBuildObserverHack;
+
} // namespace webkit_glue
#endif // WEBKIT_GLUE_MEDIA_WEB_DATA_SOURCE_H_
diff --git a/webkit/glue/media/web_data_source_factory.cc b/webkit/glue/media/web_data_source_factory.cc
new file mode 100644
index 0000000..f9036c7
--- /dev/null
+++ b/webkit/glue/media/web_data_source_factory.cc
@@ -0,0 +1,102 @@
+// Copyright (c) 2011 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 "webkit/glue/media/web_data_source_factory.h"
+
+#include "base/logging.h"
+
+namespace webkit_glue {
+
+class WebDataSourceFactory::BuildRequest
+ : public media::AsyncDataSourceFactoryBase::BuildRequest {
+ public:
+ BuildRequest(const std::string& url, BuildCallback* callback,
+ WebDataSource* data_source,
+ WebDataSourceBuildObserverHack* build_observer);
+ virtual ~BuildRequest();
+
+ protected:
+ // AsyncDataSourceFactoryBase::BuildRequest method.
+ virtual void DoStart();
+
+ private:
+ void InitDone(media::PipelineError error);
+
+ scoped_refptr<WebDataSource> data_source_;
+ WebDataSourceBuildObserverHack* build_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuildRequest);
+};
+
+WebDataSourceFactory::WebDataSourceFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ FactoryFunction factory_function,
+ WebDataSourceBuildObserverHack* build_observer)
+ : render_loop_(render_loop),
+ frame_(frame),
+ factory_function_(factory_function),
+ build_observer_(build_observer) {
+ DCHECK(render_loop_);
+ DCHECK(frame_);
+ DCHECK(factory_function_);
+}
+
+WebDataSourceFactory::~WebDataSourceFactory() {}
+
+media::DataSourceFactory* WebDataSourceFactory::Clone() const {
+ return new WebDataSourceFactory(render_loop_, frame_, factory_function_,
+ build_observer_);
+}
+
+bool WebDataSourceFactory::AllowRequests() const {
+ return true;
+}
+
+media::AsyncDataSourceFactoryBase::BuildRequest*
+WebDataSourceFactory::CreateRequest(const std::string& url,
+ BuildCallback* callback) {
+ WebDataSource* data_source = factory_function_(render_loop_, frame_);
+
+ return new WebDataSourceFactory::BuildRequest(url, callback, data_source,
+ build_observer_);
+}
+
+WebDataSourceFactory::BuildRequest::BuildRequest(
+ const std::string& url,
+ BuildCallback* callback,
+ WebDataSource* data_source,
+ WebDataSourceBuildObserverHack* build_observer)
+ : AsyncDataSourceFactoryBase::BuildRequest(url, callback),
+ data_source_(data_source),
+ build_observer_(build_observer) {
+}
+
+WebDataSourceFactory::BuildRequest::~BuildRequest() {
+ if (data_source_.get()) {
+ data_source_->CancelInitialize();
+ data_source_ = NULL;
+ }
+}
+
+void WebDataSourceFactory::BuildRequest::DoStart() {
+ data_source_->Initialize(url(), NewCallback(this, &BuildRequest::InitDone));
+}
+
+void WebDataSourceFactory::BuildRequest::InitDone(media::PipelineError error) {
+ scoped_refptr<WebDataSource> data_source;
+
+ data_source = (error == media::PIPELINE_OK) ? data_source_ : NULL;
+ data_source_ = NULL;
+
+ if (build_observer_ && data_source.get()) {
+ build_observer_->Run(data_source.get());
+ }
+
+ RequestComplete(error, data_source);
+ // Don't do anything after this line. This object is deleted by
+ // RequestComplete().
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/web_data_source_factory.h b/webkit/glue/media/web_data_source_factory.h
new file mode 100644
index 0000000..7163ef4
--- /dev/null
+++ b/webkit/glue/media/web_data_source_factory.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 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 WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_FACTORY_H_
+#define WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_FACTORY_H_
+
+#include "media/base/async_filter_factory_base.h"
+#include "webkit/glue/media/web_data_source.h"
+
+class MessageLoop;
+
+namespace WebKit {
+class WebFrame;
+}
+
+namespace webkit_glue {
+
+class WebDataSourceFactory : public media::AsyncDataSourceFactoryBase {
+ public:
+ typedef WebDataSource* (*FactoryFunction)(MessageLoop* render_loop,
+ WebKit::WebFrame* frame);
+
+ WebDataSourceFactory(MessageLoop* render_loop, WebKit::WebFrame* frame,
+ FactoryFunction factory_function,
+ WebDataSourceBuildObserverHack* build_observer);
+ virtual ~WebDataSourceFactory();
+
+ // DataSourceFactory method.
+ virtual media::DataSourceFactory* Clone() const;
+
+ protected:
+ // AsyncDataSourceFactoryBase methods.
+ virtual bool AllowRequests() const;
+ virtual AsyncDataSourceFactoryBase::BuildRequest* CreateRequest(
+ const std::string& url, BuildCallback* callback);
+
+ private:
+ class BuildRequest;
+
+ MessageLoop* render_loop_;
+ WebKit::WebFrame* frame_;
+ FactoryFunction factory_function_;
+ WebDataSourceBuildObserverHack* build_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDataSourceFactory);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_FACTORY_H_
diff --git a/webkit/glue/webkit_glue.gypi b/webkit/glue/webkit_glue.gypi
index 74cb61c..8c87826 100644
--- a/webkit/glue/webkit_glue.gypi
+++ b/webkit/glue/webkit_glue.gypi
@@ -368,6 +368,8 @@
'media/video_renderer_impl.h',
'media/web_data_source.cc',
'media/web_data_source.h',
+ 'media/web_data_source_factory.cc',
+ 'media/web_data_source_factory.h',
'media/web_video_renderer.h',
'alt_error_page_resource_fetcher.cc',
'alt_error_page_resource_fetcher.h',
diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc
index 3801406..2d5e6a6 100644
--- a/webkit/glue/webmediaplayer_impl.cc
+++ b/webkit/glue/webmediaplayer_impl.cc
@@ -8,6 +8,7 @@
#include "base/callback.h"
#include "base/command_line.h"
+#include "media/base/composite_data_source_factory.h"
#include "media/base/filter_collection.h"
#include "media/base/limits.h"
#include "media/base/media_format.h"
@@ -113,10 +114,10 @@ void WebMediaPlayerImpl::Proxy::SetVideoRenderer(
video_renderer_ = video_renderer;
}
-void WebMediaPlayerImpl::Proxy::AddDataSource(
- scoped_refptr<WebDataSource> data_source) {
- base::AutoLock auto_lock(data_sources_lock_);
- data_sources_.push_back(data_source);
+WebDataSourceBuildObserverHack* WebMediaPlayerImpl::Proxy::GetBuildObserver() {
+ if (!build_observer_.get())
+ build_observer_.reset(NewCallback(this, &Proxy::AddDataSource));
+ return build_observer_.get();
}
void WebMediaPlayerImpl::Proxy::Paint(skia::PlatformCanvas* canvas,
@@ -195,6 +196,11 @@ void WebMediaPlayerImpl::Proxy::NetworkEventCallback() {
&WebMediaPlayerImpl::Proxy::NetworkEventTask));
}
+void WebMediaPlayerImpl::Proxy::AddDataSource(WebDataSource* data_source) {
+ base::AutoLock auto_lock(data_sources_lock_);
+ data_sources_.push_back(make_scoped_refptr(data_source));
+}
+
void WebMediaPlayerImpl::Proxy::RepaintTask() {
DCHECK(MessageLoop::current() == render_loop_);
{
@@ -309,22 +315,28 @@ bool WebMediaPlayerImpl::Initialize(
&WebMediaPlayerImpl::Proxy::NetworkEventCallback));
// A simple data source that keeps all data in memory.
- scoped_refptr<SimpleDataSource> simple_data_source(
- new SimpleDataSource(MessageLoop::current(), frame));
+ scoped_ptr<media::DataSourceFactory> simple_data_source_factory(
+ SimpleDataSource::CreateFactory(MessageLoop::current(), frame,
+ proxy_->GetBuildObserver()));
// A sophisticated data source that does memory caching.
- scoped_refptr<BufferedDataSource> buffered_data_source(
- new BufferedDataSource(MessageLoop::current(), frame));
- proxy_->AddDataSource(buffered_data_source);
+ scoped_ptr<media::DataSourceFactory> buffered_data_source_factory(
+ BufferedDataSource::CreateFactory(MessageLoop::current(), frame,
+ proxy_->GetBuildObserver()));
+
+ scoped_ptr<media::CompositeDataSourceFactory> data_source_factory(
+ new media::CompositeDataSourceFactory());
if (use_simple_data_source) {
- filter_collection_->AddDataSource(simple_data_source);
- filter_collection_->AddDataSource(buffered_data_source);
+ data_source_factory->AddFactory(simple_data_source_factory.release());
+ data_source_factory->AddFactory(buffered_data_source_factory.release());
} else {
- filter_collection_->AddDataSource(buffered_data_source);
- filter_collection_->AddDataSource(simple_data_source);
+ data_source_factory->AddFactory(buffered_data_source_factory.release());
+ data_source_factory->AddFactory(simple_data_source_factory.release());
}
+ filter_collection_->SetDataSourceFactory(data_source_factory.release());
+
// Add in the default filter factories.
filter_collection_->AddDemuxer(new media::FFmpegDemuxer(
message_loop_factory_->GetMessageLoop("DemuxThread")));
@@ -797,6 +809,7 @@ void WebMediaPlayerImpl::OnPipelineError() {
case media::DEMUXER_ERROR_COULD_NOT_PARSE:
case media::DEMUXER_ERROR_NO_SUPPORTED_STREAMS:
case media::DEMUXER_ERROR_COULD_NOT_CREATE_THREAD:
+ case media::DATASOURCE_ERROR_URL_NOT_SUPPORTED:
// Format error.
SetNetworkState(WebMediaPlayer::FormatError);
break;
diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h
index cadf4d7..e7acf27 100644
--- a/webkit/glue/webmediaplayer_impl.h
+++ b/webkit/glue/webmediaplayer_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 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.
@@ -67,6 +67,7 @@
#include "third_party/WebKit/Source/WebKit/chromium/public/WebMediaPlayerClient.h"
#include "ui/gfx/rect.h"
#include "ui/gfx/size.h"
+#include "webkit/glue/media/web_data_source.h"
class GURL;
@@ -77,7 +78,6 @@ class WebFrame;
namespace webkit_glue {
class MediaResourceLoaderBridgeFactory;
-class WebDataSource;
class WebVideoRenderer;
class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
@@ -99,7 +99,7 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
// Methods for Filter -> WebMediaPlayerImpl communication.
void Repaint();
void SetVideoRenderer(scoped_refptr<WebVideoRenderer> video_renderer);
- void AddDataSource(scoped_refptr<WebDataSource> data_source);
+ WebDataSourceBuildObserverHack* GetBuildObserver();
// Methods for WebMediaPlayerImpl -> Filter communication.
void Paint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect);
@@ -125,6 +125,9 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
virtual ~Proxy();
+ // Adds a data source to data_sources_.
+ void AddDataSource(WebDataSource* data_source);
+
// Invoke |webmediaplayer_| to perform a repaint.
void RepaintTask();
@@ -150,6 +153,7 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
base::Lock data_sources_lock_;
typedef std::list<scoped_refptr<WebDataSource> > DataSourceList;
DataSourceList data_sources_;
+ scoped_ptr<WebDataSourceBuildObserverHack> build_observer_;
scoped_refptr<WebVideoRenderer> video_renderer_;