blob: 21aa7690b130f4e878fe329080c324ee9074dfb8 [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
11#include "webrtc/video_engine/overuse_frame_detector.h"
12
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>
18
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000019#include "webrtc/modules/video_coding/utility/include/exp_filter.h"
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000020#include "webrtc/system_wrappers/interface/clock.h"
21#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
pbos@webrtc.orga9575702013-08-30 17:16:32 +000022#include "webrtc/system_wrappers/interface/trace.h"
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000023#include "webrtc/video_engine/include/vie_base.h"
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000024
25namespace webrtc {
26
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000027// TODO(mflodman) Test different values for all of these to trigger correctly,
28// avoid fluctuations etc.
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000029namespace {
pbos@webrtc.orga9575702013-08-30 17:16:32 +000030const int64_t kProcessIntervalMs = 5000;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000031
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +000032// Number of initial process times before reporting.
33const int64_t kMinProcessCountBeforeReporting = 3;
34
35const int64_t kFrameTimeoutIntervalMs = 1500;
36
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000037// Consecutive checks above threshold to trigger overuse.
pbos@webrtc.orga9575702013-08-30 17:16:32 +000038const int kConsecutiveChecksAboveThreshold = 2;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000039
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000040// Minimum samples required to perform a check.
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +000041const size_t kMinFrameSampleCount = 120;
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000042
43// Weight factor to apply to the standard deviation.
44const float kWeightFactor = 0.997f;
45
46// Weight factor to apply to the average.
47const float kWeightFactorMean = 0.98f;
48
pbos@webrtc.orga9575702013-08-30 17:16:32 +000049// Delay between consecutive rampups. (Used for quick recovery.)
50const int kQuickRampUpDelayMs = 10 * 1000;
51// Delay between rampup attempts. Initially uses standard, scales up to max.
52const int kStandardRampUpDelayMs = 30 * 1000;
53const int kMaxRampUpDelayMs = 120 * 1000;
54// Expontential back-off factor, to prevent annoying up-down behaviour.
55const double kRampUpBackoffFactor = 2.0;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000056
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +000057// The initial average encode time (set to a fairly small value).
58const float kInitialAvgEncodeTimeMs = 5.0f;
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000059
60// The maximum exponent to use in VCMExpFilter.
61const float kSampleDiffMs = 33.0f;
62const float kMaxExp = 7.0f;
63
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000064} // namespace
65
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000066Statistics::Statistics() :
67 sum_(0.0),
68 count_(0),
69 filtered_samples_(new VCMExpFilter(kWeightFactorMean)),
70 filtered_variance_(new VCMExpFilter(kWeightFactor)) {
71}
72
73void Statistics::Reset() {
74 sum_ = 0.0;
75 count_ = 0;
76}
77
78void Statistics::AddSample(float sample_ms) {
79 sum_ += sample_ms;
80 ++count_;
81
82 if (count_ < kMinFrameSampleCount) {
83 // Initialize filtered samples.
84 filtered_samples_->Reset(kWeightFactorMean);
85 filtered_samples_->Apply(1.0f, InitialMean());
86 filtered_variance_->Reset(kWeightFactor);
87 filtered_variance_->Apply(1.0f, InitialVariance());
88 return;
89 }
90
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +000091 float exp = sample_ms / kSampleDiffMs;
92 exp = std::min(exp, kMaxExp);
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000093 filtered_samples_->Apply(exp, sample_ms);
94 filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->Value()) *
95 (sample_ms - filtered_samples_->Value()));
96}
97
98float Statistics::InitialMean() const {
99 if (count_ == 0)
100 return 0.0;
101 return sum_ / count_;
102}
103
104float Statistics::InitialVariance() const {
105 // Start in between the underuse and overuse threshold.
106 float average_stddev = (kNormalUseStdDevMs + kOveruseStdDevMs)/2.0f;
107 return average_stddev * average_stddev;
108}
109
110float Statistics::Mean() const { return filtered_samples_->Value(); }
111
112float Statistics::StdDev() const {
113 return sqrt(std::max(filtered_variance_->Value(), 0.0f));
114}
115
116uint64_t Statistics::Count() const { return count_; }
117
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000118
119// Class for calculating the average encode time.
120class OveruseFrameDetector::EncodeTimeAvg {
121 public:
122 EncodeTimeAvg()
123 : kWeightFactor(0.5f),
124 filtered_encode_time_ms_(new VCMExpFilter(kWeightFactor)) {
125 filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs);
126 }
127 ~EncodeTimeAvg() {}
128
129 void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) {
130 float exp = diff_last_sample_ms / kSampleDiffMs;
131 exp = std::min(exp, kMaxExp);
132 filtered_encode_time_ms_->Apply(exp, encode_time_ms);
133 }
134
135 int filtered_encode_time_ms() const {
136 return static_cast<int>(filtered_encode_time_ms_->Value() + 0.5);
137 }
138
139 private:
140 const float kWeightFactor;
141 scoped_ptr<VCMExpFilter> filtered_encode_time_ms_;
142};
143
144// Class for calculating the encode usage.
145class OveruseFrameDetector::EncodeUsage {
146 public:
147 EncodeUsage()
148 : kWeightFactorFrameDiff(0.998f),
149 kWeightFactorEncodeTime(0.995f),
150 filtered_encode_time_ms_(new VCMExpFilter(kWeightFactorEncodeTime)),
151 filtered_frame_diff_ms_(new VCMExpFilter(kWeightFactorFrameDiff)) {
152 filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs);
153 filtered_frame_diff_ms_->Apply(1.0f, kSampleDiffMs);
154 }
155 ~EncodeUsage() {}
156
157 void AddSample(float sample_ms) {
158 float exp = sample_ms / kSampleDiffMs;
159 exp = std::min(exp, kMaxExp);
160 filtered_frame_diff_ms_->Apply(exp, sample_ms);
161 }
162
163 void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) {
164 float exp = diff_last_sample_ms / kSampleDiffMs;
165 exp = std::min(exp, kMaxExp);
166 filtered_encode_time_ms_->Apply(exp, encode_time_ms);
167 }
168
169 int UsageInPercent() const {
170 float frame_diff_ms = std::max(filtered_frame_diff_ms_->Value(), 1.0f);
171 float encode_usage_percent =
172 100.0f * filtered_encode_time_ms_->Value() / frame_diff_ms;
173 return static_cast<int>(encode_usage_percent + 0.5);
174 }
175
176 private:
177 const float kWeightFactorFrameDiff;
178 const float kWeightFactorEncodeTime;
179 scoped_ptr<VCMExpFilter> filtered_encode_time_ms_;
180 scoped_ptr<VCMExpFilter> filtered_frame_diff_ms_;
181};
182
183// Class for calculating the capture queue delay change.
184class OveruseFrameDetector::CaptureQueueDelay {
185 public:
186 CaptureQueueDelay()
187 : kWeightFactor(0.5f),
188 delay_ms_(0),
189 filtered_delay_ms_per_s_(new VCMExpFilter(kWeightFactor)) {
190 filtered_delay_ms_per_s_->Apply(1.0f, 0.0f);
191 }
192 ~CaptureQueueDelay() {}
193
194 void FrameCaptured(int64_t now) {
195 const size_t kMaxSize = 200;
196 if (frames_.size() > kMaxSize) {
197 frames_.pop_front();
198 }
199 frames_.push_back(now);
200 }
201
202 void FrameProcessingStarted(int64_t now) {
203 if (frames_.empty()) {
204 return;
205 }
206 delay_ms_ = now - frames_.front();
207 frames_.pop_front();
208 }
209
210 void CalculateDelayChange(int64_t diff_last_sample_ms) {
211 if (diff_last_sample_ms <= 0) {
212 return;
213 }
214 float exp = static_cast<float>(diff_last_sample_ms) / kProcessIntervalMs;
215 exp = std::min(exp, kMaxExp);
216 filtered_delay_ms_per_s_->Apply(exp,
217 delay_ms_ * 1000.0f / diff_last_sample_ms);
218 ClearFrames();
219 }
220
221 void ClearFrames() {
222 frames_.clear();
223 }
224
225 int delay_ms() const {
226 return delay_ms_;
227 }
228
229 int filtered_delay_ms_per_s() const {
230 return static_cast<int>(filtered_delay_ms_per_s_->Value() + 0.5);
231 }
232
233 private:
234 const float kWeightFactor;
235 std::list<int64_t> frames_;
236 int delay_ms_;
237 scoped_ptr<VCMExpFilter> filtered_delay_ms_per_s_;
238};
239
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000240OveruseFrameDetector::OveruseFrameDetector(Clock* clock,
241 float normaluse_stddev_ms,
242 float overuse_stddev_ms)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000243 : crit_(CriticalSectionWrapper::CreateCriticalSection()),
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000244 normaluse_stddev_ms_(normaluse_stddev_ms),
245 overuse_stddev_ms_(overuse_stddev_ms),
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000246 min_process_count_before_reporting_(kMinProcessCountBeforeReporting),
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000247 observer_(NULL),
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000248 clock_(clock),
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000249 next_process_time_(clock_->TimeInMilliseconds()),
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000250 num_process_times_(0),
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000251 last_capture_time_(0),
252 last_overuse_time_(0),
253 checks_above_threshold_(0),
254 last_rampup_time_(0),
255 in_quick_rampup_(false),
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000256 current_rampup_delay_ms_(kStandardRampUpDelayMs),
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000257 num_pixels_(0),
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000258 last_capture_jitter_ms_(-1),
259 last_encode_sample_ms_(0),
260 encode_time_(new EncodeTimeAvg()),
261 encode_usage_(new EncodeUsage()),
262 capture_queue_delay_(new CaptureQueueDelay()) {
263}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000264
265OveruseFrameDetector::~OveruseFrameDetector() {
266}
267
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000268void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) {
269 CriticalSectionScoped cs(crit_.get());
270 observer_ = observer;
271}
272
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000273int OveruseFrameDetector::AvgEncodeTimeMs() const {
274 CriticalSectionScoped cs(crit_.get());
275 return encode_time_->filtered_encode_time_ms();
276}
277
278int OveruseFrameDetector::EncodeUsagePercent() const {
279 CriticalSectionScoped cs(crit_.get());
280 return encode_usage_->UsageInPercent();
281}
282
283int OveruseFrameDetector::AvgCaptureQueueDelayMsPerS() const {
284 CriticalSectionScoped cs(crit_.get());
285 return capture_queue_delay_->filtered_delay_ms_per_s();
286}
287
288int OveruseFrameDetector::CaptureQueueDelayMsPerS() const {
289 CriticalSectionScoped cs(crit_.get());
290 return capture_queue_delay_->delay_ms();
291}
292
293int32_t OveruseFrameDetector::TimeUntilNextProcess() {
294 CriticalSectionScoped cs(crit_.get());
295 return next_process_time_ - clock_->TimeInMilliseconds();
296}
297
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000298bool OveruseFrameDetector::DetectFrameTimeout(int64_t now) const {
299 if (last_capture_time_ == 0) {
300 return false;
301 }
302 return (now - last_capture_time_) > kFrameTimeoutIntervalMs;
303}
304
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000305void OveruseFrameDetector::FrameCaptured(int width, int height) {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000306 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000307
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000308 int64_t now = clock_->TimeInMilliseconds();
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000309 int num_pixels = width * height;
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000310 if (num_pixels != num_pixels_ || DetectFrameTimeout(now)) {
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000311 // Frame size changed, reset statistics.
312 num_pixels_ = num_pixels;
313 capture_deltas_.Reset();
314 last_capture_time_ = 0;
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000315 capture_queue_delay_->ClearFrames();
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000316 num_process_times_ = 0;
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000317 }
318
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000319 if (last_capture_time_ != 0) {
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000320 capture_deltas_.AddSample(now - last_capture_time_);
321 encode_usage_->AddSample(now - last_capture_time_);
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000322 }
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000323 last_capture_time_ = now;
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000324
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000325 capture_queue_delay_->FrameCaptured(now);
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000326}
327
328void OveruseFrameDetector::FrameProcessingStarted() {
329 CriticalSectionScoped cs(crit_.get());
330 capture_queue_delay_->FrameProcessingStarted(clock_->TimeInMilliseconds());
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000331}
332
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +0000333void OveruseFrameDetector::FrameEncoded(int encode_time_ms) {
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000334 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000335 int64_t time = clock_->TimeInMilliseconds();
336 if (last_encode_sample_ms_ != 0) {
337 int64_t diff_ms = time - last_encode_sample_ms_;
338 encode_time_->AddEncodeSample(encode_time_ms, diff_ms);
339 encode_usage_->AddEncodeSample(encode_time_ms, diff_ms);
340 }
341 last_encode_sample_ms_ = time;
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +0000342}
343
344int OveruseFrameDetector::last_capture_jitter_ms() const {
345 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000346 return last_capture_jitter_ms_;
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000347}
348
349int32_t OveruseFrameDetector::Process() {
350 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000351
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000352 int64_t now = clock_->TimeInMilliseconds();
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000353
354 // Used to protect against Process() being called too often.
355 if (now < next_process_time_)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000356 return 0;
357
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000358 int64_t diff_ms = now - next_process_time_ + kProcessIntervalMs;
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000359 next_process_time_ = now + kProcessIntervalMs;
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000360 ++num_process_times_;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000361
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000362 // Don't trigger overuse unless we've seen a certain number of frames.
363 if (capture_deltas_.Count() < kMinFrameSampleCount)
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000364 return 0;
365
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000366 capture_queue_delay_->CalculateDelayChange(diff_ms);
367
asapersson@webrtc.orgb60346e2014-02-17 19:02:15 +0000368 if (num_process_times_ <= min_process_count_before_reporting_) {
369 return 0;
370 }
371
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000372 if (IsOverusing()) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000373 // If the last thing we did was going up, and now have to back down, we need
374 // to check if this peak was short. If so we should back off to avoid going
375 // back and forth between this load, the system doesn't seem to handle it.
376 bool check_for_backoff = last_rampup_time_ > last_overuse_time_;
377 if (check_for_backoff) {
378 if (now - last_rampup_time_ < kStandardRampUpDelayMs) {
379 // Going up was not ok for very long, back off.
380 current_rampup_delay_ms_ *= kRampUpBackoffFactor;
381 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs)
382 current_rampup_delay_ms_ = kMaxRampUpDelayMs;
383 } else {
384 // Not currently backing off, reset rampup delay.
385 current_rampup_delay_ms_ = kStandardRampUpDelayMs;
386 }
387 }
388
389 last_overuse_time_ = now;
390 in_quick_rampup_ = false;
391 checks_above_threshold_ = 0;
392
393 if (observer_ != NULL)
394 observer_->OveruseDetected();
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000395 } else if (IsUnderusing(now)) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000396 last_rampup_time_ = now;
397 in_quick_rampup_ = true;
398
399 if (observer_ != NULL)
400 observer_->NormalUsage();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000401 }
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000402
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000403 WEBRTC_TRACE(
404 webrtc::kTraceInfo,
405 webrtc::kTraceVideo,
406 -1,
407 "Capture input stats: avg: %.2fms, std_dev: %.2fms (rampup delay: "
408 "%dms, overuse: >=%.2fms, "
409 "underuse: <%.2fms)",
410 capture_deltas_.Mean(),
411 capture_deltas_.StdDev(),
412 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_,
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000413 overuse_stddev_ms_,
414 normaluse_stddev_ms_);
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000415
asapersson@webrtc.org9e5b0342013-12-04 13:47:44 +0000416 last_capture_jitter_ms_ = static_cast<int>(capture_deltas_.StdDev() + 0.5);
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000417 return 0;
418}
419
420bool OveruseFrameDetector::IsOverusing() {
421 if (capture_deltas_.StdDev() >= overuse_stddev_ms_) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000422 ++checks_above_threshold_;
423 } else {
424 checks_above_threshold_ = 0;
425 }
426
427 return checks_above_threshold_ >= kConsecutiveChecksAboveThreshold;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000428}
429
430bool OveruseFrameDetector::IsUnderusing(int64_t time_now) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000431 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
432 if (time_now < last_rampup_time_ + delay)
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000433 return false;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000434
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000435 return capture_deltas_.StdDev() < normaluse_stddev_ms_;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000436}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000437} // namespace webrtc