OLD | NEW |
1 // Copyright 2014 The Chromium Authors. All rights reserved. | 1 // Copyright 2014 The Chromium Authors. All rights reserved. |
2 // Use of this source code is governed by a BSD-style license that can be | 2 // Use of this source code is governed by a BSD-style license that can be |
3 // found in the LICENSE file. | 3 // found in the LICENSE file. |
4 | 4 |
5 #include "media/blink/buffered_data_source_host_impl.h" | 5 #include "media/blink/buffered_data_source_host_impl.h" |
6 | 6 |
7 #include "media/base/timestamp_constants.h" | 7 #include "media/base/timestamp_constants.h" |
8 | 8 |
9 namespace media { | 9 namespace media { |
10 | 10 |
11 BufferedDataSourceHostImpl::BufferedDataSourceHostImpl() | 11 // We want a relatively small window for estimating bandwidth, |
| 12 // that way we don't need to worry too much about seeks and pause |
| 13 // throwing off the estimates. |
| 14 constexpr base::TimeDelta kDownloadHistoryWindowSeconds = |
| 15 base::TimeDelta::FromSecondsD(10.0); |
| 16 |
| 17 // Limit the number of entries in the rate estimator queue. |
| 18 // 1024 entries should be more than enough. |
| 19 constexpr size_t kDownloadHistoryMaxEntries = 1024; |
| 20 |
| 21 // Just in case someone gives progress one byte at a time, |
| 22 // let's aggregate progress updates together until we reach |
| 23 // at least this many bytes. |
| 24 constexpr int64_t kDownloadHistoryMinBytesPerEntry = 1000; |
| 25 |
| 26 BufferedDataSourceHostImpl::BufferedDataSourceHostImpl( |
| 27 base::RepeatingClosure progress_cb, |
| 28 base::TickClock* tick_clock) |
12 : total_bytes_(0), | 29 : total_bytes_(0), |
13 did_loading_progress_(false) { } | 30 did_loading_progress_(false), |
| 31 progress_cb_(std::move(progress_cb)), |
| 32 tick_clock_(tick_clock) {} |
14 | 33 |
15 BufferedDataSourceHostImpl::~BufferedDataSourceHostImpl() { } | 34 BufferedDataSourceHostImpl::~BufferedDataSourceHostImpl() { } |
16 | 35 |
17 void BufferedDataSourceHostImpl::SetTotalBytes(int64_t total_bytes) { | 36 void BufferedDataSourceHostImpl::SetTotalBytes(int64_t total_bytes) { |
18 total_bytes_ = total_bytes; | 37 total_bytes_ = total_bytes; |
19 } | 38 } |
20 | 39 |
| 40 int64_t BufferedDataSourceHostImpl::UnloadedBytesInInterval( |
| 41 const Interval<int64_t>& interval) const { |
| 42 int64_t bytes = 0; |
| 43 auto i = buffered_byte_ranges_.find(interval.begin); |
| 44 while (i != buffered_byte_ranges_.end()) { |
| 45 if (i.interval_begin() >= interval.end) |
| 46 break; |
| 47 if (!i.value()) { |
| 48 Interval<int64_t> intersection = i.interval().Intersect(interval); |
| 49 if (!intersection.Empty()) |
| 50 bytes += intersection.end - intersection.begin; |
| 51 } |
| 52 ++i; |
| 53 } |
| 54 return bytes; |
| 55 } |
| 56 |
21 void BufferedDataSourceHostImpl::AddBufferedByteRange(int64_t start, | 57 void BufferedDataSourceHostImpl::AddBufferedByteRange(int64_t start, |
22 int64_t end) { | 58 int64_t end) { |
23 const auto i = buffered_byte_ranges_.find(start); | 59 int64_t new_bytes = UnloadedBytesInInterval(Interval<int64_t>(start, end)); |
24 if (i.value() && i.interval_end() >= end) { | 60 if (new_bytes == 0) { |
25 // No change | 61 // No change |
26 return; | 62 return; |
27 } | 63 } |
28 buffered_byte_ranges_.SetInterval(start, end, 1); | 64 buffered_byte_ranges_.SetInterval(start, end, 1); |
29 did_loading_progress_ = true; | 65 did_loading_progress_ = true; |
| 66 |
| 67 base::TimeTicks now = tick_clock_->NowTicks(); |
| 68 int64_t bytes_so_far = 0; |
| 69 if (!download_history_.empty()) |
| 70 bytes_so_far = download_history_.back().second; |
| 71 bytes_so_far += new_bytes; |
| 72 |
| 73 // If the difference between the last entry and the second to last entry is |
| 74 // less than kDownloadHistoryMinBytesPerEntry, just overwrite the last entry. |
| 75 if (download_history_.size() > 1 && |
| 76 download_history_.back().second - (download_history_.end() - 2)->second < |
| 77 kDownloadHistoryMinBytesPerEntry) { |
| 78 download_history_.back() = std::make_pair(now, bytes_so_far); |
| 79 } else { |
| 80 download_history_.emplace_back(now, bytes_so_far); |
| 81 } |
| 82 DCHECK_GE(download_history_.size(), 1u); |
| 83 // Drop entries that are too old. |
| 84 while (download_history_.size() > kDownloadHistoryMaxEntries || |
| 85 download_history_.back().first - download_history_.front().first > |
| 86 kDownloadHistoryWindowSeconds) { |
| 87 download_history_.pop_front(); |
| 88 } |
| 89 progress_cb_.Run(); |
30 } | 90 } |
31 | 91 |
32 static base::TimeDelta TimeForByteOffset(int64_t byte_offset, | 92 static base::TimeDelta TimeForByteOffset(int64_t byte_offset, |
33 int64_t total_bytes, | 93 int64_t total_bytes, |
34 base::TimeDelta duration) { | 94 base::TimeDelta duration) { |
35 double position = static_cast<double>(byte_offset) / total_bytes; | 95 double position = static_cast<double>(byte_offset) / total_bytes; |
36 // Snap to the beginning/end where the approximation can look especially bad. | 96 // Snap to the beginning/end where the approximation can look especially bad. |
37 if (position < 0.01) | 97 if (position < 0.01) |
38 return base::TimeDelta(); | 98 return base::TimeDelta(); |
39 if (position > 0.99) | 99 if (position > 0.99) |
(...skipping 19 matching lines...) Expand all Loading... |
59 } | 119 } |
60 } | 120 } |
61 } | 121 } |
62 | 122 |
63 bool BufferedDataSourceHostImpl::DidLoadingProgress() { | 123 bool BufferedDataSourceHostImpl::DidLoadingProgress() { |
64 bool ret = did_loading_progress_; | 124 bool ret = did_loading_progress_; |
65 did_loading_progress_ = false; | 125 did_loading_progress_ = false; |
66 return ret; | 126 return ret; |
67 } | 127 } |
68 | 128 |
| 129 double BufferedDataSourceHostImpl::DownloadRate() const { |
| 130 // If the download history is really small, any estimate we make is going to |
| 131 // be wildly inaccurate, so let's not make any estimates until we have more |
| 132 // data. |
| 133 if (download_history_.size() < 5) |
| 134 return 0.0; |
| 135 |
| 136 // The data we get is bursty, so we get multiple measuring points very close |
| 137 // together. These bursts will often lead us to over-estimate the download |
| 138 // rate. By iterating over the beginning of the time series and picking the |
| 139 // data point that has the lowest download rate, we avoid over-estimating. |
| 140 const double kVeryLargeRate = 1.0E20; |
| 141 double download_rate = kVeryLargeRate; |
| 142 for (int i = 0; i < std::min<int>(20, download_history_.size() - 3); i++) { |
| 143 int64_t downloaded_bytes = |
| 144 download_history_.back().second - download_history_[i].second; |
| 145 base::TimeTicks now = tick_clock_->NowTicks(); |
| 146 base::TimeDelta downloaded_seconds = now - download_history_[i].first; |
| 147 if (downloaded_seconds <= base::TimeDelta()) |
| 148 continue; |
| 149 download_rate = std::min( |
| 150 download_rate, downloaded_bytes / downloaded_seconds.InSecondsF()); |
| 151 } |
| 152 return download_rate == kVeryLargeRate ? 0.0 : download_rate; |
| 153 } |
| 154 |
| 155 bool BufferedDataSourceHostImpl::CanPlayThrough( |
| 156 base::TimeDelta current_position, |
| 157 base::TimeDelta media_duration, |
| 158 double playback_rate) const { |
| 159 DCHECK_GE(playback_rate, 0); |
| 160 if (!total_bytes_ || media_duration <= base::TimeDelta() || |
| 161 media_duration == kInfiniteDuration) { |
| 162 return false; |
| 163 } |
| 164 if (current_position > media_duration) |
| 165 return true; |
| 166 double fraction = current_position.InSecondsF() / media_duration.InSecondsF(); |
| 167 int64_t byte_pos = total_bytes_ * fraction; |
| 168 if (byte_pos < 0) |
| 169 byte_pos = 0; |
| 170 |
| 171 int64_t unloaded_bytes = |
| 172 UnloadedBytesInInterval(Interval<int64_t>(byte_pos, total_bytes_)); |
| 173 |
| 174 if (unloaded_bytes == 0) |
| 175 return true; |
| 176 |
| 177 double download_rate = DownloadRate(); |
| 178 if (download_rate <= 0.0) |
| 179 return false; |
| 180 |
| 181 return unloaded_bytes / download_rate < |
| 182 (media_duration - current_position).InSecondsF() / playback_rate; |
| 183 } |
| 184 |
| 185 void BufferedDataSourceHostImpl::SetTickClockForTest( |
| 186 base::TickClock* tick_clock) { |
| 187 tick_clock_ = tick_clock; |
| 188 } |
| 189 |
69 } // namespace media | 190 } // namespace media |
OLD | NEW |