summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--webkit/glue/media/buffered_data_source.cc51
-rw-r--r--webkit/glue/media/buffered_data_source.h18
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc141
3 files changed, 204 insertions, 6 deletions
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index 64e09b4..1c69e36 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(
@@ -96,7 +95,8 @@ BufferedResourceLoader::BufferedResourceLoader(
read_size_(0),
read_buffer_(NULL),
first_offset_(0),
- last_offset_(0) {
+ last_offset_(0),
+ defer_allowed_(true) {
}
BufferedResourceLoader::~BufferedResourceLoader() {
@@ -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..cc7245c 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();
@@ -206,6 +209,9 @@ class BufferedResourceLoader :
int first_offset_;
int last_offset_;
+ // True if resource loader is allowed to defer, false otherwise.
+ bool defer_allowed_;
+
DISALLOW_COPY_AND_ASSIGN(BufferedResourceLoader);
};
@@ -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();
@@ -393,6 +405,10 @@ class BufferedDataSource : public media::DataSource {
// the message loop doesn't hold a reference for the watch dog task.
base::RepeatingTimer<BufferedDataSource> watch_dog_timer_;
+ // This variable is true when we are in a paused state and false when we
+ // are in a playing state.
+ bool media_is_paused_;
+
DISALLOW_COPY_AND_ASSIGN(BufferedDataSource);
};
diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc
index 14e125c..90f917d 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,125 @@ 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();
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
+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);
+}
+
// TODO(hclam): add unit test for defer loading.
class MockBufferedResourceLoader : public BufferedResourceLoader {