summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-16 01:26:40 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-16 01:26:40 +0000
commitcef6492128ea70329523cbcf5d4c204660090857 (patch)
tree7386081bed04bb489d9e19fa64f8090ff4bfb6a4 /media
parentb02c01713a168403f65d5193ad6934d761c7fbaa (diff)
downloadchromium_src-cef6492128ea70329523cbcf5d4c204660090857.zip
chromium_src-cef6492128ea70329523cbcf5d4c204660090857.tar.gz
chromium_src-cef6492128ea70329523cbcf5d4c204660090857.tar.bz2
Adding callback support to media filter Initialize() and Seek().
Also includes unit tests for AudioRendererBase and VideoRendererBase. I had to rollback my first attempt at this change. Original review: http://codereview.chromium.org/155469 BUG=16014,16031 TEST=media_unittests, layout tests Review URL: http://codereview.chromium.org/155608 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@20836 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/base/filter_host.h5
-rw-r--r--media/base/filter_host_impl.cc4
-rw-r--r--media/base/filter_host_impl.h1
-rw-r--r--media/base/filters.h44
-rw-r--r--media/base/mock_filters.cc18
-rw-r--r--media/base/mock_filters.h97
-rw-r--r--media/base/pipeline_impl.cc34
-rw-r--r--media/base/pipeline_impl.h9
-rw-r--r--media/base/pipeline_impl_unittest.cc71
-rw-r--r--media/filters/audio_renderer_base.cc16
-rw-r--r--media/filters/audio_renderer_base.h7
-rw-r--r--media/filters/audio_renderer_base_unittest.cc134
-rw-r--r--media/filters/decoder_base.h25
-rw-r--r--media/filters/ffmpeg_demuxer.cc26
-rw-r--r--media/filters/ffmpeg_demuxer.h8
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc30
-rw-r--r--media/filters/ffmpeg_video_decoder_unittest.cc22
-rw-r--r--media/filters/file_data_source.cc10
-rw-r--r--media/filters/file_data_source.h3
-rw-r--r--media/filters/file_data_source_unittest.cc21
-rw-r--r--media/filters/video_renderer_base.cc44
-rw-r--r--media/filters/video_renderer_base.h8
-rw-r--r--media/filters/video_renderer_base_unittest.cc215
-rw-r--r--media/media.gyp3
24 files changed, 696 insertions, 159 deletions
diff --git a/media/base/filter_host.h b/media/base/filter_host.h
index 4907df2a..982b3e5 100644
--- a/media/base/filter_host.h
+++ b/media/base/filter_host.h
@@ -24,11 +24,6 @@ namespace media {
class FilterHost {
public:
- // Filters must call this method to indicate that their initialization is
- // complete. They may call this from within their Initialize() method or may
- // choose call it after processing some data.
- virtual void InitializationComplete() = 0;
-
// Stops execution of the pipeline due to a fatal error. Do not call this
// method with PIPELINE_OK or PIPELINE_STOPPING (used internally by pipeline).
virtual void Error(PipelineError error) = 0;
diff --git a/media/base/filter_host_impl.cc b/media/base/filter_host_impl.cc
index 09f1791..4192afd 100644
--- a/media/base/filter_host_impl.cc
+++ b/media/base/filter_host_impl.cc
@@ -6,10 +6,6 @@
namespace media {
-void FilterHostImpl::InitializationComplete() {
- pipeline_internal_->InitializationComplete(this);
-}
-
void FilterHostImpl::Error(PipelineError error) {
pipeline_internal_->Error(error);
}
diff --git a/media/base/filter_host_impl.h b/media/base/filter_host_impl.h
index 71e2417..cb33a48 100644
--- a/media/base/filter_host_impl.h
+++ b/media/base/filter_host_impl.h
@@ -16,7 +16,6 @@ namespace media {
class FilterHostImpl : public FilterHost {
public:
// FilterHost interface.
- virtual void InitializationComplete();
virtual void Error(PipelineError error);
virtual base::TimeDelta GetTime() const;
virtual void SetTime(base::TimeDelta time);
diff --git a/media/base/filters.h b/media/base/filters.h
index b892961..84df0966 100644
--- a/media/base/filters.h
+++ b/media/base/filters.h
@@ -53,6 +53,8 @@ enum FilterType {
FILTER_VIDEO_RENDERER
};
+// Used for completing asynchronous methods.
+typedef Callback0::Type FilterCallback;
class MediaFilter : public base::RefCountedThreadSafe<MediaFilter> {
public:
@@ -94,9 +96,14 @@ class MediaFilter : public base::RefCountedThreadSafe<MediaFilter> {
// method if they need to respond to this call.
virtual void SetPlaybackRate(float playback_rate) {}
- // The pipeline is seeking to the specified time. Filters may implement
- // this method if they need to respond to this call.
- virtual void Seek(base::TimeDelta time) {}
+ // Carry out any actions required to seek to the given time, executing the
+ // callback upon completion.
+ virtual void Seek(base::TimeDelta time, FilterCallback* callback) {
+ scoped_ptr<FilterCallback> seek_callback(callback);
+ if (seek_callback.get()) {
+ seek_callback->Run();
+ }
+ }
protected:
// Only allow scoped_refptr<> to delete filters.
@@ -128,8 +135,9 @@ class DataSource : public MediaFilter {
static const size_t kReadError = static_cast<size_t>(-1);
- // Initializes this filter, returns true if successful, false otherwise.
- virtual bool Initialize(const std::string& url) = 0;
+ // Initialize a DataSource for the given URL, executing the callback upon
+ // completion.
+ virtual void Initialize(const std::string& url, FilterCallback* callback) = 0;
// Returns the MediaFormat for this filter.
virtual const MediaFormat& media_format() = 0;
@@ -166,8 +174,10 @@ class Demuxer : public MediaFilter {
mime_type == mime_type::kApplicationOctetStream);
}
- // Initializes this filter, returns true if successful, false otherwise.
- virtual bool Initialize(DataSource* data_source) = 0;
+ // Initialize a Demuxer with the given DataSource, executing the callback upon
+ // completion.
+ virtual void Initialize(DataSource* data_source,
+ FilterCallback* callback) = 0;
// Returns the number of streams available
virtual size_t GetNumberOfStreams() = 0;
@@ -223,8 +233,9 @@ class VideoDecoder : public MediaFilter {
return mime_type::kMajorTypeVideo;
}
- // Initializes this filter, returns true if successful, false otherwise.
- virtual bool Initialize(DemuxerStream* demuxer_stream) = 0;
+ // Initialize a VideoDecoder with the given DemuxerStream, executing the
+ // callback upon completion.
+ virtual void Initialize(DemuxerStream* stream, FilterCallback* callback) = 0;
// Returns the MediaFormat for this filter.
virtual const MediaFormat& media_format() = 0;
@@ -246,8 +257,9 @@ class AudioDecoder : public MediaFilter {
return mime_type::kMajorTypeAudio;
}
- // Initializes this filter, returns true if successful, false otherwise.
- virtual bool Initialize(DemuxerStream* demuxer_stream) = 0;
+ // Initialize a AudioDecoder with the given DemuxerStream, executing the
+ // callback upon completion.
+ virtual void Initialize(DemuxerStream* stream, FilterCallback* callback) = 0;
// Returns the MediaFormat for this filter.
virtual const MediaFormat& media_format() = 0;
@@ -269,8 +281,9 @@ class VideoRenderer : public MediaFilter {
return mime_type::kMajorTypeVideo;
}
- // Initializes this filter, returns true if successful, false otherwise.
- virtual bool Initialize(VideoDecoder* decoder) = 0;
+ // Initialize a VideoRenderer with the given VideoDecoder, executing the
+ // callback upon completion.
+ virtual void Initialize(VideoDecoder* decoder, FilterCallback* callback) = 0;
};
@@ -284,8 +297,9 @@ class AudioRenderer : public MediaFilter {
return mime_type::kMajorTypeAudio;
}
- // Initializes this filter, returns true if successful, false otherwise.
- virtual bool Initialize(AudioDecoder* decoder) = 0;
+ // Initialize a AudioRenderer with the given AudioDecoder, executing the
+ // callback upon completion.
+ virtual void Initialize(AudioDecoder* decoder, FilterCallback* callback) = 0;
// Sets the output volume.
virtual void SetVolume(float volume) = 0;
diff --git a/media/base/mock_filters.cc b/media/base/mock_filters.cc
new file mode 100644
index 0000000..7b854fc
--- /dev/null
+++ b/media/base/mock_filters.cc
@@ -0,0 +1,18 @@
+// Copyright (c) 2009 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/mock_filters.h"
+
+namespace media {
+
+void RunFilterCallback(::testing::Unused, FilterCallback* callback) {
+ callback->Run();
+ delete callback;
+}
+
+void DestroyFilterCallback(::testing::Unused, FilterCallback* callback) {
+ delete callback;
+}
+
+} // namespace media
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index bffba6f..6d8c8a9 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -40,6 +40,52 @@ class Destroyable : public MockClass {
DISALLOW_COPY_AND_ASSIGN(Destroyable);
};
+// Helper class used to test that callbacks are executed. It is recommend you
+// combine this class with StrictMock<> to verify that the callback is executed.
+// You can reuse the same instance of a MockFilterCallback many times since
+// gmock will track the number of times the methods are executed.
+class MockFilterCallback {
+ public:
+ MockFilterCallback() {}
+ virtual ~MockFilterCallback() {}
+
+ MOCK_METHOD0(OnCallbackDestroyed, void());
+ MOCK_METHOD0(OnFilterCallback, void());
+
+ // Helper method to create a new callback for this mock. The callback will
+ // call OnFilterCallback() when executed and OnCallbackDestroyed() when
+ // destroyed. Clients should use NiceMock<> or StrictMock<> depending on the
+ // test.
+ FilterCallback* NewCallback() {
+ return new CallbackImpl(this);
+ }
+
+ private:
+ // Private implementation of CallbackRunner used to trigger expectations on
+ // MockFilterCallback.
+ class CallbackImpl : public CallbackRunner<Tuple0> {
+ public:
+ CallbackImpl(MockFilterCallback* mock_callback)
+ : mock_callback_(mock_callback) {
+ }
+
+ virtual ~CallbackImpl() {
+ mock_callback_->OnCallbackDestroyed();
+ }
+
+ virtual void RunWithParams(const Tuple0& params) {
+ mock_callback_->OnFilterCallback();
+ }
+
+ private:
+ MockFilterCallback* mock_callback_;
+
+ DISALLOW_COPY_AND_ASSIGN(CallbackImpl);
+ };
+
+ DISALLOW_COPY_AND_ASSIGN(MockFilterCallback);
+};
+
class MockDataSource : public DataSource {
public:
MockDataSource() {}
@@ -47,10 +93,11 @@ class MockDataSource : public DataSource {
// MediaFilter implementation.
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
- MOCK_METHOD1(Seek, void(base::TimeDelta time));
+ MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback));
// DataSource implementation.
- MOCK_METHOD1(Initialize, bool(const std::string& url));
+ MOCK_METHOD2(Initialize, void(const std::string& url,
+ FilterCallback* callback));
const MediaFormat& media_format() { return media_format_; }
MOCK_METHOD2(Read, size_t(uint8* data, size_t size));
MOCK_METHOD1(GetPosition, bool(int64* position_out));
@@ -74,10 +121,11 @@ class MockDemuxer : public Demuxer {
// MediaFilter implementation.
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
- MOCK_METHOD1(Seek, void(base::TimeDelta time));
+ MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback));
// Demuxer implementation.
- MOCK_METHOD1(Initialize, bool(DataSource* data_source));
+ MOCK_METHOD2(Initialize, void(DataSource* data_source,
+ FilterCallback* callback));
MOCK_METHOD0(GetNumberOfStreams, size_t());
MOCK_METHOD1(GetStream, scoped_refptr<DemuxerStream>(int stream_id));
@@ -116,13 +164,21 @@ class MockVideoDecoder : public VideoDecoder {
public:
MockVideoDecoder() {}
+ // Sets the essential media format keys for this decoder.
+ MockVideoDecoder(const std::string& mime_type, int width, int height) {
+ media_format_.SetAsString(MediaFormat::kMimeType, mime_type);
+ media_format_.SetAsInteger(MediaFormat::kWidth, width);
+ media_format_.SetAsInteger(MediaFormat::kHeight, height);
+ }
+
// MediaFilter implementation.
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
- MOCK_METHOD1(Seek, void(base::TimeDelta time));
+ MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback));
// VideoDecoder implementation.
- MOCK_METHOD1(Initialize, bool(DemuxerStream* demuxer_stream));
+ MOCK_METHOD2(Initialize, void(DemuxerStream* stream,
+ FilterCallback* callback));
const MediaFormat& media_format() { return media_format_; }
MOCK_METHOD1(Read, void(Callback1<VideoFrame*>::Type* read_callback));
@@ -142,10 +198,11 @@ class MockAudioDecoder : public AudioDecoder {
// MediaFilter implementation.
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
- MOCK_METHOD1(Seek, void(base::TimeDelta time));
+ MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback));
// AudioDecoder implementation.
- MOCK_METHOD1(Initialize, bool(DemuxerStream* demuxer_stream));
+ MOCK_METHOD2(Initialize, void(DemuxerStream* stream,
+ FilterCallback* callback));
const MediaFormat& media_format() { return media_format_; }
MOCK_METHOD1(Read, void(Callback1<Buffer*>::Type* read_callback));
@@ -165,10 +222,11 @@ class MockVideoRenderer : public VideoRenderer {
// MediaFilter implementation.
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
- MOCK_METHOD1(Seek, void(base::TimeDelta time));
+ MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback));
// VideoRenderer implementation.
- MOCK_METHOD1(Initialize, bool(VideoDecoder* decoder));
+ MOCK_METHOD2(Initialize, void(VideoDecoder* decoder,
+ FilterCallback* callback));
protected:
virtual ~MockVideoRenderer() {}
@@ -184,10 +242,11 @@ class MockAudioRenderer : public AudioRenderer {
// MediaFilter implementation.
MOCK_METHOD0(Stop, void());
MOCK_METHOD1(SetPlaybackRate, void(float playback_rate));
- MOCK_METHOD1(Seek, void(base::TimeDelta time));
+ MOCK_METHOD2(Seek, void(base::TimeDelta time, FilterCallback* callback));
// AudioRenderer implementation.
- MOCK_METHOD1(Initialize, bool(AudioDecoder* decoder));
+ MOCK_METHOD2(Initialize, void(AudioDecoder* decoder,
+ FilterCallback* callback));
MOCK_METHOD1(SetVolume, void(float volume));
protected:
@@ -263,11 +322,15 @@ class MockFilterFactory : public FilterFactory {
DISALLOW_COPY_AND_ASSIGN(MockFilterFactory);
};
-// Helper gmock action that calls InitializationComplete() on behalf of the
-// provided filter.
-ACTION_P(InitializationComplete, filter) {
- filter->host()->InitializationComplete();
-}
+// Helper gmock function that immediately executes and destroys the
+// FilterCallback on behalf of the provided filter. Can be used when mocking
+// the Initialize() and Seek() methods.
+void RunFilterCallback(::testing::Unused, FilterCallback* callback);
+
+// Helper gmock function that immediately destroys the FilterCallback on behalf
+// of the provided filter. Can be used when mocking the Initialize() and Seek()
+// methods.
+void DestroyFilterCallback(::testing::Unused, FilterCallback* callback);
// Helper gmock action that calls Error() on behalf of the provided filter.
ACTION_P2(Error, filter, error) {
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index f021ad3..7ef051d 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -328,15 +328,6 @@ void PipelineInternal::VolumeChanged(float volume) {
NewRunnableMethod(this, &PipelineInternal::VolumeChangedTask, volume));
}
-// Called from any thread.
-void PipelineInternal::InitializationComplete(FilterHostImpl* host) {
- if (IsPipelineOk()) {
- // Continue the initialize task by proceeding to the next stage.
- message_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this, &PipelineInternal::InitializeTask));
- }
-}
-
// Called from any thread. Updates the pipeline time.
void PipelineInternal::SetTime(base::TimeDelta time) {
// TODO(scherkus): why not post a task?
@@ -350,6 +341,19 @@ void PipelineInternal::Error(PipelineError error) {
NewRunnableMethod(this, &PipelineInternal::ErrorTask, error));
}
+// Called from any thread.
+void PipelineInternal::OnFilterInitialize() {
+ // Continue the initialize task by proceeding to the next stage.
+ message_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &PipelineInternal::InitializeTask));
+}
+
+// Called from any thread.
+void PipelineInternal::OnFilterSeek() {
+ // TODO(scherkus): have PipelineInternal wait to receive replies from every
+ // filter before calling the client's |seek_callback_|.
+}
+
void PipelineInternal::StartTask(FilterFactory* filter_factory,
const std::string& url,
PipelineCallback* start_callback) {
@@ -383,8 +387,8 @@ void PipelineInternal::StartTask(FilterFactory* filter_factory,
void PipelineInternal::InitializeTask() {
DCHECK_EQ(MessageLoop::current(), message_loop_);
- // If we have received the stop signal, return immediately.
- if (state_ == kStopped)
+ // If we have received the stop or error signal, return immediately.
+ if (state_ == kStopped || state_ == kError)
return;
DCHECK(state_ == kCreated || IsPipelineInitializing());
@@ -551,7 +555,8 @@ void PipelineInternal::SeekTask(base::TimeDelta time,
for (FilterHostVector::iterator iter = filter_hosts_.begin();
iter != filter_hosts_.end();
++iter) {
- (*iter)->media_filter()->Seek(time);
+ (*iter)->media_filter()->Seek(time,
+ NewCallback(this, &PipelineInternal::OnFilterSeek));
}
// TODO(hclam): we should set the time when the above seek operations were all
@@ -602,9 +607,8 @@ void PipelineInternal::CreateFilter(FilterFactory* filter_factory,
filter_hosts_.push_back(host.release());
// Now initialize the filter.
- if (!filter->Initialize(source)) {
- Error(PIPELINE_ERROR_INITIALIZATION_FAILED);
- }
+ filter->Initialize(source,
+ NewCallback(this, &PipelineInternal::OnFilterInitialize));
}
void PipelineInternal::CreateDataSource() {
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index 911d253..e9f44d5 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -191,11 +191,6 @@ class PipelineInternal : public base::RefCountedThreadSafe<PipelineInternal> {
// Methods called by a FilterHostImpl object. These methods may be called
// on any thread, either the pipeline's thread or any other.
- // When a filter calls it's FilterHost, the filter host calls back to the
- // pipeline thread. If the pipeline thread is running a nested message loop
- // then it will be exited.
- void InitializationComplete(FilterHostImpl* host);
-
// Sets the pipeline time and schedules a task to call back to any filters
// that have registered a time update callback.
void SetTime(base::TimeDelta time);
@@ -245,6 +240,10 @@ class PipelineInternal : public base::RefCountedThreadSafe<PipelineInternal> {
state_ == kInitVideoRenderer;
}
+ // Callback executed by filters upon completing initialization and seeking.
+ void OnFilterInitialize();
+ void OnFilterSeek();
+
// The following "task" methods correspond to the public methods, but these
// methods are run as the result of posting a task to the PipelineInternal's
// message loop.
diff --git a/media/base/pipeline_impl_unittest.cc b/media/base/pipeline_impl_unittest.cc
index b673cfb..d6bd285 100644
--- a/media/base/pipeline_impl_unittest.cc
+++ b/media/base/pipeline_impl_unittest.cc
@@ -14,7 +14,9 @@
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::DoAll;
+using ::testing::Invoke;
using ::testing::Mock;
+using ::testing::NotNull;
using ::testing::Return;
using ::testing::StrictMock;
@@ -63,9 +65,8 @@ class PipelineImplTest : public ::testing::Test {
protected:
// Sets up expectations to allow the data source to initialize.
void InitializeDataSource() {
- EXPECT_CALL(*mocks_->data_source(), Initialize(""))
- .WillOnce(DoAll(InitializationComplete(mocks_->data_source()),
- Return(true)));
+ EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->data_source(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->data_source(), Stop());
}
@@ -73,9 +74,9 @@ class PipelineImplTest : public ::testing::Test {
// Sets up expectations to allow the demuxer to initialize.
typedef std::vector<MockDemuxerStream*> MockDemuxerStreamVector;
void InitializeDemuxer(MockDemuxerStreamVector* streams) {
- EXPECT_CALL(*mocks_->demuxer(), Initialize(mocks_->data_source()))
- .WillOnce(DoAll(InitializationComplete(mocks_->demuxer()),
- Return(true)));
+ EXPECT_CALL(*mocks_->demuxer(),
+ Initialize(mocks_->data_source(), NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->demuxer(), GetNumberOfStreams())
.WillRepeatedly(Return(streams->size()));
EXPECT_CALL(*mocks_->demuxer(), SetPlaybackRate(0.0f));
@@ -91,36 +92,34 @@ class PipelineImplTest : public ::testing::Test {
// Sets up expectations to allow the video decoder to initialize.
void InitializeVideoDecoder(MockDemuxerStream* stream) {
- EXPECT_CALL(*mocks_->video_decoder(), Initialize(stream))
- .WillOnce(DoAll(InitializationComplete(mocks_->video_decoder()),
- Return(true)));
+ EXPECT_CALL(*mocks_->video_decoder(), Initialize(stream, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_decoder(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->video_decoder(), Stop());
}
// Sets up expectations to allow the audio decoder to initialize.
void InitializeAudioDecoder(MockDemuxerStream* stream) {
- EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream))
- .WillOnce(DoAll(InitializationComplete(mocks_->audio_decoder()),
- Return(true)));
+ EXPECT_CALL(*mocks_->audio_decoder(), Initialize(stream, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_decoder(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->audio_decoder(), Stop());
}
// Sets up expectations to allow the video renderer to initialize.
void InitializeVideoRenderer() {
- EXPECT_CALL(*mocks_->video_renderer(), Initialize(mocks_->video_decoder()))
- .WillOnce(DoAll(InitializationComplete(mocks_->video_renderer()),
- Return(true)));
+ EXPECT_CALL(*mocks_->video_renderer(),
+ Initialize(mocks_->video_decoder(), NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->video_renderer(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->video_renderer(), Stop());
}
// Sets up expectations to allow the audio renderer to initialize.
void InitializeAudioRenderer() {
- EXPECT_CALL(*mocks_->audio_renderer(), Initialize(mocks_->audio_decoder()))
- .WillOnce(DoAll(InitializationComplete(mocks_->audio_renderer()),
- Return(true)));
+ EXPECT_CALL(*mocks_->audio_renderer(),
+ Initialize(mocks_->audio_decoder(), NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->audio_renderer(), SetPlaybackRate(0.0f));
EXPECT_CALL(*mocks_->audio_renderer(), SetVolume(1.0f));
EXPECT_CALL(*mocks_->audio_renderer(), Stop());
@@ -201,8 +200,8 @@ TEST_F(PipelineImplTest, NotStarted) {
}
TEST_F(PipelineImplTest, NeverInitializes) {
- EXPECT_CALL(*mocks_->data_source(), Initialize(""))
- .WillOnce(Return(true));
+ EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
+ .WillOnce(Invoke(&DestroyFilterCallback));
EXPECT_CALL(*mocks_->data_source(), Stop());
// This test hangs during initialization by never calling
@@ -233,10 +232,10 @@ TEST_F(PipelineImplTest, RequiredFilterMissing) {
}
TEST_F(PipelineImplTest, URLNotFound) {
- EXPECT_CALL(*mocks_->data_source(), Initialize(""))
+ EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
.WillOnce(DoAll(Error(mocks_->data_source(),
PIPELINE_ERROR_URL_NOT_FOUND),
- Return(false)));
+ Invoke(&RunFilterCallback)));
EXPECT_CALL(*mocks_->data_source(), Stop());
InitializePipeline();
@@ -247,14 +246,12 @@ TEST_F(PipelineImplTest, URLNotFound) {
TEST_F(PipelineImplTest, NoStreams) {
// Manually set these expecations because SetPlaybackRate() is not called if
// we cannot fully initialize the pipeline.
- EXPECT_CALL(*mocks_->data_source(), Initialize(""))
- .WillOnce(DoAll(InitializationComplete(mocks_->data_source()),
- Return(true)));
+ EXPECT_CALL(*mocks_->data_source(), Initialize("", NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->data_source(), Stop());
- EXPECT_CALL(*mocks_->demuxer(), Initialize(mocks_->data_source()))
- .WillOnce(DoAll(InitializationComplete(mocks_->demuxer()),
- Return(true)));
+ EXPECT_CALL(*mocks_->demuxer(), Initialize(mocks_->data_source(), NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
EXPECT_CALL(*mocks_->demuxer(), GetNumberOfStreams())
.WillRepeatedly(Return(0));
EXPECT_CALL(*mocks_->demuxer(), Stop());
@@ -341,12 +338,18 @@ TEST_F(PipelineImplTest, Seek) {
// Every filter should receive a call to Seek().
base::TimeDelta expected = base::TimeDelta::FromSeconds(2000);
- EXPECT_CALL(*mocks_->data_source(), Seek(expected));
- EXPECT_CALL(*mocks_->demuxer(), Seek(expected));
- EXPECT_CALL(*mocks_->audio_decoder(), Seek(expected));
- EXPECT_CALL(*mocks_->audio_renderer(), Seek(expected));
- EXPECT_CALL(*mocks_->video_decoder(), Seek(expected));
- EXPECT_CALL(*mocks_->video_renderer(), Seek(expected));
+ EXPECT_CALL(*mocks_->data_source(), Seek(expected, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
+ EXPECT_CALL(*mocks_->demuxer(), Seek(expected, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
+ EXPECT_CALL(*mocks_->audio_decoder(), Seek(expected, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
+ EXPECT_CALL(*mocks_->audio_renderer(), Seek(expected, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
+ EXPECT_CALL(*mocks_->video_decoder(), Seek(expected, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
+ EXPECT_CALL(*mocks_->video_renderer(), Seek(expected, NotNull()))
+ .WillOnce(Invoke(&RunFilterCallback));
// We expect a successful seek callback.
EXPECT_CALL(callbacks_, OnSeek());
diff --git a/media/filters/audio_renderer_base.cc b/media/filters/audio_renderer_base.cc
index ba52fe0..d0bad99 100644
--- a/media/filters/audio_renderer_base.cc
+++ b/media/filters/audio_renderer_base.cc
@@ -38,7 +38,7 @@ void AudioRendererBase::Stop() {
stopped_ = true;
}
-void AudioRendererBase::Seek(base::TimeDelta time) {
+void AudioRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) {
AutoLock auto_lock(lock_);
last_fill_buffer_time_ = base::TimeDelta();
@@ -53,9 +53,12 @@ void AudioRendererBase::Seek(base::TimeDelta time) {
}
}
-bool AudioRendererBase::Initialize(AudioDecoder* decoder) {
+void AudioRendererBase::Initialize(AudioDecoder* decoder,
+ FilterCallback* callback) {
DCHECK(decoder);
+ DCHECK(callback);
decoder_ = decoder;
+ initialize_callback_.reset(callback);
// Schedule our initial reads.
for (size_t i = 0; i < max_queue_size_; ++i) {
@@ -63,7 +66,11 @@ bool AudioRendererBase::Initialize(AudioDecoder* decoder) {
}
// Defer initialization until all scheduled reads have completed.
- return OnInitialize(decoder_->media_format());
+ if (!OnInitialize(decoder_->media_format())) {
+ host()->Error(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ initialize_callback_->Run();
+ initialize_callback_.reset();
+ }
}
void AudioRendererBase::OnReadComplete(Buffer* buffer_in) {
@@ -92,8 +99,9 @@ void AudioRendererBase::OnReadComplete(Buffer* buffer_in) {
host()->Error(PIPELINE_ERROR_NO_DATA);
} else {
initialized_ = true;
- host()->InitializationComplete();
}
+ initialize_callback_->Run();
+ initialize_callback_.reset();
}
}
diff --git a/media/filters/audio_renderer_base.h b/media/filters/audio_renderer_base.h
index e3ec686..3d31474 100644
--- a/media/filters/audio_renderer_base.h
+++ b/media/filters/audio_renderer_base.h
@@ -32,10 +32,10 @@ class AudioRendererBase : public AudioRenderer {
// MediaFilter implementation.
virtual void Stop();
- virtual void Seek(base::TimeDelta time);
+ virtual void Seek(base::TimeDelta time, FilterCallback* callback);
// AudioRenderer implementation.
- virtual bool Initialize(AudioDecoder* decoder);
+ virtual void Initialize(AudioDecoder* decoder, FilterCallback* callback);
protected:
// The default maximum size of the queue.
@@ -116,6 +116,9 @@ class AudioRendererBase : public AudioRenderer {
// TODO(ralphl): Update this value after seeking.
base::TimeDelta last_fill_buffer_time_;
+ // Filter callbacks.
+ scoped_ptr<FilterCallback> initialize_callback_;
+
DISALLOW_COPY_AND_ASSIGN(AudioRendererBase);
};
diff --git a/media/filters/audio_renderer_base_unittest.cc b/media/filters/audio_renderer_base_unittest.cc
new file mode 100644
index 0000000..ed0d138
--- /dev/null
+++ b/media/filters/audio_renderer_base_unittest.cc
@@ -0,0 +1,134 @@
+// Copyright (c) 2009 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 "base/stl_util-inl.h"
+#include "media/base/data_buffer.h"
+#include "media/base/mock_filter_host.h"
+#include "media/base/mock_filters.h"
+#include "media/filters/audio_renderer_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace media {
+
+// Mocked subclass of AudioRendererBase for testing purposes.
+class MockAudioRendererBase : public AudioRendererBase {
+ public:
+ MockAudioRendererBase(size_t max_queue_size)
+ : AudioRendererBase(max_queue_size) {}
+ virtual ~MockAudioRendererBase() {}
+
+ // AudioRenderer implementation.
+ MOCK_METHOD1(SetVolume, void(float volume));
+
+ // AudioRendererBase implementation.
+ MOCK_METHOD1(OnInitialize, bool(const MediaFormat& media_format));
+ MOCK_METHOD0(OnStop, void());
+
+ // Used for verifying check points during tests.
+ MOCK_METHOD1(CheckPoint, void(int id));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockAudioRendererBase);
+};
+
+class AudioRendererBaseTest : public ::testing::Test {
+ public:
+ AudioRendererBaseTest()
+ : renderer_(new MockAudioRendererBase(kMaxQueueSize)),
+ decoder_(new MockAudioDecoder()) {
+ renderer_->set_host(&host_);
+
+ // Queue all reads from the decoder.
+ EXPECT_CALL(*decoder_, Read(NotNull()))
+ .WillRepeatedly(Invoke(this, &AudioRendererBaseTest::EnqueueCallback));
+ }
+
+ virtual ~AudioRendererBaseTest() {
+ STLDeleteElements(&read_queue_);
+
+ // Expect a call into the subclass.
+ EXPECT_CALL(*renderer_, OnStop());
+ renderer_->Stop();
+ }
+
+ protected:
+ static const size_t kMaxQueueSize;
+
+ // Fixture members.
+ scoped_refptr<MockAudioRendererBase> renderer_;
+ scoped_refptr<MockAudioDecoder> decoder_;
+ StrictMock<MockFilterHost> host_;
+ StrictMock<MockFilterCallback> callback_;
+
+ // Receives asynchronous read requests sent to |decoder_|.
+ std::deque<Callback1<Buffer*>::Type*> read_queue_;
+
+ private:
+ void EnqueueCallback(Callback1<Buffer*>::Type* callback) {
+ read_queue_.push_back(callback);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(AudioRendererBaseTest);
+};
+
+const size_t AudioRendererBaseTest::kMaxQueueSize = 16u;
+
+TEST_F(AudioRendererBaseTest, Initialize_Failed) {
+ InSequence s;
+
+ // Our subclass will fail when asked to initialize.
+ EXPECT_CALL(*renderer_, OnInitialize(_))
+ .WillOnce(Return(false));
+
+ // We expect to receive an error.
+ EXPECT_CALL(host_, Error(PIPELINE_ERROR_INITIALIZATION_FAILED));
+
+ // We expect our callback to be executed.
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ // Initialize, we expect to get a bunch of read requests.
+ renderer_->Initialize(decoder_, callback_.NewCallback());
+ EXPECT_EQ(kMaxQueueSize, read_queue_.size());
+}
+
+TEST_F(AudioRendererBaseTest, Initialize_Successful) {
+ InSequence s;
+
+ // Then our subclass will be asked to initialize.
+ EXPECT_CALL(*renderer_, OnInitialize(_))
+ .WillOnce(Return(true));
+
+ // Set up a check point to verify that the callback hasn't been executed yet.
+ EXPECT_CALL(*renderer_, CheckPoint(0));
+
+ // After finishing preroll, we expect our callback to be executed.
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ // Initialize, we expect to get a bunch of read requests.
+ renderer_->Initialize(decoder_, callback_.NewCallback());
+ EXPECT_EQ(kMaxQueueSize, read_queue_.size());
+
+ // Verify our callback hasn't been executed yet.
+ renderer_->CheckPoint(0);
+
+ // Now satisfy the read requests. Our callback should be executed after
+ // exiting this loop.
+ while (!read_queue_.empty()) {
+ scoped_refptr<DataBuffer> buffer = new DataBuffer(1);
+ read_queue_.front()->Run(buffer);
+ delete read_queue_.front();
+ read_queue_.pop_front();
+ }
+}
+
+} // namespace media
diff --git a/media/filters/decoder_base.h b/media/filters/decoder_base.h
index 775d3f9..3e1ba52c 100644
--- a/media/filters/decoder_base.h
+++ b/media/filters/decoder_base.h
@@ -33,16 +33,18 @@ class DecoderBase : public Decoder {
NewRunnableMethod(this, &DecoderBase::StopTask));
}
- virtual void Seek(base::TimeDelta time) {
+ virtual void Seek(base::TimeDelta time,
+ FilterCallback* callback) {
this->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(this, &DecoderBase::SeekTask, time));
+ NewRunnableMethod(this, &DecoderBase::SeekTask, time, callback));
}
// Decoder implementation.
- virtual bool Initialize(DemuxerStream* demuxer_stream) {
+ virtual void Initialize(DemuxerStream* demuxer_stream,
+ FilterCallback* callback) {
this->message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(this, &DecoderBase::InitializeTask, demuxer_stream));
- return true;
+ NewRunnableMethod(this, &DecoderBase::InitializeTask, demuxer_stream,
+ callback));
}
virtual const MediaFormat& media_format() { return media_format_; }
@@ -127,8 +129,9 @@ class DecoderBase : public Decoder {
state_ = STOPPED;
}
- void SeekTask(base::TimeDelta time) {
+ void SeekTask(base::TimeDelta time, FilterCallback* callback) {
DCHECK_EQ(MessageLoop::current(), this->message_loop());
+ scoped_ptr<FilterCallback> c(callback);
// Delegate to the subclass first.
OnSeek(time);
@@ -139,24 +142,30 @@ class DecoderBase : public Decoder {
// Turn on the seeking flag so that we can discard buffers until a
// discontinuous buffer is received.
seeking_ = true;
+
+ // For now, signal that we're done seeking.
+ // TODO(scherkus): implement asynchronous seeking for decoder_base.h
+ callback->Run();
}
- void InitializeTask(DemuxerStream* demuxer_stream) {
+ void InitializeTask(DemuxerStream* demuxer_stream, FilterCallback* callback) {
DCHECK_EQ(MessageLoop::current(), this->message_loop());
DCHECK(state_ == UNINITIALIZED);
DCHECK(!demuxer_stream_);
+ scoped_ptr<FilterCallback> c(callback);
demuxer_stream_ = demuxer_stream;
// Delegate to subclass first.
if (!OnInitialize(demuxer_stream_)) {
this->host()->Error(PIPELINE_ERROR_DECODE);
+ callback->Run();
return;
}
// TODO(scherkus): subclass shouldn't mutate superclass media format.
DCHECK(!media_format_.empty()) << "Subclass did not set media_format_";
state_ = INITIALIZED;
- this->host()->InitializationComplete();
+ callback->Run();
}
void ReadTask(ReadCallback* read_callback) {
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index aa7ff27..8526b4e 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -261,19 +261,20 @@ void FFmpegDemuxer::Stop() {
NewRunnableMethod(this, &FFmpegDemuxer::StopTask));
}
-void FFmpegDemuxer::Seek(base::TimeDelta time) {
+void FFmpegDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) {
// TODO(hclam): by returning from this method, it is assumed that the seek
// operation is completed and filters behind the demuxer is good to issue
// more reads, but we are posting a task here, which makes the seek operation
// asynchronous, should change how seek works to make it fully asynchronous.
message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time));
+ NewRunnableMethod(this, &FFmpegDemuxer::SeekTask, time, callback));
}
-bool FFmpegDemuxer::Initialize(DataSource* data_source) {
+void FFmpegDemuxer::Initialize(DataSource* data_source,
+ FilterCallback* callback) {
message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(this, &FFmpegDemuxer::InititalizeTask, data_source));
- return true;
+ NewRunnableMethod(this, &FFmpegDemuxer::InititalizeTask, data_source,
+ callback));
}
size_t FFmpegDemuxer::GetNumberOfStreams() {
@@ -286,8 +287,10 @@ scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream(int stream) {
return streams_[stream].get();
}
-void FFmpegDemuxer::InititalizeTask(DataSource* data_source) {
+void FFmpegDemuxer::InititalizeTask(DataSource* data_source,
+ FilterCallback* callback) {
DCHECK_EQ(MessageLoop::current(), message_loop());
+ scoped_ptr<FilterCallback> c(callback);
// In order to get FFmpeg to use |data_source| for file IO we must transfer
// ownership via FFmpegGlue. We'll add |data_source| to FFmpegGlue and pass
@@ -311,6 +314,7 @@ void FFmpegDemuxer::InititalizeTask(DataSource* data_source) {
if (result < 0) {
host()->Error(DEMUXER_ERROR_COULD_NOT_OPEN);
+ callback->Run();
return;
}
@@ -325,6 +329,7 @@ void FFmpegDemuxer::InititalizeTask(DataSource* data_source) {
result = av_find_stream_info(format_context_);
if (result < 0) {
host()->Error(DEMUXER_ERROR_COULD_NOT_PARSE);
+ callback->Run();
return;
}
}
@@ -347,16 +352,18 @@ void FFmpegDemuxer::InititalizeTask(DataSource* data_source) {
}
if (streams_.empty()) {
host()->Error(DEMUXER_ERROR_NO_SUPPORTED_STREAMS);
+ callback->Run();
return;
}
// Good to go: set the duration and notify we're done initializing.
host()->SetDuration(max_duration);
- host()->InitializationComplete();
+ callback->Run();
}
-void FFmpegDemuxer::SeekTask(base::TimeDelta time) {
+void FFmpegDemuxer::SeekTask(base::TimeDelta time, FilterCallback* callback) {
DCHECK_EQ(MessageLoop::current(), message_loop());
+ scoped_ptr<FilterCallback> c(callback);
// Tell streams to flush buffers due to seeking.
StreamVector::iterator iter;
@@ -375,6 +382,9 @@ void FFmpegDemuxer::SeekTask(base::TimeDelta time) {
// TODO(scherkus): signal error.
NOTIMPLEMENTED();
}
+
+ // Notify we're finished seeking.
+ callback->Run();
}
void FFmpegDemuxer::DemuxTask() {
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 9d68e35..20b77e4 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -120,10 +120,10 @@ class FFmpegDemuxer : public Demuxer {
// MediaFilter implementation.
virtual void Stop();
- virtual void Seek(base::TimeDelta time);
+ virtual void Seek(base::TimeDelta time, FilterCallback* callback);
// Demuxer implementation.
- virtual bool Initialize(DataSource* data_source);
+ virtual void Initialize(DataSource* data_source, FilterCallback* callback);
virtual size_t GetNumberOfStreams();
virtual scoped_refptr<DemuxerStream> GetStream(int stream_id);
@@ -134,10 +134,10 @@ class FFmpegDemuxer : public Demuxer {
virtual ~FFmpegDemuxer();
// Carries out initialization on the demuxer thread.
- void InititalizeTask(DataSource* data_source);
+ void InititalizeTask(DataSource* data_source, FilterCallback* callback);
// Carries out a seek on the demuxer thread.
- void SeekTask(base::TimeDelta time);
+ void SeekTask(base::TimeDelta time, FilterCallback* callback);
// Carries out demuxing and satisfying stream reads on the demuxer thread.
void DemuxTask();
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 43b14c7..18cec27 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -133,7 +133,8 @@ class FFmpegDemuxerTest : public testing::Test {
InitializeDemuxerMocks();
// We expect a successful initialization.
- EXPECT_CALL(host_, InitializationComplete());
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
// Since we ignore data streams, the duration should be equal to the longest
// supported stream's duration (audio, in this case).
@@ -141,7 +142,7 @@ class FFmpegDemuxerTest : public testing::Test {
base::TimeDelta::FromMicroseconds(kDurations[AV_STREAM_AUDIO]);
EXPECT_CALL(host_, SetDuration(expected_duration));
- EXPECT_TRUE(demuxer_->Initialize(data_source_.get()));
+ demuxer_->Initialize(data_source_.get(), callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -150,6 +151,7 @@ class FFmpegDemuxerTest : public testing::Test {
scoped_refptr<FFmpegDemuxer> demuxer_;
scoped_refptr<StrictMock<MockDataSource> > data_source_;
StrictMock<MockFilterHost> host_;
+ StrictMock<MockFilterCallback> callback_;
MessageLoop message_loop_;
// FFmpeg fixtures.
@@ -196,8 +198,10 @@ TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) {
EXPECT_CALL(*MockFFmpeg::get(), AVOpenInputFile(_, _, NULL, 0, NULL))
.WillOnce(Return(-1));
EXPECT_CALL(host_, Error(DEMUXER_ERROR_COULD_NOT_OPEN));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
- EXPECT_TRUE(demuxer_->Initialize(data_source_.get()));
+ demuxer_->Initialize(data_source_.get(), callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -209,8 +213,10 @@ TEST_F(FFmpegDemuxerTest, Initialize_ParseFails) {
.WillOnce(Return(AVERROR_IO));
EXPECT_CALL(*MockFFmpeg::get(), AVCloseInputFile(&format_context_));
EXPECT_CALL(host_, Error(DEMUXER_ERROR_COULD_NOT_PARSE));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
- EXPECT_TRUE(demuxer_->Initialize(data_source_.get()));
+ demuxer_->Initialize(data_source_.get(), callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -221,9 +227,11 @@ TEST_F(FFmpegDemuxerTest, Initialize_NoStreams) {
InitializeDemuxerMocks();
}
EXPECT_CALL(host_, Error(DEMUXER_ERROR_NO_SUPPORTED_STREAMS));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
format_context_.nb_streams = 0;
- EXPECT_TRUE(demuxer_->Initialize(data_source_.get()));
+ demuxer_->Initialize(data_source_.get(), callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -234,10 +242,12 @@ TEST_F(FFmpegDemuxerTest, Initialize_DataStreamOnly) {
InitializeDemuxerMocks();
}
EXPECT_CALL(host_, Error(DEMUXER_ERROR_NO_SUPPORTED_STREAMS));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
EXPECT_EQ(format_context_.streams[0], &streams_[AV_STREAM_DATA]);
format_context_.nb_streams = 1;
- EXPECT_TRUE(demuxer_->Initialize(data_source_.get()));
+ demuxer_->Initialize(data_source_.get(), callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -446,6 +456,11 @@ TEST_F(FFmpegDemuxerTest, Seek) {
EXPECT_CALL(*MockFFmpeg::get(),
AVSeekFrame(&format_context_, -1, kExpectedTimestamp, kExpectedFlags))
.WillOnce(Return(0));
+
+ // ...then our callback will be executed...
+ StrictMock<MockFilterCallback> seek_callback;
+ EXPECT_CALL(seek_callback, OnFilterCallback());
+ EXPECT_CALL(seek_callback, OnCallbackDestroyed());
EXPECT_CALL(*MockFFmpeg::get(), CheckPoint(2));
// ...followed by two audio packet reads we'll trigger...
@@ -483,7 +498,8 @@ TEST_F(FFmpegDemuxerTest, Seek) {
MockFFmpeg::get()->CheckPoint(1);
// Now issue a simple forward seek, which should discard queued packets.
- demuxer_->Seek(base::TimeDelta::FromMicroseconds(kExpectedTimestamp));
+ demuxer_->Seek(base::TimeDelta::FromMicroseconds(kExpectedTimestamp),
+ seek_callback.NewCallback());
message_loop_.RunAllPending();
MockFFmpeg::get()->CheckPoint(2);
diff --git a/media/filters/ffmpeg_video_decoder_unittest.cc b/media/filters/ffmpeg_video_decoder_unittest.cc
index 3178725..cfba730 100644
--- a/media/filters/ffmpeg_video_decoder_unittest.cc
+++ b/media/filters/ffmpeg_video_decoder_unittest.cc
@@ -117,6 +117,7 @@ class FFmpegVideoDecoderTest : public testing::Test {
scoped_refptr<DataBuffer> buffer_;
scoped_refptr<DataBuffer> end_of_stream_buffer_;
StrictMock<MockFilterHost> host_;
+ StrictMock<MockFilterCallback> callback_;
MessageLoop message_loop_;
// FFmpeg fixtures.
@@ -161,8 +162,10 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_QueryInterfaceFails) {
EXPECT_CALL(*demuxer_, QueryInterface(AVStreamProvider::interface_id()))
.WillOnce(ReturnNull());
EXPECT_CALL(host_, Error(PIPELINE_ERROR_DECODE));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
- EXPECT_TRUE(decoder_->Initialize(demuxer_));
+ decoder_->Initialize(demuxer_, callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -176,8 +179,10 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_FindDecoderFails) {
EXPECT_CALL(*MockFFmpeg::get(), AVCodecFindDecoder(CODEC_ID_NONE))
.WillOnce(ReturnNull());
EXPECT_CALL(host_, Error(PIPELINE_ERROR_DECODE));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
- EXPECT_TRUE(decoder_->Initialize(demuxer_));
+ decoder_->Initialize(demuxer_, callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -193,8 +198,10 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_InitThreadFails) {
EXPECT_CALL(*MockFFmpeg::get(), AVCodecThreadInit(&codec_context_, 2))
.WillOnce(Return(-1));
EXPECT_CALL(host_, Error(PIPELINE_ERROR_DECODE));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
- EXPECT_TRUE(decoder_->Initialize(demuxer_));
+ decoder_->Initialize(demuxer_, callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -212,8 +219,10 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_OpenDecoderFails) {
EXPECT_CALL(*MockFFmpeg::get(), AVCodecOpen(&codec_context_, &codec_))
.WillOnce(Return(-1));
EXPECT_CALL(host_, Error(PIPELINE_ERROR_DECODE));
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
- EXPECT_TRUE(decoder_->Initialize(demuxer_));
+ decoder_->Initialize(demuxer_, callback_.NewCallback());
message_loop_.RunAllPending();
}
@@ -230,9 +239,10 @@ TEST_F(FFmpegVideoDecoderTest, Initialize_Successful) {
.WillOnce(Return(0));
EXPECT_CALL(*MockFFmpeg::get(), AVCodecOpen(&codec_context_, &codec_))
.WillOnce(Return(0));
- EXPECT_CALL(host_, InitializationComplete());
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
- EXPECT_TRUE(decoder_->Initialize(demuxer_));
+ decoder_->Initialize(demuxer_, callback_.NewCallback());
message_loop_.RunAllPending();
// Test that the output media format is an uncompressed video surface that
diff --git a/media/filters/file_data_source.cc b/media/filters/file_data_source.cc
index 2e13a59..8ac4499 100644
--- a/media/filters/file_data_source.cc
+++ b/media/filters/file_data_source.cc
@@ -20,8 +20,10 @@ FileDataSource::~FileDataSource() {
Stop();
}
-bool FileDataSource::Initialize(const std::string& url) {
+void FileDataSource::Initialize(const std::string& url,
+ FilterCallback* callback) {
DCHECK(!file_);
+ scoped_ptr<FilterCallback> c(callback);
#if defined(OS_WIN)
FilePath file_path(UTF8ToWide(url));
#else
@@ -33,15 +35,15 @@ bool FileDataSource::Initialize(const std::string& url) {
if (!file_) {
file_size_ = 0;
host()->Error(PIPELINE_ERROR_URL_NOT_FOUND);
- return false;
+ callback->Run();
+ return;
}
media_format_.SetAsString(MediaFormat::kMimeType,
mime_type::kApplicationOctetStream);
media_format_.SetAsString(MediaFormat::kURL, url);
host()->SetTotalBytes(file_size_);
host()->SetBufferedBytes(file_size_);
- host()->InitializationComplete();
- return true;
+ callback->Run();
}
void FileDataSource::Stop() {
diff --git a/media/filters/file_data_source.h b/media/filters/file_data_source.h
index e58e4aa..5c91ce1 100644
--- a/media/filters/file_data_source.h
+++ b/media/filters/file_data_source.h
@@ -26,7 +26,7 @@ class FileDataSource : public DataSource {
virtual void Stop();
// Implementation of DataSource.
- virtual bool Initialize(const std::string& url);
+ virtual void Initialize(const std::string& url, FilterCallback* callback);
virtual const MediaFormat& media_format();
virtual size_t Read(uint8* data, size_t size);
virtual bool GetPosition(int64* position_out);
@@ -41,6 +41,7 @@ class FileDataSource : public DataSource {
// of my tests!!!
FRIEND_TEST(FileDataSourceTest, OpenFile);
FRIEND_TEST(FileDataSourceTest, ReadData);
+ FRIEND_TEST(FileDataSourceTest, Seek);
friend class FilterFactoryImpl0<FileDataSource>;
FileDataSource();
virtual ~FileDataSource();
diff --git a/media/filters/file_data_source_unittest.cc b/media/filters/file_data_source_unittest.cc
index dd41e88..9c4b9ee8 100644
--- a/media/filters/file_data_source_unittest.cc
+++ b/media/filters/file_data_source_unittest.cc
@@ -8,6 +8,7 @@
#include "base/file_path.h"
#include "base/string_util.h"
#include "media/base/mock_filter_host.h"
+#include "media/base/mock_filters.h"
#include "media/filters/file_data_source.h"
using ::testing::NiceMock;
@@ -38,13 +39,15 @@ std::string TestFileURL() {
// Test that FileDataSource call the appropriate methods on its filter host.
TEST(FileDataSourceTest, OpenFile) {
StrictMock<MockFilterHost> host;
+ StrictMock<MockFilterCallback> callback;
EXPECT_CALL(host, SetTotalBytes(10));
EXPECT_CALL(host, SetBufferedBytes(10));
- EXPECT_CALL(host, InitializationComplete());
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
scoped_refptr<FileDataSource> filter = new FileDataSource();
filter->set_host(&host);
- EXPECT_TRUE(filter->Initialize(TestFileURL()));
+ filter->Initialize(TestFileURL(), callback.NewCallback());
}
// Use the mock filter host to directly call the Read and GetPosition methods.
@@ -55,9 +58,10 @@ TEST(FileDataSourceTest, ReadData) {
// Create our mock filter host and initialize the data source.
NiceMock<MockFilterHost> host;
+ NiceMock<MockFilterCallback> callback;
scoped_refptr<FileDataSource> filter = new FileDataSource();
filter->set_host(&host);
- EXPECT_TRUE(filter->Initialize(TestFileURL()));
+ filter->Initialize(TestFileURL(), callback.NewCallback());
EXPECT_TRUE(filter->GetSize(&size));
EXPECT_EQ(10, size);
@@ -80,4 +84,15 @@ TEST(FileDataSourceTest, ReadData) {
EXPECT_EQ(10, position);
}
+// Test that FileDataSource does nothing on Seek().
+TEST(FileDataSourceTest, Seek) {
+ StrictMock<MockFilterCallback> callback;
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
+ const base::TimeDelta kZero;
+
+ scoped_refptr<FileDataSource> filter = new FileDataSource();
+ filter->Seek(kZero, callback.NewCallback());
+}
+
} // namespace media
diff --git a/media/filters/video_renderer_base.cc b/media/filters/video_renderer_base.cc
index 7eace6f..3ca45dc 100644
--- a/media/filters/video_renderer_base.cc
+++ b/media/filters/video_renderer_base.cc
@@ -81,7 +81,7 @@ void VideoRendererBase::SetPlaybackRate(float playback_rate) {
playback_rate_ = playback_rate;
}
-void VideoRendererBase::Seek(base::TimeDelta time) {
+void VideoRendererBase::Seek(base::TimeDelta time, FilterCallback* callback) {
AutoLock auto_lock(lock_);
// We need the first frame in |frames_| to run the VideoRendererBase main
// loop, but we don't need decoded frames after the first frame since we are
@@ -93,29 +93,44 @@ void VideoRendererBase::Seek(base::TimeDelta time) {
}
}
-bool VideoRendererBase::Initialize(VideoDecoder* decoder) {
+void VideoRendererBase::Initialize(VideoDecoder* decoder,
+ FilterCallback* callback) {
AutoLock auto_lock(lock_);
+ DCHECK(decoder);
+ DCHECK(callback);
DCHECK_EQ(state_, UNINITIALIZED);
state_ = INITIALIZING;
decoder_ = decoder;
+ initialize_callback_.reset(callback);
// Notify the pipeline of the video dimensions.
int width = 0;
int height = 0;
- if (!ParseMediaFormat(decoder->media_format(), &width, &height))
- return false;
+ if (!ParseMediaFormat(decoder->media_format(), &width, &height)) {
+ host()->Error(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ initialize_callback_->Run();
+ initialize_callback_.reset();
+ return;
+ }
host()->SetVideoSize(width, height);
// Initialize the subclass.
// TODO(scherkus): do we trust subclasses not to do something silly while
// we're holding the lock?
- if (!OnInitialize(decoder))
- return false;
+ if (!OnInitialize(decoder)) {
+ host()->Error(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ initialize_callback_->Run();
+ initialize_callback_.reset();
+ return;
+ }
// Create our video thread.
if (!PlatformThread::Create(0, this, &thread_)) {
NOTREACHED() << "Video thread creation failed";
- return false;
+ host()->Error(PIPELINE_ERROR_INITIALIZATION_FAILED);
+ initialize_callback_->Run();
+ initialize_callback_.reset();
+ return;
}
#if defined(OS_WIN)
@@ -128,8 +143,6 @@ bool VideoRendererBase::Initialize(VideoDecoder* decoder) {
for (size_t i = 0; i < kMaxFrames; ++i) {
ScheduleRead();
}
-
- return true;
}
// PlatformThread::Delegate implementation.
@@ -248,11 +261,15 @@ void VideoRendererBase::OnReadComplete(VideoFrame* frame) {
if (frames_.empty()) {
// We should have initialized but there's no decoded frames in the queue.
// Raise an error.
+ state_ = ERRORED;
host()->Error(PIPELINE_ERROR_NO_DATA);
+ initialize_callback_->Run();
+ initialize_callback_.reset();
} else {
state_ = INITIALIZED;
current_frame_ = frames_.front();
- host()->InitializationComplete();
+ initialize_callback_->Run();
+ initialize_callback_.reset();
}
}
}
@@ -266,12 +283,11 @@ bool VideoRendererBase::WaitForInitialized() {
// initialized so we can call OnFrameAvailable() to provide subclasses with
// the first frame.
AutoLock auto_lock(lock_);
- DCHECK_EQ(state_, INITIALIZING);
while (state_ == INITIALIZING) {
frame_available_.Wait();
- if (state_ == STOPPED) {
- return false;
- }
+ }
+ if (state_ == STOPPED || state_ == ERRORED) {
+ return false;
}
DCHECK_EQ(state_, INITIALIZED);
DCHECK(current_frame_);
diff --git a/media/filters/video_renderer_base.h b/media/filters/video_renderer_base.h
index e71befa..b1b46ab 100644
--- a/media/filters/video_renderer_base.h
+++ b/media/filters/video_renderer_base.h
@@ -39,10 +39,10 @@ class VideoRendererBase : public VideoRenderer,
// MediaFilter implementation.
virtual void Stop();
virtual void SetPlaybackRate(float playback_rate);
- virtual void Seek(base::TimeDelta time);
+ virtual void Seek(base::TimeDelta time, FilterCallback* callback);
// VideoRenderer implementation.
- virtual bool Initialize(VideoDecoder* decoder);
+ virtual void Initialize(VideoDecoder* decoder, FilterCallback* callback);
// PlatformThread::Delegate implementation.
virtual void ThreadMain();
@@ -106,6 +106,7 @@ class VideoRendererBase : public VideoRenderer,
INITIALIZING,
INITIALIZED,
STOPPED,
+ ERRORED,
};
State state_;
@@ -117,6 +118,9 @@ class VideoRendererBase : public VideoRenderer,
float playback_rate_;
+ // Filter callbacks.
+ scoped_ptr<FilterCallback> initialize_callback_;
+
DISALLOW_COPY_AND_ASSIGN(VideoRendererBase);
};
diff --git a/media/filters/video_renderer_base_unittest.cc b/media/filters/video_renderer_base_unittest.cc
new file mode 100644
index 0000000..691815d
--- /dev/null
+++ b/media/filters/video_renderer_base_unittest.cc
@@ -0,0 +1,215 @@
+// Copyright (c) 2009 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 "base/stl_util-inl.h"
+#include "media/base/data_buffer.h"
+#include "media/base/mock_filter_host.h"
+#include "media/base/mock_filters.h"
+#include "media/base/video_frame_impl.h"
+#include "media/filters/video_renderer_base.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using ::testing::_;
+using ::testing::AnyNumber;
+using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::NotNull;
+using ::testing::Return;
+using ::testing::StrictMock;
+
+namespace media {
+
+// Mocked subclass of VideoRendererBase for testing purposes.
+class MockVideoRendererBase : public VideoRendererBase {
+ public:
+ MockVideoRendererBase() {}
+ virtual ~MockVideoRendererBase() {}
+
+ // VideoRendererBase implementation.
+ MOCK_METHOD1(OnInitialize, bool (VideoDecoder* decoder));
+ MOCK_METHOD0(OnStop, void());
+ MOCK_METHOD0(OnFrameAvailable, void());
+
+ // Used for verifying check points during tests.
+ MOCK_METHOD1(CheckPoint, void(int id));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockVideoRendererBase);
+};
+
+class VideoRendererBaseTest : public ::testing::Test {
+ public:
+ VideoRendererBaseTest()
+ : renderer_(new MockVideoRendererBase()),
+ decoder_(new MockVideoDecoder(mime_type::kUncompressedVideo, kWidth,
+ kHeight)) {
+ renderer_->set_host(&host_);
+
+ // Queue all reads from the decoder.
+ EXPECT_CALL(*decoder_, Read(NotNull()))
+ .WillRepeatedly(Invoke(this, &VideoRendererBaseTest::EnqueueCallback));
+ }
+
+ virtual ~VideoRendererBaseTest() {
+ STLDeleteElements(&read_queue_);
+
+ // Expect a call into the subclass.
+ EXPECT_CALL(*renderer_, OnStop());
+ renderer_->Stop();
+ }
+
+ protected:
+ static const size_t kWidth;
+ static const size_t kHeight;
+
+ // Fixture members.
+ scoped_refptr<MockVideoRendererBase> renderer_;
+ scoped_refptr<MockVideoDecoder> decoder_;
+ StrictMock<MockFilterHost> host_;
+ StrictMock<MockFilterCallback> callback_;
+
+ // Receives asynchronous read requests sent to |decoder_|.
+ std::deque<Callback1<VideoFrame*>::Type*> read_queue_;
+
+ private:
+ void EnqueueCallback(Callback1<VideoFrame*>::Type* callback) {
+ read_queue_.push_back(callback);
+ }
+
+ DISALLOW_COPY_AND_ASSIGN(VideoRendererBaseTest);
+};
+
+const size_t VideoRendererBaseTest::kWidth = 16u;
+const size_t VideoRendererBaseTest::kHeight = 16u;
+
+// Test initialization where the decoder's media format is malformed.
+TEST_F(VideoRendererBaseTest, Initialize_BadMediaFormat) {
+ InSequence s;
+
+ // Don't set a media format.
+ scoped_refptr<MockVideoDecoder> bad_decoder = new MockVideoDecoder();
+
+ // We expect to receive an error.
+ EXPECT_CALL(host_, Error(PIPELINE_ERROR_INITIALIZATION_FAILED));
+
+ // We expect our callback to be executed.
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ // Initialize, we expect to have no reads.
+ renderer_->Initialize(bad_decoder, callback_.NewCallback());
+ EXPECT_EQ(0u, read_queue_.size());
+}
+
+// Test initialization where the subclass failed for some reason.
+TEST_F(VideoRendererBaseTest, Initialize_Failed) {
+ InSequence s;
+
+ // We expect the video size to be set.
+ EXPECT_CALL(host_, SetVideoSize(kWidth, kHeight));
+
+ // Our subclass will fail when asked to initialize.
+ EXPECT_CALL(*renderer_, OnInitialize(_))
+ .WillOnce(Return(false));
+
+ // We expect to receive an error.
+ EXPECT_CALL(host_, Error(PIPELINE_ERROR_INITIALIZATION_FAILED));
+
+ // We expect our callback to be executed.
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ // Initialize, we expect to have no reads.
+ renderer_->Initialize(decoder_, callback_.NewCallback());
+ EXPECT_EQ(0u, read_queue_.size());
+}
+
+// Tests successful initialization, but when we immediately return an end of
+// stream frame.
+TEST_F(VideoRendererBaseTest, Initialize_NoData) {
+ InSequence s;
+
+ // We expect the video size to be set.
+ EXPECT_CALL(host_, SetVideoSize(kWidth, kHeight));
+
+ // Then our subclass will be asked to initialize.
+ EXPECT_CALL(*renderer_, OnInitialize(_))
+ .WillOnce(Return(true));
+
+ // Set up a check point to verify that the callback hasn't been executed yet.
+ EXPECT_CALL(*renderer_, CheckPoint(0));
+
+ // We'll provide end-of-stream immediately, which results in an error.
+ EXPECT_CALL(host_, Error(PIPELINE_ERROR_NO_DATA));
+
+ // Then we expect our callback to be executed.
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ // Since the callbacks are on a separate thread, expect any number of calls.
+ EXPECT_CALL(*renderer_, OnFrameAvailable())
+ .Times(AnyNumber());
+
+ // Initialize, we should expect to get a bunch of read requests.
+ renderer_->Initialize(decoder_, callback_.NewCallback());
+ EXPECT_EQ(3u, read_queue_.size());
+
+ // Verify our callback hasn't been executed yet.
+ renderer_->CheckPoint(0);
+
+ // Now satisfy the read requests. Our callback should be executed after
+ // exiting this loop.
+ while (!read_queue_.empty()) {
+ const base::TimeDelta kZero;
+ scoped_refptr<VideoFrame> frame;
+ VideoFrameImpl::CreateEmptyFrame(&frame);
+ read_queue_.front()->Run(frame);
+ delete read_queue_.front();
+ read_queue_.pop_front();
+ }
+}
+
+// Test successful initialization and preroll.
+TEST_F(VideoRendererBaseTest, Initialize_Successful) {
+ InSequence s;
+
+ // We expect the video size to be set.
+ EXPECT_CALL(host_, SetVideoSize(kWidth, kHeight));
+
+ // Then our subclass will be asked to initialize.
+ EXPECT_CALL(*renderer_, OnInitialize(_))
+ .WillOnce(Return(true));
+
+ // Set up a check point to verify that the callback hasn't been executed yet.
+ EXPECT_CALL(*renderer_, CheckPoint(0));
+
+ // After finishing preroll, we expect our callback to be executed.
+ EXPECT_CALL(callback_, OnFilterCallback());
+ EXPECT_CALL(callback_, OnCallbackDestroyed());
+
+ // Since the callbacks are on a separate thread, expect any number of calls.
+ EXPECT_CALL(*renderer_, OnFrameAvailable())
+ .Times(AnyNumber());
+
+ // Initialize, we should expect to get a bunch of read requests.
+ renderer_->Initialize(decoder_, callback_.NewCallback());
+ EXPECT_EQ(3u, read_queue_.size());
+
+ // Verify our callback hasn't been executed yet.
+ renderer_->CheckPoint(0);
+
+ // Now satisfy the read requests. Our callback should be executed after
+ // exiting this loop.
+ while (!read_queue_.empty()) {
+ const base::TimeDelta kZero;
+ scoped_refptr<VideoFrame> frame;
+ VideoFrameImpl::CreateFrame(VideoSurface::RGB32, kWidth, kHeight, kZero,
+ kZero, &frame);
+ read_queue_.front()->Run(frame);
+ delete read_queue_.front();
+ read_queue_.pop_front();
+ }
+}
+
+} // namespace media
diff --git a/media/media.gyp b/media/media.gyp
index 3b15e80..5b4b96a 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -155,6 +155,7 @@
'base/mock_ffmpeg.cc',
'base/mock_ffmpeg.h',
'base/mock_filter_host.h',
+ 'base/mock_filters.cc',
'base/mock_filters.h',
'base/mock_reader.h',
'base/pipeline_impl_unittest.cc',
@@ -162,10 +163,12 @@
'base/seekable_buffer_unittest.cc',
'base/video_frame_impl_unittest.cc',
'base/yuv_convert_unittest.cc',
+ 'filters/audio_renderer_base_unittest.cc',
'filters/ffmpeg_demuxer_unittest.cc',
'filters/ffmpeg_glue_unittest.cc',
'filters/ffmpeg_video_decoder_unittest.cc',
'filters/file_data_source_unittest.cc',
+ 'filters/video_renderer_base_unittest.cc',
],
'conditions': [
['OS=="linux"', {