diff options
-rw-r--r-- | media/base/mock_filter_host.h | 103 | ||||
-rw-r--r-- | media/base/mock_pipeline.h | 173 | ||||
-rw-r--r-- | media/build/media.vcproj | 8 | ||||
-rw-r--r-- | media/build/media_unittests.vcproj | 16 | ||||
-rw-r--r-- | media/filters/file_data_source.cc | 114 | ||||
-rw-r--r-- | media/filters/file_data_source.h | 64 | ||||
-rw-r--r-- | media/filters/file_data_source_unittest.cc | 95 | ||||
-rw-r--r-- | media/media_lib.scons | 2 | ||||
-rw-r--r-- | media/media_unittests.scons | 10 | ||||
-rw-r--r-- | media/test/data/ten_byte_file | 1 |
10 files changed, 579 insertions, 7 deletions
diff --git a/media/base/mock_filter_host.h b/media/base/mock_filter_host.h new file mode 100644 index 0000000..490587f --- /dev/null +++ b/media/base/mock_filter_host.h @@ -0,0 +1,103 @@ +// 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_BASE_MOCK_FILTER_HOST_H_ +#define MEDIA_BASE_MOCK_FILTER_HOST_H_ + +#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_pipeline.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +template <class Filter, class Source> +class MockFilterHost : public FilterHost { + public: + MockFilterHost(const MediaFormat* media_format, Source source) { + scoped_refptr<FilterFactory> factory = Filter::CreateFactory(); + filter_ = factory->Create<Filter>(media_format); + EXPECT_TRUE(filter_.get()); + if (filter_.get()) { + filter_->SetFilterHost(this); + filter_->Initialize(source); + } + } + + virtual ~MockFilterHost() { + filter_->Stop(); + } + + virtual const PipelineStatus* GetPipelineStatus() const { + return &mock_pipeline_; + } + + virtual void SetTimeUpdateCallback(Callback1<base::TimeDelta>::Type* cb) { + time_update_callback_.reset(cb); + } + + virtual void ScheduleTimeUpdateCallback(base::TimeDelta time) { + // TODO(ralphl): Implement MockFilte::ScheduleTimeUpdateCallback. + NOTIMPLEMENTED(); + } + + virtual void InitializationComplete() { + EXPECT_FALSE(mock_pipeline_.IsInitialized()); + mock_pipeline_.SetInitialized(true); + } + + virtual void PostTask(Task* task) { + // TODO(ralphl): Implement MockPipeline::PostTask. + NOTIMPLEMENTED(); + } + + virtual void Error(PipelineError error) { + mock_pipeline_.Error(error); + } + + virtual void SetTime(base::TimeDelta time) { + mock_pipeline_.SetTime(time); + } + + virtual void SetDuration(base::TimeDelta duration) { + mock_pipeline_.SetDuration(duration); + } + + virtual void SetBufferedTime(base::TimeDelta buffered_time) { + mock_pipeline_.SetBufferedTime(buffered_time); + } + + virtual void SetTotalBytes(int64 total_bytes) { + mock_pipeline_.SetTotalBytes(total_bytes); + } + + virtual void SetBufferedBytes(int64 buffered_bytes) { + mock_pipeline_.SetBufferedBytes(buffered_bytes); + } + + // Sets the size of the video output in pixel units. + virtual void SetVideoSize(size_t width, size_t height) { + mock_pipeline_.SetVideoSize(width, height); + } + + // Used by unit tests to manipulate the filter. + Filter* filter() const { return filter_; } + + MockPipeline* mock_pipeline() const { return &mock_pipeline_; } + + private: + MockPipeline mock_pipeline_; + scoped_refptr<Filter> filter_; + scoped_ptr<Callback1<base::TimeDelta>::Type> time_update_callback_; + + DISALLOW_COPY_AND_ASSIGN(MockFilterHost); +}; + +} // namespace media + +#endif // MEDIA_BASE_MOCK_FILTER_HOST_H_ diff --git a/media/base/mock_pipeline.h b/media/base/mock_pipeline.h new file mode 100644 index 0000000..d353c41 --- /dev/null +++ b/media/base/mock_pipeline.h @@ -0,0 +1,173 @@ +// 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_BASE_MOCK_PIPELINE_H_ +#define MEDIA_BASE_MOCK_PIPELINE_H_ + +#include <string> + +#include "media/base/media_format.h" +#include "media/base/pipeline.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +class MockPipeline : public media::Pipeline { + public: + MockPipeline() { + Reset(false); + } + + virtual ~MockPipeline() {} + + // Implementation of PipelineStatus interface. + virtual bool IsInitialized() const { + return initialized_; + } + + virtual base::TimeDelta GetDuration() const { + return duration_; + } + + virtual base::TimeDelta GetBufferedTime() const { + return buffered_time_; + } + + virtual int64 GetTotalBytes() const { + return total_bytes_; + } + + virtual int64 GetBufferedBytes() const { + return buffered_bytes_; + } + + virtual void GetVideoSize(size_t* width_out, size_t* height_out) const { + *width_out = width_; + *height_out = height_; + } + + virtual float GetVolume() const { + return volume_; + } + + virtual float GetPlaybackRate() const { + return playback_rate_; + } + + virtual base::TimeDelta GetTime() const { + return time_; + } + + virtual base::TimeDelta GetInterpolatedTime() const { + return time_; + } + + virtual PipelineError GetError() const { + return error_; + } + + // Implementation of Pipeline interface. + virtual bool Start(FilterFactory* filter_factory, + const std::string& url, + Callback1<bool>::Type* init_complete_callback) { + EXPECT_FALSE(initialized_); + initialized_ = true; + if (init_complete_callback) { + init_complete_callback->Run(true); + delete init_complete_callback; + } + return true; + } + + virtual void Stop() { + EXPECT_TRUE(initialized_ || error_ != media::PIPELINE_OK); + Reset(false); + } + + virtual void SetPlaybackRate(float playback_rate) { + playback_rate_ = playback_rate; + } + + virtual void Seek(base::TimeDelta time) { + time_ = time; + } + + virtual void SetVolume(float volume) { + volume_ = volume; + } + + // Public methods used by tests and by MockFilterHost to manipulate the + // state of the mock pipeline. + + // Set the state to the same as a newly created MockPipeline. If + // |reset_to_initialized| is true then the pipeline's |initialized_| state + // will be true when this method returns. + void Reset(bool reset_to_initialized) { + error_ = media::PIPELINE_OK; + volume_ = 1.0f; + playback_rate_ = 0.0f; + initialized_ = reset_to_initialized; + time_ = base::TimeDelta(); + duration_ = base::TimeDelta(); + buffered_time_ = base::TimeDelta(); + width_ = 0; + height_ = 0; + buffered_bytes_ = 0; + total_bytes_ = 0; + } + + void SetInitialized(bool init_value) { + initialized_ = init_value; + } + + void Error(media::PipelineError error) { + initialized_ = false; + error_ = error; + } + + void SetTime(base::TimeDelta time) { + time_ = time; + } + + virtual void SetDuration(base::TimeDelta duration) { + duration_ = duration; + } + + virtual void SetBufferedTime(base::TimeDelta buffered_time) { + buffered_time = buffered_time; + } + + virtual void SetTotalBytes(int64 total_bytes) { + total_bytes_ = total_bytes; + } + + virtual void SetBufferedBytes(int64 buffered_bytes) { + buffered_bytes_ = buffered_bytes; + } + + // Sets the size of the video output in pixel units. + virtual void SetVideoSize(size_t width, size_t height) { + width_ = width; + height_ = height; + } + + private: + PipelineError error_; + float volume_; + float playback_rate_; + bool initialized_; + base::TimeDelta time_; + base::TimeDelta duration_; + base::TimeDelta buffered_time_; + size_t width_; + size_t height_; + int64 buffered_bytes_; + int64 total_bytes_; + + DISALLOW_COPY_AND_ASSIGN(MockPipeline); +}; + +} // namespace media + +#endif // MEDIA_BASE_MOCK_PIPELINE_H_ diff --git a/media/build/media.vcproj b/media/build/media.vcproj index c15fc81..8f9ce19 100644 --- a/media/build/media.vcproj +++ b/media/build/media.vcproj @@ -196,6 +196,14 @@ RelativePath="..\filters\audio_renderer_impl.h" > </File> + <File + RelativePath="..\filters\file_data_source.cc" + > + </File> + <File + RelativePath="..\filters\file_data_source.h" + > + </File> </Filter> <Filter Name="audio" diff --git a/media/build/media_unittests.vcproj b/media/build/media_unittests.vcproj index a565c33..90c9cd9 100644 --- a/media/build/media_unittests.vcproj +++ b/media/build/media_unittests.vcproj @@ -160,10 +160,18 @@ > </File> <File + RelativePath="..\base\mock_filter_host.h" + > + </File> + <File RelativePath="..\base\mock_media_filters.h" > </File> <File + RelativePath="..\base\mock_pipeline.h" + > + </File> + <File RelativePath="..\base\pipeline_impl_unittest.cc" > </File> @@ -176,6 +184,14 @@ > </File> </Filter> + <Filter + Name="filters" + > + <File + RelativePath="..\filters\file_data_source_unittest.cc" + > + </File> + </Filter> </Filter> </Files> <Globals> diff --git a/media/filters/file_data_source.cc b/media/filters/file_data_source.cc new file mode 100644 index 0000000..95b0ec0 --- /dev/null +++ b/media/filters/file_data_source.cc @@ -0,0 +1,114 @@ +// 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/file_util.h" +#include "base/string_util.h" +#include "media/base/filter_host.h" +#include "media/base/filters.h" +#include "media/base/pipeline.h" +#include "media/filters/file_data_source.h" + +namespace media { + +FileDataSource::FileDataSource() + : file_(NULL), + file_size_(0) { +} + +FileDataSource::~FileDataSource() { + Stop(); +} + +bool FileDataSource::Initialize(const std::string& url) { + DCHECK(!file_); +#if defined(OS_WIN) + FilePath file_path(UTF8ToWide(url)); +#else + FilePath file_path(url); +#endif + if (file_util::GetFileSize(file_path, &file_size_)) { + file_ = file_util::OpenFile(file_path, "rb"); + } + if (!file_) { + file_size_ = 0; + host_->Error(PIPELINE_ERROR_NETWORK); + return false; + } + 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; +} + +void FileDataSource::Stop() { + AutoLock l(lock_); + if (file_) { + file_util::CloseFile(file_); + file_ = NULL; + file_size_ = 0; + } +} + +const MediaFormat* FileDataSource::GetMediaFormat() { + return &media_format_; +} + +size_t FileDataSource::Read(char* data, size_t size) { + DCHECK(file_); + AutoLock l(lock_); + if (file_) { + size_t size_read = fread(data, 1, size, file_); + if (size_read == size || !ferror(file_)) { + return size_read; + } + } + return kReadError; +} + +bool FileDataSource::GetPosition(int64* position_out) { + DCHECK(position_out); + DCHECK(file_); + AutoLock l(lock_); + if (!file_) { + *position_out = 0; + return false; + } +// Linux and mac libraries don't seem to support 64 versions of seek and +// ftell. TODO(ralph): Try to figure out how to enable int64 versions on +// these platforms. +#if defined(OS_WIN) + *position_out = _ftelli64(file_); +#else + *position_out = ftell(file_); +#endif + return true; +} + +bool FileDataSource::SetPosition(int64 position) { + DCHECK(file_); + AutoLock l(lock_); +#if defined(OS_WIN) + if (file_ && 0 == _fseeki64(file_, position, SEEK_SET)) { + return true; + } +#else + if (file_ && 0 == fseek(file_, static_cast<int32>(position), SEEK_SET)) { + return true; + } +#endif + return false; +} + +bool FileDataSource::GetSize(int64* size_out) { + DCHECK(size_out); + DCHECK(file_); + AutoLock l(lock_); + *size_out = file_size_; + return (NULL != file_); +} + +} // namespace media diff --git a/media/filters/file_data_source.h b/media/filters/file_data_source.h new file mode 100644 index 0000000..9caa435 --- /dev/null +++ b/media/filters/file_data_source.h @@ -0,0 +1,64 @@ +// 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_FILE_DATA_SOURCE_H_ +#define MEDIA_FILTERS_FILE_DATA_SOURCE_H_ + +#include <string> + +#include "base/lock.h" +#include "media/base/filters.h" + +namespace media { + +// Basic data source that treats the URL as a file path, and uses the file +// system to read data for a media pipeline. +// TODO(ralph): We will add a pure virtual interface so that the chrome +// media player delegate can give us the file handle, bytes downloaded so far, +// and file size. +class FileDataSource : public DataSource { + public: + // Public method to get a filter factory for the FileDataSource. + static FilterFactory* CreateFactory() { + return new FilterFactoryImpl0<FileDataSource>(); + } + + // Implementation of MediaFilter. + virtual void Stop(); + + // Implementation of DataSource. + virtual bool Initialize(const std::string& url); + virtual const MediaFormat* GetMediaFormat(); + virtual size_t Read(char* data, size_t size); + virtual bool GetPosition(int64* position_out); + virtual bool SetPosition(int64 position); + virtual bool GetSize(int64* size_out); + + private: + friend class FilterFactoryImpl0<FileDataSource>; + FileDataSource(); + virtual ~FileDataSource(); + + // File handle. Null if not initialized or an error occurs. + FILE* file_; + + // Size of the file in bytes. + int64 file_size_; + + // Media format handed out by the DataSource::GetMediaFormat method. + MediaFormat media_format_; + + // Critical section that protects all of the DataSource methods to prevent + // a Stop from happening while in the middle of a file I/O operation. + // TODO(ralphl): Ideally this would use asynchronous I/O or we will know + // that we will block for a short period of time in reads. Othewise, we can + // hang the pipeline Stop. + Lock lock_; + + DISALLOW_COPY_AND_ASSIGN(FileDataSource); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_FILE_DATA_SOURCE_H_ diff --git a/media/filters/file_data_source_unittest.cc b/media/filters/file_data_source_unittest.cc new file mode 100644 index 0000000..3006454 --- /dev/null +++ b/media/filters/file_data_source_unittest.cc @@ -0,0 +1,95 @@ +// 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 "base/base_paths.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/string_util.h" +#include "base/task.h" +#include "base/waitable_event.h" +#include "media/base/buffers.h" +#include "media/base/pipeline_impl.h" +#include "media/base/media_format.h" +#include "media/base/filters.h" +#include "media/base/factory.h" +#include "media/base/filter_host.h" +#include "media/base/mock_filter_host.h" +#include "media/filters/file_data_source.h" +#include "media/base/mock_media_filters.h" +#include "testing/gtest/include/gtest/gtest.h" + +using media::FileDataSource; +using media::FilterFactoryCollection; +using media::InitializationHelper; +using media::MediaFormat; +using media::MockDemuxer; +using media::MockAudioDecoder; +using media::MockAudioRenderer; +using media::MockFilterHost; +using media::PipelineImpl; + +std::string TestFileURL() { + FilePath data_dir; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &data_dir)); + data_dir = data_dir.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .Append(FILE_PATH_LITERAL("ten_byte_file")); +#if defined (OS_WIN) + return WideToUTF8(data_dir.value()); +#else + return data_dir.value(); +#endif +} + +// Use the "real" pipeline to open the file. +TEST(FileDataSourceTest, OpenFile) { + PipelineImpl pipeline; + scoped_refptr<FilterFactoryCollection> c = new FilterFactoryCollection(); + c->AddFactory(FileDataSource::CreateFactory()); + c->AddFactory(MockDemuxer::CreateFactory()); + c->AddFactory(MockAudioDecoder::CreateFactory()); + c->AddFactory(MockAudioRenderer::CreateFactory()); + InitializationHelper h; + h.Start(&pipeline, c, TestFileURL()); + h.Wait(); + EXPECT_EQ(pipeline.GetError(), media::PIPELINE_OK); + EXPECT_EQ(pipeline.GetTotalBytes(), 10); + EXPECT_EQ(pipeline.GetBufferedBytes(), 10); + pipeline.Stop(); +} + +// Use the mock filter host to directly call the Read and GetPosition methods. +TEST(FileDataSourceTest, ReadData) { + MediaFormat url_format;
+ int64 position; + int64 size; + char ten_bytes[10];
+ std::string url = TestFileURL();
+ url_format.SetAsString(MediaFormat::kMimeType, media::mime_type::kURL);
+ url_format.SetAsString(MediaFormat::kURL, url);
+ MockFilterHost<FileDataSource, std::string> mock_host(&url_format, url); + + EXPECT_TRUE(mock_host.filter()->GetSize(&size)); + EXPECT_EQ(10, size); + + EXPECT_TRUE(mock_host.filter()->GetPosition(&position)); + EXPECT_EQ(0, position); + + EXPECT_EQ(10u, mock_host.filter()->Read(ten_bytes, sizeof(ten_bytes))); + EXPECT_EQ('0', ten_bytes[0]); + EXPECT_EQ('5', ten_bytes[5]); + EXPECT_EQ('9', ten_bytes[9]); + EXPECT_TRUE(mock_host.filter()->GetPosition(&position)); + EXPECT_EQ(10, position); + EXPECT_EQ(0u, mock_host.filter()->Read(ten_bytes, sizeof(ten_bytes))); + + EXPECT_TRUE(mock_host.filter()->SetPosition(5)); + EXPECT_EQ(5u, mock_host.filter()->Read(ten_bytes, sizeof(ten_bytes))); + EXPECT_EQ('5', ten_bytes[0]); + EXPECT_TRUE(mock_host.filter()->GetPosition(&position)); + EXPECT_EQ(10, position); +} diff --git a/media/media_lib.scons b/media/media_lib.scons index 51676b1..73badec 100644 --- a/media/media_lib.scons +++ b/media/media_lib.scons @@ -44,6 +44,8 @@ input_files = ChromeFileList([ MSVSFilter('filters', [ 'filters/audio_renderer_impl.cc', 'filters/audio_renderer_impl.h', + 'filters/file_data_source.cc', + 'filters/file_data_source.h', ]), MSVSFilter('audio', [ 'audio/win/audio_manager_win.h', diff --git a/media/media_unittests.scons b/media/media_unittests.scons index 3ce9f68..7ae0b95 100644 --- a/media/media_unittests.scons +++ b/media/media_unittests.scons @@ -46,13 +46,6 @@ if env.Bit('windows'): ], ) -input_files = [ - 'base/data_buffer_unittest.cc', - 'base/mock_media_filters.h', - 'base/pipeline_impl_unittest.cc', - 'base/run_all_unittests.cc', -] - input_files = ChromeFileList([ # TODO(sgk): violate standard indentation so we don't have to # reindent too much when we remove the explicit MSVSFilter() calls @@ -65,6 +58,9 @@ input_files = ChromeFileList([ 'base/data_buffer_unittest.cc', 'base/pipeline_impl_unittest.cc', ]), + MSVSFilter('filters', [ + 'filters/file_data_source_unittest.cc', + ]), MSVSFilter('audio', [ 'audio/win/audio_output_win_unittest.cc', ]), diff --git a/media/test/data/ten_byte_file b/media/test/data/ten_byte_file new file mode 100644 index 0000000..ad47100 --- /dev/null +++ b/media/test/data/ten_byte_file @@ -0,0 +1 @@ +0123456789
\ No newline at end of file |