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