summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorralphl@chromium.org <ralphl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-18 17:06:49 +0000
committerralphl@chromium.org <ralphl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-18 17:06:49 +0000
commit47b3dc4f6ad07331dd5eeec820ec30ff4e28d4a2 (patch)
tree61469bd1df7054f74f26132374d8619698b62c59 /media
parentedd5554cecefae87179ff139ef74c9759ec8f471 (diff)
downloadchromium_src-47b3dc4f6ad07331dd5eeec820ec30ff4e28d4a2.zip
chromium_src-47b3dc4f6ad07331dd5eeec820ec30ff4e28d4a2.tar.gz
chromium_src-47b3dc4f6ad07331dd5eeec820ec30ff4e28d4a2.tar.bz2
Base class for audio and video decoders, plus a test audio and video decoder implementation.
Note that the FFmpeg decoder classes are declared, but are completely non-functional at this time. This is the first step towards getting actual, functional media decoders checked into the Chrome project. Review URL: http://codereview.chromium.org/43060 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11970 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/build/media.vcproj24
-rw-r--r--media/build/media_unittests.vcproj4
-rw-r--r--media/filters/decoder_base.h298
-rw-r--r--media/filters/ffmpeg_audio_decoder.cc33
-rw-r--r--media/filters/ffmpeg_audio_decoder.h38
-rw-r--r--media/filters/ffmpeg_video_decoder.cc33
-rw-r--r--media/filters/ffmpeg_video_decoder.h38
-rw-r--r--media/filters/test_video_decoder.h80
-rw-r--r--media/filters/video_decoder_unittest.cc45
-rw-r--r--media/media.gyp3
-rw-r--r--media/media_lib.scons1
-rw-r--r--media/media_unittests.scons2
12 files changed, 599 insertions, 0 deletions
diff --git a/media/build/media.vcproj b/media/build/media.vcproj
index 2913d35..fb48e7d 100644
--- a/media/build/media.vcproj
+++ b/media/build/media.vcproj
@@ -221,6 +221,18 @@
>
</File>
<File
+ RelativePath="..\filters\decoder_base.h"
+ >
+ </File>
+ <File
+ RelativePath="..\filters\ffmpeg_audio_decoder.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\filters\ffmpeg_audio_decoder.h"
+ >
+ </File>
+ <File
RelativePath="..\filters\ffmpeg_common.cc"
>
</File>
@@ -245,6 +257,14 @@
>
</File>
<File
+ RelativePath="..\filters\ffmpeg_video_decoder.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\filters\ffmpeg_video_decoder.h"
+ >
+ </File>
+ <File
RelativePath="..\filters\file_data_source.cc"
>
</File>
@@ -261,6 +281,10 @@
>
</File>
<File
+ RelativePath="..\filters\test_video_decoder.h"
+ >
+ </File>
+ <File
RelativePath="..\filters\video_renderer_base.cc"
>
</File>
diff --git a/media/build/media_unittests.vcproj b/media/build/media_unittests.vcproj
index e8210c9..de4d01c 100644
--- a/media/build/media_unittests.vcproj
+++ b/media/build/media_unittests.vcproj
@@ -216,6 +216,10 @@
>
</File>
<File
+ RelativePath="..\filters\video_decoder_unittest.cc"
+ >
+ </File>
+ <File
RelativePath="..\filters\video_renderer_unittest.cc"
>
</File>
diff --git a/media/filters/decoder_base.h b/media/filters/decoder_base.h
new file mode 100644
index 0000000..de57d6e
--- /dev/null
+++ b/media/filters/decoder_base.h
@@ -0,0 +1,298 @@
+// 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.
+
+// A base class that provides the plumbing for a decoder filters.
+
+#ifndef MEDIA_FILTERS_DECODER_BASE_H_
+#define MEDIA_FILTERS_DECODER_BASE_H_
+
+#include <deque>
+
+#include "base/lock.h"
+#include "base/task.h"
+#include "base/thread.h"
+#include "media/base/buffers.h"
+#include "media/base/filters.h"
+#include "media/base/filter_host.h"
+
+namespace media {
+
+template <class Decoder, class Output>
+class DecoderBase : public Decoder {
+ public:
+ // MediaFilter implementation.
+ virtual void Stop() {
+ OnStop();
+ {
+ AutoLock auto_lock(lock_);
+ running_ = false;
+ if (process_task_) {
+ process_task_->Cancel();
+ process_task_ = NULL;
+ }
+ DiscardQueues();
+ }
+ // Because decode_thread_ is a scoped_ptr this will destroy the thread,
+ // if there was one, which causes it to be shut down in an orderly way.
+ decode_thread_.reset();
+ }
+
+ // Decoder implementation.
+ virtual bool Initialize(DemuxerStream* demuxer_stream) {
+ demuxer_stream_ = demuxer_stream;
+ if (decode_thread_.get()) {
+ if (!decode_thread_->Start()) {
+ NOTREACHED();
+ return false;
+ }
+ }
+ if (OnInitialize(demuxer_stream)) {
+ DCHECK(!media_format_.empty());
+ host()->InitializationComplete();
+ return true;
+ } else {
+ demuxer_stream_ = NULL;
+ decode_thread_.reset();
+ return false;
+ }
+ }
+
+ virtual const MediaFormat* GetMediaFormat() { return &media_format_; }
+
+ // Audio or Video decoder.
+ virtual void Read(Assignable<Output>* output) {
+ AutoLock auto_lock(lock_);
+ if (IsRunning()) {
+ output->AddRef();
+ output_queue_.push_back(output);
+ ScheduleProcessTask();
+ }
+ }
+
+ // AssignableBuffer callback.
+ virtual void OnAssignment(Buffer* buffer) {
+ AutoLock auto_lock(lock_);
+ if (IsRunning()) {
+ buffer->AddRef();
+ input_queue_.push_back(buffer);
+ --pending_reads_;
+ ScheduleProcessTask();
+ }
+ }
+
+ protected:
+ // If NULL is passed for the |thread_name| then all processing of decodes
+ // will happen on the pipeline thread. If the name is non-NULL then a new
+ // thread will be created for this decoder, and it will be assigned the
+ // name provided by |thread_name|.
+ explicit DecoderBase(const char* thread_name)
+ : running_(true),
+ demuxer_stream_(NULL),
+ decode_thread_(thread_name ? new base::Thread(thread_name) : NULL),
+ pending_reads_(0),
+ process_task_(NULL) {
+ }
+
+ virtual ~DecoderBase() {
+ Stop();
+ }
+
+ // This method is called by the derived class from within the OnDecode method.
+ // It places an output buffer in the result queue. It must be called from
+ // within the OnDecode method.
+ void EnqueueResult(Output* output) {
+ AutoLock auto_lock(lock_);
+ if (IsRunning()) {
+ output->AddRef();
+ result_queue_.push_back(output);
+ }
+ }
+
+ // Method that must be implemented by the derived class. Called from within
+ // the DecoderBase::Initialize() method before any reads are submitted to
+ // the demuxer stream. Returns true if successful, otherwise false indicates
+ // a fatal error. The derived class should NOT call the filter host's
+ // InitializationComplete() method. If this method returns true, then the
+ // base class will call the host to complete initialization. During this
+ // call, the derived class must fill in the media_format_ member.
+ virtual bool OnInitialize(DemuxerStream* demuxer_stream) = 0;
+
+ // Method that may be implemented by the derived class if desired. It will
+ // be called from within the MediaFilter::Stop method prior to stopping the
+ // base class.
+ virtual void OnStop() {}
+
+ // Method that must be implemented by the derived class. If the decode
+ // operation produces one or more outputs, the derived class should call
+ // the EnequeueResult() method from within this method.
+ virtual void OnDecode(Buffer* input) = 0;
+
+ bool IsRunning() const { return running_; }
+
+ MediaFormat media_format_;
+
+ private:
+ // The GCL compiler does not like .cc files that directly access members of
+ // a base class. This inline method helps.
+ // TODO(ralphl): Does it really help? Remove this comment after try server
+ // reports success.
+ FilterHost* host() const { return Decoder::host_; }
+
+ // Schedules a task that will execute the ProcessTask method.
+ void ScheduleProcessTask() {
+ DCHECK(IsRunning());
+ if (!process_task_) {
+ process_task_ = NewRunnableMethod(this, &DecoderBase::ProcessTask);
+ if (decode_thread_.get()) {
+ decode_thread_->message_loop()->PostTask(FROM_HERE, process_task_);
+ } else {
+ host()->PostTask(process_task_);
+ }
+ }
+ }
+
+ // The core work loop of the decoder base. This method will run the methods
+ // SubmitReads(), ProcessInput(), and ProcessOutput() in a loop until they
+ // either produce no further work, or the filter is stopped. Once there is
+ // no further work to do, the method returns. A later call to the
+ // ScheduleProcessTask() method will start this task again.
+ void ProcessTask() {
+ AutoLock auto_lock(lock_);
+ bool did_some_work;
+ do {
+ did_some_work = SubmitReads();
+ did_some_work |= ProcessInput();
+ did_some_work |= ProcessOutput();
+ } while (IsRunning() && did_some_work);
+ DCHECK(process_task_ || !IsRunning());
+ process_task_ = NULL;
+ }
+
+ // If necessary, calls the |demuxer_stream_| to read buffers. Returns true
+ // if reads have happened, else false. This method must be called with
+ // |lock_| acquired. If the method submits any reads, then it will Release()
+ // the |lock_| when calling the demuxer and then re-Acquire() the |lock_|.
+ // TODO(ralphl): Update lock to have AssertAcquired and call it here
+ // TODO(ralphl): Fix AutoUnlock and use it here instead of Release/Acquire
+ bool SubmitReads() {
+ bool did_read = false;
+ if (IsRunning() &&
+ pending_reads_ + input_queue_.size() < output_queue_.size()) {
+ did_read = true;
+ size_t read = output_queue_.size() - pending_reads_ - input_queue_.size();
+ pending_reads_ += read;
+ // Release |lock_| before calling the demuxer.
+ lock_.Release();
+ while (read) {
+ demuxer_stream_->Read(new AssignableBuffer<DecoderBase, Buffer>(this));
+ --read;
+ }
+ lock_.Acquire();
+ }
+ return did_read;
+ }
+
+ // If the |input_queue_| has any buffers, this method will call the derived
+ // class's OnDecode() method.
+ // TODO(ralphl): Update lock to have AssertAcquired and call it here
+ // TODO(ralphl): Fix AutoUnlock and use it here instead of Release/Acquire
+ bool ProcessInput() {
+ bool did_decode = false;
+ while (IsRunning() && !input_queue_.empty()) {
+ did_decode = true;
+ Buffer* input = input_queue_.front();
+ input_queue_.pop_front();
+ // Release |lock_| before calling the derived class to do the decode.
+ lock_.Release();
+ OnDecode(input);
+ input->Release();
+ lock_.Acquire();
+ }
+ return did_decode;
+ }
+
+
+ // Removes any buffers from the |result_queue_| and assigns them to a pending
+ // read Assignable buffer in the |output_queue_|.
+ // TODO(ralphl): Update lock to have AssertAcquired and call it here
+ // TODO(ralphl): Fix AutoUnlock and use it here instead of Release/Acquire
+ bool ProcessOutput() {
+ bool called_renderer = false;
+ while (IsRunning() && !output_queue_.empty() && !result_queue_.empty()) {
+ called_renderer = true;
+ Output* output = result_queue_.front();
+ result_queue_.pop_front();
+ Assignable<Output>* assignable_output = output_queue_.front();
+ output_queue_.pop_front();
+ // Release |lock_| before calling the renderer.
+ lock_.Release();
+ assignable_output->SetBuffer(output);
+ output->Release();
+ assignable_output->OnAssignment();
+ assignable_output->Release();
+ lock_.Acquire();
+ }
+ return called_renderer;
+ }
+
+ // Throw away all buffers in all queues.
+ void DiscardQueues() {
+ while (!input_queue_.empty()) {
+ input_queue_.front()->Release();
+ input_queue_.pop_front();
+ }
+ while (!result_queue_.empty()) {
+ result_queue_.front()->Release();
+ result_queue_.pop_front();
+ }
+ while (!output_queue_.empty()) {
+ output_queue_.front()->Release();
+ output_queue_.pop_front();
+ }
+ }
+
+ // The critical section for the decoder.
+ Lock lock_;
+
+ // If false, then the Stop() method has been called, and no further processing
+ // of buffers should occur.
+ bool running_;
+
+ // Pointer to the demuxer stream that will feed us compressed buffers.
+ DemuxerStream* demuxer_stream_;
+
+ // If this pointer is NULL then there is no thread dedicated to this decoder
+ // and decodes will happen on the pipeline thread.
+ scoped_ptr<base::Thread> decode_thread_;
+
+ // Number of times we have called Read() on the demuxer that have not yet
+ // been satisfied.
+ size_t pending_reads_;
+
+ CancelableTask* process_task_;
+
+ // Queue of buffers read from teh |demuxer_stream_|
+ typedef std::deque<Buffer*> InputQueue;
+ InputQueue input_queue_;
+
+ // Queue of decoded samples produced in the OnDecode() method of the decoder.
+ // Any samples placed in this queue will be assigned to the OutputQueue
+ // buffers once the OnDecode() method returns.
+ // TODO(ralphl): Eventually we want to have decoders get their destination
+ // buffer from the OutputQueue and write to it directly. Until we change
+ // from the Assignable buffer to callbacks and renderer-allocated buffers,
+ // we need this extra queue.
+ typedef std::deque<Output*> ResultQueue;
+ ResultQueue result_queue_;
+
+ // Queue of buffers supplied by the renderer through the Read() method.
+ typedef std::deque<Assignable<Output>*> OutputQueue;
+ OutputQueue output_queue_;
+
+ DISALLOW_COPY_AND_ASSIGN(DecoderBase);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_DECODER_BASE_H_
diff --git a/media/filters/ffmpeg_audio_decoder.cc b/media/filters/ffmpeg_audio_decoder.cc
new file mode 100644
index 0000000..378d865
--- /dev/null
+++ b/media/filters/ffmpeg_audio_decoder.cc
@@ -0,0 +1,33 @@
+// 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/filters/ffmpeg_audio_decoder.h"
+
+namespace media {
+
+FFmpegAudioDecoder::FFmpegAudioDecoder()
+ : DecoderBase<AudioDecoder, Buffer>(NULL) {
+ NOTIMPLEMENTED();
+}
+
+FFmpegAudioDecoder::~FFmpegAudioDecoder() {
+ NOTIMPLEMENTED();
+}
+
+// static
+bool FFmpegAudioDecoder::IsMediaFormatSupported(const MediaFormat* format) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool FFmpegAudioDecoder::OnInitialize(DemuxerStream* demuxer_stream) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void FFmpegAudioDecoder::OnDecode(Buffer* input) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace
diff --git a/media/filters/ffmpeg_audio_decoder.h b/media/filters/ffmpeg_audio_decoder.h
new file mode 100644
index 0000000..9df354b
--- /dev/null
+++ b/media/filters/ffmpeg_audio_decoder.h
@@ -0,0 +1,38 @@
+// 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.
+
+
+#ifndef MEDIA_FILTERS_FFMPEG_AUDIO_DECODER_H_
+#define MEDIA_FILTERS_FFMPEG_AUDIO_DECODER_H_
+
+#include "media/base/factory.h"
+#include "media/filters/decoder_base.h"
+
+namespace media {
+
+//------------------------------------------------------------------------------
+
+class FFmpegAudioDecoder : public DecoderBase<AudioDecoder, Buffer> {
+ public:
+ static FilterFactory* CreateFactory() {
+ return new FilterFactoryImpl0<FFmpegAudioDecoder>();
+ }
+
+ static bool IsMediaFormatSupported(const MediaFormat* media_format);
+
+ virtual bool OnInitialize(DemuxerStream* demuxer_stream);
+
+ virtual void OnDecode(Buffer* input);
+
+ private:
+ friend FilterFactoryImpl0<FFmpegAudioDecoder>;
+ FFmpegAudioDecoder();
+ virtual ~FFmpegAudioDecoder();
+
+ DISALLOW_COPY_AND_ASSIGN(FFmpegAudioDecoder);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_FFMPEG_AUDIO_DECODER_H_
diff --git a/media/filters/ffmpeg_video_decoder.cc b/media/filters/ffmpeg_video_decoder.cc
new file mode 100644
index 0000000..dad8b88
--- /dev/null
+++ b/media/filters/ffmpeg_video_decoder.cc
@@ -0,0 +1,33 @@
+// 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/filters/ffmpeg_video_decoder.h"
+
+namespace media {
+
+FFmpegVideoDecoder::FFmpegVideoDecoder()
+ : DecoderBase<VideoDecoder, VideoFrame>(NULL) {
+ NOTIMPLEMENTED();
+}
+
+FFmpegVideoDecoder::~FFmpegVideoDecoder() {
+ NOTIMPLEMENTED();
+}
+
+// static
+bool FFmpegVideoDecoder::IsMediaFormatSupported(const MediaFormat* format) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+bool FFmpegVideoDecoder::OnInitialize(DemuxerStream* demuxer_stream) {
+ NOTIMPLEMENTED();
+ return false;
+}
+
+void FFmpegVideoDecoder::OnDecode(Buffer* input) {
+ NOTIMPLEMENTED();
+}
+
+} // namespace
diff --git a/media/filters/ffmpeg_video_decoder.h b/media/filters/ffmpeg_video_decoder.h
new file mode 100644
index 0000000..7e9fb91
--- /dev/null
+++ b/media/filters/ffmpeg_video_decoder.h
@@ -0,0 +1,38 @@
+// 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.
+
+
+#ifndef MEDIA_FILTERS_FFMPEG_VIDEO_DECODER_H_
+#define MEDIA_FILTERS_FFMPEG_VIDEO_DECODER_H_
+
+#include "media/base/factory.h"
+#include "media/filters/decoder_base.h"
+
+namespace media {
+
+//------------------------------------------------------------------------------
+
+class FFmpegVideoDecoder : public DecoderBase<VideoDecoder, VideoFrame> {
+ public:
+ static FilterFactory* CreateFactory() {
+ return new FilterFactoryImpl0<FFmpegVideoDecoder>();
+ }
+
+ static bool IsMediaFormatSupported(const MediaFormat* media_format);
+
+ virtual bool OnInitialize(DemuxerStream* demuxer_stream);
+
+ virtual void OnDecode(Buffer* input);
+
+ private:
+ friend FilterFactoryImpl0<FFmpegVideoDecoder>;
+ FFmpegVideoDecoder();
+ virtual ~FFmpegVideoDecoder();
+
+ DISALLOW_COPY_AND_ASSIGN(FFmpegVideoDecoder);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_FFMPEG_VIDEO_DECODER_H_
diff --git a/media/filters/test_video_decoder.h b/media/filters/test_video_decoder.h
new file mode 100644
index 0000000..3ca6a3f
--- /dev/null
+++ b/media/filters/test_video_decoder.h
@@ -0,0 +1,80 @@
+// 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.
+//
+
+#ifndef MEDIA_FILTERS_TEST_VIDEO_DECODER_H_
+#define MEDIA_FILTERS_TEST_VIDEO_DECODER_H_
+
+#include <string>
+
+#include "media/base/buffers.h"
+#include "media/base/factory.h"
+#include "media/base/filters.h"
+#include "media/base/video_frame_impl.h"
+#include "media/filters/decoder_base.h"
+
+namespace media {
+
+class TestVideoDecoder : public DecoderBase<VideoDecoder, VideoFrame> {
+ public:
+ TestVideoDecoder()
+ : DecoderBase<VideoDecoder, VideoFrame>(NULL),
+ video_width_(0),
+ video_height_(0) {
+ }
+
+ bool OnInitialize(DemuxerStream* demuxer_stream) {
+ const MediaFormat* media_format = demuxer_stream->GetMediaFormat();
+ std::string mime_type;
+ int width, height;
+ if (media_format->GetAsString(MediaFormat::kMimeType, &mime_type) &&
+ mime_type.compare(mime_type::kH264AnnexB) == 0 &&
+ media_format->GetAsInteger(MediaFormat::kWidth, &width) &&
+ media_format->GetAsInteger(MediaFormat::kHeight, &height)) {
+ video_width_ = width;
+ video_height_ = height;
+ media_format_.SetAsString(MediaFormat::kMimeType,
+ mime_type::kUncompressedVideo);
+ media_format_.SetAsInteger(MediaFormat::kWidth, width);
+ media_format_.SetAsInteger(MediaFormat::kHeight, height);
+ return true;
+ }
+ return false;
+ }
+
+ void OnDecode(Buffer* buffer) {
+ scoped_refptr<VideoFrame> frame;
+ VideoFrameImpl::CreateFrame(VideoSurface::YV12,
+ video_width_,
+ video_height_,
+ buffer->GetTimestamp(),
+ buffer->GetDuration(),
+ &frame);
+ if (frame) {
+ MockVideoDecoder::InitializeYV12Frame(frame, 0.5f);
+ EnqueueResult(frame);
+ } else {
+ host_->Error(PIPELINE_ERROR_OUT_OF_MEMORY);
+ }
+ }
+
+ static bool IsMediaFormatSupported(const MediaFormat* media_format) {
+ std::string mime_type;
+ return (media_format->GetAsString(MediaFormat::kMimeType, &mime_type) &&
+ mime_type == mime_type::kH264AnnexB);
+ }
+
+ private:
+ friend class scoped_refptr<TestVideoDecoder>;
+ virtual ~TestVideoDecoder() {}
+
+ size_t video_width_;
+ size_t video_height_;
+
+ DISALLOW_COPY_AND_ASSIGN(TestVideoDecoder);
+};
+
+} // namespace
+
+#endif // MEDIA_FILTERS_TEST_VIDEO_DECODER_H_
diff --git a/media/filters/video_decoder_unittest.cc b/media/filters/video_decoder_unittest.cc
new file mode 100644
index 0000000..d6bb9dc
--- /dev/null
+++ b/media/filters/video_decoder_unittest.cc
@@ -0,0 +1,45 @@
+// 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 <string>
+
+#include "media/base/factory.h"
+#include "media/base/filter_host.h"
+#include "media/base/filters.h"
+#include "media/base/media_format.h"
+#include "media/base/mock_media_filters.h"
+#include "media/base/pipeline_impl.h"
+#include "media/filters/test_video_decoder.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+using media::FilterFactoryCollection;
+using media::InstanceFilterFactory;
+using media::MockAudioDecoder;
+using media::MockAudioRenderer;
+using media::MockDataSource;
+using media::MockDemuxer;
+using media::MockFilterConfig;
+using media::MockVideoRenderer;
+using media::PipelineImpl;
+using media::TestVideoDecoder;
+using media::VideoFrame;
+
+TEST(VideoDecoder, CreateTestDecoder) {
+ std::string url("");
+ PipelineImpl p;
+ scoped_refptr<TestVideoDecoder> test_decoder = new TestVideoDecoder();
+ MockFilterConfig config;
+ scoped_refptr<FilterFactoryCollection> c = new FilterFactoryCollection();
+ c->AddFactory(MockDataSource::CreateFactory(&config));
+ c->AddFactory(MockDemuxer::CreateFactory(&config));
+ c->AddFactory(new InstanceFilterFactory<TestVideoDecoder>(test_decoder));
+ c->AddFactory(MockAudioDecoder::CreateFactory(&config));
+ c->AddFactory(MockAudioRenderer::CreateFactory(&config));
+ c->AddFactory(MockVideoRenderer::CreateFactory(&config));
+ media::InitializationHelper h;
+ h.Start(&p, c, url);
+ p.SetPlaybackRate(1.0f);
+ p.Stop();
+}
+
diff --git a/media/media.gyp b/media/media.gyp
index 55ab20e..2151cde 100644
--- a/media/media.gyp
+++ b/media/media.gyp
@@ -63,8 +63,10 @@
'filters/audio_renderer_base.h',
'filters/audio_renderer_impl.cc',
'filters/audio_renderer_impl.h',
+ 'filters/decoder_base.h',
'filters/file_data_source.cc',
'filters/file_data_source.h',
+ 'filters/test_video_decoder.h',
'filters/null_audio_renderer.cc',
'filters/null_audio_renderer.h',
'filters/video_renderer_base.cc',
@@ -93,6 +95,7 @@
'base/video_frame_impl_unittest.cc',
'base/yuv_convert_unittest.cc',
'filters/file_data_source_unittest.cc',
+ 'filters/video_decoder_unittest.cc',
'filters/video_renderer_unittest.cc',
],
},
diff --git a/media/media_lib.scons b/media/media_lib.scons
index ce2c922..3a13b43 100644
--- a/media/media_lib.scons
+++ b/media/media_lib.scons
@@ -50,6 +50,7 @@ input_files = ChromeFileList([
'filters/audio_renderer_base.h',
'filters/audio_renderer_impl.cc',
'filters/audio_renderer_impl.h',
+ 'filters/decoder_base.h',
'filters/file_data_source.cc',
'filters/file_data_source.h',
'filters/null_audio_renderer.cc',
diff --git a/media/media_unittests.scons b/media/media_unittests.scons
index 31116cb..3a01710 100644
--- a/media/media_unittests.scons
+++ b/media/media_unittests.scons
@@ -62,8 +62,10 @@ input_files = ChromeFileList([
]),
MSVSFilter('filters', [
'filters/file_data_source_unittest.cc',
+ 'filters/test_video_decoder.h',
'filters/test_video_renderer.h',
'filters/video_renderer_unittest.cc',
+ 'filters/video_decoder_unittest.cc',
]),
MSVSFilter('audio', [
'audio/win/audio_output_win_unittest.cc',