diff options
author | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-15 23:07:36 +0000 |
---|---|---|
committer | vrk@google.com <vrk@google.com@0039d316-1c4b-4281-b951-d872f2087c98> | 2010-07-15 23:07:36 +0000 |
commit | 151285d88536365ebb2fdcf1dcb4f972ca74add2 (patch) | |
tree | a69ff40c4716cb0786d2cefcfd95f8b38339f541 /webkit/glue/media | |
parent | d663eee8d5e65747a54654648980b7ba72f9d56b (diff) | |
download | chromium_src-151285d88536365ebb2fdcf1dcb4f972ca74add2.zip chromium_src-151285d88536365ebb2fdcf1dcb4f972ca74add2.tar.gz chromium_src-151285d88536365ebb2fdcf1dcb4f972ca74add2.tar.bz2 |
Video Buffering: Caches data to disk when paused (resubmit)
Resubmitting this buffering patch because original patch was reverted.
This patch clears up the memory leak issue in the unit tests and adds
valgrind suppressions to silence the uninitialized variable false positives.
BUG=42285
TEST=test_shell_tests
Review URL: http://codereview.chromium.org/2908003
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@52569 0039d316-1c4b-4281-b951-d872f2087c98
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 { |