blob: 96cea176ff983ecd003e6e95bc7103dce4b99388 [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
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000013#include <algorithm>
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000014#include <assert.h>
pbos@webrtc.orga9575702013-08-30 17:16:32 +000015#include <math.h>
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000016
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000017#include "webrtc/modules/video_coding/utility/include/exp_filter.h"
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000018#include "webrtc/system_wrappers/interface/clock.h"
19#include "webrtc/system_wrappers/interface/critical_section_wrapper.h"
pbos@webrtc.orga9575702013-08-30 17:16:32 +000020#include "webrtc/system_wrappers/interface/trace.h"
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +000021#include "webrtc/video_engine/include/vie_base.h"
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +000022
23namespace webrtc {
24
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000025// TODO(mflodman) Test different values for all of these to trigger correctly,
26// avoid fluctuations etc.
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000027namespace {
pbos@webrtc.orga9575702013-08-30 17:16:32 +000028const int64_t kProcessIntervalMs = 5000;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000029
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000030// Consecutive checks above threshold to trigger overuse.
pbos@webrtc.orga9575702013-08-30 17:16:32 +000031const int kConsecutiveChecksAboveThreshold = 2;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000032
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000033// Minimum samples required to perform a check.
34const size_t kMinFrameSampleCount = 15;
35
36// Weight factor to apply to the standard deviation.
37const float kWeightFactor = 0.997f;
38
39// Weight factor to apply to the average.
40const float kWeightFactorMean = 0.98f;
41
pbos@webrtc.orga9575702013-08-30 17:16:32 +000042// Delay between consecutive rampups. (Used for quick recovery.)
43const int kQuickRampUpDelayMs = 10 * 1000;
44// Delay between rampup attempts. Initially uses standard, scales up to max.
45const int kStandardRampUpDelayMs = 30 * 1000;
46const int kMaxRampUpDelayMs = 120 * 1000;
47// Expontential back-off factor, to prevent annoying up-down behaviour.
48const double kRampUpBackoffFactor = 2.0;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000049
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000050} // namespace
51
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000052Statistics::Statistics() :
53 sum_(0.0),
54 count_(0),
55 filtered_samples_(new VCMExpFilter(kWeightFactorMean)),
56 filtered_variance_(new VCMExpFilter(kWeightFactor)) {
57}
58
59void Statistics::Reset() {
60 sum_ = 0.0;
61 count_ = 0;
62}
63
64void Statistics::AddSample(float sample_ms) {
65 sum_ += sample_ms;
66 ++count_;
67
68 if (count_ < kMinFrameSampleCount) {
69 // Initialize filtered samples.
70 filtered_samples_->Reset(kWeightFactorMean);
71 filtered_samples_->Apply(1.0f, InitialMean());
72 filtered_variance_->Reset(kWeightFactor);
73 filtered_variance_->Apply(1.0f, InitialVariance());
74 return;
75 }
76
77 float exp = sample_ms/33.0f;
78 exp = std::min(exp, 7.0f);
79 filtered_samples_->Apply(exp, sample_ms);
80 filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->Value()) *
81 (sample_ms - filtered_samples_->Value()));
82}
83
84float Statistics::InitialMean() const {
85 if (count_ == 0)
86 return 0.0;
87 return sum_ / count_;
88}
89
90float Statistics::InitialVariance() const {
91 // Start in between the underuse and overuse threshold.
92 float average_stddev = (kNormalUseStdDevMs + kOveruseStdDevMs)/2.0f;
93 return average_stddev * average_stddev;
94}
95
96float Statistics::Mean() const { return filtered_samples_->Value(); }
97
98float Statistics::StdDev() const {
99 return sqrt(std::max(filtered_variance_->Value(), 0.0f));
100}
101
102uint64_t Statistics::Count() const { return count_; }
103
104OveruseFrameDetector::OveruseFrameDetector(Clock* clock,
105 float normaluse_stddev_ms,
106 float overuse_stddev_ms)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000107 : crit_(CriticalSectionWrapper::CreateCriticalSection()),
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000108 normaluse_stddev_ms_(normaluse_stddev_ms),
109 overuse_stddev_ms_(overuse_stddev_ms),
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000110 observer_(NULL),
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000111 clock_(clock),
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000112 next_process_time_(clock_->TimeInMilliseconds()),
113 last_capture_time_(0),
114 last_overuse_time_(0),
115 checks_above_threshold_(0),
116 last_rampup_time_(0),
117 in_quick_rampup_(false),
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000118 current_rampup_delay_ms_(kStandardRampUpDelayMs),
119 num_pixels_(0) {}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000120
121OveruseFrameDetector::~OveruseFrameDetector() {
122}
123
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000124void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) {
125 CriticalSectionScoped cs(crit_.get());
126 observer_ = observer;
127}
128
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000129void OveruseFrameDetector::FrameCaptured(int width, int height) {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000130 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000131
132 int num_pixels = width * height;
133 if (num_pixels != num_pixels_) {
134 // Frame size changed, reset statistics.
135 num_pixels_ = num_pixels;
136 capture_deltas_.Reset();
137 last_capture_time_ = 0;
138 }
139
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000140 int64_t time = clock_->TimeInMilliseconds();
141 if (last_capture_time_ != 0) {
142 capture_deltas_.AddSample(time - last_capture_time_);
143 }
144 last_capture_time_ = time;
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000145}
146
147int32_t OveruseFrameDetector::TimeUntilNextProcess() {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000148 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000149 return next_process_time_ - clock_->TimeInMilliseconds();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000150}
151
152int32_t OveruseFrameDetector::Process() {
153 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000154
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000155 int64_t now = clock_->TimeInMilliseconds();
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000156
157 // Used to protect against Process() being called too often.
158 if (now < next_process_time_)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000159 return 0;
160
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000161 next_process_time_ = now + kProcessIntervalMs;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000162
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000163 // Don't trigger overuse unless we've seen a certain number of frames.
164 if (capture_deltas_.Count() < kMinFrameSampleCount)
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000165 return 0;
166
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000167 if (IsOverusing()) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000168 // If the last thing we did was going up, and now have to back down, we need
169 // to check if this peak was short. If so we should back off to avoid going
170 // back and forth between this load, the system doesn't seem to handle it.
171 bool check_for_backoff = last_rampup_time_ > last_overuse_time_;
172 if (check_for_backoff) {
173 if (now - last_rampup_time_ < kStandardRampUpDelayMs) {
174 // Going up was not ok for very long, back off.
175 current_rampup_delay_ms_ *= kRampUpBackoffFactor;
176 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs)
177 current_rampup_delay_ms_ = kMaxRampUpDelayMs;
178 } else {
179 // Not currently backing off, reset rampup delay.
180 current_rampup_delay_ms_ = kStandardRampUpDelayMs;
181 }
182 }
183
184 last_overuse_time_ = now;
185 in_quick_rampup_ = false;
186 checks_above_threshold_ = 0;
187
188 if (observer_ != NULL)
189 observer_->OveruseDetected();
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000190 } else if (IsUnderusing(now)) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000191 last_rampup_time_ = now;
192 in_quick_rampup_ = true;
193
194 if (observer_ != NULL)
195 observer_->NormalUsage();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000196 }
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000197
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000198 WEBRTC_TRACE(
199 webrtc::kTraceInfo,
200 webrtc::kTraceVideo,
201 -1,
202 "Capture input stats: avg: %.2fms, std_dev: %.2fms (rampup delay: "
203 "%dms, overuse: >=%.2fms, "
204 "underuse: <%.2fms)",
205 capture_deltas_.Mean(),
206 capture_deltas_.StdDev(),
207 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_,
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000208 overuse_stddev_ms_,
209 normaluse_stddev_ms_);
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000210
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000211 return 0;
212}
213
214bool OveruseFrameDetector::IsOverusing() {
215 if (capture_deltas_.StdDev() >= overuse_stddev_ms_) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000216 ++checks_above_threshold_;
217 } else {
218 checks_above_threshold_ = 0;
219 }
220
221 return checks_above_threshold_ >= kConsecutiveChecksAboveThreshold;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000222}
223
224bool OveruseFrameDetector::IsUnderusing(int64_t time_now) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000225 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
226 if (time_now < last_rampup_time_ + delay)
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000227 return false;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000228
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000229 return capture_deltas_.StdDev() < normaluse_stddev_ms_;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000230}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000231} // namespace webrtc