diff options
author | ralphl@chromium.org <ralphl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-18 17:06:49 +0000 |
---|---|---|
committer | ralphl@chromium.org <ralphl@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-18 17:06:49 +0000 |
commit | 47b3dc4f6ad07331dd5eeec820ec30ff4e28d4a2 (patch) | |
tree | 61469bd1df7054f74f26132374d8619698b62c59 /media | |
parent | edd5554cecefae87179ff139ef74c9759ec8f471 (diff) | |
download | chromium_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.vcproj | 24 | ||||
-rw-r--r-- | media/build/media_unittests.vcproj | 4 | ||||
-rw-r--r-- | media/filters/decoder_base.h | 298 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.cc | 33 | ||||
-rw-r--r-- | media/filters/ffmpeg_audio_decoder.h | 38 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.cc | 33 | ||||
-rw-r--r-- | media/filters/ffmpeg_video_decoder.h | 38 | ||||
-rw-r--r-- | media/filters/test_video_decoder.h | 80 | ||||
-rw-r--r-- | media/filters/video_decoder_unittest.cc | 45 | ||||
-rw-r--r-- | media/media.gyp | 3 | ||||
-rw-r--r-- | media/media_lib.scons | 1 | ||||
-rw-r--r-- | media/media_unittests.scons | 2 |
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', |