From 38259a7a83545e07681d921564468844c7b03337 Mon Sep 17 00:00:00 2001 From: "hclam@chromium.org" Date: Wed, 29 Jul 2009 21:49:49 +0000 Subject: 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 --- media/base/filter_host.h | 3 + media/base/filters.h | 5 +- media/base/mock_filter_host.h | 1 + media/base/mock_filters.h | 2 +- media/base/pipeline.h | 4 + media/base/pipeline_impl.cc | 12 + media/base/pipeline_impl.h | 6 + media/filters/ffmpeg_demuxer.cc | 6 +- media/filters/ffmpeg_demuxer.h | 2 +- media/filters/ffmpeg_demuxer_unittest.cc | 6 +- media/filters/ffmpeg_glue.cc | 2 +- media/filters/ffmpeg_glue.h | 2 +- media/filters/ffmpeg_glue_unittest.cc | 8 +- media/filters/file_data_source.cc | 5 +- media/filters/file_data_source.h | 2 +- webkit/api/public/WebMediaPlayer.h | 10 + webkit/api/src/WebMediaPlayerClientImpl.cpp | 28 +++ webkit/api/src/WebMediaPlayerClientImpl.h | 2 + webkit/glue/media/buffered_data_source.cc | 245 ++++++++++++--------- webkit/glue/media/buffered_data_source.h | 66 ++++-- webkit/glue/media/buffered_data_source_unittest.cc | 160 ++++++++++---- webkit/glue/media/simple_data_source.cc | 4 +- webkit/glue/media/simple_data_source.h | 2 +- webkit/glue/webmediaplayer_impl.cc | 35 ++- webkit/glue/webmediaplayer_impl.h | 3 + 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 { // 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 > > protocol( new StrictMock >()); - 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( + 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(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(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 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 bridge_factory_; - // A downloader object for loading the media resource. + // A resource loader for the media resource. scoped_ptr loader_; + // A resource loader that probes the server's ability to serve range requests. + scoped_ptr probe_loader_; + + // Callback method from the pipeline for initialization. + scoped_ptr initialize_callback_; + // Read parameters received from the Read() method call. scoped_ptr 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 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()); + probe_loader_.reset(new StrictMock()); InSequence s; StrictMock 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(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 *new_loader = new StrictMock(); @@ -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(position), size)); - + EXPECT_TRUE(loader_.get() == NULL); loader_.reset(new_loader); } @@ -627,6 +685,7 @@ class BufferedDataSourceTest : public testing::Test { scoped_ptr > bridge_factory_; scoped_ptr > loader_; + scoped_ptr > probe_loader_; scoped_refptr data_source_; scoped_refptr 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(pipeline_->GetTotalBytes()); - double buffered_bytes = static_cast(pipeline_->GetBufferedBytes()); - double duration = static_cast(pipeline_->GetDuration().InSecondsF()); - return static_cast(duration * (buffered_bytes / total_bytes)); + return static_cast(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 -- cgit v1.1