Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(307)

Side by Side Diff: media/base/download_rate_monitor.cc

Issue 8399023: Fire canplaythrough event at the proper time for audio/video (Closed) Base URL: svn://svn.chromium.org/chrome/trunk/src
Patch Set: . Created 9 years, 1 month ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View unified diff | Download patch | Annotate | Revision Log
OLDNEW
(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 DownloadRateMonitor::Sample::Sample(
24 const BufferingPoint& start, const BufferingPoint& end) {
25 Reset();
26 start_ = start;
27 set_end(end);
28 }
29
30 void DownloadRateMonitor::Sample::set_end(const BufferingPoint& new_end) {
31 DCHECK(!start_.timestamp.is_null());
32 DCHECK(new_end.buffered_bytes >= start_.buffered_bytes);
33 DCHECK(new_end.timestamp >= start_.timestamp);
34 end_ = new_end;
35 }
36
37 float DownloadRateMonitor::Sample::bytes_per_second() const {
38 if (seconds_elapsed() > 0.0 && bytes_downloaded() >= 0)
39 return bytes_downloaded() / seconds_elapsed();
40 return -1.0;
41 }
42
43 float DownloadRateMonitor::Sample::seconds_elapsed() const {
44 if (start_.timestamp.is_null() || end_.timestamp.is_null())
45 return -1.0;
46 return (end_.timestamp - start_.timestamp).InSecondsF();
47 }
48
49 int64 DownloadRateMonitor::Sample::bytes_downloaded() const {
50 if (start_.timestamp.is_null() || end_.timestamp.is_null())
51 return -1.0;
52 return end_.buffered_bytes - start_.buffered_bytes;
53 }
54
55 bool DownloadRateMonitor::Sample::is_null() const {
56 return start_.timestamp.is_null() && end_.timestamp.is_null();
57 }
58
59 void DownloadRateMonitor::Sample::Reset() {
60 start_ = BufferingPoint();
61 end_ = BufferingPoint();
62 }
63
64 void DownloadRateMonitor::Sample::RestartAtEndBufferingPoint() {
65 start_ = end_;
66 end_ = BufferingPoint();
67 }
68
69 DownloadRateMonitor::DownloadRateMonitor() {
70 Reset();
71 }
72
73 void DownloadRateMonitor::Start(
74 const CanPlayThroughCB& canplaythrough_cb, int media_bitrate) {
75 DCHECK(stopped_);
76 canplaythrough_cb_ = canplaythrough_cb;
77 stopped_ = false;
78 bitrate_ = media_bitrate;
79 current_sample_.Reset();
80 buffered_bytes_ = 0;
81
82 NotifyCanPlayThroughIfNeeded();
83 }
84
85 void DownloadRateMonitor::SetBufferedBytes(
86 int64 buffered_bytes, const base::Time& timestamp) {
87 if (stopped_)
88 return;
89
90 is_downloading_data_ = true;
91
92 // Check monotonically nondecreasing constraint.
93 base::Time previous_time;
94 if (!current_sample_.is_null())
95 previous_time = current_sample_.end().timestamp;
96 else if (!sample_window_.empty())
97 previous_time = sample_window_.back().end().timestamp;
98
99 // If we go backward in time, dismiss the sample.
100 if (!previous_time.is_null() && timestamp < previous_time)
101 return;
102
103 // If the buffer level has dropped, invalidate current sample.
104 if (buffered_bytes < buffered_bytes_)
105 current_sample_.Reset();
106 buffered_bytes_ = buffered_bytes;
107
108 BufferingPoint latest_point = { buffered_bytes, timestamp };
109 if (current_sample_.is_null())
110 current_sample_ = Sample(latest_point, latest_point);
111 else
112 current_sample_.set_end(latest_point);
113
114 UpdateSampleWindow();
115 NotifyCanPlayThroughIfNeeded();
116 }
117
118 void DownloadRateMonitor::SetNetworkActivity(bool is_downloading_data) {
119 if (is_downloading_data == is_downloading_data_)
120 return;
121 // Invalidate the current sample if downloading is going from start to stopped
122 // or vice versa.
123 current_sample_.Reset();
124 is_downloading_data_ = is_downloading_data;
125 }
126
127 void DownloadRateMonitor::Stop() {
128 stopped_ = true;
129 current_sample_.Reset();
130 buffered_bytes_ = 0;
131 }
132
133 void DownloadRateMonitor::Reset() {
134 canplaythrough_cb_.Reset();
135 has_notified_can_play_through_ = false;
136 current_sample_.Reset();
137 sample_window_.clear();
138 is_downloading_data_ = false;
139 total_bytes_ = 0;
140 buffered_bytes_ = 0;
141 loaded_ = false;
142 bitrate_ = 0;
143 stopped_ = true;
144 }
145
146 int64 DownloadRateMonitor::bytes_downloaded_in_window() const {
147 // There are max |kNumberOfSamples| so we might as well recompute each time.
148 int64 total = 0;
149 for (size_t i = 0; i < sample_window_.size(); ++i)
150 total += sample_window_[i].bytes_downloaded();
151 return total;
152 }
153
154 float DownloadRateMonitor::seconds_elapsed_in_window() const {
155 // There are max |kNumberOfSamples| so we might as well recompute each time.
156 float total = 0.0;
157 for (size_t i = 0; i < sample_window_.size(); ++i)
158 total += sample_window_[i].seconds_elapsed();
159 return total;
160 }
161
162 void DownloadRateMonitor::UpdateSampleWindow() {
163 if (current_sample_.seconds_elapsed() < kSamplePeriod)
164 return;
165
166 // Add latest sample and remove oldest sample.
167 sample_window_.push_back(current_sample_);
168 if (sample_window_.size() > kNumberOfSamples)
169 sample_window_.pop_front();
170
171 // Prepare for next measurement.
172 current_sample_.RestartAtEndBufferingPoint();
173 }
174
175 float DownloadRateMonitor::ApproximateDownloadByteRate() const {
176 // Compute and return the average download byte rate from within the sample
177 // window.
178 // XXX: If the data is arriving really bursty-ly, say getting a big chunk
179 // of data every 5 seconds, then with this implementation it will take 25
180 // seconds until bitrate is calculated ... is that ok?
acolwell GONE FROM CHROMIUM 2011/11/15 21:43:10 nit: Should probably remove the "is that ok?". Cou
vrk (LEFT CHROMIUM) 2011/11/15 22:41:35 Done and yes.
181 if (sample_window_.size() >= kNumberOfSamples &&
182 seconds_elapsed_in_window() > 0.0) {
183 return bytes_downloaded_in_window() / seconds_elapsed_in_window();
184 }
185
186 // Could not determine approximate download byte rate.
187 return -1.0;
188 }
189
190 bool DownloadRateMonitor::ShouldNotifyCanPlayThrough() {
191 // A stream with |total_bytes_| == 0 means the size of the stream cannot be
192 // determined or is undefined in the streaming case. In these scenarios,
193 // CanPlayThrough should never fire.
194 if (has_notified_can_play_through_ || total_bytes_ == 0)
195 return false;
196
197 // If the media is from a local file (|loaded_|) or if all bytes are
198 // buffered, fire CanPlayThrough.
199 if (loaded_ || buffered_bytes_ == total_bytes_)
200 return true;
201
202 // Cannot approximate when the media can play through if bitrate is unknown.
203 if (bitrate_ <= 0)
204 return false;
205
206 float bytes_needed_per_second = bitrate_ / 8;
207 float download_rate = ApproximateDownloadByteRate();
208
209 // If we are downloading at or faster than the media's bitrate, then we can
210 // play through to the end of the media without stopping to buffer.
211 if (download_rate > 0)
212 return download_rate >= bytes_needed_per_second;
213
214 // If download rate is unknown, it may be because the media is being
215 // downloaded so fast that it cannot collect an adequate number of samples
216 // before the download gets deferred.
217 //
218 // To catch this case, we also look at how much data is being downloaded
219 // immediately after the download begins.
220 if (sample_window_.size() < kNumberOfSamples) {
221 int64 bytes_downloaded_since_start =
222 bytes_downloaded_in_window() + current_sample_.bytes_downloaded();
223 float seconds_elapsed_since_start =
224 seconds_elapsed_in_window() + current_sample_.seconds_elapsed();
225
226 // If we download 4 seconds of data in less than 2 seconds of time, we're
227 // probably downloading at a fast enough rate that we can play through.
228 // This is an arbitrary metric that will likely need tweaking.
229 if (seconds_elapsed_since_start < 2.0 &&
230 bytes_downloaded_since_start > 4.0 * bytes_needed_per_second) {
231 return true;
acolwell GONE FROM CHROMIUM 2011/11/15 21:43:10 Does this leave a gap between 2-5 seconds where ca
vrk (LEFT CHROMIUM) 2011/11/15 22:41:35 Talked offline - yes, and this is intentional.
232 }
233 }
234
235 return false;
236 }
237
238 void DownloadRateMonitor::NotifyCanPlayThroughIfNeeded() {
239 if (ShouldNotifyCanPlayThrough() && !canplaythrough_cb_.is_null()) {
240 canplaythrough_cb_.Run();
241 has_notified_can_play_through_ = true;
242 }
243 }
244
245 } // namespace media
OLDNEW

Powered by Google App Engine
This is Rietveld 408576698