blob: c45a0179eae635e780b74597444ea2292e57537e [file] [log] [blame]
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +00001/*
2 * Copyright (c) 2013 The WebRTC project authors. All Rights Reserved.
3 *
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
6 * tree. An additional intellectual property rights grant can be found
7 * in the file PATENTS. All contributing project authors may
8 * be found in the AUTHORS file in the root of the source tree.
9 */
10
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020011#include "video/overuse_frame_detector.h"
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000012
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000013#include <assert.h>
pbos@webrtc.orga9575702013-08-30 17:16:32 +000014#include <math.h>
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000015
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000016#include <algorithm>
17#include <list>
asapersson@webrtc.org734a5322014-06-10 06:35:22 +000018#include <map>
sprangc5d62e22017-04-02 23:53:04 -070019#include <string>
20#include <utility>
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000021
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020022#include "api/video/video_frame.h"
23#include "common_video/include/frame_callback.h"
24#include "rtc_base/checks.h"
25#include "rtc_base/logging.h"
Niels Möller7dc26b72017-12-06 10:27:48 +010026#include "rtc_base/numerics/exp_filter.h"
Mirko Bonadei92ea95e2017-09-15 06:47:31 +020027#include "rtc_base/timeutils.h"
28#include "system_wrappers/include/field_trial.h"
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000029
pbosa1025072016-05-14 03:04:19 -070030#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
torbjorng448468d2016-02-10 08:11:57 -080031#include <mach/mach.h>
pbosa1025072016-05-14 03:04:19 -070032#endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
torbjorng448468d2016-02-10 08:11:57 -080033
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000034namespace webrtc {
35
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000036namespace {
perkjd52063f2016-09-07 06:32:18 -070037const int64_t kCheckForOveruseIntervalMs = 5000;
38const int64_t kTimeToFirstCheckForOveruseMs = 100;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000039
pbos@webrtc.orga9575702013-08-30 17:16:32 +000040// Delay between consecutive rampups. (Used for quick recovery.)
41const int kQuickRampUpDelayMs = 10 * 1000;
42// Delay between rampup attempts. Initially uses standard, scales up to max.
asapersson@webrtc.org23a4d852014-08-13 14:33:49 +000043const int kStandardRampUpDelayMs = 40 * 1000;
asapersson@webrtc.org2881ab12014-06-12 08:46:46 +000044const int kMaxRampUpDelayMs = 240 * 1000;
pbos@webrtc.orga9575702013-08-30 17:16:32 +000045// Expontential back-off factor, to prevent annoying up-down behaviour.
46const double kRampUpBackoffFactor = 2.0;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000047
asapersson@webrtc.orgd9803072014-06-16 14:27:19 +000048// Max number of overuses detected before always applying the rampup delay.
asapersson@webrtc.org23a4d852014-08-13 14:33:49 +000049const int kMaxOverusesBeforeApplyRampupDelay = 4;
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000050
Niels Möller7dc26b72017-12-06 10:27:48 +010051// The maximum exponent to use in VCMExpFilter.
52const float kMaxExp = 7.0f;
53// Default value used before first reconfiguration.
54const int kDefaultFrameRate = 30;
55// Default sample diff, default frame rate.
56const float kDefaultSampleDiffMs = 1000.0f / kDefaultFrameRate;
57// A factor applied to the sample diff on OnTargetFramerateUpdated to determine
58// a max limit for the sample diff. For instance, with a framerate of 30fps,
59// the sample diff is capped to (1000 / 30) * 1.35 = 45ms. This prevents
60// triggering too soon if there are individual very large outliers.
61const float kMaxSampleDiffMarginFactor = 1.35f;
62// Minimum framerate allowed for usage calculation. This prevents crazy long
63// encode times from being accepted if the frame rate happens to be low.
64const int kMinFramerate = 7;
65const int kMaxFramerate = 30;
66
sprangb1ca0732017-02-01 08:38:12 -080067const auto kScaleReasonCpu = AdaptationObserverInterface::AdaptReason::kCpu;
torbjorng448468d2016-02-10 08:11:57 -080068
asapersson@webrtc.org9aed0022014-10-16 06:57:12 +000069// Class for calculating the processing usage on the send-side (the average
70// processing time of a frame divided by the average time difference between
71// captured frames).
Niels Möller904f8692017-12-07 11:22:39 +010072class SendProcessingUsage : public OveruseFrameDetector::ProcessingUsage {
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000073 public:
Niels Möllere08cf3a2017-12-07 15:23:58 +010074 explicit SendProcessingUsage(const CpuOveruseOptions& options,
75 EncodedFrameObserver* encoder_timing)
Niels Möller7dc26b72017-12-06 10:27:48 +010076 : kWeightFactorFrameDiff(0.998f),
77 kWeightFactorProcessing(0.995f),
78 kInitialSampleDiffMs(40.0f),
Niels Möller7dc26b72017-12-06 10:27:48 +010079 options_(options),
Niels Möllere08cf3a2017-12-07 15:23:58 +010080 encoder_timing_(encoder_timing),
81 count_(0),
82 last_processed_capture_time_us_(-1),
Niels Möller7dc26b72017-12-06 10:27:48 +010083 max_sample_diff_ms_(kDefaultSampleDiffMs * kMaxSampleDiffMarginFactor),
84 filtered_processing_ms_(new rtc::ExpFilter(kWeightFactorProcessing)),
85 filtered_frame_diff_ms_(new rtc::ExpFilter(kWeightFactorFrameDiff)) {
asapersson@webrtc.orgce12f1f2014-03-24 21:59:16 +000086 Reset();
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000087 }
sprangc5d62e22017-04-02 23:53:04 -070088 virtual ~SendProcessingUsage() {}
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000089
Niels Möller904f8692017-12-07 11:22:39 +010090 void Reset() override {
Niels Möllere08cf3a2017-12-07 15:23:58 +010091 frame_timing_.clear();
Niels Möller7dc26b72017-12-06 10:27:48 +010092 count_ = 0;
Niels Möllere08cf3a2017-12-07 15:23:58 +010093 last_processed_capture_time_us_ = -1;
Niels Möller7dc26b72017-12-06 10:27:48 +010094 max_sample_diff_ms_ = kDefaultSampleDiffMs * kMaxSampleDiffMarginFactor;
95 filtered_frame_diff_ms_->Reset(kWeightFactorFrameDiff);
96 filtered_frame_diff_ms_->Apply(1.0f, kInitialSampleDiffMs);
97 filtered_processing_ms_->Reset(kWeightFactorProcessing);
98 filtered_processing_ms_->Apply(1.0f, InitialProcessingMs());
asapersson@webrtc.orgce12f1f2014-03-24 21:59:16 +000099 }
100
Niels Möller904f8692017-12-07 11:22:39 +0100101 void SetMaxSampleDiffMs(float diff_ms) override {
102 max_sample_diff_ms_ = diff_ms;
103 }
sprangfda496a2017-06-15 04:21:07 -0700104
Niels Möllere08cf3a2017-12-07 15:23:58 +0100105 void FrameCaptured(const VideoFrame& frame,
106 int64_t time_when_first_seen_us,
107 int64_t last_capture_time_us) override {
108 if (last_capture_time_us != -1)
109 AddCaptureSample(1e-3 * (time_when_first_seen_us - last_capture_time_us));
110
111 frame_timing_.push_back(FrameTiming(frame.timestamp_us(), frame.timestamp(),
112 time_when_first_seen_us));
Niels Möller7dc26b72017-12-06 10:27:48 +0100113 }
114
Niels Möllere08cf3a2017-12-07 15:23:58 +0100115 rtc::Optional<int> FrameSent(uint32_t timestamp,
116 int64_t time_sent_in_us) override {
117 rtc::Optional<int> encode_duration_us;
118 // Delay before reporting actual encoding time, used to have the ability to
119 // detect total encoding time when encoding more than one layer. Encoding is
120 // here assumed to finish within a second (or that we get enough long-time
121 // samples before one second to trigger an overuse even when this is not the
122 // case).
123 static const int64_t kEncodingTimeMeasureWindowMs = 1000;
124 for (auto& it : frame_timing_) {
125 if (it.timestamp == timestamp) {
126 it.last_send_us = time_sent_in_us;
127 break;
128 }
129 }
130 // TODO(pbos): Handle the case/log errors when not finding the corresponding
131 // frame (either very slow encoding or incorrect wrong timestamps returned
132 // from the encoder).
133 // This is currently the case for all frames on ChromeOS, so logging them
134 // would be spammy, and triggering overuse would be wrong.
135 // https://crbug.com/350106
136 while (!frame_timing_.empty()) {
137 FrameTiming timing = frame_timing_.front();
138 if (time_sent_in_us - timing.capture_us <
139 kEncodingTimeMeasureWindowMs * rtc::kNumMicrosecsPerMillisec) {
140 break;
141 }
142 if (timing.last_send_us != -1) {
143 encode_duration_us.emplace(
144 static_cast<int>(timing.last_send_us - timing.capture_us));
145 if (encoder_timing_) {
146 // TODO(nisse): Update encoder_timing_ to also use us units.
147 encoder_timing_->OnEncodeTiming(
148 timing.capture_time_us / rtc::kNumMicrosecsPerMillisec,
149 *encode_duration_us / rtc::kNumMicrosecsPerMillisec);
150 }
151 if (last_processed_capture_time_us_ != -1) {
152 int64_t diff_us = timing.capture_us - last_processed_capture_time_us_;
153 AddSample(1e-3 * (*encode_duration_us), 1e-3 * diff_us);
154 }
155 last_processed_capture_time_us_ = timing.capture_us;
156 }
157 frame_timing_.pop_front();
158 }
159 return encode_duration_us;
Niels Möller7dc26b72017-12-06 10:27:48 +0100160 }
161
Niels Möller904f8692017-12-07 11:22:39 +0100162 int Value() override {
Niels Möller7dc26b72017-12-06 10:27:48 +0100163 if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) {
164 return static_cast<int>(InitialUsageInPercent() + 0.5f);
asapersson@webrtc.orgce12f1f2014-03-24 21:59:16 +0000165 }
Niels Möller7dc26b72017-12-06 10:27:48 +0100166 float frame_diff_ms = std::max(filtered_frame_diff_ms_->filtered(), 1.0f);
167 frame_diff_ms = std::min(frame_diff_ms, max_sample_diff_ms_);
168 float encode_usage_percent =
169 100.0f * filtered_processing_ms_->filtered() / frame_diff_ms;
170 return static_cast<int>(encode_usage_percent + 0.5);
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000171 }
172
asapersson@webrtc.org2881ab12014-06-12 08:46:46 +0000173 private:
Niels Möllere08cf3a2017-12-07 15:23:58 +0100174 struct FrameTiming {
175 FrameTiming(int64_t capture_time_us, uint32_t timestamp, int64_t now)
176 : capture_time_us(capture_time_us),
177 timestamp(timestamp),
178 capture_us(now),
179 last_send_us(-1) {}
180 int64_t capture_time_us;
181 uint32_t timestamp;
182 int64_t capture_us;
183 int64_t last_send_us;
184 };
185
186 void AddCaptureSample(float sample_ms) {
187 float exp = sample_ms / kDefaultSampleDiffMs;
188 exp = std::min(exp, kMaxExp);
189 filtered_frame_diff_ms_->Apply(exp, sample_ms);
190 }
191
192 void AddSample(float processing_ms, int64_t diff_last_sample_ms) {
193 ++count_;
194 float exp = diff_last_sample_ms / kDefaultSampleDiffMs;
195 exp = std::min(exp, kMaxExp);
196 filtered_processing_ms_->Apply(exp, processing_ms);
197 }
198
Niels Möller7dc26b72017-12-06 10:27:48 +0100199 float InitialUsageInPercent() const {
200 // Start in between the underuse and overuse threshold.
201 return (options_.low_encode_usage_threshold_percent +
202 options_.high_encode_usage_threshold_percent) / 2.0f;
203 }
204
205 float InitialProcessingMs() const {
206 return InitialUsageInPercent() * kInitialSampleDiffMs / 100;
207 }
208
209 const float kWeightFactorFrameDiff;
210 const float kWeightFactorProcessing;
211 const float kInitialSampleDiffMs;
Niels Möllere08cf3a2017-12-07 15:23:58 +0100212
Peter Boström4b91bd02015-06-26 06:58:16 +0200213 const CpuOveruseOptions options_;
Niels Möllere08cf3a2017-12-07 15:23:58 +0100214 EncodedFrameObserver* const encoder_timing_;
215 std::list<FrameTiming> frame_timing_;
216 uint64_t count_;
217 int64_t last_processed_capture_time_us_;
Niels Möller7dc26b72017-12-06 10:27:48 +0100218 float max_sample_diff_ms_;
219 std::unique_ptr<rtc::ExpFilter> filtered_processing_ms_;
220 std::unique_ptr<rtc::ExpFilter> filtered_frame_diff_ms_;
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000221};
222
sprangc5d62e22017-04-02 23:53:04 -0700223// Class used for manual testing of overuse, enabled via field trial flag.
Niels Möller904f8692017-12-07 11:22:39 +0100224class OverdoseInjector : public SendProcessingUsage {
sprangc5d62e22017-04-02 23:53:04 -0700225 public:
226 OverdoseInjector(const CpuOveruseOptions& options,
Niels Möllere08cf3a2017-12-07 15:23:58 +0100227 EncodedFrameObserver* encoder_timing,
sprangc5d62e22017-04-02 23:53:04 -0700228 int64_t normal_period_ms,
229 int64_t overuse_period_ms,
230 int64_t underuse_period_ms)
Niels Möllere08cf3a2017-12-07 15:23:58 +0100231 : SendProcessingUsage(options, encoder_timing),
sprangc5d62e22017-04-02 23:53:04 -0700232 normal_period_ms_(normal_period_ms),
233 overuse_period_ms_(overuse_period_ms),
234 underuse_period_ms_(underuse_period_ms),
235 state_(State::kNormal),
236 last_toggling_ms_(-1) {
237 RTC_DCHECK_GT(overuse_period_ms, 0);
238 RTC_DCHECK_GT(normal_period_ms, 0);
Mirko Bonadei675513b2017-11-09 11:09:25 +0100239 RTC_LOG(LS_INFO) << "Simulating overuse with intervals " << normal_period_ms
240 << "ms normal mode, " << overuse_period_ms
241 << "ms overuse mode.";
sprangc5d62e22017-04-02 23:53:04 -0700242 }
243
244 ~OverdoseInjector() override {}
245
246 int Value() override {
247 int64_t now_ms = rtc::TimeMillis();
248 if (last_toggling_ms_ == -1) {
249 last_toggling_ms_ = now_ms;
250 } else {
251 switch (state_) {
252 case State::kNormal:
253 if (now_ms > last_toggling_ms_ + normal_period_ms_) {
254 state_ = State::kOveruse;
255 last_toggling_ms_ = now_ms;
Mirko Bonadei675513b2017-11-09 11:09:25 +0100256 RTC_LOG(LS_INFO) << "Simulating CPU overuse.";
sprangc5d62e22017-04-02 23:53:04 -0700257 }
258 break;
259 case State::kOveruse:
260 if (now_ms > last_toggling_ms_ + overuse_period_ms_) {
261 state_ = State::kUnderuse;
262 last_toggling_ms_ = now_ms;
Mirko Bonadei675513b2017-11-09 11:09:25 +0100263 RTC_LOG(LS_INFO) << "Simulating CPU underuse.";
sprangc5d62e22017-04-02 23:53:04 -0700264 }
265 break;
266 case State::kUnderuse:
267 if (now_ms > last_toggling_ms_ + underuse_period_ms_) {
268 state_ = State::kNormal;
269 last_toggling_ms_ = now_ms;
Mirko Bonadei675513b2017-11-09 11:09:25 +0100270 RTC_LOG(LS_INFO) << "Actual CPU overuse measurements in effect.";
sprangc5d62e22017-04-02 23:53:04 -0700271 }
272 break;
273 }
274 }
275
276 rtc::Optional<int> overried_usage_value;
277 switch (state_) {
278 case State::kNormal:
279 break;
280 case State::kOveruse:
281 overried_usage_value.emplace(250);
282 break;
283 case State::kUnderuse:
284 overried_usage_value.emplace(5);
285 break;
286 }
Niels Möller7dc26b72017-12-06 10:27:48 +0100287
sprangc5d62e22017-04-02 23:53:04 -0700288 return overried_usage_value.value_or(SendProcessingUsage::Value());
289 }
290
291 private:
292 const int64_t normal_period_ms_;
293 const int64_t overuse_period_ms_;
294 const int64_t underuse_period_ms_;
295 enum class State { kNormal, kOveruse, kUnderuse } state_;
296 int64_t last_toggling_ms_;
297};
298
Niels Möller904f8692017-12-07 11:22:39 +0100299} // namespace
300
301CpuOveruseOptions::CpuOveruseOptions()
302 : high_encode_usage_threshold_percent(85),
303 frame_timeout_interval_ms(1500),
304 min_frame_samples(120),
305 min_process_count(3),
306 high_threshold_consecutive_count(2) {
307#if defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
308 // This is proof-of-concept code for letting the physical core count affect
309 // the interval into which we attempt to scale. For now, the code is Mac OS
310 // specific, since that's the platform were we saw most problems.
311 // TODO(torbjorng): Enhance SystemInfo to return this metric.
312
313 mach_port_t mach_host = mach_host_self();
314 host_basic_info hbi = {};
315 mach_msg_type_number_t info_count = HOST_BASIC_INFO_COUNT;
316 kern_return_t kr =
317 host_info(mach_host, HOST_BASIC_INFO, reinterpret_cast<host_info_t>(&hbi),
318 &info_count);
319 mach_port_deallocate(mach_task_self(), mach_host);
320
321 int n_physical_cores;
322 if (kr != KERN_SUCCESS) {
323 // If we couldn't get # of physical CPUs, don't panic. Assume we have 1.
324 n_physical_cores = 1;
325 RTC_LOG(LS_ERROR)
326 << "Failed to determine number of physical cores, assuming 1";
327 } else {
328 n_physical_cores = hbi.physical_cpu;
329 RTC_LOG(LS_INFO) << "Number of physical cores:" << n_physical_cores;
330 }
331
332 // Change init list default for few core systems. The assumption here is that
333 // encoding, which we measure here, takes about 1/4 of the processing of a
334 // two-way call. This is roughly true for x86 using both vp8 and vp9 without
335 // hardware encoding. Since we don't affect the incoming stream here, we only
336 // control about 1/2 of the total processing needs, but this is not taken into
337 // account.
338 if (n_physical_cores == 1)
339 high_encode_usage_threshold_percent = 20; // Roughly 1/4 of 100%.
340 else if (n_physical_cores == 2)
341 high_encode_usage_threshold_percent = 40; // Roughly 1/4 of 200%.
342#endif // defined(WEBRTC_MAC) && !defined(WEBRTC_IOS)
343
344 // Note that we make the interval 2x+epsilon wide, since libyuv scaling steps
345 // are close to that (when squared). This wide interval makes sure that
346 // scaling up or down does not jump all the way across the interval.
347 low_encode_usage_threshold_percent =
348 (high_encode_usage_threshold_percent - 1) / 2;
349}
350
351std::unique_ptr<OveruseFrameDetector::ProcessingUsage>
Niels Möllere08cf3a2017-12-07 15:23:58 +0100352OveruseFrameDetector::CreateProcessingUsage(
353 const CpuOveruseOptions& options,
354 EncodedFrameObserver* encoder_timing) {
Niels Möller904f8692017-12-07 11:22:39 +0100355 std::unique_ptr<ProcessingUsage> instance;
sprangc5d62e22017-04-02 23:53:04 -0700356 std::string toggling_interval =
357 field_trial::FindFullName("WebRTC-ForceSimulatedOveruseIntervalMs");
358 if (!toggling_interval.empty()) {
359 int normal_period_ms = 0;
360 int overuse_period_ms = 0;
361 int underuse_period_ms = 0;
362 if (sscanf(toggling_interval.c_str(), "%d-%d-%d", &normal_period_ms,
363 &overuse_period_ms, &underuse_period_ms) == 3) {
364 if (normal_period_ms > 0 && overuse_period_ms > 0 &&
365 underuse_period_ms > 0) {
Niels Möllere08cf3a2017-12-07 15:23:58 +0100366 instance = rtc::MakeUnique<OverdoseInjector>(
367 options, encoder_timing, normal_period_ms, overuse_period_ms,
368 underuse_period_ms);
sprangc5d62e22017-04-02 23:53:04 -0700369 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100370 RTC_LOG(LS_WARNING)
sprangc5d62e22017-04-02 23:53:04 -0700371 << "Invalid (non-positive) normal/overuse/underuse periods: "
372 << normal_period_ms << " / " << overuse_period_ms << " / "
373 << underuse_period_ms;
374 }
375 } else {
Mirko Bonadei675513b2017-11-09 11:09:25 +0100376 RTC_LOG(LS_WARNING) << "Malformed toggling interval: "
377 << toggling_interval;
sprangc5d62e22017-04-02 23:53:04 -0700378 }
379 }
380
381 if (!instance) {
382 // No valid overuse simulation parameters set, use normal usage class.
Niels Möllere08cf3a2017-12-07 15:23:58 +0100383 instance = rtc::MakeUnique<SendProcessingUsage>(options, encoder_timing);
sprangc5d62e22017-04-02 23:53:04 -0700384 }
385
386 return instance;
387}
388
perkjd52063f2016-09-07 06:32:18 -0700389class OveruseFrameDetector::CheckOveruseTask : public rtc::QueuedTask {
390 public:
391 explicit CheckOveruseTask(OveruseFrameDetector* overuse_detector)
392 : overuse_detector_(overuse_detector) {
393 rtc::TaskQueue::Current()->PostDelayedTask(
394 std::unique_ptr<rtc::QueuedTask>(this), kTimeToFirstCheckForOveruseMs);
395 }
396
397 void Stop() {
398 RTC_CHECK(task_checker_.CalledSequentially());
399 overuse_detector_ = nullptr;
400 }
401
402 private:
403 bool Run() override {
404 RTC_CHECK(task_checker_.CalledSequentially());
405 if (!overuse_detector_)
406 return true; // This will make the task queue delete this task.
407 overuse_detector_->CheckForOveruse();
408
409 rtc::TaskQueue::Current()->PostDelayedTask(
410 std::unique_ptr<rtc::QueuedTask>(this), kCheckForOveruseIntervalMs);
411 // Return false to prevent this task from being deleted. Ownership has been
412 // transferred to the task queue when PostDelayedTask was called.
413 return false;
414 }
415 rtc::SequencedTaskChecker task_checker_;
416 OveruseFrameDetector* overuse_detector_;
417};
418
pbos@webrtc.org3e6e2712015-02-26 12:19:31 +0000419OveruseFrameDetector::OveruseFrameDetector(
Peter Boström4b91bd02015-06-26 06:58:16 +0200420 const CpuOveruseOptions& options,
sprangb1ca0732017-02-01 08:38:12 -0800421 AdaptationObserverInterface* observer,
Peter Boströme4499152016-02-05 11:13:28 +0100422 EncodedFrameObserver* encoder_timing,
pbos@webrtc.org3e6e2712015-02-26 12:19:31 +0000423 CpuOveruseMetricsObserver* metrics_observer)
perkjd52063f2016-09-07 06:32:18 -0700424 : check_overuse_task_(nullptr),
425 options_(options),
Peter Boström4b91bd02015-06-26 06:58:16 +0200426 observer_(observer),
pbos@webrtc.org3e6e2712015-02-26 12:19:31 +0000427 metrics_observer_(metrics_observer),
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000428 num_process_times_(0),
nissee0e3bdf2017-01-18 02:16:20 -0800429 // TODO(nisse): Use rtc::Optional
430 last_capture_time_us_(-1),
asapersson74d85e12015-09-24 00:53:32 -0700431 num_pixels_(0),
Niels Möller7dc26b72017-12-06 10:27:48 +0100432 max_framerate_(kDefaultFrameRate),
Peter Boströme4499152016-02-05 11:13:28 +0100433 last_overuse_time_ms_(-1),
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000434 checks_above_threshold_(0),
asapersson@webrtc.orgd9803072014-06-16 14:27:19 +0000435 num_overuse_detections_(0),
Peter Boströme4499152016-02-05 11:13:28 +0100436 last_rampup_time_ms_(-1),
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000437 in_quick_rampup_(false),
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000438 current_rampup_delay_ms_(kStandardRampUpDelayMs),
Niels Möllere08cf3a2017-12-07 15:23:58 +0100439 usage_(CreateProcessingUsage(options, encoder_timing)) {
perkjd52063f2016-09-07 06:32:18 -0700440 task_checker_.Detach();
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000441}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000442
443OveruseFrameDetector::~OveruseFrameDetector() {
perkjd52063f2016-09-07 06:32:18 -0700444 RTC_DCHECK(!check_overuse_task_) << "StopCheckForOverUse must be called.";
445}
446
447void OveruseFrameDetector::StartCheckForOveruse() {
448 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
449 RTC_DCHECK(!check_overuse_task_);
450 check_overuse_task_ = new CheckOveruseTask(this);
451}
452void OveruseFrameDetector::StopCheckForOveruse() {
453 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
454 check_overuse_task_->Stop();
455 check_overuse_task_ = nullptr;
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000456}
457
Peter Boströme4499152016-02-05 11:13:28 +0100458void OveruseFrameDetector::EncodedFrameTimeMeasured(int encode_duration_ms) {
perkjd52063f2016-09-07 06:32:18 -0700459 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Peter Boströme4499152016-02-05 11:13:28 +0100460 if (!metrics_)
461 metrics_ = rtc::Optional<CpuOveruseMetrics>(CpuOveruseMetrics());
462 metrics_->encode_usage_percent = usage_->Value();
asapersson@webrtc.org9aed0022014-10-16 06:57:12 +0000463
Peter Boströme4499152016-02-05 11:13:28 +0100464 metrics_observer_->OnEncodedFrameTimeMeasured(encode_duration_ms, *metrics_);
asapersson@webrtc.orgab6bf4f2014-05-27 07:43:15 +0000465}
466
asapersson@webrtc.org8a8c3ef2014-03-20 13:15:01 +0000467bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const {
perkjd52063f2016-09-07 06:32:18 -0700468 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
asapersson@webrtc.org8a8c3ef2014-03-20 13:15:01 +0000469 if (num_pixels != num_pixels_) {
470 return true;
471 }
472 return false;
473}
474
nissee0e3bdf2017-01-18 02:16:20 -0800475bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now_us) const {
perkjd52063f2016-09-07 06:32:18 -0700476 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
nissee0e3bdf2017-01-18 02:16:20 -0800477 if (last_capture_time_us_ == -1)
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000478 return false;
nissee0e3bdf2017-01-18 02:16:20 -0800479 return (now_us - last_capture_time_us_) >
480 options_.frame_timeout_interval_ms * rtc::kNumMicrosecsPerMillisec;
asapersson@webrtc.org8a8c3ef2014-03-20 13:15:01 +0000481}
482
Niels Möller7dc26b72017-12-06 10:27:48 +0100483void OveruseFrameDetector::ResetAll(int num_pixels) {
484 // Reset state, as a result resolution being changed. Do not however change
485 // the current frame rate back to the default.
perkjd52063f2016-09-07 06:32:18 -0700486 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Niels Möller7dc26b72017-12-06 10:27:48 +0100487 num_pixels_ = num_pixels;
asapersson@webrtc.org9aed0022014-10-16 06:57:12 +0000488 usage_->Reset();
nissee0e3bdf2017-01-18 02:16:20 -0800489 last_capture_time_us_ = -1;
asapersson@webrtc.org8a8c3ef2014-03-20 13:15:01 +0000490 num_process_times_ = 0;
Peter Boströme4499152016-02-05 11:13:28 +0100491 metrics_ = rtc::Optional<CpuOveruseMetrics>();
Niels Möller7dc26b72017-12-06 10:27:48 +0100492 OnTargetFramerateUpdated(max_framerate_);
sprangfda496a2017-06-15 04:21:07 -0700493}
494
Niels Möller7dc26b72017-12-06 10:27:48 +0100495void OveruseFrameDetector::OnTargetFramerateUpdated(int framerate_fps) {
perkjd52063f2016-09-07 06:32:18 -0700496 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Niels Möller7dc26b72017-12-06 10:27:48 +0100497 RTC_DCHECK_GE(framerate_fps, 0);
498 max_framerate_ = std::min(kMaxFramerate, framerate_fps);
499 usage_->SetMaxSampleDiffMs((1000 / std::max(kMinFramerate, max_framerate_)) *
500 kMaxSampleDiffMarginFactor);
asapersson@webrtc.org9aed0022014-10-16 06:57:12 +0000501}
502
Niels Möller7dc26b72017-12-06 10:27:48 +0100503void OveruseFrameDetector::FrameCaptured(const VideoFrame& frame,
504 int64_t time_when_first_seen_us) {
perkjd52063f2016-09-07 06:32:18 -0700505 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Niels Möllereee7ced2017-12-01 11:25:01 +0100506
Niels Möller7dc26b72017-12-06 10:27:48 +0100507 if (FrameSizeChanged(frame.width() * frame.height()) ||
508 FrameTimeoutDetected(time_when_first_seen_us)) {
509 ResetAll(frame.width() * frame.height());
510 }
511
Niels Möllere08cf3a2017-12-07 15:23:58 +0100512 usage_->FrameCaptured(frame, time_when_first_seen_us, last_capture_time_us_);
Niels Möller7dc26b72017-12-06 10:27:48 +0100513 last_capture_time_us_ = time_when_first_seen_us;
Niels Möller7dc26b72017-12-06 10:27:48 +0100514}
515
516void OveruseFrameDetector::FrameSent(uint32_t timestamp,
517 int64_t time_sent_in_us) {
518 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
Niels Möllere08cf3a2017-12-07 15:23:58 +0100519 rtc::Optional<int> encode_duration_us =
520 usage_->FrameSent(timestamp, time_sent_in_us);
521
522 if (encode_duration_us) {
523 EncodedFrameTimeMeasured(*encode_duration_us /
524 rtc::kNumMicrosecsPerMillisec);
Peter Boströme4499152016-02-05 11:13:28 +0100525 }
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +0000526}
527
perkjd52063f2016-09-07 06:32:18 -0700528void OveruseFrameDetector::CheckForOveruse() {
529 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
530 ++num_process_times_;
531 if (num_process_times_ <= options_.min_process_count || !metrics_)
532 return;
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000533
nissee0e3bdf2017-01-18 02:16:20 -0800534 int64_t now_ms = rtc::TimeMillis();
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000535
perkjd52063f2016-09-07 06:32:18 -0700536 if (IsOverusing(*metrics_)) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000537 // If the last thing we did was going up, and now have to back down, we need
538 // to check if this peak was short. If so we should back off to avoid going
539 // back and forth between this load, the system doesn't seem to handle it.
Peter Boströme4499152016-02-05 11:13:28 +0100540 bool check_for_backoff = last_rampup_time_ms_ > last_overuse_time_ms_;
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000541 if (check_for_backoff) {
nissee0e3bdf2017-01-18 02:16:20 -0800542 if (now_ms - last_rampup_time_ms_ < kStandardRampUpDelayMs ||
asapersson@webrtc.orgd9803072014-06-16 14:27:19 +0000543 num_overuse_detections_ > kMaxOverusesBeforeApplyRampupDelay) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000544 // Going up was not ok for very long, back off.
545 current_rampup_delay_ms_ *= kRampUpBackoffFactor;
546 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs)
547 current_rampup_delay_ms_ = kMaxRampUpDelayMs;
548 } else {
549 // Not currently backing off, reset rampup delay.
550 current_rampup_delay_ms_ = kStandardRampUpDelayMs;
551 }
552 }
553
nissee0e3bdf2017-01-18 02:16:20 -0800554 last_overuse_time_ms_ = now_ms;
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000555 in_quick_rampup_ = false;
556 checks_above_threshold_ = 0;
asapersson@webrtc.orgd9803072014-06-16 14:27:19 +0000557 ++num_overuse_detections_;
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000558
Peter Boström74f6e9e2016-04-04 17:56:10 +0200559 if (observer_)
sprangb1ca0732017-02-01 08:38:12 -0800560 observer_->AdaptDown(kScaleReasonCpu);
nissee0e3bdf2017-01-18 02:16:20 -0800561 } else if (IsUnderusing(*metrics_, now_ms)) {
562 last_rampup_time_ms_ = now_ms;
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000563 in_quick_rampup_ = true;
564
Peter Boström74f6e9e2016-04-04 17:56:10 +0200565 if (observer_)
sprangb1ca0732017-02-01 08:38:12 -0800566 observer_->AdaptUp(kScaleReasonCpu);
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000567 }
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000568
mflodman@webrtc.org5574dac2014-04-07 10:56:31 +0000569 int rampup_delay =
570 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
asapersson74d85e12015-09-24 00:53:32 -0700571
Mirko Bonadei675513b2017-11-09 11:09:25 +0100572 RTC_LOG(LS_VERBOSE) << " Frame stats: "
573 << " encode usage " << metrics_->encode_usage_percent
574 << " overuse detections " << num_overuse_detections_
575 << " rampup delay " << rampup_delay;
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000576}
577
asapersson74d85e12015-09-24 00:53:32 -0700578bool OveruseFrameDetector::IsOverusing(const CpuOveruseMetrics& metrics) {
perkjd52063f2016-09-07 06:32:18 -0700579 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
sprangc5d62e22017-04-02 23:53:04 -0700580
Peter Boström01f364e2016-01-07 16:38:25 +0100581 if (metrics.encode_usage_percent >=
582 options_.high_encode_usage_threshold_percent) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000583 ++checks_above_threshold_;
584 } else {
585 checks_above_threshold_ = 0;
586 }
asapersson@webrtc.org8a8c3ef2014-03-20 13:15:01 +0000587 return checks_above_threshold_ >= options_.high_threshold_consecutive_count;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000588}
589
asapersson74d85e12015-09-24 00:53:32 -0700590bool OveruseFrameDetector::IsUnderusing(const CpuOveruseMetrics& metrics,
591 int64_t time_now) {
perkjd52063f2016-09-07 06:32:18 -0700592 RTC_DCHECK_CALLED_SEQUENTIALLY(&task_checker_);
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000593 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
Peter Boströme4499152016-02-05 11:13:28 +0100594 if (time_now < last_rampup_time_ms_ + delay)
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000595 return false;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000596
Peter Boström01f364e2016-01-07 16:38:25 +0100597 return metrics.encode_usage_percent <
598 options_.low_encode_usage_threshold_percent;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000599}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000600} // namespace webrtc