• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 <math.h>
12 #include <stdlib.h>  // fabsf
13 
14 #include "webrtc/modules/remote_bitrate_estimator/overuse_detector.h"
15 #include "webrtc/modules/remote_bitrate_estimator/remote_rate_control.h"
16 #include "webrtc/modules/rtp_rtcp/source/rtp_utility.h"
17 #include "webrtc/system_wrappers/interface/trace.h"
18 
19 enum { kOverUsingTimeThreshold = 100 };
20 enum { kMinFramePeriodHistoryLength = 60 };
21 
22 namespace webrtc {
OveruseDetector(const OverUseDetectorOptions & options)23 OveruseDetector::OveruseDetector(const OverUseDetectorOptions& options)
24     : options_(options),
25       current_frame_(),
26       prev_frame_(),
27       num_of_deltas_(0),
28       slope_(options_.initial_slope),
29       offset_(options_.initial_offset),
30       E_(),
31       process_noise_(),
32       avg_noise_(options_.initial_avg_noise),
33       var_noise_(options_.initial_var_noise),
34       threshold_(options_.initial_threshold),
35       ts_delta_hist_(),
36       prev_offset_(0.0),
37       time_over_using_(-1),
38       over_use_counter_(0),
39       hypothesis_(kBwNormal) {
40   memcpy(E_, options_.initial_e, sizeof(E_));
41   memcpy(process_noise_, options_.initial_process_noise,
42          sizeof(process_noise_));
43 }
44 
~OveruseDetector()45 OveruseDetector::~OveruseDetector() {
46   ts_delta_hist_.clear();
47 }
48 
Update(uint16_t packet_size,int64_t timestamp_ms,uint32_t timestamp,const int64_t arrival_time_ms)49 void OveruseDetector::Update(uint16_t packet_size,
50                              int64_t timestamp_ms,
51                              uint32_t timestamp,
52                              const int64_t arrival_time_ms) {
53   bool new_timestamp = (timestamp != current_frame_.timestamp);
54   if (timestamp_ms >= 0) {
55     if (prev_frame_.timestamp_ms == -1 && current_frame_.timestamp_ms == -1) {
56       SwitchTimeBase();
57     }
58     new_timestamp = (timestamp_ms != current_frame_.timestamp_ms);
59   }
60   if (current_frame_.timestamp == -1) {
61     // This is the first incoming packet. We don't have enough data to update
62     // the filter, so we store it until we have two frames of data to process.
63     current_frame_.timestamp = timestamp;
64     current_frame_.timestamp_ms = timestamp_ms;
65   } else if (!PacketInOrder(timestamp, timestamp_ms)) {
66     return;
67   } else if (new_timestamp) {
68     // First packet of a later frame, the previous frame sample is ready.
69     if (prev_frame_.complete_time_ms >= 0) {  // This is our second frame.
70       int64_t t_delta = 0;
71       double ts_delta = 0;
72       TimeDeltas(current_frame_, prev_frame_, &t_delta, &ts_delta);
73       UpdateKalman(t_delta, ts_delta, current_frame_.size, prev_frame_.size);
74     }
75     prev_frame_ = current_frame_;
76     // The new timestamp is now the current frame.
77     current_frame_.timestamp = timestamp;
78     current_frame_.timestamp_ms = timestamp_ms;
79     current_frame_.size = 0;
80   }
81   // Accumulate the frame size
82   current_frame_.size += packet_size;
83   current_frame_.complete_time_ms = arrival_time_ms;
84 }
85 
State() const86 BandwidthUsage OveruseDetector::State() const {
87   return hypothesis_;
88 }
89 
NoiseVar() const90 double OveruseDetector::NoiseVar() const {
91   return var_noise_;
92 }
93 
SetRateControlRegion(RateControlRegion region)94 void OveruseDetector::SetRateControlRegion(RateControlRegion region) {
95   switch (region) {
96     case kRcMaxUnknown: {
97       threshold_ = options_.initial_threshold;
98       break;
99     }
100     case kRcAboveMax:
101     case kRcNearMax: {
102       threshold_ = options_.initial_threshold / 2;
103       break;
104     }
105   }
106 }
107 
SwitchTimeBase()108 void OveruseDetector::SwitchTimeBase() {
109   current_frame_.size = 0;
110   current_frame_.complete_time_ms = -1;
111   current_frame_.timestamp = -1;
112   prev_frame_ = current_frame_;
113 }
114 
TimeDeltas(const FrameSample & current_frame,const FrameSample & prev_frame,int64_t * t_delta,double * ts_delta)115 void OveruseDetector::TimeDeltas(const FrameSample& current_frame,
116                                  const FrameSample& prev_frame,
117                                  int64_t* t_delta,
118                                  double* ts_delta) {
119   assert(t_delta);
120   assert(ts_delta);
121   num_of_deltas_++;
122   if (num_of_deltas_ > 1000) {
123     num_of_deltas_ = 1000;
124   }
125   if (current_frame.timestamp_ms == -1) {
126     uint32_t timestamp_diff = current_frame.timestamp - prev_frame.timestamp;
127     *ts_delta = timestamp_diff / 90.0;
128   } else {
129     *ts_delta = current_frame.timestamp_ms - prev_frame.timestamp_ms;
130   }
131   *t_delta = current_frame.complete_time_ms - prev_frame.complete_time_ms;
132   assert(*ts_delta > 0);
133 }
134 
PacketInOrder(uint32_t timestamp,int64_t timestamp_ms)135 bool OveruseDetector::PacketInOrder(uint32_t timestamp, int64_t timestamp_ms) {
136   if (current_frame_.timestamp_ms == -1 && current_frame_.timestamp > -1) {
137     return InOrderTimestamp(timestamp, current_frame_.timestamp);
138   } else if (current_frame_.timestamp_ms > 0) {
139     // Using timestamps converted to NTP time.
140     return timestamp_ms > current_frame_.timestamp_ms;
141   }
142   // This is the first packet.
143   return true;
144 }
145 
InOrderTimestamp(uint32_t timestamp,uint32_t prev_timestamp)146 bool OveruseDetector::InOrderTimestamp(uint32_t timestamp,
147                                        uint32_t prev_timestamp) {
148   uint32_t timestamp_diff = timestamp - prev_timestamp;
149   // Assume that a diff this big must be due to reordering. Don't update
150   // with reordered samples.
151   return (timestamp_diff < 0x80000000);
152 }
153 
CurrentDrift()154 double OveruseDetector::CurrentDrift() {
155   return 1.0;
156 }
157 
UpdateKalman(int64_t t_delta,double ts_delta,uint32_t frame_size,uint32_t prev_frame_size)158 void OveruseDetector::UpdateKalman(int64_t t_delta,
159                                    double ts_delta,
160                                    uint32_t frame_size,
161                                    uint32_t prev_frame_size) {
162   const double min_frame_period = UpdateMinFramePeriod(ts_delta);
163   const double drift = CurrentDrift();
164   // Compensate for drift
165   const double t_ts_delta = t_delta - ts_delta / drift;
166   double fs_delta = static_cast<double>(frame_size) - prev_frame_size;
167 
168   // Update the Kalman filter
169   const double scale_factor =  min_frame_period / (1000.0 / 30.0);
170   E_[0][0] += process_noise_[0] * scale_factor;
171   E_[1][1] += process_noise_[1] * scale_factor;
172 
173   if ((hypothesis_ == kBwOverusing && offset_ < prev_offset_) ||
174       (hypothesis_ == kBwUnderusing && offset_ > prev_offset_)) {
175     E_[1][1] += 10 * process_noise_[1] * scale_factor;
176   }
177 
178   const double h[2] = {fs_delta, 1.0};
179   const double Eh[2] = {E_[0][0]*h[0] + E_[0][1]*h[1],
180                         E_[1][0]*h[0] + E_[1][1]*h[1]};
181 
182   const double residual = t_ts_delta - slope_*h[0] - offset_;
183 
184   const bool stable_state =
185       (BWE_MIN(num_of_deltas_, 60) * fabs(offset_) < threshold_);
186   // We try to filter out very late frames. For instance periodic key
187   // frames doesn't fit the Gaussian model well.
188   if (fabs(residual) < 3 * sqrt(var_noise_)) {
189     UpdateNoiseEstimate(residual, min_frame_period, stable_state);
190   } else {
191     UpdateNoiseEstimate(3 * sqrt(var_noise_), min_frame_period, stable_state);
192   }
193 
194   const double denom = var_noise_ + h[0]*Eh[0] + h[1]*Eh[1];
195 
196   const double K[2] = {Eh[0] / denom,
197                        Eh[1] / denom};
198 
199   const double IKh[2][2] = {{1.0 - K[0]*h[0], -K[0]*h[1]},
200                             {-K[1]*h[0], 1.0 - K[1]*h[1]}};
201   const double e00 = E_[0][0];
202   const double e01 = E_[0][1];
203 
204   // Update state
205   E_[0][0] = e00 * IKh[0][0] + E_[1][0] * IKh[0][1];
206   E_[0][1] = e01 * IKh[0][0] + E_[1][1] * IKh[0][1];
207   E_[1][0] = e00 * IKh[1][0] + E_[1][0] * IKh[1][1];
208   E_[1][1] = e01 * IKh[1][0] + E_[1][1] * IKh[1][1];
209 
210   // Covariance matrix, must be positive semi-definite
211   assert(E_[0][0] + E_[1][1] >= 0 &&
212          E_[0][0] * E_[1][1] - E_[0][1] * E_[1][0] >= 0 &&
213          E_[0][0] >= 0);
214 
215   slope_ = slope_ + K[0] * residual;
216   prev_offset_ = offset_;
217   offset_ = offset_ + K[1] * residual;
218 
219   Detect(ts_delta);
220 }
221 
UpdateMinFramePeriod(double ts_delta)222 double OveruseDetector::UpdateMinFramePeriod(double ts_delta) {
223   double min_frame_period = ts_delta;
224   if (ts_delta_hist_.size() >= kMinFramePeriodHistoryLength) {
225     std::list<double>::iterator first_item = ts_delta_hist_.begin();
226     ts_delta_hist_.erase(first_item);
227   }
228   std::list<double>::iterator it = ts_delta_hist_.begin();
229   for (; it != ts_delta_hist_.end(); it++) {
230     min_frame_period = BWE_MIN(*it, min_frame_period);
231   }
232   ts_delta_hist_.push_back(ts_delta);
233   return min_frame_period;
234 }
235 
UpdateNoiseEstimate(double residual,double ts_delta,bool stable_state)236 void OveruseDetector::UpdateNoiseEstimate(double residual,
237                                           double ts_delta,
238                                           bool stable_state) {
239   if (!stable_state) {
240     return;
241   }
242   // Faster filter during startup to faster adapt to the jitter level
243   // of the network alpha is tuned for 30 frames per second, but
244   double alpha = 0.01;
245   if (num_of_deltas_ > 10*30) {
246     alpha = 0.002;
247   }
248   // Only update the noise estimate if we're not over-using
249   // beta is a function of alpha and the time delta since
250   // the previous update.
251   const double beta = pow(1 - alpha, ts_delta * 30.0 / 1000.0);
252   avg_noise_ = beta * avg_noise_
253               + (1 - beta) * residual;
254   var_noise_ = beta * var_noise_
255               + (1 - beta) * (avg_noise_ - residual) * (avg_noise_ - residual);
256   if (var_noise_ < 1e-7) {
257     var_noise_ = 1e-7;
258   }
259 }
260 
Detect(double ts_delta)261 BandwidthUsage OveruseDetector::Detect(double ts_delta) {
262   if (num_of_deltas_ < 2) {
263     return kBwNormal;
264   }
265   const double T = BWE_MIN(num_of_deltas_, 60) * offset_;
266   if (fabs(T) > threshold_) {
267     if (offset_ > 0) {
268       if (time_over_using_ == -1) {
269         // Initialize the timer. Assume that we've been
270         // over-using half of the time since the previous
271         // sample.
272         time_over_using_ = ts_delta / 2;
273       } else {
274         // Increment timer
275         time_over_using_ += ts_delta;
276       }
277       over_use_counter_++;
278       if (time_over_using_ > kOverUsingTimeThreshold
279           && over_use_counter_ > 1) {
280         if (offset_ >= prev_offset_) {
281           time_over_using_ = 0;
282           over_use_counter_ = 0;
283           hypothesis_ = kBwOverusing;
284         }
285       }
286     } else {
287       time_over_using_ = -1;
288       over_use_counter_ = 0;
289       hypothesis_ = kBwUnderusing;
290     }
291   } else {
292     time_over_using_ = -1;
293     over_use_counter_ = 0;
294     hypothesis_ = kBwNormal;
295   }
296   return hypothesis_;
297 }
298 }  // namespace webrtc
299