forked from chromium/chromium
-
Notifications
You must be signed in to change notification settings - Fork 0
/
buffered_data_source_host_impl.cc
182 lines (159 loc) · 6.53 KB
/
buffered_data_source_host_impl.cc
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
// Copyright 2014 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 "media/blink/buffered_data_source_host_impl.h"
#include "media/base/timestamp_constants.h"
namespace media {
// 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,
const base::TickClock* tick_clock)
: total_bytes_(0),
did_loading_progress_(false),
progress_cb_(std::move(progress_cb)),
tick_clock_(tick_clock) {}
BufferedDataSourceHostImpl::~BufferedDataSourceHostImpl() = default;
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) {
int64_t new_bytes = UnloadedBytesInInterval(Interval<int64_t>(start, end));
if (new_bytes > 0)
did_loading_progress_ = true;
buffered_byte_ranges_.SetInterval(start, end, 1);
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,
int64_t total_bytes,
base::TimeDelta duration) {
double position = static_cast<double>(byte_offset) / total_bytes;
// Snap to the beginning/end where the approximation can look especially bad.
if (position < 0.01)
return base::TimeDelta();
if (position > 0.99)
return duration;
return base::TimeDelta::FromMilliseconds(
static_cast<int64_t>(position * duration.InMilliseconds()));
}
void BufferedDataSourceHostImpl::AddBufferedTimeRanges(
Ranges<base::TimeDelta>* buffered_time_ranges,
base::TimeDelta media_duration) const {
DCHECK(media_duration != kNoTimestamp);
DCHECK(media_duration != kInfiniteDuration);
if (total_bytes_ && !buffered_byte_ranges_.empty()) {
for (const auto i : buffered_byte_ranges_) {
if (i.second) {
int64_t start = i.first.begin;
int64_t end = i.first.end;
buffered_time_ranges->Add(
TimeForByteOffset(start, total_bytes_, media_duration),
TimeForByteOffset(end, total_bytes_, media_duration));
}
}
}
}
bool BufferedDataSourceHostImpl::DidLoadingProgress() {
bool ret = did_loading_progress_;
did_loading_progress_ = false;
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 download_time = now - download_history_[i].first;
if (download_time <= base::TimeDelta())
continue;
download_rate =
std::min(download_rate, downloaded_bytes / download_time.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;
const int64_t byte_pos =
std::max<int64_t>(total_bytes_ * (current_position / media_duration), 0);
const int64_t unloaded_bytes =
UnloadedBytesInInterval(Interval<int64_t>(byte_pos, total_bytes_));
if (unloaded_bytes == 0)
return true;
double download_rate = DownloadRate();
return (download_rate > 0) &&
((unloaded_bytes / download_rate) <
((media_duration - current_position).InSecondsF() / playback_rate));
}
void BufferedDataSourceHostImpl::SetTickClockForTest(
const base::TickClock* tick_clock) {
tick_clock_ = tick_clock;
}
} // namespace media