Chromium Code Reviews| 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 |