summaryrefslogtreecommitdiffstats
path: root/webkit/glue/media
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/media')
-rw-r--r--webkit/glue/media/buffered_data_source.cc49
-rw-r--r--webkit/glue/media/buffered_data_source.h18
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc147
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 {