summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-10 17:58:40 +0000
committerscherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-03-10 17:58:40 +0000
commit048539b80280b675fb545ad846e3b912da1c7a0f (patch)
treebec176a345363da5145c4f4e001d229d8d3f5f73 /media
parentdafd23c752513be80b430d1b9e97fab0ec68c54a (diff)
downloadchromium_src-048539b80280b675fb545ad846e3b912da1c7a0f.zip
chromium_src-048539b80280b675fb545ad846e3b912da1c7a0f.tar.gz
chromium_src-048539b80280b675fb545ad846e3b912da1c7a0f.tar.bz2
Checking in media::FFmpegGlue and some common FFmpeg code.
FFmpegGlue acts as an adapter between FFmpeg's URLProtocol and the media::DataSource interface, allowing us to use media::DataSource implementations for handling FFmpeg's IO. Review URL: http://codereview.chromium.org/28165 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11345 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r--media/DEPS1
-rw-r--r--media/base/mock_media_filters.h35
-rw-r--r--media/build/media.vcproj20
-rw-r--r--media/build/media_unittests.vcproj8
-rw-r--r--media/filters/ffmpeg_common.cc18
-rw-r--r--media/filters/ffmpeg_common.h33
-rw-r--r--media/filters/ffmpeg_glue.cc162
-rw-r--r--media/filters/ffmpeg_glue.h80
-rw-r--r--media/filters/ffmpeg_glue_unittest.cc309
9 files changed, 656 insertions, 10 deletions
diff --git a/media/DEPS b/media/DEPS
index 48e8875..2b2d7d8 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -1,2 +1,3 @@
include_rules = [
+ "+third_party/ffmpeg/include",
]
diff --git a/media/base/mock_media_filters.h b/media/base/mock_media_filters.h
index 079c681..0620eff 100644
--- a/media/base/mock_media_filters.h
+++ b/media/base/mock_media_filters.h
@@ -37,6 +37,7 @@ enum MockDataSourceBehavior {
struct MockFilterConfig {
MockFilterConfig()
: data_source_behavior(MOCK_DATA_SOURCE_NORMAL_INIT),
+ data_source_value('!'),
has_video(true),
video_width(1280u),
video_height(720u),
@@ -52,6 +53,7 @@ struct MockFilterConfig {
}
MockDataSourceBehavior data_source_behavior;
+ char data_source_value;
bool has_video;
size_t video_width;
size_t video_height;
@@ -76,7 +78,16 @@ class MockDataSource : public DataSource {
explicit MockDataSource(const MockFilterConfig* config)
: config_(config),
- position_(0) {
+ position_(0),
+ deleted_(NULL) {
+ }
+
+ MockDataSource(const MockFilterConfig* config, bool* deleted)
+ : config_(config),
+ position_(0),
+ deleted_(deleted) {
+ EXPECT_TRUE(deleted);
+ EXPECT_FALSE(*deleted);
}
// Implementation of MediaFilter.
@@ -121,7 +132,7 @@ class MockDataSource : public DataSource {
if (size < read) {
read = size;
}
- memset(data, 0, read);
+ memset(data, config_->data_source_value, read);
return read;
}
@@ -131,8 +142,6 @@ class MockDataSource : public DataSource {
}
virtual bool SetPosition(int64 position) {
- EXPECT_GE(position, 0u);
- EXPECT_LE(position, config_->media_total_bytes);
if (position < 0u || position > config_->media_total_bytes) {
return false;
}
@@ -141,12 +150,22 @@ class MockDataSource : public DataSource {
}
virtual bool GetSize(int64* size_out) {
- *size_out = config_->media_total_bytes;
+ if (config_->media_total_bytes >= 0) {
+ *size_out = config_->media_total_bytes;
+ return true;
+ }
return false;
}
+ // Simple position getter for unit testing.
+ int64 position() const { return position_; }
+
private:
- virtual ~MockDataSource() {}
+ virtual ~MockDataSource() {
+ if (deleted_) {
+ *deleted_ = true;
+ }
+ }
void TaskBehavior() {
switch (config_->data_source_behavior) {
@@ -166,6 +185,10 @@ class MockDataSource : public DataSource {
int64 position_;
MediaFormat media_format_;
+ // Set to true inside the destructor. Used in FFmpegGlue unit tests for
+ // testing proper reference counting.
+ bool* deleted_;
+
DISALLOW_COPY_AND_ASSIGN(MockDataSource);
};
diff --git a/media/build/media.vcproj b/media/build/media.vcproj
index d0b081f..d0a78f6 100644
--- a/media/build/media.vcproj
+++ b/media/build/media.vcproj
@@ -18,7 +18,7 @@
<Configuration
Name="Debug|Win32"
ConfigurationType="4"
- InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
@@ -69,7 +69,7 @@
<Configuration
Name="Release|Win32"
ConfigurationType="4"
- InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
@@ -213,6 +213,22 @@
>
</File>
<File
+ RelativePath="..\filters\ffmpeg_common.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\filters\ffmpeg_common.h"
+ >
+ </File>
+ <File
+ RelativePath="..\filters\ffmpeg_glue.cc"
+ >
+ </File>
+ <File
+ RelativePath="..\filters\ffmpeg_glue.h"
+ >
+ </File>
+ <File
RelativePath="..\filters\file_data_source.cc"
>
</File>
diff --git a/media/build/media_unittests.vcproj b/media/build/media_unittests.vcproj
index db414e7..214bab6d3 100644
--- a/media/build/media_unittests.vcproj
+++ b/media/build/media_unittests.vcproj
@@ -18,7 +18,7 @@
<Configuration
Name="Debug|Win32"
ConfigurationType="1"
- InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
@@ -79,7 +79,7 @@
<Configuration
Name="Release|Win32"
ConfigurationType="1"
- InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
+ InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
@@ -192,6 +192,10 @@
Name="filters"
>
<File
+ RelativePath="..\filters\ffmpeg_glue_unittest.cc"
+ >
+ </File>
+ <File
RelativePath="..\filters\file_data_source_unittest.cc"
>
</File>
diff --git a/media/filters/ffmpeg_common.cc b/media/filters/ffmpeg_common.cc
new file mode 100644
index 0000000..80ace45
--- /dev/null
+++ b/media/filters/ffmpeg_common.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/filters/ffmpeg_common.h"
+
+namespace media {
+
+const char kFFmpegCodecID[] = "FFmpegCodecID";
+
+namespace mime_type {
+
+const char kFFmpegAudio[] = "audio/x-ffmpeg";
+const char kFFmpegVideo[] = "video/x-ffmpeg";
+
+} // namespace mime_type
+
+} // namespace media
diff --git a/media/filters/ffmpeg_common.h b/media/filters/ffmpeg_common.h
new file mode 100644
index 0000000..baddbb1
--- /dev/null
+++ b/media/filters/ffmpeg_common.h
@@ -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.
+
+// Used for FFmpeg error codes.
+#include <cerrno>
+
+#include "base/compiler_specific.h"
+
+// Include FFmpeg header files.
+extern "C" {
+// Temporarily disable possible loss of data warning.
+// TODO(scherkus): fix and upstream the compiler warnings.
+MSVC_PUSH_DISABLE_WARNING(4244);
+#include "third_party/ffmpeg/include/libavcodec/avcodec.h"
+#include "third_party/ffmpeg/include/libavformat/avformat.h"
+MSVC_POP_WARNING();
+} // extern "C"
+
+namespace media {
+
+// MediaFormat key identifying the CodecID.
+extern const char kFFmpegCodecID[];
+
+// FFmpeg MIME types.
+namespace mime_type {
+
+extern const char kFFmpegAudio[];
+extern const char kFFmpegVideo[];
+
+} // namespace mime_type
+
+} // namespace media
diff --git a/media/filters/ffmpeg_glue.cc b/media/filters/ffmpeg_glue.cc
new file mode 100644
index 0000000..0d3957b
--- /dev/null
+++ b/media/filters/ffmpeg_glue.cc
@@ -0,0 +1,162 @@
+// 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/string_util.h"
+#include "media/base/filters.h"
+#include "media/filters/ffmpeg_common.h"
+#include "media/filters/ffmpeg_glue.h"
+
+namespace {
+
+// FFmpeg protocol interface.
+int OpenContext(URLContext* h, const char* filename, int flags) {
+ scoped_refptr<media::DataSource> data_source;
+ media::FFmpegGlue::get()->GetDataSource(filename, &data_source);
+ if (!data_source)
+ return AVERROR_IO;
+
+ data_source->AddRef();
+ h->priv_data = data_source;
+ h->flags = URL_RDONLY;
+ // TODO(scherkus): data source should be able to tell us if we're streaming.
+ h->is_streamed = FALSE;
+ return 0;
+}
+
+int ReadContext(URLContext* h, unsigned char* buf, int size) {
+ media::DataSource* data_source =
+ reinterpret_cast<media::DataSource*>(h->priv_data);
+ int result = data_source->Read(buf, size);
+ if (result < 0)
+ result = AVERROR_IO;
+ return result;
+}
+
+int WriteContext(URLContext* h, unsigned char* buf, int size) {
+ // We don't support writing.
+ return AVERROR_IO;
+}
+
+offset_t SeekContext(URLContext* h, offset_t offset, int whence) {
+ media::DataSource* data_source =
+ reinterpret_cast<media::DataSource*>(h->priv_data);
+ offset_t new_offset = AVERROR_IO;
+ switch (whence) {
+ case SEEK_SET:
+ if (data_source->SetPosition(offset))
+ data_source->GetPosition(&new_offset);
+ break;
+
+ case SEEK_CUR:
+ int64 pos;
+ if (!data_source->GetPosition(&pos))
+ break;
+ if (data_source->SetPosition(pos + offset))
+ data_source->GetPosition(&new_offset);
+ break;
+
+ case SEEK_END:
+ int64 size;
+ if (!data_source->GetSize(&size))
+ break;
+ if (data_source->SetPosition(size + offset))
+ data_source->GetPosition(&new_offset);
+ break;
+
+ case AVSEEK_SIZE:
+ data_source->GetSize(&new_offset);
+ break;
+
+ default:
+ NOTREACHED();
+ }
+ if (new_offset < 0)
+ new_offset = AVERROR_IO;
+ return new_offset;
+}
+
+int CloseContext(URLContext* h) {
+ media::DataSource* data_source =
+ reinterpret_cast<media::DataSource*>(h->priv_data);
+ data_source->Release();
+ h->priv_data = NULL;
+ return 0;
+}
+
+} // namespace
+
+//------------------------------------------------------------------------------
+
+namespace media {
+
+// Use the HTTP protocol to avoid any file path separator issues.
+static const char kProtocol[] = "http";
+
+// Fill out our FFmpeg protocol definition.
+static URLProtocol kFFmpegProtocol = {
+ kProtocol,
+ &OpenContext,
+ &ReadContext,
+ &WriteContext,
+ &SeekContext,
+ &CloseContext,
+};
+
+FFmpegGlue::FFmpegGlue() {
+ // Register our protocol glue code with FFmpeg.
+ avcodec_init();
+ register_protocol(&kFFmpegProtocol);
+
+ // Now register the rest of FFmpeg.
+ av_register_all();
+}
+
+FFmpegGlue::~FFmpegGlue() {
+ DataSourceMap::iterator iter = data_sources_.begin();
+ while (iter != data_sources_.end()) {
+ DataSource* data_source = iter->second;
+ iter = data_sources_.erase(iter);
+ }
+}
+
+std::string FFmpegGlue::AddDataSource(DataSource* data_source) {
+ AutoLock auto_lock(lock_);
+ std::string key = GetDataSourceKey(data_source);
+ if (data_sources_.find(key) == data_sources_.end()) {
+ data_sources_[key] = data_source;
+ }
+ return key;
+}
+
+void FFmpegGlue::RemoveDataSource(DataSource* data_source) {
+ AutoLock auto_lock(lock_);
+ DataSourceMap::iterator iter = data_sources_.begin();
+ while (iter != data_sources_.end()) {
+ if (iter->second == data_source) {
+ iter = data_sources_.erase(iter);
+ } else {
+ ++iter;
+ }
+ }
+}
+
+void FFmpegGlue::GetDataSource(const std::string& key,
+ scoped_refptr<DataSource>* data_source) {
+ AutoLock auto_lock(lock_);
+ DataSourceMap::iterator iter = data_sources_.find(key);
+ if (iter == data_sources_.end()) {
+ *data_source = NULL;
+ return;
+ }
+ *data_source = iter->second;
+}
+
+std::string FFmpegGlue::GetDataSourceKey(DataSource* data_source) {
+ // Use the DataSource's memory address to generate the unique string. This
+ // also has the nice property that adding the same DataSource reference will
+ // not generate duplicate entries.
+ return StringPrintf("%s://0x%lx", kProtocol, static_cast<void*>(data_source));
+}
+
+} // namespace media
diff --git a/media/filters/ffmpeg_glue.h b/media/filters/ffmpeg_glue.h
new file mode 100644
index 0000000..0db1ce8
--- /dev/null
+++ b/media/filters/ffmpeg_glue.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.
+
+// FFmpegGlue is an adapter for FFmpeg's URLProtocol interface that allows us to
+// use a DataSource implementation with FFmpeg. For convenience we use FFmpeg's
+// av_open_input_file function, which analyzes the filename given to it and
+// automatically initializes the appropriate URLProtocol.
+//
+// Since the DataSource is already open by time we call av_open_input_file, we
+// need a way for av_open_input_file to find the correct DataSource instance.
+// The solution is to maintain a map of "filenames" to DataSource instances,
+// where filenames are actually just a unique identifier. For simplicity,
+// FFmpegGlue is registered as an HTTP handler and generates filenames based on
+// the memory address of the DataSource, i.e., http://0xc0bf4870. Since there
+// may be multiple FFmpegDemuxers active at one time, FFmpegGlue is a
+// thread-safe singleton.
+//
+// Usage: FFmpegDemuxer adds the DataSource to FFmpegGlue's map and is given a
+// filename to pass to av_open_input_file. FFmpegDemuxer calls
+// av_open_input_file with the filename, which results in FFmpegGlue returning
+// the DataSource as a URLProtocol instance to FFmpeg. Since FFmpegGlue is only
+// needed for opening files, when av_open_input_file returns FFmpegDemuxer
+// removes the DataSource from FFmpegGlue's map.
+
+#ifndef MEDIA_FILTERS_FFMPEG_GLUE_H_
+#define MEDIA_FILTERS_FFMPEG_GLUE_H_
+
+#include <map>
+#include <string>
+
+#include "base/lock.h"
+#include "base/singleton.h"
+
+// FFmpeg forward declarations.
+struct URLContext;
+typedef int64 offset_t;
+
+namespace media {
+
+class DataSource;
+
+class FFmpegGlue : public Singleton<FFmpegGlue> {
+ public:
+ // Adds a DataSource to the FFmpeg glue layer and returns a unique string that
+ // can be passed to FFmpeg to identify the data source.
+ std::string AddDataSource(DataSource* data_source);
+
+ // Removes a DataSource from the FFmpeg glue layer. Using strings from
+ // previously added DataSources will no longer work.
+ void RemoveDataSource(DataSource* data_source);
+
+ // Assigns the DataSource identified with by the given key to |data_source|,
+ // or assigns NULL if no such DataSource could be found.
+ void GetDataSource(const std::string& key,
+ scoped_refptr<DataSource>* data_source);
+
+ private:
+ // Only allow Singleton to create and delete FFmpegGlue.
+ friend struct DefaultSingletonTraits<FFmpegGlue>;
+ FFmpegGlue();
+ virtual ~FFmpegGlue();
+
+ // Returns the unique key for this data source, which can be passed to
+ // av_open_input_file as the filename.
+ std::string GetDataSourceKey(DataSource* data_source);
+
+ // Mutual exclusion while adding/removing items from the map.
+ Lock lock_;
+
+ // Map between keys and DataSource references.
+ typedef std::map< std::string, scoped_refptr<DataSource> > DataSourceMap;
+ DataSourceMap data_sources_;
+
+ DISALLOW_COPY_AND_ASSIGN(FFmpegGlue);
+};
+
+} // namespace media
+
+#endif // MEDIA_FILTERS_FFMPEG_GLUE_H_
diff --git a/media/filters/ffmpeg_glue_unittest.cc b/media/filters/ffmpeg_glue_unittest.cc
new file mode 100644
index 0000000..d1d2a83
--- /dev/null
+++ b/media/filters/ffmpeg_glue_unittest.cc
@@ -0,0 +1,309 @@
+// 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/filters.h"
+#include "media/base/mock_media_filters.h"
+#include "media/filters/ffmpeg_common.h"
+#include "media/filters/ffmpeg_glue.h"
+#include "testing/gtest/include/gtest/gtest.h"
+
+// FFmpeg mocks to remove dependency on having the DLLs present.
+extern "C" {
+static bool g_avcodec_init = false;
+static URLProtocol* g_protocol = NULL;
+static bool g_av_register_all = false;
+
+void avcodec_init() {
+ EXPECT_FALSE(g_avcodec_init);
+ g_avcodec_init = true;
+}
+
+int register_protocol(URLProtocol* protocol) {
+ EXPECT_FALSE(g_protocol);
+ g_protocol = protocol;
+ return 0;
+}
+
+void av_register_all() {
+ EXPECT_FALSE(g_av_register_all);
+ g_av_register_all = true;
+}
+} // extern "C"
+
+TEST(FFmpegGlueTest, InitializeFFmpeg) {
+ // Singleton should initialize FFmpeg.
+ media::FFmpegGlue* glue = media::FFmpegGlue::get();
+ EXPECT_TRUE(glue);
+ EXPECT_TRUE(g_avcodec_init);
+ EXPECT_TRUE(g_protocol);
+ EXPECT_TRUE(g_av_register_all);
+
+ // Make sure URLProtocol was filled out correctly.
+ EXPECT_STREQ("http", g_protocol->name);
+ EXPECT_TRUE(g_protocol->url_close);
+ EXPECT_TRUE(g_protocol->url_open);
+ EXPECT_TRUE(g_protocol->url_read);
+ EXPECT_TRUE(g_protocol->url_seek);
+ EXPECT_TRUE(g_protocol->url_write);
+}
+
+TEST(FFmpegGlueTest, AddRemoveGetDataSource) {
+ // Prepare testing data.
+ media::FFmpegGlue* glue = media::FFmpegGlue::get();
+
+ // Create our data sources and add them to the glue layer.
+ bool deleted_a = false;
+ bool deleted_b = false;
+ media::MockFilterConfig config_a;
+ media::MockFilterConfig config_b;
+ scoped_refptr<media::MockDataSource> data_source_a
+ = new media::MockDataSource(&config_a, &deleted_a);
+ scoped_refptr<media::MockDataSource> data_source_b
+ = new media::MockDataSource(&config_b, &deleted_b);
+
+ // Make sure the keys are unique.
+ std::string key_a = glue->AddDataSource(data_source_a);
+ std::string key_b = glue->AddDataSource(data_source_b);
+ EXPECT_EQ(0, key_a.find("http://"));
+ EXPECT_EQ(0, key_b.find("http://"));
+ EXPECT_NE(key_a, key_b);
+
+ // Our keys should return our data sources.
+ scoped_refptr<media::DataSource> data_source_c;
+ scoped_refptr<media::DataSource> data_source_d;
+ glue->GetDataSource(key_a, &data_source_c);
+ glue->GetDataSource(key_b, &data_source_d);
+ EXPECT_EQ(data_source_a, data_source_c);
+ EXPECT_EQ(data_source_b, data_source_d);
+
+ // Adding the same DataSource should create the same key and not add an extra
+ // reference.
+ std::string key_a2 = glue->AddDataSource(data_source_a);
+ EXPECT_EQ(key_a, key_a2);
+ glue->GetDataSource(key_a2, &data_source_c);
+ EXPECT_EQ(data_source_a, data_source_c);
+
+ // Removes the data sources and then releases our references. They should be
+ // deleted.
+ glue->RemoveDataSource(data_source_a);
+ glue->GetDataSource(key_a, &data_source_c);
+ EXPECT_FALSE(data_source_c);
+ glue->GetDataSource(key_b, &data_source_d);
+ EXPECT_EQ(data_source_b, data_source_d);
+ glue->RemoveDataSource(data_source_b);
+ glue->GetDataSource(key_b, &data_source_d);
+ EXPECT_FALSE(data_source_d);
+ EXPECT_FALSE(deleted_a);
+ EXPECT_FALSE(deleted_b);
+ data_source_a = NULL;
+ data_source_b = NULL;
+ EXPECT_TRUE(deleted_a);
+ EXPECT_TRUE(deleted_b);
+}
+
+TEST(FFmpegGlueTest, OpenClose) {
+ // Prepare testing data.
+ media::FFmpegGlue* glue = media::FFmpegGlue::get();
+
+ // Create our data source and add them to the glue layer.
+ bool deleted = false;
+ media::MockFilterConfig config;
+ scoped_refptr<media::MockDataSource> data_source
+ = new media::MockDataSource(&config, &deleted);
+ std::string key = glue->AddDataSource(data_source);
+
+ // Prepare FFmpeg URLContext structure.
+ URLContext context;
+ memset(&context, 0, sizeof(context));
+
+ // Test opening a URLContext with a data source that doesn't exist.
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_open(&context, "foobar", 0));
+
+ // Test opening a URLContext with our data source.
+ EXPECT_EQ(0, g_protocol->url_open(&context, key.c_str(), 0));
+ EXPECT_EQ(URL_RDONLY, context.flags);
+ EXPECT_EQ(data_source, context.priv_data);
+ EXPECT_FALSE(context.is_streamed);
+
+ // Remove the data source from the glue layer, releasing a reference.
+ glue->RemoveDataSource(data_source);
+ EXPECT_FALSE(deleted);
+
+ // Remove our own reference -- URLContext should maintain a reference.
+ data_source = NULL;
+ EXPECT_FALSE(deleted);
+
+ // Close the URLContext, which should release the final reference.
+ EXPECT_EQ(0, g_protocol->url_close(&context));
+ EXPECT_TRUE(deleted);
+}
+
+TEST(FFmpegGlueTest, ReadingWriting) {
+ // Prepare testing data.
+ media::FFmpegGlue* glue = media::FFmpegGlue::get();
+ const size_t kBufferSize = 16;
+ unsigned char buffer[kBufferSize];
+
+ // Configure MockDataSource to be 8 characters long and fill reads with
+ // periods. Therefore our expected string should be a character of 8 periods.
+ const size_t kExpectedSize = 8;
+ media::MockFilterConfig config;
+ config.media_total_bytes = kExpectedSize;
+ config.data_source_value = '.';
+ const char kExpected[] = "........";
+ COMPILE_ASSERT(kExpectedSize == (arraysize(kExpected) - 1), string_length);
+
+ // Create our data source and add them to the glue layer.
+ bool deleted = false;
+ scoped_refptr<media::MockDataSource> data_source
+ = new media::MockDataSource(&config, &deleted);
+ std::string key = glue->AddDataSource(data_source);
+
+ // Open our data source and then remove it from the glue layer.
+ URLContext context;
+ memset(&context, 0, sizeof(context));
+ EXPECT_EQ(0, g_protocol->url_open(&context, key.c_str(), 0));
+ glue->RemoveDataSource(data_source);
+ EXPECT_FALSE(deleted);
+
+ // Writing should always fail.
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, NULL, 0));
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, buffer, 0));
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, buffer, -1));
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, buffer, kBufferSize));
+ EXPECT_EQ(0, data_source->position());
+
+ // Reading should return same amount of bytes if <= kExpectedSize.
+ EXPECT_EQ(0, g_protocol->url_read(&context, buffer, 0));
+ EXPECT_EQ(kExpectedSize / 2,
+ g_protocol->url_read(&context, buffer, kExpectedSize / 2));
+ EXPECT_EQ(kExpectedSize,
+ g_protocol->url_read(&context, buffer, kExpectedSize));
+ buffer[kExpectedSize] = '\0';
+ EXPECT_STREQ(kExpected, reinterpret_cast<char*>(buffer));
+
+ // Test reading more than kExpectedSize for simulating EOF.
+ EXPECT_EQ(kExpectedSize, g_protocol->url_read(&context, buffer, kBufferSize));
+ buffer[kExpectedSize] = '\0';
+ EXPECT_STREQ(kExpected, reinterpret_cast<char*>(buffer));
+
+ // Close our data source.
+ EXPECT_EQ(0, g_protocol->url_close(&context));
+ EXPECT_FALSE(deleted);
+
+ // Remove our own reference, which should release the final reference.
+ data_source = NULL;
+ EXPECT_TRUE(deleted);
+}
+
+TEST(FFmpegGlueTest, Seeking) {
+ // Prepare testing data.
+ media::FFmpegGlue* glue = media::FFmpegGlue::get();
+ const int64 kSize = 32;
+
+ // Create our data source and add them to the glue layer.
+ bool deleted = false;
+ media::MockFilterConfig config;
+ config.media_total_bytes = kSize;
+ scoped_refptr<media::MockDataSource> data_source
+ = new media::MockDataSource(&config, &deleted);
+ std::string key = glue->AddDataSource(data_source);
+
+ // Open our data source and then remove it from the glue layer.
+ URLContext context;
+ memset(&context, 0, sizeof(context));
+ EXPECT_EQ(0, g_protocol->url_open(&context, key.c_str(), 0));
+ glue->RemoveDataSource(data_source);
+ EXPECT_FALSE(deleted);
+
+ // Test SEEK_SET operations.
+ config.media_total_bytes = -1;
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, SEEK_SET));
+
+ config.media_total_bytes = kSize;
+ EXPECT_TRUE(data_source->SetPosition(0));
+ EXPECT_EQ(0, g_protocol->url_seek(&context, 0, SEEK_SET));
+ EXPECT_TRUE(data_source->SetPosition(5));
+ EXPECT_EQ(0, g_protocol->url_seek(&context, 0, SEEK_SET));
+ EXPECT_EQ(0, data_source->position());
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, -5, SEEK_SET));
+ EXPECT_EQ(0, data_source->position());
+ EXPECT_EQ(kSize, g_protocol->url_seek(&context, kSize, SEEK_SET));
+ EXPECT_EQ(kSize, data_source->position());
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, kSize+1, SEEK_SET));
+ EXPECT_EQ(kSize, data_source->position());
+
+ // Test SEEK_CUR operations.
+ config.media_total_bytes = -1;
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, SEEK_CUR));
+
+ config.media_total_bytes = kSize;
+ EXPECT_TRUE(data_source->SetPosition(0));
+ EXPECT_EQ(0, g_protocol->url_seek(&context, 0, SEEK_CUR));
+ EXPECT_TRUE(data_source->SetPosition(5));
+ EXPECT_EQ(5, g_protocol->url_seek(&context, 0, SEEK_CUR));
+ EXPECT_EQ(0, g_protocol->url_seek(&context, -5, SEEK_CUR));
+ EXPECT_EQ(0, data_source->position());
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, -1, SEEK_CUR));
+ EXPECT_EQ(kSize, g_protocol->url_seek(&context, kSize, SEEK_CUR));
+ EXPECT_EQ(kSize, data_source->position());
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 1, SEEK_CUR));
+ EXPECT_EQ(kSize, data_source->position());
+
+ // Test SEEK_END operations.
+ config.media_total_bytes = -1;
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, SEEK_END));
+
+ config.media_total_bytes = kSize;
+ EXPECT_TRUE(data_source->SetPosition(0));
+ EXPECT_EQ(kSize, g_protocol->url_seek(&context, 0, SEEK_END));
+ EXPECT_EQ(kSize, data_source->position());
+ EXPECT_EQ(kSize-5, g_protocol->url_seek(&context, -5, SEEK_END));
+ EXPECT_EQ(kSize-5, data_source->position());
+ EXPECT_EQ(0, g_protocol->url_seek(&context, -kSize, SEEK_END));
+ EXPECT_EQ(0, data_source->position());
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 1, SEEK_END));
+ EXPECT_EQ(0, data_source->position());
+
+ // Test AVSEEK_SIZE operation.
+ config.media_total_bytes = -1;
+ EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, AVSEEK_SIZE));
+
+ config.media_total_bytes = kSize;
+ EXPECT_TRUE(data_source->SetPosition(0));
+ EXPECT_EQ(kSize, g_protocol->url_seek(&context, 0, AVSEEK_SIZE));
+
+ // Close our data source.
+ EXPECT_EQ(0, g_protocol->url_close(&context));
+ EXPECT_FALSE(deleted);
+
+ // Remove our own reference, which should release the final reference.
+ data_source = NULL;
+ EXPECT_TRUE(deleted);
+}
+
+TEST(FFmpegGlueTest, Destructor) {
+ // Prepare testing data.
+ media::FFmpegGlue* glue = media::FFmpegGlue::get();
+
+ // We use a static bool since ~FFmpegGlue() will set it to true sometime
+ // after this function exits.
+ static bool deleted = false;
+
+ // Create our data source and add them to the glue layer.
+ media::MockFilterConfig config;
+ scoped_refptr<media::MockDataSource> data_source
+ = new media::MockDataSource(&config, &deleted);
+ std::string key = glue->AddDataSource(data_source);
+
+ // Remove our own reference.
+ data_source = NULL;
+ EXPECT_FALSE(deleted);
+
+ // ~FFmpegGlue() will be called when this unit test finishes execution. By
+ // leaving something inside FFmpegGlue's map we get to test our cleanup code.
+ //
+ // MockDataSource will be holding onto a bad MockFilterConfig pointer at this
+ // point but since no one is calling it everything will be ok.
+}