OLD | NEW |
---|---|
1 /* | 1 /* |
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. | 2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved. |
3 * | 3 * |
4 * Use of this source code is governed by a BSD-style license | 4 * Use of this source code is governed by a BSD-style license |
5 * that can be found in the LICENSE file in the root of the source | 5 * that can be found in the LICENSE file in the root of the source |
6 * tree. An additional intellectual property rights grant can be found | 6 * tree. An additional intellectual property rights grant can be found |
7 * in the file PATENTS. All contributing project authors may | 7 * in the file PATENTS. All contributing project authors may |
8 * be found in the AUTHORS file in the root of the source tree. | 8 * be found in the AUTHORS file in the root of the source tree. |
9 */ | 9 */ |
10 | 10 |
11 #include "webrtc/video/overuse_frame_detector.h" | 11 #include "webrtc/video/overuse_frame_detector.h" |
12 | 12 |
13 #include <assert.h> | 13 #include <assert.h> |
14 #include <math.h> | 14 #include <math.h> |
15 | 15 |
16 #include <algorithm> | 16 #include <algorithm> |
17 #include <list> | 17 #include <list> |
18 #include <map> | 18 #include <map> |
19 | 19 |
20 #include "webrtc/base/checks.h" | 20 #include "webrtc/base/checks.h" |
21 #include "webrtc/base/exp_filter.h" | 21 #include "webrtc/base/exp_filter.h" |
22 #include "webrtc/base/logging.h" | 22 #include "webrtc/base/logging.h" |
23 #include "webrtc/frame_callback.h" | |
23 #include "webrtc/system_wrappers/include/clock.h" | 24 #include "webrtc/system_wrappers/include/clock.h" |
25 #include "webrtc/video_frame.h" | |
24 | 26 |
25 namespace webrtc { | 27 namespace webrtc { |
26 | 28 |
27 namespace { | 29 namespace { |
28 const int64_t kProcessIntervalMs = 5000; | 30 const int64_t kProcessIntervalMs = 5000; |
29 | 31 |
30 // Delay between consecutive rampups. (Used for quick recovery.) | 32 // Delay between consecutive rampups. (Used for quick recovery.) |
31 const int kQuickRampUpDelayMs = 10 * 1000; | 33 const int kQuickRampUpDelayMs = 10 * 1000; |
32 // Delay between rampup attempts. Initially uses standard, scales up to max. | 34 // Delay between rampup attempts. Initially uses standard, scales up to max. |
33 const int kStandardRampUpDelayMs = 40 * 1000; | 35 const int kStandardRampUpDelayMs = 40 * 1000; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
108 const float kWeightFactorFrameDiff; | 110 const float kWeightFactorFrameDiff; |
109 const float kWeightFactorProcessing; | 111 const float kWeightFactorProcessing; |
110 const float kInitialSampleDiffMs; | 112 const float kInitialSampleDiffMs; |
111 const float kMaxSampleDiffMs; | 113 const float kMaxSampleDiffMs; |
112 uint64_t count_; | 114 uint64_t count_; |
113 const CpuOveruseOptions options_; | 115 const CpuOveruseOptions options_; |
114 rtc::scoped_ptr<rtc::ExpFilter> filtered_processing_ms_; | 116 rtc::scoped_ptr<rtc::ExpFilter> filtered_processing_ms_; |
115 rtc::scoped_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; | 117 rtc::scoped_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; |
116 }; | 118 }; |
117 | 119 |
118 // Class for calculating the processing time of frames. | |
119 class OveruseFrameDetector::FrameQueue { | |
120 public: | |
121 FrameQueue() : last_processing_time_ms_(-1) {} | |
122 ~FrameQueue() {} | |
123 | |
124 // Called when a frame is captured. | |
125 // Starts the measuring of the processing time of the frame. | |
126 void Start(int64_t capture_time, int64_t now) { | |
127 const size_t kMaxSize = 90; // Allows for processing time of 1.5s at 60fps. | |
128 if (frame_times_.size() > kMaxSize) { | |
129 LOG(LS_WARNING) << "Max size reached, removed oldest frame."; | |
130 frame_times_.erase(frame_times_.begin()); | |
131 } | |
132 if (frame_times_.find(capture_time) != frame_times_.end()) { | |
133 // Frame should not exist. | |
134 assert(false); | |
135 return; | |
136 } | |
137 frame_times_[capture_time] = now; | |
138 } | |
139 | |
140 // Called when the processing of a frame has finished. | |
141 // Returns the processing time of the frame. | |
142 int End(int64_t capture_time, int64_t now) { | |
143 std::map<int64_t, int64_t>::iterator it = frame_times_.find(capture_time); | |
144 if (it == frame_times_.end()) { | |
145 return -1; | |
146 } | |
147 // Remove any old frames up to current. | |
148 // Old frames have been skipped by the capture process thread. | |
149 // TODO(asapersson): Consider measuring time from first frame in list. | |
150 last_processing_time_ms_ = now - (*it).second; | |
151 frame_times_.erase(frame_times_.begin(), ++it); | |
152 return last_processing_time_ms_; | |
153 } | |
154 | |
155 void Reset() { frame_times_.clear(); } | |
156 int NumFrames() const { return static_cast<int>(frame_times_.size()); } | |
157 int last_processing_time_ms() const { return last_processing_time_ms_; } | |
158 | |
159 private: | |
160 // Captured frames mapped by the capture time. | |
161 std::map<int64_t, int64_t> frame_times_; | |
162 int last_processing_time_ms_; | |
163 }; | |
164 | |
165 | |
166 OveruseFrameDetector::OveruseFrameDetector( | 120 OveruseFrameDetector::OveruseFrameDetector( |
167 Clock* clock, | 121 Clock* clock, |
168 const CpuOveruseOptions& options, | 122 const CpuOveruseOptions& options, |
169 CpuOveruseObserver* observer, | 123 CpuOveruseObserver* observer, |
124 EncodedFrameObserver* encoder_timing, | |
170 CpuOveruseMetricsObserver* metrics_observer) | 125 CpuOveruseMetricsObserver* metrics_observer) |
171 : options_(options), | 126 : options_(options), |
172 observer_(observer), | 127 observer_(observer), |
128 encoder_timing_(encoder_timing), | |
173 metrics_observer_(metrics_observer), | 129 metrics_observer_(metrics_observer), |
174 clock_(clock), | 130 clock_(clock), |
175 num_process_times_(0), | 131 num_process_times_(0), |
176 last_capture_time_(0), | 132 last_capture_ms_(-1), |
133 last_processed_capture_ms_(-1), | |
177 num_pixels_(0), | 134 num_pixels_(0), |
178 next_process_time_(clock_->TimeInMilliseconds()), | 135 next_process_time_ms_(clock_->TimeInMilliseconds()), |
179 last_overuse_time_(0), | 136 last_overuse_time_ms_(-1), |
180 checks_above_threshold_(0), | 137 checks_above_threshold_(0), |
181 num_overuse_detections_(0), | 138 num_overuse_detections_(0), |
182 last_rampup_time_(0), | 139 last_rampup_time_(0), |
183 in_quick_rampup_(false), | 140 in_quick_rampup_(false), |
184 current_rampup_delay_ms_(kStandardRampUpDelayMs), | 141 current_rampup_delay_ms_(kStandardRampUpDelayMs), |
185 last_sample_time_ms_(0), | 142 usage_(new SendProcessingUsage(options)) { |
186 usage_(new SendProcessingUsage(options)), | |
187 frame_queue_(new FrameQueue()) { | |
188 RTC_DCHECK(metrics_observer != nullptr); | 143 RTC_DCHECK(metrics_observer != nullptr); |
189 // Make sure stats are initially up-to-date. This simplifies unit testing | |
190 // since we don't have to trigger an update using one of the methods which | |
191 // would also alter the overuse state. | |
192 UpdateCpuOveruseMetrics(); | |
193 processing_thread_.DetachFromThread(); | 144 processing_thread_.DetachFromThread(); |
194 } | 145 } |
195 | 146 |
196 OveruseFrameDetector::~OveruseFrameDetector() { | 147 OveruseFrameDetector::~OveruseFrameDetector() { |
197 } | 148 } |
198 | 149 |
199 int OveruseFrameDetector::LastProcessingTimeMs() const { | 150 void OveruseFrameDetector::EncodedFrameTimeMeasured(int encode_duration_ms) { |
200 rtc::CritScope cs(&crit_); | 151 if (!metrics_) |
201 return frame_queue_->last_processing_time_ms(); | 152 metrics_ = rtc::Optional<CpuOveruseMetrics>(CpuOveruseMetrics()); |
202 } | 153 metrics_->encode_usage_percent = usage_->Value(); |
203 | 154 |
204 int OveruseFrameDetector::FramesInQueue() const { | 155 metrics_observer_->OnEncodedFrameTimeMeasured(encode_duration_ms, *metrics_); |
205 rtc::CritScope cs(&crit_); | |
206 return frame_queue_->NumFrames(); | |
207 } | |
208 | |
209 void OveruseFrameDetector::UpdateCpuOveruseMetrics() { | |
210 metrics_.encode_usage_percent = usage_->Value(); | |
211 | |
212 metrics_observer_->CpuOveruseMetricsUpdated(metrics_); | |
213 } | 156 } |
214 | 157 |
215 int64_t OveruseFrameDetector::TimeUntilNextProcess() { | 158 int64_t OveruseFrameDetector::TimeUntilNextProcess() { |
216 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | 159 RTC_DCHECK(processing_thread_.CalledOnValidThread()); |
217 return next_process_time_ - clock_->TimeInMilliseconds(); | 160 return next_process_time_ms_ - clock_->TimeInMilliseconds(); |
218 } | 161 } |
219 | 162 |
220 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { | 163 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { |
221 if (num_pixels != num_pixels_) { | 164 if (num_pixels != num_pixels_) { |
222 return true; | 165 return true; |
223 } | 166 } |
224 return false; | 167 return false; |
225 } | 168 } |
226 | 169 |
227 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { | 170 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { |
228 if (last_capture_time_ == 0) { | 171 if (last_capture_ms_ == -1) |
229 return false; | 172 return false; |
230 } | 173 return (now - last_capture_ms_) > options_.frame_timeout_interval_ms; |
231 return (now - last_capture_time_) > options_.frame_timeout_interval_ms; | |
232 } | 174 } |
233 | 175 |
234 void OveruseFrameDetector::ResetAll(int num_pixels) { | 176 void OveruseFrameDetector::ResetAll(int num_pixels) { |
235 num_pixels_ = num_pixels; | 177 num_pixels_ = num_pixels; |
236 usage_->Reset(); | 178 usage_->Reset(); |
237 frame_queue_->Reset(); | 179 frame_timing_.clear(); |
238 last_capture_time_ = 0; | 180 last_capture_ms_ = -1; |
181 last_processed_capture_ms_ = -1; | |
239 num_process_times_ = 0; | 182 num_process_times_ = 0; |
240 UpdateCpuOveruseMetrics(); | 183 metrics_ = rtc::Optional<CpuOveruseMetrics>(); |
241 } | 184 } |
242 | 185 |
243 void OveruseFrameDetector::FrameCaptured(int width, | 186 void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame) { |
244 int height, | |
245 int64_t capture_time_ms) { | |
246 rtc::CritScope cs(&crit_); | 187 rtc::CritScope cs(&crit_); |
247 | 188 |
248 int64_t now = clock_->TimeInMilliseconds(); | 189 int64_t now = clock_->TimeInMilliseconds(); |
249 if (FrameSizeChanged(width * height) || FrameTimeoutDetected(now)) { | 190 if (FrameSizeChanged(frame.width() * frame.height()) || |
250 ResetAll(width * height); | 191 FrameTimeoutDetected(now)) { |
192 ResetAll(frame.width() * frame.height()); | |
251 } | 193 } |
252 | 194 |
253 if (last_capture_time_ != 0) | 195 if (last_capture_ms_ != 0) |
åsapersson
2016/02/02 15:19:10
!= -1
pbos-webrtc
2016/02/02 15:41:45
Thanks, done!
| |
254 usage_->AddCaptureSample(now - last_capture_time_); | 196 usage_->AddCaptureSample(now - last_capture_ms_); |
255 | 197 |
256 last_capture_time_ = now; | 198 last_capture_ms_ = now; |
257 | 199 |
258 frame_queue_->Start(capture_time_ms, now); | 200 frame_timing_.push_back( |
201 FrameTiming(frame.ntp_time_ms(), frame.timestamp(), now)); | |
259 } | 202 } |
260 | 203 |
261 void OveruseFrameDetector::FrameSent(int64_t capture_time_ms) { | 204 void OveruseFrameDetector::FrameSent(uint32_t timestamp) { |
262 rtc::CritScope cs(&crit_); | 205 rtc::CritScope cs(&crit_); |
263 int delay_ms = frame_queue_->End(capture_time_ms, | 206 // Delay before reporting actual encoding time, used to have the ability to |
264 clock_->TimeInMilliseconds()); | 207 // detect total encoding time when encoding more than one layer. Encoding is |
265 if (delay_ms > 0) { | 208 // here assumed to finish within a second (or that we get enough long-time |
266 AddProcessingTime(delay_ms); | 209 // samples before one second to trigger an overuse even when this is not the |
210 // case). | |
211 static const int64_t kEncodingTimeMeasureWindowMs = 1000; | |
212 int64_t now = clock_->TimeInMilliseconds(); | |
213 for (auto& it : frame_timing_) { | |
214 if (it.timestamp == timestamp) { | |
215 it.last_send_ms = now; | |
216 break; | |
217 } | |
218 } | |
219 // TODO(pbos): Handle the case/log errors when not finding the corresponding | |
220 // frame (either very slow encoding or incorrect wrong timestamps returned | |
221 // from the encoder). | |
222 // This is currently the case for all frames on ChromeOS, so logging them | |
223 // would be spammy, and triggering overuse would be wrong. | |
224 // https://crbug.com/350106 | |
225 while (!frame_timing_.empty()) { | |
226 FrameTiming timing = frame_timing_.front(); | |
227 if (now - timing.capture_ms < kEncodingTimeMeasureWindowMs) | |
228 break; | |
229 if (timing.last_send_ms != -1) { | |
230 int encode_duration_ms = | |
231 static_cast<int>(timing.last_send_ms - timing.capture_ms); | |
232 if (encoder_timing_) { | |
233 encoder_timing_->OnEncodeTiming(timing.capture_ntp_ms, | |
234 encode_duration_ms); | |
235 } | |
236 if (last_processed_capture_ms_ != -1) { | |
237 int64_t diff_ms = timing.capture_ms - last_processed_capture_ms_; | |
238 usage_->AddSample(encode_duration_ms, diff_ms); | |
239 } | |
240 last_processed_capture_ms_ = timing.capture_ms; | |
241 EncodedFrameTimeMeasured(encode_duration_ms); | |
242 } | |
243 frame_timing_.pop_front(); | |
267 } | 244 } |
268 } | 245 } |
269 | 246 |
270 void OveruseFrameDetector::AddProcessingTime(int elapsed_ms) { | |
271 int64_t now = clock_->TimeInMilliseconds(); | |
272 if (last_sample_time_ms_ != 0) { | |
273 int64_t diff_ms = now - last_sample_time_ms_; | |
274 usage_->AddSample(elapsed_ms, diff_ms); | |
275 } | |
276 last_sample_time_ms_ = now; | |
277 UpdateCpuOveruseMetrics(); | |
278 } | |
279 | |
280 int32_t OveruseFrameDetector::Process() { | 247 int32_t OveruseFrameDetector::Process() { |
281 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | 248 RTC_DCHECK(processing_thread_.CalledOnValidThread()); |
282 | 249 |
283 int64_t now = clock_->TimeInMilliseconds(); | 250 int64_t now = clock_->TimeInMilliseconds(); |
284 | 251 |
285 // Used to protect against Process() being called too often. | 252 // Used to protect against Process() being called too often. |
286 if (now < next_process_time_) | 253 if (now < next_process_time_ms_) |
287 return 0; | 254 return 0; |
288 | 255 |
289 next_process_time_ = now + kProcessIntervalMs; | 256 next_process_time_ms_ = now + kProcessIntervalMs; |
290 | 257 |
291 CpuOveruseMetrics current_metrics; | 258 CpuOveruseMetrics current_metrics; |
292 { | 259 { |
293 rtc::CritScope cs(&crit_); | 260 rtc::CritScope cs(&crit_); |
294 ++num_process_times_; | 261 ++num_process_times_; |
262 if (num_process_times_ <= options_.min_process_count || !metrics_) | |
263 return 0; | |
295 | 264 |
296 current_metrics = metrics_; | 265 if (!metrics_) |
297 if (num_process_times_ <= options_.min_process_count) | |
298 return 0; | 266 return 0; |
åsapersson
2016/02/02 15:19:10
checked above
pbos-webrtc
2016/02/02 15:41:45
Done.
| |
267 | |
268 current_metrics = *metrics_; | |
299 } | 269 } |
300 | 270 |
301 if (IsOverusing(current_metrics)) { | 271 if (IsOverusing(current_metrics)) { |
302 // If the last thing we did was going up, and now have to back down, we need | 272 // If the last thing we did was going up, and now have to back down, we need |
303 // to check if this peak was short. If so we should back off to avoid going | 273 // to check if this peak was short. If so we should back off to avoid going |
304 // back and forth between this load, the system doesn't seem to handle it. | 274 // back and forth between this load, the system doesn't seem to handle it. |
305 bool check_for_backoff = last_rampup_time_ > last_overuse_time_; | 275 bool check_for_backoff = last_rampup_time_ > last_overuse_time_ms_; |
åsapersson
2016/02/02 15:19:10
also initialize last_rampup_time_ to -1 (and renam
pbos-webrtc
2016/02/02 15:41:44
Done.
| |
306 if (check_for_backoff) { | 276 if (check_for_backoff) { |
307 if (now - last_rampup_time_ < kStandardRampUpDelayMs || | 277 if (now - last_rampup_time_ < kStandardRampUpDelayMs || |
308 num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) { | 278 num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) { |
309 // Going up was not ok for very long, back off. | 279 // Going up was not ok for very long, back off. |
310 current_rampup_delay_ms_ *= kRampUpBackoffFactor; | 280 current_rampup_delay_ms_ *= kRampUpBackoffFactor; |
311 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs) | 281 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs) |
312 current_rampup_delay_ms_ = kMaxRampUpDelayMs; | 282 current_rampup_delay_ms_ = kMaxRampUpDelayMs; |
313 } else { | 283 } else { |
314 // Not currently backing off, reset rampup delay. | 284 // Not currently backing off, reset rampup delay. |
315 current_rampup_delay_ms_ = kStandardRampUpDelayMs; | 285 current_rampup_delay_ms_ = kStandardRampUpDelayMs; |
316 } | 286 } |
317 } | 287 } |
318 | 288 |
319 last_overuse_time_ = now; | 289 last_overuse_time_ms_ = now; |
320 in_quick_rampup_ = false; | 290 in_quick_rampup_ = false; |
321 checks_above_threshold_ = 0; | 291 checks_above_threshold_ = 0; |
322 ++num_overuse_detections_; | 292 ++num_overuse_detections_; |
323 | 293 |
324 if (observer_ != NULL) | 294 if (observer_ != NULL) |
325 observer_->OveruseDetected(); | 295 observer_->OveruseDetected(); |
326 } else if (IsUnderusing(current_metrics, now)) { | 296 } else if (IsUnderusing(current_metrics, now)) { |
327 last_rampup_time_ = now; | 297 last_rampup_time_ = now; |
328 in_quick_rampup_ = true; | 298 in_quick_rampup_ = true; |
329 | 299 |
(...skipping 25 matching lines...) Expand all Loading... | |
355 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, | 325 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, |
356 int64_t time_now) { | 326 int64_t time_now) { |
357 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 327 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
358 if (time_now < last_rampup_time_ + delay) | 328 if (time_now < last_rampup_time_ + delay) |
359 return false; | 329 return false; |
360 | 330 |
361 return metrics.encode_usage_percent < | 331 return metrics.encode_usage_percent < |
362 options_.low_encode_usage_threshold_percent; | 332 options_.low_encode_usage_threshold_percent; |
363 } | 333 } |
364 } // namespace webrtc | 334 } // namespace webrtc |
OLD | NEW |