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