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> | |
21 | 19 |
22 #include "webrtc/api/video/video_frame.h" | 20 #include "webrtc/api/video/video_frame.h" |
23 #include "webrtc/base/checks.h" | 21 #include "webrtc/base/checks.h" |
24 #include "webrtc/base/logging.h" | 22 #include "webrtc/base/logging.h" |
25 #include "webrtc/base/numerics/exp_filter.h" | 23 #include "webrtc/base/numerics/exp_filter.h" |
26 #include "webrtc/common_video/include/frame_callback.h" | 24 #include "webrtc/common_video/include/frame_callback.h" |
27 #include "webrtc/system_wrappers/include/field_trial.h" | |
28 | 25 |
29 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 26 #if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
30 #include <mach/mach.h> | 27 #include <mach/mach.h> |
31 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) | 28 #endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS) |
32 | 29 |
33 namespace webrtc { | 30 namespace webrtc { |
34 | 31 |
35 namespace { | 32 namespace { |
36 const int64_t kCheckForOveruseIntervalMs = 5000; | 33 const int64_t kCheckForOveruseIntervalMs = 5000; |
37 const int64_t kTimeToFirstCheckForOveruseMs = 100; | 34 const int64_t kTimeToFirstCheckForOveruseMs = 100; |
(...skipping 74 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
112 : kWeightFactorFrameDiff(0.998f), | 109 : kWeightFactorFrameDiff(0.998f), |
113 kWeightFactorProcessing(0.995f), | 110 kWeightFactorProcessing(0.995f), |
114 kInitialSampleDiffMs(40.0f), | 111 kInitialSampleDiffMs(40.0f), |
115 kMaxSampleDiffMs(45.0f), | 112 kMaxSampleDiffMs(45.0f), |
116 count_(0), | 113 count_(0), |
117 options_(options), | 114 options_(options), |
118 filtered_processing_ms_(new rtc::ExpFilter(kWeightFactorProcessing)), | 115 filtered_processing_ms_(new rtc::ExpFilter(kWeightFactorProcessing)), |
119 filtered_frame_diff_ms_(new rtc::ExpFilter(kWeightFactorFrameDiff)) { | 116 filtered_frame_diff_ms_(new rtc::ExpFilter(kWeightFactorFrameDiff)) { |
120 Reset(); | 117 Reset(); |
121 } | 118 } |
122 virtual ~SendProcessingUsage() {} | 119 ~SendProcessingUsage() {} |
123 | 120 |
124 void Reset() { | 121 void Reset() { |
125 count_ = 0; | 122 count_ = 0; |
126 filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff); | 123 filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff); |
127 filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs); | 124 filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs); |
128 filtered_processing_ms_->Reset(kWeightFactorProcessing); | 125 filtered_processing_ms_->Reset(kWeightFactorProcessing); |
129 filtered_processing_ms_->Apply(1.0f, InitialProcessingMs()); | 126 filtered_processing_ms_->Apply(1.0f, InitialProcessingMs()); |
130 } | 127 } |
131 | 128 |
132 void AddCaptureSample(float sample_ms) { | 129 void AddCaptureSample(float sample_ms) { |
133 float exp = sample_ms / kSampleDiffMs; | 130 float exp = sample_ms / kSampleDiffMs; |
134 exp = std::min(exp, kMaxExp); | 131 exp = std::min(exp, kMaxExp); |
135 filtered_frame_diff_ms_->Apply(exp, sample_ms); | 132 filtered_frame_diff_ms_->Apply(exp, sample_ms); |
136 } | 133 } |
137 | 134 |
138 void AddSample(float processing_ms, int64_t diff_last_sample_ms) { | 135 void AddSample(float processing_ms, int64_t diff_last_sample_ms) { |
139 ++count_; | 136 ++count_; |
140 float exp = diff_last_sample_ms / kSampleDiffMs; | 137 float exp = diff_last_sample_ms / kSampleDiffMs; |
141 exp = std::min(exp, kMaxExp); | 138 exp = std::min(exp, kMaxExp); |
142 filtered_processing_ms_->Apply(exp, processing_ms); | 139 filtered_processing_ms_->Apply(exp, processing_ms); |
143 } | 140 } |
144 | 141 |
145 virtual int Value() { | 142 int Value() const { |
146 if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) { | 143 if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) { |
147 return static_cast<int>(InitialUsageInPercent() + 0.5f); | 144 return static_cast<int>(InitialUsageInPercent() + 0.5f); |
148 } | 145 } |
149 float frame_diff_ms = std::max(filtered_frame_diff_ms_->filtered(), 1.0f); | 146 float frame_diff_ms = std::max(filtered_frame_diff_ms_->filtered(), 1.0f); |
150 frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs); | 147 frame_diff_ms = std::min(frame_diff_ms, kMaxSampleDiffMs); |
151 float encode_usage_percent = | 148 float encode_usage_percent = |
152 100.0f * filtered_processing_ms_->filtered() / frame_diff_ms; | 149 100.0f * filtered_processing_ms_->filtered() / frame_diff_ms; |
153 return static_cast<int>(encode_usage_percent + 0.5); | 150 return static_cast<int>(encode_usage_percent + 0.5); |
154 } | 151 } |
155 | 152 |
(...skipping 11 matching lines...) Expand all Loading... |
167 const float kWeightFactorFrameDiff; | 164 const float kWeightFactorFrameDiff; |
168 const float kWeightFactorProcessing; | 165 const float kWeightFactorProcessing; |
169 const float kInitialSampleDiffMs; | 166 const float kInitialSampleDiffMs; |
170 const float kMaxSampleDiffMs; | 167 const float kMaxSampleDiffMs; |
171 uint64_t count_; | 168 uint64_t count_; |
172 const CpuOveruseOptions options_; | 169 const CpuOveruseOptions options_; |
173 std::unique_ptr<rtc::ExpFilter> filtered_processing_ms_; | 170 std::unique_ptr<rtc::ExpFilter> filtered_processing_ms_; |
174 std::unique_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; | 171 std::unique_ptr<rtc::ExpFilter> filtered_frame_diff_ms_; |
175 }; | 172 }; |
176 | 173 |
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 overuse_period_ms, | |
183 int64_t normal_period_ms) | |
184 : OveruseFrameDetector::SendProcessingUsage(options), | |
185 overuse_period_ms_(overuse_period_ms), | |
186 normal_period_ms_(normal_period_ms), | |
187 is_overusing_(false), | |
188 last_toggling_ms_(-1) { | |
189 RTC_DCHECK_GT(overuse_period_ms, 0); | |
190 RTC_DCHECK_GT(normal_period_ms, 0); | |
191 LOG(LS_INFO) << "Simulating overuse with intervals " << normal_period_ms | |
192 << "ms normal mode, " << overuse_period_ms | |
193 << "ms overuse mode."; | |
194 } | |
195 | |
196 ~OverdoseInjector() override {} | |
197 | |
198 int Value() override { | |
199 int64_t now_ms = rtc::TimeMillis(); | |
200 if (last_toggling_ms_ == -1) { | |
201 last_toggling_ms_ = now_ms; | |
202 } else { | |
203 int64_t toggle_time_ms = | |
204 last_toggling_ms_ + | |
205 (is_overusing_ ? overuse_period_ms_ : normal_period_ms_); | |
206 if (now_ms > toggle_time_ms) { | |
207 is_overusing_ = !is_overusing_; | |
208 last_toggling_ms_ = now_ms; | |
209 if (is_overusing_) { | |
210 LOG(LS_INFO) << "Simulating CPU overuse."; | |
211 } else { | |
212 LOG(LS_INFO) << "Disabling CPU overuse simulation."; | |
213 } | |
214 } | |
215 } | |
216 | |
217 if (is_overusing_) | |
218 return 250; // 250% should be enough for anyone. | |
219 | |
220 return SendProcessingUsage::Value(); | |
221 } | |
222 | |
223 private: | |
224 const int64_t overuse_period_ms_; | |
225 const int64_t normal_period_ms_; | |
226 bool is_overusing_; | |
227 int64_t last_toggling_ms_; | |
228 }; | |
229 | |
230 std::unique_ptr<OveruseFrameDetector::SendProcessingUsage> | |
231 OveruseFrameDetector::CreateSendProcessingUsage( | |
232 const CpuOveruseOptions& options) { | |
233 std::unique_ptr<SendProcessingUsage> instance; | |
234 std::string toggling_interval = | |
235 field_trial::FindFullName("WebRTC-ForceSimulatedOveruseIntervalMs"); | |
236 if (!toggling_interval.empty()) { | |
237 int overuse_period_ms = 0; | |
238 int normal_period_ms = 0; | |
239 if (sscanf(toggling_interval.c_str(), "%d-%d", &overuse_period_ms, | |
240 &normal_period_ms) == 2) { | |
241 if (overuse_period_ms > 0 && normal_period_ms > 0) { | |
242 instance.reset( | |
243 new OverdoseInjector(options, overuse_period_ms, normal_period_ms)); | |
244 } else { | |
245 LOG(LS_WARNING) << "Invalid (non-positive) overuse / normal periods: " | |
246 << overuse_period_ms << " / " << normal_period_ms; | |
247 } | |
248 } else { | |
249 LOG(LS_WARNING) << "Malformed toggling interval: " << toggling_interval; | |
250 } | |
251 } | |
252 | |
253 if (!instance) { | |
254 // No valid overuse simulation parameters set, use normal usage class. | |
255 instance.reset(new SendProcessingUsage(options)); | |
256 } | |
257 | |
258 return instance; | |
259 } | |
260 | |
261 class OveruseFrameDetector::CheckOveruseTask : public rtc::QueuedTask { | 174 class OveruseFrameDetector::CheckOveruseTask : public rtc::QueuedTask { |
262 public: | 175 public: |
263 explicit CheckOveruseTask(OveruseFrameDetector* overuse_detector) | 176 explicit CheckOveruseTask(OveruseFrameDetector* overuse_detector) |
264 : overuse_detector_(overuse_detector) { | 177 : overuse_detector_(overuse_detector) { |
265 rtc::TaskQueue::Current()->PostDelayedTask( | 178 rtc::TaskQueue::Current()->PostDelayedTask( |
266 std::unique_ptr<rtc::QueuedTask>(this), kTimeToFirstCheckForOveruseMs); | 179 std::unique_ptr<rtc::QueuedTask>(this), kTimeToFirstCheckForOveruseMs); |
267 } | 180 } |
268 | 181 |
269 void Stop() { | 182 void Stop() { |
270 RTC_CHECK(task_checker_.CalledSequentially()); | 183 RTC_CHECK(task_checker_.CalledSequentially()); |
(...skipping 31 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
302 // TODO(nisse): Use rtc::Optional | 215 // TODO(nisse): Use rtc::Optional |
303 last_capture_time_us_(-1), | 216 last_capture_time_us_(-1), |
304 last_processed_capture_time_us_(-1), | 217 last_processed_capture_time_us_(-1), |
305 num_pixels_(0), | 218 num_pixels_(0), |
306 last_overuse_time_ms_(-1), | 219 last_overuse_time_ms_(-1), |
307 checks_above_threshold_(0), | 220 checks_above_threshold_(0), |
308 num_overuse_detections_(0), | 221 num_overuse_detections_(0), |
309 last_rampup_time_ms_(-1), | 222 last_rampup_time_ms_(-1), |
310 in_quick_rampup_(false), | 223 in_quick_rampup_(false), |
311 current_rampup_delay_ms_(kStandardRampUpDelayMs), | 224 current_rampup_delay_ms_(kStandardRampUpDelayMs), |
312 usage_(CreateSendProcessingUsage(options)) { | 225 usage_(new SendProcessingUsage(options)) { |
313 task_checker_.Detach(); | 226 task_checker_.Detach(); |
314 } | 227 } |
315 | 228 |
316 OveruseFrameDetector::~OveruseFrameDetector() { | 229 OveruseFrameDetector::~OveruseFrameDetector() { |
317 RTC_DCHECK(!check_overuse_task_) << "StopCheckForOverUse must be called."; | 230 RTC_DCHECK(!check_overuse_task_) << "StopCheckForOverUse must be called."; |
318 } | 231 } |
319 | 232 |
320 void OveruseFrameDetector::StartCheckForOveruse() { | 233 void OveruseFrameDetector::StartCheckForOveruse() { |
321 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 234 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
322 RTC_DCHECK(!check_overuse_task_); | 235 RTC_DCHECK(!check_overuse_task_); |
(...skipping 77 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
400 } | 313 } |
401 // 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 |
402 // frame (either very slow encoding or incorrect wrong timestamps returned | 315 // frame (either very slow encoding or incorrect wrong timestamps returned |
403 // from the encoder). | 316 // from the encoder). |
404 // 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 |
405 // would be spammy, and triggering overuse would be wrong. | 318 // would be spammy, and triggering overuse would be wrong. |
406 // https://crbug.com/350106 | 319 // https://crbug.com/350106 |
407 while (!frame_timing_.empty()) { | 320 while (!frame_timing_.empty()) { |
408 FrameTiming timing = frame_timing_.front(); | 321 FrameTiming timing = frame_timing_.front(); |
409 if (time_sent_in_us - timing.capture_us < | 322 if (time_sent_in_us - timing.capture_us < |
410 kEncodingTimeMeasureWindowMs * rtc::kNumMicrosecsPerMillisec) { | 323 kEncodingTimeMeasureWindowMs * rtc::kNumMicrosecsPerMillisec) |
411 break; | 324 break; |
412 } | |
413 if (timing.last_send_us != -1) { | 325 if (timing.last_send_us != -1) { |
414 int encode_duration_us = | 326 int encode_duration_us = |
415 static_cast<int>(timing.last_send_us - timing.capture_us); | 327 static_cast<int>(timing.last_send_us - timing.capture_us); |
416 if (encoder_timing_) { | 328 if (encoder_timing_) { |
417 // TODO(nisse): Update encoder_timing_ to also use us units. | 329 // TODO(nisse): Update encoder_timing_ to also use us units. |
418 encoder_timing_->OnEncodeTiming(timing.capture_time_us / | 330 encoder_timing_->OnEncodeTiming(timing.capture_time_us / |
419 rtc::kNumMicrosecsPerMillisec, | 331 rtc::kNumMicrosecsPerMillisec, |
420 encode_duration_us / | 332 encode_duration_us / |
421 rtc::kNumMicrosecsPerMillisec); | 333 rtc::kNumMicrosecsPerMillisec); |
422 } | 334 } |
(...skipping 54 matching lines...) Expand 10 before | Expand all | Expand 10 after Loading... |
477 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 389 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
478 | 390 |
479 LOG(LS_VERBOSE) << " Frame stats: " | 391 LOG(LS_VERBOSE) << " Frame stats: " |
480 << " encode usage " << metrics_->encode_usage_percent | 392 << " encode usage " << metrics_->encode_usage_percent |
481 << " overuse detections " << num_overuse_detections_ | 393 << " overuse detections " << num_overuse_detections_ |
482 << " rampup delay " << rampup_delay; | 394 << " rampup delay " << rampup_delay; |
483 } | 395 } |
484 | 396 |
485 bool OveruseFrameDetector::IsOverusing(const CpuOveruseMetrics& metrics) { | 397 bool OveruseFrameDetector::IsOverusing(const CpuOveruseMetrics& metrics) { |
486 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 398 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
487 | |
488 if (metrics.encode_usage_percent >= | 399 if (metrics.encode_usage_percent >= |
489 options_.high_encode_usage_threshold_percent) { | 400 options_.high_encode_usage_threshold_percent) { |
490 ++checks_above_threshold_; | 401 ++checks_above_threshold_; |
491 } else { | 402 } else { |
492 checks_above_threshold_ = 0; | 403 checks_above_threshold_ = 0; |
493 } | 404 } |
494 return checks_above_threshold_ >= options_.high_threshold_consecutive_count; | 405 return checks_above_threshold_ >= options_.high_threshold_consecutive_count; |
495 } | 406 } |
496 | 407 |
497 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, | 408 bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics, |
498 int64_t time_now) { | 409 int64_t time_now) { |
499 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); | 410 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_); |
500 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; | 411 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_; |
501 if (time_now < last_rampup_time_ms_ + delay) | 412 if (time_now < last_rampup_time_ms_ + delay) |
502 return false; | 413 return false; |
503 | 414 |
504 return metrics.encode_usage_percent < | 415 return metrics.encode_usage_percent < |
505 options_.low_encode_usage_threshold_percent; | 416 options_.low_encode_usage_threshold_percent; |
506 } | 417 } |
507 } // namespace webrtc | 418 } // namespace webrtc |
OLD | NEW |