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 |
(...skipping 13 matching lines...) Expand all Loading... |
24 #include "webrtc/system_wrappers/include/clock.h" | 24 #include "webrtc/system_wrappers/include/clock.h" |
25 #include "webrtc/video_frame.h" | 25 #include "webrtc/video_frame.h" |
26 | 26 |
27 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 27 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
28 #include <mach/mach.h> | 28 #include <mach/mach.h> |
29 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 29 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
30 | 30 |
31 namespace webrtc { | 31 namespace webrtc { |
32 | 32 |
33 namespace { | 33 namespace { |
34 const int64_t kProcessIntervalMs = 5000; | 34 const int64_t kCheckForOveruseIntervalMs = 5000; |
| 35 const int64_t kTimeToFirstCheckForOveruseMs = 100; |
35 | 36 |
36 // Delay between consecutive rampups. (Used for quick recovery.) | 37 // Delay between consecutive rampups. (Used for quick recovery.) |
37 const int kQuickRampUpDelayMs = 10 * 1000; | 38 const int kQuickRampUpDelayMs = 10 * 1000; |
38 // Delay between rampup attempts. Initially uses standard, scales up to max. | 39 // Delay between rampup attempts. Initially uses standard, scales up to max. |
39 const int kStandardRampUpDelayMs = 40 * 1000; | 40 const int kStandardRampUpDelayMs = 40 * 1000; |
40 const int kMaxRampUpDelayMs = 240 * 1000; | 41 const int kMaxRampUpDelayMs = 240 * 1000; |
41 // Expontential back-off factor, to prevent annoying up-down behaviour. | 42 // Expontential back-off factor, to prevent annoying up-down behaviour. |
42 const double kRampUpBackoffFactor = 2.0; | 43 const double kRampUpBackoffFactor = 2.0; |
43 | 44 |
44 // Max number of overuses detected before always applying the rampup delay. | 45 // Max number of overuses detected before always applying the rampup delay. |
(...skipping 118 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
163 const float kWeightFactorFrameDiff; | 164 const float kWeightFactorFrameDiff; |
164 const float kWeightFactorProcessing; | 165 const float kWeightFactorProcessing; |
165 const float kInitialSampleDiffMs; | 166 const float kInitialSampleDiffMs; |
166 const float kMaxSampleDiffMs; | 167 const float kMaxSampleDiffMs; |
167 uint64_t count_; | 168 uint64_t count_; |
168 const CpuOveruseOptions options_; | 169 const CpuOveruseOptions options_; |
169 std::unique_ptr<rtc::ExpFilter> filtered_processing_ms_; | 170 std::unique_ptr<rtc::ExpFilter> filtered_processing_ms_; |
170 std::unique_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; | 171 std::unique_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; |
171 }; | 172 }; |
172 | 173 |
| 174 class OveruseFrameDetector::CheckOveruseTask : public rtc::QueuedTask { |
| 175 public: |
| 176 explicit CheckOveruseTask(OveruseFrameDetector* overuse_detector) |
| 177 : overuse_detector_(overuse_detector) { |
| 178 rtc::TaskQueue::Current()->PostDelayedTask( |
| 179 std::unique_ptr<rtc::QueuedTask>(this), kTimeToFirstCheckForOveruseMs); |
| 180 } |
| 181 |
| 182 void Stop() { |
| 183 RTC_CHECK(task_checker_.CalledSequentially()); |
| 184 overuse_detector_ = nullptr; |
| 185 } |
| 186 |
| 187 private: |
| 188 bool Run() override { |
| 189 RTC_CHECK(task_checker_.CalledSequentially()); |
| 190 if (!overuse_detector_) |
| 191 return true; // This will make the task queue delete this task. |
| 192 overuse_detector_->CheckForOveruse(); |
| 193 |
| 194 rtc::TaskQueue::Current()->PostDelayedTask( |
| 195 std::unique_ptr<rtc::QueuedTask>(this), kCheckForOveruseIntervalMs); |
| 196 // Return false to prevent this task from being deleted. Ownership has been |
| 197 // transferred to the task queue when PostDelayedTask was called. |
| 198 return false; |
| 199 } |
| 200 rtc::SequencedTaskChecker task_checker_; |
| 201 OveruseFrameDetector* overuse_detector_; |
| 202 }; |
| 203 |
173 OveruseFrameDetector::OveruseFrameDetector( | 204 OveruseFrameDetector::OveruseFrameDetector( |
174 Clock* clock, | 205 Clock* clock, |
175 const CpuOveruseOptions& options, | 206 const CpuOveruseOptions& options, |
176 CpuOveruseObserver* observer, | 207 CpuOveruseObserver* observer, |
177 EncodedFrameObserver* encoder_timing, | 208 EncodedFrameObserver* encoder_timing, |
178 CpuOveruseMetricsObserver* metrics_observer) | 209 CpuOveruseMetricsObserver* metrics_observer) |
179 : options_(options), | 210 : check_overuse_task_(nullptr), |
| 211 options_(options), |
180 observer_(observer), | 212 observer_(observer), |
181 encoder_timing_(encoder_timing), | 213 encoder_timing_(encoder_timing), |
182 metrics_observer_(metrics_observer), | 214 metrics_observer_(metrics_observer), |
183 clock_(clock), | 215 clock_(clock), |
184 num_process_times_(0), | 216 num_process_times_(0), |
185 last_capture_time_ms_(-1), | 217 last_capture_time_ms_(-1), |
186 last_processed_capture_time_ms_(-1), | 218 last_processed_capture_time_ms_(-1), |
187 num_pixels_(0), | 219 num_pixels_(0), |
188 next_process_time_ms_(clock_->TimeInMilliseconds()), | |
189 last_overuse_time_ms_(-1), | 220 last_overuse_time_ms_(-1), |
190 checks_above_threshold_(0), | 221 checks_above_threshold_(0), |
191 num_overuse_detections_(0), | 222 num_overuse_detections_(0), |
192 last_rampup_time_ms_(-1), | 223 last_rampup_time_ms_(-1), |
193 in_quick_rampup_(false), | 224 in_quick_rampup_(false), |
194 current_rampup_delay_ms_(kStandardRampUpDelayMs), | 225 current_rampup_delay_ms_(kStandardRampUpDelayMs), |
195 usage_(new SendProcessingUsage(options)) { | 226 usage_(new SendProcessingUsage(options)) { |
196 processing_thread_.DetachFromThread(); | 227 task_checker_.Detach(); |
197 } | 228 } |
198 | 229 |
199 OveruseFrameDetector::~OveruseFrameDetector() { | 230 OveruseFrameDetector::~OveruseFrameDetector() { |
| 231 RTC_DCHECK(!check_overuse_task_) << "StopCheckForOverUse must be called."; |
| 232 } |
| 233 |
| 234 void OveruseFrameDetector::StartCheckForOveruse() { |
| 235 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 236 RTC_DCHECK(!check_overuse_task_); |
| 237 check_overuse_task_ = new CheckOveruseTask(this); |
| 238 } |
| 239 void OveruseFrameDetector::StopCheckForOveruse() { |
| 240 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 241 check_overuse_task_->Stop(); |
| 242 check_overuse_task_ = nullptr; |
200 } | 243 } |
201 | 244 |
202 void OveruseFrameDetector::EncodedFrameTimeMeasured(int encode_duration_ms) { | 245 void OveruseFrameDetector::EncodedFrameTimeMeasured(int encode_duration_ms) { |
| 246 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
203 if (!metrics_) | 247 if (!metrics_) |
204 metrics_ = rtc::Optional<CpuOveruseMetrics>(CpuOveruseMetrics()); | 248 metrics_ = rtc::Optional<CpuOveruseMetrics>(CpuOveruseMetrics()); |
205 metrics_->encode_usage_percent = usage_->Value(); | 249 metrics_->encode_usage_percent = usage_->Value(); |
206 | 250 |
207 metrics_observer_->OnEncodedFrameTimeMeasured(encode_duration_ms, *metrics_); | 251 metrics_observer_->OnEncodedFrameTimeMeasured(encode_duration_ms, *metrics_); |
208 } | 252 } |
209 | 253 |
210 int64_t OveruseFrameDetector::TimeUntilNextProcess() { | |
211 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | |
212 return next_process_time_ms_ - clock_->TimeInMilliseconds(); | |
213 } | |
214 | |
215 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { | 254 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { |
| 255 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
216 if (num_pixels != num_pixels_) { | 256 if (num_pixels != num_pixels_) { |
217 return true; | 257 return true; |
218 } | 258 } |
219 return false; | 259 return false; |
220 } | 260 } |
221 | 261 |
222 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { | 262 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { |
| 263 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
223 if (last_capture_time_ms_ == -1) | 264 if (last_capture_time_ms_ == -1) |
224 return false; | 265 return false; |
225 return (now - last_capture_time_ms_) > options_.frame_timeout_interval_ms; | 266 return (now - last_capture_time_ms_) > options_.frame_timeout_interval_ms; |
226 } | 267 } |
227 | 268 |
228 void OveruseFrameDetector::ResetAll(int num_pixels) { | 269 void OveruseFrameDetector::ResetAll(int num_pixels) { |
| 270 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
229 num_pixels_ = num_pixels; | 271 num_pixels_ = num_pixels; |
230 usage_->Reset(); | 272 usage_->Reset(); |
231 frame_timing_.clear(); | 273 frame_timing_.clear(); |
232 last_capture_time_ms_ = -1; | 274 last_capture_time_ms_ = -1; |
233 last_processed_capture_time_ms_ = -1; | 275 last_processed_capture_time_ms_ = -1; |
234 num_process_times_ = 0; | 276 num_process_times_ = 0; |
235 metrics_ = rtc::Optional<CpuOveruseMetrics>(); | 277 metrics_ = rtc::Optional<CpuOveruseMetrics>(); |
236 } | 278 } |
237 | 279 |
238 void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame) { | 280 void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame, |
239 rtc::CritScope cs(&crit_); | 281 int64_t time_when_first_seen_ms) { |
| 282 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
240 | 283 |
241 int64_t now = clock_->TimeInMilliseconds(); | |
242 if (FrameSizeChanged(frame.width() * frame.height()) || | 284 if (FrameSizeChanged(frame.width() * frame.height()) || |
243 FrameTimeoutDetected(now)) { | 285 FrameTimeoutDetected(time_when_first_seen_ms)) { |
244 ResetAll(frame.width() * frame.height()); | 286 ResetAll(frame.width() * frame.height()); |
245 } | 287 } |
246 | 288 |
247 if (last_capture_time_ms_ != -1) | 289 if (last_capture_time_ms_ != -1) |
248 usage_->AddCaptureSample(now - last_capture_time_ms_); | 290 usage_->AddCaptureSample(time_when_first_seen_ms - last_capture_time_ms_); |
249 | 291 |
250 last_capture_time_ms_ = now; | 292 last_capture_time_ms_ = time_when_first_seen_ms; |
251 | 293 |
252 frame_timing_.push_back( | 294 frame_timing_.push_back(FrameTiming(frame.ntp_time_ms(), frame.timestamp(), |
253 FrameTiming(frame.ntp_time_ms(), frame.timestamp(), now)); | 295 time_when_first_seen_ms)); |
254 } | 296 } |
255 | 297 |
256 void OveruseFrameDetector::FrameSent(uint32_t timestamp) { | 298 void OveruseFrameDetector::FrameSent(uint32_t timestamp, |
257 rtc::CritScope cs(&crit_); | 299 int64_t time_sent_in_ms) { |
| 300 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
258 // Delay before reporting actual encoding time, used to have the ability to | 301 // Delay before reporting actual encoding time, used to have the ability to |
259 // detect total encoding time when encoding more than one layer. Encoding is | 302 // detect total encoding time when encoding more than one layer. Encoding is |
260 // here assumed to finish within a second (or that we get enough long-time | 303 // here assumed to finish within a second (or that we get enough long-time |
261 // samples before one second to trigger an overuse even when this is not the | 304 // samples before one second to trigger an overuse even when this is not the |
262 // case). | 305 // case). |
263 static const int64_t kEncodingTimeMeasureWindowMs = 1000; | 306 static const int64_t kEncodingTimeMeasureWindowMs = 1000; |
264 int64_t now = clock_->TimeInMilliseconds(); | |
265 for (auto& it : frame_timing_) { | 307 for (auto& it : frame_timing_) { |
266 if (it.timestamp == timestamp) { | 308 if (it.timestamp == timestamp) { |
267 it.last_send_ms = now; | 309 it.last_send_ms = time_sent_in_ms; |
268 break; | 310 break; |
269 } | 311 } |
270 } | 312 } |
271 // TODO(pbos): Handle the case/log errors when not finding the corresponding | 313 // TODO(pbos): Handle the case/log errors when not finding the corresponding |
272 // frame (either very slow encoding or incorrect wrong timestamps returned | 314 // frame (either very slow encoding or incorrect wrong timestamps returned |
273 // from the encoder). | 315 // from the encoder). |
274 // This is currently the case for all frames on ChromeOS, so logging them | 316 // This is currently the case for all frames on ChromeOS, so logging them |
275 // would be spammy, and triggering overuse would be wrong. | 317 // would be spammy, and triggering overuse would be wrong. |
276 // https://crbug.com/350106 | 318 // https://crbug.com/350106 |
277 while (!frame_timing_.empty()) { | 319 while (!frame_timing_.empty()) { |
278 FrameTiming timing = frame_timing_.front(); | 320 FrameTiming timing = frame_timing_.front(); |
279 if (now - timing.capture_ms < kEncodingTimeMeasureWindowMs) | 321 if (time_sent_in_ms - timing.capture_ms < kEncodingTimeMeasureWindowMs) |
280 break; | 322 break; |
281 if (timing.last_send_ms != -1) { | 323 if (timing.last_send_ms != -1) { |
282 int encode_duration_ms = | 324 int encode_duration_ms = |
283 static_cast<int>(timing.last_send_ms - timing.capture_ms); | 325 static_cast<int>(timing.last_send_ms - timing.capture_ms); |
284 if (encoder_timing_) { | 326 if (encoder_timing_) { |
285 encoder_timing_->OnEncodeTiming(timing.capture_ntp_ms, | 327 encoder_timing_->OnEncodeTiming(timing.capture_ntp_ms, |
286 encode_duration_ms); | 328 encode_duration_ms); |
287 } | 329 } |
288 if (last_processed_capture_time_ms_ != -1) { | 330 if (last_processed_capture_time_ms_ != -1) { |
289 int64_t diff_ms = timing.capture_ms - last_processed_capture_time_ms_; | 331 int64_t diff_ms = timing.capture_ms - last_processed_capture_time_ms_; |
290 usage_->AddSample(encode_duration_ms, diff_ms); | 332 usage_->AddSample(encode_duration_ms, diff_ms); |
291 } | 333 } |
292 last_processed_capture_time_ms_ = timing.capture_ms; | 334 last_processed_capture_time_ms_ = timing.capture_ms; |
293 EncodedFrameTimeMeasured(encode_duration_ms); | 335 EncodedFrameTimeMeasured(encode_duration_ms); |
294 } | 336 } |
295 frame_timing_.pop_front(); | 337 frame_timing_.pop_front(); |
296 } | 338 } |
297 } | 339 } |
298 | 340 |
299 void OveruseFrameDetector::Process() { | 341 void OveruseFrameDetector::CheckForOveruse() { |
300 RTC_DCHECK(processing_thread_.CalledOnValidThread()); | 342 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 343 ++num_process_times_; |
| 344 if (num_process_times_ <= options_.min_process_count || !metrics_) |
| 345 return; |
301 | 346 |
302 int64_t now = clock_->TimeInMilliseconds(); | 347 int64_t now = clock_->TimeInMilliseconds(); |
303 | 348 |
304 // Used to protect against Process() being called too often. | 349 if (IsOverusing(*metrics_)) { |
305 if (now < next_process_time_ms_) | |
306 return; | |
307 | |
308 next_process_time_ms_ = now + kProcessIntervalMs; | |
309 | |
310 CpuOveruseMetrics current_metrics; | |
311 { | |
312 rtc::CritScope cs(&crit_); | |
313 ++num_process_times_; | |
314 if (num_process_times_ <= options_.min_process_count || !metrics_) | |
315 return; | |
316 | |
317 current_metrics = *metrics_; | |
318 } | |
319 | |
320 if (IsOverusing(current_metrics)) { | |
321 // If the last thing we did was going up, and now have to back down, we need | 350 // If the last thing we did was going up, and now have to back down, we need |
322 // to check if this peak was short. If so we should back off to avoid going | 351 // to check if this peak was short. If so we should back off to avoid going |
323 // back and forth between this load, the system doesn't seem to handle it. | 352 // back and forth between this load, the system doesn't seem to handle it. |
324 bool check_for_backoff = last_rampup_time_ms_ > last_overuse_time_ms_; | 353 bool check_for_backoff = last_rampup_time_ms_ > last_overuse_time_ms_; |
325 if (check_for_backoff) { | 354 if (check_for_backoff) { |
326 if (now - last_rampup_time_ms_ < kStandardRampUpDelayMs || | 355 if (now - last_rampup_time_ms_ < kStandardRampUpDelayMs || |
327 num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) { | 356 num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) { |
328 // Going up was not ok for very long, back off. | 357 // Going up was not ok for very long, back off. |
329 current_rampup_delay_ms_ *= kRampUpBackoffFactor; | 358 current_rampup_delay_ms_ *= kRampUpBackoffFactor; |
330 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs) | 359 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs) |
331 current_rampup_delay_ms_ = kMaxRampUpDelayMs; | 360 current_rampup_delay_ms_ = kMaxRampUpDelayMs; |
332 } else { | 361 } else { |
333 // Not currently backing off, reset rampup delay. | 362 // Not currently backing off, reset rampup delay. |
334 current_rampup_delay_ms_ = kStandardRampUpDelayMs; | 363 current_rampup_delay_ms_ = kStandardRampUpDelayMs; |
335 } | 364 } |
336 } | 365 } |
337 | 366 |
338 last_overuse_time_ms_ = now; | 367 last_overuse_time_ms_ = now; |
339 in_quick_rampup_ = false; | 368 in_quick_rampup_ = false; |
340 checks_above_threshold_ = 0; | 369 checks_above_threshold_ = 0; |
341 ++num_overuse_detections_; | 370 ++num_overuse_detections_; |
342 | 371 |
343 if (observer_) | 372 if (observer_) |
344 observer_->OveruseDetected(); | 373 observer_->OveruseDetected(); |
345 } else if (IsUnderusing(current_metrics, now)) { | 374 } else if (IsUnderusing(*metrics_, now)) { |
346 last_rampup_time_ms_ = now; | 375 last_rampup_time_ms_ = now; |
347 in_quick_rampup_ = true; | 376 in_quick_rampup_ = true; |
348 | 377 |
349 if (observer_) | 378 if (observer_) |
350 observer_->NormalUsage(); | 379 observer_->NormalUsage(); |
351 } | 380 } |
352 | 381 |
353 int rampup_delay = | 382 int rampup_delay = |
354 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 383 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
355 | 384 |
356 LOG(LS_VERBOSE) << " Frame stats: " | 385 LOG(LS_VERBOSE) << " Frame stats: " |
357 << " encode usage " << current_metrics.encode_usage_percent | 386 << " encode usage " << metrics_->encode_usage_percent |
358 << " overuse detections " << num_overuse_detections_ | 387 << " overuse detections " << num_overuse_detections_ |
359 << " rampup delay " << rampup_delay; | 388 << " rampup delay " << rampup_delay; |
360 } | 389 } |
361 | 390 |
362 bool OveruseFrameDetector::IsOverusing(const CpuOveruseMetrics& metrics) { | 391 bool OveruseFrameDetector::IsOverusing(const CpuOveruseMetrics& metrics) { |
| 392 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
363 if (metrics.encode_usage_percent >= | 393 if (metrics.encode_usage_percent >= |
364 options_.high_encode_usage_threshold_percent) { | 394 options_.high_encode_usage_threshold_percent) { |
365 ++checks_above_threshold_; | 395 ++checks_above_threshold_; |
366 } else { | 396 } else { |
367 checks_above_threshold_ = 0; | 397 checks_above_threshold_ = 0; |
368 } | 398 } |
369 return checks_above_threshold_ >= options_.high_threshold_consecutive_count; | 399 return checks_above_threshold_ >= options_.high_threshold_consecutive_count; |
370 } | 400 } |
371 | 401 |
372 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, | 402 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, |
373 int64_t time_now) { | 403 int64_t time_now) { |
| 404 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
374 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 405 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
375 if (time_now < last_rampup_time_ms_ + delay) | 406 if (time_now < last_rampup_time_ms_ + delay) |
376 return false; | 407 return false; |
377 | 408 |
378 return metrics.encode_usage_percent < | 409 return metrics.encode_usage_percent < |
379 options_.low_encode_usage_threshold_percent; | 410 options_.low_encode_usage_threshold_percent; |
380 } | 411 } |
381 } // namespace webrtc | 412 } // namespace webrtc |
OLD | NEW |