blob: c284f646dec514d1992c6be516616100508a0096 [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
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +000050// The initial average encode time (set to a fairly small value).
51const float kInitialAvgEncodeTimeMs = 5.0f;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +000052} // namespace
53
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +000054Statistics::Statistics() :
55 sum_(0.0),
56 count_(0),
57 filtered_samples_(new VCMExpFilter(kWeightFactorMean)),
58 filtered_variance_(new VCMExpFilter(kWeightFactor)) {
59}
60
61void Statistics::Reset() {
62 sum_ = 0.0;
63 count_ = 0;
64}
65
66void Statistics::AddSample(float sample_ms) {
67 sum_ += sample_ms;
68 ++count_;
69
70 if (count_ < kMinFrameSampleCount) {
71 // Initialize filtered samples.
72 filtered_samples_->Reset(kWeightFactorMean);
73 filtered_samples_->Apply(1.0f, InitialMean());
74 filtered_variance_->Reset(kWeightFactor);
75 filtered_variance_->Apply(1.0f, InitialVariance());
76 return;
77 }
78
79 float exp = sample_ms/33.0f;
80 exp = std::min(exp, 7.0f);
81 filtered_samples_->Apply(exp, sample_ms);
82 filtered_variance_->Apply(exp, (sample_ms - filtered_samples_->Value()) *
83 (sample_ms - filtered_samples_->Value()));
84}
85
86float Statistics::InitialMean() const {
87 if (count_ == 0)
88 return 0.0;
89 return sum_ / count_;
90}
91
92float Statistics::InitialVariance() const {
93 // Start in between the underuse and overuse threshold.
94 float average_stddev = (kNormalUseStdDevMs + kOveruseStdDevMs)/2.0f;
95 return average_stddev * average_stddev;
96}
97
98float Statistics::Mean() const { return filtered_samples_->Value(); }
99
100float Statistics::StdDev() const {
101 return sqrt(std::max(filtered_variance_->Value(), 0.0f));
102}
103
104uint64_t Statistics::Count() const { return count_; }
105
106OveruseFrameDetector::OveruseFrameDetector(Clock* clock,
107 float normaluse_stddev_ms,
108 float overuse_stddev_ms)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000109 : crit_(CriticalSectionWrapper::CreateCriticalSection()),
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000110 normaluse_stddev_ms_(normaluse_stddev_ms),
111 overuse_stddev_ms_(overuse_stddev_ms),
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000112 observer_(NULL),
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000113 clock_(clock),
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000114 next_process_time_(clock_->TimeInMilliseconds()),
115 last_capture_time_(0),
116 last_overuse_time_(0),
117 checks_above_threshold_(0),
118 last_rampup_time_(0),
119 in_quick_rampup_(false),
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000120 current_rampup_delay_ms_(kStandardRampUpDelayMs),
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000121 num_pixels_(0),
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +0000122 avg_encode_time_ms_(kInitialAvgEncodeTimeMs) {}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000123
124OveruseFrameDetector::~OveruseFrameDetector() {
125}
126
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000127void OveruseFrameDetector::SetObserver(CpuOveruseObserver* observer) {
128 CriticalSectionScoped cs(crit_.get());
129 observer_ = observer;
130}
131
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000132void OveruseFrameDetector::FrameCaptured(int width, int height) {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000133 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000134
135 int num_pixels = width * height;
136 if (num_pixels != num_pixels_) {
137 // Frame size changed, reset statistics.
138 num_pixels_ = num_pixels;
139 capture_deltas_.Reset();
140 last_capture_time_ = 0;
141 }
142
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000143 int64_t time = clock_->TimeInMilliseconds();
144 if (last_capture_time_ != 0) {
145 capture_deltas_.AddSample(time - last_capture_time_);
146 }
147 last_capture_time_ = time;
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000148}
149
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +0000150void OveruseFrameDetector::FrameEncoded(int encode_time_ms) {
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000151 CriticalSectionScoped cs(crit_.get());
asapersson@webrtc.orgc7ff8f92013-11-26 11:12:33 +0000152 const float kWeight = 0.1f;
153 avg_encode_time_ms_ = kWeight * encode_time_ms +
154 (1.0f - kWeight) * avg_encode_time_ms_;
155}
156
157int OveruseFrameDetector::last_capture_jitter_ms() const {
158 CriticalSectionScoped cs(crit_.get());
159 return static_cast<int>(capture_deltas_.StdDev() + 0.5);
160}
161
162int OveruseFrameDetector::avg_encode_time_ms() const {
163 CriticalSectionScoped cs(crit_.get());
164 return static_cast<int>(avg_encode_time_ms_ + 0.5);
asapersson@webrtc.orgb24d3352013-11-20 13:51:40 +0000165}
166
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000167int32_t OveruseFrameDetector::TimeUntilNextProcess() {
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000168 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000169 return next_process_time_ - clock_->TimeInMilliseconds();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000170}
171
172int32_t OveruseFrameDetector::Process() {
173 CriticalSectionScoped cs(crit_.get());
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000174
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000175 int64_t now = clock_->TimeInMilliseconds();
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000176
177 // Used to protect against Process() being called too often.
178 if (now < next_process_time_)
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000179 return 0;
180
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000181 next_process_time_ = now + kProcessIntervalMs;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000182
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000183 // Don't trigger overuse unless we've seen a certain number of frames.
184 if (capture_deltas_.Count() < kMinFrameSampleCount)
mflodman@webrtc.org6879c8a2013-07-23 11:35:00 +0000185 return 0;
186
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000187 if (IsOverusing()) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000188 // If the last thing we did was going up, and now have to back down, we need
189 // to check if this peak was short. If so we should back off to avoid going
190 // back and forth between this load, the system doesn't seem to handle it.
191 bool check_for_backoff = last_rampup_time_ > last_overuse_time_;
192 if (check_for_backoff) {
193 if (now - last_rampup_time_ < kStandardRampUpDelayMs) {
194 // Going up was not ok for very long, back off.
195 current_rampup_delay_ms_ *= kRampUpBackoffFactor;
196 if (current_rampup_delay_ms_ > kMaxRampUpDelayMs)
197 current_rampup_delay_ms_ = kMaxRampUpDelayMs;
198 } else {
199 // Not currently backing off, reset rampup delay.
200 current_rampup_delay_ms_ = kStandardRampUpDelayMs;
201 }
202 }
203
204 last_overuse_time_ = now;
205 in_quick_rampup_ = false;
206 checks_above_threshold_ = 0;
207
208 if (observer_ != NULL)
209 observer_->OveruseDetected();
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000210 } else if (IsUnderusing(now)) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000211 last_rampup_time_ = now;
212 in_quick_rampup_ = true;
213
214 if (observer_ != NULL)
215 observer_->NormalUsage();
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000216 }
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000217
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000218 WEBRTC_TRACE(
219 webrtc::kTraceInfo,
220 webrtc::kTraceVideo,
221 -1,
222 "Capture input stats: avg: %.2fms, std_dev: %.2fms (rampup delay: "
223 "%dms, overuse: >=%.2fms, "
224 "underuse: <%.2fms)",
225 capture_deltas_.Mean(),
226 capture_deltas_.StdDev(),
227 in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_,
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000228 overuse_stddev_ms_,
229 normaluse_stddev_ms_);
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000230
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000231 return 0;
232}
233
234bool OveruseFrameDetector::IsOverusing() {
235 if (capture_deltas_.StdDev() >= overuse_stddev_ms_) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000236 ++checks_above_threshold_;
237 } else {
238 checks_above_threshold_ = 0;
239 }
240
241 return checks_above_threshold_ >= kConsecutiveChecksAboveThreshold;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000242}
243
244bool OveruseFrameDetector::IsUnderusing(int64_t time_now) {
pbos@webrtc.orga9575702013-08-30 17:16:32 +0000245 int delay = in_quick_rampup_ ? kQuickRampUpDelayMs : current_rampup_delay_ms_;
246 if (time_now < last_rampup_time_ + delay)
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000247 return false;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000248
asapersson@webrtc.orge2af6222013-09-23 20:05:39 +0000249 return capture_deltas_.StdDev() < normaluse_stddev_ms_;
mflodman@webrtc.orgd4412fe2013-07-31 16:42:21 +0000250}
mflodman@webrtc.orge6168f52013-06-26 11:23:01 +0000251} // namespace webrtc