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.cc283
-rw-r--r--webkit/glue/media/buffered_data_source.h42
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc60
-rw-r--r--webkit/glue/media/buffered_resource_loader.cc175
-rw-r--r--webkit/glue/media/buffered_resource_loader.h58
-rw-r--r--webkit/glue/media/buffered_resource_loader_unittest.cc216
-rw-r--r--webkit/glue/media/simple_data_source.cc245
-rw-r--r--webkit/glue/media/simple_data_source.h26
-rw-r--r--webkit/glue/media/simple_data_source_unittest.cc44
-rw-r--r--webkit/glue/media/video_renderer_impl.cc10
-rw-r--r--webkit/glue/media/video_renderer_impl.h10
-rw-r--r--webkit/glue/media/web_data_source.h24
-rw-r--r--webkit/glue/media/web_data_source_factory.cc103
-rw-r--r--webkit/glue/media/web_data_source_factory.h51
-rw-r--r--webkit/glue/media/web_video_renderer.h4
15 files changed, 933 insertions, 418 deletions
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index 7902221..8aafd26 100644
--- a/webkit/glue/media/buffered_data_source.cc
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -6,6 +6,7 @@
#include "media/base/filter_host.h"
#include "net/base/net_errors.h"
+#include "webkit/glue/media/web_data_source_factory.h"
#include "webkit/glue/webkit_glue.h"
using WebKit::WebFrame;
@@ -27,10 +28,25 @@ static const int kReadTrials = 3;
// of FFmpeg.
static const int kInitialReadBufferSize = 32768;
+static WebDataSource* NewBufferedDataSource(MessageLoop* render_loop,
+ WebKit::WebFrame* frame) {
+ return new BufferedDataSource(render_loop, frame);
+}
+
+// static
+media::DataSourceFactory* BufferedDataSource::CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer) {
+ return new WebDataSourceFactory(render_loop, frame, &NewBufferedDataSource,
+ build_observer);
+}
+
BufferedDataSource::BufferedDataSource(
MessageLoop* render_loop,
WebFrame* frame)
: total_bytes_(kPositionNotSpecified),
+ buffered_bytes_(0),
loaded_(false),
streaming_(false),
frame_(frame),
@@ -48,6 +64,8 @@ BufferedDataSource::BufferedDataSource(
stop_signal_received_(false),
stopped_on_render_loop_(false),
media_is_paused_(true),
+ media_has_played_(false),
+ preload_(media::METADATA),
using_range_request_(true) {
}
@@ -73,18 +91,25 @@ base::TimeDelta BufferedDataSource::GetTimeoutMilliseconds() {
return base::TimeDelta::FromMilliseconds(kTimeoutMilliseconds);
}
-/////////////////////////////////////////////////////////////////////////////
-// media::Filter implementation.
+void BufferedDataSource::set_host(media::FilterHost* host) {
+ DataSource::set_host(host);
+
+ if (loader_.get())
+ UpdateHostState();
+}
+
void BufferedDataSource::Initialize(const std::string& url,
- media::FilterCallback* callback) {
+ media::PipelineStatusCallback* callback) {
// Saves the url.
url_ = GURL(url);
- if (!IsProtocolSupportedForMedia(url_)) {
- // This method is called on the thread where host() lives so it is safe
- // to make this call.
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
- callback->Run();
+ // This data source doesn't support data:// protocol so reject it.
+ if (url_.SchemeIs(kDataScheme)) {
+ callback->Run(media::DATASOURCE_ERROR_URL_NOT_SUPPORTED);
+ delete callback;
+ return;
+ } else if (!IsProtocolSupportedForMedia(url_)) {
+ callback->Run(media::PIPELINE_ERROR_NETWORK);
delete callback;
return;
}
@@ -92,8 +117,6 @@ void BufferedDataSource::Initialize(const std::string& url,
DCHECK(callback);
initialize_callback_.reset(callback);
- media_format_.SetAsString(media::MediaFormat::kMimeType,
- media::mime_type::kApplicationOctetStream);
media_format_.SetAsString(media::MediaFormat::kURL, url);
// Post a task to complete the initialization task.
@@ -101,13 +124,18 @@ void BufferedDataSource::Initialize(const std::string& url,
NewRunnableMethod(this, &BufferedDataSource::InitializeTask));
}
-bool BufferedDataSource::IsUrlSupported(const std::string& url) {
- GURL gurl(url);
+void BufferedDataSource::CancelInitialize() {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(initialize_callback_.get());
- // This data source doesn't support data:// protocol so reject it.
- return IsProtocolSupportedForMedia(gurl) && !gurl.SchemeIs(kDataScheme);
+ initialize_callback_.reset();
+
+ render_loop_->PostTask(
+ FROM_HERE, NewRunnableMethod(this, &BufferedDataSource::CleanupTask));
}
+/////////////////////////////////////////////////////////////////////////////
+// media::Filter implementation.
void BufferedDataSource::Stop(media::FilterCallback* callback) {
{
base::AutoLock auto_lock(lock_);
@@ -128,6 +156,12 @@ void BufferedDataSource::SetPlaybackRate(float playback_rate) {
playback_rate));
}
+void BufferedDataSource::SetPreload(media::Preload preload) {
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::SetPreloadTask,
+ preload));
+}
+
/////////////////////////////////////////////////////////////////////////////
// media::DataSource implementation.
void BufferedDataSource::Read(int64 position, size_t size, uint8* data,
@@ -174,15 +208,6 @@ bool BufferedDataSource::HasSingleOrigin() {
void BufferedDataSource::Abort() {
DCHECK(MessageLoop::current() == render_loop_);
- {
- base::AutoLock auto_lock(lock_);
-
- // If we are told to abort, immediately return from any pending read
- // with an error.
- if (read_callback_.get())
- DoneRead_Locked(net::ERR_FAILED);
- }
-
CleanupTask();
frame_ = NULL;
}
@@ -192,7 +217,7 @@ void BufferedDataSource::Abort() {
void BufferedDataSource::InitializeTask() {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(!loader_.get());
- if (stopped_on_render_loop_)
+ if (stopped_on_render_loop_ || !initialize_callback_.get())
return;
// Kick starts the watch dog task that will handle connection timeout.
@@ -256,7 +281,13 @@ void BufferedDataSource::CleanupTask() {
if (stopped_on_render_loop_)
return;
- read_callback_.reset();
+ // Signal that stop task has finished execution.
+ // NOTE: it's vital that this be set under lock, as that's how Read() tests
+ // before registering a new |read_callback_| (which is cleared below).
+ stopped_on_render_loop_ = true;
+
+ if (read_callback_.get())
+ DoneRead_Locked(net::ERR_FAILED);
}
// Stop the watch dog.
@@ -272,9 +303,6 @@ void BufferedDataSource::CleanupTask() {
read_buffer_ = 0;
read_submitted_time_ = base::Time();
read_attempts_ = 0;
-
- // Signal that stop task has finished execution.
- stopped_on_render_loop_ = true;
}
void BufferedDataSource::RestartLoadingTask() {
@@ -290,7 +318,8 @@ void BufferedDataSource::RestartLoadingTask() {
}
loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
- loader_->SetAllowDefer(!media_is_paused_);
+ BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
+ loader_->UpdateDeferStrategy(strategy);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
NewCallback(this, &BufferedDataSource::NetworkEventCallback),
@@ -326,7 +355,8 @@ void BufferedDataSource::WatchDogTask() {
// retry the request.
loader_->Stop();
loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
- loader_->SetAllowDefer(!media_is_paused_);
+ BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
+ loader_->UpdateDeferStrategy(strategy);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
NewCallback(this, &BufferedDataSource::NetworkEventCallback),
@@ -340,13 +370,37 @@ void BufferedDataSource::SetPlaybackRateTask(float playback_rate) {
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);
+ if (!media_has_played_ && previously_paused && !media_is_paused_)
+ media_has_played_ = true;
+
+ BufferedResourceLoader::DeferStrategy strategy = ChooseDeferStrategy();
+ loader_->UpdateDeferStrategy(strategy);
+}
+
+void BufferedDataSource::SetPreloadTask(media::Preload preload) {
+ DCHECK(MessageLoop::current() == render_loop_);
+ preload_ = preload;
+}
+
+BufferedResourceLoader::DeferStrategy
+BufferedDataSource::ChooseDeferStrategy() {
+ // If the user indicates preload=metadata, then just load exactly
+ // what is needed for starting the pipeline and prerolling frames.
+ if (preload_ == media::METADATA && !media_has_played_)
+ return BufferedResourceLoader::kReadThenDefer;
+
+ // In general, we want to try to buffer the entire video when the video
+ // is paused. But we don't want to do this if the video hasn't played yet
+ // and preload!=auto.
+ if (media_is_paused_ &&
+ (preload_ == media::AUTO || media_has_played_)) {
+ return BufferedResourceLoader::kNeverDefer;
}
+
+ // When the video is playing, regardless of preload state, we buffer up
+ // to a hard limit and enable/disable deferring when the buffer is
+ // depleted/full.
+ return BufferedResourceLoader::kThresholdDefer;
}
// This method is the place where actual read happens, |loader_| must be valid
@@ -386,13 +440,15 @@ void BufferedDataSource::DoneRead_Locked(int error) {
read_buffer_ = 0;
}
-void BufferedDataSource::DoneInitialization_Locked() {
+void BufferedDataSource::DoneInitialization_Locked(
+ media::PipelineStatus status) {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(initialize_callback_.get());
lock_.AssertAcquired();
- initialize_callback_->Run();
- initialize_callback_.reset();
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback(
+ initialize_callback_.release());
+ initialize_callback->Run(status);
}
/////////////////////////////////////////////////////////////////////////////
@@ -402,15 +458,20 @@ void BufferedDataSource::HttpInitialStartCallback(int error) {
DCHECK(loader_.get());
int64 instance_size = loader_->instance_size();
- bool partial_response = loader_->partial_response();
bool success = error == net::OK;
+ if (!initialize_callback_.get()) {
+ loader_->Stop();
+ return;
+ }
+
if (success) {
// TODO(hclam): Needs more thinking about supporting servers without range
// request or their partial response is not complete.
total_bytes_ = instance_size;
loaded_ = false;
- streaming_ = (instance_size == kPositionNotSpecified) || !partial_response;
+ streaming_ = (instance_size == kPositionNotSpecified) ||
+ !loader_->range_supported();
} else {
// TODO(hclam): In case of failure, we can retry several times.
loader_->Stop();
@@ -429,85 +490,90 @@ void BufferedDataSource::HttpInitialStartCallback(int error) {
return;
}
- // 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.
- base::AutoLock auto_lock(lock_);
- if (stop_signal_received_)
- return;
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ // TODO: Review locking in this class and figure out a way to run the callback
+ // w/o the lock.
+ scoped_refptr<BufferedDataSource> destruction_guard(this);
+ {
+ // 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.
+ base::AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
- if (!success) {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
- DoneInitialization_Locked();
- return;
- }
+ if (!success) {
+ DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
+ return;
+ }
- if (streaming_) {
- // If the server didn't reply with an instance size, it is likely this
- // is a streaming response.
- host()->SetStreaming(true);
- } else {
- // 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(0);
+ UpdateHostState();
+ DoneInitialization_Locked(media::PIPELINE_OK);
}
-
- // Currently, only files can be used reliably w/o a network.
- host()->SetLoaded(false);
- DoneInitialization_Locked();
}
void BufferedDataSource::NonHttpInitialStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(loader_.get());
+ if (!initialize_callback_.get()) {
+ loader_->Stop();
+ return;
+ }
+
int64 instance_size = loader_->instance_size();
bool success = error == net::OK && instance_size != kPositionNotSpecified;
if (success) {
total_bytes_ = instance_size;
+ buffered_bytes_ = total_bytes_;
loaded_ = true;
} 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
- // 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.
- base::AutoLock auto_lock(lock_);
- if (stop_signal_received_)
- return;
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ // TODO: Review locking in this class and figure out a way to run the callback
+ // w/o the lock.
+ scoped_refptr<BufferedDataSource> destruction_guard(this);
+ {
+ // 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.
+ base::AutoLock auto_lock(lock_);
+ if (stop_signal_received_ || !initialize_callback_.get())
+ return;
- if (success) {
- host()->SetTotalBytes(total_bytes_);
- host()->SetBufferedBytes(total_bytes_);
- host()->SetLoaded(loaded_);
- } else {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
+ if (!success) {
+ DoneInitialization_Locked(media::PIPELINE_ERROR_NETWORK);
+ return;
+ }
+
+ UpdateHostState();
+ DoneInitialization_Locked(media::PIPELINE_OK);
}
- DoneInitialization_Locked();
}
void BufferedDataSource::PartialReadStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(loader_.get());
- // This callback method is invoked after we have verified the server has
- // range request capability, so as a safety guard verify again the response
- // is partial.
- if (error == net::OK && loader_->partial_response()) {
- // Once the range request has started successfully, we can proceed with
+ if (error == net::OK) {
+ // Once the request has started successfully, we can proceed with
// reading from it.
ReadInternal();
return;
@@ -562,6 +628,14 @@ 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);
+ } else if (error == 0 && total_bytes_ == kPositionNotSpecified) {
+ // We've reached the end of the file and we didn't know the total size
+ // before. Update the total size so Read()s past the end of the file will
+ // fail like they would if we had known the file size at the beginning.
+ total_bytes_ = loader_->instance_size();
+
+ if (host() && total_bytes_ != kPositionNotSpecified)
+ host()->SetTotalBytes(total_bytes_);
}
DoneRead_Locked(error);
}
@@ -596,9 +670,28 @@ void BufferedDataSource::NetworkEventCallback() {
if (network_activity != network_activity_) {
network_activity_ = network_activity;
- host()->SetNetworkActivity(network_activity);
+ if (host())
+ host()->SetNetworkActivity(network_activity);
+ }
+
+ buffered_bytes_ = buffered_position + 1;
+ if (host())
+ host()->SetBufferedBytes(buffered_bytes_);
+}
+
+void BufferedDataSource::UpdateHostState() {
+ media::FilterHost* filter_host = host();
+ if (!filter_host)
+ return;
+
+ filter_host->SetLoaded(loaded_);
+
+ if (streaming_) {
+ filter_host->SetStreaming(true);
+ } else {
+ filter_host->SetTotalBytes(total_bytes_);
+ filter_host->SetBufferedBytes(buffered_bytes_);
}
- host()->SetBufferedBytes(buffered_position + 1);
}
} // namespace webkit_glue
diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h
index 895b259..933ffb9 100644
--- a/webkit/glue/media/buffered_data_source.h
+++ b/webkit/glue/media/buffered_data_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,23 +8,29 @@
#include <string>
#include "base/callback.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/synchronization/lock.h"
+#include "media/base/filter_factories.h"
+#include "media/base/filters.h"
#include "webkit/glue/media/buffered_resource_loader.h"
namespace webkit_glue {
class BufferedDataSource : public WebDataSource {
public:
+ // Creates a DataSourceFactory for building BufferedDataSource objects.
+ static media::DataSourceFactory* CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer);
+
BufferedDataSource(MessageLoop* render_loop,
WebKit::WebFrame* frame);
virtual ~BufferedDataSource();
// media::Filter implementation.
- virtual void Initialize(const std::string& url,
- media::FilterCallback* callback);
- virtual bool IsUrlSupported(const std::string& url);
+ virtual void set_host(media::FilterHost* host);
virtual void Stop(media::FilterCallback* callback);
virtual void SetPlaybackRate(float playback_rate);
@@ -35,12 +41,16 @@ class BufferedDataSource : public WebDataSource {
media::DataSource::ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
virtual bool IsStreaming();
+ virtual void SetPreload(media::Preload preload);
const media::MediaFormat& media_format() {
return media_format_;
}
// webkit_glue::WebDataSource implementation.
+ virtual void Initialize(const std::string& url,
+ media::PipelineStatusCallback* callback);
+ virtual void CancelInitialize();
virtual bool HasSingleOrigin();
virtual void Abort();
@@ -82,6 +92,13 @@ class BufferedDataSource : public WebDataSource {
// and signals the buffered resource loader accordingly.
void SetPlaybackRateTask(float playback_rate);
+ // This task saves the preload value for the media.
+ void SetPreloadTask(media::Preload preload);
+
+ // Decides which DeferStrategy to used based on the current state of the
+ // BufferedDataSource.
+ BufferedResourceLoader::DeferStrategy ChooseDeferStrategy();
+
// The method that performs actual read. This method can only be executed on
// the render thread.
void ReadInternal();
@@ -90,7 +107,7 @@ class BufferedDataSource : public WebDataSource {
void DoneRead_Locked(int error);
// Calls |initialize_callback_| and reset it.
- void DoneInitialization_Locked();
+ void DoneInitialization_Locked(media::PipelineStatus status);
// Callback method for |loader_| if URL for the resource requested is using
// HTTP protocol. This method is called when response for initial request is
@@ -116,6 +133,8 @@ class BufferedDataSource : public WebDataSource {
// Callback method when a network event is received.
void NetworkEventCallback();
+ void UpdateHostState();
+
media::MediaFormat media_format_;
// URL of the resource requested.
@@ -126,6 +145,7 @@ class BufferedDataSource : public WebDataSource {
// member is guaranteed to happen after it is first written, so we don't
// need to protect it.
int64 total_bytes_;
+ int64 buffered_bytes_;
// True if this data source is considered loaded.
bool loaded_;
@@ -144,7 +164,7 @@ class BufferedDataSource : public WebDataSource {
bool network_activity_;
// Callback method from the pipeline for initialization.
- scoped_ptr<media::FilterCallback> initialize_callback_;
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback_;
// Read parameters received from the Read() method call.
scoped_ptr<media::DataSource::ReadCallback> read_callback_;
@@ -185,6 +205,14 @@ class BufferedDataSource : public WebDataSource {
// are in a playing state.
bool media_is_paused_;
+ // This variable is true when the user has requested the video to play at
+ // least once.
+ bool media_has_played_;
+
+ // This variable holds the value of the preload attribute for the video
+ // element.
+ media::Preload preload_;
+
// 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 41f37d6..528a66f 100644
--- a/webkit/glue/media/buffered_data_source_unittest.cc
+++ b/webkit/glue/media/buffered_data_source_unittest.cc
@@ -76,7 +76,7 @@ class MockBufferedResourceLoader : public BufferedResourceLoader {
net::CompletionCallback* callback));
MOCK_METHOD0(content_length, int64());
MOCK_METHOD0(instance_size, int64());
- MOCK_METHOD0(partial_response, bool());
+ MOCK_METHOD0(range_supported, bool());
MOCK_METHOD0(network_activity, bool());
MOCK_METHOD0(url, const GURL&());
MOCK_METHOD0(GetBufferedFirstBytePosition, int64());
@@ -164,10 +164,14 @@ class BufferedDataSourceTest : public testing::Test {
ON_CALL(*loader_, instance_size())
.WillByDefault(Return(instance_size));
- ON_CALL(*loader_, partial_response())
+
+ // range_supported() return true if we expect to get a partial response.
+ ON_CALL(*loader_, range_supported())
.WillByDefault(Return(partial_response));
+
ON_CALL(*loader_, url())
.WillByDefault(ReturnRef(gurl_));
+ media::PipelineStatus expected_init_status = media::PIPELINE_OK;
if (initialized_ok) {
// Expected loaded or not.
EXPECT_CALL(host_, SetLoaded(loaded));
@@ -183,12 +187,13 @@ class BufferedDataSourceTest : public testing::Test {
EXPECT_CALL(host_, SetStreaming(true));
}
} else {
- EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
+ expected_init_status = media::PIPELINE_ERROR_NETWORK;
EXPECT_CALL(*loader_, Stop());
}
// Actual initialization of the data source.
- data_source_->Initialize(url, media::NewExpectedCallback());
+ data_source_->Initialize(url,
+ media::NewExpectedStatusCallback(expected_init_status));
message_loop_->RunAllPending();
if (initialized_ok) {
@@ -275,7 +280,7 @@ class BufferedDataSourceTest : public testing::Test {
loader_ = NULL;
}
- void ReadDataSourceMiss(int64 position, int size) {
+ void ReadDataSourceMiss(int64 position, int size, int start_error) {
EXPECT_TRUE(loader_);
// 1. Reply with a cache miss for the read.
@@ -296,19 +301,26 @@ class BufferedDataSourceTest : public testing::Test {
// 3. Then the new loader will be started.
EXPECT_CALL(*new_loader, Start(NotNull(), NotNull(), NotNull()))
- .WillOnce(DoAll(Assign(&error_, net::OK),
+ .WillOnce(DoAll(Assign(&error_, start_error),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
- EXPECT_CALL(*new_loader, partial_response())
- .WillRepeatedly(Return(loader_->partial_response()));
- // 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),
- Invoke(this,
- &BufferedDataSourceTest::InvokeReadCallback)));
+ if (start_error == net::OK) {
+ EXPECT_CALL(*new_loader, range_supported())
+ .WillRepeatedly(Return(loader_->range_supported()));
- EXPECT_CALL(*this, ReadCallback(size));
+ // 4a. 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),
+ Invoke(this,
+ &BufferedDataSourceTest::InvokeReadCallback)));
+
+ EXPECT_CALL(*this, ReadCallback(size));
+ } else {
+ // 4b. The read callback is called with an error because Start() on the
+ // new loader returned an error.
+ EXPECT_CALL(*this, ReadCallback(media::DataSource::kReadError));
+ }
data_source_->Read(
position, size, buffer_,
@@ -316,7 +328,8 @@ class BufferedDataSourceTest : public testing::Test {
message_loop_->RunAllPending();
// Make sure data is correct.
- EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size));
+ if (start_error == net::OK)
+ EXPECT_EQ(0, memcmp(buffer_, data_ + static_cast<int>(position), size));
loader_ = new_loader;
}
@@ -365,8 +378,8 @@ class BufferedDataSourceTest : public testing::Test {
.WillOnce(DoAll(Assign(&error_, net::OK),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
- EXPECT_CALL(*new_loader, partial_response())
- .WillRepeatedly(Return(loader_->partial_response()));
+ EXPECT_CALL(*new_loader, range_supported())
+ .WillRepeatedly(Return(loader_->range_supported()));
// 4. Then again a read request is made to the new loader.
EXPECT_CALL(*new_loader, Read(position, size, NotNull(), NotNull()))
@@ -457,8 +470,17 @@ TEST_F(BufferedDataSourceTest, ReadCacheHit) {
TEST_F(BufferedDataSourceTest, ReadCacheMiss) {
InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
- ReadDataSourceMiss(1000, 10);
- ReadDataSourceMiss(20, 10);
+ ReadDataSourceMiss(1000, 10, net::OK);
+ ReadDataSourceMiss(20, 10, net::OK);
+ StopDataSource();
+}
+
+// Test the case where the initial response from the server indicates that
+// Range requests are supported, but a later request prove otherwise.
+TEST_F(BufferedDataSourceTest, ServerLiesAboutRangeSupport) {
+ InitializeDataSource(kHttpUrl, net::OK, true, 1024, LOADING);
+ ReadDataSourceHit(10, 10, 10);
+ ReadDataSourceMiss(1000, 10, net::ERR_INVALID_RESPONSE);
StopDataSource();
}
diff --git a/webkit/glue/media/buffered_resource_loader.cc b/webkit/glue/media/buffered_resource_loader.cc
index 4a3ae8b..8d7f1dd 100644
--- a/webkit/glue/media/buffered_resource_loader.cc
+++ b/webkit/glue/media/buffered_resource_loader.cc
@@ -50,10 +50,10 @@ BufferedResourceLoader::BufferedResourceLoader(
int64 last_byte_position)
: buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)),
deferred_(false),
- defer_allowed_(true),
+ defer_strategy_(kReadThenDefer),
completed_(false),
range_requested_(false),
- partial_response_(false),
+ range_supported_(false),
url_(url),
first_byte_position_(first_byte_position),
last_byte_position_(last_byte_position),
@@ -90,7 +90,6 @@ void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
event_callback_.reset(event_callback);
if (first_byte_position_ != kPositionNotSpecified) {
- range_requested_ = true;
// TODO(hclam): server may not support range request so |offset_| may not
// equal to |first_byte_position_|.
offset_ = first_byte_position_;
@@ -103,10 +102,14 @@ void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
// Prepare the request.
WebURLRequest request(url_);
request.setTargetType(WebURLRequest::TargetIsMedia);
- request.setHTTPHeaderField(WebString::fromUTF8("Range"),
- WebString::fromUTF8(GenerateHeaders(
- first_byte_position_,
- last_byte_position_)));
+
+ if (IsRangeRequest()) {
+ range_requested_ = true;
+ request.setHTTPHeaderField(WebString::fromUTF8("Range"),
+ WebString::fromUTF8(GenerateHeaders(
+ first_byte_position_,
+ last_byte_position_)));
+ }
frame->setReferrerForRequest(request, WebKit::WebURL());
// This flag is for unittests as we don't want to reset |url_loader|
@@ -180,10 +183,15 @@ void BufferedResourceLoader::Read(int64 position,
// If we can serve the request now, do the actual read.
if (CanFulfillRead()) {
ReadInternal();
- DisableDeferIfNeeded();
+ UpdateDeferBehavior();
return;
}
+ // If you're deferred and you can't fulfill the read because you don't have
+ // enough data, you will never fulfill the read.
+ // Update defer behavior to re-enable deferring if need be.
+ UpdateDeferBehavior();
+
// If we expected the read request to be fulfilled later, returns
// immediately and let more data to flow in.
if (WillFulfillRead())
@@ -199,11 +207,6 @@ int64 BufferedResourceLoader::GetBufferedPosition() {
return kPositionNotSpecified;
}
-void BufferedResourceLoader::SetAllowDefer(bool is_allowed) {
- defer_allowed_ = is_allowed;
- DisableDeferIfNeeded();
-}
-
int64 BufferedResourceLoader::content_length() {
return content_length_;
}
@@ -212,8 +215,8 @@ int64 BufferedResourceLoader::instance_size() {
return instance_size_;
}
-bool BufferedResourceLoader::partial_response() {
- return partial_response_;
+bool BufferedResourceLoader::range_supported() {
+ return range_supported_;
}
bool BufferedResourceLoader::network_activity() {
@@ -244,7 +247,7 @@ void BufferedResourceLoader::willSendRequest(
return;
}
- // Only allow |single_origin_| if we haven't seen a different origin yet.
+ // Only allow |single_origin_| if we haven't seen a different origin yet.
if (single_origin_)
single_origin_ = url_.GetOrigin() == GURL(newRequest.url()).GetOrigin();
@@ -274,6 +277,8 @@ void BufferedResourceLoader::didReceiveResponse(
if (!start_callback_.get())
return;
+ bool partial_response = false;
+
// We make a strong assumption that when we reach here we have either
// received a response from HTTP/HTTPS protocol or the request was
// successful (in particular range request). So we only verify the partial
@@ -281,13 +286,20 @@ void BufferedResourceLoader::didReceiveResponse(
if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
int error = net::OK;
- if (response.httpStatusCode() == kHttpPartialContent)
- partial_response_ = true;
+ // Check to see whether the server supports byte ranges.
+ std::string accept_ranges =
+ response.httpHeaderField("Accept-Ranges").utf8();
+ range_supported_ = (accept_ranges.find("bytes") != std::string::npos);
+
+ partial_response = (response.httpStatusCode() == kHttpPartialContent);
- if (range_requested_ && partial_response_) {
+ if (range_requested_) {
// If we have verified the partial response and it is correct, we will
- // return net::OK.
- if (!VerifyPartialResponse(response))
+ // return net::OK. It's also possible for a server to support range
+ // requests without advertising Accept-Ranges: bytes.
+ if (partial_response && VerifyPartialResponse(response))
+ range_supported_ = true;
+ else
error = net::ERR_INVALID_RESPONSE;
} else if (response.httpStatusCode() != kHttpOK) {
// We didn't request a range but server didn't reply with "200 OK".
@@ -302,7 +314,7 @@ void BufferedResourceLoader::didReceiveResponse(
} else {
// For any protocol other than HTTP and HTTPS, assume range request is
// always fulfilled.
- partial_response_ = range_requested_;
+ partial_response = range_requested_;
}
// Expected content length can be |kPositionNotSpecified|, in that case
@@ -311,7 +323,7 @@ void BufferedResourceLoader::didReceiveResponse(
// If we have not requested a range, then the size of the instance is equal
// to the content length.
- if (!partial_response_)
+ if (!partial_response)
instance_size_ = content_length_;
// Calls with a successful response.
@@ -321,7 +333,8 @@ void BufferedResourceLoader::didReceiveResponse(
void BufferedResourceLoader::didReceiveData(
WebURLLoader* loader,
const char* data,
- int data_length) {
+ int data_length,
+ int encoded_data_length) {
DCHECK(!completed_);
DCHECK_GT(data_length, 0);
@@ -334,21 +347,19 @@ void BufferedResourceLoader::didReceiveData(
buffer_->Append(reinterpret_cast<const uint8*>(data), data_length);
// If there is an active read request, try to fulfill the request.
- if (HasPendingRead() && CanFulfillRead()) {
+ 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.
- EnableDeferIfNeeded();
+ UpdateDeferBehavior();
+
+ // Consume excess bytes from our in-memory buffer if necessary.
+ 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;
+ }
// Notify that we have received some data.
NotifyNetworkEvent();
@@ -373,6 +384,11 @@ void BufferedResourceLoader::didFinishLoading(
DCHECK(!completed_);
completed_ = true;
+ // If we didn't know the |instance_size_| we do now.
+ if (instance_size_ == kPositionNotSpecified) {
+ instance_size_ = offset_ + buffer_->forward_bytes();
+ }
+
// If there is a start callback, calls it.
if (start_callback_.get()) {
DoneStart(net::OK);
@@ -431,32 +447,83 @@ bool BufferedResourceLoader::HasSingleOrigin() const {
/////////////////////////////////////////////////////////////////////////////
// Helper methods.
-void BufferedResourceLoader::EnableDeferIfNeeded() {
- if (!defer_allowed_)
+void BufferedResourceLoader::UpdateDeferBehavior() {
+ if (!url_loader_.get() || !buffer_.get())
return;
- if (!deferred_ &&
- buffer_->forward_bytes() >= buffer_->forward_capacity()) {
- deferred_ = true;
+ if ((deferred_ && ShouldDisableDefer()) ||
+ (!deferred_ && ShouldEnableDefer())) {
+ bool eventOccurred = ToggleDeferring();
+ if (eventOccurred)
+ NotifyNetworkEvent();
+ }
+}
- if (url_loader_.get())
- url_loader_->setDefersLoading(true);
+void BufferedResourceLoader::UpdateDeferStrategy(DeferStrategy strategy) {
+ defer_strategy_ = strategy;
+ UpdateDeferBehavior();
+}
- NotifyNetworkEvent();
+bool BufferedResourceLoader::ShouldEnableDefer() {
+ // If we're already deferring, then enabling makes no sense.
+ if (deferred_)
+ return false;
+
+ switch(defer_strategy_) {
+ // Never defer at all, so never enable defer.
+ case kNeverDefer:
+ return false;
+
+ // Defer if nothing is being requested.
+ case kReadThenDefer:
+ return !read_callback_.get();
+
+ // Defer if we've reached the max capacity of the threshold.
+ case kThresholdDefer:
+ return buffer_->forward_bytes() >= buffer_->forward_capacity();
}
+ // Otherwise don't enable defer.
+ return false;
}
-void BufferedResourceLoader::DisableDeferIfNeeded() {
- if (deferred_ &&
- (!defer_allowed_ ||
- buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) {
- deferred_ = false;
+bool BufferedResourceLoader::ShouldDisableDefer() {
+ // If we're not deferring, then disabling makes no sense.
+ if (!deferred_)
+ return false;
- if (url_loader_.get())
- url_loader_->setDefersLoading(false);
+ switch(defer_strategy_) {
+ // Always disable deferring.
+ case kNeverDefer:
+ return true;
+
+ // We have an outstanding read request, and we have not buffered enough
+ // yet to fulfill the request; disable defer to get more data.
+ case kReadThenDefer: {
+ size_t amount_buffered = buffer_->forward_bytes();
+ size_t amount_to_read = static_cast<size_t>(read_size_);
+ return read_callback_.get() && amount_buffered < amount_to_read;
+ }
- NotifyNetworkEvent();
+ // We have less than half the capacity of our threshold, so
+ // disable defer to get more data.
+ case kThresholdDefer: {
+ size_t amount_buffered = buffer_->forward_bytes();
+ size_t half_capacity = buffer_->forward_capacity() / 2;
+ return amount_buffered < half_capacity;
+ }
}
+
+ // Otherwise keep deferring.
+ return false;
+}
+
+bool BufferedResourceLoader::ToggleDeferring() {
+ deferred_ = !deferred_;
+ if (url_loader_.get()) {
+ url_loader_->setDefersLoading(deferred_);
+ return true;
+ }
+ return false;
}
bool BufferedResourceLoader::CanFulfillRead() {
@@ -579,4 +646,8 @@ void BufferedResourceLoader::NotifyNetworkEvent() {
event_callback_->Run();
}
+bool BufferedResourceLoader::IsRangeRequest() const {
+ return first_byte_position_ != kPositionNotSpecified;
+}
+
} // namespace webkit_glue
diff --git a/webkit/glue/media/buffered_resource_loader.h b/webkit/glue/media/buffered_resource_loader.h
index fe01fb4..39e89d8 100644
--- a/webkit/glue/media/buffered_resource_loader.h
+++ b/webkit/glue/media/buffered_resource_loader.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -8,7 +8,7 @@
#include <string>
#include "base/callback.h"
-#include "base/scoped_ptr.h"
+#include "base/memory/scoped_ptr.h"
#include "base/timer.h"
#include "googleurl/src/gurl.h"
#include "media/base/seekable_buffer.h"
@@ -36,6 +36,15 @@ class BufferedResourceLoader :
public base::RefCountedThreadSafe<BufferedResourceLoader>,
public WebKit::WebURLLoaderClient {
public:
+ // kNeverDefer - Aggresively buffer; never defer loading while paused.
+ // kReadThenDefer - Request only enough data to fulfill read requests.
+ // kThresholdDefer - Try to keep amount of buffered data at a threshold.
+ enum DeferStrategy {
+ kNeverDefer,
+ kReadThenDefer,
+ kThresholdDefer,
+ };
+
typedef Callback0::Type NetworkEventCallback;
// |url| - URL for the resource to be loaded.
@@ -85,9 +94,6 @@ class BufferedResourceLoader :
// |kPositionNotSpecified| if such value is not available.
virtual int64 GetBufferedPosition();
- // 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 |kPositionNotSpecified|, then content length is
// unknown.
@@ -97,9 +103,8 @@ class BufferedResourceLoader :
// |kPositionNotSpecified|, then the size is unknown.
virtual int64 instance_size();
- // Returns true if the response for this loader is a partial response.
- // It means a 206 response in HTTP/HTTPS protocol.
- virtual bool partial_response();
+ // Returns true if the server supports byte range requests.
+ virtual bool range_supported();
// Returns true if network is currently active.
virtual bool network_activity();
@@ -124,11 +129,12 @@ class BufferedResourceLoader :
const WebKit::WebURLResponse& response);
virtual void didDownloadData(
WebKit::WebURLLoader* loader,
- int dataLength);
+ int data_length);
virtual void didReceiveData(
WebKit::WebURLLoader* loader,
const char* data,
- int dataLength);
+ int data_length,
+ int encoded_data_length);
virtual void didReceiveCachedMetadata(
WebKit::WebURLLoader* loader,
const char* data, int dataLength);
@@ -141,19 +147,30 @@ class BufferedResourceLoader :
bool HasSingleOrigin() const;
+ // Sets the defer strategy to the given value.
+ void UpdateDeferStrategy(DeferStrategy strategy);
+
protected:
friend class base::RefCountedThreadSafe<BufferedResourceLoader>;
-
virtual ~BufferedResourceLoader();
private:
friend class BufferedResourceLoaderTest;
- // Defer the resource loading if the buffer is full.
- void EnableDeferIfNeeded();
+ // Toggles whether the resource loading is deferred or not.
+ // Returns true if a network event was fired.
+ bool ToggleDeferring();
- // Disable defer loading if we are under-buffered.
- void DisableDeferIfNeeded();
+ // Returns true if we should defer resource loading, based
+ // on current buffering scheme.
+ bool ShouldEnableDefer();
+
+ // Returns true if we should enable resource loading, based
+ // on current buffering scheme.
+ bool ShouldDisableDefer();
+
+ // Updates deferring behavior based on current buffering scheme.
+ void UpdateDeferBehavior();
// Returns true if the current read request can be fulfilled by what is in
// the buffer.
@@ -190,14 +207,17 @@ class BufferedResourceLoader :
bool HasPendingRead() { return read_callback_.get() != NULL; }
+ // Helper function that returns true if a range request was specified.
+ bool IsRangeRequest() const;
+
// A sliding window of buffer.
scoped_ptr<media::SeekableBuffer> buffer_;
// True if resource loading was deferred.
bool deferred_;
- // True if resource loader is allowed to defer, false otherwise.
- bool defer_allowed_;
+ // Current buffering algorithm in place for resource loading.
+ DeferStrategy defer_strategy_;
// True if resource loading has completed.
bool completed_;
@@ -205,8 +225,8 @@ class BufferedResourceLoader :
// True if a range request was made.
bool range_requested_;
- // True if response data received is a partial range.
- bool partial_response_;
+ // True if Range header is supported.
+ bool range_supported_;
// Does the work of loading and sends data back to this client.
scoped_ptr<WebKit::WebURLLoader> url_loader_;
diff --git a/webkit/glue/media/buffered_resource_loader_unittest.cc b/webkit/glue/media/buffered_resource_loader_unittest.cc
index aef44ca..03fd360 100644
--- a/webkit/glue/media/buffered_resource_loader_unittest.cc
+++ b/webkit/glue/media/buffered_resource_loader_unittest.cc
@@ -103,7 +103,15 @@ class BufferedResourceLoaderTest : public testing::Test {
}
void FullResponse(int64 instance_size) {
- EXPECT_CALL(*this, StartCallback(net::OK));
+ FullResponse(instance_size, net::OK);
+ }
+
+ void FullResponse(int64 instance_size, int status) {
+ EXPECT_CALL(*this, StartCallback(status));
+ if (status != net::OK) {
+ EXPECT_CALL(*url_loader_, cancel())
+ .WillOnce(RequestCanceled(loader_));
+ }
WebURLResponse response(gurl_);
response.setHTTPHeaderField(WebString::fromUTF8("Content-Length"),
@@ -112,15 +120,23 @@ class BufferedResourceLoaderTest : public testing::Test {
response.setExpectedContentLength(instance_size);
response.setHTTPStatusCode(kHttpOK);
loader_->didReceiveResponse(url_loader_, response);
- EXPECT_EQ(instance_size, loader_->content_length());
- EXPECT_EQ(instance_size, loader_->instance_size());
- EXPECT_FALSE(loader_->partial_response());
+
+ if (status == net::OK) {
+ EXPECT_EQ(instance_size, loader_->content_length());
+ EXPECT_EQ(instance_size, loader_->instance_size());
+ }
+
+ EXPECT_FALSE(loader_->range_supported());
}
void PartialResponse(int64 first_position, int64 last_position,
int64 instance_size) {
+ PartialResponse(first_position, last_position, instance_size, false, true);
+ }
+
+ void PartialResponse(int64 first_position, int64 last_position,
+ int64 instance_size, bool chunked, bool accept_ranges) {
EXPECT_CALL(*this, StartCallback(net::OK));
- int64 content_length = last_position - first_position + 1;
WebURLResponse response(gurl_);
response.setHTTPHeaderField(WebString::fromUTF8("Content-Range"),
@@ -129,12 +145,36 @@ class BufferedResourceLoaderTest : public testing::Test {
first_position,
last_position,
instance_size)));
+
+ // HTTP 1.1 doesn't permit Content-Length with Transfer-Encoding: chunked.
+ int64 content_length = -1;
+ if (chunked) {
+ response.setHTTPHeaderField(WebString::fromUTF8("Transfer-Encoding"),
+ WebString::fromUTF8("chunked"));
+ } else {
+ content_length = last_position - first_position + 1;
+ }
response.setExpectedContentLength(content_length);
+
+ // A server isn't required to return Accept-Ranges even though it might.
+ if (accept_ranges) {
+ response.setHTTPHeaderField(WebString::fromUTF8("Accept-Ranges"),
+ WebString::fromUTF8("bytes"));
+ }
+
response.setHTTPStatusCode(kHttpPartialContent);
loader_->didReceiveResponse(url_loader_, response);
+
+ // XXX: what's the difference between these two? For example in the chunked
+ // range request case, Content-Length is unspecified (because it's chunked)
+ // but Content-Range: a-b/c can be returned, where c == Content-Length
+ //
+ // Can we eliminate one?
EXPECT_EQ(content_length, loader_->content_length());
EXPECT_EQ(instance_size, loader_->instance_size());
- EXPECT_TRUE(loader_->partial_response());
+
+ // A valid partial response should always result in this being true.
+ EXPECT_TRUE(loader_->range_supported());
}
void Redirect(const char* url) {
@@ -160,7 +200,9 @@ class BufferedResourceLoaderTest : public testing::Test {
EXPECT_CALL(*this, NetworkCallback())
.RetiresOnSaturation();
loader_->didReceiveData(url_loader_,
- reinterpret_cast<char*>(data_ + position), size);
+ reinterpret_cast<char*>(data_ + position),
+ size,
+ size);
}
// Helper method to read from |loader_|.
@@ -174,18 +216,8 @@ 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(*url_loader_, setDefersLoading(false));
- EXPECT_CALL(*this, NetworkCallback());
- }
- loader_->SetAllowDefer(false);
- }
-
- // Helper method to allow deferring in |loader_|.
- void AllowLoaderDefer() {
- loader_->SetAllowDefer(true);
+ void ConfirmLoaderDeferredState(bool expectedVal) {
+ EXPECT_EQ(loader_->deferred_, expectedVal);
}
MOCK_METHOD1(StartCallback, void(int error));
@@ -232,8 +264,7 @@ TEST_F(BufferedResourceLoaderTest, BadHttpResponse) {
TEST_F(BufferedResourceLoaderTest, NotPartialResponse) {
Initialize(kHttpUrl, 100, -1);
Start();
- FullResponse(1024);
- StopWhenLoad();
+ FullResponse(1024, net::ERR_INVALID_RESPONSE);
}
// Tests that a 200 response is received.
@@ -252,6 +283,27 @@ TEST_F(BufferedResourceLoaderTest, PartialResponse) {
StopWhenLoad();
}
+TEST_F(BufferedResourceLoaderTest, PartialResponse_Chunked) {
+ Initialize(kHttpUrl, 100, 200);
+ Start();
+ PartialResponse(100, 200, 1024, true, true);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, PartialResponse_NoAcceptRanges) {
+ Initialize(kHttpUrl, 100, 200);
+ Start();
+ PartialResponse(100, 200, 1024, false, false);
+ StopWhenLoad();
+}
+
+TEST_F(BufferedResourceLoaderTest, PartialResponse_ChunkedNoAcceptRanges) {
+ Initialize(kHttpUrl, 100, 200);
+ Start();
+ PartialResponse(100, 200, 1024, true, false);
+ StopWhenLoad();
+}
+
// Tests that an invalid partial response is received.
TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
Initialize(kHttpUrl, 0, 10);
@@ -273,6 +325,7 @@ TEST_F(BufferedResourceLoaderTest, InvalidPartialResponse) {
// Tests the logic of sliding window for data buffering and reading.
TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
Initialize(kHttpUrl, 10, 29);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
Start();
PartialResponse(10, 29, 30);
@@ -323,6 +376,7 @@ TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
Initialize(kHttpUrl, 10, 0x00FFFFFF);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
Start();
PartialResponse(10, 0x00FFFFFF, 0x01000000);
@@ -366,128 +420,86 @@ TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
loader_->didFail(url_loader_, error);
}
-// 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) {
+// Tests the data buffering logic of NeverDefer strategy.
+TEST_F(BufferedResourceLoaderTest, NeverDeferStrategy) {
Initialize(kHttpUrl, 10, 99);
SetLoaderBuffer(10, 20);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer);
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();
+ // Read past the buffer size; should not defer regardless.
WriteLoader(10, 10);
WriteLoader(20, 50);
- AllowLoaderDefer();
+ ConfirmLoaderDeferredState(false);
+ // Should move past window.
EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
ReadLoader(10, 10, buffer);
+
StopWhenLoad();
}
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredNoDataReceived) {
+// Tests the data buffering logic of ReadThenDefer strategy.
+TEST_F(BufferedResourceLoaderTest, ReadThenDeferStrategy) {
Initialize(kHttpUrl, 10, 99);
SetLoaderBuffer(10, 20);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kReadThenDefer);
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(*url_loader_, setDefersLoading(true));
+ // Make an outstanding read request.
+ // We should disable deferring after the read request, so expect
+ // a network event.
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);
+ ReadLoader(10, 10, buffer);
- uint8 buffer[10];
+ // Receive almost enough data to cover, shouldn't defer.
+ WriteLoader(10, 9);
+ ConfirmLoaderDeferredState(false);
- // 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(*url_loader_, setDefersLoading(true));
+ // As soon as we have received enough data to fulfill the read, defer.
EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 30);
+ EXPECT_CALL(*this, ReadCallback(10));
+ WriteLoader(19, 1);
- DisallowLoaderDefer();
- WriteLoader(40, 5);
- AllowLoaderDefer();
+ ConfirmLoaderDeferredState(true);
+ VerifyBuffer(buffer, 10, 10);
- EXPECT_CALL(*this, ReadCallback(10));
- ReadLoader(20, 10, buffer);
- VerifyBuffer(buffer, 20, 10);
StopWhenLoad();
}
-TEST_F(BufferedResourceLoaderTest, AllowDefer_DeferredReadPastWindow) {
+// Tests the data buffering logic of ThresholdDefer strategy.
+TEST_F(BufferedResourceLoaderTest, ThresholdDeferStrategy) {
Initialize(kHttpUrl, 10, 99);
SetLoaderBuffer(10, 20);
+ loader_->UpdateDeferStrategy(BufferedResourceLoader::kThresholdDefer);
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(*url_loader_, setDefersLoading(true));
+ WriteLoader(10, 5);
+ // Haven't reached threshold, don't defer.
+ ConfirmLoaderDeferredState(false);
+
+ // We're at the threshold now, let's defer.
EXPECT_CALL(*this, NetworkCallback());
- WriteLoader(10, 40);
+ WriteLoader(15, 5);
+ ConfirmLoaderDeferredState(true);
- DisallowLoaderDefer();
- WriteLoader(50, 20);
- WriteLoader(70, 40);
- AllowLoaderDefer();
+ // Now we've read over half of the buffer, disable deferring.
+ EXPECT_CALL(*this, ReadCallback(6));
+ EXPECT_CALL(*this, NetworkCallback());
+ ReadLoader(10, 6, buffer);
+
+ ConfirmLoaderDeferredState(false);
+ VerifyBuffer(buffer, 10, 6);
- EXPECT_CALL(*this, ReadCallback(net::ERR_CACHE_MISS));
- ReadLoader(20, 5, buffer);
StopWhenLoad();
}
diff --git a/webkit/glue/media/simple_data_source.cc b/webkit/glue/media/simple_data_source.cc
index 5c3eb18..6647e52 100644
--- a/webkit/glue/media/simple_data_source.cc
+++ b/webkit/glue/media/simple_data_source.cc
@@ -12,12 +12,27 @@
#include "net/url_request/url_request_status.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKit.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebKitClient.h"
+#include "webkit/glue/media/web_data_source_factory.h"
#include "webkit/glue/webkit_glue.h"
namespace webkit_glue {
static const char kDataScheme[] = "data";
+static WebDataSource* NewSimpleDataSource(MessageLoop* render_loop,
+ WebKit::WebFrame* frame) {
+ return new SimpleDataSource(render_loop, frame);
+}
+
+// static
+media::DataSourceFactory* SimpleDataSource::CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer) {
+ return new WebDataSourceFactory(render_loop, frame, &NewSimpleDataSource,
+ build_observer);
+}
+
SimpleDataSource::SimpleDataSource(
MessageLoop* render_loop,
WebKit::WebFrame* frame)
@@ -35,6 +50,15 @@ SimpleDataSource::~SimpleDataSource() {
DCHECK(state_ == UNINITIALIZED || state_ == STOPPED);
}
+void SimpleDataSource::set_host(media::FilterHost* host) {
+ DataSource::set_host(host);
+
+ base::AutoLock auto_lock(lock_);
+ if (state_ == INITIALIZED) {
+ UpdateHostState();
+ }
+}
+
void SimpleDataSource::Stop(media::FilterCallback* callback) {
base::AutoLock auto_lock(lock_);
state_ = STOPPED;
@@ -48,26 +72,42 @@ void SimpleDataSource::Stop(media::FilterCallback* callback) {
NewRunnableMethod(this, &SimpleDataSource::CancelTask));
}
-void SimpleDataSource::Initialize(const std::string& url,
- media::FilterCallback* callback) {
- base::AutoLock auto_lock(lock_);
- DCHECK_EQ(state_, UNINITIALIZED);
- DCHECK(callback);
- state_ = INITIALIZING;
- initialize_callback_.reset(callback);
-
- // Validate the URL.
- SetURL(GURL(url));
- if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
- initialize_callback_->Run();
- initialize_callback_.reset();
- return;
+void SimpleDataSource::Initialize(
+ const std::string& url,
+ media::PipelineStatusCallback* callback) {
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+ DCHECK_EQ(state_, UNINITIALIZED);
+ DCHECK(callback);
+ state_ = INITIALIZING;
+ initialize_callback_.reset(callback);
+
+ // Validate the URL.
+ SetURL(GURL(url));
+ if (!url_.is_valid() || !IsProtocolSupportedForMedia(url_)) {
+ DoneInitialization_Locked(false);
+ return;
+ }
+
+ // Post a task to the render thread to start loading the resource.
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &SimpleDataSource::StartTask));
}
+}
- // Post a task to the render thread to start loading the resource.
+void SimpleDataSource::CancelInitialize() {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(initialize_callback_.get());
+ state_ = STOPPED;
+ initialize_callback_.reset();
+
+ // Post a task to the render thread to cancel loading the resource.
render_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this, &SimpleDataSource::StartTask));
+ NewRunnableMethod(this, &SimpleDataSource::CancelTask));
}
const media::MediaFormat& SimpleDataSource::media_format() {
@@ -99,6 +139,8 @@ bool SimpleDataSource::IsStreaming() {
return false;
}
+void SimpleDataSource::SetPreload(media::Preload preload) {}
+
void SimpleDataSource::SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader) {
url_loader_.reset(mock_loader);
keep_test_loader_ = true;
@@ -141,7 +183,8 @@ void SimpleDataSource::didDownloadData(
void SimpleDataSource::didReceiveData(
WebKit::WebURLLoader* loader,
const char* data,
- int data_length) {
+ int data_length,
+ int encoded_data_length) {
DCHECK(MessageLoop::current() == render_loop_);
data_.append(data, data_length);
}
@@ -157,44 +200,56 @@ void SimpleDataSource::didFinishLoading(
WebKit::WebURLLoader* loader,
double finishTime) {
DCHECK(MessageLoop::current() == render_loop_);
- base::AutoLock auto_lock(lock_);
- // It's possible this gets called after Stop(), in which case |host_| is no
- // longer valid.
- if (state_ == STOPPED)
- return;
-
- // Otherwise we should be initializing and have created a WebURLLoader.
- DCHECK_EQ(state_, INITIALIZING);
-
- // If we don't get a content length or the request has failed, report it
- // as a network error.
- if (size_ == -1)
- size_ = data_.length();
- DCHECK(static_cast<size_t>(size_) == data_.length());
-
- DoneInitialization_Locked(true);
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+ // It's possible this gets called after Stop(), in which case |host_| is no
+ // longer valid.
+ if (state_ == STOPPED)
+ return;
+
+ // Otherwise we should be initializing and have created a WebURLLoader.
+ DCHECK_EQ(state_, INITIALIZING);
+
+ // If we don't get a content length or the request has failed, report it
+ // as a network error.
+ if (size_ == -1)
+ size_ = data_.length();
+ DCHECK(static_cast<size_t>(size_) == data_.length());
+
+ DoneInitialization_Locked(true);
+ }
}
void SimpleDataSource::didFail(
WebKit::WebURLLoader* loader,
const WebKit::WebURLError& error) {
DCHECK(MessageLoop::current() == render_loop_);
- base::AutoLock auto_lock(lock_);
- // It's possible this gets called after Stop(), in which case |host_| is no
- // longer valid.
- if (state_ == STOPPED)
- return;
-
- // Otherwise we should be initializing and have created a WebURLLoader.
- DCHECK_EQ(state_, INITIALIZING);
-
- // If we don't get a content length or the request has failed, report it
- // as a network error.
- if (size_ == -1)
- size_ = data_.length();
- DCHECK(static_cast<size_t>(size_) == data_.length());
-
- DoneInitialization_Locked(false);
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+ // It's possible this gets called after Stop(), in which case |host_| is no
+ // longer valid.
+ if (state_ == STOPPED)
+ return;
+
+ // Otherwise we should be initializing and have created a WebURLLoader.
+ DCHECK_EQ(state_, INITIALIZING);
+
+ // If we don't get a content length or the request has failed, report it
+ // as a network error.
+ if (size_ == -1)
+ size_ = data_.length();
+ DCHECK(static_cast<size_t>(size_) == data_.length());
+
+ DoneInitialization_Locked(false);
+ }
}
bool SimpleDataSource::HasSingleOrigin() {
@@ -210,44 +265,48 @@ void SimpleDataSource::Abort() {
void SimpleDataSource::SetURL(const GURL& url) {
url_ = url;
media_format_.Clear();
- media_format_.SetAsString(media::MediaFormat::kMimeType,
- media::mime_type::kApplicationOctetStream);
media_format_.SetAsString(media::MediaFormat::kURL, url.spec());
}
void SimpleDataSource::StartTask() {
DCHECK(MessageLoop::current() == render_loop_);
- base::AutoLock auto_lock(lock_);
-
- // We may have stopped.
- if (state_ == STOPPED)
- return;
-
- CHECK(frame_);
-
- DCHECK_EQ(state_, INITIALIZING);
-
- if (url_.SchemeIs(kDataScheme)) {
- // If this using data protocol, we just need to decode it.
- std::string mime_type, charset;
- bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
-
- // Don't care about the mime-type just proceed if decoding was successful.
- size_ = data_.length();
- DoneInitialization_Locked(success);
- } else {
- // Prepare the request.
- WebKit::WebURLRequest request(url_);
- request.setTargetType(WebKit::WebURLRequest::TargetIsMedia);
-
- frame_->setReferrerForRequest(request, WebKit::WebURL());
-
- // This flag is for unittests as we don't want to reset |url_loader|
- if (!keep_test_loader_)
- url_loader_.reset(frame_->createAssociatedURLLoader());
-
- // Start the resource loading.
- url_loader_->loadAsynchronously(request, this);
+ // Reference to prevent destruction while inside the |initialize_callback_|
+ // call. This is a temporary fix to prevent crashes caused by holding the
+ // lock and running the destructor.
+ scoped_refptr<SimpleDataSource> destruction_guard(this);
+ {
+ base::AutoLock auto_lock(lock_);
+
+ // We may have stopped.
+ if (state_ == STOPPED)
+ return;
+
+ CHECK(frame_);
+
+ DCHECK_EQ(state_, INITIALIZING);
+
+ if (url_.SchemeIs(kDataScheme)) {
+ // If this using data protocol, we just need to decode it.
+ std::string mime_type, charset;
+ bool success = net::DataURL::Parse(url_, &mime_type, &charset, &data_);
+
+ // Don't care about the mime-type just proceed if decoding was successful.
+ size_ = data_.length();
+ DoneInitialization_Locked(success);
+ } else {
+ // Prepare the request.
+ WebKit::WebURLRequest request(url_);
+ request.setTargetType(WebKit::WebURLRequest::TargetIsMedia);
+
+ frame_->setReferrerForRequest(request, WebKit::WebURL());
+
+ // This flag is for unittests as we don't want to reset |url_loader|
+ if (!keep_test_loader_)
+ url_loader_.reset(frame_->createAssociatedURLLoader());
+
+ // Start the resource loading.
+ url_loader_->loadAsynchronously(request, this);
+ }
}
}
@@ -265,17 +324,29 @@ void SimpleDataSource::CancelTask() {
void SimpleDataSource::DoneInitialization_Locked(bool success) {
lock_.AssertAcquired();
+ media::PipelineStatus status = media::PIPELINE_ERROR_NETWORK;
if (success) {
state_ = INITIALIZED;
+
+ UpdateHostState();
+ status = media::PIPELINE_OK;
+ } else {
+ state_ = UNINITIALIZED;
+ url_loader_.reset();
+ }
+
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback(
+ initialize_callback_.release());
+ initialize_callback->Run(status);
+}
+
+void SimpleDataSource::UpdateHostState() {
+ if (host()) {
host()->SetTotalBytes(size_);
host()->SetBufferedBytes(size_);
// If scheme is file or data, say we are loaded.
host()->SetLoaded(url_.SchemeIsFile() || url_.SchemeIs(kDataScheme));
- } else {
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
}
- initialize_callback_->Run();
- initialize_callback_.reset();
}
} // namespace webkit_glue
diff --git a/webkit/glue/media/simple_data_source.h b/webkit/glue/media/simple_data_source.h
index 10d93ff..2c4075a 100644
--- a/webkit/glue/media/simple_data_source.h
+++ b/webkit/glue/media/simple_data_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -13,8 +13,9 @@
#include <algorithm>
#include <string>
+#include "base/memory/scoped_ptr.h"
#include "base/message_loop.h"
-#include "base/scoped_ptr.h"
+#include "media/base/filter_factories.h"
#include "media/base/filters.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebFrame.h"
#include "third_party/WebKit/Source/WebKit/chromium/public/WebURLLoader.h"
@@ -31,20 +32,26 @@ namespace webkit_glue {
class SimpleDataSource : public WebDataSource,
public WebKit::WebURLLoaderClient {
public:
+ // Creates a DataSourceFactory for building SimpleDataSource objects.
+ static media::DataSourceFactory* CreateFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ WebDataSourceBuildObserverHack* build_observer);
+
SimpleDataSource(MessageLoop* render_loop, WebKit::WebFrame* frame);
virtual ~SimpleDataSource();
// media::Filter implementation.
+ virtual void set_host(media::FilterHost* host);
virtual void Stop(media::FilterCallback* callback);
// media::DataSource implementation.
- virtual void Initialize(const std::string& url,
- media::FilterCallback* callback);
virtual const media::MediaFormat& media_format();
virtual void Read(int64 position, size_t size,
uint8* data, ReadCallback* read_callback);
virtual bool GetSize(int64* size_out);
virtual bool IsStreaming();
+ virtual void SetPreload(media::Preload preload);
// Used to inject a mock used for unittests.
virtual void SetURLLoaderForTest(WebKit::WebURLLoader* mock_loader);
@@ -67,7 +74,8 @@ class SimpleDataSource : public WebDataSource,
virtual void didReceiveData(
WebKit::WebURLLoader* loader,
const char* data,
- int dataLength);
+ int dataLength,
+ int encodedDataLength);
virtual void didReceiveCachedMetadata(
WebKit::WebURLLoader* loader,
const char* data, int dataLength);
@@ -79,6 +87,9 @@ class SimpleDataSource : public WebDataSource,
const WebKit::WebURLError&);
// webkit_glue::WebDataSource implementation.
+ virtual void Initialize(const std::string& url,
+ media::PipelineStatusCallback* callback);
+ virtual void CancelInitialize();
virtual bool HasSingleOrigin();
virtual void Abort();
@@ -95,6 +106,9 @@ class SimpleDataSource : public WebDataSource,
// Perform initialization completion tasks under a lock.
void DoneInitialization_Locked(bool success);
+ // Update host() stats like total bytes & buffered bytes.
+ void UpdateHostState();
+
// Primarily used for asserting the bridge is loading on the render thread.
MessageLoop* render_loop_;
@@ -123,7 +137,7 @@ class SimpleDataSource : public WebDataSource,
base::Lock lock_;
// Filter callbacks.
- scoped_ptr<media::FilterCallback> initialize_callback_;
+ scoped_ptr<media::PipelineStatusCallback> initialize_callback_;
// Used to ensure mocks for unittests are used instead of reset in Start().
bool keep_test_loader_;
diff --git a/webkit/glue/media/simple_data_source_unittest.cc b/webkit/glue/media/simple_data_source_unittest.cc
index 977807b..61e41f2 100644
--- a/webkit/glue/media/simple_data_source_unittest.cc
+++ b/webkit/glue/media/simple_data_source_unittest.cc
@@ -59,7 +59,7 @@ class SimpleDataSourceTest : public testing::Test {
}
void InitializeDataSource(const char* url,
- media::MockCallback* callback) {
+ media::MockStatusCallback* callback) {
gurl_ = GURL(url);
frame_.reset(new NiceMock<MockWebFrame>());
@@ -86,7 +86,7 @@ class SimpleDataSourceTest : public testing::Test {
EXPECT_EQ(kDataSize, size);
for (int i = 0; i < kDataSize; ++i) {
- data_source_->didReceiveData(NULL, data_ + i, 1);
+ data_source_->didReceiveData(NULL, data_ + i, 1, 1);
}
EXPECT_CALL(host_, SetLoaded(is_loaded));
@@ -103,7 +103,6 @@ class SimpleDataSourceTest : public testing::Test {
void RequestFailed() {
InSequence s;
- EXPECT_CALL(host_, SetError(media::PIPELINE_ERROR_NETWORK));
WebURLError error;
error.reason = net::ERR_FAILED;
@@ -158,19 +157,22 @@ class SimpleDataSourceTest : public testing::Test {
};
TEST_F(SimpleDataSourceTest, InitializeHTTP) {
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(false);
DestroyDataSource();
}
TEST_F(SimpleDataSourceTest, InitializeHTTPS) {
- InitializeDataSource(kHttpsUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpsUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(false);
DestroyDataSource();
}
TEST_F(SimpleDataSourceTest, InitializeFile) {
- InitializeDataSource(kFileUrl, media::NewExpectedCallback());
+ InitializeDataSource(kFileUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(true);
DestroyDataSource();
}
@@ -181,8 +183,6 @@ TEST_F(SimpleDataSourceTest, InitializeData) {
data_source_ = new SimpleDataSource(MessageLoop::current(),
frame_.get());
- EXPECT_TRUE(data_source_->IsUrlSupported(kDataUrl));
-
// There is no need to provide a message loop to data source.
data_source_->set_host(&host_);
data_source_->SetURLLoaderForTest(url_loader_);
@@ -191,14 +191,16 @@ TEST_F(SimpleDataSourceTest, InitializeData) {
EXPECT_CALL(host_, SetTotalBytes(sizeof(kDataUrlDecoded)));
EXPECT_CALL(host_, SetBufferedBytes(sizeof(kDataUrlDecoded)));
- data_source_->Initialize(kDataUrl, media::NewExpectedCallback());
+ data_source_->Initialize(kDataUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
MessageLoop::current()->RunAllPending();
DestroyDataSource();
}
TEST_F(SimpleDataSourceTest, RequestFailed) {
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_ERROR_NETWORK));
RequestFailed();
DestroyDataSource();
}
@@ -206,8 +208,8 @@ TEST_F(SimpleDataSourceTest, RequestFailed) {
TEST_F(SimpleDataSourceTest, StopWhenDownloading) {
// The callback should be deleted, but not executed.
// TODO(scherkus): should this really be the behaviour? Seems strange...
- StrictMock<media::MockCallback>* callback =
- new StrictMock<media::MockCallback>();
+ StrictMock<media::MockStatusCallback>* callback =
+ new StrictMock<media::MockStatusCallback>();
EXPECT_CALL(*callback, Destructor());
InitializeDataSource(kHttpUrl, callback);
@@ -217,7 +219,8 @@ TEST_F(SimpleDataSourceTest, StopWhenDownloading) {
}
TEST_F(SimpleDataSourceTest, AsyncRead) {
- InitializeDataSource(kFileUrl, media::NewExpectedCallback());
+ InitializeDataSource(kFileUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(true);
AsyncRead();
DestroyDataSource();
@@ -228,20 +231,23 @@ TEST_F(SimpleDataSourceTest, AsyncRead) {
// is fixed.
TEST_F(SimpleDataSourceTest, HasSingleOrigin) {
// Make sure no redirect case works as expected.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
RequestSucceeded(false);
EXPECT_TRUE(data_source_->HasSingleOrigin());
DestroyDataSource();
// Test redirect to the same domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToSameDomainUrl1);
RequestSucceeded(false);
EXPECT_TRUE(data_source_->HasSingleOrigin());
DestroyDataSource();
// Test redirect twice to the same domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToSameDomainUrl1);
Redirect(kHttpRedirectToSameDomainUrl2);
RequestSucceeded(false);
@@ -249,14 +255,16 @@ TEST_F(SimpleDataSourceTest, HasSingleOrigin) {
DestroyDataSource();
// Test redirect to a different domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToDifferentDomainUrl1);
RequestSucceeded(false);
EXPECT_FALSE(data_source_->HasSingleOrigin());
DestroyDataSource();
// Test redirect to the same domain and then to a different domain.
- InitializeDataSource(kHttpUrl, media::NewExpectedCallback());
+ InitializeDataSource(kHttpUrl,
+ media::NewExpectedStatusCallback(media::PIPELINE_OK));
Redirect(kHttpRedirectToSameDomainUrl1);
Redirect(kHttpRedirectToDifferentDomainUrl1);
RequestSucceeded(false);
diff --git a/webkit/glue/media/video_renderer_impl.cc b/webkit/glue/media/video_renderer_impl.cc
index 3bbd626..5e159fe 100644
--- a/webkit/glue/media/video_renderer_impl.cc
+++ b/webkit/glue/media/video_renderer_impl.cc
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -49,7 +49,7 @@ void VideoRendererImpl::SetRect(const gfx::Rect& rect) {
}
// This method is always called on the renderer's thread.
-void VideoRendererImpl::Paint(skia::PlatformCanvas* canvas,
+void VideoRendererImpl::Paint(SkCanvas* canvas,
const gfx::Rect& dest_rect) {
scoped_refptr<media::VideoFrame> video_frame;
GetCurrentFrame(&video_frame);
@@ -98,7 +98,7 @@ void VideoRendererImpl::PutCurrentFrame(
// 4. Canvas is opaque.
// TODO(hclam): The fast paint method should support flipping and mirroring.
// Disable the flipping and mirroring checks once we have it.
-bool VideoRendererImpl::CanFastPaint(skia::PlatformCanvas* canvas,
+bool VideoRendererImpl::CanFastPaint(SkCanvas* canvas,
const gfx::Rect& dest_rect) {
// Fast paint does not handle opacity value other than 1.0. Hence use slow
// paint if opacity is not 1.0. Since alpha = opacity * 0xFF, we check that
@@ -152,7 +152,7 @@ bool VideoRendererImpl::CanFastPaint(skia::PlatformCanvas* canvas,
}
void VideoRendererImpl::SlowPaint(media::VideoFrame* video_frame,
- skia::PlatformCanvas* canvas,
+ SkCanvas* canvas,
const gfx::Rect& dest_rect) {
// 1. Convert YUV frame to RGB.
base::TimeDelta timestamp = video_frame->GetTimestamp();
@@ -199,7 +199,7 @@ void VideoRendererImpl::SlowPaint(media::VideoFrame* video_frame,
}
void VideoRendererImpl::FastPaint(media::VideoFrame* video_frame,
- skia::PlatformCanvas* canvas,
+ SkCanvas* canvas,
const gfx::Rect& dest_rect) {
DCHECK(video_frame->format() == media::VideoFrame::YV12 ||
video_frame->format() == media::VideoFrame::YV16);
diff --git a/webkit/glue/media/video_renderer_impl.h b/webkit/glue/media/video_renderer_impl.h
index b73c099..b14038b 100644
--- a/webkit/glue/media/video_renderer_impl.h
+++ b/webkit/glue/media/video_renderer_impl.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
//
@@ -30,7 +30,7 @@ class VideoRendererImpl : public WebVideoRenderer {
// WebVideoRenderer implementation.
virtual void SetWebMediaPlayerImplProxy(WebMediaPlayerImpl::Proxy* proxy);
virtual void SetRect(const gfx::Rect& rect);
- virtual void Paint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect);
+ virtual void Paint(SkCanvas* canvas, const gfx::Rect& dest_rect);
virtual void GetCurrentFrame(scoped_refptr<media::VideoFrame>* frame_out);
virtual void PutCurrentFrame(scoped_refptr<media::VideoFrame> frame);
@@ -47,18 +47,18 @@ class VideoRendererImpl : public WebVideoRenderer {
private:
// Determine the conditions to perform fast paint. Returns true if we can do
// fast paint otherwise false.
- bool CanFastPaint(skia::PlatformCanvas* canvas, const gfx::Rect& dest_rect);
+ bool CanFastPaint(SkCanvas* canvas, const gfx::Rect& dest_rect);
// Slow paint does a YUV => RGB, and scaled blit in two separate operations.
void SlowPaint(media::VideoFrame* video_frame,
- skia::PlatformCanvas* canvas,
+ SkCanvas* canvas,
const gfx::Rect& dest_rect);
// Fast paint does YUV => RGB, scaling, blitting all in one step into the
// canvas. It's not always safe and appropriate to perform fast paint.
// CanFastPaint() is used to determine the conditions.
void FastPaint(media::VideoFrame* video_frame,
- skia::PlatformCanvas* canvas,
+ SkCanvas* canvas,
const gfx::Rect& dest_rect);
void TransformToSkIRect(const SkMatrix& matrix, const gfx::Rect& src_rect,
diff --git a/webkit/glue/media/web_data_source.h b/webkit/glue/media/web_data_source.h
index 956ac9e..25f1258 100644
--- a/webkit/glue/media/web_data_source.h
+++ b/webkit/glue/media/web_data_source.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -6,6 +6,7 @@
#define WEBKIT_GLUE_MEDIA_WEB_DATA_SOURCE_H_
#include "media/base/filters.h"
+#include "media/base/pipeline_status.h"
namespace webkit_glue {
@@ -16,6 +17,18 @@ class WebDataSource : public media::DataSource {
WebDataSource();
virtual ~WebDataSource();
+ // Initialize this object using |url|. This object calls |callback| when
+ // initialization has completed.
+ virtual void Initialize(const std::string& url,
+ media::PipelineStatusCallback* callback) = 0;
+
+ // Called to cancel initialization. The callback passed in Initialize() will
+ // be destroyed and will not be called after this method returns. Once this
+ // method returns, the object will be in an uninitialized state and
+ // Initialize() cannot be called again. The caller is expected to release
+ // its handle to this object and never call it again.
+ virtual void CancelInitialize() = 0;
+
// Returns true if the media resource has a single origin, false otherwise.
//
// Method called on the render thread.
@@ -31,6 +44,15 @@ class WebDataSource : public media::DataSource {
DISALLOW_COPY_AND_ASSIGN(WebDataSource);
};
+// Temporary hack to allow WebMediaPlayerImpl::Proxy::AddDataSource() to
+// be called when WebDataSource objects are created. This can be removed
+// once WebMediaPlayerImpl::Proxy is fixed so it doesn't have to track
+// WebDataSources. Proxy only has to track WebDataSources so it can call Abort()
+// on them at shutdown. Once cancellation is added to DataSource and pause
+// support in Demuxers cancel pending reads, Proxy shouldn't have to keep
+// a WebDataSource list or call Abort().
+typedef Callback1<WebDataSource*>::Type WebDataSourceBuildObserverHack;
+
} // namespace webkit_glue
#endif // WEBKIT_GLUE_MEDIA_WEB_DATA_SOURCE_H_
diff --git a/webkit/glue/media/web_data_source_factory.cc b/webkit/glue/media/web_data_source_factory.cc
new file mode 100644
index 0000000..d98e2da
--- /dev/null
+++ b/webkit/glue/media/web_data_source_factory.cc
@@ -0,0 +1,103 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "webkit/glue/media/web_data_source_factory.h"
+
+#include "base/logging.h"
+
+namespace webkit_glue {
+
+class WebDataSourceFactory::BuildRequest
+ : public media::AsyncDataSourceFactoryBase::BuildRequest {
+ public:
+ BuildRequest(const std::string& url, BuildCallback* callback,
+ WebDataSource* data_source,
+ WebDataSourceBuildObserverHack* build_observer);
+ virtual ~BuildRequest();
+
+ protected:
+ // AsyncDataSourceFactoryBase::BuildRequest method.
+ virtual void DoStart();
+
+ private:
+ void InitDone(media::PipelineStatus status);
+
+ scoped_refptr<WebDataSource> data_source_;
+ WebDataSourceBuildObserverHack* build_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(BuildRequest);
+};
+
+WebDataSourceFactory::WebDataSourceFactory(
+ MessageLoop* render_loop,
+ WebKit::WebFrame* frame,
+ FactoryFunction factory_function,
+ WebDataSourceBuildObserverHack* build_observer)
+ : render_loop_(render_loop),
+ frame_(frame),
+ factory_function_(factory_function),
+ build_observer_(build_observer) {
+ DCHECK(render_loop_);
+ DCHECK(frame_);
+ DCHECK(factory_function_);
+}
+
+WebDataSourceFactory::~WebDataSourceFactory() {}
+
+media::DataSourceFactory* WebDataSourceFactory::Clone() const {
+ return new WebDataSourceFactory(render_loop_, frame_, factory_function_,
+ build_observer_);
+}
+
+bool WebDataSourceFactory::AllowRequests() const {
+ return true;
+}
+
+media::AsyncDataSourceFactoryBase::BuildRequest*
+WebDataSourceFactory::CreateRequest(const std::string& url,
+ BuildCallback* callback) {
+ WebDataSource* data_source = factory_function_(render_loop_, frame_);
+
+ return new WebDataSourceFactory::BuildRequest(url, callback, data_source,
+ build_observer_);
+}
+
+WebDataSourceFactory::BuildRequest::BuildRequest(
+ const std::string& url,
+ BuildCallback* callback,
+ WebDataSource* data_source,
+ WebDataSourceBuildObserverHack* build_observer)
+ : AsyncDataSourceFactoryBase::BuildRequest(url, callback),
+ data_source_(data_source),
+ build_observer_(build_observer) {
+}
+
+WebDataSourceFactory::BuildRequest::~BuildRequest() {
+ if (data_source_.get()) {
+ data_source_->CancelInitialize();
+ data_source_ = NULL;
+ }
+}
+
+void WebDataSourceFactory::BuildRequest::DoStart() {
+ data_source_->Initialize(url(), NewCallback(this, &BuildRequest::InitDone));
+}
+
+void WebDataSourceFactory::BuildRequest::InitDone(
+ media::PipelineStatus status) {
+ scoped_refptr<WebDataSource> data_source;
+
+ data_source = (status == media::PIPELINE_OK) ? data_source_ : NULL;
+ data_source_ = NULL;
+
+ if (build_observer_ && data_source.get()) {
+ build_observer_->Run(data_source.get());
+ }
+
+ RequestComplete(status, data_source);
+ // Don't do anything after this line. This object is deleted by
+ // RequestComplete().
+}
+
+} // namespace webkit_glue
diff --git a/webkit/glue/media/web_data_source_factory.h b/webkit/glue/media/web_data_source_factory.h
new file mode 100644
index 0000000..7163ef4
--- /dev/null
+++ b/webkit/glue/media/web_data_source_factory.h
@@ -0,0 +1,51 @@
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#ifndef WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_FACTORY_H_
+#define WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_FACTORY_H_
+
+#include "media/base/async_filter_factory_base.h"
+#include "webkit/glue/media/web_data_source.h"
+
+class MessageLoop;
+
+namespace WebKit {
+class WebFrame;
+}
+
+namespace webkit_glue {
+
+class WebDataSourceFactory : public media::AsyncDataSourceFactoryBase {
+ public:
+ typedef WebDataSource* (*FactoryFunction)(MessageLoop* render_loop,
+ WebKit::WebFrame* frame);
+
+ WebDataSourceFactory(MessageLoop* render_loop, WebKit::WebFrame* frame,
+ FactoryFunction factory_function,
+ WebDataSourceBuildObserverHack* build_observer);
+ virtual ~WebDataSourceFactory();
+
+ // DataSourceFactory method.
+ virtual media::DataSourceFactory* Clone() const;
+
+ protected:
+ // AsyncDataSourceFactoryBase methods.
+ virtual bool AllowRequests() const;
+ virtual AsyncDataSourceFactoryBase::BuildRequest* CreateRequest(
+ const std::string& url, BuildCallback* callback);
+
+ private:
+ class BuildRequest;
+
+ MessageLoop* render_loop_;
+ WebKit::WebFrame* frame_;
+ FactoryFunction factory_function_;
+ WebDataSourceBuildObserverHack* build_observer_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebDataSourceFactory);
+};
+
+} // namespace webkit_glue
+
+#endif // WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_FACTORY_H_
diff --git a/webkit/glue/media/web_video_renderer.h b/webkit/glue/media/web_video_renderer.h
index efd3109..9e1eed5 100644
--- a/webkit/glue/media/web_video_renderer.h
+++ b/webkit/glue/media/web_video_renderer.h
@@ -1,4 +1,4 @@
-// Copyright (c) 2010 The Chromium Authors. All rights reserved.
+// Copyright (c) 2011 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
@@ -32,7 +32,7 @@ class WebVideoRenderer : public media::VideoRendererBase {
// |dest_rect|.
//
// Method called on the render thread.
- virtual void Paint(skia::PlatformCanvas* canvas,
+ virtual void Paint(SkCanvas* canvas,
const gfx::Rect& dest_rect) = 0;
// Clients of this class (painter/compositor) should use GetCurrentFrame()