diff options
Diffstat (limited to 'webkit/glue/media')
-rw-r--r-- | webkit/glue/media/buffered_data_source.cc | 49 | ||||
-rw-r--r-- | webkit/glue/media/buffered_data_source.h | 18 | ||||
-rw-r--r-- | webkit/glue/media/buffered_data_source_unittest.cc | 147 |
3 files changed, 209 insertions, 5 deletions
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc index 64e09b4..dfac588 100644 --- a/webkit/glue/media/buffered_data_source.cc +++ b/webkit/glue/media/buffered_data_source.cc @@ -69,7 +69,6 @@ bool IsDataProtocol(const GURL& url) { } // namespace namespace webkit_glue { - ///////////////////////////////////////////////////////////////////////////// // BufferedResourceLoader BufferedResourceLoader::BufferedResourceLoader( @@ -79,6 +78,7 @@ BufferedResourceLoader::BufferedResourceLoader( int64 last_byte_position) : buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)), deferred_(false), + defer_allowed_(true), completed_(false), range_requested_(false), partial_response_(false), @@ -225,6 +225,11 @@ int64 BufferedResourceLoader::GetBufferedLastBytePosition() { return kPositionNotSpecified; } +void BufferedResourceLoader::SetAllowDefer(bool is_allowed) { + defer_allowed_ = is_allowed; + DisableDeferIfNeeded(); +} + ///////////////////////////////////////////////////////////////////////////// // BufferedResourceLoader, // webkit_glue::ResourceLoaderBridge::Peer implementations @@ -326,6 +331,15 @@ void BufferedResourceLoader::OnReceivedData(const char* data, int len) { // If there is an active read request, try to fulfill the request. if (HasPendingRead() && CanFulfillRead()) { ReadInternal(); + } else if (!defer_allowed_) { + // If we're not allowed to defer, slide the buffer window forward instead + // of deferring. + if (buffer_->forward_bytes() > buffer_->forward_capacity()) { + size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity(); + bool success = buffer_->Seek(excess); + DCHECK(success); + offset_ += first_offset_ + excess; + } } // At last see if the buffer is full and we need to defer the downloading. @@ -381,6 +395,9 @@ void BufferedResourceLoader::OnCompletedRequest( ///////////////////////////////////////////////////////////////////////////// // BufferedResourceLoader, private void BufferedResourceLoader::EnableDeferIfNeeded() { + if (!defer_allowed_) + return; + if (!deferred_ && buffer_->forward_bytes() >= buffer_->forward_capacity()) { deferred_ = true; @@ -394,7 +411,8 @@ void BufferedResourceLoader::EnableDeferIfNeeded() { void BufferedResourceLoader::DisableDeferIfNeeded() { if (deferred_ && - buffer_->forward_bytes() < buffer_->forward_capacity() / 2) { + (!defer_allowed_ || + buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) { deferred_ = false; if (bridge_.get()) @@ -539,7 +557,8 @@ BufferedDataSource::BufferedDataSource( intermediate_read_buffer_size_(kInitialReadBufferSize), render_loop_(render_loop), stop_signal_received_(false), - stopped_on_render_loop_(false) { + stopped_on_render_loop_(false), + media_is_paused_(true) { } BufferedDataSource::~BufferedDataSource() { @@ -605,6 +624,12 @@ void BufferedDataSource::Stop(media::FilterCallback* callback) { NewRunnableMethod(this, &BufferedDataSource::CleanupTask)); } +void BufferedDataSource::SetPlaybackRate(float playback_rate) { + render_loop_->PostTask(FROM_HERE, + NewRunnableMethod(this, &BufferedDataSource::SetPlaybackRateTask, + playback_rate)); +} + ///////////////////////////////////////////////////////////////////////////// // BufferedDataSource, media::DataSource implementation void BufferedDataSource::Read(int64 position, size_t size, uint8* data, @@ -730,6 +755,7 @@ void BufferedDataSource::RestartLoadingTask() { return; loader_ = CreateResourceLoader(read_position_, -1); + loader_->SetAllowDefer(!media_is_paused_); loader_->Start( NewCallback(this, &BufferedDataSource::PartialReadStartCallback), NewCallback(this, &BufferedDataSource::NetworkEventCallback)); @@ -760,11 +786,28 @@ void BufferedDataSource::WatchDogTask() { // retry the request. loader_->Stop(); loader_ = CreateResourceLoader(read_position_, -1); + loader_->SetAllowDefer(!media_is_paused_); loader_->Start( NewCallback(this, &BufferedDataSource::PartialReadStartCallback), NewCallback(this, &BufferedDataSource::NetworkEventCallback)); } +void BufferedDataSource::SetPlaybackRateTask(float playback_rate) { + DCHECK(MessageLoop::current() == render_loop_); + DCHECK(loader_.get()); + + bool previously_paused = media_is_paused_; + media_is_paused_ = (playback_rate == 0.0); + + // Disallow deferring data when we are pausing, allow deferring data + // when we resume playing. + if (previously_paused && !media_is_paused_) { + loader_->SetAllowDefer(true); + } else if (!previously_paused && media_is_paused_) { + loader_->SetAllowDefer(false); + } +} + // This method is the place where actual read happens, |loader_| must be valid // prior to make this method call. void BufferedDataSource::ReadInternal() { diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h index 4440f2f..4aff24b 100644 --- a/webkit/glue/media/buffered_data_source.h +++ b/webkit/glue/media/buffered_data_source.h @@ -25,7 +25,6 @@ #include "webkit/glue/media/media_resource_loader_bridge_factory.h" namespace webkit_glue { - ///////////////////////////////////////////////////////////////////////////// // BufferedResourceLoader // This class works inside demuxer thread and render thread. It contains a @@ -90,6 +89,9 @@ class BufferedResourceLoader : // is not available. virtual int64 GetBufferedLastBytePosition(); + // Sets whether deferring data is allowed or disallowed. + virtual void SetAllowDefer(bool is_allowed); + // Gets the content length in bytes of the instance after this loader has been // started. If this value is -1, then content length is unknown. virtual int64 content_length() { return content_length_; } @@ -131,6 +133,7 @@ class BufferedResourceLoader : virtual ~BufferedResourceLoader(); private: + friend class BufferedResourceLoaderTest; // Defer the resource loading if the buffer is full. void EnableDeferIfNeeded(); @@ -170,6 +173,9 @@ class BufferedResourceLoader : // True if resource loading was deferred. bool deferred_; + // True if resource loader is allowed to defer, false otherwise. + bool defer_allowed_; + // True if resource loading has completed. bool completed_; @@ -231,6 +237,7 @@ class BufferedDataSource : public media::DataSource { virtual void Initialize(const std::string& url, media::FilterCallback* callback); virtual void Stop(media::FilterCallback* callback); + virtual void SetPlaybackRate(float playback_rate); // media::DataSource implementation. // Called from demuxer thread. @@ -288,6 +295,11 @@ class BufferedDataSource : public media::DataSource { // creates a new one to accommodate the read request. void WatchDogTask(); + // This task uses the current playback rate with the previous playback rate + // to determine whether we are going from pause to play and play to pause, + // and signals the buffered resource loader accordingly. + void SetPlaybackRateTask(float playback_rate); + // The method that performs actual read. This method can only be executed on // the render thread. void ReadInternal(); @@ -387,6 +399,10 @@ class BufferedDataSource : public media::DataSource { // on the render thread. bool stopped_on_render_loop_; + // This variable is true when we are in a paused state and false when we + // are in a playing state. + bool media_is_paused_; + // This timer is to run the WatchDogTask repeatedly. We use a timer instead // of doing PostDelayedTask() reduce the extra reference held by the message // loop. The RepeatingTimer does PostDelayedTask() internally, by using it diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc index 14e125c..fd07d4e 100644 --- a/webkit/glue/media/buffered_data_source_unittest.cc +++ b/webkit/glue/media/buffered_data_source_unittest.cc @@ -79,6 +79,11 @@ class BufferedResourceLoaderTest : public testing::Test { EXPECT_EQ(gurl_.spec(), loader_->GetURLForDebugging().spec()); } + void SetLoaderBuffer(size_t forward_capacity, size_t backward_capacity) { + loader_->buffer_.reset( + new media::SeekableBuffer(backward_capacity, forward_capacity)); + } + void Start() { InSequence s; EXPECT_CALL(bridge_factory_, @@ -140,7 +145,8 @@ class BufferedResourceLoaderTest : public testing::Test { // Helper method to write to |loader_| from |data_|. void WriteLoader(int position, int size) { - EXPECT_CALL(*this, NetworkCallback()); + EXPECT_CALL(*this, NetworkCallback()) + .RetiresOnSaturation(); loader_->OnReceivedData(reinterpret_cast<char*>(data_ + position), size); } @@ -155,6 +161,20 @@ class BufferedResourceLoaderTest : public testing::Test { EXPECT_EQ(0, memcmp(buffer, data_ + pos, size)); } + // Helper method to disallow deferring in |loader_|. + void DisallowLoaderDefer() { + if (loader_->deferred_) { + EXPECT_CALL(*bridge_, SetDefersLoading(false)); + EXPECT_CALL(*this, NetworkCallback()); + } + loader_->SetAllowDefer(false); + } + + // Helper method to allow deferring in |loader_|. + void AllowLoaderDefer() { + loader_->SetAllowDefer(true); + } + MOCK_METHOD1(StartCallback, void(int error)); MOCK_METHOD1(ReadCallback, void(int error)); MOCK_METHOD0(NetworkCallback, void()); @@ -362,6 +382,131 @@ TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) { loader_->OnCompletedRequest(status, ""); } +// Tests the logic of caching data to disk when media is paused. +TEST_F(BufferedResourceLoaderTest, AllowDefer_NoDataReceived) { + Initialize(kHttpUrl, 10, 99); + SetLoaderBuffer(10, 20); + Start(); + PartialResponse(10, 99, 100); + + // Start in undeferred state, then disallow defer, then allow defer + // without receiving data in between. + DisallowLoaderDefer(); + AllowLoaderDefer(); + StopWhenLoad(); +} + +TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadSameWindow) { + Initialize(kHttpUrl, 10, 99); + SetLoaderBuffer(10, 20); + Start(); + PartialResponse(10, 99, 100); + + uint8 buffer[10]; + + // Start in undeferred state, disallow defer, receive data but don't shift + // buffer window, then allow defer and read. + DisallowLoaderDefer(); + WriteLoader(10, 10); + AllowLoaderDefer(); + + EXPECT_CALL(*this, ReadCallback(10)); + ReadLoader(10, 10, buffer); + VerifyBuffer(buffer, 10, 10); + StopWhenLoad(); +} + +TEST_F(BufferedResourceLoaderTest, AllowDefer_ReadPastWindow) { + Initialize(kHttpUrl, 10, 99); + SetLoaderBuffer(10, 20); + Start(); + PartialResponse(10, 99, 100); + + uint8 buffer[10]; + + // Not deferred, disallow defer, received data and shift buffer window, + // allow defer, then read in area outside of buffer window. + DisallowLoaderDefer(); + WriteLoader(10, 10); + WriteLoader(20, 50); + AllowLoaderDefer(); + + EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); + ReadLoader(10, 10, buffer); + StopWhenLoad(); +} + +TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) { + Initialize(kHttpUrl, 10, 99); + SetLoaderBuffer(10, 20); + Start(); + PartialResponse(10, 99, 100); + + uint8 buffer[10]; + + // Start in deferred state, then disallow defer, receive no data, and + // allow defer and read. + EXPECT_CALL(*bridge_, SetDefersLoading(true)); + EXPECT_CALL(*this, NetworkCallback()); + WriteLoader(10, 40); + + DisallowLoaderDefer(); + AllowLoaderDefer(); + + EXPECT_CALL(*this, ReadCallback(10)); + ReadLoader(20, 10, buffer); + VerifyBuffer(buffer, 20, 10); + StopWhenLoad(); +} + +TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadSameWindow) { + Initialize(kHttpUrl, 10, 99); + SetLoaderBuffer(10, 20); + Start(); + PartialResponse(10, 99, 100); + + uint8 buffer[10]; + + // Start in deferred state, disallow defer, receive data and shift buffer + // window, allow defer, and read in a place that's still in the window. + EXPECT_CALL(*bridge_, SetDefersLoading(true)); + EXPECT_CALL(*this, NetworkCallback()); + WriteLoader(10, 30); + + DisallowLoaderDefer(); + WriteLoader(40, 5); + AllowLoaderDefer(); + + EXPECT_CALL(*this, ReadCallback(10)); + ReadLoader(20, 10, buffer); + VerifyBuffer(buffer, 20, 10); + StopWhenLoad(); +} + +TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) { + Initialize(kHttpUrl, 10, 99); + SetLoaderBuffer(10, 20); + Start(); + PartialResponse(10, 99, 100); + + uint8 buffer[10]; + + // Start in deferred state, disallow defer, receive data and shift buffer + // window, allow defer, and read outside of the buffer window. + EXPECT_CALL(*bridge_, SetDefersLoading(true)); + EXPECT_CALL(*this, NetworkCallback()); + WriteLoader(10, 40); + + DisallowLoaderDefer(); + WriteLoader(50, 20); + WriteLoader(70, 40); + AllowLoaderDefer(); + + EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS)); + ReadLoader(20, 5, buffer); + StopWhenLoad(); +} + // TODO(hclam): add unit test for defer loading. class MockBufferedResourceLoader : public BufferedResourceLoader { |