summaryrefslogtreecommitdiffstats
path: root/webkit/glue/media/buffered_data_source.cc
diff options
context:
space:
mode:
Diffstat (limited to 'webkit/glue/media/buffered_data_source.cc')
-rw-r--r--webkit/glue/media/buffered_data_source.cc323
1 files changed, 212 insertions, 111 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