Index: content/renderer/media/buffered_data_source.cc |
diff --git a/content/renderer/media/buffered_data_source.cc b/content/renderer/media/buffered_data_source.cc |
deleted file mode 100644 |
index 5fc0c59fb6d92749089d7964621f521d07d3bdc8..0000000000000000000000000000000000000000 |
--- a/content/renderer/media/buffered_data_source.cc |
+++ /dev/null |
@@ -1,536 +0,0 @@ |
-// 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 "content/renderer/media/buffered_data_source.h" |
- |
-#include "base/bind.h" |
-#include "base/callback_helpers.h" |
-#include "base/single_thread_task_runner.h" |
-#include "content/public/common/url_constants.h" |
-#include "media/base/media_log.h" |
-#include "net/base/net_errors.h" |
- |
-using blink::WebFrame; |
- |
-namespace { |
- |
-// BufferedDataSource has an intermediate buffer, this value governs the initial |
-// size of that buffer. It is set to 32KB because this is a typical read size |
-// of FFmpeg. |
-const int kInitialReadBufferSize = 32768; |
- |
-// Number of cache misses we allow for a single Read() before signaling an |
-// error. |
-const int kNumCacheMissRetries = 3; |
- |
-} // namespace |
- |
-namespace content { |
- |
-class BufferedDataSource::ReadOperation { |
- public: |
- ReadOperation(int64 position, int size, uint8* data, |
- const media::DataSource::ReadCB& callback); |
- ~ReadOperation(); |
- |
- // Runs |callback_| with the given |result|, deleting the operation |
- // afterwards. |
- static void Run(scoped_ptr<ReadOperation> read_op, int result); |
- |
- // State for the number of times this read operation has been retried. |
- int retries() { return retries_; } |
- void IncrementRetries() { ++retries_; } |
- |
- int64 position() { return position_; } |
- int size() { return size_; } |
- uint8* data() { return data_; } |
- |
- private: |
- int retries_; |
- |
- const int64 position_; |
- const int size_; |
- uint8* data_; |
- media::DataSource::ReadCB callback_; |
- |
- DISALLOW_IMPLICIT_CONSTRUCTORS(ReadOperation); |
-}; |
- |
-BufferedDataSource::ReadOperation::ReadOperation( |
- int64 position, int size, uint8* data, |
- const media::DataSource::ReadCB& callback) |
- : retries_(0), |
- position_(position), |
- size_(size), |
- data_(data), |
- callback_(callback) { |
- DCHECK(!callback_.is_null()); |
-} |
- |
-BufferedDataSource::ReadOperation::~ReadOperation() { |
- DCHECK(callback_.is_null()); |
-} |
- |
-// static |
-void BufferedDataSource::ReadOperation::Run( |
- scoped_ptr<ReadOperation> read_op, int result) { |
- base::ResetAndReturn(&read_op->callback_).Run(result); |
-} |
- |
-BufferedDataSource::BufferedDataSource( |
- const GURL& url, |
- BufferedResourceLoader::CORSMode cors_mode, |
- const scoped_refptr<base::SingleThreadTaskRunner>& task_runner, |
- WebFrame* frame, |
- media::MediaLog* media_log, |
- BufferedDataSourceHost* host, |
- const DownloadingCB& downloading_cb) |
- : url_(url), |
- cors_mode_(cors_mode), |
- total_bytes_(kPositionNotSpecified), |
- streaming_(false), |
- frame_(frame), |
- intermediate_read_buffer_(new uint8[kInitialReadBufferSize]), |
- intermediate_read_buffer_size_(kInitialReadBufferSize), |
- render_task_runner_(task_runner), |
- stop_signal_received_(false), |
- media_has_played_(false), |
- preload_(AUTO), |
- bitrate_(0), |
- playback_rate_(0.0), |
- media_log_(media_log), |
- host_(host), |
- downloading_cb_(downloading_cb), |
- weak_factory_(this) { |
- DCHECK(host_); |
- DCHECK(!downloading_cb_.is_null()); |
-} |
- |
-BufferedDataSource::~BufferedDataSource() {} |
- |
-// A factory method to create BufferedResourceLoader using the read parameters. |
-// This method can be overridden to inject mock BufferedResourceLoader object |
-// for testing purpose. |
-BufferedResourceLoader* BufferedDataSource::CreateResourceLoader( |
- int64 first_byte_position, int64 last_byte_position) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- |
- BufferedResourceLoader::DeferStrategy strategy = preload_ == METADATA ? |
- BufferedResourceLoader::kReadThenDefer : |
- BufferedResourceLoader::kCapacityDefer; |
- |
- return new BufferedResourceLoader(url_, |
- cors_mode_, |
- first_byte_position, |
- last_byte_position, |
- strategy, |
- bitrate_, |
- playback_rate_, |
- media_log_.get()); |
-} |
- |
-void BufferedDataSource::Initialize(const InitializeCB& init_cb) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- DCHECK(!init_cb.is_null()); |
- DCHECK(!loader_.get()); |
- |
- init_cb_ = init_cb; |
- |
- if (url_.SchemeIsHTTPOrHTTPS()) { |
- // Do an unbounded range request starting at the beginning. If the server |
- // responds with 200 instead of 206 we'll fall back into a streaming mode. |
- loader_.reset(CreateResourceLoader(0, kPositionNotSpecified)); |
- } 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_.reset(CreateResourceLoader(kPositionNotSpecified, |
- kPositionNotSpecified)); |
- } |
- |
- base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr(); |
- loader_->Start( |
- base::Bind(&BufferedDataSource::StartCallback, weak_this), |
- base::Bind(&BufferedDataSource::LoadingStateChangedCallback, weak_this), |
- base::Bind(&BufferedDataSource::ProgressCallback, weak_this), |
- frame_); |
-} |
- |
-void BufferedDataSource::SetPreload(Preload preload) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- preload_ = preload; |
-} |
- |
-bool BufferedDataSource::HasSingleOrigin() { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- DCHECK(init_cb_.is_null() && loader_.get()) |
- << "Initialize() must complete before calling HasSingleOrigin()"; |
- return loader_->HasSingleOrigin(); |
-} |
- |
-bool BufferedDataSource::DidPassCORSAccessCheck() const { |
- return loader_.get() && loader_->DidPassCORSAccessCheck(); |
-} |
- |
-void BufferedDataSource::Abort() { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- { |
- base::AutoLock auto_lock(lock_); |
- StopInternal_Locked(); |
- } |
- StopLoader(); |
- frame_ = NULL; |
-} |
- |
-void BufferedDataSource::MediaPlaybackRateChanged(float playback_rate) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- DCHECK(loader_.get()); |
- |
- if (playback_rate < 0.0f) |
- return; |
- |
- playback_rate_ = playback_rate; |
- loader_->SetPlaybackRate(playback_rate); |
-} |
- |
-void BufferedDataSource::MediaIsPlaying() { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- media_has_played_ = true; |
- UpdateDeferStrategy(false); |
-} |
- |
-void BufferedDataSource::MediaIsPaused() { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- UpdateDeferStrategy(true); |
-} |
- |
-///////////////////////////////////////////////////////////////////////////// |
-// media::DataSource implementation. |
-void BufferedDataSource::Stop() { |
- { |
- base::AutoLock auto_lock(lock_); |
- StopInternal_Locked(); |
- } |
- |
- render_task_runner_->PostTask( |
- FROM_HERE, |
- base::Bind(&BufferedDataSource::StopLoader, weak_factory_.GetWeakPtr())); |
-} |
- |
-void BufferedDataSource::SetBitrate(int bitrate) { |
- render_task_runner_->PostTask(FROM_HERE, |
- base::Bind(&BufferedDataSource::SetBitrateTask, |
- weak_factory_.GetWeakPtr(), |
- bitrate)); |
-} |
- |
-void BufferedDataSource::Read( |
- int64 position, int size, uint8* data, |
- const media::DataSource::ReadCB& read_cb) { |
- DVLOG(1) << "Read: " << position << " offset, " << size << " bytes"; |
- 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(&BufferedDataSource::ReadTask, weak_factory_.GetWeakPtr())); |
-} |
- |
-bool BufferedDataSource::GetSize(int64* size_out) { |
- if (total_bytes_ != kPositionNotSpecified) { |
- *size_out = total_bytes_; |
- return true; |
- } |
- *size_out = 0; |
- return false; |
-} |
- |
-bool BufferedDataSource::IsStreaming() { |
- return streaming_; |
-} |
- |
-///////////////////////////////////////////////////////////////////////////// |
-// Render thread tasks. |
-void BufferedDataSource::ReadTask() { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- ReadInternal(); |
-} |
- |
-void BufferedDataSource::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 BufferedDataSource::StopLoader() { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- |
- if (loader_) |
- loader_->Stop(); |
-} |
- |
-void BufferedDataSource::SetBitrateTask(int bitrate) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- DCHECK(loader_.get()); |
- |
- bitrate_ = bitrate; |
- loader_->SetBitrate(bitrate); |
-} |
- |
-// This method is the place where actual read happens, |loader_| must be valid |
-// prior to make this method call. |
-void BufferedDataSource::ReadInternal() { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- int64 position = 0; |
- int size = 0; |
- { |
- base::AutoLock auto_lock(lock_); |
- if (stop_signal_received_) |
- return; |
- |
- position = read_op_->position(); |
- size = read_op_->size(); |
- } |
- |
- // First we prepare the intermediate read buffer for BufferedResourceLoader |
- // to write to. |
- if (size > intermediate_read_buffer_size_) { |
- intermediate_read_buffer_.reset(new uint8[size]); |
- } |
- |
- // Perform the actual read with BufferedResourceLoader. |
- loader_->Read(position, |
- size, |
- intermediate_read_buffer_.get(), |
- base::Bind(&BufferedDataSource::ReadCallback, |
- weak_factory_.GetWeakPtr())); |
-} |
- |
- |
-///////////////////////////////////////////////////////////////////////////// |
-// BufferedResourceLoader callback methods. |
-void BufferedDataSource::StartCallback( |
- BufferedResourceLoader::Status status) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- DCHECK(loader_.get()); |
- |
- 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_->Stop(); |
- return; |
- } |
- |
- // All responses must be successful. Resources that are assumed to be fully |
- // buffered must have a known content length. |
- bool success = status == BufferedResourceLoader::kOk && |
- (!assume_fully_buffered() || |
- loader_->instance_size() != kPositionNotSpecified); |
- |
- if (success) { |
- total_bytes_ = loader_->instance_size(); |
- streaming_ = |
- !assume_fully_buffered() && |
- (total_bytes_ == kPositionNotSpecified || !loader_->range_supported()); |
- |
- media_log_->SetDoubleProperty("total_bytes", |
- static_cast<double>(total_bytes_)); |
- media_log_->SetBooleanProperty("streaming", streaming_); |
- } else { |
- loader_->Stop(); |
- } |
- |
- // 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_); |
- } |
- |
- media_log_->SetBooleanProperty("single_origin", loader_->HasSingleOrigin()); |
- media_log_->SetBooleanProperty("passed_cors_access_check", |
- loader_->DidPassCORSAccessCheck()); |
- media_log_->SetBooleanProperty("range_header_supported", |
- loader_->range_supported()); |
- } |
- |
- base::ResetAndReturn(&init_cb_).Run(success); |
-} |
- |
-void BufferedDataSource::PartialReadStartCallback( |
- BufferedResourceLoader::Status status) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- DCHECK(loader_.get()); |
- |
- if (status == BufferedResourceLoader::kOk) { |
- // Once the request has started successfully, we can proceed with |
- // reading from it. |
- ReadInternal(); |
- return; |
- } |
- |
- // Stop the resource loader since we have received an error. |
- loader_->Stop(); |
- |
- // 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; |
- ReadOperation::Run(read_op_.Pass(), kReadError); |
-} |
- |
-void BufferedDataSource::ReadCallback( |
- BufferedResourceLoader::Status status, |
- int bytes_read) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- |
- // 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 (status != BufferedResourceLoader::kOk) { |
- // Stop the resource load if it failed. |
- loader_->Stop(); |
- |
- if (status == BufferedResourceLoader::kCacheMiss && |
- read_op_->retries() < kNumCacheMissRetries) { |
- read_op_->IncrementRetries(); |
- |
- // Recreate a loader starting from where we last left off until the |
- // end of the resource. |
- loader_.reset(CreateResourceLoader( |
- read_op_->position(), kPositionNotSpecified)); |
- |
- base::WeakPtr<BufferedDataSource> weak_this = weak_factory_.GetWeakPtr(); |
- loader_->Start( |
- base::Bind(&BufferedDataSource::PartialReadStartCallback, weak_this), |
- base::Bind(&BufferedDataSource::LoadingStateChangedCallback, |
- weak_this), |
- base::Bind(&BufferedDataSource::ProgressCallback, weak_this), |
- frame_); |
- return; |
- } |
- |
- ReadOperation::Run(read_op_.Pass(), kReadError); |
- return; |
- } |
- |
- if (bytes_read > 0) { |
- memcpy(read_op_->data(), intermediate_read_buffer_.get(), bytes_read); |
- } else if (bytes_read == 0 && total_bytes_ == kPositionNotSpecified) { |
- // We've reached the end of the file and we didn't know the total size |
- // before. Update the total size so Read()s past the end of the file will |
- // fail like they would if we had known the file size at the beginning. |
- total_bytes_ = loader_->instance_size(); |
- |
- if (total_bytes_ != kPositionNotSpecified) { |
- host_->SetTotalBytes(total_bytes_); |
- host_->AddBufferedByteRange(loader_->first_byte_position(), |
- total_bytes_); |
- } |
- } |
- ReadOperation::Run(read_op_.Pass(), bytes_read); |
-} |
- |
-void BufferedDataSource::LoadingStateChangedCallback( |
- BufferedResourceLoader::LoadingState state) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- |
- if (assume_fully_buffered()) |
- return; |
- |
- bool is_downloading_data; |
- switch (state) { |
- case BufferedResourceLoader::kLoading: |
- is_downloading_data = true; |
- break; |
- case BufferedResourceLoader::kLoadingDeferred: |
- case BufferedResourceLoader::kLoadingFinished: |
- is_downloading_data = false; |
- break; |
- |
- // TODO(scherkus): we don't signal network activity changes when loads |
- // fail to preserve existing behaviour when deferring is toggled, however |
- // we should consider changing DownloadingCB to also propagate loading |
- // state. For example there isn't any signal today to notify the client that |
- // loading has failed (we only get errors on subsequent reads). |
- case BufferedResourceLoader::kLoadingFailed: |
- return; |
- } |
- |
- downloading_cb_.Run(is_downloading_data); |
-} |
- |
-void BufferedDataSource::ProgressCallback(int64 position) { |
- DCHECK(render_task_runner_->BelongsToCurrentThread()); |
- |
- if (assume_fully_buffered()) |
- return; |
- |
- // 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(loader_->first_byte_position(), position); |
-} |
- |
-void BufferedDataSource::UpdateDeferStrategy(bool paused) { |
- // No need to aggressively buffer when we are assuming the resource is fully |
- // buffered. |
- if (assume_fully_buffered()) { |
- loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer); |
- return; |
- } |
- |
- // If the playback has started (at which point the preload value is ignored) |
- // and we're paused, then try to load as much as possible (the loader will |
- // fall back to kCapacityDefer if it knows the current response won't be |
- // useful from the cache in the future). |
- if (media_has_played_ && paused && loader_->range_supported()) { |
- loader_->UpdateDeferStrategy(BufferedResourceLoader::kNeverDefer); |
- return; |
- } |
- |
- // If media is currently playing or the page indicated preload=auto or the |
- // the server does not support the byte range request or we do not want to go |
- // too far ahead of the read head, use threshold strategy to enable/disable |
- // deferring when the buffer is full/depleted. |
- loader_->UpdateDeferStrategy(BufferedResourceLoader::kCapacityDefer); |
-} |
- |
-} // namespace content |