OLD | NEW |
---|---|
(Empty) | |
1 // Copyright (c) 2011 The Chromium Authors. All rights reserved. | |
2 // Use of this source code is governed by a BSD-style license that can be | |
3 // found in the LICENSE file. | |
4 | |
5 #include "media/base/download_rate_monitor.h" | |
6 | |
7 #include "base/bind.h" | |
8 #include "base/time.h" | |
9 | |
10 namespace media { | |
11 | |
12 // Number of samples to use to collect and average for each measurement of | |
13 // download rate. | |
14 static const size_t kNumberOfSamples = 5; | |
15 | |
16 // Minimum number of seconds represented in a sample period. | |
17 static const float kSamplePeriod = 1.0; | |
18 | |
19 DownloadRateMonitor::Sample::Sample() { | |
20 Reset(); | |
21 } | |
22 | |
23 void DownloadRateMonitor::Sample::SetStartPoint(const Point& new_start) { | |
24 if (!end_.second.is_null()) | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
Change to CHECK based on out of band discussion.
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
I actually got rid of SetStartPoint altogether and
| |
25 DCHECK(new_start <= end_); | |
26 start_ = new_start; | |
27 } | |
28 | |
29 void DownloadRateMonitor::Sample::SetEndPoint(const Point& new_end) { | |
30 if (!start_.second.is_null()) | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
ditto
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
Done.
| |
31 DCHECK(new_end >= start_); | |
32 end_ = new_end; | |
33 } | |
34 | |
35 float DownloadRateMonitor::Sample::bytes_per_second() const { | |
36 if (seconds_elapsed() > 0.0 && bytes_downloaded() > 0) | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
This will hide situations like not getting any byt
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
You're right, we don't want to hide that situation
| |
37 return bytes_downloaded() / seconds_elapsed(); | |
38 return -1.0; | |
39 } | |
40 | |
41 float DownloadRateMonitor::Sample::seconds_elapsed() const { | |
42 if (start_.second.is_null() || end_.second.is_null()) | |
43 return -1.0; | |
44 return (end_.second - start_.second).InSecondsF(); | |
45 } | |
46 | |
47 int64 DownloadRateMonitor::Sample::bytes_downloaded() const { | |
48 return end_.first - start_.first; | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
no is_null() checks? I think this breaks if start_
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
Done.
| |
49 } | |
50 | |
51 bool DownloadRateMonitor::Sample::is_null() const { | |
52 return start_.second.is_null() && end_.second.is_null(); | |
53 } | |
54 | |
55 void DownloadRateMonitor::Sample::Reset() { | |
56 start_ = std::make_pair(0, base::Time()); | |
57 end_ = std::make_pair(0, base::Time()); | |
58 } | |
59 | |
60 void DownloadRateMonitor::Sample::RestartAtEndPoint() { | |
61 start_ = end_; | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
does end_ need to be set to null here?
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
It doesn't *need* to be, but maybe it makes more s
| |
62 } | |
63 | |
64 DownloadRateMonitor::DownloadRateMonitor() | |
65 : has_notified_can_play_through_(false), | |
66 is_downloading_data_(false), | |
67 total_bytes_(0), | |
68 buffered_bytes_(0), | |
69 loaded_(false), | |
70 bitrate_(0), | |
71 started_(false) { | |
72 } | |
73 | |
74 void DownloadRateMonitor::Start( | |
75 const CanPlayThroughCB& canplaythrough_cb, int media_bitrate) { | |
76 canplaythrough_cb_ = canplaythrough_cb; | |
77 started_ = true; | |
78 bitrate_ = media_bitrate; | |
79 current_sample_.Reset(); | |
80 | |
81 NotifyCanPlayThroughIfNeeded(); | |
82 } | |
83 | |
84 void DownloadRateMonitor::SetBufferedBytes( | |
85 int64 buffered_bytes, const base::Time& timestamp) { | |
86 if (!started_) | |
87 return; | |
88 buffered_bytes_ = buffered_bytes; | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
Probably should add a CHECK for your monotonically
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
Done.
| |
89 is_downloading_data_ = true; | |
90 | |
91 // If the current sample's buffering mark is farther along than the incoming, | |
92 // invalidate the current sample. | |
93 if (current_sample_.start_point().first > buffered_bytes) | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
Doesn't this violate your monotonically nondecreas
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
I'll answer this slightly out of order :)
On 2011/
| |
94 current_sample_.Reset(); | |
95 | |
96 Sample::Point latest_point = std::make_pair(buffered_bytes, timestamp); | |
97 if (current_sample_.is_null()) | |
98 current_sample_.SetStartPoint(latest_point); | |
99 current_sample_.SetEndPoint(latest_point); | |
100 | |
101 UpdateSampleWindow(); | |
102 NotifyCanPlayThroughIfNeeded(); | |
103 } | |
104 | |
105 void DownloadRateMonitor::SetNetworkActivity(bool is_downloading_data) { | |
106 // Invalidate the current sample if downloading is going from start to stopped | |
107 // or vice versa. | |
108 if (is_downloading_data != is_downloading_data_) | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
consider reversing condition & early return.
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
Done.
| |
109 current_sample_.Reset(); | |
110 is_downloading_data_ = is_downloading_data; | |
111 } | |
112 | |
113 int64 DownloadRateMonitor::bytes_downloaded_in_window() const { | |
114 // There are max |kNumberOfSamples|; might as well recompute each time. | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
remove ;
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
Done.
| |
115 int64 total = 0; | |
116 for (size_t i = 0; i < sample_window_.size(); ++i) | |
117 total += sample_window_[i].bytes_downloaded(); | |
118 return total; | |
119 } | |
120 | |
121 float DownloadRateMonitor::seconds_elapsed_in_window() const { | |
122 float total = 0.0; | |
123 // There are max |kNumberOfSamples|; might as well recompute each time. | |
124 for (size_t i = 0; i < sample_window_.size(); ++i) | |
125 total += sample_window_[i].seconds_elapsed(); | |
126 return total; | |
127 } | |
128 | |
129 void DownloadRateMonitor::UpdateSampleWindow() { | |
130 if (current_sample_.seconds_elapsed() < kSamplePeriod) | |
131 return; | |
132 | |
133 // Add latest sample and remove oldest sample. | |
134 sample_window_.push_back(current_sample_); | |
135 if (sample_window_.size() > kNumberOfSamples) | |
136 sample_window_.pop_front(); | |
137 | |
138 // Prepare for next measurement. | |
139 current_sample_.RestartAtEndPoint(); | |
140 } | |
141 | |
142 float DownloadRateMonitor::ApproximateDownloadByteRate() const { | |
143 // Compute and return the average download byte rate from within the sample | |
144 // window. | |
145 // XXX: If the data is arriving really bursty-ly, say getting a big chunk | |
scherkus (not reviewing)
2011/11/09 02:55:15
hmm... hard to say whether we'll see that in pract
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
This actually does happen in practice, in the cont
| |
146 // of data every 5 seconds, then with this implementation it will take 25 | |
147 // seconds until bitrate is calculated ... is that ok? | |
148 if (sample_window_.size() >= kNumberOfSamples) { | |
149 DCHECK(seconds_elapsed_in_window() > 0.0); | |
150 if (seconds_elapsed_in_window() > 0.0) | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
I don't think we need the DCHECK AND the if().
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
Without the if, there might be a divide-by-0 in Re
| |
151 return bytes_downloaded_in_window() / seconds_elapsed_in_window(); | |
152 } | |
153 | |
154 // Could not determine approximate download byte rate. | |
155 return -1.0; | |
156 } | |
157 | |
158 bool DownloadRateMonitor::ShouldNotifyCanPlayThrough() { | |
159 // A stream with |total_bytes_| == 0 means the size of the stream cannot be | |
160 // determined or is undefined in the streaming case. In these scenarios, | |
161 // CanPlayThrough should never fire. | |
162 if (has_notified_can_play_through_ || total_bytes_ == 0) | |
acolwell GONE FROM CHROMIUM
2011/11/09 00:50:22
so does this mean that autoplay can't be implement
vrk (LEFT CHROMIUM)
2011/11/11 02:51:06
What it means is, one wouldn't be able to implemen
| |
163 return false; | |
164 | |
165 // If the media is from a local file (|loaded_|) or if all bytes are | |
166 // buffered, fire CanPlayThrough. | |
167 if (loaded_ || buffered_bytes_ == total_bytes_) | |
168 return true; | |
169 | |
170 // Cannot approximate when the media can play through if bitrate is unknown. | |
171 if (bitrate_ <= 0) | |
172 return false; | |
173 | |
174 float bytes_needed_per_second = bitrate_ / 8; | |
175 float download_rate = ApproximateDownloadByteRate(); | |
176 | |
177 // If we are downloading at or faster than the media's bitrate, then we can | |
178 // play through to the end of the media without stopping to buffer. | |
179 if (download_rate > 0) | |
180 return download_rate > bytes_needed_per_second; | |
181 | |
182 // If download rate is unknown, it may be because the media is being | |
183 // downloaded so fast that it cannot collect an adequate number of samples | |
184 // before the download gets deferred. | |
185 // | |
186 // To catch this case, we also look at how much data is being downloaded | |
187 // immediately after the download begins. | |
188 if (sample_window_.size() < kNumberOfSamples) { | |
189 int64 bytes_downloaded_since_start = | |
190 bytes_downloaded_in_window() + current_sample_.bytes_downloaded(); | |
191 float seconds_elapsed_since_start = | |
192 seconds_elapsed_in_window() + current_sample_.seconds_elapsed(); | |
193 | |
194 // If we download 4 seconds of data in less than 2 seconds of time, we're | |
195 // probably downloading at a fast enough rate that we can play through. | |
196 // This is an arbitrary metric that will likely need tweaking. | |
197 if (seconds_elapsed_since_start < 2.0 && | |
198 bytes_downloaded_since_start > 4.0 * bytes_needed_per_second) { | |
199 return true; | |
200 } | |
201 } | |
202 | |
203 return false; | |
204 } | |
205 | |
206 void DownloadRateMonitor::NotifyCanPlayThroughIfNeeded() { | |
207 if (ShouldNotifyCanPlayThrough() && !canplaythrough_cb_.is_null()) { | |
208 canplaythrough_cb_.Run(); | |
209 has_notified_can_play_through_ = true; | |
210 } | |
211 } | |
212 | |
213 } // namespace media | |
OLD | NEW |