1 /*
2 * Copyright (c) 2012 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 "modules/remote_bitrate_estimator/overuse_detector.h"
12
13 #include <math.h>
14 #include <stdio.h>
15
16 #include <algorithm>
17 #include <string>
18
19 #include "modules/remote_bitrate_estimator/include/bwe_defines.h"
20 #include "modules/remote_bitrate_estimator/test/bwe_test_logging.h"
21 #include "rtc_base/checks.h"
22 #include "rtc_base/numerics/safe_minmax.h"
23
24 namespace webrtc {
25
26 const char kAdaptiveThresholdExperiment[] = "WebRTC-AdaptiveBweThreshold";
27 const char kEnabledPrefix[] = "Enabled";
28 const size_t kEnabledPrefixLength = sizeof(kEnabledPrefix) - 1;
29 const char kDisabledPrefix[] = "Disabled";
30 const size_t kDisabledPrefixLength = sizeof(kDisabledPrefix) - 1;
31
32 const double kMaxAdaptOffsetMs = 15.0;
33 const double kOverUsingTimeThreshold = 10;
34 const int kMaxNumDeltas = 60;
35
AdaptiveThresholdExperimentIsDisabled(const WebRtcKeyValueConfig & key_value_config)36 bool AdaptiveThresholdExperimentIsDisabled(
37 const WebRtcKeyValueConfig& key_value_config) {
38 std::string experiment_string =
39 key_value_config.Lookup(kAdaptiveThresholdExperiment);
40 const size_t kMinExperimentLength = kDisabledPrefixLength;
41 if (experiment_string.length() < kMinExperimentLength)
42 return false;
43 return experiment_string.substr(0, kDisabledPrefixLength) == kDisabledPrefix;
44 }
45
46 // Gets thresholds from the experiment name following the format
47 // "WebRTC-AdaptiveBweThreshold/Enabled-0.5,0.002/".
ReadExperimentConstants(const WebRtcKeyValueConfig & key_value_config,double * k_up,double * k_down)48 bool ReadExperimentConstants(const WebRtcKeyValueConfig& key_value_config,
49 double* k_up,
50 double* k_down) {
51 std::string experiment_string =
52 key_value_config.Lookup(kAdaptiveThresholdExperiment);
53 const size_t kMinExperimentLength = kEnabledPrefixLength + 3;
54 if (experiment_string.length() < kMinExperimentLength ||
55 experiment_string.substr(0, kEnabledPrefixLength) != kEnabledPrefix)
56 return false;
57 return sscanf(experiment_string.substr(kEnabledPrefixLength + 1).c_str(),
58 "%lf,%lf", k_up, k_down) == 2;
59 }
60
OveruseDetector(const WebRtcKeyValueConfig * key_value_config)61 OveruseDetector::OveruseDetector(const WebRtcKeyValueConfig* key_value_config)
62 // Experiment is on by default, but can be disabled with finch by setting
63 // the field trial string to "WebRTC-AdaptiveBweThreshold/Disabled/".
64 : in_experiment_(!AdaptiveThresholdExperimentIsDisabled(*key_value_config)),
65 k_up_(0.0087),
66 k_down_(0.039),
67 overusing_time_threshold_(100),
68 threshold_(12.5),
69 last_update_ms_(-1),
70 prev_offset_(0.0),
71 time_over_using_(-1),
72 overuse_counter_(0),
73 hypothesis_(BandwidthUsage::kBwNormal) {
74 if (!AdaptiveThresholdExperimentIsDisabled(*key_value_config))
75 InitializeExperiment(*key_value_config);
76 }
77
~OveruseDetector()78 OveruseDetector::~OveruseDetector() {}
79
State() const80 BandwidthUsage OveruseDetector::State() const {
81 return hypothesis_;
82 }
83
Detect(double offset,double ts_delta,int num_of_deltas,int64_t now_ms)84 BandwidthUsage OveruseDetector::Detect(double offset,
85 double ts_delta,
86 int num_of_deltas,
87 int64_t now_ms) {
88 if (num_of_deltas < 2) {
89 return BandwidthUsage::kBwNormal;
90 }
91 const double T = std::min(num_of_deltas, kMaxNumDeltas) * offset;
92 BWE_TEST_LOGGING_PLOT(1, "T", now_ms, T);
93 BWE_TEST_LOGGING_PLOT(1, "threshold", now_ms, threshold_);
94 if (T > threshold_) {
95 if (time_over_using_ == -1) {
96 // Initialize the timer. Assume that we've been
97 // over-using half of the time since the previous
98 // sample.
99 time_over_using_ = ts_delta / 2;
100 } else {
101 // Increment timer
102 time_over_using_ += ts_delta;
103 }
104 overuse_counter_++;
105 if (time_over_using_ > overusing_time_threshold_ && overuse_counter_ > 1) {
106 if (offset >= prev_offset_) {
107 time_over_using_ = 0;
108 overuse_counter_ = 0;
109 hypothesis_ = BandwidthUsage::kBwOverusing;
110 }
111 }
112 } else if (T < -threshold_) {
113 time_over_using_ = -1;
114 overuse_counter_ = 0;
115 hypothesis_ = BandwidthUsage::kBwUnderusing;
116 } else {
117 time_over_using_ = -1;
118 overuse_counter_ = 0;
119 hypothesis_ = BandwidthUsage::kBwNormal;
120 }
121 prev_offset_ = offset;
122
123 UpdateThreshold(T, now_ms);
124
125 return hypothesis_;
126 }
127
UpdateThreshold(double modified_offset,int64_t now_ms)128 void OveruseDetector::UpdateThreshold(double modified_offset, int64_t now_ms) {
129 if (!in_experiment_)
130 return;
131
132 if (last_update_ms_ == -1)
133 last_update_ms_ = now_ms;
134
135 if (fabs(modified_offset) > threshold_ + kMaxAdaptOffsetMs) {
136 // Avoid adapting the threshold to big latency spikes, caused e.g.,
137 // by a sudden capacity drop.
138 last_update_ms_ = now_ms;
139 return;
140 }
141
142 const double k = fabs(modified_offset) < threshold_ ? k_down_ : k_up_;
143 const int64_t kMaxTimeDeltaMs = 100;
144 int64_t time_delta_ms = std::min(now_ms - last_update_ms_, kMaxTimeDeltaMs);
145 threshold_ += k * (fabs(modified_offset) - threshold_) * time_delta_ms;
146 threshold_ = rtc::SafeClamp(threshold_, 6.f, 600.f);
147 last_update_ms_ = now_ms;
148 }
149
InitializeExperiment(const WebRtcKeyValueConfig & key_value_config)150 void OveruseDetector::InitializeExperiment(
151 const WebRtcKeyValueConfig& key_value_config) {
152 RTC_DCHECK(in_experiment_);
153 double k_up = 0.0;
154 double k_down = 0.0;
155 overusing_time_threshold_ = kOverUsingTimeThreshold;
156 if (ReadExperimentConstants(key_value_config, &k_up, &k_down)) {
157 k_up_ = k_up;
158 k_down_ = k_down;
159 }
160 }
161 } // namespace webrtc
162