diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-13 20:47:40 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2012-11-13 20:47:40 +0000 |
commit | f85a3dfb035114748fd3ea58b9f26ee3f8d785dc (patch) | |
tree | 823afd3d832eceb123413d242e6564f6ed13f514 /media | |
parent | 856eb49fe9279aba4f1a1602ce091a6d45b1d2b4 (diff) | |
download | chromium_src-f85a3dfb035114748fd3ea58b9f26ee3f8d785dc.zip chromium_src-f85a3dfb035114748fd3ea58b9f26ee3f8d785dc.tar.gz chromium_src-f85a3dfb035114748fd3ea58b9f26ee3f8d785dc.tar.bz2 |
Refactor FFmpegURLProtocol code from FFmpegDemuxer into BlockingUrlProtocol.
I cleaned up some comments and surrounding test code in FFmpegDemuxer and FileDataSource as well. There'll be some more clean up after I get this landed.
BUG=160640
TEST=FFmpegDemuxer and BlockingUrlProtocol tests
Review URL: https://codereview.chromium.org/11410052
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@167462 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media')
-rw-r--r-- | media/filters/blocking_url_protocol.cc | 87 | ||||
-rw-r--r-- | media/filters/blocking_url_protocol.h | 73 | ||||
-rw-r--r-- | media/filters/blocking_url_protocol_unittest.cc | 127 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 99 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.h | 35 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 94 | ||||
-rw-r--r-- | media/filters/file_data_source.cc | 16 | ||||
-rw-r--r-- | media/filters/file_data_source.h | 19 | ||||
-rw-r--r-- | media/media.gyp | 6 |
9 files changed, 332 insertions, 224 deletions
diff --git a/media/filters/blocking_url_protocol.cc b/media/filters/blocking_url_protocol.cc new file mode 100644 index 0000000..ceb5d71 --- /dev/null +++ b/media/filters/blocking_url_protocol.cc @@ -0,0 +1,87 @@ +// Copyright (c) 2012 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/blocking_url_protocol.h" + +#include "base/bind.h" +#include "media/base/data_source.h" +#include "media/ffmpeg/ffmpeg_common.h" + +namespace media { + +BlockingUrlProtocol::BlockingUrlProtocol( + const scoped_refptr<DataSource>& data_source, + const base::Closure& error_cb) + : data_source_(data_source), + error_cb_(error_cb), + read_event_(false, false), + read_has_failed_(false), + last_read_bytes_(0), + read_position_(0) { +} + +BlockingUrlProtocol::~BlockingUrlProtocol() {} + +void BlockingUrlProtocol::Abort() { + SignalReadCompleted(DataSource::kReadError); +} + +int BlockingUrlProtocol::Read(int size, uint8* data) { + // Read errors are unrecoverable. + if (read_has_failed_) + return AVERROR(EIO); + + // Even though FFmpeg defines AVERROR_EOF, it's not to be used with I/O + // routines. Instead return 0 for any read at or past EOF. + int64 file_size; + if (data_source_->GetSize(&file_size) && read_position_ >= file_size) + return 0; + + // Blocking read from data source until |last_read_bytes_| is set and event is + // signalled. + data_source_->Read(read_position_, size, data, base::Bind( + &BlockingUrlProtocol::SignalReadCompleted, base::Unretained(this))); + read_event_.Wait(); + + if (last_read_bytes_ == DataSource::kReadError) { + // TODO(scherkus): We shouldn't fire |error_cb_| if it was due to Abort(). + error_cb_.Run(); + read_has_failed_ = true; + return AVERROR(EIO); + } + + read_position_ += last_read_bytes_; + return last_read_bytes_; +} + +bool BlockingUrlProtocol::GetPosition(int64* position_out) { + *position_out = read_position_; + return true; +} + +bool BlockingUrlProtocol::SetPosition(int64 position) { + int64 file_size; + if ((data_source_->GetSize(&file_size) && position >= file_size) || + position < 0) { + return false; + } + + read_position_ = position; + return true; +} + +bool BlockingUrlProtocol::GetSize(int64* size_out) { + return data_source_->GetSize(size_out); +} + +bool BlockingUrlProtocol::IsStreaming() { + return data_source_->IsStreaming(); +} + +void BlockingUrlProtocol::SignalReadCompleted(int size) { + last_read_bytes_ = size; + read_event_.Signal(); +} + +} // namespace media diff --git a/media/filters/blocking_url_protocol.h b/media/filters/blocking_url_protocol.h new file mode 100644 index 0000000..dbffbbc --- /dev/null +++ b/media/filters/blocking_url_protocol.h @@ -0,0 +1,73 @@ +// Copyright (c) 2012 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_FILTERS_BLOCKING_URL_PROTOCOL_H_ +#define MEDIA_FILTERS_BLOCKING_URL_PROTOCOL_H_ + +#include "base/basictypes.h" +#include "base/callback.h" +#include "base/synchronization/waitable_event.h" +#include "media/filters/ffmpeg_glue.h" + +namespace media { + +class DataSource; + +// An implementation of FFmpegURLProtocol that blocks until the underlying +// asynchronous DataSource::Read() operation completes. +// +// TODO(scherkus): Should be more thread-safe as |last_read_bytes_| is updated +// from multiple threads without any protection. +class MEDIA_EXPORT BlockingUrlProtocol : public FFmpegURLProtocol { + public: + // Implements FFmpegURLProtocol using the given |data_source|. |error_cb| is + // fired any time DataSource::Read() returns an error. + // + // TODO(scherkus): After all blocking operations are isolated on a separate + // thread we should be able to eliminate |error_cb|. + BlockingUrlProtocol(const scoped_refptr<DataSource>& data_source, + const base::Closure& error_cb); + virtual ~BlockingUrlProtocol(); + + // Aborts any pending reads by returning a read error. After this method + // returns all subsequent calls to Read() will immediately fail. + // + // TODO(scherkus): Currently this will cause |error_cb| to fire. Fix. + void Abort(); + + // FFmpegURLProtocol implementation. + virtual int Read(int size, uint8* data) OVERRIDE; + virtual bool GetPosition(int64* position_out) OVERRIDE; + virtual bool SetPosition(int64 position) OVERRIDE; + virtual bool GetSize(int64* size_out) OVERRIDE; + virtual bool IsStreaming() OVERRIDE; + + private: + // Sets |last_read_bytes_| and signals the blocked thread that the read + // has completed. + void SignalReadCompleted(int size); + + scoped_refptr<DataSource> data_source_; + base::Closure error_cb_; + + // Used to convert an asynchronous DataSource::Read() into a blocking + // FFmpegUrlProtocol::Read(). + base::WaitableEvent read_event_; + + // Read errors and aborts are unrecoverable. Any subsequent reads will + // immediately fail when this is set to true. + bool read_has_failed_; + + // Cached number of bytes last read from the data source. + int last_read_bytes_; + + // Cached position within the data source. + int64 read_position_; + + DISALLOW_IMPLICIT_CONSTRUCTORS(BlockingUrlProtocol); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_BLOCKING_URL_PROTOCOL_H_ diff --git a/media/filters/blocking_url_protocol_unittest.cc b/media/filters/blocking_url_protocol_unittest.cc new file mode 100644 index 0000000..8bd30c1 --- /dev/null +++ b/media/filters/blocking_url_protocol_unittest.cc @@ -0,0 +1,127 @@ +// Copyright (c) 2012 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/bind.h" +#include "base/file_path.h" +#include "base/file_util.h" +#include "base/path_service.h" +#include "base/synchronization/waitable_event.h" +#include "media/filters/blocking_url_protocol.h" +#include "media/filters/file_data_source.h" +#include "media/ffmpeg/ffmpeg_common.h" +#include "testing/gtest/include/gtest/gtest.h" +#include "testing/gmock/include/gmock/gmock.h" + +namespace media { + +class BlockingUrlProtocolTest : public testing::Test { + public: + BlockingUrlProtocolTest() { + FilePath file_path; + EXPECT_TRUE(PathService::Get(base::DIR_SOURCE_ROOT, &file_path)); + file_path = file_path.Append(FILE_PATH_LITERAL("media")) + .Append(FILE_PATH_LITERAL("test")) + .Append(FILE_PATH_LITERAL("data")) + .AppendASCII("bear-320x240.webm"); + + data_source_ = new FileDataSource(); + CHECK(data_source_->Initialize(file_path.MaybeAsASCII())); + + url_protocol_.reset(new BlockingUrlProtocol(data_source_, base::Bind( + &BlockingUrlProtocolTest::OnDataSourceError, base::Unretained(this)))); + } + + virtual ~BlockingUrlProtocolTest() { + base::WaitableEvent stop_event(false, false); + data_source_->Stop(base::Bind( + &base::WaitableEvent::Signal, base::Unretained(&stop_event))); + stop_event.Wait(); + } + + MOCK_METHOD0(OnDataSourceError, void()); + + scoped_refptr<FileDataSource> data_source_; + scoped_ptr<BlockingUrlProtocol> url_protocol_; + + private: + DISALLOW_COPY_AND_ASSIGN(BlockingUrlProtocolTest); +}; + + +TEST_F(BlockingUrlProtocolTest, Read) { + // Set read head to zero as Initialize() will have parsed a bit of the file. + int64 position = 0; + EXPECT_TRUE(url_protocol_->SetPosition(0)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + EXPECT_EQ(0, position); + + // Read 32 bytes from offset zero and verify position. + uint8 buffer[32]; + EXPECT_EQ(32, url_protocol_->Read(32, buffer)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + EXPECT_EQ(32, position); + + // Read an additional 32 bytes and verify position. + EXPECT_EQ(32, url_protocol_->Read(32, buffer)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + EXPECT_EQ(64, position); + + // Seek to end and read until EOF. + int64 size = 0; + EXPECT_TRUE(url_protocol_->GetSize(&size)); + EXPECT_TRUE(url_protocol_->SetPosition(size - 48)); + EXPECT_EQ(32, url_protocol_->Read(32, buffer)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + EXPECT_EQ(size - 16, position); + + EXPECT_EQ(16, url_protocol_->Read(32, buffer)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + EXPECT_EQ(size, position); + + EXPECT_EQ(0, url_protocol_->Read(32, buffer)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + EXPECT_EQ(size, position); +} + +TEST_F(BlockingUrlProtocolTest, ReadError) { + data_source_->force_read_errors_for_testing(); + + uint8 buffer[32]; + EXPECT_CALL(*this, OnDataSourceError()); + EXPECT_EQ(AVERROR(EIO), url_protocol_->Read(32, buffer)); +} + +TEST_F(BlockingUrlProtocolTest, GetSetPosition) { + int64 size; + int64 position; + EXPECT_TRUE(url_protocol_->GetSize(&size)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + + EXPECT_TRUE(url_protocol_->SetPosition(512)); + EXPECT_FALSE(url_protocol_->SetPosition(size)); + EXPECT_FALSE(url_protocol_->SetPosition(size + 1)); + EXPECT_FALSE(url_protocol_->SetPosition(-1)); + EXPECT_TRUE(url_protocol_->GetPosition(&position)); + EXPECT_EQ(512, position); +} + +TEST_F(BlockingUrlProtocolTest, GetSize) { + int64 data_source_size = 0; + int64 url_protocol_size = 0; + EXPECT_TRUE(data_source_->GetSize(&data_source_size)); + EXPECT_TRUE(url_protocol_->GetSize(&url_protocol_size)); + EXPECT_NE(0, data_source_size); + EXPECT_EQ(data_source_size, url_protocol_size); +} + +TEST_F(BlockingUrlProtocolTest, IsStreaming) { + EXPECT_FALSE(data_source_->IsStreaming()); + EXPECT_FALSE(url_protocol_->IsStreaming()); + + data_source_->force_streaming_for_testing(); + EXPECT_TRUE(data_source_->IsStreaming()); + EXPECT_TRUE(url_protocol_->IsStreaming()); +} + +} // namespace media diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index c1c12f3..ee04455 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -263,14 +263,12 @@ FFmpegDemuxer::FFmpegDemuxer( : host_(NULL), message_loop_(message_loop), data_source_(data_source), - read_event_(false, false), - read_has_failed_(false), - last_read_bytes_(0), - read_position_(0), bitrate_(0), start_time_(kNoTimestamp()), audio_disabled_(false), - duration_known_(false) { + duration_known_(false), + url_protocol_(data_source, base::Bind( + &FFmpegDemuxer::OnDataSourceError, base::Unretained(this))) { DCHECK(message_loop_); DCHECK(data_source_); } @@ -287,8 +285,10 @@ void FFmpegDemuxer::Stop(const base::Closure& callback) { message_loop_->PostTask(FROM_HERE, base::Bind(&FFmpegDemuxer::StopTask, this, callback)); - // Then wakes up the thread from reading. - SignalReadCompleted(DataSource::kReadError); + // TODO(scherkus): This should be on |message_loop_| but today that thread is + // potentially blocked. Move to StopTask() after all blocking calls are + // guaranteed to run on a separate thread. + url_protocol_.Abort(); } void FFmpegDemuxer::Seek(base::TimeDelta time, const PipelineStatusCB& cb) { @@ -332,73 +332,6 @@ base::TimeDelta FFmpegDemuxer::GetStartTime() const { return start_time_; } -int FFmpegDemuxer::Read(int size, uint8* data) { - DCHECK(host_); - 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(EIO); - - // Even though FFmpeg defines AVERROR_EOF, it's not to be used with I/O - // routines. Instead return 0 for any read at or past EOF. - int64 file_size; - if (data_source_->GetSize(&file_size) && read_position_ >= file_size) - return 0; - - // Asynchronous read from data source. - data_source_->Read(read_position_, size, data, base::Bind( - &FFmpegDemuxer::SignalReadCompleted, this)); - - // 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. - int last_read_bytes = WaitForRead(); - if (last_read_bytes == DataSource::kReadError) { - host_->OnDemuxerError(PIPELINE_ERROR_READ); - - // Returns with a negative number to signal an error to FFmpeg. - read_has_failed_ = true; - return AVERROR(EIO); - } - read_position_ += last_read_bytes; - - return last_read_bytes; -} - -bool FFmpegDemuxer::GetPosition(int64* position_out) { - DCHECK(host_); - *position_out = read_position_; - return true; -} - -bool FFmpegDemuxer::SetPosition(int64 position) { - DCHECK(host_); - DCHECK(data_source_); - - int64 file_size; - if ((data_source_->GetSize(&file_size) && position >= file_size) || - position < 0) { - return false; - } - - read_position_ = position; - return true; -} - -bool FFmpegDemuxer::GetSize(int64* size_out) { - DCHECK(host_); - DCHECK(data_source_); - return data_source_->GetSize(size_out); -} - -bool FFmpegDemuxer::IsStreaming() { - DCHECK(host_); - DCHECK(data_source_); - return data_source_->IsStreaming(); -} - scoped_refptr<base::MessageLoopProxy> FFmpegDemuxer::message_loop() { return message_loop_; } @@ -448,7 +381,7 @@ void FFmpegDemuxer::InitializeTask(DemuxerHost* host, // see http://crbug.com/122071 data_source_->set_host(host); - glue_.reset(new FFmpegGlue(this)); + glue_.reset(new FFmpegGlue(&url_protocol_)); AVFormatContext* format_context = glue_->format_context(); // Disable ID3v1 tag reading to avoid costly seeks to end of file for data we @@ -539,7 +472,7 @@ void FFmpegDemuxer::InitializeTask(DemuxerHost* host, duration_known_ = (max_duration != kInfiniteDuration()); int64 filesize_in_bytes = 0; - GetSize(&filesize_in_bytes); + url_protocol_.GetSize(&filesize_in_bytes); bitrate_ = CalculateBitrate(format_context, max_duration, filesize_in_bytes); if (bitrate_ > 0) data_source_->SetBitrate(bitrate_); @@ -683,16 +616,6 @@ void FFmpegDemuxer::StreamHasEnded() { } } -int FFmpegDemuxer::WaitForRead() { - read_event_.Wait(); - return last_read_bytes_; -} - -void FFmpegDemuxer::SignalReadCompleted(int size) { - last_read_bytes_ = size; - read_event_.Signal(); -} - void FFmpegDemuxer::NotifyBufferingChanged() { DCHECK(message_loop_->BelongsToCurrentThread()); Ranges<base::TimeDelta> buffered; @@ -712,4 +635,8 @@ void FFmpegDemuxer::NotifyBufferingChanged() { host_->AddBufferedTimeRange(buffered.start(i), buffered.end(i)); } +void FFmpegDemuxer::OnDataSourceError() { + host_->OnDemuxerError(PIPELINE_ERROR_READ); +} + } // namespace media diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h index 23b5dc80..eea4a0a 100644 --- a/media/filters/ffmpeg_demuxer.h +++ b/media/filters/ffmpeg_demuxer.h @@ -27,13 +27,12 @@ #include "base/callback.h" #include "base/gtest_prod_util.h" -#include "base/synchronization/waitable_event.h" #include "media/base/audio_decoder_config.h" #include "media/base/decoder_buffer.h" #include "media/base/demuxer.h" #include "media/base/pipeline.h" #include "media/base/video_decoder_config.h" -#include "media/filters/ffmpeg_glue.h" +#include "media/filters/blocking_url_protocol.h" // FFmpeg forward declarations. struct AVPacket; @@ -43,6 +42,7 @@ struct AVStream; namespace media { class FFmpegDemuxer; +class FFmpegGlue; class FFmpegH264ToAnnexBBitstreamConverter; class ScopedPtrAVFreePacket; @@ -138,7 +138,7 @@ class FFmpegDemuxerStream : public DemuxerStream { DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerStream); }; -class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol { +class MEDIA_EXPORT FFmpegDemuxer : public Demuxer { public: FFmpegDemuxer(const scoped_refptr<base::MessageLoopProxy>& message_loop, const scoped_refptr<DataSource>& data_source); @@ -157,13 +157,6 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol { DemuxerStream::Type type) OVERRIDE; virtual base::TimeDelta GetStartTime() const OVERRIDE; - // FFmpegURLProtocol implementation. - virtual int Read(int size, uint8* data) OVERRIDE; - virtual bool GetPosition(int64* position_out) OVERRIDE; - virtual bool SetPosition(int64 position) OVERRIDE; - virtual bool GetSize(int64* size_out) OVERRIDE; - virtual bool IsStreaming() OVERRIDE; - // Provide access to FFmpegDemuxerStream. scoped_refptr<base::MessageLoopProxy> message_loop(); @@ -204,12 +197,8 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol { // Must be called on the demuxer thread. void StreamHasEnded(); - // Wait for asynchronous read to complete and return number of bytes read. - virtual int WaitForRead(); - - // Signal the blocked thread that the read has completed, with |size| bytes - // read or kReadError in case of error. - virtual void SignalReadCompleted(int size); + // Called by |url_protocol_| whenever |data_source_| returns a read error. + void OnDataSourceError(); // Returns the stream from |streams_| that matches |type| as an // FFmpegDemuxerStream. @@ -236,18 +225,6 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol { // 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_; - - int last_read_bytes_; - int64 read_position_; - // Derived bitrate after initialization has completed. int bitrate_; @@ -264,6 +241,8 @@ class MEDIA_EXPORT FFmpegDemuxer : public Demuxer, public FFmpegURLProtocol { // stream -- at this moment we definitely know duration. bool duration_known_; + // FFmpegURLProtocol implementation and corresponding glue bits. + BlockingUrlProtocol url_protocol_; scoped_ptr<FFmpegGlue> glue_; DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxer); diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index b86008b..2aff102 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -72,17 +72,13 @@ class FFmpegDemuxerTest : public testing::Test { } void CreateDemuxer(const std::string& name) { - CreateDemuxer(name, false); - } - - void CreateDemuxer(const std::string& name, bool disable_file_size) { CHECK(!demuxer_); EXPECT_CALL(host_, SetTotalBytes(_)).Times(AnyNumber()); EXPECT_CALL(host_, AddBufferedByteRange(_, _)).Times(AnyNumber()); EXPECT_CALL(host_, AddBufferedTimeRange(_, _)).Times(AnyNumber()); - CreateDataSource(name, disable_file_size); + CreateDataSource(name); demuxer_ = new FFmpegDemuxer(message_loop_.message_loop_proxy(), data_source_); } @@ -160,7 +156,7 @@ class FFmpegDemuxerTest : public testing::Test { } private: - void CreateDataSource(const std::string& name, bool disable_file_size) { + void CreateDataSource(const std::string& name) { CHECK(!data_source_); FilePath file_path; @@ -171,7 +167,7 @@ class FFmpegDemuxerTest : public testing::Test { .Append(FILE_PATH_LITERAL("data")) .AppendASCII(name); - data_source_ = new FileDataSource(disable_file_size); + data_source_ = new FileDataSource(); EXPECT_TRUE(data_source_->Initialize(file_path.MaybeAsASCII())); } @@ -191,7 +187,7 @@ TEST_F(FFmpegDemuxerTest, Initialize_OpenFails) { // avformat_open_input(), but has avformat_find_stream_info() fail. // //TEST_F(FFmpegDemuxerTest, Initialize_ParseFails) { -// CreateDemuxer("find_stream_info_fail.webm"); +// ("find_stream_info_fail.webm"); // demuxer_->Initialize( // &host_, NewExpectedStatusCB(DEMUXER_ERROR_COULD_NOT_PARSE)); // message_loop_.RunAllPending(); @@ -347,7 +343,7 @@ TEST_F(FFmpegDemuxerTest, Read_EndOfStream) { TEST_F(FFmpegDemuxerTest, Read_EndOfStream_NoDuration) { // Verify that end of stream buffers are created. - CreateDemuxer("bear-320x240.webm", false); + CreateDemuxer("bear-320x240.webm"); InitializeDemuxer(); set_duration_known(false); EXPECT_CALL(host_, SetDuration(_)); @@ -529,86 +525,6 @@ TEST_F(FFmpegDemuxerTest, DisableAudioStream) { EXPECT_TRUE(got_eos_buffer); } -TEST_F(FFmpegDemuxerTest, ProtocolRead) { - CreateDemuxer("bear-320x240.webm"); - InitializeDemuxer(); - - // Set read head to zero as Initialize() will have parsed a bit of the file. - int64 position = 0; - EXPECT_TRUE(demuxer_->SetPosition(0)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - EXPECT_EQ(0, position); - - // Read 32 bytes from offset zero and verify position. - uint8 buffer[32]; - EXPECT_EQ(32, demuxer_->Read(32, buffer)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - EXPECT_EQ(32, position); - - // Read an additional 32 bytes and verify position. - EXPECT_EQ(32, demuxer_->Read(32, buffer)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - EXPECT_EQ(64, position); - - // Seek to end and read until EOF. - int64 size = 0; - EXPECT_TRUE(demuxer_->GetSize(&size)); - EXPECT_TRUE(demuxer_->SetPosition(size - 48)); - EXPECT_EQ(32, demuxer_->Read(32, buffer)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - EXPECT_EQ(size - 16, position); - - EXPECT_EQ(16, demuxer_->Read(32, buffer)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - EXPECT_EQ(size, position); - - EXPECT_EQ(0, demuxer_->Read(32, buffer)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - EXPECT_EQ(size, position); - - demuxer_->Stop(NewExpectedClosure()); - message_loop_.RunAllPending(); -} - -TEST_F(FFmpegDemuxerTest, ProtocolGetSetPosition) { - CreateDemuxer("bear-320x240.webm"); - InitializeDemuxer(); - - InSequence s; - - int64 size; - int64 position; - EXPECT_TRUE(demuxer_->GetSize(&size)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - - EXPECT_TRUE(demuxer_->SetPosition(512)); - EXPECT_FALSE(demuxer_->SetPosition(size)); - EXPECT_FALSE(demuxer_->SetPosition(size + 1)); - EXPECT_FALSE(demuxer_->SetPosition(-1)); - EXPECT_TRUE(demuxer_->GetPosition(&position)); - EXPECT_EQ(512, position); -} - -TEST_F(FFmpegDemuxerTest, ProtocolGetSize) { - CreateDemuxer("bear-320x240.webm"); - InitializeDemuxer(); - - int64 data_source_size = 0; - int64 demuxer_size = 0; - EXPECT_TRUE(data_source_->GetSize(&data_source_size)); - EXPECT_TRUE(demuxer_->GetSize(&demuxer_size)); - EXPECT_NE(0, data_source_size); - EXPECT_EQ(data_source_size, demuxer_size); -} - -TEST_F(FFmpegDemuxerTest, ProtocolIsStreaming) { - CreateDemuxer("bear-320x240.webm"); - InitializeDemuxer(); - - EXPECT_FALSE(data_source_->IsStreaming()); - EXPECT_FALSE(demuxer_->IsStreaming()); -} - // Verify that seek works properly when the WebM cues data is at the start of // the file instead of at the end. TEST_F(FFmpegDemuxerTest, SeekWithCuesBeforeFirstCluster) { diff --git a/media/filters/file_data_source.cc b/media/filters/file_data_source.cc index ceb7a5a..d8376b5 100644 --- a/media/filters/file_data_source.cc +++ b/media/filters/file_data_source.cc @@ -15,13 +15,8 @@ namespace media { FileDataSource::FileDataSource() : file_(NULL), file_size_(0), - disable_file_size_(false) { -} - -FileDataSource::FileDataSource(bool disable_file_size) - : file_(NULL), - file_size_(0), - disable_file_size_(disable_file_size) { + force_read_errors_(false), + force_streaming_(false) { } bool FileDataSource::Initialize(const std::string& url) { @@ -63,7 +58,8 @@ void FileDataSource::Read(int64 position, int size, uint8* data, const DataSource::ReadCB& read_cb) { DCHECK(file_); base::AutoLock l(lock_); - if (file_) { + + if (!force_read_errors_ && file_) { #if defined(OS_WIN) if (_fseeki64(file_, position, SEEK_SET)) { read_cb.Run(DataSource::kReadError); @@ -92,11 +88,11 @@ bool FileDataSource::GetSize(int64* size_out) { DCHECK(file_); base::AutoLock l(lock_); *size_out = file_size_; - return (NULL != file_ && !disable_file_size_); + return file_; } bool FileDataSource::IsStreaming() { - return false; + return force_streaming_; } void FileDataSource::SetBitrate(int bitrate) {} diff --git a/media/filters/file_data_source.h b/media/filters/file_data_source.h index d5b81a4..a2c728d 100644 --- a/media/filters/file_data_source.h +++ b/media/filters/file_data_source.h @@ -7,7 +7,6 @@ #include <string> -#include "base/gtest_prod_util.h" #include "base/synchronization/lock.h" #include "media/base/data_source.h" @@ -18,7 +17,6 @@ namespace media { class MEDIA_EXPORT FileDataSource : public DataSource { public: FileDataSource(); - FileDataSource(bool disable_file_size); bool Initialize(const std::string& url); @@ -31,6 +29,10 @@ class MEDIA_EXPORT FileDataSource : public DataSource { virtual bool IsStreaming() OVERRIDE; virtual void SetBitrate(int bitrate) OVERRIDE; + // Unit test helpers. Recreate the object if you want the default behaviour. + void force_read_errors_for_testing() { force_read_errors_ = true; } + void force_streaming_for_testing() { force_streaming_ = true; } + protected: virtual ~FileDataSource(); @@ -44,17 +46,12 @@ class MEDIA_EXPORT FileDataSource : public DataSource { // Size of the file in bytes. int64 file_size_; - // True if the FileDataSource should ignore its set file size, false - // otherwise. - bool disable_file_size_; - - // Critical section that protects all of the DataSource methods to prevent - // a Stop from happening while in the middle of a file I/O operation. - // TODO(ralphl): Ideally this would use asynchronous I/O or we will know - // that we will block for a short period of time in reads. Otherwise, we can - // hang the pipeline Stop. + // Serialize all operations to prevent stopping during reads. base::Lock lock_; + bool force_read_errors_; + bool force_streaming_; + DISALLOW_COPY_AND_ASSIGN(FileDataSource); }; diff --git a/media/media.gyp b/media/media.gyp index 9fb5f37..d3b12c6 100644 --- a/media/media.gyp +++ b/media/media.gyp @@ -253,6 +253,8 @@ 'filters/audio_renderer_algorithm.h', 'filters/audio_renderer_impl.cc', 'filters/audio_renderer_impl.h', + 'filters/blocking_url_protocol.cc', + 'filters/blocking_url_protocol.h', 'filters/chunk_demuxer.cc', 'filters/chunk_demuxer.h', 'filters/decrypting_audio_decoder.cc', @@ -365,6 +367,8 @@ 'filters/audio_file_reader.h', 'filters/chunk_demuxer.cc', 'filters/chunk_demuxer.h', + 'filters/blocking_url_protocol.cc', + 'filters/blocking_url_protocol.h', 'filters/ffmpeg_audio_decoder.cc', 'filters/ffmpeg_audio_decoder.h', 'filters/ffmpeg_demuxer.cc', @@ -647,6 +651,7 @@ 'ffmpeg/ffmpeg_common_unittest.cc', 'filters/audio_renderer_algorithm_unittest.cc', 'filters/audio_renderer_impl_unittest.cc', + 'filters/blocking_url_protocol_unittest.cc', 'filters/chunk_demuxer_unittest.cc', 'filters/decrypting_audio_decoder_unittest.cc', 'filters/decrypting_video_decoder_unittest.cc', @@ -713,6 +718,7 @@ 'base/test_data_util.cc', 'base/test_data_util.h', 'ffmpeg/ffmpeg_common_unittest.cc', + 'filters/blocking_url_protocol_unittest.cc', 'filters/chunk_demuxer_unittest.cc', 'filters/ffmpeg_audio_decoder_unittest.cc', 'filters/ffmpeg_demuxer_unittest.cc', |