| 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
|
|
|