Chromium Code Reviews| 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 #include <string> | |
| 20 #include <utility> | |
| 19 | 21 |
| 20 #include "webrtc/api/video/video_frame.h" | 22 #include "webrtc/api/video/video_frame.h" |
| 21 #include "webrtc/base/checks.h" | 23 #include "webrtc/base/checks.h" |
| 22 #include "webrtc/base/logging.h" | 24 #include "webrtc/base/logging.h" |
| 23 #include "webrtc/base/numerics/exp_filter.h" | 25 #include "webrtc/base/numerics/exp_filter.h" |
| 24 #include "webrtc/common_video/include/frame_callback.h" | 26 #include "webrtc/common_video/include/frame_callback.h" |
| 27 #include "webrtc/system_wrappers/include/field_trial.h" | |
| 25 | 28 |
| 26 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 29 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
| 27 #include <mach/mach.h> | 30 #include <mach/mach.h> |
| 28 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 31 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
| 29 | 32 |
| 30 namespace webrtc { | 33 namespace webrtc { |
| 31 | 34 |
| 32 namespace { | 35 namespace { |
| 33 const int64_t kCheckForOveruseIntervalMs = 5000; | 36 const int64_t kCheckForOveruseIntervalMs = 5000; |
| 34 const int64_t kTimeToFirstCheckForOveruseMs = 100; | 37 const int64_t kTimeToFirstCheckForOveruseMs = 100; |
| (...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 109 : kWeightFactorFrameDiff(0.998f), | 112 : kWeightFactorFrameDiff(0.998f), |
| 110 kWeightFactorProcessing(0.995f), | 113 kWeightFactorProcessing(0.995f), |
| 111 kInitialSampleDiffMs(40.0f), | 114 kInitialSampleDiffMs(40.0f), |
| 112 kMaxSampleDiffMs(45.0f), | 115 kMaxSampleDiffMs(45.0f), |
| 113 count_(0), | 116 count_(0), |
| 114 options_(options), | 117 options_(options), |
| 115 filtered_processing_ms_(new rtc::ExpFilter(kWeightFactorProcessing)), | 118 filtered_processing_ms_(new rtc::ExpFilter(kWeightFactorProcessing)), |
| 116 filtered_frame_diff_ms_(new rtc::ExpFilter(kWeightFactorFrameDiff)) { | 119 filtered_frame_diff_ms_(new rtc::ExpFilter(kWeightFactorFrameDiff)) { |
| 117 Reset(); | 120 Reset(); |
| 118 } | 121 } |
| 119 ~SendProcessingUsage() {} | 122 virtual ~SendProcessingUsage() {} |
| 120 | 123 |
| 121 void Reset() { | 124 void Reset() { |
| 122 count_ = 0; | 125 count_ = 0; |
| 123 filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff); | 126 filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff); |
| 124 filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs); | 127 filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs); |
| 125 filtered_processing_ms_->Reset(kWeightFactorProcessing); | 128 filtered_processing_ms_->Reset(kWeightFactorProcessing); |
| 126 filtered_processing_ms_->Apply(1.0f, InitialProcessingMs()); | 129 filtered_processing_ms_->Apply(1.0f, InitialProcessingMs()); |
| 127 } | 130 } |
| 128 | 131 |
| 129 void AddCaptureSample(float sample_ms) { | 132 void AddCaptureSample(float sample_ms) { |
| 130 float exp = sample_ms / kSampleDiffMs; | 133 float exp = sample_ms / kSampleDiffMs; |
| 131 exp = std::min(exp, kMaxExp); | 134 exp = std::min(exp, kMaxExp); |
| 132 filtered_frame_diff_ms_->Apply(exp, sample_ms); | 135 filtered_frame_diff_ms_->Apply(exp, sample_ms); |
| 133 } | 136 } |
| 134 | 137 |
| 135 void AddSample(float processing_ms, int64_t diff_last_sample_ms) { | 138 void AddSample(float processing_ms, int64_t diff_last_sample_ms) { |
| 136 ++count_; | 139 ++count_; |
| 137 float exp = diff_last_sample_ms / kSampleDiffMs; | 140 float exp = diff_last_sample_ms / kSampleDiffMs; |
| 138 exp = std::min(exp, kMaxExp); | 141 exp = std::min(exp, kMaxExp); |
| 139 filtered_processing_ms_->Apply(exp, processing_ms); | 142 filtered_processing_ms_->Apply(exp, processing_ms); |
| 140 } | 143 } |
| 141 | 144 |
| 142 int Value() const { | 145 virtual int Value() { |
| 143 if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) { | 146 if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) { |
| 144 return static_cast<int>(InitialUsageInPercent() + 0.5f); | 147 return static_cast<int>(InitialUsageInPercent() + 0.5f); |
| 145 } | 148 } |
| 146 float frame_diff_ms = std::max(filtered_frame_diff_ms_->filtered(), 1.0f); | 149 float frame_diff_ms = std::max(filtered_frame_diff_ms_->filtered(), 1.0f); |
| 147 frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs); | 150 frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs); |
| 148 float encode_usage_percent = | 151 float encode_usage_percent = |
| 149 100.0f * filtered_processing_ms_->filtered() / frame_diff_ms; | 152 100.0f * filtered_processing_ms_->filtered() / frame_diff_ms; |
| 150 return static_cast<int>(encode_usage_percent + 0.5); | 153 return static_cast<int>(encode_usage_percent + 0.5); |
| 151 } | 154 } |
| 152 | 155 |
| (...skipping 11 matching lines...) Expand all Loading... | |
| 164 const float kWeightFactorFrameDiff; | 167 const float kWeightFactorFrameDiff; |
| 165 const float kWeightFactorProcessing; | 168 const float kWeightFactorProcessing; |
| 166 const float kInitialSampleDiffMs; | 169 const float kInitialSampleDiffMs; |
| 167 const float kMaxSampleDiffMs; | 170 const float kMaxSampleDiffMs; |
| 168 uint64_t count_; | 171 uint64_t count_; |
| 169 const CpuOveruseOptions options_; | 172 const CpuOveruseOptions options_; |
| 170 std::unique_ptr<rtc::ExpFilter> filtered_processing_ms_; | 173 std::unique_ptr<rtc::ExpFilter> filtered_processing_ms_; |
| 171 std::unique_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; | 174 std::unique_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; |
| 172 }; | 175 }; |
| 173 | 176 |
| 177 // Class used for manual testing of overuse, enabled via field trial flag. | |
| 178 class OveruseFrameDetector::OverdoseInjector | |
| 179 : public OveruseFrameDetector::SendProcessingUsage { | |
| 180 public: | |
| 181 OverdoseInjector(const CpuOveruseOptions& options, | |
| 182 int64_t normal_period_ms, | |
| 183 int64_t overuse_period_ms, | |
| 184 int64_t underuse_period_ms) | |
| 185 : OveruseFrameDetector::SendProcessingUsage(options), | |
| 186 normal_period_ms_(normal_period_ms), | |
| 187 overuse_period_ms_(overuse_period_ms), | |
| 188 underuse_period_ms_(underuse_period_ms), | |
| 189 state_(State::kNormal), | |
| 190 last_toggling_ms_(-1) { | |
| 191 RTC_DCHECK_GT(overuse_period_ms, 0); | |
| 192 RTC_DCHECK_GT(normal_period_ms, 0); | |
| 193 LOG(LS_INFO) << "Simulating overuse with intervals " << normal_period_ms | |
| 194 << "ms normal mode, " << overuse_period_ms | |
| 195 << "ms overuse mode."; | |
| 196 } | |
| 197 | |
| 198 ~OverdoseInjector() override {} | |
| 199 | |
| 200 int Value() override { | |
| 201 int64_t now_ms = rtc::TimeMillis(); | |
| 202 rtc::Optional<int> overried_usage_value; | |
| 203 if (last_toggling_ms_ == -1) { | |
| 204 last_toggling_ms_ = now_ms; | |
| 205 } else { | |
| 206 switch (state_) { | |
|
ilnik
2017/03/31 09:20:19
If i understood correctly, this may be called ever
sprang_webrtc
2017/03/31 09:46:42
Done.
| |
| 207 case State::kNormal: | |
| 208 if (now_ms > last_toggling_ms_ + normal_period_ms_) { | |
| 209 state_ = State::kOveruse; | |
| 210 last_toggling_ms_ = now_ms; | |
| 211 LOG(LS_INFO) << "Simulating CPU overuse."; | |
| 212 } | |
| 213 break; | |
| 214 case State::kOveruse: | |
| 215 if (now_ms > last_toggling_ms_ + overuse_period_ms_) { | |
| 216 state_ = State::kUnderuse; | |
| 217 last_toggling_ms_ = now_ms; | |
| 218 LOG(LS_INFO) << "Simulating CPU underuse."; | |
| 219 } | |
| 220 overried_usage_value.emplace(250); | |
| 221 break; | |
| 222 case State::kUnderuse: | |
| 223 if (now_ms > last_toggling_ms_ + underuse_period_ms_) { | |
| 224 state_ = State::kNormal; | |
| 225 last_toggling_ms_ = now_ms; | |
| 226 LOG(LS_INFO) << "Actual CPU overuse measurements in effect."; | |
| 227 } | |
| 228 overried_usage_value.emplace(5); | |
| 229 break; | |
| 230 } | |
| 231 } | |
| 232 | |
| 233 return overried_usage_value.value_or(SendProcessingUsage::Value()); | |
| 234 } | |
| 235 | |
| 236 private: | |
| 237 const int64_t normal_period_ms_; | |
| 238 const int64_t overuse_period_ms_; | |
| 239 const int64_t underuse_period_ms_; | |
| 240 enum class State { kNormal, kOveruse, kUnderuse } state_; | |
| 241 int64_t last_toggling_ms_; | |
| 242 }; | |
| 243 | |
| 244 std::unique_ptr<OveruseFrameDetector::SendProcessingUsage> | |
| 245 OveruseFrameDetector::CreateSendProcessingUsage( | |
| 246 const CpuOveruseOptions& options) { | |
| 247 std::unique_ptr<SendProcessingUsage> instance; | |
| 248 std::string toggling_interval = | |
| 249 field_trial::FindFullName("WebRTC-ForceSimulatedOveruseIntervalMs"); | |
| 250 if (!toggling_interval.empty()) { | |
| 251 int normal_period_ms = 0; | |
| 252 int overuse_period_ms = 0; | |
| 253 int underuse_period_ms = 0; | |
| 254 if (sscanf(toggling_interval.c_str(), "%d-%d-%d", &normal_period_ms, | |
| 255 &overuse_period_ms, &underuse_period_ms) == 3) { | |
| 256 if (normal_period_ms > 0 && overuse_period_ms > 0 && | |
| 257 underuse_period_ms > 0) { | |
| 258 instance.reset(new OverdoseInjector( | |
| 259 options, normal_period_ms, overuse_period_ms, underuse_period_ms)); | |
| 260 } else { | |
| 261 LOG(LS_WARNING) | |
| 262 << "Invalid (non-positive) normal/overuse/underuse periods: " | |
| 263 << normal_period_ms << " / " << overuse_period_ms << " / " | |
| 264 << underuse_period_ms; | |
| 265 } | |
| 266 } else { | |
| 267 LOG(LS_WARNING) << "Malformed toggling interval: " << toggling_interval; | |
| 268 } | |
| 269 } | |
| 270 | |
| 271 if (!instance) { | |
| 272 // No valid overuse simulation parameters set, use normal usage class. | |
| 273 instance.reset(new SendProcessingUsage(options)); | |
| 274 } | |
| 275 | |
| 276 return instance; | |
| 277 } | |
| 278 | |
| 174 class OveruseFrameDetector::CheckOveruseTask : public rtc::QueuedTask { | 279 class OveruseFrameDetector::CheckOveruseTask : public rtc::QueuedTask { |
| 175 public: | 280 public: |
| 176 explicit CheckOveruseTask(OveruseFrameDetector* overuse_detector) | 281 explicit CheckOveruseTask(OveruseFrameDetector* overuse_detector) |
| 177 : overuse_detector_(overuse_detector) { | 282 : overuse_detector_(overuse_detector) { |
| 178 rtc::TaskQueue::Current()->PostDelayedTask( | 283 rtc::TaskQueue::Current()->PostDelayedTask( |
| 179 std::unique_ptr<rtc::QueuedTask>(this), kTimeToFirstCheckForOveruseMs); | 284 std::unique_ptr<rtc::QueuedTask>(this), kTimeToFirstCheckForOveruseMs); |
| 180 } | 285 } |
| 181 | 286 |
| 182 void Stop() { | 287 void Stop() { |
| 183 RTC_CHECK(task_checker_.CalledSequentially()); | 288 RTC_CHECK(task_checker_.CalledSequentially()); |
| (...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 215 // TODO(nisse): Use rtc::Optional | 320 // TODO(nisse): Use rtc::Optional |
| 216 last_capture_time_us_(-1), | 321 last_capture_time_us_(-1), |
| 217 last_processed_capture_time_us_(-1), | 322 last_processed_capture_time_us_(-1), |
| 218 num_pixels_(0), | 323 num_pixels_(0), |
| 219 last_overuse_time_ms_(-1), | 324 last_overuse_time_ms_(-1), |
| 220 checks_above_threshold_(0), | 325 checks_above_threshold_(0), |
| 221 num_overuse_detections_(0), | 326 num_overuse_detections_(0), |
| 222 last_rampup_time_ms_(-1), | 327 last_rampup_time_ms_(-1), |
| 223 in_quick_rampup_(false), | 328 in_quick_rampup_(false), |
| 224 current_rampup_delay_ms_(kStandardRampUpDelayMs), | 329 current_rampup_delay_ms_(kStandardRampUpDelayMs), |
| 225 usage_(new SendProcessingUsage(options)) { | 330 usage_(CreateSendProcessingUsage(options)) { |
| 226 task_checker_.Detach(); | 331 task_checker_.Detach(); |
| 227 } | 332 } |
| 228 | 333 |
| 229 OveruseFrameDetector::~OveruseFrameDetector() { | 334 OveruseFrameDetector::~OveruseFrameDetector() { |
| 230 RTC_DCHECK(!check_overuse_task_) << "StopCheckForOverUse must be called."; | 335 RTC_DCHECK(!check_overuse_task_) << "StopCheckForOverUse must be called."; |
| 231 } | 336 } |
| 232 | 337 |
| 233 void OveruseFrameDetector::StartCheckForOveruse() { | 338 void OveruseFrameDetector::StartCheckForOveruse() { |
| 234 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 339 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 235 RTC_DCHECK(!check_overuse_task_); | 340 RTC_DCHECK(!check_overuse_task_); |
| (...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 313 } | 418 } |
| 314 // TODO(pbos): Handle the case/log errors when not finding the corresponding | 419 // TODO(pbos): Handle the case/log errors when not finding the corresponding |
| 315 // frame (either very slow encoding or incorrect wrong timestamps returned | 420 // frame (either very slow encoding or incorrect wrong timestamps returned |
| 316 // from the encoder). | 421 // from the encoder). |
| 317 // This is currently the case for all frames on ChromeOS, so logging them | 422 // This is currently the case for all frames on ChromeOS, so logging them |
| 318 // would be spammy, and triggering overuse would be wrong. | 423 // would be spammy, and triggering overuse would be wrong. |
| 319 // https://crbug.com/350106 | 424 // https://crbug.com/350106 |
| 320 while (!frame_timing_.empty()) { | 425 while (!frame_timing_.empty()) { |
| 321 FrameTiming timing = frame_timing_.front(); | 426 FrameTiming timing = frame_timing_.front(); |
| 322 if (time_sent_in_us - timing.capture_us < | 427 if (time_sent_in_us - timing.capture_us < |
| 323 kEncodingTimeMeasureWindowMs * rtc::kNumMicrosecsPerMillisec) | 428 kEncodingTimeMeasureWindowMs * rtc::kNumMicrosecsPerMillisec) { |
| 324 break; | 429 break; |
| 430 } | |
| 325 if (timing.last_send_us != -1) { | 431 if (timing.last_send_us != -1) { |
| 326 int encode_duration_us = | 432 int encode_duration_us = |
| 327 static_cast<int>(timing.last_send_us - timing.capture_us); | 433 static_cast<int>(timing.last_send_us - timing.capture_us); |
| 328 if (encoder_timing_) { | 434 if (encoder_timing_) { |
| 329 // TODO(nisse): Update encoder_timing_ to also use us units. | 435 // TODO(nisse): Update encoder_timing_ to also use us units. |
| 330 encoder_timing_->OnEncodeTiming(timing.capture_time_us / | 436 encoder_timing_->OnEncodeTiming(timing.capture_time_us / |
| 331 rtc::kNumMicrosecsPerMillisec, | 437 rtc::kNumMicrosecsPerMillisec, |
| 332 encode_duration_us / | 438 encode_duration_us / |
| 333 rtc::kNumMicrosecsPerMillisec); | 439 rtc::kNumMicrosecsPerMillisec); |
| 334 } | 440 } |
| (...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... | |
| 389 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 495 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
| 390 | 496 |
| 391 LOG(LS_VERBOSE) << " Frame stats: " | 497 LOG(LS_VERBOSE) << " Frame stats: " |
| 392 << " encode usage " << metrics_->encode_usage_percent | 498 << " encode usage " << metrics_->encode_usage_percent |
| 393 << " overuse detections " << num_overuse_detections_ | 499 << " overuse detections " << num_overuse_detections_ |
| 394 << " rampup delay " << rampup_delay; | 500 << " rampup delay " << rampup_delay; |
| 395 } | 501 } |
| 396 | 502 |
| 397 bool OveruseFrameDetector::IsOverusing(const CpuOveruseMetrics& metrics) { | 503 bool OveruseFrameDetector::IsOverusing(const CpuOveruseMetrics& metrics) { |
| 398 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 504 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 505 | |
| 399 if (metrics.encode_usage_percent >= | 506 if (metrics.encode_usage_percent >= |
| 400 options_.high_encode_usage_threshold_percent) { | 507 options_.high_encode_usage_threshold_percent) { |
| 401 ++checks_above_threshold_; | 508 ++checks_above_threshold_; |
| 402 } else { | 509 } else { |
| 403 checks_above_threshold_ = 0; | 510 checks_above_threshold_ = 0; |
| 404 } | 511 } |
| 405 return checks_above_threshold_ >= options_.high_threshold_consecutive_count; | 512 return checks_above_threshold_ >= options_.high_threshold_consecutive_count; |
| 406 } | 513 } |
| 407 | 514 |
| 408 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, | 515 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, |
| 409 int64_t time_now) { | 516 int64_t time_now) { |
| 410 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 517 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
| 411 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 518 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
| 412 if (time_now < last_rampup_time_ms_ + delay) | 519 if (time_now < last_rampup_time_ms_ + delay) |
| 413 return false; | 520 return false; |
| 414 | 521 |
| 415 return metrics.encode_usage_percent < | 522 return metrics.encode_usage_percent < |
| 416 options_.low_encode_usage_threshold_percent; | 523 options_.low_encode_usage_threshold_percent; |
| 417 } | 524 } |
| 418 } // namespace webrtc | 525 } // namespace webrtc |
| OLD | NEW |