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