Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(245)

Unified Diff: webkit/glue/media/buffered_data_source.cc

Issue 160076: BufferedDataSource to support server without range request support... (Closed) Base URL: svn://chrome-svn/chrome/trunk/src/
Patch Set: '' Created 11 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « webkit/glue/media/buffered_data_source.h ('k') | webkit/glue/media/buffered_data_source_unittest.cc » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: webkit/glue/media/buffered_data_source.cc
===================================================================
--- webkit/glue/media/buffered_data_source.cc (revision 21893)
+++ webkit/glue/media/buffered_data_source.cc (working copy)
@@ -32,11 +32,6 @@
// Forward capacity of the buffer, by default 10MB.
const size_t kForwardCapacity = 10 * kMegabyte;
-// The maximum offset to seek to, this value limits the range of seek to
-// prevent corruption of calculations. The buffer has a maximum size of 12MB
-// so this is enough to contain every valid operations.
-const int kMaxSeek = 100 * kMegabyte;
-
// The threshold of bytes that we should wait until the data arrives in the
// future instead of restarting a new connection. This number is defined in the
// number of bytes, we should determine this value from typical connection speed
@@ -63,6 +58,7 @@
const int kInitialReadBufferSize = 32768;
// A helper method that accepts only HTTP, HTTPS and FILE protocol.
+// TODO(hclam): Support also FTP protocol.
bool IsSchemeSupported(const GURL& url) {
return url.SchemeIs(kHttpScheme) ||
url.SchemeIs(kHttpsScheme) ||
@@ -92,6 +88,7 @@
bridge_(NULL),
offset_(0),
content_length_(kPositionNotSpecified),
+ instance_size_(kPositionNotSpecified),
read_callback_(NULL),
read_position_(0),
read_size_(0),
@@ -159,29 +156,39 @@
read_size_ = read_size;
read_buffer_ = buffer;
- // Check that the read parameters are within the range that we can handle.
- // If the read request is made too far from the current offset, report that
- // we cannot serve the request.
- if (VerifyRead()) {
- // If we can serve the request now, do the actual read.
- if (CanFulfillRead()) {
- ReadInternal();
- DisableDeferIfNeeded();
- return;
- }
+ // If read position is beyond the instance size, we cannot read there.
+ if (instance_size_ != kPositionNotSpecified &&
+ instance_size_ <= read_position_) {
+ DoneRead(0);
+ return;
+ }
- // If we expected the read request to be fulfilled later, returns
- // immediately and let more data to flow in.
- if (WillFulfillRead())
- return;
-
- // Make a callback to report failure.
+ // Make sure |offset_| and |read_position_| does not differ by a large
+ // amount.
+ if (read_position_ > offset_ + kint32max ||
+ read_position_ < offset_ + kint32min) {
DoneRead(net::ERR_CACHE_MISS);
return;
}
- // TODO(hclam): We should report a better error code than just 0.
- DoneRead(0);
+ // Prepare the parameters.
+ first_offset_ = static_cast<int>(read_position_ - offset_);
+ last_offset_ = first_offset_ + read_size_;
+
+ // If we can serve the request now, do the actual read.
+ if (CanFulfillRead()) {
+ ReadInternal();
+ DisableDeferIfNeeded();
+ return;
+ }
+
+ // If we expected the read request to be fulfilled later, returns
+ // immediately and let more data to flow in.
+ if (WillFulfillRead())
+ return;
+
+ // Make a callback to report failure.
+ DoneRead(net::ERR_CACHE_MISS);
}
/////////////////////////////////////////////////////////////////////////////
@@ -220,15 +227,10 @@
// if not report failure.
error = net::ERR_INVALID_RESPONSE;
} else if (range_requested_) {
- if (info.headers->response_code() != kHttpPartialContent ||
- !info.headers->GetContentRange(&first_byte_position,
- &last_byte_position,
- &instance_size)) {
- // We requested a range, but server didn't reply with partial content or
- // the "Content-Range" header is corrupted.
- // TODO(hclam): should also make sure this is the range we requested.
- error = net::ERR_INVALID_RESPONSE;
- }
+ // If we have verified the partial response and it is correct, we will
+ // return net::OK.
+ if (!VerifyPartialResponse(info))
+ error = net::ERR_REQUEST_RANGE_NOT_SATISFIABLE;
} else if (info.headers->response_code() != kHttpOK) {
// We didn't request a range but server didn't reply with "200 OK".
error = net::ERR_FAILED;
@@ -245,6 +247,11 @@
// not specified and this is a streaming response.
content_length_ = info.content_length;
+ // If we have not requested a range, then the size of the instance is equal
+ // to the content length.
+ if (!range_requested_)
+ instance_size_ = content_length_;
+
// We only care about the first byte position if it's given by the server.
// TODO(hclam): If server replies with a different offset, consider failing
// here.
@@ -370,21 +377,6 @@
return true;
}
-bool BufferedResourceLoader::VerifyRead() {
- // Make sure |offset_| and |read_position_| does not differ by a large
- // amount
- if (read_position_ > offset_ + kMaxSeek)
- return false;
- else if (read_position_ < offset_ - kMaxSeek)
- return false;
-
- // If we can manage the read request with int32 math, then prepare the
- // parameters.
- first_offset_ = static_cast<int>(read_position_ - offset_);
- last_offset_ = first_offset_ + read_size_;
- return true;
-}
-
void BufferedResourceLoader::ReadInternal() {
// Seek to the first byte requested.
bool ret = buffer_->Seek(first_offset_);
@@ -413,22 +405,50 @@
start_callback_.reset();
}
+bool BufferedResourceLoader::VerifyPartialResponse(
+ const ResourceLoaderBridge::ResponseInfo& info) {
+ if (info.headers->response_code() != kHttpPartialContent)
+ return false;
+
+ int64 first_byte_position, last_byte_position, instance_size;
+ if (!info.headers->GetContentRange(&first_byte_position,
+ &last_byte_position,
+ &instance_size)) {
+ return false;
+ }
+
+ if (instance_size != kPositionNotSpecified)
+ instance_size_ = instance_size;
+
+ if (first_byte_position_ != -1 &&
+ first_byte_position_ != first_byte_position) {
+ return false;
+ }
+
+ // TODO(hclam): I should also check |last_byte_position|, but since
+ // we will never make such a request that it is ok to leave it unimplemented.
+ return true;
+}
+
//////////////////////////////////////////////////////////////////////////////
// BufferedDataSource, protected
BufferedDataSource::BufferedDataSource(
MessageLoop* render_loop,
webkit_glue::MediaResourceLoaderBridgeFactory* bridge_factory)
: total_bytes_(kPositionNotSpecified),
+ streaming_(false),
bridge_factory_(bridge_factory),
loader_(NULL),
+ initialize_callback_(NULL),
read_callback_(NULL),
read_position_(0),
read_size_(0),
read_buffer_(NULL),
+ initial_response_received_(false),
+ probe_response_received_(false),
intermediate_read_buffer_(new uint8[kInitialReadBufferSize]),
intermediate_read_buffer_size_(kInitialReadBufferSize),
render_loop_(render_loop),
- initialize_callback_(NULL),
stopped_(false) {
}
@@ -500,8 +520,8 @@
return false;
}
-bool BufferedDataSource::IsSeekable() {
- return total_bytes_ != kPositionNotSpecified;
+bool BufferedDataSource::IsStreaming() {
+ return streaming_;
}
/////////////////////////////////////////////////////////////////////////////
@@ -509,13 +529,20 @@
void BufferedDataSource::InitializeTask() {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(!loader_.get());
+ DCHECK(!probe_loader_.get());
- // Creates a new resource loader with the full range.
+ // Creates a new resource loader with the full range and a probe resource
+ // loader. Creates a probe resource loader to make sure the server supports
+ // partial range request.
+ // TODO(hclam): Only request 1 byte for this probe request, it may be useful
+ // that we perform a suffix range request and fetch the index. That way we
+ // can minimize the number of requests made.
loader_.reset(CreateLoader(-1, -1));
+ probe_loader_.reset(CreateLoader(1, 1));
- // And then start the resource request.
- loader_->Start(NewCallback(this,
- &BufferedDataSource::InitializeStartCallback));
+ loader_->Start(NewCallback(this, &BufferedDataSource::InitialStartCallback));
+ probe_loader_->Start(
+ NewCallback(this, &BufferedDataSource::ProbeStartCallback));
}
void BufferedDataSource::ReadTask(
@@ -539,10 +566,12 @@
DCHECK(MessageLoop::current() == render_loop_);
// We just need to stop the loader, so it stops activity.
- if (loader_.get()) {
+ if (loader_.get())
loader_->Stop();
- loader_.reset();
- }
+
+ // If the probe request is still active, stop it too.
+ if (probe_loader_.get())
+ probe_loader_->Stop();
}
void BufferedDataSource::SwapLoaderTask(BufferedResourceLoader* loader) {
@@ -567,8 +596,7 @@
}
// Perform the actual read with BufferedResourceLoader.
- loader_->Read(read_position_, read_size_,
- intermediate_read_buffer_.get(),
+ loader_->Read(read_position_, read_size_, intermediate_read_buffer_.get(),
NewCallback(this, &BufferedDataSource::ReadCallback));
}
@@ -603,7 +631,7 @@
// BufferedDataSource, callback methods.
// These methods are called on the render thread for the events reported by
// BufferedResourceLoader.
-void BufferedDataSource::InitializeStartCallback(int error) {
+void BufferedDataSource::InitialStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
// We need to prevent calling to filter host and running the callback if
@@ -618,30 +646,62 @@
if (stopped_)
return;
- DCHECK(loader_.get());
-
- if (error == net::OK) {
- total_bytes_ = loader_->content_length();
- // TODO(hclam): Figure out what to do when total bytes is not known.
- if (total_bytes_ >= 0) {
- host()->SetTotalBytes(total_bytes_);
-
- // This value governs the range that we can seek to.
- // TODO(hclam): Report the correct value of buffered bytes.
- host()->SetBufferedBytes(total_bytes_);
- }
- } else {
+ if (error != net::OK) {
// TODO(hclam): In case of failure, we can retry several times.
- // Also it might be bad to access host() here.
host()->SetError(media::PIPELINE_ERROR_NETWORK);
-
- // Stops the loader, just to be safe.
+ DCHECK(loader_.get());
+ DCHECK(probe_loader_.get());
loader_->Stop();
+ probe_loader_->Stop();
+ DoneInitialization();
+ return;
}
- DoneInitialization();
+ total_bytes_ = loader_->instance_size();
+ if (total_bytes_ >= 0) {
+ // 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 the server didn't reply with a content length, it is likely this
+ // is a streaming response.
+ streaming_ = true;
+ host()->SetStreaming(true);
+ }
+
+ initial_response_received_ = true;
+ if (probe_response_received_)
+ DoneInitialization();
}
+void BufferedDataSource::ProbeStartCallback(int error) {
+ DCHECK(MessageLoop::current() == render_loop_);
+
+ // 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.
+ AutoLock auto_lock(lock_);
+ if (stopped_)
+ return;
+
+ if (error != net::OK) {
+ streaming_ = true;
+ host()->SetStreaming(true);
+ }
+
+ DCHECK(probe_loader_.get());
+ probe_loader_->Stop();
+ probe_response_received_ = true;
+ if (initial_response_received_)
+ DoneInitialization();
+}
+
void BufferedDataSource::PartialReadStartCallback(int error) {
DCHECK(MessageLoop::current() == render_loop_);
DCHECK(loader_.get());
@@ -651,6 +711,8 @@
// 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
@@ -662,12 +724,7 @@
AutoLock auto_lock(lock_);
if (stopped_)
return;
-
- // TODO(hclam): It may be bad to access host() here.
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
-
- // Kill the loader just to be safe.
- loader_->Stop();
+ DoneRead(net::ERR_INVALID_RESPONSE);
}
}
@@ -693,19 +750,10 @@
// 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.
- // We have the following conditions:
- // 1. Read is beyond the content length of the file (if known).
- // 2. We have tried too many times (TODO here).
- if (read_position_ >= total_bytes_) {
- DoneRead(0);
- return;
- }
-
// TODO(hclam): we need to count how many times it failed to prevent
// excessive trials.
@@ -716,18 +764,11 @@
// 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,
+ NewRunnableMethod(this, &BufferedDataSource::SwapLoaderTask,
CreateLoader(read_position_, -1)));
} else {
- // The read has finished with error.
+ loader_->Stop();
DoneRead(error);
-
- // TODO(hclam): It may be bad to access host() here.
- host()->SetError(media::PIPELINE_ERROR_NETWORK);
-
- // Stops the laoder.
- loader_->Stop();
}
}
« no previous file with comments | « webkit/glue/media/buffered_data_source.h ('k') | webkit/glue/media/buffered_data_source_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698