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

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
« no previous file with comments | « media/base/download_rate_monitor.h ('k') | media/base/download_rate_monitor_unittest.cc » ('j') | no next file with comments »
Toggle Intra-line Diffs ('i') | Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
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
25 DownloadRateMonitor::Sample::Sample(
26 const BufferingPoint& start, const BufferingPoint& end) {
27 Reset();
28 start_ = start;
29 set_end(end);
30 }
31
32 void DownloadRateMonitor::Sample::set_end(const BufferingPoint& new_end) {
33 DCHECK(!start_.timestamp.is_null());
34 DCHECK(new_end.buffered_bytes >= start_.buffered_bytes);
35 DCHECK(new_end.timestamp >= start_.timestamp);
36 end_ = new_end;
37 }
38
39 float DownloadRateMonitor::Sample::bytes_per_second() const {
40 if (seconds_elapsed() > 0.0 && bytes_downloaded() >= 0)
41 return bytes_downloaded() / seconds_elapsed();
42 return -1.0;
43 }
44
45 float DownloadRateMonitor::Sample::seconds_elapsed() const {
46 if (start_.timestamp.is_null() || end_.timestamp.is_null())
47 return -1.0;
48 return (end_.timestamp - start_.timestamp).InSecondsF();
49 }
50
51 int64 DownloadRateMonitor::Sample::bytes_downloaded() const {
52 if (start_.timestamp.is_null() || end_.timestamp.is_null())
53 return -1.0;
54 return end_.buffered_bytes - start_.buffered_bytes;
55 }
56
57 bool DownloadRateMonitor::Sample::is_null() const {
58 return start_.timestamp.is_null() && end_.timestamp.is_null();
59 }
60
61 void DownloadRateMonitor::Sample::Reset() {
62 start_ = BufferingPoint();
63 end_ = BufferingPoint();
64 }
65
66 void DownloadRateMonitor::Sample::RestartAtEndBufferingPoint() {
67 start_ = end_;
68 end_ = BufferingPoint();
69 }
70
71 DownloadRateMonitor::DownloadRateMonitor() {
72 Reset();
73 }
74
75 void DownloadRateMonitor::Start(
76 const base::Closure& canplaythrough_cb, int media_bitrate) {
77 DCHECK(stopped_);
78 canplaythrough_cb_ = canplaythrough_cb;
79 stopped_ = false;
80 bitrate_ = media_bitrate;
81 current_sample_.Reset();
82 buffered_bytes_ = 0;
83
84 NotifyCanPlayThroughIfNeeded();
85 }
86
87 void DownloadRateMonitor::SetBufferedBytes(
88 int64 buffered_bytes, const base::Time& timestamp) {
89 if (stopped_)
90 return;
91
92 is_downloading_data_ = true;
93
94 // Check monotonically nondecreasing constraint.
95 base::Time previous_time;
96 if (!current_sample_.is_null())
97 previous_time = current_sample_.end().timestamp;
98 else if (!sample_window_.empty())
99 previous_time = sample_window_.back().end().timestamp;
100
101 // If we go backward in time, dismiss the sample.
102 if (!previous_time.is_null() && timestamp < previous_time)
103 return;
104
105 // If the buffer level has dropped, invalidate current sample.
106 if (buffered_bytes < buffered_bytes_)
107 current_sample_.Reset();
108 buffered_bytes_ = buffered_bytes;
109
110 BufferingPoint latest_point = { buffered_bytes, timestamp };
111 if (current_sample_.is_null())
112 current_sample_ = Sample(latest_point, latest_point);
113 else
114 current_sample_.set_end(latest_point);
115
116 UpdateSampleWindow();
117 NotifyCanPlayThroughIfNeeded();
118 }
119
120 void DownloadRateMonitor::SetNetworkActivity(bool is_downloading_data) {
121 if (is_downloading_data == is_downloading_data_)
122 return;
123 // Invalidate the current sample if downloading is going from start to stopped
124 // or vice versa.
125 current_sample_.Reset();
126 is_downloading_data_ = is_downloading_data;
127 }
128
129 void DownloadRateMonitor::Stop() {
130 stopped_ = true;
131 current_sample_.Reset();
132 buffered_bytes_ = 0;
133 }
134
135 void DownloadRateMonitor::Reset() {
136 canplaythrough_cb_.Reset();
137 has_notified_can_play_through_ = false;
138 current_sample_.Reset();
139 sample_window_.clear();
140 is_downloading_data_ = false;
141 total_bytes_ = -1;
142 buffered_bytes_ = 0;
143 loaded_ = false;
144 bitrate_ = 0;
145 stopped_ = true;
146 }
147
148 DownloadRateMonitor::~DownloadRateMonitor() { }
149
150 int64 DownloadRateMonitor::bytes_downloaded_in_window() const {
151 // There are max |kNumberOfSamples| so we might as well recompute each time.
152 int64 total = 0;
153 for (size_t i = 0; i < sample_window_.size(); ++i)
154 total += sample_window_[i].bytes_downloaded();
155 return total;
156 }
157
158 float DownloadRateMonitor::seconds_elapsed_in_window() const {
159 // There are max |kNumberOfSamples| so we might as well recompute each time.
160 float total = 0.0;
161 for (size_t i = 0; i < sample_window_.size(); ++i)
162 total += sample_window_[i].seconds_elapsed();
163 return total;
164 }
165
166 void DownloadRateMonitor::UpdateSampleWindow() {
167 if (current_sample_.seconds_elapsed() < kSamplePeriod)
168 return;
169
170 // Add latest sample and remove oldest sample.
171 sample_window_.push_back(current_sample_);
172 if (sample_window_.size() > kNumberOfSamples)
173 sample_window_.pop_front();
174
175 // Prepare for next measurement.
176 current_sample_.RestartAtEndBufferingPoint();
177 }
178
179 float DownloadRateMonitor::ApproximateDownloadByteRate() const {
180 // Compute and return the average download byte rate from within the sample
181 // window.
182 // NOTE: In the unlikely case where the data is arriving really bursty-ly,
183 // say getting a big chunk of data every 5 seconds, then with this
184 // implementation it will take 25 seconds until bitrate is calculated.
185 if (sample_window_.size() >= kNumberOfSamples &&
186 seconds_elapsed_in_window() > 0.0) {
187 return bytes_downloaded_in_window() / seconds_elapsed_in_window();
188 }
189
190 // Could not determine approximate download byte rate.
191 return -1.0;
192 }
193
194 bool DownloadRateMonitor::ShouldNotifyCanPlayThrough() {
195 if (stopped_)
196 return false;
197
198 // Only notify CanPlayThrough once for now.
199 if (has_notified_can_play_through_)
200 return false;
201
202 // If the media is from a local file (|loaded_|) or if all bytes are
203 // buffered, fire CanPlayThrough.
204 if (loaded_ || buffered_bytes_ == total_bytes_)
205 return true;
206
207 // Cannot approximate when the media can play through if bitrate is unknown.
208 if (bitrate_ <= 0)
209 return false;
210
211 float bytes_needed_per_second = bitrate_ / 8;
212 float download_rate = ApproximateDownloadByteRate();
213
214 // If we are downloading at or faster than the media's bitrate, then we can
215 // play through to the end of the media without stopping to buffer.
216 if (download_rate > 0)
217 return download_rate >= bytes_needed_per_second;
218
219 // If download rate is unknown, it may be because the media is being
220 // downloaded so fast that it cannot collect an adequate number of samples
221 // before the download gets deferred.
222 //
223 // To catch this case, we also look at how much data is being downloaded
224 // immediately after the download begins.
225 if (sample_window_.size() < kNumberOfSamples) {
226 int64 bytes_downloaded_since_start =
227 bytes_downloaded_in_window() + current_sample_.bytes_downloaded();
228 float seconds_elapsed_since_start =
229 seconds_elapsed_in_window() + current_sample_.seconds_elapsed();
230
231 // If we download 4 seconds of data in less than 2 seconds of time, we're
232 // probably downloading at a fast enough rate that we can play through.
233 // This is an arbitrary metric that will likely need tweaking.
234 if (seconds_elapsed_since_start < 2.0 &&
235 bytes_downloaded_since_start > 4.0 * bytes_needed_per_second) {
236 return true;
237 }
238 }
239
240 return false;
241 }
242
243 void DownloadRateMonitor::NotifyCanPlayThroughIfNeeded() {
244 if (ShouldNotifyCanPlayThrough() && !canplaythrough_cb_.is_null()) {
245 canplaythrough_cb_.Run();
246 has_notified_can_play_through_ = true;
247 }
248 }
249
250 } // namespace media
OLDNEW
« no previous file with comments | « media/base/download_rate_monitor.h ('k') | media/base/download_rate_monitor_unittest.cc » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698