| 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/api/video/video_frame.h" | 20 #include "webrtc/api/video/video_frame.h" |
| 21 #include "webrtc/base/checks.h" | 21 #include "webrtc/base/checks.h" |
| 22 #include "webrtc/base/logging.h" | 22 #include "webrtc/base/logging.h" |
| 23 #include "webrtc/base/numerics/exp_filter.h" | 23 #include "webrtc/base/numerics/exp_filter.h" |
| 24 #include "webrtc/common_video/include/frame_callback.h" | 24 #include "webrtc/common_video/include/frame_callback.h" |
| 25 #include "webrtc/system_wrappers/include/clock.h" | |
| 26 | 25 |
| 27 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 26 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
| 28 #include <mach/mach.h> | 27 #include <mach/mach.h> |
| 29 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 28 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
| 30 | 29 |
| 31 namespace webrtc { | 30 namespace webrtc { |
| 32 | 31 |
| 33 namespace { | 32 namespace { |
| 34 const int64_t kCheckForOveruseIntervalMs = 5000; | 33 const int64_t kCheckForOveruseIntervalMs = 5000; |
| 35 const int64_t kTimeToFirstCheckForOveruseMs = 100; | 34 const int64_t kTimeToFirstCheckForOveruseMs = 100; |
| (...skipping 160 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
| 196 std::unique_ptr<rtc::QueuedTask>(this), kCheckForOveruseIntervalMs); | 195 std::unique_ptr<rtc::QueuedTask>(this), kCheckForOveruseIntervalMs); |
| 197 // Return false to prevent this task from being deleted. Ownership has been | 196 // Return false to prevent this task from being deleted. Ownership has been |
| 198 // transferred to the task queue when PostDelayedTask was called. | 197 // transferred to the task queue when PostDelayedTask was called. |
| 199 return false; | 198 return false; |
| 200 } | 199 } |
| 201 rtc::SequencedTaskChecker task_checker_; | 200 rtc::SequencedTaskChecker task_checker_; |
| 202 OveruseFrameDetector* overuse_detector_; | 201 OveruseFrameDetector* overuse_detector_; |
| 203 }; | 202 }; |
| 204 | 203 |
| 205 OveruseFrameDetector::OveruseFrameDetector( | 204 OveruseFrameDetector::OveruseFrameDetector( |
| 206 Clock* clock, | |
| 207 const CpuOveruseOptions& options, | 205 const CpuOveruseOptions& options, |
| 208 ScalingObserverInterface* observer, | 206 ScalingObserverInterface* observer, |
| 209 EncodedFrameObserver* encoder_timing, | 207 EncodedFrameObserver* encoder_timing, |
| 210 CpuOveruseMetricsObserver* metrics_observer) | 208 CpuOveruseMetricsObserver* metrics_observer) |
| 211 : check_overuse_task_(nullptr), | 209 : check_overuse_task_(nullptr), |
| 212 options_(options), | 210 options_(options), |
| 213 observer_(observer), | 211 observer_(observer), |
| 214 encoder_timing_(encoder_timing), | 212 encoder_timing_(encoder_timing), |
| 215 metrics_observer_(metrics_observer), | 213 metrics_observer_(metrics_observer), |
| 216 clock_(clock), | |
| 217 num_process_times_(0), | 214 num_process_times_(0), |
| 218 last_capture_time_ms_(-1), | 215 // TODO(nisse): Use rtc::Optional |
| 219 last_processed_capture_time_ms_(-1), | 216 last_capture_time_us_(-1), |
| 217 last_processed_capture_time_us_(-1), |
| 220 num_pixels_(0), | 218 num_pixels_(0), |
| 221 last_overuse_time_ms_(-1), | 219 last_overuse_time_ms_(-1), |
| 222 checks_above_threshold_(0), | 220 checks_above_threshold_(0), |
| 223 num_overuse_detections_(0), | 221 num_overuse_detections_(0), |
| 224 last_rampup_time_ms_(-1), | 222 last_rampup_time_ms_(-1), |
| 225 in_quick_rampup_(false), | 223 in_quick_rampup_(false), |
| 226 current_rampup_delay_ms_(kStandardRampUpDelayMs), | 224 current_rampup_delay_ms_(kStandardRampUpDelayMs), |
| 227 usage_(new SendProcessingUsage(options)) { | 225 usage_(new SendProcessingUsage(options)) { |
| 228 task_checker_.Detach(); | 226 task_checker_.Detach(); |
| 229 } | 227 } |
| (...skipping 23 matching lines...) Expand all Loading... |
| 253 } | 251 } |
| 254 | 252 |
| 255 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { | 253 bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const { |
| 256 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 254 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 257 if (num_pixels != num_pixels_) { | 255 if (num_pixels != num_pixels_) { |
| 258 return true; | 256 return true; |
| 259 } | 257 } |
| 260 return false; | 258 return false; |
| 261 } | 259 } |
| 262 | 260 |
| 263 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const { | 261 bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now_us) const { |
| 264 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 262 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 265 if (last_capture_time_ms_ == -1) | 263 if (last_capture_time_us_ == -1) |
| 266 return false; | 264 return false; |
| 267 return (now - last_capture_time_ms_) > options_.frame_timeout_interval_ms; | 265 return (now_us - last_capture_time_us_) > |
| 266 options_.frame_timeout_interval_ms * rtc::kNumMicrosecsPerMillisec; |
| 268 } | 267 } |
| 269 | 268 |
| 270 void OveruseFrameDetector::ResetAll(int num_pixels) { | 269 void OveruseFrameDetector::ResetAll(int num_pixels) { |
| 271 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 270 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 272 num_pixels_ = num_pixels; | 271 num_pixels_ = num_pixels; |
| 273 usage_->Reset(); | 272 usage_->Reset(); |
| 274 frame_timing_.clear(); | 273 frame_timing_.clear(); |
| 275 last_capture_time_ms_ = -1; | 274 last_capture_time_us_ = -1; |
| 276 last_processed_capture_time_ms_ = -1; | 275 last_processed_capture_time_us_ = -1; |
| 277 num_process_times_ = 0; | 276 num_process_times_ = 0; |
| 278 metrics_ = rtc::Optional<CpuOveruseMetrics>(); | 277 metrics_ = rtc::Optional<CpuOveruseMetrics>(); |
| 279 } | 278 } |
| 280 | 279 |
| 281 void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame, | 280 void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame, |
| 282 int64_t time_when_first_seen_ms) { | 281 int64_t time_when_first_seen_us) { |
| 283 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 282 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 284 | 283 |
| 285 if (FrameSizeChanged(frame.width() * frame.height()) || | 284 if (FrameSizeChanged(frame.width() * frame.height()) || |
| 286 FrameTimeoutDetected(time_when_first_seen_ms)) { | 285 FrameTimeoutDetected(time_when_first_seen_us)) { |
| 287 ResetAll(frame.width() * frame.height()); | 286 ResetAll(frame.width() * frame.height()); |
| 288 } | 287 } |
| 289 | 288 |
| 290 if (last_capture_time_ms_ != -1) | 289 if (last_capture_time_us_ != -1) |
| 291 usage_->AddCaptureSample(time_when_first_seen_ms - last_capture_time_ms_); | 290 usage_->AddCaptureSample( |
| 291 1e-3 * (time_when_first_seen_us - last_capture_time_us_)); |
| 292 | 292 |
| 293 last_capture_time_ms_ = time_when_first_seen_ms; | 293 last_capture_time_us_ = time_when_first_seen_us; |
| 294 | 294 |
| 295 frame_timing_.push_back(FrameTiming(frame.ntp_time_ms(), frame.timestamp(), | 295 frame_timing_.push_back(FrameTiming(frame.timestamp_us(), frame.timestamp(), |
| 296 time_when_first_seen_ms)); | 296 time_when_first_seen_us)); |
| 297 } | 297 } |
| 298 | 298 |
| 299 void OveruseFrameDetector::FrameSent(uint32_t timestamp, | 299 void OveruseFrameDetector::FrameSent(uint32_t timestamp, |
| 300 int64_t time_sent_in_ms) { | 300 int64_t time_sent_in_us) { |
| 301 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 301 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 302 // Delay before reporting actual encoding time, used to have the ability to | 302 // Delay before reporting actual encoding time, used to have the ability to |
| 303 // detect total encoding time when encoding more than one layer. Encoding is | 303 // detect total encoding time when encoding more than one layer. Encoding is |
| 304 // here assumed to finish within a second (or that we get enough long-time | 304 // here assumed to finish within a second (or that we get enough long-time |
| 305 // samples before one second to trigger an overuse even when this is not the | 305 // samples before one second to trigger an overuse even when this is not the |
| 306 // case). | 306 // case). |
| 307 static const int64_t kEncodingTimeMeasureWindowMs = 1000; | 307 static const int64_t kEncodingTimeMeasureWindowMs = 1000; |
| 308 for (auto& it : frame_timing_) { | 308 for (auto& it : frame_timing_) { |
| 309 if (it.timestamp == timestamp) { | 309 if (it.timestamp == timestamp) { |
| 310 it.last_send_ms = time_sent_in_ms; | 310 it.last_send_us = time_sent_in_us; |
| 311 break; | 311 break; |
| 312 } | 312 } |
| 313 } | 313 } |
| 314 // TODO(pbos): Handle the case/log errors when not finding the corresponding | 314 // TODO(pbos): Handle the case/log errors when not finding the corresponding |
| 315 // frame (either very slow encoding or incorrect wrong timestamps returned | 315 // frame (either very slow encoding or incorrect wrong timestamps returned |
| 316 // from the encoder). | 316 // from the encoder). |
| 317 // This is currently the case for all frames on ChromeOS, so logging them | 317 // This is currently the case for all frames on ChromeOS, so logging them |
| 318 // would be spammy, and triggering overuse would be wrong. | 318 // would be spammy, and triggering overuse would be wrong. |
| 319 // https://crbug.com/350106 | 319 // https://crbug.com/350106 |
| 320 while (!frame_timing_.empty()) { | 320 while (!frame_timing_.empty()) { |
| 321 FrameTiming timing = frame_timing_.front(); | 321 FrameTiming timing = frame_timing_.front(); |
| 322 if (time_sent_in_ms - timing.capture_ms < kEncodingTimeMeasureWindowMs) | 322 if (time_sent_in_us - timing.capture_us < |
| 323 kEncodingTimeMeasureWindowMs * rtc::kNumMicrosecsPerMillisec) |
| 323 break; | 324 break; |
| 324 if (timing.last_send_ms != -1) { | 325 if (timing.last_send_us != -1) { |
| 325 int encode_duration_ms = | 326 int encode_duration_us = |
| 326 static_cast<int>(timing.last_send_ms - timing.capture_ms); | 327 static_cast<int>(timing.last_send_us - timing.capture_us); |
| 327 if (encoder_timing_) { | 328 if (encoder_timing_) { |
| 328 encoder_timing_->OnEncodeTiming(timing.capture_ntp_ms, | 329 // TODO(nisse): Update encoder_timing_ to also use us units. |
| 329 encode_duration_ms); | 330 encoder_timing_->OnEncodeTiming(timing.capture_time_us / |
| 331 rtc::kNumMicrosecsPerMillisec, |
| 332 encode_duration_us / |
| 333 rtc::kNumMicrosecsPerMillisec); |
| 330 } | 334 } |
| 331 if (last_processed_capture_time_ms_ != -1) { | 335 if (last_processed_capture_time_us_ != -1) { |
| 332 int64_t diff_ms = timing.capture_ms - last_processed_capture_time_ms_; | 336 int64_t diff_us = timing.capture_us - last_processed_capture_time_us_; |
| 333 usage_->AddSample(encode_duration_ms, diff_ms); | 337 usage_->AddSample(1e-3 * encode_duration_us, 1e-3 * diff_us); |
| 334 } | 338 } |
| 335 last_processed_capture_time_ms_ = timing.capture_ms; | 339 last_processed_capture_time_us_ = timing.capture_us; |
| 336 EncodedFrameTimeMeasured(encode_duration_ms); | 340 EncodedFrameTimeMeasured(encode_duration_us / |
| 341 rtc::kNumMicrosecsPerMillisec); |
| 337 } | 342 } |
| 338 frame_timing_.pop_front(); | 343 frame_timing_.pop_front(); |
| 339 } | 344 } |
| 340 } | 345 } |
| 341 | 346 |
| 342 void OveruseFrameDetector::CheckForOveruse() { | 347 void OveruseFrameDetector::CheckForOveruse() { |
| 343 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 348 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 344 ++num_process_times_; | 349 ++num_process_times_; |
| 345 if (num_process_times_ <= options_.min_process_count || !metrics_) | 350 if (num_process_times_ <= options_.min_process_count || !metrics_) |
| 346 return; | 351 return; |
| 347 | 352 |
| 348 int64_t now = clock_->TimeInMilliseconds(); | 353 int64_t now_ms = rtc::TimeMillis(); |
| 349 | 354 |
| 350 if (IsOverusing(*metrics_)) { | 355 if (IsOverusing(*metrics_)) { |
| 351 // If the last thing we did was going up, and now have to back down, we need | 356 // If the last thing we did was going up, and now have to back down, we need |
| 352 // to check if this peak was short. If so we should back off to avoid going | 357 // to check if this peak was short. If so we should back off to avoid going |
| 353 // back and forth between this load, the system doesn't seem to handle it. | 358 // back and forth between this load, the system doesn't seem to handle it. |
| 354 bool check_for_backoff = last_rampup_time_ms_ > last_overuse_time_ms_; | 359 bool check_for_backoff = last_rampup_time_ms_ > last_overuse_time_ms_; |
| 355 if (check_for_backoff) { | 360 if (check_for_backoff) { |
| 356 if (now - last_rampup_time_ms_ < kStandardRampUpDelayMs || | 361 if (now_ms - last_rampup_time_ms_ < kStandardRampUpDelayMs || |
| 357 num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) { | 362 num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) { |
| 358 // Going up was not ok for very long, back off. | 363 // Going up was not ok for very long, back off. |
| 359 current_rampup_delay_ms_ *= kRampUpBackoffFactor; | 364 current_rampup_delay_ms_ *= kRampUpBackoffFactor; |
| 360 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs) | 365 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs) |
| 361 current_rampup_delay_ms_ = kMaxRampUpDelayMs; | 366 current_rampup_delay_ms_ = kMaxRampUpDelayMs; |
| 362 } else { | 367 } else { |
| 363 // Not currently backing off, reset rampup delay. | 368 // Not currently backing off, reset rampup delay. |
| 364 current_rampup_delay_ms_ = kStandardRampUpDelayMs; | 369 current_rampup_delay_ms_ = kStandardRampUpDelayMs; |
| 365 } | 370 } |
| 366 } | 371 } |
| 367 | 372 |
| 368 last_overuse_time_ms_ = now; | 373 last_overuse_time_ms_ = now_ms; |
| 369 in_quick_rampup_ = false; | 374 in_quick_rampup_ = false; |
| 370 checks_above_threshold_ = 0; | 375 checks_above_threshold_ = 0; |
| 371 ++num_overuse_detections_; | 376 ++num_overuse_detections_; |
| 372 | 377 |
| 373 if (observer_) | 378 if (observer_) |
| 374 observer_->ScaleDown(kScaleReasonCpu); | 379 observer_->ScaleDown(kScaleReasonCpu); |
| 375 } else if (IsUnderusing(*metrics_, now)) { | 380 } else if (IsUnderusing(*metrics_, now_ms)) { |
| 376 last_rampup_time_ms_ = now; | 381 last_rampup_time_ms_ = now_ms; |
| 377 in_quick_rampup_ = true; | 382 in_quick_rampup_ = true; |
| 378 | 383 |
| 379 if (observer_) | 384 if (observer_) |
| 380 observer_->ScaleUp(kScaleReasonCpu); | 385 observer_->ScaleUp(kScaleReasonCpu); |
| 381 } | 386 } |
| 382 | 387 |
| 383 int rampup_delay = | 388 int rampup_delay = |
| 384 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 389 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
| 385 | 390 |
| 386 LOG(LS_VERBOSE) << " Frame stats: " | 391 LOG(LS_VERBOSE) << " Frame stats: " |
| (...skipping 17 matching lines...) Expand all Loading... |
| 404 int64_t time_now) { | 409 int64_t time_now) { |
| 405 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 410 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 406 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 411 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
| 407 if (time_now < last_rampup_time_ms_ + delay) | 412 if (time_now < last_rampup_time_ms_ + delay) |
| 408 return false; | 413 return false; |
| 409 | 414 |
| 410 return metrics.encode_usage_percent < | 415 return metrics.encode_usage_percent < |
| 411 options_.low_encode_usage_threshold_percent; | 416 options_.low_encode_usage_threshold_percent; |
| 412 } | 417 } |
| 413 } // namespace webrtc | 418 } // namespace webrtc |
| OLD | NEW |