summaryrefslogtreecommitdiffstats
path: root/webkit/glue
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue')
-rw-r--r--webkit/glue/media/buffered_data_source.cc323
-rw-r--r--webkit/glue/media/buffered_data_source.h67
-rw-r--r--webkit/glue/media/buffered_data_source_unittest.cc58
-rw-r--r--webkit/glue/webmediaplayer_impl.cc28
-rw-r--r--webkit/glue/webmediaplayer_impl.h6
5 files changed, 330 insertions, 152 deletions
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index a4d753b..34924c9 100644
--- a/webkit/glue/media/buffered_data_source.cc
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -98,13 +98,17 @@ BufferedResourceLoader::BufferedResourceLoader(
BufferedResourceLoader::~BufferedResourceLoader() {
}
-void BufferedResourceLoader::Start(net::CompletionCallback* start_callback) {
+void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
+ NetworkEventCallback* event_callback) {
// Make sure we have not started.
DCHECK(!bridge_.get());
DCHECK(!start_callback_.get());
+ DCHECK(!event_callback_.get());
DCHECK(start_callback);
+ DCHECK(event_callback);
start_callback_.reset(start_callback);
+ event_callback_.reset(event_callback);
if (first_byte_position_ != kPositionNotSpecified) {
range_requested_ = true;
@@ -133,8 +137,14 @@ void BufferedResourceLoader::Start(net::CompletionCallback* start_callback) {
void BufferedResourceLoader::Stop() {
// Reset callbacks.
start_callback_.reset();
+ event_callback_.reset();
read_callback_.reset();
+ // Use the internal buffer to signal that we have been stopped.
+ // TODO(hclam): Not so pretty to do this.
+ if (!buffer_.get())
+ return;
+
// Destroy internal buffer.
buffer_.reset();
@@ -199,6 +209,18 @@ void BufferedResourceLoader::Read(int64 position,
DoneRead(net::ERR_CACHE_MISS);
}
+int64 BufferedResourceLoader::GetBufferedFirstBytePosition() {
+ if (buffer_.get())
+ return offset_ - static_cast<int>(buffer_->backward_bytes());
+ return kPositionNotSpecified;
+}
+
+int64 BufferedResourceLoader::GetBufferedLastBytePosition() {
+ if (buffer_.get())
+ return offset_ + static_cast<int>(buffer_->forward_bytes()) - 1;
+ return kPositionNotSpecified;
+}
+
/////////////////////////////////////////////////////////////////////////////
// BufferedResourceLoader,
// webkit_glue::ResourceLoaderBridge::Peer implementations
@@ -300,6 +322,9 @@ void BufferedResourceLoader::OnReceivedData(const char* data, int len) {
// At last see if the buffer is full and we need to defer the downloading.
EnableDeferIfNeeded();
+
+ // Notify that we have received some data.
+ NotifyNetworkEvent();
}
void BufferedResourceLoader::OnCompletedRequest(
@@ -335,6 +360,9 @@ void BufferedResourceLoader::OnCompletedRequest(
// There must not be any outstanding read request.
DCHECK(!HasPendingRead());
+ // Notify that network response is completed.
+ NotifyNetworkEvent();
+
// We incremented the reference count when the loader was started. We balance
// that reference here so that we get destroyed. This is also the only safe
// place to destroy the ResourceLoaderBridge.
@@ -351,6 +379,8 @@ void BufferedResourceLoader::EnableDeferIfNeeded() {
if (bridge_.get())
bridge_->SetDefersLoading(true);
+
+ NotifyNetworkEvent();
}
}
@@ -361,6 +391,8 @@ void BufferedResourceLoader::DisableDeferIfNeeded() {
if (bridge_.get())
bridge_->SetDefersLoading(false);
+
+ NotifyNetworkEvent();
}
}
@@ -418,21 +450,6 @@ void BufferedResourceLoader::ReadInternal() {
DoneRead(read);
}
-void BufferedResourceLoader::DoneRead(int error) {
- read_callback_->RunWithParams(Tuple1<int>(error));
- read_callback_.reset();
- read_position_ = 0;
- read_size_ = 0;
- read_buffer_ = NULL;
- first_offset_ = 0;
- last_offset_ = 0;
-}
-
-void BufferedResourceLoader::DoneStart(int error) {
- start_callback_->RunWithParams(Tuple1<int>(error));
- start_callback_.reset();
-}
-
bool BufferedResourceLoader::VerifyPartialResponse(
const ResourceLoaderBridge::ResponseInfo& info) {
int64 first_byte_position, last_byte_position, instance_size;
@@ -455,15 +472,37 @@ bool BufferedResourceLoader::VerifyPartialResponse(
return true;
}
-//////////////////////////////////////////////////////////////////////////////
+void BufferedResourceLoader::DoneRead(int error) {
+ read_callback_->RunWithParams(Tuple1<int>(error));
+ read_callback_.reset();
+ read_position_ = 0;
+ read_size_ = 0;
+ read_buffer_ = NULL;
+ first_offset_ = 0;
+ last_offset_ = 0;
+}
+
+void BufferedResourceLoader::DoneStart(int error) {
+ start_callback_->RunWithParams(Tuple1<int>(error));
+ start_callback_.reset();
+}
+
+void BufferedResourceLoader::NotifyNetworkEvent() {
+ if (event_callback_.get())
+ event_callback_->Run();
+}
+
+/////////////////////////////////////////////////////////////////////////////
// BufferedDataSource, protected
BufferedDataSource::BufferedDataSource(
MessageLoop* render_loop,
webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
: total_bytes_(kPositionNotSpecified),
+ loaded_(false),
streaming_(false),
bridge_factory_(bridge_factory),
loader_(NULL),
+ network_activity_(false),
initialize_callback_(NULL),
read_callback_(NULL),
read_position_(0),
@@ -473,8 +512,8 @@ BufferedDataSource::BufferedDataSource(
intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
intermediate_read_buffer_size_(kInitialReadBufferSize),
render_loop_(render_loop),
- stopped_(false),
- stop_task_finished_(false) {
+ stop_signal_received_(false),
+ stopped_on_render_loop_(false) {
}
BufferedDataSource::~BufferedDataSource() {
@@ -483,7 +522,7 @@ BufferedDataSource::~BufferedDataSource() {
// A factory method to create BufferedResourceLoader using the read parameters.
// This method can be overrided to inject mock BufferedResourceLoader object
// for testing purpose.
-BufferedResourceLoader* BufferedDataSource::CreateLoader(
+BufferedResourceLoader* BufferedDataSource::CreateResourceLoader(
int64 first_byte_position, int64 last_byte_position) {
DCHECK(MessageLoop::current() == render_loop_);
@@ -530,7 +569,7 @@ void BufferedDataSource::Initialize(const std::string& url,
void BufferedDataSource::Stop() {
{
AutoLock auto_lock(lock_);
- stopped_ = true;
+ stop_signal_received_ = true;
}
render_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &BufferedDataSource::StopTask));
@@ -538,8 +577,7 @@ void BufferedDataSource::Stop() {
/////////////////////////////////////////////////////////////////////////////
// BufferedDataSource, media::DataSource implementation
-void BufferedDataSource::Read(int64 position, size_t size,
- uint8* data,
+void BufferedDataSource::Read(int64 position, size_t size, uint8* data,
media::DataSource::ReadCallback* read_callback) {
render_loop_->PostTask(FROM_HERE,
NewRunnableMethod(this, &BufferedDataSource::ReadTask,
@@ -564,6 +602,7 @@ bool BufferedDataSource::IsStreaming() {
void BufferedDataSource::InitializeTask() {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(!loader_.get());
+ DCHECK(!stopped_on_render_loop_);
// Kick starts the watch dog task that will handle connection timeout.
// We run the watch dog 2 times faster the actual timeout so as to catch
@@ -579,18 +618,18 @@ void BufferedDataSource::InitializeTask() {
// This also serve as a probe to determine server capability to serve
// range request.
// TODO(hclam): Do some experiments for the best approach.
- loader_ = CreateLoader(0, 1024);
+ loader_ = CreateResourceLoader(0, 1024);
loader_->Start(
- NewCallback(this,
- &BufferedDataSource::HttpInitialStartCallback));
+ NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
} else {
// For all other protocols, assume they support range request. We fetch
// the full range of the resource to obtain the instance size because
// we won't be served HTTP headers.
- loader_ = CreateLoader(-1, -1);
+ loader_ = CreateResourceLoader(-1, -1);
loader_->Start(
- NewCallback(this,
- &BufferedDataSource::NonHttpInitialStartCallback));
+ NewCallback(this, &BufferedDataSource::NonHttpInitialStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
}
}
@@ -601,9 +640,9 @@ void BufferedDataSource::ReadTask(
// If StopTask() was executed we should return immediately. We check this
// variable to prevent doing any actual work after clean up was done. We do
- // not check |stopped_| because anything use of it has to be within |lock_|
- // which is not desirable.
- if (stop_task_finished_)
+ // not check |stop_signal_received_| because anything use of it has to be
+ // within |lock_| which is not desirable.
+ if (stopped_on_render_loop_)
return;
DCHECK(!read_callback_.get());
@@ -623,6 +662,7 @@ void BufferedDataSource::ReadTask(
void BufferedDataSource::StopTask() {
DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(!stopped_on_render_loop_);
// Stop the watch dog.
watch_dog_timer_.Stop();
@@ -640,21 +680,34 @@ void BufferedDataSource::StopTask() {
read_attempts_ = 0;
// Signal that stop task has finished execution.
- stop_task_finished_ = true;
+ stopped_on_render_loop_ = true;
}
-void BufferedDataSource::SwapLoaderTask(
- scoped_refptr<BufferedResourceLoader> loader) {
+void BufferedDataSource::RestartLoadingTask() {
DCHECK(MessageLoop::current() == render_loop_);
- DCHECK(loader);
- loader_ = loader;
- loader_->Start(NewCallback(this,
- &BufferedDataSource::PartialReadStartCallback));
+ // This variable is set in StopTask(). We check this and do an early return.
+ // The sequence of actions which enable this conditions is:
+ // 1. Stop() is called from the pipeline.
+ // 2. ReadCallback() is called from the resource loader.
+ // 3. StopTask() is executed.
+ // 4. RestartLoadingTask() is executed.
+ if (stopped_on_render_loop_)
+ return;
+
+ // If there's no outstanding read then return early.
+ if (!read_callback_.get())
+ return;
+
+ loader_ = CreateResourceLoader(read_position_, -1);
+ loader_->Start(
+ NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
}
void BufferedDataSource::WatchDogTask() {
DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(!stopped_on_render_loop_);
// We only care if there is an active read request.
if (!read_callback_.get())
@@ -673,10 +726,13 @@ void BufferedDataSource::WatchDogTask() {
++read_attempts_;
read_submitted_time_ = base::Time::Now();
- // Stops the current loader and swap in a new resource loader and
+ // Stops the current loader and creates a new resource loader and
// retry the request.
loader_->Stop();
- SwapLoaderTask(CreateLoader(read_position_, -1));
+ loader_ = CreateResourceLoader(read_position_, -1);
+ loader_->Start(
+ NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
+ NewCallback(this, &BufferedDataSource::NetworkEventCallback));
}
// This method is the place where actual read happens, |loader_| must be valid
@@ -698,8 +754,9 @@ void BufferedDataSource::ReadInternal() {
// Method to report the results of the current read request. Also reset all
// the read parameters.
-void BufferedDataSource::DoneRead(int error) {
+void BufferedDataSource::DoneRead_Locked(int error) {
DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(read_callback_.get());
lock_.AssertAcquired();
if (error >= 0) {
@@ -715,7 +772,8 @@ void BufferedDataSource::DoneRead(int error) {
read_buffer_ = 0;
}
-void BufferedDataSource::DoneInitialization() {
+void BufferedDataSource::DoneInitialization_Locked() {
+ DCHECK(MessageLoop::current() == render_loop_);
DCHECK(initialize_callback_.get());
lock_.AssertAcquired();
@@ -729,6 +787,22 @@ void BufferedDataSource::DoneInitialization() {
// BufferedResourceLoader.
void BufferedDataSource::HttpInitialStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ int64 instance_size = loader_->instance_size();
+ bool partial_response = loader_->partial_response();
+ bool success = error == net::OK;
+
+ 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;
+ } else {
+ // TODO(hclam): In case of failure, we can retry several times.
+ 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
@@ -739,40 +813,44 @@ void BufferedDataSource::HttpInitialStartCallback(int error) {
// this object when Stop() method is ever called. Locking this method is safe
// because |lock_| is only acquired in tasks on render thread.
AutoLock auto_lock(lock_);
- if (stopped_)
+ if (stop_signal_received_)
return;
- if (error != net::OK) {
- // TODO(hclam): In case of failure, we can retry several times.
+ if (!success) {
host()->SetError(media::PIPELINE_ERROR_NETWORK);
- DCHECK(loader_.get());
- loader_->Stop();
- DoneInitialization();
+ DoneInitialization_Locked();
return;
}
- // TODO(hclam): Needs more thinking about supporting servers without range
- // request or their partial response is not complete.
- total_bytes_ = loader_->instance_size();
- if (total_bytes_ >= 0 && loader_->partial_response()) {
- // 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(total_bytes_);
- } else {
+ if (streaming_) {
// If the server didn't reply with an instance size, it is likely this
// is a streaming response.
- streaming_ = true;
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);
}
// Currently, only files can be used reliably w/o a network.
host()->SetLoaded(false);
- DoneInitialization();
+ DoneInitialization_Locked();
}
void BufferedDataSource::NonHttpInitialStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ int64 instance_size = loader_->instance_size();
+ bool success = error == net::OK && instance_size != kPositionNotSpecified;
+
+ if (success) {
+ total_bytes_ = instance_size;
+ 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
@@ -783,23 +861,17 @@ void BufferedDataSource::NonHttpInitialStartCallback(int error) {
// this object when Stop() method is ever called. Locking this method is safe
// because |lock_| is only acquired in tasks on render thread.
AutoLock auto_lock(lock_);
- if (stopped_)
+ if (stop_signal_received_)
return;
- DCHECK(loader_.get());
-
- if (error != net::OK || loader_->instance_size() == kPositionNotSpecified) {
+ if (success) {
+ host()->SetTotalBytes(total_bytes_);
+ host()->SetBufferedBytes(total_bytes_);
+ host()->SetLoaded(loaded_);
+ } else {
host()->SetError(media::PIPELINE_ERROR_NETWORK);
- loader_->Stop();
- DoneInitialization();
- return;
}
-
- total_bytes_ = loader_->instance_size();
- host()->SetTotalBytes(total_bytes_);
- host()->SetBufferedBytes(total_bytes_);
- host()->SetLoaded(true);
- DoneInitialization();
+ DoneInitialization_Locked();
}
void BufferedDataSource::PartialReadStartCallback(int error) {
@@ -813,27 +885,42 @@ void BufferedDataSource::PartialReadStartCallback(int error) {
// Once the range request has started successfully, we can proceed with
// reading from it.
ReadInternal();
- } 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. So 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.
- AutoLock auto_lock(lock_);
- if (stopped_)
- return;
- DoneRead(net::ERR_INVALID_RESPONSE);
+ return;
}
+
+ // Stop the resource loader since we have received an error.
+ 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. So 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.
+ AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+ DoneRead_Locked(net::ERR_INVALID_RESPONSE);
}
void BufferedDataSource::ReadCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
+ if (error < 0) {
+ DCHECK(loader_.get());
+
+ // Stop the resource load if it failed.
+ loader_->Stop();
+
+ if (error == net::ERR_CACHE_MISS) {
+ render_loop_->PostTask(FROM_HERE,
+ NewRunnableMethod(this, &BufferedDataSource::RestartLoadingTask));
+ 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
@@ -843,36 +930,50 @@ void BufferedDataSource::ReadCallback(int error) {
// this object when Stop() method is ever called. Locking this method is safe
// because |lock_| is only acquired in tasks on render thread.
AutoLock auto_lock(lock_);
- if (stopped_)
+ if (stop_signal_received_)
return;
- DCHECK(loader_.get());
- DCHECK(read_callback_.get());
-
- if (error >= 0) {
+ if (error > 0) {
// 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);
- DoneRead(error);
- } else if (error == net::ERR_CACHE_MISS) {
- // If the current loader cannot serve this read request, we need to create
- // a new one.
- // TODO(hclam): we need to count how many times it failed to prevent
- // excessive trials.
-
- // Stops the current resource loader.
- loader_->Stop();
+ }
+ DoneRead_Locked(error);
+}
- // Since this method is called from the current buffered resource loader,
- // we cannot delete it. So we need to post a task to swap in a new
- // resource loader and starts it.
- render_loop_->PostTask(FROM_HERE,
- NewRunnableMethod(this, &BufferedDataSource::SwapLoaderTask,
- CreateLoader(read_position_, -1)));
- } else {
- loader_->Stop();
- DoneRead(error);
+void BufferedDataSource::NetworkEventCallback() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ DCHECK(loader_.get());
+
+ // In case of non-HTTP request we don't need to report network events,
+ // so return immediately.
+ if (loaded_)
+ return;
+
+ bool network_activity = loader_->network_activity();
+ int64 buffered_last_byte_position = loader_->GetBufferedLastBytePosition();
+
+ // If we get an unspecified value, return immediately.
+ if (buffered_last_byte_position == kPositionNotSpecified)
+ 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. So 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.
+ AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+
+ if (network_activity != network_activity_) {
+ network_activity_ = network_activity;
+ host()->SetNetworkActivity(network_activity);
}
+ host()->SetBufferedBytes(buffered_last_byte_position + 1);
}
} // namespace webkit_glue
diff --git a/webkit/glue/media/buffered_data_source.h b/webkit/glue/media/buffered_data_source.h
index 532fa56..dbbb54c 100644
--- a/webkit/glue/media/buffered_data_source.h
+++ b/webkit/glue/media/buffered_data_source.h
@@ -5,7 +5,9 @@
#ifndef WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_H_
#define WEBKIT_GLUE_MEDIA_BUFFERED_DATA_SOURCE_H_
+#include <algorithm>
#include <string>
+#include <vector>
#include "base/lock.h"
#include "base/scoped_ptr.h"
@@ -33,6 +35,8 @@ class BufferedResourceLoader :
public base::RefCountedThreadSafe<BufferedResourceLoader>,
public webkit_glue::ResourceLoaderBridge::Peer {
public:
+ typedef Callback0::Type NetworkEventCallback;
+
// |bridge_factory| - Factory to create a ResourceLoaderBridge.
// |url| - URL for the resource to be loaded.
// |first_byte_position| - First byte to start loading from, -1 for not
@@ -57,7 +61,10 @@ class BufferedResourceLoader :
// An invalid response is received from the server.
// - (Anything else)
// An error code that indicates the request has failed.
- virtual void Start(net::CompletionCallback* callback);
+ // |event_callback| is called when the response is completed, data is
+ // received, the request is suspended or resumed.
+ virtual void Start(net::CompletionCallback* callback,
+ NetworkEventCallback* event_callback);
// Stop this loader, cancels and request and release internal buffer.
virtual void Stop();
@@ -75,6 +82,14 @@ class BufferedResourceLoader :
virtual void Read(int64 position, int read_size,
uint8* buffer, net::CompletionCallback* callback);
+ // Returns the position of the first byte buffered. Returns -1 if such value
+ // is not available.
+ virtual int64 GetBufferedFirstBytePosition();
+
+ // Returns the position of the last byte buffered. Returns -1 if such value
+ // is not available.
+ virtual int64 GetBufferedLastBytePosition();
+
// 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_; }
@@ -87,6 +102,9 @@ class BufferedResourceLoader :
// It means a 206 response in HTTP/HTTPS protocol.
virtual bool partial_response() { return partial_response_; }
+ // Returns true if network is currently active.
+ virtual bool network_activity() { return !completed_ && !deferred_; }
+
/////////////////////////////////////////////////////////////////////////////
// webkit_glue::ResourceLoaderBridge::Peer implementations.
virtual void OnUploadProgress(uint64 position, uint64 size) {}
@@ -134,6 +152,9 @@ class BufferedResourceLoader :
// Done with start. Invokes the start callback and reset it.
void DoneStart(int error);
+ // Calls |event_callback_| in terms of a network event.
+ void NotifyNetworkEvent();
+
bool HasPendingRead() { return read_callback_.get() != NULL; }
// A sliding window of buffer.
@@ -156,6 +177,9 @@ class BufferedResourceLoader :
int64 first_byte_position_;
int64 last_byte_position_;
+ // Callback method that listens to network events.
+ scoped_ptr<NetworkEventCallback> event_callback_;
+
// Members used during request start.
scoped_ptr<net::CompletionCallback> start_callback_;
scoped_ptr<webkit_glue::ResourceLoaderBridge> bridge_;
@@ -218,8 +242,8 @@ class BufferedDataSource : public media::DataSource {
// A factory method to create a BufferedResourceLoader based on the read
// parameters. We can override this file to object a mock
// BufferedResourceLoader for testing.
- virtual BufferedResourceLoader* CreateLoader(int64 first_byte_position,
- int64 last_byte_position);
+ virtual BufferedResourceLoader* CreateResourceLoader(
+ int64 first_byte_position, int64 last_byte_position);
// Gets the number of milliseconds to declare a request timeout since
// the request was made. This method is made virtual so as to inject a
@@ -232,25 +256,23 @@ class BufferedDataSource : public media::DataSource {
MessageLoop*,
webkit_glue::MediaResourceLoaderBridgeFactory*>;
- // Posted to perform initialization on render thread.
+ // Posted to perform initialization on render thread and start resource
+ // loading.
void InitializeTask();
- // Task posted to perform resource loading and actual reading on the render
- // thread.
- void ReadTask(int64 position, int read_size,
- uint8* read_buffer,
+ // Task posted to perform actual reading on the render thread.
+ void ReadTask(int64 position, int read_size, uint8* read_buffer,
media::DataSource::ReadCallback* read_callback);
// Task posted when Stop() is called.
void StopTask();
- // Reset |loader_| with |loader| and starts it. This task is posted from
- // callback method from the current buffered resource loader.
- void SwapLoaderTask(scoped_refptr<BufferedResourceLoader> loader);
+ // Restart resource loading on render thread.
+ void RestartLoadingTask();
// This task monitors the current active read request. If the current read
// request has timed out, this task will destroy the current loader and
- // creates a new to accomodate the read request.
+ // creates a new one to accomodate the read request.
void WatchDogTask();
// The method that performs actual read. This method can only be executed on
@@ -258,10 +280,10 @@ class BufferedDataSource : public media::DataSource {
void ReadInternal();
// Calls |read_callback_| and reset all read parameters.
- void DoneRead(int error);
+ void DoneRead_Locked(int error);
// Calls |initialize_callback_| and reset it.
- void DoneInitialization();
+ void DoneInitialization_Locked();
// Callback method for |loader_| if URL for the resource requested is using
// HTTP protocol. This method is called when response for initial request is
@@ -284,6 +306,9 @@ class BufferedDataSource : public media::DataSource {
// the error code or the number of bytes read.
void ReadCallback(int error);
+ // Callback method when a network event is received.
+ void NetworkEventCallback();
+
media::MediaFormat media_format_;
// URL of the resource requested.
@@ -295,6 +320,9 @@ class BufferedDataSource : public media::DataSource {
// need to protect it.
int64 total_bytes_;
+ // True if this data source is considered loaded.
+ bool loaded_;
+
// This value will be true if this data source can only support streaming.
// i.e. range request is not supported.
bool streaming_;
@@ -305,6 +333,9 @@ class BufferedDataSource : public media::DataSource {
// A resource loader for the media resource.
scoped_refptr<BufferedResourceLoader> loader_;
+ // True if network is active.
+ bool network_activity_;
+
// Callback method from the pipeline for initialization.
scoped_ptr<media::FilterCallback> initialize_callback_;
@@ -337,11 +368,11 @@ class BufferedDataSource : public media::DataSource {
// Stop signal to suppressing activities. This variable is set on the pipeline
// thread and read from the render thread.
- bool stopped_;
+ bool stop_signal_received_;
- // This variable is set by StopTask() and read from ReadTask(). It is used to
- // prevent ReadTask() from doing anything after StopTask() is executed.
- bool stop_task_finished_;
+ // This variable is set by StopTask() that indicates this object is stopped
+ // on the render thread.
+ bool stopped_on_render_loop_;
// This timer is to run the WatchDogTask repeatedly. We use a timer instead
// of doing PostDelayedTask() reduce the extra reference held by the message
diff --git a/webkit/glue/media/buffered_data_source_unittest.cc b/webkit/glue/media/buffered_data_source_unittest.cc
index c05c9b9..53915f1 100644
--- a/webkit/glue/media/buffered_data_source_unittest.cc
+++ b/webkit/glue/media/buffered_data_source_unittest.cc
@@ -82,8 +82,9 @@ class BufferedResourceLoaderTest : public testing::Test {
CreateBridge(gurl_, _, first_position_, last_position_))
.WillOnce(Return(bridge_.get()));
EXPECT_CALL(*bridge_, Start(loader_.get()));
- loader_->Start(NewCallback(this,
- &BufferedResourceLoaderTest::StartCallback));
+ loader_->Start(
+ NewCallback(this, &BufferedResourceLoaderTest::StartCallback),
+ NewCallback(this, &BufferedResourceLoaderTest::NetworkCallback));
}
void FullResponse(int64 instance_size) {
@@ -134,6 +135,7 @@ class BufferedResourceLoaderTest : public testing::Test {
// Helper method to write to |loader_| from |data_|.
void WriteLoader(int position, int size) {
+ EXPECT_CALL(*this, NetworkCallback());
loader_->OnReceivedData(reinterpret_cast<char*>(data_ + position), size);
}
@@ -150,6 +152,7 @@ class BufferedResourceLoaderTest : public testing::Test {
MOCK_METHOD1(StartCallback, void(int error));
MOCK_METHOD1(ReadCallback, void(int error));
+ MOCK_METHOD0(NetworkCallback, void());
protected:
GURL gurl_;
@@ -282,6 +285,7 @@ TEST_F(BufferedResourceLoaderTest, BufferAndRead) {
ReadLoader(9, 10, buffer);
// Response has completed.
+ EXPECT_CALL(*this, NetworkCallback());
EXPECT_CALL(*bridge_, OnDestroy())
.WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
URLRequestStatus status;
@@ -327,6 +331,7 @@ TEST_F(BufferedResourceLoaderTest, ReadOutsideBuffer) {
ReadLoader(25, 10, buffer);
EXPECT_CALL(*this, ReadCallback(5));
+ EXPECT_CALL(*this, NetworkCallback());
EXPECT_CALL(*bridge_, OnDestroy())
.WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
URLRequestStatus status;
@@ -344,6 +349,7 @@ TEST_F(BufferedResourceLoaderTest, RequestFailedWhenRead) {
ReadLoader(10, 10, buffer);
EXPECT_CALL(*this, ReadCallback(net::ERR_FAILED));
+ EXPECT_CALL(*this, NetworkCallback());
EXPECT_CALL(*bridge_, OnDestroy())
.WillOnce(Invoke(this, &BufferedResourceLoaderTest::ReleaseBridge));
URLRequestStatus status;
@@ -358,20 +364,24 @@ class MockBufferedResourceLoader : public BufferedResourceLoader {
MockBufferedResourceLoader() : BufferedResourceLoader() {
}
- MOCK_METHOD1(Start, void(net::CompletionCallback* read_callback));
+ MOCK_METHOD2(Start, void(net::CompletionCallback* read_callback,
+ NetworkEventCallback* network_callback));
MOCK_METHOD0(Stop, void());
MOCK_METHOD4(Read, void(int64 position, int read_size, uint8* buffer,
net::CompletionCallback* callback));
MOCK_METHOD0(content_length, int64());
MOCK_METHOD0(instance_size, int64());
MOCK_METHOD0(partial_response, bool());
+ MOCK_METHOD0(network_activity, bool());
+ MOCK_METHOD0(GetBufferedFirstBytePosition, int64());
+ MOCK_METHOD0(GetBufferedLastBytePosition, int64());
private:
DISALLOW_COPY_AND_ASSIGN(MockBufferedResourceLoader);
};
// A mock BufferedDataSource to inject mock BufferedResourceLoader through
-// CreateLoader() method.
+// CreateResourceLoader() method.
class MockBufferedDataSource : public BufferedDataSource {
public:
// Static methods for creating this class.
@@ -390,13 +400,12 @@ class MockBufferedDataSource : public BufferedDataSource {
return base::TimeDelta::FromMilliseconds(100);
}
- MOCK_METHOD2(CreateLoader, BufferedResourceLoader*(int64 first_position,
- int64 last_position));
+ MOCK_METHOD2(CreateResourceLoader, BufferedResourceLoader*(
+ int64 first_position, int64 last_position));
protected:
MockBufferedDataSource(
- MessageLoop* message_loop,
- MediaResourceLoaderBridgeFactory* factory)
+ MessageLoop* message_loop, MediaResourceLoaderBridgeFactory* factory)
: BufferedDataSource(message_loop, factory) {
}
@@ -459,11 +468,11 @@ class BufferedDataSourceTest : public testing::Test {
bool loaded = networkState == LOADED;
{
InSequence s;
- EXPECT_CALL(*data_source_, CreateLoader(_, _))
+ EXPECT_CALL(*data_source_, CreateResourceLoader(_, _))
.WillOnce(Return(loader_.get()));
// The initial response loader will be started.
- EXPECT_CALL(*loader_, Start(NotNull()))
+ EXPECT_CALL(*loader_, Start(NotNull(), NotNull()))
.WillOnce(
DoAll(Assign(&error_, error),
Invoke(this,
@@ -471,18 +480,21 @@ class BufferedDataSourceTest : public testing::Test {
}
StrictMock<media::MockFilterCallback> callback;
+ EXPECT_CALL(*loader_, instance_size())
+ .WillRepeatedly(Return(instance_size));
+ EXPECT_CALL(*loader_, partial_response())
+ .WillRepeatedly(Return(partial_response));
if (error == net::OK) {
// Expected loaded or not.
EXPECT_CALL(host_, SetLoaded(loaded));
- EXPECT_CALL(*loader_, instance_size())
- .WillRepeatedly(Return(instance_size));
- EXPECT_CALL(*loader_, partial_response())
- .WillRepeatedly(Return(partial_response));
// TODO(hclam): The condition for streaming needs to be adjusted.
if (instance_size != -1 && (loaded || partial_response)) {
EXPECT_CALL(host_, SetTotalBytes(instance_size));
- EXPECT_CALL(host_, SetBufferedBytes(instance_size));
+ if (loaded)
+ EXPECT_CALL(host_, SetBufferedBytes(instance_size));
+ else
+ EXPECT_CALL(host_, SetBufferedBytes(0));
} else {
EXPECT_CALL(host_, SetStreaming(true));
}
@@ -526,9 +538,13 @@ class BufferedDataSourceTest : public testing::Test {
bridge_factory_.release();
}
- void InvokeStartCallback(net::CompletionCallback* callback) {
+ void InvokeStartCallback(
+ net::CompletionCallback* callback,
+ BufferedResourceLoader::NetworkEventCallback* network_callback) {
callback->RunWithParams(Tuple1<int>(error_));
delete callback;
+ // TODO(hclam): Save this callback.
+ delete network_callback;
}
void InvokeReadCallback(int64 position, int size, uint8* buffer,
@@ -578,11 +594,11 @@ class BufferedDataSourceTest : public testing::Test {
// 2. Then the current loader will be stop and destroyed.
StrictMock<MockBufferedResourceLoader> *new_loader =
new StrictMock<MockBufferedResourceLoader>();
- EXPECT_CALL(*data_source_, CreateLoader(position, -1))
+ EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1))
.WillOnce(Return(new_loader));
// 3. Then the new loader will be started.
- EXPECT_CALL(*new_loader, Start(NotNull()))
+ EXPECT_CALL(*new_loader, Start(NotNull(), NotNull()))
.WillOnce(DoAll(Assign(&error_, net::OK),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
@@ -642,13 +658,13 @@ class BufferedDataSourceTest : public testing::Test {
// 2. Then the current loader will be stop and destroyed.
StrictMock<MockBufferedResourceLoader> *new_loader =
new StrictMock<MockBufferedResourceLoader>();
- EXPECT_CALL(*data_source_, CreateLoader(position, -1))
+ EXPECT_CALL(*data_source_, CreateResourceLoader(position, -1))
.WillOnce(Return(new_loader));
// 3. Then the new loader will be started and respond to queries about
// whether this is a partial response using the value of the previous
// loader.
- EXPECT_CALL(*new_loader, Start(NotNull()))
+ EXPECT_CALL(*new_loader, Start(NotNull(), NotNull()))
.WillOnce(DoAll(Assign(&error_, net::OK),
Invoke(this,
&BufferedDataSourceTest::InvokeStartCallback)));
@@ -684,7 +700,7 @@ class BufferedDataSourceTest : public testing::Test {
scoped_ptr<StrictMock<MockMediaResourceLoaderBridgeFactory> >
bridge_factory_;
scoped_refptr<StrictMock<MockBufferedResourceLoader> > loader_;
- scoped_refptr<MockBufferedDataSource > data_source_;
+ scoped_refptr<MockBufferedDataSource> data_source_;
scoped_refptr<media::FilterFactory> factory_;
StrictMock<media::MockFilterHost> host_;
diff --git a/webkit/glue/webmediaplayer_impl.cc b/webkit/glue/webmediaplayer_impl.cc
index 7bf5c1e..2f9306f 100644
--- a/webkit/glue/webmediaplayer_impl.cc
+++ b/webkit/glue/webmediaplayer_impl.cc
@@ -123,6 +123,11 @@ void WebMediaPlayerImpl::Proxy::PipelineErrorCallback() {
&WebMediaPlayerImpl::Proxy::PipelineErrorTask));
}
+void WebMediaPlayerImpl::Proxy::NetworkEventCallback() {
+ render_loop_->PostTask(FROM_HERE, NewRunnableMethod(this,
+ &WebMediaPlayerImpl::Proxy::NetworkEventTask));
+}
+
void WebMediaPlayerImpl::Proxy::RepaintTask() {
DCHECK(MessageLoop::current() == render_loop_);
{
@@ -163,6 +168,13 @@ void WebMediaPlayerImpl::Proxy::PipelineErrorTask() {
}
}
+void WebMediaPlayerImpl::Proxy::NetworkEventTask() {
+ DCHECK(MessageLoop::current() == render_loop_);
+ if (webmediaplayer_) {
+ webmediaplayer_->OnNetworkEvent();
+ }
+}
+
/////////////////////////////////////////////////////////////////////////////
// WebMediaPlayerImpl implementation
@@ -199,6 +211,8 @@ WebMediaPlayerImpl::WebMediaPlayerImpl(WebKit::WebMediaPlayerClient* client,
&WebMediaPlayerImpl::Proxy::PipelineEndedCallback));
pipeline_->SetPipelineErrorCallback(NewCallback(proxy_.get(),
&WebMediaPlayerImpl::Proxy::PipelineErrorCallback));
+ pipeline_->SetNetworkEventCallback(NewCallback(proxy_.get(),
+ &WebMediaPlayerImpl::Proxy::NetworkEventCallback));
// Add in the default filter factories.
filter_factory_->AddFactory(media::FFmpegDemuxer::CreateFilterFactory());
@@ -505,10 +519,10 @@ void WebMediaPlayerImpl::OnPipelineInitialize() {
DCHECK(MessageLoop::current() == main_loop_);
if (pipeline_->GetError() == media::PIPELINE_OK) {
// Only keep one time range starting from 0.
- WebKit::WebTimeRanges new_buffered(static_cast<size_t>(1));
+ WebKit::WebTimeRanges new_buffered(1u);
new_buffered[0].start = 0.0f;
new_buffered[0].end =
- static_cast<float>(pipeline_->GetBufferedTime().InSecondsF());
+ static_cast<float>(pipeline_->GetDuration().InSecondsF());
buffered_.swap(new_buffered);
// Since we have initialized the pipeline, say we have everything.
@@ -583,6 +597,16 @@ void WebMediaPlayerImpl::OnPipelineError() {
Repaint();
}
+void WebMediaPlayerImpl::OnNetworkEvent() {
+ DCHECK(MessageLoop::current() == main_loop_);
+ if (pipeline_->GetError() == media::PIPELINE_OK) {
+ if (pipeline_->IsNetworkActive())
+ SetNetworkState(WebKit::WebMediaPlayer::Loading);
+ else
+ SetNetworkState(WebKit::WebMediaPlayer::Idle);
+ }
+}
+
void WebMediaPlayerImpl::SetNetworkState(
WebKit::WebMediaPlayer::NetworkState state) {
DCHECK(MessageLoop::current() == main_loop_);
diff --git a/webkit/glue/webmediaplayer_impl.h b/webkit/glue/webmediaplayer_impl.h
index 9348983..5c5de44 100644
--- a/webkit/glue/webmediaplayer_impl.h
+++ b/webkit/glue/webmediaplayer_impl.h
@@ -108,6 +108,7 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
void PipelineSeekCallback();
void PipelineEndedCallback();
void PipelineErrorCallback();
+ void NetworkEventCallback();
private:
// Invoke |webmediaplayer_| to perform a repaint.
@@ -125,6 +126,9 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
// Notify |webmediaplayer_| that a pipeline error has been set.
void PipelineErrorTask();
+ // Notify |webmediaplayer_| that there's a network event.
+ void NetworkEventTask();
+
// The render message loop where WebKit lives.
MessageLoop* render_loop_;
WebMediaPlayerImpl* webmediaplayer_;
@@ -229,6 +233,8 @@ class WebMediaPlayerImpl : public WebKit::WebMediaPlayer,
void OnPipelineError();
+ void OnNetworkEvent();
+
private:
// Helpers that set the network/ready state and notifies the client if
// they've changed.