summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/base/filters.h22
-rw-r--r--media/base/mock_filters.h5
-rw-r--r--media/base/pipeline_impl.cc6
-rw-r--r--media/filters/ffmpeg_demuxer.cc99
-rw-r--r--media/filters/ffmpeg_demuxer.h45
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc111
-rw-r--r--media/filters/ffmpeg_glue.cc88
-rw-r--r--media/filters/ffmpeg_glue.h58
-rw-r--r--media/filters/ffmpeg_glue_unittest.cc199
-rw-r--r--media/filters/file_data_source.cc59
-rw-r--r--media/filters/file_data_source.h5
-rw-r--r--media/filters/file_data_source_unittest.cc39
12 files changed, 498 insertions, 238 deletions
diff --git a/media/base/filters.h b/media/base/filters.h
index 84df0966..3029cbc 100644
--- a/media/base/filters.h
+++ b/media/base/filters.h
@@ -123,6 +123,9 @@ class MediaFilter : public base::RefCountedThreadSafe<MediaFilter> {
class DataSource : public MediaFilter {
public:
+ typedef Callback1<size_t>::Type ReadCallback;
+ static const size_t kReadError = static_cast<size_t>(-1);
+
static const FilterType filter_type() {
return FILTER_DATA_SOURCE;
}
@@ -133,8 +136,6 @@ class DataSource : public MediaFilter {
mime_type == mime_type::kURL);
}
- static const size_t kReadError = static_cast<size_t>(-1);
-
// Initialize a DataSource for the given URL, executing the callback upon
// completion.
virtual void Initialize(const std::string& url, FilterCallback* callback) = 0;
@@ -142,16 +143,13 @@ class DataSource : public MediaFilter {
// Returns the MediaFormat for this filter.
virtual const MediaFormat& media_format() = 0;
- // Read the given amount of bytes into data, returns the number of bytes read
- // if successful, kReadError otherwise.
- virtual size_t Read(uint8* data, size_t size) = 0;
-
- // Returns true and the current file position for this file, false if the
- // file position could not be retrieved.
- virtual bool GetPosition(int64* position_out) = 0;
-
- // Returns true if the file position could be set, false otherwise.
- virtual bool SetPosition(int64 position) = 0;
+ // Reads |size| bytes from |position| into |data|. And when the read is done
+ // or failed, |read_callback| is called with the number of bytes read or
+ // kReadError in case of error.
+ // TODO(hclam): should change |size| to int! It makes the code so messy
+ // with size_t and int all over the place..
+ virtual void Read(int64 position, size_t size,
+ uint8* data, ReadCallback* read_callback) = 0;
// Returns true and the file size, false if the file size could not be
// retrieved.
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 8c44996..c933bd2 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -99,9 +99,8 @@ class MockDataSource : public DataSource {
MOCK_METHOD2(Initialize, void(const std::string& url,
FilterCallback* callback));
const MediaFormat& media_format() { return media_format_; }
- MOCK_METHOD2(Read, size_t(uint8* data, size_t size));
- MOCK_METHOD1(GetPosition, bool(int64* position_out));
- MOCK_METHOD1(SetPosition, bool(int64 position));
+ MOCK_METHOD4(Read, void(int64 position, size_t size, uint8* data,
+ DataSource::ReadCallback* callback));
MOCK_METHOD1(GetSize, bool(int64* size_out));
MOCK_METHOD0(IsSeekable, bool());
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index 9c2fb10..7fc4695 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -524,7 +524,11 @@ void PipelineInternal::ErrorTask(PipelineError error) {
DCHECK_NE(PIPELINE_OK, error) << "PIPELINE_OK isn't an error!";
// Suppress executing additional error logic.
- if (state_ == kError) {
+ // TODO(hclam): Remove the condition for kStopped. It is there only because
+ // FFmpegDemuxer submits a read error while reading after it is called to
+ // stop. After FFmpegDemuxer is cleaned up we should remove this condition
+ // and add an extra assert.
+ if (state_ == kError || state_ == kStopped) {
return;
}
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index bf595e2..60c6fcd 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -215,7 +215,11 @@ base::TimeDelta FFmpegDemuxerStream::ConvertTimestamp(int64 timestamp) {
// FFmpegDemuxer
//
FFmpegDemuxer::FFmpegDemuxer()
- : format_context_(NULL) {
+ : format_context_(NULL),
+ read_event_(false, false),
+ read_has_failed_(false),
+ last_read_bytes_(0),
+ read_position_(0) {
}
FFmpegDemuxer::~FFmpegDemuxer() {
@@ -259,6 +263,9 @@ void FFmpegDemuxer::Stop() {
// Post a task to notify the streams to stop as well.
message_loop()->PostTask(FROM_HERE,
NewRunnableMethod(this, &FFmpegDemuxer::StopTask));
+
+ // Then wakes up the thread from reading.
+ SignalReadCompleted(DataSource::kReadError);
}
void FFmpegDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) {
@@ -273,7 +280,7 @@ void FFmpegDemuxer::Seek(base::TimeDelta time, FilterCallback* callback) {
void FFmpegDemuxer::Initialize(DataSource* data_source,
FilterCallback* callback) {
message_loop()->PostTask(FROM_HERE,
- NewRunnableMethod(this, &FFmpegDemuxer::InititalizeTask, data_source,
+ NewRunnableMethod(this, &FFmpegDemuxer::InitializeTask, data_source,
callback));
}
@@ -287,30 +294,76 @@ scoped_refptr<DemuxerStream> FFmpegDemuxer::GetStream(int stream) {
return streams_[stream].get();
}
-void FFmpegDemuxer::InititalizeTask(DataSource* data_source,
- FilterCallback* callback) {
+int FFmpegDemuxer::Read(int size, uint8* data) {
+ DCHECK(data_source_);
+
+ // If read has ever failed, return with an error.
+ // TODO(hclam): use a more meaningful constant as error.
+ if (read_has_failed_)
+ return AVERROR_IO;
+
+ // Asynchronous read from data source.
+ data_source_->Read(read_position_, size, data,
+ NewCallback(this, &FFmpegDemuxer::OnReadCompleted));
+
+ // TODO(hclam): The method is called on the demuxer thread and this method
+ // call will block the thread. We need to implemented an additional thread to
+ // let FFmpeg demuxer methods to run on.
+ size_t last_read_bytes = WaitForRead();
+ if (last_read_bytes == DataSource::kReadError) {
+ host()->SetError(PIPELINE_ERROR_READ);
+
+ // Returns with a negative number to signal an error to FFmpeg.
+ read_has_failed_ = true;
+ return AVERROR_IO;
+ }
+ read_position_ += last_read_bytes;
+ return last_read_bytes;
+}
+
+bool FFmpegDemuxer::GetPosition(int64* position_out) {
+ *position_out = read_position_;
+ return true;
+}
+
+bool FFmpegDemuxer::SetPosition(int64 position) {
+ DCHECK(data_source_);
+
+ int64 file_size;
+ if (!data_source_->GetSize(&file_size) || position >= file_size)
+ return false;
+
+ read_position_ = position;
+ return true;
+}
+
+bool FFmpegDemuxer::GetSize(int64* size_out) {
+ DCHECK(data_source_);
+
+ return data_source_->GetSize(size_out);
+}
+
+bool FFmpegDemuxer::IsStreamed() {
+ return false;
+}
+
+void FFmpegDemuxer::InitializeTask(DataSource* data_source,
+ FilterCallback* callback) {
DCHECK_EQ(MessageLoop::current(), message_loop());
scoped_ptr<FilterCallback> c(callback);
- // In order to get FFmpeg to use |data_source| for file IO we must transfer
- // ownership via FFmpegGlue. We'll add |data_source| to FFmpegGlue and pass
- // the resulting key to FFmpeg. FFmpeg will pass the key to FFmpegGlue which
- // will take care of attaching |data_source| to an FFmpeg context. After
- // we finish initializing the FFmpeg context we can remove |data_source| from
- // FFmpegGlue.
- //
- // Refer to media/filters/ffmpeg_glue.h for details.
+ data_source_ = data_source;
- // Add our data source and get our unique key.
- std::string key = FFmpegGlue::get()->AddDataSource(data_source);
+ // Add ourself to Protocol list and get our unique key.
+ std::string key = FFmpegGlue::get()->AddProtocol(this);
// Open FFmpeg AVFormatContext.
DCHECK(!format_context_);
AVFormatContext* context = NULL;
int result = av_open_input_file(&context, key.c_str(), NULL, 0, NULL);
- // Remove our data source.
- FFmpegGlue::get()->RemoveDataSource(data_source);
+ // Remove ourself from protocol list.
+ FFmpegGlue::get()->RemoveProtocol(this);
if (result < 0) {
host()->SetError(DEMUXER_ERROR_COULD_NOT_OPEN);
@@ -473,4 +526,18 @@ void FFmpegDemuxer::StreamHasEnded() {
}
}
+void FFmpegDemuxer::OnReadCompleted(size_t size) {
+ SignalReadCompleted(size);
+}
+
+size_t FFmpegDemuxer::WaitForRead() {
+ read_event_.Wait();
+ return last_read_bytes_;
+}
+
+void FFmpegDemuxer::SignalReadCompleted(size_t size) {
+ last_read_bytes_ = size;
+ read_event_.Signal();
+}
+
} // namespace media
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 20b77e4..ebd1274 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -25,11 +25,14 @@
#include <deque>
#include <vector>
+#include "base/waitable_event.h"
#include "media/base/buffers.h"
#include "media/base/factory.h"
#include "media/base/filters.h"
#include "media/base/media_format.h"
+#include "media/filters/ffmpeg_glue.h"
#include "media/filters/ffmpeg_interfaces.h"
+#include "testing/gtest/include/gtest/gtest_prod.h"
// FFmpeg forward declarations.
struct AVCodecContext;
@@ -108,7 +111,8 @@ class FFmpegDemuxerStream : public DemuxerStream, public AVStreamProvider {
DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerStream);
};
-class FFmpegDemuxer : public Demuxer {
+class FFmpegDemuxer : public Demuxer,
+ public FFmpegURLProtocol {
public:
// FilterFactory provider.
static FilterFactory* CreateFilterFactory() {
@@ -127,14 +131,24 @@ class FFmpegDemuxer : public Demuxer {
virtual size_t GetNumberOfStreams();
virtual scoped_refptr<DemuxerStream> GetStream(int stream_id);
+ // FFmpegProtocol implementation.
+ virtual int Read(int size, uint8* data);
+ virtual bool GetPosition(int64* position_out);
+ virtual bool SetPosition(int64 position);
+ virtual bool GetSize(int64* size_out);
+ virtual bool IsStreamed();
+
private:
// Only allow a factory to create this class.
friend class FilterFactoryImpl0<FFmpegDemuxer>;
+ friend class MockFFmpegDemuxer;
+ FRIEND_TEST(FFmpegDemuxerTest, ProtocolRead);
+
FFmpegDemuxer();
virtual ~FFmpegDemuxer();
// Carries out initialization on the demuxer thread.
- void InititalizeTask(DataSource* data_source, FilterCallback* callback);
+ void InitializeTask(DataSource* data_source, FilterCallback* callback);
// Carries out a seek on the demuxer thread.
void SeekTask(base::TimeDelta time, FilterCallback* callback);
@@ -157,6 +171,17 @@ class FFmpegDemuxer : public Demuxer {
// Must be called on the demuxer thread.
void StreamHasEnded();
+ // Read callback method to be passed to DataSource. When the asynchronous
+ // read has completed, this method will be called from DataSource with
+ // number of bytes read or kDataSource in case of error.
+ void OnReadCompleted(size_t size);
+
+ // Wait for asynchronous read to complete and return number of bytes read.
+ virtual size_t WaitForRead();
+
+ // Signal that read has completed, and |size| bytes have been read.
+ virtual void SignalReadCompleted(size_t size);
+
// FFmpeg context handle.
AVFormatContext* format_context_;
@@ -178,6 +203,22 @@ class FFmpegDemuxer : public Demuxer {
StreamVector streams_;
StreamVector packet_streams_;
+ // Reference to the data source. Asynchronous read requests are submitted to
+ // this object.
+ scoped_refptr<DataSource> data_source_;
+
+ // This member is used to block on read method calls from FFmpeg and wait
+ // until the asynchronous reads in the data source to complete. It is also
+ // signaled when the demuxer is being stopped.
+ base::WaitableEvent read_event_;
+
+ // Flag to indicate if read has ever failed. Once set to true, it will
+ // never be reset. This flag is set true and accessed in Read().
+ bool read_has_failed_;
+
+ size_t last_read_bytes_;
+ int64 read_position_;
+
DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxer);
};
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index 6f05004..08580da 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -4,6 +4,7 @@
#include <deque>
+#include "base/thread.h"
#include "media/base/filters.h"
#include "media/base/mock_ffmpeg.h"
#include "media/base/mock_filter_host.h"
@@ -16,9 +17,12 @@
using ::testing::_;
using ::testing::DoAll;
using ::testing::InSequence;
+using ::testing::Invoke;
+using ::testing::NotNull;
using ::testing::Return;
using ::testing::SetArgumentPointee;
using ::testing::StrictMock;
+using ::testing::WithArgs;
namespace media {
@@ -76,7 +80,7 @@ class FFmpegDemuxerTest : public testing::Test {
memset(&streams_, 0, sizeof(streams_));
memset(&codecs_, 0, sizeof(codecs_));
- // Initialize AVCodexContext structures.
+ // Initialize AVCodecContext structures.
codecs_[AV_STREAM_DATA].codec_type = CODEC_TYPE_DATA;
codecs_[AV_STREAM_DATA].codec_id = CODEC_ID_NONE;
@@ -662,4 +666,109 @@ TEST_F(FFmpegDemuxerTest, Stop) {
MockFFmpeg::get()->CheckPoint(1);
}
+class MockFFmpegDemuxer : public FFmpegDemuxer {
+ public:
+ MockFFmpegDemuxer() {}
+ virtual ~MockFFmpegDemuxer() {}
+
+ MOCK_METHOD0(WaitForRead, size_t());
+ MOCK_METHOD1(SignalReadCompleted, void(size_t size));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockFFmpegDemuxer);
+};
+
+// A gmock helper method to execute the callback and deletes it.
+void RunCallback(size_t size, DataSource::ReadCallback* callback) {
+ DCHECK(callback);
+ callback->RunWithParams(Tuple1<size_t>(size));
+ delete callback;
+}
+
+TEST_F(FFmpegDemuxerTest, ProtocolRead) {
+ // Creates a demuxer.
+ scoped_refptr<MockFFmpegDemuxer> demuxer = new MockFFmpegDemuxer();
+ ASSERT_TRUE(demuxer);
+ demuxer->set_host(&host_);
+ demuxer->set_message_loop(&message_loop_);
+ demuxer->data_source_ = data_source_;
+
+ uint8 kBuffer[1];
+ InSequence s;
+ // Actions taken in the first read.
+ EXPECT_CALL(*data_source_, Read(0, 512, kBuffer, NotNull()))
+ .WillOnce(WithArgs<1, 3>(Invoke(&RunCallback)));
+ EXPECT_CALL(*demuxer, SignalReadCompleted(512));
+ EXPECT_CALL(*demuxer, WaitForRead())
+ .WillOnce(Return(512));
+
+ // Second read.
+ EXPECT_CALL(*data_source_, Read(512, 512, kBuffer, NotNull()))
+ .WillOnce(WithArgs<1, 3>(Invoke(&RunCallback)));
+ EXPECT_CALL(*demuxer, SignalReadCompleted(512));
+ EXPECT_CALL(*demuxer, WaitForRead())
+ .WillOnce(Return(512));
+
+ // Called during SetPosition().
+ EXPECT_CALL(*data_source_, GetSize(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(1024), Return(true)));
+
+ // This read complete signal is generated when demuxer is stopped.
+ EXPECT_CALL(*demuxer, SignalReadCompleted(DataSource::kReadError));
+
+ EXPECT_EQ(512, demuxer->Read(512, kBuffer));
+ int64 position;
+ EXPECT_TRUE(demuxer->GetPosition(&position));
+ EXPECT_EQ(512, position);
+ EXPECT_EQ(512, demuxer->Read(512, kBuffer));
+ EXPECT_FALSE(demuxer->SetPosition(1024));
+
+ demuxer->Stop();
+}
+
+TEST_F(FFmpegDemuxerTest, ProtocolGetSetPosition) {
+ {
+ SCOPED_TRACE("");
+ InitializeDemuxer();
+ }
+
+ InSequence s;
+
+ EXPECT_CALL(*data_source_, GetSize(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(1024), Return(true)));
+ EXPECT_CALL(*data_source_, GetSize(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(1024), Return(true)));
+
+ int64 position;
+ EXPECT_TRUE(demuxer_->GetPosition(&position));
+ EXPECT_EQ(0, position);
+
+ EXPECT_TRUE(demuxer_->SetPosition(512));
+ EXPECT_FALSE(demuxer_->SetPosition(2048));
+ EXPECT_TRUE(demuxer_->GetPosition(&position));
+ EXPECT_EQ(512, position);
+}
+
+TEST_F(FFmpegDemuxerTest, ProtocolGetSize) {
+ {
+ SCOPED_TRACE("");
+ InitializeDemuxer();
+ }
+
+ EXPECT_CALL(*data_source_, GetSize(_))
+ .WillOnce(DoAll(SetArgumentPointee<0>(1024), Return(true)));
+
+ int64 size;
+ EXPECT_TRUE(demuxer_->GetSize(&size));
+ EXPECT_EQ(1024, size);
+}
+
+TEST_F(FFmpegDemuxerTest, ProtocolIsStreamed) {
+ {
+ SCOPED_TRACE("");
+ InitializeDemuxer();
+ }
+ EXPECT_FALSE(demuxer_->IsStreamed());
+}
+
} // namespace media
diff --git a/media/filters/ffmpeg_glue.cc b/media/filters/ffmpeg_glue.cc
index ca3ed1d..135b3bb 100644
--- a/media/filters/ffmpeg_glue.cc
+++ b/media/filters/ffmpeg_glue.cc
@@ -9,24 +9,26 @@
namespace {
+media::FFmpegURLProtocol* ToProtocol(void* data) {
+ return reinterpret_cast<media::FFmpegURLProtocol*>(data);
+}
+
// 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)
+ media::FFmpegURLProtocol* protocol;
+ media::FFmpegGlue::get()->GetProtocol(filename, &protocol);
+ if (!protocol)
return AVERROR_IO;
- data_source->AddRef();
- h->priv_data = data_source;
+ h->priv_data = protocol;
h->flags = URL_RDONLY;
- h->is_streamed = !data_source->IsSeekable();
+ h->is_streamed = protocol->IsStreamed();
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);
+ media::FFmpegURLProtocol* protocol = ToProtocol(h->priv_data);
+ int result = protocol->Read(size, buf);
if (result < 0)
result = AVERROR_IO;
return result;
@@ -38,33 +40,32 @@ int WriteContext(URLContext* h, unsigned char* buf, int size) {
}
offset_t SeekContext(URLContext* h, offset_t offset, int whence) {
- media::DataSource* data_source =
- reinterpret_cast<media::DataSource*>(h->priv_data);
+ media::FFmpegURLProtocol* protocol = ToProtocol(h->priv_data);
offset_t new_offset = AVERROR_IO;
switch (whence) {
case SEEK_SET:
- if (data_source->SetPosition(offset))
- data_source->GetPosition(&new_offset);
+ if (protocol->SetPosition(offset))
+ protocol->GetPosition(&new_offset);
break;
case SEEK_CUR:
int64 pos;
- if (!data_source->GetPosition(&pos))
+ if (!protocol->GetPosition(&pos))
break;
- if (data_source->SetPosition(pos + offset))
- data_source->GetPosition(&new_offset);
+ if (protocol->SetPosition(pos + offset))
+ protocol->GetPosition(&new_offset);
break;
case SEEK_END:
int64 size;
- if (!data_source->GetSize(&size))
+ if (!protocol->GetSize(&size))
break;
- if (data_source->SetPosition(size + offset))
- data_source->GetPosition(&new_offset);
+ if (protocol->SetPosition(size + offset))
+ protocol->GetPosition(&new_offset);
break;
case AVSEEK_SIZE:
- data_source->GetSize(&new_offset);
+ protocol->GetSize(&new_offset);
break;
default:
@@ -76,9 +77,6 @@ offset_t SeekContext(URLContext* h, offset_t offset, int whence) {
}
int CloseContext(URLContext* h) {
- media::DataSource* data_source =
- reinterpret_cast<media::DataSource*>(h->priv_data);
- data_source->Release();
h->priv_data = NULL;
return 0;
}
@@ -93,7 +91,7 @@ namespace media {
static const char kProtocol[] = "http";
// Fill out our FFmpeg protocol definition.
-static URLProtocol kFFmpegProtocol = {
+static URLProtocol kFFmpegURLProtocol = {
kProtocol,
&OpenContext,
&ReadContext,
@@ -105,7 +103,7 @@ static URLProtocol kFFmpegProtocol = {
FFmpegGlue::FFmpegGlue() {
// Register our protocol glue code with FFmpeg.
avcodec_init();
- av_register_protocol(&kFFmpegProtocol);
+ av_register_protocol(&kFFmpegURLProtocol);
// Now register the rest of FFmpeg.
av_register_all();
@@ -114,43 +112,43 @@ FFmpegGlue::FFmpegGlue() {
FFmpegGlue::~FFmpegGlue() {
}
-std::string FFmpegGlue::AddDataSource(DataSource* data_source) {
+std::string FFmpegGlue::AddProtocol(FFmpegURLProtocol* protocol) {
AutoLock auto_lock(lock_);
- std::string key = GetDataSourceKey(data_source);
- if (data_sources_.find(key) == data_sources_.end()) {
- data_sources_[key] = data_source;
+ std::string key = GetProtocolKey(protocol);
+ if (protocols_.find(key) == protocols_.end()) {
+ protocols_[key] = protocol;
}
return key;
}
-void FFmpegGlue::RemoveDataSource(DataSource* data_source) {
+void FFmpegGlue::RemoveProtocol(FFmpegURLProtocol* protocol) {
AutoLock auto_lock(lock_);
- for (DataSourceMap::iterator cur, iter = data_sources_.begin();
- iter != data_sources_.end();) {
+ for (ProtocolMap::iterator cur, iter = protocols_.begin();
+ iter != protocols_.end();) {
cur = iter;
iter++;
- if (cur->second == data_source)
- data_sources_.erase(cur);
+ if (cur->second == protocol)
+ protocols_.erase(cur);
}
}
-void FFmpegGlue::GetDataSource(const std::string& key,
- scoped_refptr<DataSource>* data_source) {
+void FFmpegGlue::GetProtocol(const std::string& key,
+ FFmpegURLProtocol** protocol) {
AutoLock auto_lock(lock_);
- DataSourceMap::iterator iter = data_sources_.find(key);
- if (iter == data_sources_.end()) {
- *data_source = NULL;
+ ProtocolMap::iterator iter = protocols_.find(key);
+ if (iter == protocols_.end()) {
+ *protocol = NULL;
return;
}
- *data_source = iter->second;
+ *protocol = 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));
+std::string FFmpegGlue::GetProtocolKey(FFmpegURLProtocol* protocol) {
+ // Use the FFmpegURLProtocol's memory address to generate the unique string.
+ // This also has the nice property that adding the same FFmpegURLProtocol
+ // reference will not generate duplicate entries.
+ return StringPrintf("%s://0x%lx", kProtocol, static_cast<void*>(protocol));
}
} // namespace media
diff --git a/media/filters/ffmpeg_glue.h b/media/filters/ffmpeg_glue.h
index 0db1ce8..228c964 100644
--- a/media/filters/ffmpeg_glue.h
+++ b/media/filters/ffmpeg_glue.h
@@ -38,22 +38,50 @@ typedef int64 offset_t;
namespace media {
-class DataSource;
+class FFmpegURLProtocol {
+ public:
+ FFmpegURLProtocol() {
+ }
+
+ virtual ~FFmpegURLProtocol() {
+ }
+
+ // Read the given amount of bytes into data, returns the number of bytes read
+ // if successful, kReadError otherwise.
+ virtual int Read(int size, uint8* data) = 0;
+
+ // Returns true and the current file position for this file, false if the
+ // file position could not be retrieved.
+ virtual bool GetPosition(int64* position_out) = 0;
+
+ // Returns true if the file position could be set, false otherwise.
+ virtual bool SetPosition(int64 position) = 0;
+
+ // Returns true and the file size, false if the file size could not be
+ // retrieved.
+ virtual bool GetSize(int64* size_out) = 0;
+
+ // Returns false if this protocol supports random seeking.
+ virtual bool IsStreamed() = 0;
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(FFmpegURLProtocol);
+};
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);
+ // Adds a FFmpegProtocol to the FFmpeg glue layer and returns a unique string
+ // that can be passed to FFmpeg to identify the data source.
+ std::string AddProtocol(FFmpegURLProtocol* protocol);
- // Removes a DataSource from the FFmpeg glue layer. Using strings from
- // previously added DataSources will no longer work.
- void RemoveDataSource(DataSource* data_source);
+ // Removes a FFmpegProtocol from the FFmpeg glue layer. Using strings from
+ // previously added FFmpegProtocols will no longer work.
+ void RemoveProtocol(FFmpegURLProtocol* protocol);
- // 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);
+ // Assigns the FFmpegProtocol identified with by the given key to
+ // |protocol|, or assigns NULL if no such FFmpegProtocol could be found.
+ void GetProtocol(const std::string& key,
+ FFmpegURLProtocol** protocol);
private:
// Only allow Singleton to create and delete FFmpegGlue.
@@ -63,14 +91,14 @@ class FFmpegGlue : public Singleton<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);
+ std::string GetProtocolKey(FFmpegURLProtocol* protocol);
// 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_;
+ // Map between keys and FFmpegProtocol references.
+ typedef std::map<std::string, FFmpegURLProtocol*> ProtocolMap;
+ ProtocolMap protocols_;
DISALLOW_COPY_AND_ASSIGN(FFmpegGlue);
};
diff --git a/media/filters/ffmpeg_glue_unittest.cc b/media/filters/ffmpeg_glue_unittest.cc
index 9f60868..904f6a5 100644
--- a/media/filters/ffmpeg_glue_unittest.cc
+++ b/media/filters/ffmpeg_glue_unittest.cc
@@ -17,6 +17,21 @@ using ::testing::StrictMock;
namespace media {
+class MockProtocol : public FFmpegURLProtocol {
+ public:
+ MockProtocol() {
+ }
+
+ MOCK_METHOD2(Read, int(int size, uint8* data));
+ MOCK_METHOD1(GetPosition, bool(int64* position_out));
+ MOCK_METHOD1(SetPosition, bool(int64 position));
+ MOCK_METHOD1(GetSize, bool(int64* size_out));
+ MOCK_METHOD0(IsStreamed, bool());
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(MockProtocol);
+};
+
class FFmpegGlueTest : public ::testing::Test {
public:
FFmpegGlueTest() {
@@ -27,17 +42,17 @@ class FFmpegGlueTest : public ::testing::Test {
MockFFmpeg::set(NULL);
}
- // Helper to open a URLContext pointing to the given mocked data source.
+ // Helper to open a URLContext pointing to the given mocked protocol.
// Callers are expected to close the context at the end of their test.
- virtual void OpenContext(MockDataSource* data_source, URLContext* context) {
- // IsSeekable() is called when opening.
- EXPECT_CALL(*data_source, IsSeekable()).WillOnce(Return(false));
+ virtual void OpenContext(MockProtocol* protocol, URLContext* context) {
+ // IsStreamed() is called when opening.
+ EXPECT_CALL(*protocol, IsStreamed()).WillOnce(Return(true));
- // Add the data source to the glue layer and open a context.
- std::string key = FFmpegGlue::get()->AddDataSource(data_source);
+ // Add the protocol to the glue layer and open a context.
+ std::string key = FFmpegGlue::get()->AddProtocol(protocol);
memset(context, 0, sizeof(*context));
EXPECT_EQ(0, protocol_->url_open(context, key.c_str(), 0));
- FFmpegGlue::get()->RemoveDataSource(data_source);
+ FFmpegGlue::get()->RemoveProtocol(protocol);
}
protected:
@@ -68,55 +83,55 @@ TEST_F(FFmpegGlueTest, InitializeFFmpeg) {
EXPECT_TRUE(protocol_->url_write);
}
-TEST_F(FFmpegGlueTest, AddRemoveGetDataSource) {
+TEST_F(FFmpegGlueTest, AddRemoveGetProtocol) {
// Prepare testing data.
FFmpegGlue* glue = FFmpegGlue::get();
- // Create our data sources and add them to the glue layer.
- scoped_refptr<StrictMock<Destroyable<MockDataSource> > > data_source_a
- = new StrictMock<Destroyable<MockDataSource> >();
- scoped_refptr<StrictMock<Destroyable<MockDataSource> > > data_source_b
- = new StrictMock<Destroyable<MockDataSource> >();
+ // Create our protocols and add them to the glue layer.
+ scoped_ptr<StrictMock<Destroyable<MockProtocol> > > protocol_a(
+ new StrictMock<Destroyable<MockProtocol> >());
+ scoped_ptr<StrictMock<Destroyable<MockProtocol> > > protocol_b(
+ new StrictMock<Destroyable<MockProtocol> >());
// Make sure the keys are unique.
- std::string key_a = glue->AddDataSource(data_source_a);
- std::string key_b = glue->AddDataSource(data_source_b);
+ std::string key_a = glue->AddProtocol(protocol_a.get());
+ std::string key_b = glue->AddProtocol(protocol_b.get());
EXPECT_EQ(0u, key_a.find("http://"));
EXPECT_EQ(0u, key_b.find("http://"));
EXPECT_NE(key_a, key_b);
- // Our keys should return our data sources.
- scoped_refptr<DataSource> data_source_c;
- scoped_refptr<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);
+ // Our keys should return our protocols.
+ FFmpegURLProtocol* protocol_c;
+ FFmpegURLProtocol* protocol_d;
+ glue->GetProtocol(key_a, &protocol_c);
+ glue->GetProtocol(key_b, &protocol_d);
+ EXPECT_EQ(protocol_a.get(), protocol_c);
+ EXPECT_EQ(protocol_b.get(), protocol_d);
- // Adding the same DataSource should create the same key and not add an extra
+ // Adding the same Protocol should create the same key and not add an extra
// reference.
- std::string key_a2 = glue->AddDataSource(data_source_a);
+ std::string key_a2 = glue->AddProtocol(protocol_a.get());
EXPECT_EQ(key_a, key_a2);
- glue->GetDataSource(key_a2, &data_source_c);
- EXPECT_EQ(data_source_a, data_source_c);
+ glue->GetProtocol(key_a2, &protocol_c);
+ EXPECT_EQ(protocol_a.get(), protocol_c);
- // Removes the data sources then releases our references. They should be
+ // Removes the protocols then releases our references. They should be
// destroyed.
InSequence s;
- EXPECT_CALL(*data_source_a, OnDestroy());
- EXPECT_CALL(*data_source_b, OnDestroy());
+ EXPECT_CALL(*protocol_a, OnDestroy());
+ EXPECT_CALL(*protocol_b, OnDestroy());
EXPECT_CALL(mock_ffmpeg_, CheckPoint(0));
- 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);
- data_source_a = NULL;
- data_source_b = NULL;
+ glue->RemoveProtocol(protocol_a.get());
+ glue->GetProtocol(key_a, &protocol_c);
+ EXPECT_FALSE(protocol_c);
+ glue->GetProtocol(key_b, &protocol_d);
+ EXPECT_EQ(protocol_b.get(), protocol_d);
+ glue->RemoveProtocol(protocol_b.get());
+ glue->GetProtocol(key_b, &protocol_d);
+ EXPECT_FALSE(protocol_d);
+ protocol_a.reset();
+ protocol_b.reset();
// Data sources should be deleted by this point.
mock_ffmpeg_.CheckPoint(0);
@@ -126,41 +141,41 @@ TEST_F(FFmpegGlueTest, OpenClose) {
// Prepare testing data.
FFmpegGlue* glue = FFmpegGlue::get();
- // Create our data source and add them to the glue layer.
- scoped_refptr<StrictMock<Destroyable<MockDataSource> > > data_source
- = new StrictMock<Destroyable<MockDataSource> >();
- EXPECT_CALL(*data_source, IsSeekable()).WillOnce(Return(false));
- std::string key = glue->AddDataSource(data_source);
+ // Create our protocol and add them to the glue layer.
+ scoped_ptr<StrictMock<Destroyable<MockProtocol> > > protocol(
+ new StrictMock<Destroyable<MockProtocol> >());
+ EXPECT_CALL(*protocol, IsStreamed()).WillOnce(Return(true));
+ std::string key = glue->AddProtocol(protocol.get());
// Prepare FFmpeg URLContext structure.
URLContext context;
memset(&context, 0, sizeof(context));
- // Test opening a URLContext with a data source that doesn't exist.
+ // Test opening a URLContext with a protocol that doesn't exist.
EXPECT_EQ(AVERROR_IO, protocol_->url_open(&context, "foobar", 0));
- // Test opening a URLContext with our data source.
+ // Test opening a URLContext with our protocol.
EXPECT_EQ(0, protocol_->url_open(&context, key.c_str(), 0));
EXPECT_EQ(URL_RDONLY, context.flags);
- EXPECT_EQ(data_source, context.priv_data);
+ EXPECT_EQ(protocol.get(), context.priv_data);
EXPECT_TRUE(context.is_streamed);
// We're going to remove references one by one until the last reference is
- // held by FFmpeg. Once we close the URLContext, the data source should be
+ // held by FFmpeg. Once we close the URLContext, the protocol should be
// destroyed.
InSequence s;
EXPECT_CALL(mock_ffmpeg_, CheckPoint(0));
EXPECT_CALL(mock_ffmpeg_, CheckPoint(1));
- EXPECT_CALL(*data_source, OnDestroy());
+ EXPECT_CALL(*protocol, OnDestroy());
EXPECT_CALL(mock_ffmpeg_, CheckPoint(2));
- // Remove the data source from the glue layer, releasing a reference.
- glue->RemoveDataSource(data_source);
+ // Remove the protocol from the glue layer, releasing a reference.
+ glue->RemoveProtocol(protocol.get());
mock_ffmpeg_.CheckPoint(0);
// Remove our own reference -- URLContext should maintain a reference.
- data_source = NULL;
mock_ffmpeg_.CheckPoint(1);
+ protocol.reset();
// Close the URLContext, which should release the final reference.
EXPECT_EQ(0, protocol_->url_close(&context));
@@ -168,64 +183,64 @@ TEST_F(FFmpegGlueTest, OpenClose) {
}
TEST_F(FFmpegGlueTest, Write) {
- scoped_refptr<StrictMock<MockDataSource> > data_source
- = new StrictMock<MockDataSource>();
+ scoped_ptr<StrictMock<MockProtocol> > protocol(
+ new StrictMock<MockProtocol>());
URLContext context;
- OpenContext(data_source, &context);
+ OpenContext(protocol.get(), &context);
const int kBufferSize = 16;
uint8 buffer[kBufferSize];
- // Writing should always fail and never call the data source.
+ // Writing should always fail and never call the protocol.
EXPECT_EQ(AVERROR_IO, protocol_->url_write(&context, NULL, 0));
EXPECT_EQ(AVERROR_IO, protocol_->url_write(&context, buffer, 0));
EXPECT_EQ(AVERROR_IO, protocol_->url_write(&context, buffer, kBufferSize));
- // Destroy the data source.
+ // Destroy the protocol.
protocol_->url_close(&context);
}
TEST_F(FFmpegGlueTest, Read) {
- scoped_refptr<StrictMock<MockDataSource> > data_source
- = new StrictMock<MockDataSource>();
+ scoped_ptr<StrictMock<MockProtocol> > protocol(
+ new StrictMock<MockProtocol>());
URLContext context;
- OpenContext(data_source, &context);
+ OpenContext(protocol.get(), &context);
const int kBufferSize = 16;
uint8 buffer[kBufferSize];
// Reads are for the most part straight-through calls to Read().
InSequence s;
- EXPECT_CALL(*data_source, Read(buffer, 0))
+ EXPECT_CALL(*protocol, Read(0, buffer))
.WillOnce(Return(0));
- EXPECT_CALL(*data_source, Read(buffer, kBufferSize))
+ EXPECT_CALL(*protocol, Read(kBufferSize, buffer))
.WillOnce(Return(kBufferSize));
- EXPECT_CALL(*data_source, Read(buffer, kBufferSize))
+ EXPECT_CALL(*protocol, Read(kBufferSize, buffer))
.WillOnce(Return(DataSource::kReadError));
EXPECT_EQ(0, protocol_->url_read(&context, buffer, 0));
EXPECT_EQ(kBufferSize, protocol_->url_read(&context, buffer, kBufferSize));
EXPECT_EQ(AVERROR_IO, protocol_->url_read(&context, buffer, kBufferSize));
- // Destroy the data source.
+ // Destroy the protocol.
protocol_->url_close(&context);
}
TEST_F(FFmpegGlueTest, Seek) {
- scoped_refptr<StrictMock<MockDataSource> > data_source
- = new StrictMock<MockDataSource>();
+ scoped_ptr<StrictMock<MockProtocol> > protocol(
+ new StrictMock<MockProtocol>());
URLContext context;
- OpenContext(data_source, &context);
+ OpenContext(protocol.get(), &context);
// SEEK_SET should be a straight-through call to SetPosition(), which when
// successful will return the result from GetPosition().
InSequence s;
- EXPECT_CALL(*data_source, SetPosition(-16))
+ EXPECT_CALL(*protocol, SetPosition(-16))
.WillOnce(Return(false));
- EXPECT_CALL(*data_source, SetPosition(16))
+ EXPECT_CALL(*protocol, SetPosition(16))
.WillOnce(Return(true));
- EXPECT_CALL(*data_source, GetPosition(_))
+ EXPECT_CALL(*protocol, GetPosition(_))
.WillOnce(DoAll(SetArgumentPointee<0>(8), Return(true)));
EXPECT_EQ(AVERROR_IO, protocol_->url_seek(&context, -16, SEEK_SET));
@@ -233,19 +248,19 @@ TEST_F(FFmpegGlueTest, Seek) {
// SEEK_CUR should call GetPosition() first, and if it succeeds add the offset
// to the result then call SetPosition()+GetPosition().
- EXPECT_CALL(*data_source, GetPosition(_))
+ EXPECT_CALL(*protocol, GetPosition(_))
.WillOnce(Return(false));
- EXPECT_CALL(*data_source, GetPosition(_))
+ EXPECT_CALL(*protocol, GetPosition(_))
.WillOnce(DoAll(SetArgumentPointee<0>(8), Return(true)));
- EXPECT_CALL(*data_source, SetPosition(16))
+ EXPECT_CALL(*protocol, SetPosition(16))
.WillOnce(Return(false));
- EXPECT_CALL(*data_source, GetPosition(_))
+ EXPECT_CALL(*protocol, GetPosition(_))
.WillOnce(DoAll(SetArgumentPointee<0>(8), Return(true)));
- EXPECT_CALL(*data_source, SetPosition(16))
+ EXPECT_CALL(*protocol, SetPosition(16))
.WillOnce(Return(true));
- EXPECT_CALL(*data_source, GetPosition(_))
+ EXPECT_CALL(*protocol, GetPosition(_))
.WillOnce(DoAll(SetArgumentPointee<0>(16), Return(true)));
EXPECT_EQ(AVERROR_IO, protocol_->url_seek(&context, 8, SEEK_CUR));
@@ -254,19 +269,19 @@ TEST_F(FFmpegGlueTest, Seek) {
// SEEK_END should call GetSize() first, and if it succeeds add the offset
// to the result then call SetPosition()+GetPosition().
- EXPECT_CALL(*data_source, GetSize(_))
+ EXPECT_CALL(*protocol, GetSize(_))
.WillOnce(Return(false));
- EXPECT_CALL(*data_source, GetSize(_))
+ EXPECT_CALL(*protocol, GetSize(_))
.WillOnce(DoAll(SetArgumentPointee<0>(16), Return(true)));
- EXPECT_CALL(*data_source, SetPosition(8))
+ EXPECT_CALL(*protocol, SetPosition(8))
.WillOnce(Return(false));
- EXPECT_CALL(*data_source, GetSize(_))
+ EXPECT_CALL(*protocol, GetSize(_))
.WillOnce(DoAll(SetArgumentPointee<0>(16), Return(true)));
- EXPECT_CALL(*data_source, SetPosition(8))
+ EXPECT_CALL(*protocol, SetPosition(8))
.WillOnce(Return(true));
- EXPECT_CALL(*data_source, GetPosition(_))
+ EXPECT_CALL(*protocol, GetPosition(_))
.WillOnce(DoAll(SetArgumentPointee<0>(8), Return(true)));
EXPECT_EQ(AVERROR_IO, protocol_->url_seek(&context, -8, SEEK_END));
@@ -274,34 +289,34 @@ TEST_F(FFmpegGlueTest, Seek) {
EXPECT_EQ(8, protocol_->url_seek(&context, -8, SEEK_END));
// AVSEEK_SIZE should be a straight-through call to GetSize().
- EXPECT_CALL(*data_source, GetSize(_))
+ EXPECT_CALL(*protocol, GetSize(_))
.WillOnce(Return(false));
- EXPECT_CALL(*data_source, GetSize(_))
+ EXPECT_CALL(*protocol, GetSize(_))
.WillOnce(DoAll(SetArgumentPointee<0>(16), Return(true)));
EXPECT_EQ(AVERROR_IO, protocol_->url_seek(&context, 0, AVSEEK_SIZE));
EXPECT_EQ(16, protocol_->url_seek(&context, 0, AVSEEK_SIZE));
- // Destroy the data source.
+ // Destroy the protocol.
protocol_->url_close(&context);
}
TEST_F(FFmpegGlueTest, Destroy) {
- // Create our data source and add them to the glue layer.
- scoped_refptr<StrictMock<Destroyable<MockDataSource> > > data_source
- = new StrictMock<Destroyable<MockDataSource> >();
- std::string key = FFmpegGlue::get()->AddDataSource(data_source);
+ // Create our protocol and add them to the glue layer.
+ scoped_ptr<StrictMock<Destroyable<MockProtocol> > > protocol(
+ new StrictMock<Destroyable<MockProtocol> >());
+ std::string key = FFmpegGlue::get()->AddProtocol(protocol.get());
- // We should expect the data source to get destroyed when the unit test
+ // We should expect the protocol to get destroyed when the unit test
// exits.
InSequence s;
EXPECT_CALL(mock_ffmpeg_, CheckPoint(0));
- EXPECT_CALL(*data_source, OnDestroy());
+ EXPECT_CALL(*protocol, OnDestroy());
// Remove our own reference, we shouldn't be destroyed yet.
- data_source = NULL;
mock_ffmpeg_.CheckPoint(0);
+ protocol.reset();
// ~FFmpegGlue() will be called when this unit test finishes execution. By
// leaving something inside FFmpegGlue's map we get to test our cleanup code.
diff --git a/media/filters/file_data_source.cc b/media/filters/file_data_source.cc
index 36e0582..d440918 100644
--- a/media/filters/file_data_source.cc
+++ b/media/filters/file_data_source.cc
@@ -2,6 +2,8 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
+#include <limits>
+
#include "base/file_util.h"
#include "base/string_util.h"
#include "media/base/filter_host.h"
@@ -59,50 +61,35 @@ const MediaFormat& FileDataSource::media_format() {
return media_format_;
}
-size_t FileDataSource::Read(uint8* data, size_t size) {
+void FileDataSource::Read(int64 position, size_t size, uint8* data,
+ ReadCallback* read_callback) {
DCHECK(file_);
AutoLock l(lock_);
if (file_) {
+#if defined(OS_WIN)
+ if (_fseeki64(file_, position, SEEK_SET)) {
+ read_callback->RunWithParams(
+ Tuple1<size_t>(static_cast<size_t>(DataSource::kReadError)));
+ return;
+ }
+#else
+ CHECK(position <= std::numeric_limits<int32>::max());
+ // TODO(hclam): Change fseek() to support 64-bit position.
+ if (fseek(file_, static_cast<int32>(position), SEEK_SET)) {
+ read_callback->RunWithParams(
+ Tuple1<size_t>(static_cast<size_t>(DataSource::kReadError)));
+ return;
+ }
+#endif
size_t size_read = fread(data, 1, size, file_);
if (size_read == size || !ferror(file_)) {
- return size_read;
+ read_callback->RunWithParams(
+ Tuple1<size_t>(static_cast<size_t>(size_read)));
+ return;
}
}
- 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;
+ read_callback->RunWithParams(Tuple1<size_t>(static_cast<size_t>(kReadError)));
}
bool FileDataSource::GetSize(int64* size_out) {
diff --git a/media/filters/file_data_source.h b/media/filters/file_data_source.h
index 5c91ce1..760b7c5 100644
--- a/media/filters/file_data_source.h
+++ b/media/filters/file_data_source.h
@@ -28,9 +28,8 @@ class FileDataSource : public DataSource {
// Implementation of DataSource.
virtual void Initialize(const std::string& url, FilterCallback* callback);
virtual const MediaFormat& media_format();
- virtual size_t Read(uint8* data, size_t size);
- virtual bool GetPosition(int64* position_out);
- virtual bool SetPosition(int64 position);
+ virtual void Read(int64 position, size_t size, uint8* data,
+ ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
virtual bool IsSeekable();
diff --git a/media/filters/file_data_source_unittest.cc b/media/filters/file_data_source_unittest.cc
index 9c4b9ee8..193f31a 100644
--- a/media/filters/file_data_source_unittest.cc
+++ b/media/filters/file_data_source_unittest.cc
@@ -14,6 +14,21 @@
using ::testing::NiceMock;
using ::testing::StrictMock;
+namespace {
+
+class ReadCallbackHandler {
+ public:
+ ReadCallbackHandler() {
+ }
+
+ MOCK_METHOD1(ReadCallback, void(size_t size));
+
+ private:
+ DISALLOW_COPY_AND_ASSIGN(ReadCallbackHandler);
+};
+
+} // namespace
+
namespace media {
// Returns a path to the test file which contains the string "0123456789"
@@ -52,7 +67,6 @@ TEST(FileDataSourceTest, OpenFile) {
// Use the mock filter host to directly call the Read and GetPosition methods.
TEST(FileDataSourceTest, ReadData) {
- int64 position;
int64 size;
uint8 ten_bytes[10];
@@ -60,28 +74,29 @@ TEST(FileDataSourceTest, ReadData) {
NiceMock<MockFilterHost> host;
NiceMock<MockFilterCallback> callback;
scoped_refptr<FileDataSource> filter = new FileDataSource();
+
filter->set_host(&host);
filter->Initialize(TestFileURL(), callback.NewCallback());
EXPECT_TRUE(filter->GetSize(&size));
EXPECT_EQ(10, size);
- EXPECT_TRUE(filter->GetPosition(&position));
- EXPECT_EQ(0, position);
-
- EXPECT_EQ(10u, filter->Read(ten_bytes, sizeof(ten_bytes)));
+ ReadCallbackHandler handler;
+ EXPECT_CALL(handler, ReadCallback(10));
+ filter->Read(0, 10, ten_bytes,
+ NewCallback(&handler, &ReadCallbackHandler::ReadCallback));
EXPECT_EQ('0', ten_bytes[0]);
EXPECT_EQ('5', ten_bytes[5]);
EXPECT_EQ('9', ten_bytes[9]);
- EXPECT_TRUE(filter->GetPosition(&position));
- EXPECT_EQ(10, position);
- EXPECT_EQ(0u, filter->Read(ten_bytes, sizeof(ten_bytes)));
- EXPECT_TRUE(filter->SetPosition(5));
- EXPECT_EQ(5u, filter->Read(ten_bytes, sizeof(ten_bytes)));
+ EXPECT_CALL(handler, ReadCallback(0));
+ filter->Read(10, 10, ten_bytes,
+ NewCallback(&handler, &ReadCallbackHandler::ReadCallback));
+
+ EXPECT_CALL(handler, ReadCallback(5));
+ filter->Read(5, 10, ten_bytes,
+ NewCallback(&handler, &ReadCallbackHandler::ReadCallback));
EXPECT_EQ('5', ten_bytes[0]);
- EXPECT_TRUE(filter->GetPosition(&position));
- EXPECT_EQ(10, position);
}
// Test that FileDataSource does nothing on Seek().