blob: 262c76725f85d7d9f5e915ec0d3d14a36df60406 [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),
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000119 num_pixels_(0),
120 last_capture_jitter_ms_(-1) {}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000121
122OveruseFrameDetector::~OveruseFrameDetector() {
123}
124
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000125void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) {
126 CriticalSectionScoped cs(crit_.get());
127 observer_ = observer;
128}
129
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000130void OveruseFrameDetector::FrameCaptured(int width, int height) {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000131 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000132
133 int num_pixels = width * height;
134 if (num_pixels != num_pixels_) {
135 // Frame size changed, reset statistics.
136 num_pixels_ = num_pixels;
137 capture_deltas_.Reset();
138 last_capture_time_ = 0;
139 }
140
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000141 int64_t time = clock_->TimeInMilliseconds();
142 if (last_capture_time_ != 0) {
143 capture_deltas_.AddSample(time - last_capture_time_);
144 }
145 last_capture_time_ = time;
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000146}
147
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000148int OveruseFrameDetector::last_capture_jitter_ms() {
149 CriticalSectionScoped cs(crit_.get());
150 return last_capture_jitter_ms_;
151}
152
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000153int32_t OveruseFrameDetector::TimeUntilNextProcess() {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000154 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000155 return next_process_time_ - clock_->TimeInMilliseconds();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000156}
157
158int32_t OveruseFrameDetector::Process() {
159 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000160
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000161 int64_t now = clock_->TimeInMilliseconds();
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000162
163 // Used to protect against Process() being called too often.
164 if (now < next_process_time_)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000165 return 0;
166
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000167 next_process_time_ = now + kProcessIntervalMs;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000168
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000169 // Don't trigger overuse unless we've seen a certain number of frames.
170 if (capture_deltas_.Count() < kMinFrameSampleCount)
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000171 return 0;
172
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000173 if (IsOverusing()) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000174 // If the last thing we did was going up, and now have to back down, we need
175 // to check if this peak was short. If so we should back off to avoid going
176 // back and forth between this load, the system doesn't seem to handle it.
177 bool check_for_backoff = last_rampup_time_ > last_overuse_time_;
178 if (check_for_backoff) {
179 if (now - last_rampup_time_ < kStandardRampUpDelayMs) {
180 // Going up was not ok for very long, back off.
181 current_rampup_delay_ms_ *= kRampUpBackoffFactor;
182 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs)
183 current_rampup_delay_ms_ = kMaxRampUpDelayMs;
184 } else {
185 // Not currently backing off, reset rampup delay.
186 current_rampup_delay_ms_ = kStandardRampUpDelayMs;
187 }
188 }
189
190 last_overuse_time_ = now;
191 in_quick_rampup_ = false;
192 checks_above_threshold_ = 0;
193
194 if (observer_ != NULL)
195 observer_->OveruseDetected();
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000196 } else if (IsUnderusing(now)) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000197 last_rampup_time_ = now;
198 in_quick_rampup_ = true;
199
200 if (observer_ != NULL)
201 observer_->NormalUsage();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000202 }
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000203
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000204 WEBRTC_TRACE(
205 webrtc::kTraceInfo,
206 webrtc::kTraceVideo,
207 -1,
208 "Capture input stats: avg: %.2fms, std_dev: %.2fms (rampup delay: "
209 "%dms, overuse: >=%.2fms, "
210 "underuse: <%.2fms)",
211 capture_deltas_.Mean(),
212 capture_deltas_.StdDev(),
213 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_,
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000214 overuse_stddev_ms_,
215 normaluse_stddev_ms_);
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000216
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000217 last_capture_jitter_ms_ = static_cast<int>(capture_deltas_.StdDev());
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000218 return 0;
219}
220
221bool OveruseFrameDetector::IsOverusing() {
222 if (capture_deltas_.StdDev() >= overuse_stddev_ms_) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000223 ++checks_above_threshold_;
224 } else {
225 checks_above_threshold_ = 0;
226 }
227
228 return checks_above_threshold_ >= kConsecutiveChecksAboveThreshold;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000229}
230
231bool OveruseFrameDetector::IsUnderusing(int64_t time_now) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000232 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
233 if (time_now < last_rampup_time_ + delay)
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000234 return false;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000235
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000236 return capture_deltas_.StdDev() < normaluse_stddev_ms_;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000237}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000238} // namespace webrtc