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

Unified Diff: media/blink/multibuffer_data_source.cc

Issue 1399603003: Tie multibuffers to URLs (Closed) Base URL: https://chromium.googlesource.com/chromium/src.git@media_cache
Patch Set: compile fixes Created 5 years, 2 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
Index: media/blink/multibuffer_data_source.cc
diff --git a/media/blink/multibuffer_data_source.cc b/media/blink/multibuffer_data_source.cc
new file mode 100644
index 0000000000000000000000000000000000000000..c8c8bdbb7def6976ecd6eb146eeff5f7e0eba728
--- /dev/null
+++ b/media/blink/multibuffer_data_source.cc
@@ -0,0 +1,553 @@
+// Copyright 2013 The Chromium Authors. All rights reserved.
+// Use of this source code is governed by a BSD-style license that can be
+// found in the LICENSE file.
+
+#include "media/blink/multibuffer_data_source.h"
+
+#include "base/bind.h"
+#include "base/callback_helpers.h"
+#include "base/location.h"
+#include "base/single_thread_task_runner.h"
+#include "media/base/media_log.h"
+#include "media/blink/multibuffer_reader.h"
+#include "net/base/net_errors.h"
+
+using blink::WebFrame;
+
+namespace {
+
+// Minimum preload buffer.
+const int64 kMinBufferPreload = 2 << 20; // 2 Mb
+// Maxmimum preload buffer.
+const int64 kMaxBufferPreload = 20 << 20; // 20 Mb
+
+// Preload this much extra, then stop preloading until we fall below the
+// kTargetSecondsBufferedAhead.
+const int64 kPreloadHighExtra = 1 << 20; // 1 Mb
+
+// Total size of the pinned region in the cache.
+const int64 kMaxBufferSize = 25 << 20; // 25 Mb
+
+// If bitrate is not known, use this.
+const int64 kDefaultBitrate = 200 * 8 << 10; // 200 Kbps.
+
+// Maximum bitrate for buffer calculations.
+const int64 kMaxBitrate = 20 * 8 << 20; // 20 Mbps.
+
+// Maximum playback rate for buffer calculations.
+const double kMaxPlaybackRate = 25.0;
+
+// Preload this many seconds of data by default.
+const int64 kTargetSecondsBufferedAhead = 10;
+
+// Keep this many seconds of data for going back by default.
+const int64 kTargetSecondsBufferedBehind = 2;
+
+} // namespace
+
+namespace media {
+
+template<typename T>
+T clamp(T value, T min, T max) {
+ return std::max(std::min(value, max), min);
+}
+
+class MultibufferDataSource::ReadOperation {
+ public:
+ ReadOperation(int64 position, int size, uint8* data,
+ const DataSource::ReadCB& callback);
+ ~ReadOperation();
+
+ // Runs |callback_| with the given |result|, deleting the operation
+ // afterwards.
+ static void Run(scoped_ptr<ReadOperation> read_op, int result);
+
+ int64 position() { return position_; }
+ int size() { return size_; }
+ uint8* data() { return data_; }
+
+ private:
+ const int64 position_;
+ const int size_;
+ uint8* data_;
+ DataSource::ReadCB callback_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation);
+};
+
+MultibufferDataSource::ReadOperation::ReadOperation(
+ int64 position, int size, uint8* data,
+ const DataSource::ReadCB& callback)
+ : position_(position),
+ size_(size),
+ data_(data),
+ callback_(callback) {
+ DCHECK(!callback_.is_null());
+}
+
+MultibufferDataSource::ReadOperation::~ReadOperation() {
+ DCHECK(callback_.is_null());
+}
+
+// static
+void MultibufferDataSource::ReadOperation::Run(
+ scoped_ptr<ReadOperation> read_op, int result) {
+ base::ResetAndReturn(&read_op->callback_).Run(result);
+}
+
+MultibufferDataSource::MultibufferDataSource(
+ const GURL& url,
+ UrlData::CORSMode cors_mode,
+ const scoped_refptr<base::SingleThreadTaskRunner>& task_runner,
+ ResourceMultiBuffer* multibuffer,
+ WebFrame* frame,
+ MediaLog* media_log,
+ BufferedDataSourceHost* host,
+ const DownloadingCB& downloading_cb)
+ : cors_mode_(cors_mode),
+ total_bytes_(kPositionNotSpecified),
+ streaming_(false),
+ loading_(false),
+ render_task_runner_(task_runner),
+ multibuffer_(multibuffer),
+ frame_(frame),
+ stop_signal_received_(false),
+ media_has_played_(false),
+ single_origin_(true),
+ cancel_on_defer_(false),
+ preload_(AUTO),
+ bitrate_(0),
+ playback_rate_(0.0),
+ media_log_(media_log),
+ host_(host),
+ downloading_cb_(downloading_cb),
+ weak_factory_(this) {
+ weak_ptr_ = weak_factory_.GetWeakPtr();
+ DCHECK(host_);
+ DCHECK(!downloading_cb_.is_null());
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ url_data_ = multibuffer_->url_index()->GetByUrl(url, cors_mode_);
+ url_data_->Use();
+ DCHECK(url_data_);
+}
+
+MultibufferDataSource::~MultibufferDataSource() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+}
+
+bool MultibufferDataSource::media_has_played() const {
+ return media_has_played_;
+}
+
+bool MultibufferDataSource::assume_fully_buffered() {
+ return !url_data_->url().SchemeIsHTTPOrHTTPS();
+}
+
+// A factory method to create BufferedResourceLoader using the read parameters.
liberato (no reviews please) 2015/10/16 21:50:36 comment is out of date. also, it doesn't create a
hubbe 2015/10/16 23:47:05 Removed the comment.
+void MultibufferDataSource::CreateResourceLoader(
+ int64 first_byte_position, int64 last_byte_position) {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+
+ base::WeakPtr<MultibufferDataSource> weak_this = weak_factory_.GetWeakPtr();
+ loader_.reset(new MultiBufferReader(
+ multibuffer_,
+ destination_url_data_ ? destination_url_data_ : url_data_,
+ first_byte_position,
+ last_byte_position,
+ base::Bind(&MultibufferDataSource::ProgressCallback, weak_this)));
+ UpdateBufferSizes();
+}
+
+void MultibufferDataSource::Initialize(const InitializeCB& init_cb) {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ DCHECK(!init_cb.is_null());
+ DCHECK(!loader_.get());
+
+ init_cb_ = init_cb;
+
+ CreateResourceLoader(0, kPositionNotSpecified);
+
+ base::WeakPtr<MultibufferDataSource> weak_this = weak_factory_.GetWeakPtr();
+
+ // We're not allowed to call Wait() if data is already available.
+ if (loader_->Available()) {
+ render_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&MultibufferDataSource::StartCallback, weak_this));
+ } else {
+ loader_->Wait(1, base::Bind(
+ &MultibufferDataSource::StartCallback, weak_this));
+ }
+ UpdateLoadingState();
+}
+
+void MultibufferDataSource::SetPreload(Preload preload) {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ preload_ = preload;
+ UpdateBufferSizes();
+}
+
+bool MultibufferDataSource::HasSingleOrigin() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ DCHECK(init_cb_.is_null() && loader_.get())
+ << "Initialize() must complete before calling HasSingleOrigin()";
+ return single_origin_;
+}
+
+bool MultibufferDataSource::DidPassCORSAccessCheck() const {
+ if (cors_mode_ == UrlData::kUnspecified)
+ return false;
+ // If init_cb is set, we initialization is not finished yet.
+ if (!init_cb_.is_null())
+ return false;
+ // Loader will be false if there was a failure.
+ if (!loader_)
+ return false;
+ return true;
+}
+
+void MultibufferDataSource::Abort() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ {
+ base::AutoLock auto_lock(lock_);
+ StopInternal_Locked();
+ }
+ StopLoader();
+ frame_ = NULL;
+}
+
+void MultibufferDataSource::MediaPlaybackRateChanged(double playback_rate) {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ DCHECK(loader_.get());
+
+ if (playback_rate < 0.0)
+ return;
+
+ playback_rate_ = playback_rate;
+ cancel_on_defer_ = false;
+ UpdateBufferSizes();
+}
+
+void MultibufferDataSource::MediaIsPlaying() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ media_has_played_ = true;
+ cancel_on_defer_ = false;
+ paused_ = false;
+ preload_ = AUTO;
+ UpdateBufferSizes();
+}
+
+void MultibufferDataSource::MediaIsPaused() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ paused_ = true;
+ UpdateBufferSizes();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// DataSource implementation.
+void MultibufferDataSource::Stop() {
+ {
+ base::AutoLock auto_lock(lock_);
+ StopInternal_Locked();
+ }
+
+ render_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&MultibufferDataSource::StopLoader,
+ weak_factory_.GetWeakPtr()));
+}
+
+void MultibufferDataSource::SetBitrate(int bitrate) {
+ render_task_runner_->PostTask(FROM_HERE,
+ base::Bind(&MultibufferDataSource::SetBitrateTask,
+ weak_factory_.GetWeakPtr(),
+ bitrate));
+}
+
+void MultibufferDataSource::OnBufferingHaveEnough() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ if (loader_ && preload_ == METADATA && !media_has_played_ && !IsStreaming()) {
+ cancel_on_defer_ = true;
+ if (!loading_)
+ loader_.reset(nullptr);
+ }
+}
+
+void MultibufferDataSource::Read(
+ int64 position, int size, uint8* data,
+ const DataSource::ReadCB& read_cb) {
+ DVLOG(1) << "Read: " << position << " offset, " << size << " bytes";
+ // Reading is not allowed until after initialization.
+ DCHECK(init_cb_.is_null());
+ DCHECK(!read_cb.is_null());
+
+ {
+ base::AutoLock auto_lock(lock_);
+ DCHECK(!read_op_);
+
+ if (stop_signal_received_) {
+ read_cb.Run(kReadError);
+ return;
+ }
+
+ read_op_.reset(new ReadOperation(position, size, data, read_cb));
+ }
+
+ render_task_runner_->PostTask(
+ FROM_HERE,
+ base::Bind(&MultibufferDataSource::ReadTask, weak_factory_.GetWeakPtr()));
+}
+
+bool MultibufferDataSource::GetSize(int64* size_out) {
+ if (destination_url_data_) {
+ *size_out = destination_url_data_->length();
+ if (*size_out != kPositionNotSpecified) {
+ return true;
+ }
+ }
+ *size_out = 0;
+ return false;
+}
+
+bool MultibufferDataSource::IsStreaming() {
+ return streaming_;
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// This method is the place where actual read happens,
+void MultibufferDataSource::ReadTask() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+
+ base::AutoLock auto_lock(lock_);
+ int bytes_read = 0;
+ if (stop_signal_received_)
+ return;
+ DCHECK(read_op_);
+ DCHECK(read_op_->size());
+
+ if (!loader_) {
+ CreateResourceLoader(read_op_->position(), kPositionNotSpecified);
+ }
liberato (no reviews please) 2015/10/16 21:50:36 else?
hubbe 2015/10/16 23:47:05 Sure, why not?
+
+ loader_->Seek(read_op_->position());
+
+ int64_t available = loader_->Available();
+ if (available < 0) {
+ // A failure has occured.
+ ReadOperation::Run(read_op_.Pass(), kReadError);
+ return;
+ }
+ if (available) {
+ bytes_read = static_cast<int>(
+ std::min<int64_t>(available, read_op_->size()));
+ bytes_read = loader_->TryRead(read_op_->data(), bytes_read);
+ ReadOperation::Run(read_op_.Pass(), bytes_read);
+ } else {
+ loader_->Wait(1, base::Bind(&MultibufferDataSource::ReadTask,
+ weak_factory_.GetWeakPtr()));
+ UpdateLoadingState();
+ }
+}
+
+void MultibufferDataSource::StopInternal_Locked() {
+ lock_.AssertAcquired();
+ if (stop_signal_received_)
+ return;
+
+ stop_signal_received_ = true;
+
+ // Initialize() isn't part of the DataSource interface so don't call it in
+ // response to Stop().
+ init_cb_.Reset();
+
+ if (read_op_)
+ ReadOperation::Run(read_op_.Pass(), kReadError);
+}
+
+void MultibufferDataSource::StopLoader() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ loader_.reset(nullptr);
+ UpdateLoadingState();
+}
+
+void MultibufferDataSource::SetBitrateTask(int bitrate) {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ DCHECK(loader_.get());
+
+ bitrate_ = bitrate;
+ UpdateBufferSizes();
+}
+
+/////////////////////////////////////////////////////////////////////////////
+// BufferedResourceLoader callback methods.
+void MultibufferDataSource::StartCallback() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ DCHECK(loader_);
+
+ bool init_cb_is_null = false;
+ {
+ base::AutoLock auto_lock(lock_);
+ init_cb_is_null = init_cb_.is_null();
+ }
+ if (init_cb_is_null) {
+ loader_.reset();
+ return;
+ }
+
+ destination_url_data_ = loader_->GetUrlData();
+
+ // All responses must be successful. Resources that are assumed to be fully
+ // buffered must have a known content length.
+ bool success =
+ loader_->Available() > 0 &&
+ destination_url_data_ &&
+ (!assume_fully_buffered() ||
+ destination_url_data_->length() != kPositionNotSpecified);
+
+ if (success) {
+ total_bytes_ = destination_url_data_->length();
+ streaming_ =
+ !assume_fully_buffered() &&
+ (total_bytes_ == kPositionNotSpecified ||
+ !destination_url_data_->range_supported());
+
+ media_log_->SetDoubleProperty("total_bytes",
+ static_cast<double>(total_bytes_));
+ media_log_->SetBooleanProperty("streaming", streaming_);
+ } else {
+ loader_.reset(nullptr);
+ }
+
+ // TODO(scherkus): we shouldn't have to lock to signal host(), see
+ // http://crbug.com/113712 for details.
+ base::AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+
+ if (success) {
+ if (total_bytes_ != kPositionNotSpecified) {
+ host_->SetTotalBytes(total_bytes_);
+ if (assume_fully_buffered())
+ host_->AddBufferedByteRange(0, total_bytes_);
+ }
+
+ // Progress callback might be called after the start callback,
+ // make sure that we update has_single_origin_ now.
liberato (no reviews please) 2015/10/16 21:50:36 single_origin_
hubbe 2015/10/16 23:47:05 Done.
+ UpdateSingleOrigin();
+
+ media_log_->SetBooleanProperty("single_origin", single_origin_);
+ media_log_->SetBooleanProperty("passed_cors_access_check",
+ DidPassCORSAccessCheck());
+ media_log_->SetBooleanProperty("range_header_supported",
+ destination_url_data_->range_supported());
+ }
+
+ UpdateLoadingState();
+ render_task_runner_->PostTask(
+ FROM_HERE, base::Bind(base::ResetAndReturn(&init_cb_), success));
+}
+
+void MultibufferDataSource::UpdateSingleOrigin() {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+ if (loader_ && destination_url_data_) {
+ scoped_refptr<UrlData> new_url_data = loader_->GetUrlData();
+ if (new_url_data && new_url_data != destination_url_data_) {
+ // A redirect has happened.
+ // Check if origin has changed.
+ if (destination_url_data_->url().GetOrigin() !=
+ new_url_data->url().GetOrigin()) {
+ single_origin_ = false;
+ }
+ }
+ }
+}
+
+void MultibufferDataSource::ProgressCallback(int64 begin, int64 end) {
+ DCHECK(render_task_runner_->BelongsToCurrentThread());
+
+ UpdateSingleOrigin();
+ if (assume_fully_buffered())
+ return;
+
+ if (end > begin) {
+ // TODO(scherkus): we shouldn't have to lock to signal host(), see
+ // http://crbug.com/113712 for details.
+ base::AutoLock auto_lock(lock_);
+ if (stop_signal_received_)
+ return;
+
+ host_->AddBufferedByteRange(begin, end);
+ }
+
+ UpdateLoadingState();
+}
+
+void MultibufferDataSource::UpdateLoadingState() {
+ // Update loading state.
+ if ((!!loader_ && loader_->IsLoading()) != loading_) {
liberato (no reviews please) 2015/10/16 21:50:36 extra points to use s/!=/^ :)
hubbe 2015/10/16 23:47:05 Acknowledged.
+ loading_ = !loading_;
+
+ if (!loading_ && cancel_on_defer_) {
+ loader_.reset(nullptr);
+ }
+
+ // Callback could kill us, be sure to call it last.
+ downloading_cb_.Run(loading_);
+ }
+}
+
+void MultibufferDataSource::UpdateBufferSizes() {
+ if (!loader_)
+ return;
+
+ if (!assume_fully_buffered()) {
+ // If the playback has started and we're paused, then try to load as much as
+ // possible, assuming that the file is cacheable. (If not, why bother?)
+ if (media_has_played_ && paused_ &&
+ destination_url_data_ &&
+ destination_url_data_->range_supported() &&
+ destination_url_data_->cacheable()) {
+ loader_->SetPreload(1LL << 40, 1LL << 40); // 1 Tb
+ return;
+ }
+ }
+
+ // Use a default bit rate if unknown and clamp to prevent overflow.
+ int64 bitrate = clamp<int64>(bitrate_, 0, kMaxBitrate);
+ if (bitrate == 0)
+ bitrate = kDefaultBitrate;
+
+ // Only scale the buffer window for playback rates greater than 1.0 in
+ // magnitude and clamp to prevent overflow.
+ bool backward_playback = false;
+ double playback_rate = playback_rate_;
+ if (playback_rate < 0.0) {
+ backward_playback = true;
+ playback_rate *= -1.0;
+ }
+
+ playback_rate = std::max(playback_rate, 1.0);
+ playback_rate = std::min(playback_rate, kMaxPlaybackRate);
+
+ int64 bytes_per_second = (bitrate / 8.0) * playback_rate;
+
+ int64 preload = clamp(kTargetSecondsBufferedAhead * bytes_per_second,
+ kMinBufferPreload,
+ kMaxBufferPreload);
+ int64 back_buffer = clamp(kTargetSecondsBufferedBehind * bytes_per_second,
+ kMinBufferPreload,
+ kMaxBufferPreload);
+ if (backward_playback)
+ std::swap(preload, back_buffer);
+
+ int64 pin_forwards = kMaxBufferSize - back_buffer;
+ DCHECK_LE(preload_ + kPreloadHighExtra, pin_forwards);
+ loader_->SetMaxBuffer(back_buffer, pin_forwards);
+
+ if (preload_ == METADATA) {
+ loader_->SetPreload(0, 0);
+ } else {
+ loader_->SetPreload(preload + kPreloadHighExtra, preload);
+ }
+}
+
+} // namespace media

Powered by Google App Engine
This is Rietveld 408576698