Index: media/blink/buffered_data_source_host_impl.cc |
diff --git a/media/blink/buffered_data_source_host_impl.cc b/media/blink/buffered_data_source_host_impl.cc |
index 30542adcf37416f2006fd8acb6dbf5eac57d4833..1b4f8723f7ad841c155840f5c651fed603c43159 100644 |
--- a/media/blink/buffered_data_source_host_impl.cc |
+++ b/media/blink/buffered_data_source_host_impl.cc |
@@ -8,9 +8,28 @@ |
namespace media { |
-BufferedDataSourceHostImpl::BufferedDataSourceHostImpl() |
+// We want a relatively small window for estimating bandwidth, |
+// that way we don't need to worry too much about seeks and pause |
+// throwing off the estimates. |
+constexpr base::TimeDelta kDownloadHistoryWindowSeconds = |
+ base::TimeDelta::FromSecondsD(10.0); |
+ |
+// Limit the number of entries in the rate estimator queue. |
+// 1024 entries should be more than enough. |
+constexpr size_t kDownloadHistoryMaxEntries = 1024; |
+ |
+// Just in case someone gives progress one byte at a time, |
+// let's aggregate progress updates together until we reach |
+// at least this many bytes. |
+constexpr int64_t kDownloadHistoryMinBytesPerEntry = 1000; |
+ |
+BufferedDataSourceHostImpl::BufferedDataSourceHostImpl( |
+ base::RepeatingClosure progress_cb, |
+ base::TickClock* tick_clock) |
: total_bytes_(0), |
- did_loading_progress_(false) { } |
+ did_loading_progress_(false), |
+ progress_cb_(std::move(progress_cb)), |
+ tick_clock_(tick_clock) {} |
BufferedDataSourceHostImpl::~BufferedDataSourceHostImpl() { } |
@@ -18,15 +37,56 @@ void BufferedDataSourceHostImpl::SetTotalBytes(int64_t total_bytes) { |
total_bytes_ = total_bytes; |
} |
+int64_t BufferedDataSourceHostImpl::UnloadedBytesInInterval( |
+ const Interval<int64_t>& interval) const { |
+ int64_t bytes = 0; |
+ auto i = buffered_byte_ranges_.find(interval.begin); |
+ while (i != buffered_byte_ranges_.end()) { |
+ if (i.interval_begin() >= interval.end) |
+ break; |
+ if (!i.value()) { |
+ Interval<int64_t> intersection = i.interval().Intersect(interval); |
+ if (!intersection.Empty()) |
+ bytes += intersection.end - intersection.begin; |
+ } |
+ ++i; |
+ } |
+ return bytes; |
+} |
+ |
void BufferedDataSourceHostImpl::AddBufferedByteRange(int64_t start, |
int64_t end) { |
- const auto i = buffered_byte_ranges_.find(start); |
- if (i.value() && i.interval_end() >= end) { |
+ int64_t new_bytes = UnloadedBytesInInterval(Interval<int64_t>(start, end)); |
+ if (new_bytes == 0) { |
// No change |
return; |
} |
buffered_byte_ranges_.SetInterval(start, end, 1); |
did_loading_progress_ = true; |
+ |
+ base::TimeTicks now = tick_clock_->NowTicks(); |
+ int64_t bytes_so_far = 0; |
+ if (!download_history_.empty()) |
+ bytes_so_far = download_history_.back().second; |
+ bytes_so_far += new_bytes; |
+ |
+ // If the difference between the last entry and the second to last entry is |
+ // less than kDownloadHistoryMinBytesPerEntry, just overwrite the last entry. |
+ if (download_history_.size() > 1 && |
+ download_history_.back().second - (download_history_.end() - 2)->second < |
+ kDownloadHistoryMinBytesPerEntry) { |
+ download_history_.back() = std::make_pair(now, bytes_so_far); |
+ } else { |
+ download_history_.emplace_back(now, bytes_so_far); |
+ } |
+ DCHECK_GE(download_history_.size(), 1u); |
+ // Drop entries that are too old. |
+ while (download_history_.size() > kDownloadHistoryMaxEntries || |
+ download_history_.back().first - download_history_.front().first > |
+ kDownloadHistoryWindowSeconds) { |
+ download_history_.pop_front(); |
+ } |
+ progress_cb_.Run(); |
} |
static base::TimeDelta TimeForByteOffset(int64_t byte_offset, |
@@ -66,4 +126,65 @@ bool BufferedDataSourceHostImpl::DidLoadingProgress() { |
return ret; |
} |
+double BufferedDataSourceHostImpl::DownloadRate() const { |
+ // If the download history is really small, any estimate we make is going to |
+ // be wildly inaccurate, so let's not make any estimates until we have more |
+ // data. |
+ if (download_history_.size() < 5) |
+ return 0.0; |
+ |
+ // The data we get is bursty, so we get multiple measuring points very close |
+ // together. These bursts will often lead us to over-estimate the download |
+ // rate. By iterating over the beginning of the time series and picking the |
+ // data point that has the lowest download rate, we avoid over-estimating. |
+ const double kVeryLargeRate = 1.0E20; |
+ double download_rate = kVeryLargeRate; |
+ for (int i = 0; i < std::min<int>(20, download_history_.size() - 3); i++) { |
+ int64_t downloaded_bytes = |
+ download_history_.back().second - download_history_[i].second; |
+ base::TimeTicks now = tick_clock_->NowTicks(); |
+ base::TimeDelta downloaded_seconds = now - download_history_[i].first; |
+ if (downloaded_seconds <= base::TimeDelta()) |
+ continue; |
+ download_rate = std::min( |
+ download_rate, downloaded_bytes / downloaded_seconds.InSecondsF()); |
+ } |
+ return download_rate == kVeryLargeRate ? 0.0 : download_rate; |
+} |
+ |
+bool BufferedDataSourceHostImpl::CanPlayThrough( |
+ base::TimeDelta current_position, |
+ base::TimeDelta media_duration, |
+ double playback_rate) const { |
+ DCHECK_GE(playback_rate, 0); |
+ if (!total_bytes_ || media_duration <= base::TimeDelta() || |
+ media_duration == kInfiniteDuration) { |
+ return false; |
+ } |
+ if (current_position > media_duration) |
+ return true; |
+ double fraction = current_position.InSecondsF() / media_duration.InSecondsF(); |
+ int64_t byte_pos = total_bytes_ * fraction; |
+ if (byte_pos < 0) |
+ byte_pos = 0; |
+ |
+ int64_t unloaded_bytes = |
+ UnloadedBytesInInterval(Interval<int64_t>(byte_pos, total_bytes_)); |
+ |
+ if (unloaded_bytes == 0) |
+ return true; |
+ |
+ double download_rate = DownloadRate(); |
+ if (download_rate <= 0.0) |
+ return false; |
+ |
+ return unloaded_bytes / download_rate < |
+ (media_duration - current_position).InSecondsF() / playback_rate; |
+} |
+ |
+void BufferedDataSourceHostImpl::SetTickClockForTest( |
+ base::TickClock* tick_clock) { |
+ tick_clock_ = tick_clock; |
+} |
+ |
} // namespace media |