summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-22 21:37:17 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-22 21:37:17 +0000
commit8e296bbd2d147c270d1839ab9dd93418532322e5 (patch)
tree250887882d757ad020aba85af7db935c4107a105 /media
parent944a14b8d46e63e603850c6c8b0d0638dc492a14 (diff)
downloadchromium_src-8e296bbd2d147c270d1839ab9dd93418532322e5.zip
chromium_src-8e296bbd2d147c270d1839ab9dd93418532322e5.tar.gz
chromium_src-8e296bbd2d147c270d1839ab9dd93418532322e5.tar.bz2
Changes to provide asynchronous read in data source:
1. FFmpegGlue now taks a FFmpegProtocol instead of DataSource as input 2. FFmpegDemuxr implements FFmpegProtocol and does the blocking read and submit asynchronous read request to DataSource (with unit tests) 3. Changed SimpleDataSource to work with asynchronous read (with unit tests) 4. Reimplemented BufferedDataSource to work with asynchronous read (with unit tests) 5. Moved BufferedDataSource from chrome/renderer/media to webkit/glue/media (for faster build/debug and better coverage in automated testing) TEST=BufferedDataSourceTest.*, SimpleDataSourceTest.*, FFmpegDemuxerTest.* Review URL: http://codereview.chromium.org/149567 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21326 0039d316-1c4b-4281-b951-d872f2087c98
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().