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.cc617
1 files changed, 14 insertions, 603 deletions
diff --git a/webkit/glue/media/buffered_data_source.cc b/webkit/glue/media/buffered_data_source.cc
index ec7a3a0..c1aed27 100644
--- a/webkit/glue/media/buffered_data_source.cc
+++ b/webkit/glue/media/buffered_data_source.cc
@@ -2,61 +2,16 @@
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
-#include "base/callback.h"
-#include "base/compiler_specific.h"
-#include "base/format_macros.h"
-#include "base/message_loop.h"
-#include "base/process_util.h"
-#include "base/stl_util-inl.h"
-#include "base/string_number_conversions.h"
-#include "base/string_util.h"
+#include "webkit/glue/media/buffered_data_source.h"
+
#include "media/base/filter_host.h"
-#include "media/base/media_format.h"
-#include "net/base/load_flags.h"
#include "net/base/net_errors.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebKit.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebKitClient.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebString.h"
-#include "third_party/WebKit/WebKit/chromium/public/WebURLError.h"
-#include "webkit/glue/media/buffered_data_source.h"
-#include "webkit/glue/multipart_response_delegate.h"
#include "webkit/glue/webkit_glue.h"
-#include "webkit/glue/webmediaplayer_impl.h"
using WebKit::WebFrame;
-using WebKit::WebString;
-using WebKit::WebURLError;
-using WebKit::WebURLLoader;
-using WebKit::WebURLRequest;
-using WebKit::WebURLResponse;
-using webkit_glue::MultipartResponseDelegate;
namespace {
-const char kHttpScheme[] = "http";
-const char kHttpsScheme[] = "https";
-const char kDataScheme[] = "data";
-const int64 kPositionNotSpecified = -1;
-const int kHttpOK = 200;
-const int kHttpPartialContent = 206;
-
-// Define the number of bytes in a megabyte.
-const size_t kMegabyte = 1024 * 1024;
-
-// Backward capacity of the buffer, by default 2MB.
-const size_t kBackwardCapcity = 2 * kMegabyte;
-
-// Forward capacity of the buffer, by default 10MB.
-const size_t kForwardCapacity = 10 * 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
-// and amount of time for a suitable wait. Now I just make a guess for this
-// number to be 2MB.
-// TODO(hclam): determine a better value for this.
-const int kForwardWaitThreshold = 2 * kMegabyte;
-
// Defines how long we should wait for more data before we declare a connection
// timeout and start a new request.
// TODO(hclam): Set it to 5s, calibrate this value later.
@@ -72,556 +27,10 @@ const int kReadTrials = 3;
// of FFmpeg.
const int kInitialReadBufferSize = 32768;
-// Returns true if |url| operates on HTTP protocol.
-bool IsHttpProtocol(const GURL& url) {
- return url.SchemeIs(kHttpScheme) || url.SchemeIs(kHttpsScheme);
-}
-
-bool IsDataProtocol(const GURL& url) {
- return url.SchemeIs(kDataScheme);
-}
-
-} // namespace
+} // namespace
namespace webkit_glue {
-/////////////////////////////////////////////////////////////////////////////
-// BufferedResourceLoader
-BufferedResourceLoader::BufferedResourceLoader(
- const GURL& url,
- int64 first_byte_position,
- int64 last_byte_position)
- : buffer_(new media::SeekableBuffer(kBackwardCapcity, kForwardCapacity)),
- deferred_(false),
- defer_allowed_(true),
- completed_(false),
- range_requested_(false),
- partial_response_(false),
- url_(url),
- first_byte_position_(first_byte_position),
- last_byte_position_(last_byte_position),
- start_callback_(NULL),
- offset_(0),
- content_length_(kPositionNotSpecified),
- instance_size_(kPositionNotSpecified),
- read_callback_(NULL),
- read_position_(0),
- read_size_(0),
- read_buffer_(NULL),
- first_offset_(0),
- last_offset_(0),
- keep_test_loader_(false) {
-}
-
-BufferedResourceLoader::~BufferedResourceLoader() {
- if (!completed_ && url_loader_.get())
- url_loader_->cancel();
-}
-
-void BufferedResourceLoader::Start(net::CompletionCallback* start_callback,
- NetworkEventCallback* event_callback,
- WebFrame* frame) {
- // Make sure we have not started.
- DCHECK(!start_callback_.get());
- DCHECK(!event_callback_.get());
- DCHECK(start_callback);
- DCHECK(event_callback);
- CHECK(frame);
-
- start_callback_.reset(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_;
- }
-
- // Increment the reference count right before we start the request. This
- // reference will be release when this request has ended.
- AddRef();
-
- // Prepare the request.
- WebURLRequest request(url_);
- request.setTargetType(WebURLRequest::TargetIsMedia);
- 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|
- if (!keep_test_loader_)
- url_loader_.reset(frame->createAssociatedURLLoader());
-
- // Start the resource loading.
- url_loader_->loadAsynchronously(request, this);
-}
-
-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();
-
- if (url_loader_.get()) {
- if (deferred_)
- url_loader_->setDefersLoading(false);
- deferred_ = false;
-
- if (!completed_) {
- url_loader_->cancel();
- completed_ = true;
- }
- }
-}
-
-void BufferedResourceLoader::Read(int64 position,
- int read_size,
- uint8* buffer,
- net::CompletionCallback* read_callback) {
- DCHECK(!read_callback_.get());
- DCHECK(buffer_.get());
- DCHECK(read_callback);
- DCHECK(buffer);
-
- // Save the parameter of reading.
- read_callback_.reset(read_callback);
- read_position_ = position;
- read_size_ = read_size;
- read_buffer_ = buffer;
-
- // If read position is beyond the instance size, we cannot read there.
- if (instance_size_ != kPositionNotSpecified &&
- instance_size_ <= read_position_) {
- DoneRead(0);
- return;
- }
-
- // 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;
- }
-
- // 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);
-}
-
-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;
-}
-
-void BufferedResourceLoader::SetAllowDefer(bool is_allowed) {
- defer_allowed_ = is_allowed;
- DisableDeferIfNeeded();
-}
-
-int64 BufferedResourceLoader::content_length() {
- return content_length_;
-}
-
-int64 BufferedResourceLoader::instance_size() {
- return instance_size_;
-}
-
-bool BufferedResourceLoader::partial_response() {
- return partial_response_;
-}
-
-bool BufferedResourceLoader::network_activity() {
- return !completed_ && !deferred_;
-}
-
-const GURL& BufferedResourceLoader::url() {
- return url_;
-}
-
-void BufferedResourceLoader::SetURLLoaderForTest(WebURLLoader* mock_loader) {
- url_loader_.reset(mock_loader);
- keep_test_loader_ = true;
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// BufferedResourceLoader, WebKit::WebURLLoaderClient implementations.
-void BufferedResourceLoader::willSendRequest(
- WebURLLoader* loader,
- WebURLRequest& newRequest,
- const WebURLResponse& redirectResponse) {
-
- // The load may have been stopped and |start_callback| is destroyed.
- // In this case we shouldn't do anything.
- if (!start_callback_.get()) {
- // Set the url in the request to an invalid value (empty url).
- newRequest.setURL(WebKit::WebURL());
- return;
- }
-
- if (!IsProtocolSupportedForMedia(newRequest.url())) {
- // Set the url in the request to an invalid value (empty url).
- newRequest.setURL(WebKit::WebURL());
- DoneStart(net::ERR_ADDRESS_INVALID);
- Stop();
- return;
- }
-
- url_ = newRequest.url();
-}
-
-void BufferedResourceLoader::didSendData(
- WebURLLoader* loader,
- unsigned long long bytes_sent,
- unsigned long long total_bytes_to_be_sent) {
- NOTIMPLEMENTED();
-}
-
-void BufferedResourceLoader::didReceiveResponse(
- WebURLLoader* loader,
- const WebURLResponse& response) {
-
- // The loader may have been stopped and |start_callback| is destroyed.
- // In this case we shouldn't do anything.
- if (!start_callback_.get())
- return;
-
- // 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
- // response for HTTP and HTTPS protocol.
- if (IsHttpProtocol(url_)) {
- int error = net::OK;
-
- if (response.httpStatusCode() == kHttpPartialContent)
- partial_response_ = true;
-
- if (range_requested_ && partial_response_) {
- // If we have verified the partial response and it is correct, we will
- // return net::OK.
- if (!VerifyPartialResponse(response))
- error = net::ERR_INVALID_RESPONSE;
- } else if (response.httpStatusCode() != kHttpOK) {
- // We didn't request a range but server didn't reply with "200 OK".
- error = net::ERR_FAILED;
- }
-
- if (error != net::OK) {
- DoneStart(error);
- Stop();
- return;
- }
- } else {
- // For any protocol other than HTTP and HTTPS, assume range request is
- // always fulfilled.
- partial_response_ = range_requested_;
- }
-
- // Expected content length can be -1, in that case |content_length_| is
- // not specified and this is a streaming response.
- content_length_ = response.expectedContentLength();
-
- // If we have not requested a range, then the size of the instance is equal
- // to the content length.
- if (!partial_response_)
- instance_size_ = content_length_;
-
- // Calls with a successful response.
- DoneStart(net::OK);
-}
-
-void BufferedResourceLoader::didReceiveData(
- WebURLLoader* loader,
- const char* data,
- int data_length) {
- DCHECK(!completed_);
- DCHECK_GT(data_length, 0);
-
- // If this loader has been stopped, |buffer_| would be destroyed.
- // In this case we shouldn't do anything.
- if (!buffer_.get())
- return;
-
- // Writes more data to |buffer_|.
- buffer_->Append(reinterpret_cast<const uint8*>(data), data_length);
-
- // If there is an active read request, try to fulfill the request.
- if (HasPendingRead() && CanFulfillRead()) {
- ReadInternal();
- } else if (!defer_allowed_) {
- // If we're not allowed to defer, slide the buffer window forward instead
- // of deferring.
- if (buffer_->forward_bytes() > buffer_->forward_capacity()) {
- size_t excess = buffer_->forward_bytes() - buffer_->forward_capacity();
- bool success = buffer_->Seek(excess);
- DCHECK(success);
- offset_ += first_offset_ + excess;
- }
- }
-
- // At last see if the buffer is full and we need to defer the downloading.
- EnableDeferIfNeeded();
-
- // Notify that we have received some data.
- NotifyNetworkEvent();
-}
-
-void BufferedResourceLoader::didDownloadData(
- WebKit::WebURLLoader* loader,
- int dataLength) {
- NOTIMPLEMENTED();
-}
-
-void BufferedResourceLoader::didReceiveCachedMetadata(
- WebURLLoader* loader,
- const char* data,
- int data_length) {
- NOTIMPLEMENTED();
-}
-
-void BufferedResourceLoader::didFinishLoading(
- WebURLLoader* loader,
- double finishTime) {
- DCHECK(!completed_);
- completed_ = true;
-
- // If there is a start callback, calls it.
- if (start_callback_.get()) {
- DoneStart(net::OK);
- }
-
- // If there is a pending read but the request has ended, returns with what
- // we have.
- if (HasPendingRead()) {
- // Make sure we have a valid buffer before we satisfy a read request.
- DCHECK(buffer_.get());
-
- // Try to fulfill with what is in the buffer.
- if (CanFulfillRead())
- ReadInternal();
- else
- DoneRead(net::ERR_CACHE_MISS);
- }
-
- // There must not be any outstanding read request.
- DCHECK(!HasPendingRead());
-
- // Notify that network response is completed.
- NotifyNetworkEvent();
-
- url_loader_.reset();
- Release();
-}
-
-void BufferedResourceLoader::didFail(
- WebURLLoader* loader,
- const WebURLError& error) {
- DCHECK(!completed_);
- completed_ = true;
-
- // If there is a start callback, calls it.
- if (start_callback_.get()) {
- DoneStart(error.reason);
- }
-
- // If there is a pending read but the request failed, return with the
- // reason for the error.
- if (HasPendingRead()) {
- DoneRead(error.reason);
- }
-
- // Notify that network response is completed.
- NotifyNetworkEvent();
-
- url_loader_.reset();
- Release();
-}
-
-/////////////////////////////////////////////////////////////////////////////
-// BufferedResourceLoader, private
-void BufferedResourceLoader::EnableDeferIfNeeded() {
- if (!defer_allowed_)
- return;
-
- if (!deferred_ &&
- buffer_->forward_bytes() >= buffer_->forward_capacity()) {
- deferred_ = true;
-
- if (url_loader_.get())
- url_loader_->setDefersLoading(true);
-
- NotifyNetworkEvent();
- }
-}
-
-void BufferedResourceLoader::DisableDeferIfNeeded() {
- if (deferred_ &&
- (!defer_allowed_ ||
- buffer_->forward_bytes() < buffer_->forward_capacity() / 2)) {
- deferred_ = false;
-
- if (url_loader_.get())
- url_loader_->setDefersLoading(false);
-
- NotifyNetworkEvent();
- }
-}
-
-bool BufferedResourceLoader::CanFulfillRead() {
- // If we are reading too far in the backward direction.
- if (first_offset_ < 0 &&
- first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
- return false;
-
- // If the start offset is too far ahead.
- if (first_offset_ >= static_cast<int>(buffer_->forward_bytes()))
- return false;
-
- // At the point, we verified that first byte requested is within the buffer.
- // If the request has completed, then just returns with what we have now.
- if (completed_)
- return true;
-
- // If the resource request is still active, make sure the whole requested
- // range is covered.
- if (last_offset_ > static_cast<int>(buffer_->forward_bytes()))
- return false;
-
- return true;
-}
-
-bool BufferedResourceLoader::WillFulfillRead() {
- // Reading too far in the backward direction.
- if (first_offset_ < 0 &&
- first_offset_ + static_cast<int>(buffer_->backward_bytes()) < 0)
- return false;
-
- // Try to read too far ahead.
- if (last_offset_ > kForwardWaitThreshold)
- return false;
-
- // The resource request has completed, there's no way we can fulfill the
- // read request.
- if (completed_)
- return false;
-
- return true;
-}
-
-void BufferedResourceLoader::ReadInternal() {
- // Seek to the first byte requested.
- bool ret = buffer_->Seek(first_offset_);
- DCHECK(ret);
-
- // Then do the read.
- int read = static_cast<int>(buffer_->Read(read_buffer_, read_size_));
- offset_ += first_offset_ + read;
-
- // And report with what we have read.
- DoneRead(read);
-}
-
-bool BufferedResourceLoader::VerifyPartialResponse(
- const WebURLResponse& response) {
- int first_byte_position, last_byte_position, instance_size;
-
- if (!MultipartResponseDelegate::ReadContentRanges(response,
- &first_byte_position,
- &last_byte_position,
- &instance_size)) {
- return false;
- }
-
- if (instance_size != kPositionNotSpecified) {
- instance_size_ = instance_size;
- }
-
- if (first_byte_position_ != kPositionNotSpecified &&
- 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;
-}
-
-std::string BufferedResourceLoader::GenerateHeaders(
- int64 first_byte_position,
- int64 last_byte_position) {
- // Construct the value for the range header.
- std::string header;
- if (first_byte_position > kPositionNotSpecified &&
- last_byte_position > kPositionNotSpecified) {
- if (first_byte_position <= last_byte_position) {
- header = base::StringPrintf("bytes=%" PRId64 "-%" PRId64,
- first_byte_position,
- last_byte_position);
- }
- } else if (first_byte_position > kPositionNotSpecified) {
- header = base::StringPrintf("bytes=%" PRId64 "-",
- first_byte_position);
- } else if (last_byte_position > kPositionNotSpecified) {
- NOTIMPLEMENTED() << "Suffix range not implemented";
- }
- return header;
-}
-
-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
BufferedDataSource::BufferedDataSource(
MessageLoop* render_loop,
WebFrame* frame)
@@ -701,7 +110,7 @@ bool BufferedDataSource::IsUrlSupported(const std::string& url) {
GURL gurl(url);
// This data source doesn't support data:// protocol so reject it.
- return IsProtocolSupportedForMedia(gurl) && !IsDataProtocol(gurl);
+ return IsProtocolSupportedForMedia(gurl) && !gurl.SchemeIs(kDataScheme);
}
void BufferedDataSource::Stop(media::FilterCallback* callback) {
@@ -781,7 +190,7 @@ void BufferedDataSource::InitializeTask() {
this,
&BufferedDataSource::WatchDogTask);
- if (IsHttpProtocol(url_)) {
+ if (url_.SchemeIs(kHttpScheme) || url_.SchemeIs(kHttpsScheme)) {
// Fetch only first 1024 bytes as this usually covers the header portion
// of a media file that gives enough information about the codecs, etc.
// This also serve as a probe to determine server capability to serve
@@ -796,7 +205,8 @@ void BufferedDataSource::InitializeTask() {
// 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_ = CreateResourceLoader(-1, -1);
+ loader_ = CreateResourceLoader(kPositionNotSpecified,
+ kPositionNotSpecified);
loader_->Start(
NewCallback(this, &BufferedDataSource::NonHttpInitialStartCallback),
NewCallback(this, &BufferedDataSource::NetworkEventCallback),
@@ -859,7 +269,7 @@ void BufferedDataSource::RestartLoadingTask() {
if (!read_callback_.get())
return;
- loader_ = CreateResourceLoader(read_position_, -1);
+ loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
loader_->SetAllowDefer(!media_is_paused_);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
@@ -892,7 +302,7 @@ void BufferedDataSource::WatchDogTask() {
// Stops the current loader and creates a new resource loader and
// retry the request.
loader_->Stop();
- loader_ = CreateResourceLoader(read_position_, -1);
+ loader_ = CreateResourceLoader(read_position_, kPositionNotSpecified);
loader_->SetAllowDefer(!media_is_paused_);
loader_->Start(
NewCallback(this, &BufferedDataSource::PartialReadStartCallback),
@@ -992,7 +402,8 @@ void BufferedDataSource::HttpInitialStartCallback(int error) {
// Assuming that the Range header was causing the problem. Retry without
// the Range header.
using_range_request_ = false;
- loader_ = CreateResourceLoader(-1, -1);
+ loader_ = CreateResourceLoader(kPositionNotSpecified,
+ kPositionNotSpecified);
loader_->Start(
NewCallback(this, &BufferedDataSource::HttpInitialStartCallback),
NewCallback(this, &BufferedDataSource::NetworkEventCallback),
@@ -1150,10 +561,10 @@ void BufferedDataSource::NetworkEventCallback() {
return;
bool network_activity = loader_->network_activity();
- int64 buffered_last_byte_position = loader_->GetBufferedLastBytePosition();
+ int64 buffered_position = loader_->GetBufferedPosition();
// If we get an unspecified value, return immediately.
- if (buffered_last_byte_position == kPositionNotSpecified)
+ if (buffered_position == kPositionNotSpecified)
return;
// We need to prevent calling to filter host and running the callback if
@@ -1172,7 +583,7 @@ void BufferedDataSource::NetworkEventCallback() {
network_activity_ = network_activity;
host()->SetNetworkActivity(network_activity);
}
- host()->SetBufferedBytes(buffered_last_byte_position + 1);
+ host()->SetBufferedBytes(buffered_position + 1);
}
} // namespace webkit_glue