blob: bc283e27c25b80c17dde79b97bd4bed86b5a1a07 [file] [log] [blame]
mflodman@webrtc.orgf8f91d62013-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.orgcb9a72b2013-07-31 16:42:21 +000013#include <assert.h>
pbos@webrtc.orgce9de712013-08-30 17:16:32 +000014#include <math.h>
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +000015
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +000016#include <algorithm>
17#include <list>
18
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000019#include "webrtc/modules/video_coding/utility/include/exp_filter.h"
mflodman@webrtc.orgf8f91d62013-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.orgce9de712013-08-30 17:16:32 +000022#include "webrtc/system_wrappers/interface/trace.h"
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +000023
24namespace webrtc {
25
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +000026// TODO(mflodman) Test different values for all of these to trigger correctly,
27// avoid fluctuations etc.
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +000028namespace {
pbos@webrtc.orgce9de712013-08-30 17:16:32 +000029const int64_t kProcessIntervalMs = 5000;
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +000030
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000031// Weight factor to apply to the standard deviation.
32const float kWeightFactor = 0.997f;
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000033// Weight factor to apply to the average.
34const float kWeightFactorMean = 0.98f;
35
pbos@webrtc.orgce9de712013-08-30 17:16:32 +000036// Delay between consecutive rampups. (Used for quick recovery.)
37const int kQuickRampUpDelayMs = 10 * 1000;
38// Delay between rampup attempts. Initially uses standard, scales up to max.
39const int kStandardRampUpDelayMs = 30 * 1000;
40const int kMaxRampUpDelayMs = 120 * 1000;
41// Expontential back-off factor, to prevent annoying up-down behaviour.
42const double kRampUpBackoffFactor = 2.0;
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +000043
asapersson@webrtc.orgdd4f8662013-11-26 11:12:33 +000044// The initial average encode time (set to a fairly small value).
45const float kInitialAvgEncodeTimeMs = 5.0f;
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +000046
47// The maximum exponent to use in VCMExpFilter.
48const float kSampleDiffMs = 33.0f;
49const float kMaxExp = 7.0f;
50
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +000051} // namespace
52
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000053Statistics::Statistics() :
54 sum_(0.0),
55 count_(0),
56 filtered_samples_(new VCMExpFilter(kWeightFactorMean)),
57 filtered_variance_(new VCMExpFilter(kWeightFactor)) {
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +000058 Reset();
59}
60
61void Statistics::SetOptions(const CpuOveruseOptions& options) {
62 options_ = options;
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000063}
64
65void Statistics::Reset() {
66 sum_ = 0.0;
67 count_ = 0;
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +000068 filtered_variance_->Reset(kWeightFactor);
69 filtered_variance_->Apply(1.0f, InitialVariance());
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000070}
71
72void Statistics::AddSample(float sample_ms) {
73 sum_ += sample_ms;
74 ++count_;
75
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +000076 if (count_ < static_cast<uint32_t>(options_.min_frame_samples)) {
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000077 // Initialize filtered samples.
78 filtered_samples_->Reset(kWeightFactorMean);
79 filtered_samples_->Apply(1.0f, InitialMean());
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000080 return;
81 }
82
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +000083 float exp = sample_ms / kSampleDiffMs;
84 exp = std::min(exp, kMaxExp);
asapersson@webrtc.orgae145042013-09-23 20:05:39 +000085 filtered_samples_->Apply(exp, sample_ms);
86 filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->Value()) *
87 (sample_ms - filtered_samples_->Value()));
88}
89
90float Statistics::InitialMean() const {
91 if (count_ == 0)
92 return 0.0;
93 return sum_ / count_;
94}
95
96float Statistics::InitialVariance() const {
97 // Start in between the underuse and overuse threshold.
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +000098 float average_stddev = (options_.low_capture_jitter_threshold_ms +
99 options_.high_capture_jitter_threshold_ms) / 2.0f;
asapersson@webrtc.orgae145042013-09-23 20:05:39 +0000100 return average_stddev * average_stddev;
101}
102
103float Statistics::Mean() const { return filtered_samples_->Value(); }
104
105float Statistics::StdDev() const {
106 return sqrt(std::max(filtered_variance_->Value(), 0.0f));
107}
108
109uint64_t Statistics::Count() const { return count_; }
110
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000111
112// Class for calculating the average encode time.
113class OveruseFrameDetector::EncodeTimeAvg {
114 public:
115 EncodeTimeAvg()
116 : kWeightFactor(0.5f),
117 filtered_encode_time_ms_(new VCMExpFilter(kWeightFactor)) {
118 filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs);
119 }
120 ~EncodeTimeAvg() {}
121
122 void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) {
123 float exp = diff_last_sample_ms / kSampleDiffMs;
124 exp = std::min(exp, kMaxExp);
125 filtered_encode_time_ms_->Apply(exp, encode_time_ms);
126 }
127
128 int filtered_encode_time_ms() const {
129 return static_cast<int>(filtered_encode_time_ms_->Value() + 0.5);
130 }
131
132 private:
133 const float kWeightFactor;
134 scoped_ptr<VCMExpFilter> filtered_encode_time_ms_;
135};
136
137// Class for calculating the encode usage.
138class OveruseFrameDetector::EncodeUsage {
139 public:
140 EncodeUsage()
141 : kWeightFactorFrameDiff(0.998f),
142 kWeightFactorEncodeTime(0.995f),
143 filtered_encode_time_ms_(new VCMExpFilter(kWeightFactorEncodeTime)),
144 filtered_frame_diff_ms_(new VCMExpFilter(kWeightFactorFrameDiff)) {
145 filtered_encode_time_ms_->Apply(1.0f, kInitialAvgEncodeTimeMs);
146 filtered_frame_diff_ms_->Apply(1.0f, kSampleDiffMs);
147 }
148 ~EncodeUsage() {}
149
150 void AddSample(float sample_ms) {
151 float exp = sample_ms / kSampleDiffMs;
152 exp = std::min(exp, kMaxExp);
153 filtered_frame_diff_ms_->Apply(exp, sample_ms);
154 }
155
156 void AddEncodeSample(float encode_time_ms, int64_t diff_last_sample_ms) {
157 float exp = diff_last_sample_ms / kSampleDiffMs;
158 exp = std::min(exp, kMaxExp);
159 filtered_encode_time_ms_->Apply(exp, encode_time_ms);
160 }
161
162 int UsageInPercent() const {
163 float frame_diff_ms = std::max(filtered_frame_diff_ms_->Value(), 1.0f);
164 float encode_usage_percent =
165 100.0f * filtered_encode_time_ms_->Value() / frame_diff_ms;
166 return static_cast<int>(encode_usage_percent + 0.5);
167 }
168
169 private:
170 const float kWeightFactorFrameDiff;
171 const float kWeightFactorEncodeTime;
172 scoped_ptr<VCMExpFilter> filtered_encode_time_ms_;
173 scoped_ptr<VCMExpFilter> filtered_frame_diff_ms_;
174};
175
176// Class for calculating the capture queue delay change.
177class OveruseFrameDetector::CaptureQueueDelay {
178 public:
179 CaptureQueueDelay()
180 : kWeightFactor(0.5f),
181 delay_ms_(0),
182 filtered_delay_ms_per_s_(new VCMExpFilter(kWeightFactor)) {
183 filtered_delay_ms_per_s_->Apply(1.0f, 0.0f);
184 }
185 ~CaptureQueueDelay() {}
186
187 void FrameCaptured(int64_t now) {
188 const size_t kMaxSize = 200;
189 if (frames_.size() > kMaxSize) {
190 frames_.pop_front();
191 }
192 frames_.push_back(now);
193 }
194
195 void FrameProcessingStarted(int64_t now) {
196 if (frames_.empty()) {
197 return;
198 }
199 delay_ms_ = now - frames_.front();
200 frames_.pop_front();
201 }
202
203 void CalculateDelayChange(int64_t diff_last_sample_ms) {
204 if (diff_last_sample_ms <= 0) {
205 return;
206 }
207 float exp = static_cast<float>(diff_last_sample_ms) / kProcessIntervalMs;
208 exp = std::min(exp, kMaxExp);
209 filtered_delay_ms_per_s_->Apply(exp,
210 delay_ms_ * 1000.0f / diff_last_sample_ms);
211 ClearFrames();
212 }
213
214 void ClearFrames() {
215 frames_.clear();
216 }
217
218 int delay_ms() const {
219 return delay_ms_;
220 }
221
222 int filtered_delay_ms_per_s() const {
223 return static_cast<int>(filtered_delay_ms_per_s_->Value() + 0.5);
224 }
225
226 private:
227 const float kWeightFactor;
228 std::list<int64_t> frames_;
229 int delay_ms_;
230 scoped_ptr<VCMExpFilter> filtered_delay_ms_per_s_;
231};
232
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000233OveruseFrameDetector::OveruseFrameDetector(Clock* clock)
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000234 : crit_(CriticalSectionWrapper::CreateCriticalSection()),
mflodman@webrtc.orgbf76ae22013-07-23 11:35:00 +0000235 observer_(NULL),
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000236 clock_(clock),
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000237 next_process_time_(clock_->TimeInMilliseconds()),
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000238 num_process_times_(0),
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000239 last_capture_time_(0),
240 last_overuse_time_(0),
241 checks_above_threshold_(0),
242 last_rampup_time_(0),
243 in_quick_rampup_(false),
asapersson@webrtc.orgae145042013-09-23 20:05:39 +0000244 current_rampup_delay_ms_(kStandardRampUpDelayMs),
asapersson@webrtc.org47475852013-11-20 13:51:40 +0000245 num_pixels_(0),
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000246 last_encode_sample_ms_(0),
247 encode_time_(new EncodeTimeAvg()),
248 encode_usage_(new EncodeUsage()),
249 capture_queue_delay_(new CaptureQueueDelay()) {
250}
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000251
252OveruseFrameDetector::~OveruseFrameDetector() {
253}
254
mflodman@webrtc.orgbf76ae22013-07-23 11:35:00 +0000255void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) {
256 CriticalSectionScoped cs(crit_.get());
257 observer_ = observer;
258}
259
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000260void OveruseFrameDetector::SetOptions(const CpuOveruseOptions& options) {
261 assert(options.min_frame_samples > 0);
262 CriticalSectionScoped cs(crit_.get());
263 if (options_.Equals(options)) {
264 return;
265 }
266 options_ = options;
267 capture_deltas_.SetOptions(options);
268 ResetAll(num_pixels_);
269}
270
271int OveruseFrameDetector::CaptureJitterMs() const {
272 CriticalSectionScoped cs(crit_.get());
273 return static_cast<int>(capture_deltas_.StdDev() + 0.5);
274}
275
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000276int OveruseFrameDetector::AvgEncodeTimeMs() const {
277 CriticalSectionScoped cs(crit_.get());
278 return encode_time_->filtered_encode_time_ms();
279}
280
281int OveruseFrameDetector::EncodeUsagePercent() const {
282 CriticalSectionScoped cs(crit_.get());
283 return encode_usage_->UsageInPercent();
284}
285
286int OveruseFrameDetector::AvgCaptureQueueDelayMsPerS() const {
287 CriticalSectionScoped cs(crit_.get());
288 return capture_queue_delay_->filtered_delay_ms_per_s();
289}
290
291int OveruseFrameDetector::CaptureQueueDelayMsPerS() const {
292 CriticalSectionScoped cs(crit_.get());
293 return capture_queue_delay_->delay_ms();
294}
295
296int32_t OveruseFrameDetector::TimeUntilNextProcess() {
297 CriticalSectionScoped cs(crit_.get());
298 return next_process_time_ - clock_->TimeInMilliseconds();
299}
300
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000301bool OveruseFrameDetector::FrameSizeChanged(int num_pixels) const {
302 if (num_pixels != num_pixels_) {
303 return true;
304 }
305 return false;
306}
307
308bool OveruseFrameDetector::FrameTimeoutDetected(int64_t now) const {
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000309 if (last_capture_time_ == 0) {
310 return false;
311 }
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000312 return (now - last_capture_time_) > options_.frame_timeout_interval_ms;
313}
314
315void OveruseFrameDetector::ResetAll(int num_pixels) {
316 num_pixels_ = num_pixels;
317 capture_deltas_.Reset();
318 capture_queue_delay_->ClearFrames();
319 last_capture_time_ = 0;
320 num_process_times_ = 0;
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000321}
322
asapersson@webrtc.orgae145042013-09-23 20:05:39 +0000323void OveruseFrameDetector::FrameCaptured(int width, int height) {
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000324 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.orgae145042013-09-23 20:05:39 +0000325
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000326 int64_t now = clock_->TimeInMilliseconds();
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000327 if (FrameSizeChanged(width * height) || FrameTimeoutDetected(now)) {
328 ResetAll(width * height);
asapersson@webrtc.orgae145042013-09-23 20:05:39 +0000329 }
330
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000331 if (last_capture_time_ != 0) {
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000332 capture_deltas_.AddSample(now - last_capture_time_);
333 encode_usage_->AddSample(now - last_capture_time_);
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000334 }
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000335 last_capture_time_ = now;
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000336
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000337 capture_queue_delay_->FrameCaptured(now);
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000338}
339
340void OveruseFrameDetector::FrameProcessingStarted() {
341 CriticalSectionScoped cs(crit_.get());
342 capture_queue_delay_->FrameProcessingStarted(clock_->TimeInMilliseconds());
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000343}
344
asapersson@webrtc.orgdd4f8662013-11-26 11:12:33 +0000345void OveruseFrameDetector::FrameEncoded(int encode_time_ms) {
asapersson@webrtc.org47475852013-11-20 13:51:40 +0000346 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000347 int64_t time = clock_->TimeInMilliseconds();
348 if (last_encode_sample_ms_ != 0) {
349 int64_t diff_ms = time - last_encode_sample_ms_;
350 encode_time_->AddEncodeSample(encode_time_ms, diff_ms);
351 encode_usage_->AddEncodeSample(encode_time_ms, diff_ms);
352 }
353 last_encode_sample_ms_ = time;
asapersson@webrtc.orgdd4f8662013-11-26 11:12:33 +0000354}
355
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000356int32_t OveruseFrameDetector::Process() {
357 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000358
mflodman@webrtc.orgbf76ae22013-07-23 11:35:00 +0000359 int64_t now = clock_->TimeInMilliseconds();
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000360
361 // Used to protect against Process() being called too often.
362 if (now < next_process_time_)
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000363 return 0;
364
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000365 int64_t diff_ms = now - next_process_time_ + kProcessIntervalMs;
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000366 next_process_time_ = now + kProcessIntervalMs;
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000367 ++num_process_times_;
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000368
asapersson@webrtc.orgf1630b12013-12-04 13:47:44 +0000369 capture_queue_delay_->CalculateDelayChange(diff_ms);
370
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000371 if (num_process_times_ <= options_.min_process_count) {
asapersson@webrtc.org99507962014-02-17 19:02:15 +0000372 return 0;
373 }
374
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000375 if (IsOverusing()) {
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000376 // If the last thing we did was going up, and now have to back down, we need
377 // to check if this peak was short. If so we should back off to avoid going
378 // back and forth between this load, the system doesn't seem to handle it.
379 bool check_for_backoff = last_rampup_time_ > last_overuse_time_;
380 if (check_for_backoff) {
381 if (now - last_rampup_time_ < kStandardRampUpDelayMs) {
382 // Going up was not ok for very long, back off.
383 current_rampup_delay_ms_ *= kRampUpBackoffFactor;
384 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs)
385 current_rampup_delay_ms_ = kMaxRampUpDelayMs;
386 } else {
387 // Not currently backing off, reset rampup delay.
388 current_rampup_delay_ms_ = kStandardRampUpDelayMs;
389 }
390 }
391
392 last_overuse_time_ = now;
393 in_quick_rampup_ = false;
394 checks_above_threshold_ = 0;
395
396 if (observer_ != NULL)
397 observer_->OveruseDetected();
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000398 } else if (IsUnderusing(now)) {
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000399 last_rampup_time_ = now;
400 in_quick_rampup_ = true;
401
402 if (observer_ != NULL)
403 observer_->NormalUsage();
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000404 }
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000405
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000406 WEBRTC_TRACE(
407 webrtc::kTraceInfo,
408 webrtc::kTraceVideo,
409 -1,
410 "Capture input stats: avg: %.2fms, std_dev: %.2fms (rampup delay: "
411 "%dms, overuse: >=%.2fms, "
412 "underuse: <%.2fms)",
413 capture_deltas_.Mean(),
414 capture_deltas_.StdDev(),
415 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_,
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000416 options_.high_capture_jitter_threshold_ms,
417 options_.low_capture_jitter_threshold_ms);
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000418
asapersson@webrtc.orgae145042013-09-23 20:05:39 +0000419 return 0;
420}
421
422bool OveruseFrameDetector::IsOverusing() {
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000423 bool overusing = options_.enable_capture_jitter_method &&
424 (capture_deltas_.StdDev() >= options_.high_capture_jitter_threshold_ms);
425 if (overusing) {
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000426 ++checks_above_threshold_;
427 } else {
428 checks_above_threshold_ = 0;
429 }
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000430 return checks_above_threshold_ >= options_.high_threshold_consecutive_count;
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000431}
432
433bool OveruseFrameDetector::IsUnderusing(int64_t time_now) {
pbos@webrtc.orgce9de712013-08-30 17:16:32 +0000434 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
435 if (time_now < last_rampup_time_ + delay)
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000436 return false;
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000437
asapersson@webrtc.org9da327c2014-03-20 13:15:01 +0000438 bool underusing = options_.enable_capture_jitter_method &&
439 (capture_deltas_.StdDev() < options_.low_capture_jitter_threshold_ms);
440 return underusing;
mflodman@webrtc.orgcb9a72b2013-07-31 16:42:21 +0000441}
mflodman@webrtc.orgf8f91d62013-06-26 11:23:01 +0000442} // namespace webrtc