summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-29 21:49:49 +0000
committerhclam@chromium.org <hclam@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2009-07-29 21:49:49 +0000
commit38259a7a83545e07681d921564468844c7b03337 (patch)
treeceddb850f76be8ee565d9cc2d3d34d7ae445a1d7
parent6b33da129646087bbc173a72c84e0690e91740de (diff)
downloadchromium_src-38259a7a83545e07681d921564468844c7b03337.zip
chromium_src-38259a7a83545e07681d921564468844c7b03337.tar.gz
chromium_src-38259a7a83545e07681d921564468844c7b03337.tar.bz2
BufferedDataSource to support server without range request support
This patch will enable BufferedDataSource to support servers with no range request support. It will start a probe request of 1 byte size besides the regular request. If the server does not support range request, we will turn on the is_streamed flag of FFmpeg and will not do any seeking. BUG=17628 TEST=test_shell_tests --gtest_filter=BufferedDataSource.* Review URL: http://codereview.chromium.org/160076 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@21999 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r--media/base/filter_host.h3
-rw-r--r--media/base/filters.h5
-rw-r--r--media/base/mock_filter_host.h1
-rw-r--r--media/base/mock_filters.h2
-rw-r--r--media/base/pipeline.h4
-rw-r--r--media/base/pipeline_impl.cc12
-rw-r--r--media/base/pipeline_impl.h6
-rw-r--r--media/filters/ffmpeg_demuxer.cc6
-rw-r--r--media/filters/ffmpeg_demuxer.h2
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc6
-rw-r--r--media/filters/ffmpeg_glue.cc2
-rw-r--r--media/filters/ffmpeg_glue.h2
-rw-r--r--media/filters/ffmpeg_glue_unittest.cc8
-rw-r--r--media/filters/file_data_source.cc5
-rw-r--r--media/filters/file_data_source.h2
-rw-r--r--webkit/api/public/WebMediaPlayer.h10
-rw-r--r--webkit/api/src/WebMediaPlayerClientImpl.cpp28
-rw-r--r--webkit/api/src/WebMediaPlayerClientImpl.h2
-rw-r--r--webkit/glue/media/buffered_data_source.cc245
-rw-r--r--webkit/glue/media/buffered_data_source.h66
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc160
-rw-r--r--webkit/glue/media/simple_data_source.cc4
-rw-r--r--webkit/glue/media/simple_data_source.h2
-rw-r--r--webkit/glue/webmediaplayer_impl.cc35
-rw-r--r--webkit/glue/webmediaplayer_impl.h3
25 files changed, 434 insertions, 187 deletions
diff --git a/media/base/filter_host.h b/media/base/filter_host.h
index 5e70797..65aeebb 100644
--- a/media/base/filter_host.h
+++ b/media/base/filter_host.h
@@ -53,6 +53,9 @@ class FilterHost {
// Sets the size of the video output in pixel units.
virtual void SetVideoSize(size_t width, size_t height) = 0;
+ // Sets the flag to indicate that we are doing streaming.
+ virtual void SetStreaming(bool streaming) = 0;
+
protected:
virtual ~FilterHost() {}
};
diff --git a/media/base/filters.h b/media/base/filters.h
index 8fdfe011..3faede5 100644
--- a/media/base/filters.h
+++ b/media/base/filters.h
@@ -174,8 +174,9 @@ class DataSource : public MediaFilter {
// retrieved.
virtual bool GetSize(int64* size_out) = 0;
- // Returns true if this data source supports random seeking.
- virtual bool IsSeekable() = 0;
+ // Returns true if we are performing streaming. In this case seeking is
+ // not possible.
+ virtual bool IsStreaming() = 0;
};
diff --git a/media/base/mock_filter_host.h b/media/base/mock_filter_host.h
index 8bab1b5..a7150c2 100644
--- a/media/base/mock_filter_host.h
+++ b/media/base/mock_filter_host.h
@@ -36,6 +36,7 @@ class MockFilterHost : public FilterHost {
MOCK_METHOD1(SetTotalBytes, void(int64 total_bytes));
MOCK_METHOD1(SetBufferedBytes, void(int64 buffered_bytes));
MOCK_METHOD2(SetVideoSize, void(size_t width, size_t height));
+ MOCK_METHOD1(SetStreaming, void(bool streamed));
private:
DISALLOW_COPY_AND_ASSIGN(MockFilterHost);
diff --git a/media/base/mock_filters.h b/media/base/mock_filters.h
index 7954aa9..d3546c4 100644
--- a/media/base/mock_filters.h
+++ b/media/base/mock_filters.h
@@ -102,7 +102,7 @@ class MockDataSource : public DataSource {
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());
+ MOCK_METHOD0(IsStreaming, bool());
protected:
virtual ~MockDataSource() {}
diff --git a/media/base/pipeline.h b/media/base/pipeline.h
index c0286e5..b54d442 100644
--- a/media/base/pipeline.h
+++ b/media/base/pipeline.h
@@ -147,6 +147,10 @@ class Pipeline : public base::RefCountedThreadSafe<Pipeline> {
// or the video has not been rendered yet, the width and height will be 0.
virtual void GetVideoSize(size_t* width_out, size_t* height_out) const = 0;
+ // If this method returns true, that means the data source is a streaming
+ // data source. Seeking may not be possible.
+ virtual bool IsStreaming() const = 0;
+
// Gets the current error status for the pipeline. If the pipeline is
// operating correctly, this will return OK.
virtual PipelineError GetError() const = 0;
diff --git a/media/base/pipeline_impl.cc b/media/base/pipeline_impl.cc
index a09c2c7..10ab9a3 100644
--- a/media/base/pipeline_impl.cc
+++ b/media/base/pipeline_impl.cc
@@ -237,6 +237,11 @@ void PipelineImpl::GetVideoSize(size_t* width_out, size_t* height_out) const {
*height_out = video_height_;
}
+bool PipelineImpl::IsStreaming() const {
+ AutoLock auto_lock(lock_);
+ return streaming_;
+}
+
PipelineError PipelineImpl::GetError() const {
AutoLock auto_lock(lock_);
return error_;
@@ -249,6 +254,7 @@ void PipelineImpl::ResetState() {
duration_ = kZero;
buffered_time_ = kZero;
buffered_bytes_ = 0;
+ streaming_ = false;
total_bytes_ = 0;
video_width_ = 0;
video_height_ = 0;
@@ -343,6 +349,12 @@ void PipelineImpl::SetVideoSize(size_t width, size_t height) {
video_height_ = height;
}
+void PipelineImpl::SetStreaming(bool streaming) {
+ DCHECK(IsRunning());
+ AutoLock auto_lock(lock_);
+ streaming_ = streaming;
+}
+
void PipelineImpl::InsertRenderedMimeType(const std::string& major_mime_type) {
DCHECK(IsRunning());
AutoLock auto_lock(lock_);
diff --git a/media/base/pipeline_impl.h b/media/base/pipeline_impl.h
index 02c8bce..e83822d 100644
--- a/media/base/pipeline_impl.h
+++ b/media/base/pipeline_impl.h
@@ -81,6 +81,7 @@ class PipelineImpl : public Pipeline, public FilterHost {
virtual int64 GetBufferedBytes() const;
virtual int64 GetTotalBytes() const;
virtual void GetVideoSize(size_t* width_out, size_t* height_out) const;
+ virtual bool IsStreaming() const;
virtual PipelineError GetError() const;
private:
@@ -129,6 +130,7 @@ class PipelineImpl : public Pipeline, public FilterHost {
virtual void SetTotalBytes(int64 total_bytes);
virtual void SetBufferedBytes(int64 buffered_bytes);
virtual void SetVideoSize(size_t width, size_t height);
+ virtual void SetStreaming(bool streamed);
// Method called during initialization to insert a mime type into the
// |rendered_mime_types_| set.
@@ -267,6 +269,10 @@ class PipelineImpl : public Pipeline, public FilterHost {
size_t video_width_;
size_t video_height_;
+ // Sets by the filters to indicate whether the data source is a streaming
+ // source.
+ bool streaming_;
+
// Current volume level (from 0.0f to 1.0f). This value is set immediately
// via SetVolume() and a task is dispatched on the message loop to notify the
// filters.
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc
index 189aac4..4ad463a 100644
--- a/media/filters/ffmpeg_demuxer.cc
+++ b/media/filters/ffmpeg_demuxer.cc
@@ -345,8 +345,10 @@ bool FFmpegDemuxer::GetSize(int64* size_out) {
return data_source_->GetSize(size_out);
}
-bool FFmpegDemuxer::IsStreamed() {
- return false;
+bool FFmpegDemuxer::IsStreaming() {
+ DCHECK(data_source_);
+
+ return data_source_->IsStreaming();
}
void FFmpegDemuxer::InitializeTask(DataSource* data_source,
diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h
index 0777855..c897ca4 100644
--- a/media/filters/ffmpeg_demuxer.h
+++ b/media/filters/ffmpeg_demuxer.h
@@ -136,7 +136,7 @@ class FFmpegDemuxer : public Demuxer,
virtual bool GetPosition(int64* position_out);
virtual bool SetPosition(int64 position);
virtual bool GetSize(int64* size_out);
- virtual bool IsStreamed();
+ virtual bool IsStreaming();
private:
// Only allow a factory to create this class.
diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc
index e8c9df7..ccd0611 100644
--- a/media/filters/ffmpeg_demuxer_unittest.cc
+++ b/media/filters/ffmpeg_demuxer_unittest.cc
@@ -776,12 +776,14 @@ TEST_F(FFmpegDemuxerTest, ProtocolGetSize) {
EXPECT_EQ(1024, size);
}
-TEST_F(FFmpegDemuxerTest, ProtocolIsStreamed) {
+TEST_F(FFmpegDemuxerTest, ProtocolIsStreaming) {
{
SCOPED_TRACE("");
InitializeDemuxer();
}
- EXPECT_FALSE(demuxer_->IsStreamed());
+ EXPECT_CALL(*data_source_, IsStreaming())
+ .WillOnce(Return(false));
+ EXPECT_FALSE(demuxer_->IsStreaming());
}
} // namespace media
diff --git a/media/filters/ffmpeg_glue.cc b/media/filters/ffmpeg_glue.cc
index 135b3bb..35e35c91 100644
--- a/media/filters/ffmpeg_glue.cc
+++ b/media/filters/ffmpeg_glue.cc
@@ -22,7 +22,7 @@ int OpenContext(URLContext* h, const char* filename, int flags) {
h->priv_data = protocol;
h->flags = URL_RDONLY;
- h->is_streamed = protocol->IsStreamed();
+ h->is_streamed = protocol->IsStreaming();
return 0;
}
diff --git a/media/filters/ffmpeg_glue.h b/media/filters/ffmpeg_glue.h
index 228c964..bc39ab2 100644
--- a/media/filters/ffmpeg_glue.h
+++ b/media/filters/ffmpeg_glue.h
@@ -62,7 +62,7 @@ class FFmpegURLProtocol {
virtual bool GetSize(int64* size_out) = 0;
// Returns false if this protocol supports random seeking.
- virtual bool IsStreamed() = 0;
+ virtual bool IsStreaming() = 0;
private:
DISALLOW_COPY_AND_ASSIGN(FFmpegURLProtocol);
diff --git a/media/filters/ffmpeg_glue_unittest.cc b/media/filters/ffmpeg_glue_unittest.cc
index 904f6a5..303a8ba 100644
--- a/media/filters/ffmpeg_glue_unittest.cc
+++ b/media/filters/ffmpeg_glue_unittest.cc
@@ -26,7 +26,7 @@ class MockProtocol : public FFmpegURLProtocol {
MOCK_METHOD1(GetPosition, bool(int64* position_out));
MOCK_METHOD1(SetPosition, bool(int64 position));
MOCK_METHOD1(GetSize, bool(int64* size_out));
- MOCK_METHOD0(IsStreamed, bool());
+ MOCK_METHOD0(IsStreaming, bool());
private:
DISALLOW_COPY_AND_ASSIGN(MockProtocol);
@@ -45,8 +45,8 @@ class FFmpegGlueTest : public ::testing::Test {
// 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(MockProtocol* protocol, URLContext* context) {
- // IsStreamed() is called when opening.
- EXPECT_CALL(*protocol, IsStreamed()).WillOnce(Return(true));
+ // IsStreaming() is called when opening.
+ EXPECT_CALL(*protocol, IsStreaming()).WillOnce(Return(true));
// Add the protocol to the glue layer and open a context.
std::string key = FFmpegGlue::get()->AddProtocol(protocol);
@@ -144,7 +144,7 @@ TEST_F(FFmpegGlueTest, OpenClose) {
// 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));
+ EXPECT_CALL(*protocol, IsStreaming()).WillOnce(Return(true));
std::string key = glue->AddProtocol(protocol.get());
// Prepare FFmpeg URLContext structure.
diff --git a/media/filters/file_data_source.cc b/media/filters/file_data_source.cc
index a6c381c..b14ad9f 100644
--- a/media/filters/file_data_source.cc
+++ b/media/filters/file_data_source.cc
@@ -101,9 +101,8 @@ bool FileDataSource::GetSize(int64* size_out) {
return (NULL != file_);
}
-bool FileDataSource::IsSeekable() {
- // A file data source is always seekable.
- return true;
+bool FileDataSource::IsStreaming() {
+ return false;
}
} // namespace media
diff --git a/media/filters/file_data_source.h b/media/filters/file_data_source.h
index 760b7c5..164e74c 100644
--- a/media/filters/file_data_source.h
+++ b/media/filters/file_data_source.h
@@ -31,7 +31,7 @@ class FileDataSource : public DataSource {
virtual void Read(int64 position, size_t size, uint8* data,
ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
- virtual bool IsSeekable();
+ virtual bool IsStreaming();
private:
// Only allow factories and tests to create this object.
diff --git a/webkit/api/public/WebMediaPlayer.h b/webkit/api/public/WebMediaPlayer.h
index 8b60c77..56bc967 100644
--- a/webkit/api/public/WebMediaPlayer.h
+++ b/webkit/api/public/WebMediaPlayer.h
@@ -59,6 +59,13 @@ namespace WebKit {
HaveEnoughData,
};
+ enum MovieLoadType{
+ Unknown,
+ Download,
+ StoredStream,
+ LiveStream,
+ };
+
virtual ~WebMediaPlayer() {}
virtual void load(const WebURL&) = 0;
@@ -104,6 +111,9 @@ namespace WebKit {
virtual unsigned long long bytesLoaded() const = 0;
virtual unsigned long long totalBytes() const = 0;
+
+ virtual bool hasSingleSecurityOrigin() const = 0;
+ virtual MovieLoadType movieLoadType() const = 0;
};
} // namespace WebKit
diff --git a/webkit/api/src/WebMediaPlayerClientImpl.cpp b/webkit/api/src/WebMediaPlayerClientImpl.cpp
index d26bd16..884d45b 100644
--- a/webkit/api/src/WebMediaPlayerClientImpl.cpp
+++ b/webkit/api/src/WebMediaPlayerClientImpl.cpp
@@ -361,6 +361,34 @@ void WebMediaPlayerClientImpl::setAutobuffer(bool autoBuffer)
m_webMediaPlayer->setAutoBuffer(autoBuffer);
}
+bool WebMediaPlayerClientImpl::hasSingleSecurityOrigin() const
+{
+ if (m_webMediaPlayer.get())
+ return m_webMediaPlayer->hasSingleSecurityOrigin();
+ return false;
+}
+
+MediaPlayer::MovieLoadType WebMediaPlayerClientImpl::movieLoadType() const
+{
+ COMPILE_ASSERT(
+ int(WebMediaPlayer::Unknown) == int(MediaPlayer::Unknown),
+ Unknown);
+ COMPILE_ASSERT(
+ int(WebMediaPlayer::Download) == int(MediaPlayer::Download),
+ Download);
+ COMPILE_ASSERT(
+ int(WebMediaPlayer::StoredStream) == int(MediaPlayer::StoredStream),
+ StoredStream);
+ COMPILE_ASSERT(
+ int(WebMediaPlayer::LiveStream) == int(MediaPlayer::LiveStream),
+ LiveStream);
+
+ if (m_webMediaPlayer.get())
+ return static_cast<MediaPlayer::MovieLoadType>(
+ m_webMediaPlayer->movieLoadType());
+ return MediaPlayer::Unknown;
+}
+
MediaPlayerPrivateInterface* WebMediaPlayerClientImpl::create(MediaPlayer* player)
{
WebMediaPlayerClientImpl* client = new WebMediaPlayerClientImpl();
diff --git a/webkit/api/src/WebMediaPlayerClientImpl.h b/webkit/api/src/WebMediaPlayerClientImpl.h
index d758276..721073c 100644
--- a/webkit/api/src/WebMediaPlayerClientImpl.h
+++ b/webkit/api/src/WebMediaPlayerClientImpl.h
@@ -90,6 +90,8 @@ namespace WebKit {
virtual unsigned bytesLoaded() const;
virtual void setSize(const WebCore::IntSize&);
virtual void paint(WebCore::GraphicsContext*, const WebCore::IntRect&);
+ virtual bool hasSingleSecurityOrigin() const;
+ virtual WebCore::MediaPlayer::MovieLoadType movieLoadType() const;
private:
WebMediaPlayerClientImpl();
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index 6b71ec9..4fc3549 100644
--- a/webkit/glue/media/buffered_data_source.cc
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -32,11 +32,6 @@ const size_t kBackwardCapcity = 2 * kMegabyte;
// Forward capacity of the buffer, by default 10MB.
const size_t kForwardCapacity = 10 * kMegabyte;
-// The maximum offset to seek to, this value limits the range of seek to
-// prevent corruption of calculations. The buffer has a maximum size of 12MB
-// so this is enough to contain every valid operations.
-const int kMaxSeek = 100 * kMegabyte;
-
// The threshold of bytes that we should wait until the data arrives in the
// future instead of restarting a new connection. This number is defined in the
// number of bytes, we should determine this value from typical connection speed
@@ -63,6 +58,7 @@ const int kReadTrials = 3;
const int kInitialReadBufferSize = 32768;
// A helper method that accepts only HTTP, HTTPS and FILE protocol.
+// TODO(hclam): Support also FTP protocol.
bool IsSchemeSupported(const GURL& url) {
return url.SchemeIs(kHttpScheme) ||
url.SchemeIs(kHttpsScheme) ||
@@ -92,6 +88,7 @@ BufferedResourceLoader::BufferedResourceLoader(
bridge_(NULL),
offset_(0),
content_length_(kPositionNotSpecified),
+ instance_size_(kPositionNotSpecified),
read_callback_(NULL),
read_position_(0),
read_size_(0),
@@ -159,29 +156,39 @@ void BufferedResourceLoader::Read(int64 position,
read_size_ = read_size;
read_buffer_ = buffer;
- // Check that the read parameters are within the range that we can handle.
- // If the read request is made too far from the current offset, report that
- // we cannot serve the request.
- if (VerifyRead()) {
- // If we can serve the request now, do the actual read.
- if (CanFulfillRead()) {
- ReadInternal();
- DisableDeferIfNeeded();
- return;
- }
-
- // If we expected the read request to be fulfilled later, returns
- // immediately and let more data to flow in.
- if (WillFulfillRead())
- return;
+ // If read position is beyond the instance size, we cannot read there.
+ if (instance_size_ != kPositionNotSpecified &&
+ instance_size_ <= read_position_) {
+ DoneRead(0);
+ return;
+ }
- // Make a callback to report failure.
+ // Make sure |offset_| and |read_position_| does not differ by a large
+ // amount.
+ if (read_position_ > offset_ + kint32max ||
+ read_position_ < offset_ + kint32min) {
DoneRead(net::ERR_CACHE_MISS);
return;
}
- // TODO(hclam): We should report a better error code than just 0.
- DoneRead(0);
+ // Prepare the parameters.
+ first_offset_ = static_cast<int>(read_position_ - offset_);
+ last_offset_ = first_offset_ + read_size_;
+
+ // If we can serve the request now, do the actual read.
+ if (CanFulfillRead()) {
+ ReadInternal();
+ DisableDeferIfNeeded();
+ return;
+ }
+
+ // If we expected the read request to be fulfilled later, returns
+ // immediately and let more data to flow in.
+ if (WillFulfillRead())
+ return;
+
+ // Make a callback to report failure.
+ DoneRead(net::ERR_CACHE_MISS);
}
/////////////////////////////////////////////////////////////////////////////
@@ -220,15 +227,10 @@ void BufferedResourceLoader::OnReceivedResponse(
// if not report failure.
error = net::ERR_INVALID_RESPONSE;
} else if (range_requested_) {
- if (info.headers->response_code() != kHttpPartialContent ||
- !info.headers->GetContentRange(&first_byte_position,
- &last_byte_position,
- &instance_size)) {
- // We requested a range, but server didn't reply with partial content or
- // the "Content-Range" header is corrupted.
- // TODO(hclam): should also make sure this is the range we requested.
- error = net::ERR_INVALID_RESPONSE;
- }
+ // If we have verified the partial response and it is correct, we will
+ // return net::OK.
+ if (!VerifyPartialResponse(info))
+ error = net::ERR_REQUEST_RANGE_NOT_SATISFIABLE;
} else if (info.headers->response_code() != kHttpOK) {
// We didn't request a range but server didn't reply with "200 OK".
error = net::ERR_FAILED;
@@ -245,6 +247,11 @@ void BufferedResourceLoader::OnReceivedResponse(
// not specified and this is a streaming response.
content_length_ = info.content_length;
+ // If we have not requested a range, then the size of the instance is equal
+ // to the content length.
+ if (!range_requested_)
+ instance_size_ = content_length_;
+
// We only care about the first byte position if it's given by the server.
// TODO(hclam): If server replies with a different offset, consider failing
// here.
@@ -370,21 +377,6 @@ bool BufferedResourceLoader::WillFulfillRead() {
return true;
}
-bool BufferedResourceLoader::VerifyRead() {
- // Make sure |offset_| and |read_position_| does not differ by a large
- // amount
- if (read_position_ > offset_ + kMaxSeek)
- return false;
- else if (read_position_ < offset_ - kMaxSeek)
- return false;
-
- // If we can manage the read request with int32 math, then prepare the
- // parameters.
- first_offset_ = static_cast<int>(read_position_ - offset_);
- last_offset_ = first_offset_ + read_size_;
- return true;
-}
-
void BufferedResourceLoader::ReadInternal() {
// Seek to the first byte requested.
bool ret = buffer_->Seek(first_offset_);
@@ -413,22 +405,50 @@ void BufferedResourceLoader::DoneStart(int error) {
start_callback_.reset();
}
+bool BufferedResourceLoader::VerifyPartialResponse(
+ const ResourceLoaderBridge::ResponseInfo& info) {
+ if (info.headers->response_code() != kHttpPartialContent)
+ return false;
+
+ int64 first_byte_position, last_byte_position, instance_size;
+ if (!info.headers->GetContentRange(&first_byte_position,
+ &last_byte_position,
+ &instance_size)) {
+ return false;
+ }
+
+ if (instance_size != kPositionNotSpecified)
+ instance_size_ = instance_size;
+
+ if (first_byte_position_ != -1 &&
+ first_byte_position_ != first_byte_position) {
+ return false;
+ }
+
+ // TODO(hclam): I should also check |last_byte_position|, but since
+ // we will never make such a request that it is ok to leave it unimplemented.
+ return true;
+}
+
//////////////////////////////////////////////////////////////////////////////
// BufferedDataSource, protected
BufferedDataSource::BufferedDataSource(
MessageLoop* render_loop,
webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
: total_bytes_(kPositionNotSpecified),
+ streaming_(false),
bridge_factory_(bridge_factory),
loader_(NULL),
+ initialize_callback_(NULL),
read_callback_(NULL),
read_position_(0),
read_size_(0),
read_buffer_(NULL),
+ initial_response_received_(false),
+ probe_response_received_(false),
intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
intermediate_read_buffer_size_(kInitialReadBufferSize),
render_loop_(render_loop),
- initialize_callback_(NULL),
stopped_(false) {
}
@@ -507,8 +527,8 @@ bool BufferedDataSource::GetSize(int64* size_out) {
return false;
}
-bool BufferedDataSource::IsSeekable() {
- return total_bytes_ != kPositionNotSpecified;
+bool BufferedDataSource::IsStreaming() {
+ return streaming_;
}
/////////////////////////////////////////////////////////////////////////////
@@ -516,6 +536,7 @@ bool BufferedDataSource::IsSeekable() {
void BufferedDataSource::InitializeTask() {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(!loader_.get());
+ DCHECK(!probe_loader_.get());
// Kick starts the watch dog task that will handle connection timeout.
// We run the watch dog 2 times faster the actual timeout so as to catch
@@ -525,12 +546,18 @@ void BufferedDataSource::InitializeTask() {
this,
&BufferedDataSource::WatchDogTask);
- // Creates a new resource loader with the full range.
+ // Creates a new resource loader with the full range and a probe resource
+ // loader. Creates a probe resource loader to make sure the server supports
+ // partial range request.
+ // TODO(hclam): Only request 1 byte for this probe request, it may be useful
+ // that we perform a suffix range request and fetch the index. That way we
+ // can minimize the number of requests made.
loader_.reset(CreateLoader(-1, -1));
+ probe_loader_.reset(CreateLoader(1, 1));
- // And then start the resource request.
- loader_->Start(NewCallback(this,
- &BufferedDataSource::InitializeStartCallback));
+ loader_->Start(NewCallback(this, &BufferedDataSource::InitialStartCallback));
+ probe_loader_->Start(
+ NewCallback(this, &BufferedDataSource::ProbeStartCallback));
}
void BufferedDataSource::ReadTask(
@@ -559,10 +586,12 @@ void BufferedDataSource::StopTask() {
watch_dog_timer_.Stop();
// We just need to stop the loader, so it stops activity.
- if (loader_.get()) {
+ if (loader_.get())
loader_->Stop();
- loader_.reset();
- }
+
+ // If the probe request is still active, stop it too.
+ if (probe_loader_.get())
+ probe_loader_->Stop();
// Reset the parameters of the current read request.
read_callback_.reset();
@@ -593,7 +622,7 @@ void BufferedDataSource::WatchDogTask() {
base::TimeDelta delta = base::Time::Now() - read_submitted_time_;
if (delta < GetTimeoutMilliseconds())
return;
-
+
// TODO(hclam): Maybe raise an error here. But if an error is reported
// the whole pipeline may get destroyed...
if (read_attempts_ >= kReadTrials)
@@ -621,8 +650,7 @@ void BufferedDataSource::ReadInternal() {
}
// Perform the actual read with BufferedResourceLoader.
- loader_->Read(read_position_, read_size_,
- intermediate_read_buffer_.get(),
+ loader_->Read(read_position_, read_size_, intermediate_read_buffer_.get(),
NewCallback(this, &BufferedDataSource::ReadCallback));
}
@@ -657,7 +685,7 @@ void BufferedDataSource::DoneInitialization() {
// BufferedDataSource, callback methods.
// These methods are called on the render thread for the events reported by
// BufferedResourceLoader.
-void BufferedDataSource::InitializeStartCallback(int error) {
+void BufferedDataSource::InitialStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
// We need to prevent calling to filter host and running the callback if
@@ -672,28 +700,60 @@ void BufferedDataSource::InitializeStartCallback(int error) {
if (stopped_)
return;
- DCHECK(loader_.get());
-
- if (error == net::OK) {
- total_bytes_ = loader_->content_length();
- // TODO(hclam): Figure out what to do when total bytes is not known.
- if (total_bytes_ >= 0) {
- host()->SetTotalBytes(total_bytes_);
-
- // This value governs the range that we can seek to.
- // TODO(hclam): Report the correct value of buffered bytes.
- host()->SetBufferedBytes(total_bytes_);
- }
- } else {
+ if (error != net::OK) {
// TODO(hclam): In case of failure, we can retry several times.
- // Also it might be bad to access host() here.
host()->SetError(media::PIPELINE_ERROR_NETWORK);
-
- // Stops the loader, just to be safe.
+ DCHECK(loader_.get());
+ DCHECK(probe_loader_.get());
loader_->Stop();
+ probe_loader_->Stop();
+ DoneInitialization();
+ return;
+ }
+
+ total_bytes_ = loader_->instance_size();
+ if (total_bytes_ >= 0) {
+ // This value governs the range that we can seek to.
+ // TODO(hclam): Report the correct value of buffered bytes.
+ host()->SetTotalBytes(total_bytes_);
+ host()->SetBufferedBytes(total_bytes_);
+ } else {
+ // If the server didn't reply with a content length, it is likely this
+ // is a streaming response.
+ streaming_ = true;
+ host()->SetStreaming(true);
+ }
+
+ initial_response_received_ = true;
+ if (probe_response_received_)
+ DoneInitialization();
+}
+
+void BufferedDataSource::ProbeStartCallback(int error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+
+ // We need to prevent calling to filter host and running the callback if
+ // we have received the stop signal. We need to lock down the whole callback
+ // method to prevent bad things from happening. The reason behind this is
+ // that we cannot guarantee tasks on render thread have completely stopped
+ // when we receive the Stop() method call. The only way to solve this is to
+ // let tasks on render thread to run but make sure they don't call outside
+ // this object when Stop() method is ever called. Locking this method is safe
+ // because |lock_| is only acquired in tasks on render thread.
+ AutoLock auto_lock(lock_);
+ if (stopped_)
+ return;
+
+ if (error != net::OK) {
+ streaming_ = true;
+ host()->SetStreaming(true);
}
- DoneInitialization();
+ DCHECK(probe_loader_.get());
+ probe_loader_->Stop();
+ probe_response_received_ = true;
+ if (initial_response_received_)
+ DoneInitialization();
}
void BufferedDataSource::PartialReadStartCallback(int error) {
@@ -705,6 +765,8 @@ void BufferedDataSource::PartialReadStartCallback(int error) {
// reading from it.
ReadInternal();
} else {
+ loader_->Stop();
+
// We need to prevent calling to filter host and running the callback if
// we have received the stop signal. We need to lock down the whole callback
// method to prevent bad things from happening. The reason behind this is
@@ -716,12 +778,7 @@ void BufferedDataSource::PartialReadStartCallback(int error) {
AutoLock auto_lock(lock_);
if (stopped_)
return;
-
- // TODO(hclam): It may be bad to access host() here.
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
-
- // Kill the loader just to be safe.
- loader_->Stop();
+ DoneRead(net::ERR_INVALID_RESPONSE);
}
}
@@ -747,19 +804,10 @@ void BufferedDataSource::ReadCallback(int error) {
// If a position error code is received, read was successful. So copy
// from intermediate read buffer to the target read buffer.
memcpy(read_buffer_, intermediate_read_buffer_.get(), error);
-
DoneRead(error);
} else if (error == net::ERR_CACHE_MISS) {
// If the current loader cannot serve this read request, we need to create
// a new one.
- // We have the following conditions:
- // 1. Read is beyond the content length of the file (if known).
- // 2. We have tried too many times (TODO here).
- if (read_position_ >= total_bytes_) {
- DoneRead(0);
- return;
- }
-
// TODO(hclam): we need to count how many times it failed to prevent
// excessive trials.
@@ -770,18 +818,11 @@ void BufferedDataSource::ReadCallback(int error) {
// we cannot delete it. So we need to post a task to swap in a new
// resource loader and starts it.
render_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this,
- &BufferedDataSource::SwapLoaderTask,
+ NewRunnableMethod(this, &BufferedDataSource::SwapLoaderTask,
CreateLoader(read_position_, -1)));
} else {
- // The read has finished with error.
- DoneRead(error);
-
- // TODO(hclam): It may be bad to access host() here.
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
-
- // Stops the laoder.
loader_->Stop();
+ DoneRead(error);
}
}
diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h
index 0e1df1c..6dd51a7 100644
--- a/webkit/glue/media/buffered_data_source.h
+++ b/webkit/glue/media/buffered_data_source.h
@@ -45,7 +45,18 @@ class BufferedResourceLoader : public webkit_glue::ResourceLoaderBridge::Peer {
// Start the resource loading with the specified URL and range.
// This method operates in asynchronous mode. Once there's a response from the
- // server, success or fail |start_callback| is called with the result.
+ // server, success or fail |callback| is called with the result.
+ // |callback| is called with the following values:
+ // - net::OK
+ // The request has started successfully.
+ // - net::ERR_REQUEST_RANGE_NOT_SATISFIABLE
+ // A range request was made to the server but the server doesn't support it.
+ // - net::ERR_FAILED
+ // The request has failed because of an error with the network.
+ // - net::ERR_INVALID_RESPONSE
+ // An invalid response is received from the server.
+ // - (Anything else)
+ // An error code that indicates the request has failed.
virtual void Start(net::CompletionCallback* callback);
// Stop this loader, cancels and request and release internal buffer.
@@ -54,13 +65,24 @@ class BufferedResourceLoader : public webkit_glue::ResourceLoaderBridge::Peer {
// Reads the specified |read_size| from |position| into |buffer| and when
// the operation is done invoke |callback| with number of bytes read or an
// error code.
+ // |callback| is called with the following values:
+ // - (Anything greater than or equal 0)
+ // Read was successful with the indicated number of bytes read.
+ // - net::ERR_FAILED
+ // The read has failed because of an error with the network.
+ // - net::ERR_CACHE_MISS
+ // The read was made too far away from the current buffered position.
virtual void Read(int64 position, int read_size,
uint8* buffer, net::CompletionCallback* callback);
// Gets the content length in bytes of the instance after this loader has been
- // started.
+ // started. If this value is -1, then content length is unknown.
virtual int64 content_length() { return content_length_; }
+ // Gets the original size of the file requested. If this value is -1, then
+ // the size is unknown.
+ virtual int64 instance_size() { return instance_size_; }
+
/////////////////////////////////////////////////////////////////////////////
// webkit_glue::ResourceLoaderBridge::Peer implementations.
virtual void OnUploadProgress(uint64 position, uint64 size) {}
@@ -92,13 +114,13 @@ class BufferedResourceLoader : public webkit_glue::ResourceLoaderBridge::Peer {
// Returns true if the current read request will be fulfilled in the future.
bool WillFulfillRead();
- // Checks parameters and make sure they are valid.
- bool VerifyRead();
-
// Method that does the actual read and calls the |read_callbac_|, assuming
// the request range is in |buffer_|.
void ReadInternal();
+ // If we have made a range request, verify the response from the server.
+ bool VerifyPartialResponse(const ResourceLoaderBridge::ResponseInfo& info);
+
// Done with read. Invokes the read callback and reset parameters for the
// read request.
void DoneRead(int error);
@@ -125,6 +147,7 @@ class BufferedResourceLoader : public webkit_glue::ResourceLoaderBridge::Peer {
scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_;
int64 offset_;
int64 content_length_;
+ int64 instance_size_;
// Members used during a read operation. They should be reset after each
// read has completed or failed.
@@ -166,7 +189,7 @@ class BufferedDataSource : public media::DataSource {
uint8* data,
media::DataSource::ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
- virtual bool IsSeekable();
+ virtual bool IsStreaming();
const media::MediaFormat& media_format() {
return media_format_;
@@ -226,9 +249,13 @@ class BufferedDataSource : public media::DataSource {
// Calls |initialize_callback_| and reset it.
void DoneInitialization();
- // Callback method to perform BufferedResourceLoader::Start() during
- // initialization.
- void InitializeStartCallback(int error);
+ // Callback method for |loader_|. This method is called when response for
+ // initial request is received.
+ void InitialStartCallback(int error);
+
+ // Callback method for |probe_loader_|. This method is called when the
+ // response for probe request is received.
+ void ProbeStartCallback(int error);
// Callback method to be passed to BufferedResourceLoader during range
// request. Once a resource request has started, this method will be called
@@ -252,12 +279,22 @@ class BufferedDataSource : public media::DataSource {
// need to protect it.
int64 total_bytes_;
+ // This value will be true if this data source can only support streaming.
+ // i.e. range request is not supported.
+ bool streaming_;
+
// A factory object to produce ResourceLoaderBridge.
scoped_ptr<webkit_glue::MediaResourceLoaderBridgeFactory> bridge_factory_;
- // A downloader object for loading the media resource.
+ // A resource loader for the media resource.
scoped_ptr<BufferedResourceLoader> loader_;
+ // A resource loader that probes the server's ability to serve range requests.
+ scoped_ptr<BufferedResourceLoader> probe_loader_;
+
+ // Callback method from the pipeline for initialization.
+ scoped_ptr<media::FilterCallback> initialize_callback_;
+
// Read parameters received from the Read() method call.
scoped_ptr<media::DataSource::ReadCallback> read_callback_;
int64 read_position_;
@@ -266,6 +303,12 @@ class BufferedDataSource : public media::DataSource {
base::Time read_submitted_time_;
int read_attempts_;
+ // This flag is set to true if the initial request has started.
+ bool initial_response_received_;
+
+ // This flag is set to true if the probe request has started.
+ bool probe_response_received_;
+
// This buffer is intermediate, we use it for BufferedResourceLoader to write
// to. And when read in BufferedResourceLoader is done, we copy data from
// this buffer to |read_buffer_|. The reason for an additional copy is that
@@ -282,9 +325,6 @@ class BufferedDataSource : public media::DataSource {
// The message loop of the render thread.
MessageLoop* render_loop_;
- // Filter callbacks.
- scoped_ptr<media::FilterCallback> initialize_callback_;
-
// Protects |stopped_|.
Lock lock_;
diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc
index d25ac72..8418bea 100644
--- a/webkit/glue/media/buffered_data_source_unittest.cc
+++ b/webkit/glue/media/buffered_data_source_unittest.cc
@@ -71,32 +71,34 @@ class BufferedResourceLoaderTest : public testing::Test {
&BufferedResourceLoaderTest::StartCallback));
}
- void FullResponse(int64 content_length) {
+ void FullResponse(int64 instance_size) {
EXPECT_CALL(*this, StartCallback(net::OK));
ResourceLoaderBridge::ResponseInfo info;
std::string header = StringPrintf("HTTP/1.1 200 OK\n"
- "Content-Length: %lld", content_length);
+ "Content-Length: %lld", instance_size);
replace(header.begin(), header.end(), '\n', '\0');
info.headers = new net::HttpResponseHeaders(header);
- info.content_length = content_length;
+ info.content_length = instance_size;
loader_->OnReceivedResponse(info, false);
- EXPECT_EQ(content_length, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->instance_size());
}
- void PartialResponse(int64 content_length) {
+ void PartialResponse(int64 instance_size) {
EXPECT_CALL(*this, StartCallback(net::OK));
+ int64 content_length = last_position_ - first_position_ + 1;
ResourceLoaderBridge::ResponseInfo info;
std::string header = StringPrintf("HTTP/1.1 206 Partial Content\n"
"Content-Range: bytes %lld-%lld/%lld",
first_position_,
last_position_,
- content_length);
+ instance_size);
replace(header.begin(), header.end(), '\n', '\0');
info.headers = new net::HttpResponseHeaders(header);
info.content_length = content_length;
loader_->OnReceivedResponse(info, false);
- // TODO(hclam): Right now BufferedResourceLoader doesn't care about the
- // partial range replied by the server. Do the check here.
+ EXPECT_EQ(content_length, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->instance_size());
}
void StopWhenLoad() {
@@ -185,7 +187,7 @@ TEST_F(BufferedResourceLoaderTest, NotPartialRange) {
Initialize(kHttpUrl, 100, -1);
Start();
- EXPECT_CALL(*this, StartCallback(net::ERR_INVALID_RESPONSE));
+ EXPECT_CALL(*this, StartCallback(net::ERR_REQUEST_RANGE_NOT_SATISFIABLE));
EXPECT_CALL(*bridge_, Cancel());
EXPECT_CALL(*bridge_, OnDestroy())
.WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
@@ -240,7 +242,7 @@ TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
ReadLoader(10, 10, buffer);
VerifyBuffer(buffer, 10, 10);
- // Read backwith outside buffer.
+ // Read backward outside buffer.
EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
ReadLoader(9, 10, buffer);
@@ -259,7 +261,9 @@ TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
// Try to read outside buffered range after request has completed.
EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
ReadLoader(5, 10, buffer);
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
+
+ // Try to read beyond the instance size.
+ EXPECT_CALL(*this, ReadCallback(0));
ReadLoader(30, 10, buffer);
}
@@ -328,6 +332,7 @@ class MockBufferedResourceLoader : public BufferedResourceLoader {
MOCK_METHOD4(Read, void(int64 position, int read_size, uint8* buffer,
net::CompletionCallback* callback));
MOCK_METHOD0(content_length, int64());
+ MOCK_METHOD0(instance_size, int64());
MOCK_METHOD0(OnDestroy, void());
private:
@@ -402,7 +407,8 @@ class BufferedDataSourceTest : public testing::Test {
message_loop_.release();
}
- void InitializeDataSource(const char* url, int error, int64 content_length) {
+ void InitializeDataSource(const char* url, int error, int probe_error,
+ int64 instance_size) {
// Saves the url first.
gurl_ = GURL(url);
@@ -418,34 +424,81 @@ class BufferedDataSourceTest : public testing::Test {
// Creates the first mock loader to be injected.
loader_.reset(new StrictMock<MockBufferedResourceLoader>());
+ probe_loader_.reset(new StrictMock<MockBufferedResourceLoader>());
InSequence s;
StrictMock<media::MockFilterCallback> callback;
+
+ // There is one resource loader with full range will be created.
EXPECT_CALL(*data_source_, CreateLoader(-1, -1))
.WillOnce(Return(loader_.get()));
+
+ // Then another resource loader with a small partial range is created.
+ EXPECT_CALL(*data_source_, CreateLoader(1, 1))
+ .WillOnce(Return(probe_loader_.get()));
+
+ // The initial response loader will be started.
EXPECT_CALL(*loader_, Start(NotNull()))
.WillOnce(DoAll(Assign(&error_, error),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
- if (error != net::OK) {
+ if (error == net::OK) {
+ EXPECT_CALL(*loader_, instance_size())
+ .WillOnce(Return(instance_size));
+ if (instance_size != -1) {
+ EXPECT_CALL(host_, SetTotalBytes(instance_size));
+ EXPECT_CALL(host_, SetBufferedBytes(instance_size));
+ } else {
+ EXPECT_CALL(host_, SetStreaming(true));
+ }
+
+ // Then the probe resource loader will start.
+ EXPECT_CALL(*probe_loader_, Start(NotNull()))
+ .WillOnce(DoAll(Assign(&error_, probe_error),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeStartCallback)));
+ if (probe_error != net::OK)
+ EXPECT_CALL(host_, SetStreaming(true));
+ EXPECT_CALL(*probe_loader_, Stop());
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
+ } else {
EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
EXPECT_CALL(*loader_, Stop());
- } else {
- EXPECT_CALL(*loader_, content_length())
- .WillOnce(Return(content_length));
- EXPECT_CALL(host_, SetTotalBytes(content_length));
- EXPECT_CALL(host_, SetBufferedBytes(content_length));
+ EXPECT_CALL(*probe_loader_, Stop());
+ EXPECT_CALL(callback, OnFilterCallback());
+ EXPECT_CALL(callback, OnCallbackDestroyed());
+
+ // This expectation looks a little strange, but this is actually what
+ // will happen since we are not running a message loop. So simply
+ // delete the callback.
+ EXPECT_CALL(*probe_loader_, Start(NotNull()))
+ .WillOnce(DeleteArg<0>());
}
- EXPECT_CALL(callback, OnFilterCallback());
- EXPECT_CALL(callback, OnCallbackDestroyed());
data_source_->Initialize(url, callback.NewCallback());
message_loop_->RunAllPending();
if (error == net::OK) {
+ // Verify the size of the data source.
int64 size;
- EXPECT_TRUE(data_source_->GetSize(&size));
- EXPECT_EQ(content_length, size);
+ if (instance_size != -1) {
+ EXPECT_TRUE(data_source_->GetSize(&size));
+ EXPECT_EQ(instance_size, size);
+
+ if (probe_error == net::OK) {
+ EXPECT_FALSE(data_source_->IsStreaming());
+ }
+ } else {
+ EXPECT_FALSE(data_source_->GetSize(&size));
+ EXPECT_EQ(0, size);
+ EXPECT_TRUE(data_source_->IsStreaming());
+ }
+
+ // Verify the data source is streamed if the probe has received an error.
+ if (probe_error != net::OK) {
+ EXPECT_TRUE(data_source_->IsStreaming());
+ }
}
}
@@ -453,10 +506,14 @@ class BufferedDataSourceTest : public testing::Test {
if (loader_.get()) {
InSequence s;
EXPECT_CALL(*loader_, Stop());
- EXPECT_CALL(*loader_, OnDestroy())
- .WillOnce(Invoke(this, &BufferedDataSourceTest::ReleaseLoader));
+ EXPECT_CALL(*probe_loader_, Stop());
}
+ EXPECT_CALL(*loader_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedDataSourceTest::ReleaseLoader));
+ EXPECT_CALL(*probe_loader_, OnDestroy())
+ .WillOnce(Invoke(this, &BufferedDataSourceTest::ReleaseProbeLoader));
+
data_source_->Stop();
message_loop_->RunAllPending();
}
@@ -469,6 +526,10 @@ class BufferedDataSourceTest : public testing::Test {
loader_.release();
}
+ void ReleaseProbeLoader() {
+ probe_loader_.release();
+ }
+
void InvokeStartCallback(net::CompletionCallback* callback) {
callback->RunWithParams(Tuple1<int>(error_));
delete callback;
@@ -560,15 +621,12 @@ class BufferedDataSourceTest : public testing::Test {
Invoke(this,
&BufferedDataSourceTest::InvokeReadCallback)));
- // 2. The read has failed, so read callback will be called.
- EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
-
- // 3. Host will then receive an error.
- EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
-
- // 4. The the loader is destroyed.
+ // 2. Host will then receive an error.
EXPECT_CALL(*loader_, Stop());
+ // 3. The read has failed, so read callback will be called.
+ EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
+
data_source_->Read(
position, size, buffer_,
NewCallback(this, &BufferedDataSourceTest::ReadCallback));
@@ -581,7 +639,7 @@ class BufferedDataSourceTest : public testing::Test {
// 1. Drop the request and let it times out.
EXPECT_CALL(*loader_, Read(position, size, NotNull(), NotNull()))
.WillOnce(DeleteArg<3>());
-
+
// 2. Then the current loader will be stop and destroyed.
StrictMock<MockBufferedResourceLoader> *new_loader =
new StrictMock<MockBufferedResourceLoader>();
@@ -590,13 +648,13 @@ class BufferedDataSourceTest : public testing::Test {
.WillOnce(Return(new_loader));
EXPECT_CALL(*loader_, OnDestroy())
.WillOnce(Invoke(this, &BufferedDataSourceTest::ReleaseLoader));
-
+
// 3. Then the new loader will be started.
EXPECT_CALL(*new_loader, Start(NotNull()))
.WillOnce(DoAll(Assign(&error_, net::OK),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
-
+
// 4. Then again a read request is made to the new loader.
EXPECT_CALL(*new_loader, Read(position, size, NotNull(), NotNull()))
.WillOnce(DoAll(Assign(&error_, size),
@@ -606,7 +664,7 @@ class BufferedDataSourceTest : public testing::Test {
&MessageLoop::Quit)));
EXPECT_CALL(*this, ReadCallback(size));
-
+
data_source_->Read(
position, size, buffer_,
NewCallback(this, &BufferedDataSourceTest::ReadCallback));
@@ -617,7 +675,7 @@ class BufferedDataSourceTest : public testing::Test {
// Make sure data is correct.
EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size));
-
+
EXPECT_TRUE(loader_.get() == NULL);
loader_.reset(new_loader);
}
@@ -627,6 +685,7 @@ class BufferedDataSourceTest : public testing::Test {
scoped_ptr<StrictMock<MockMediaResourceLoaderBridgeFactory> >
bridge_factory_;
scoped_ptr<StrictMock<MockBufferedResourceLoader> > loader_;
+ scoped_ptr<StrictMock<MockBufferedResourceLoader> > probe_loader_;
scoped_refptr<MockBufferedDataSource > data_source_;
scoped_refptr<media::FilterFactory> factory_;
@@ -643,17 +702,36 @@ class BufferedDataSourceTest : public testing::Test {
};
TEST_F(BufferedDataSourceTest, InitializationSuccess) {
- InitializeDataSource(kHttpUrl, net::OK, 1024);
+ InitializeDataSource(kHttpUrl, net::OK, net::OK, 1024);
StopDataSource();
}
TEST_F(BufferedDataSourceTest, InitiailizationFailed) {
- InitializeDataSource(kHttpUrl, net::ERR_FILE_NOT_FOUND, 0);
+ InitializeDataSource(kHttpUrl, net::ERR_FILE_NOT_FOUND,
+ net::ERR_FILE_NOT_FOUND, 0);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, MissingContentLength) {
+ InitializeDataSource(kHttpUrl, net::OK, net::OK, -1);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest, RangeRequestNotSupported) {
+ InitializeDataSource(kHttpUrl, net::OK,
+ net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, 1024);
+ StopDataSource();
+}
+
+TEST_F(BufferedDataSourceTest,
+ MissingContentLengthAndRangeRequestNotSupported) {
+ InitializeDataSource(kHttpUrl, net::OK,
+ net::ERR_REQUEST_RANGE_NOT_SATISFIABLE, -1);
StopDataSource();
}
TEST_F(BufferedDataSourceTest, ReadCacheHit) {
- InitializeDataSource(kHttpUrl, net::OK, 25);
+ InitializeDataSource(kHttpUrl, net::OK, net::OK, 25);
// Performs read with cache hit.
ReadDataSourceHit(10, 10, 10);
@@ -665,14 +743,14 @@ TEST_F(BufferedDataSourceTest, ReadCacheHit) {
}
TEST_F(BufferedDataSourceTest, ReadCacheMiss) {
- InitializeDataSource(kHttpUrl, net::OK, 1024);
+ InitializeDataSource(kHttpUrl, net::OK, net::OK, 1024);
ReadDataSourceMiss(1000, 10);
ReadDataSourceMiss(20, 10);
StopDataSource();
}
TEST_F(BufferedDataSourceTest, ReadFailed) {
- InitializeDataSource(kHttpUrl, net::OK, 1024);
+ InitializeDataSource(kHttpUrl, net::OK, net::OK, 1024);
ReadDataSourceHit(10, 10, 10);
ReadDataSourceFailed(10, 10, net::ERR_CONNECTION_RESET);
StopDataSource();
diff --git a/webkit/glue/media/simple_data_source.cc b/webkit/glue/media/simple_data_source.cc
index 27d286e..48c7138 100644
--- a/webkit/glue/media/simple_data_source.cc
+++ b/webkit/glue/media/simple_data_source.cc
@@ -101,8 +101,8 @@ bool SimpleDataSource::GetSize(int64* size_out) {
return true;
}
-bool SimpleDataSource::IsSeekable() {
- return true;
+bool SimpleDataSource::IsStreaming() {
+ return false;
}
void SimpleDataSource::OnDownloadProgress(uint64 position, uint64 size) {}
diff --git a/webkit/glue/media/simple_data_source.h b/webkit/glue/media/simple_data_source.h
index 1d15389..acfc503 100644
--- a/webkit/glue/media/simple_data_source.h
+++ b/webkit/glue/media/simple_data_source.h
@@ -44,7 +44,7 @@ class SimpleDataSource : public media::DataSource,
virtual void Read(int64 position, size_t size,
uint8* data, ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
- virtual bool IsSeekable();
+ virtual bool IsStreaming();
// webkit_glue::ResourceLoaderBridge::Peer implementation.
virtual void OnDownloadProgress(uint64 position, uint64 size);
diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc
index d8bad19..e31139a 100644
--- a/webkit/glue/webmediaplayer_impl.cc
+++ b/webkit/glue/webmediaplayer_impl.cc
@@ -6,6 +6,7 @@
#include "base/command_line.h"
#include "googleurl/src/gurl.h"
+#include "media/base/media_format.h"
#include "media/filters/ffmpeg_audio_decoder.h"
#include "media/filters/ffmpeg_demuxer.h"
#include "media/filters/ffmpeg_video_decoder.h"
@@ -264,9 +265,7 @@ bool WebMediaPlayerImpl::totalBytesKnown() {
bool WebMediaPlayerImpl::hasVideo() const {
DCHECK(MessageLoop::current() == main_loop_);
- size_t width, height;
- pipeline_->GetVideoSize(&width, &height);
- return width != 0 && height != 0;
+ return pipeline_->IsRendered(media::mime_type::kMajorTypeVideo);
}
WebKit::WebSize WebMediaPlayerImpl::naturalSize() const {
@@ -317,14 +316,13 @@ float WebMediaPlayerImpl::maxTimeBuffered() const {
float WebMediaPlayerImpl::maxTimeSeekable() const {
DCHECK(MessageLoop::current() == main_loop_);
- // TODO(scherkus): move this logic down into the pipeline.
- if (pipeline_->GetTotalBytes() == 0) {
+ // If we are performing streaming, we report that we cannot seek at all.
+ // We are using this flag to indicate if the data source supports seeking
+ // or not. We should be able to seek even if we are performing streaming.
+ // TODO(hclam): We need to update this when we have better caching.
+ if (pipeline_->IsStreaming())
return 0.0f;
- }
- double total_bytes = static_cast<double>(pipeline_->GetTotalBytes());
- double buffered_bytes = static_cast<double>(pipeline_->GetBufferedBytes());
- double duration = static_cast<double>(pipeline_->GetDuration().InSecondsF());
- return static_cast<float>(duration * (buffered_bytes / total_bytes));
+ return static_cast<float>(pipeline_->GetDuration().InSecondsF());
}
unsigned long long WebMediaPlayerImpl::bytesLoaded() const {
@@ -354,6 +352,23 @@ void WebMediaPlayerImpl::paint(WebCanvas* canvas,
proxy_->Paint(canvas, rect);
}
+bool WebMediaPlayerImpl::hasSingleSecurityOrigin() const {
+ // TODO(hclam): Implement this.
+ return false;
+}
+
+WebKit::WebMediaPlayer::MovieLoadType
+ WebMediaPlayerImpl::movieLoadType() const {
+ DCHECK(MessageLoop::current() == main_loop_);
+
+ // TODO(hclam): If the pipeline is performing streaming, we say that this is
+ // a live stream. But instead it should be a StoredStream if we have proper
+ // caching.
+ if (pipeline_->IsStreaming())
+ return WebKit::WebMediaPlayer::LiveStream;
+ return WebKit::WebMediaPlayer::Unknown;
+}
+
void WebMediaPlayerImpl::WillDestroyCurrentMessageLoop() {
Destroy();
main_loop_ = NULL;
diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h
index a73ea71..3fd44fe 100644
--- a/webkit/glue/webmediaplayer_impl.h
+++ b/webkit/glue/webmediaplayer_impl.h
@@ -201,6 +201,9 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
virtual unsigned long long bytesLoaded() const;
virtual unsigned long long totalBytes() const;
+ virtual bool hasSingleSecurityOrigin() const;
+ virtual WebKit::WebMediaPlayer::MovieLoadType movieLoadType() const;
+
// As we are closing the tab or even the browser, |main_loop_| is destroyed
// even before this object gets destructed, so we need to know when
// |main_loop_| is being destroyed and we can stop posting repaint task