blob: 3015d3dd0f154b0d46249e7d0082f22edfdeca05 [file] [log] [blame]
Christoffer Rodbro3a837482018-11-19 15:30:23 +01001/*
2 * Copyright (c) 2018 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/bitrate_controller/loss_based_bandwidth_estimation.h"
12
13#include <algorithm>
14#include <string>
15#include <vector>
16
17#include "api/units/data_rate.h"
18#include "api/units/time_delta.h"
19#include "system_wrappers/include/field_trial.h"
20
21namespace webrtc {
22namespace {
23const char kBweLossBasedControl[] = "WebRTC-Bwe-LossBasedControl";
24
25// Increase slower when RTT is high.
26double GetIncreaseFactor(const LossBasedControlConfig& config, TimeDelta rtt) {
27 // Clamp the RTT
28 if (rtt < config.increase_low_rtt) {
29 rtt = config.increase_low_rtt;
30 } else if (rtt > config.increase_high_rtt) {
31 rtt = config.increase_high_rtt;
32 }
33 auto rtt_range = config.increase_high_rtt.Get() - config.increase_low_rtt;
34 if (rtt_range <= TimeDelta::Zero()) {
35 RTC_DCHECK(false); // Only on misconfiguration.
36 return config.min_increase_factor;
37 }
38 auto rtt_offset = rtt - config.increase_low_rtt;
39 auto relative_offset = std::max(0.0, std::min(rtt_offset / rtt_range, 1.0));
40 auto factor_range = config.max_increase_factor - config.min_increase_factor;
41 return config.min_increase_factor + (1 - relative_offset) * factor_range;
42}
43
44double LossFromBitrate(DataRate bitrate,
45 DataRate loss_bandwidth_balance,
46 double exponent) {
47 if (loss_bandwidth_balance >= bitrate)
48 return 1.0;
49 return pow(loss_bandwidth_balance / bitrate, exponent);
50}
51
52DataRate BitrateFromLoss(double loss,
53 DataRate loss_bandwidth_balance,
54 double exponent) {
55 if (exponent <= 0) {
56 RTC_DCHECK(false);
57 return DataRate::Infinity();
58 }
59 if (loss < 1e-5)
60 return DataRate::Infinity();
61 return loss_bandwidth_balance * pow(loss, -1.0 / exponent);
62}
63
64double ExponentialUpdate(TimeDelta window, TimeDelta interval) {
65 // Use the convention that exponential window length (which is really
66 // infinite) is the time it takes to dampen to 1/e.
67 if (window <= TimeDelta::Zero()) {
68 RTC_DCHECK(false);
69 return 1.0f;
70 }
71 return 1.0f - exp(interval / window * -1.0);
72}
73
74} // namespace
75
76LossBasedControlConfig::LossBasedControlConfig()
77 : enabled(field_trial::IsEnabled(kBweLossBasedControl)),
78 min_increase_factor("min_incr", 1.02),
79 max_increase_factor("max_incr", 1.08),
80 increase_low_rtt("incr_low_rtt", TimeDelta::ms(200)),
81 increase_high_rtt("incr_high_rtt", TimeDelta::ms(800)),
82 decrease_factor("decr", 0.99),
83 loss_window("loss_win", TimeDelta::ms(800)),
84 loss_max_window("loss_max_win", TimeDelta::ms(800)),
85 acknowledged_rate_max_window("ackrate_max_win", TimeDelta::ms(800)),
86 increase_offset("incr_offset", DataRate::bps(1000)),
87 loss_bandwidth_balance_increase("balance_incr", DataRate::kbps(0.5)),
88 loss_bandwidth_balance_decrease("balance_decr", DataRate::kbps(4)),
89 loss_bandwidth_balance_exponent("exponent", 0.5),
90 allow_resets("resets", false),
91 decrease_interval("decr_intvl", TimeDelta::ms(300)),
92 loss_report_timeout("timeout", TimeDelta::ms(6000)) {
93 std::string trial_string = field_trial::FindFullName(kBweLossBasedControl);
94 ParseFieldTrial(
95 {&min_increase_factor, &max_increase_factor, &increase_low_rtt,
96 &increase_high_rtt, &decrease_factor, &loss_window, &loss_max_window,
97 &acknowledged_rate_max_window, &increase_offset,
98 &loss_bandwidth_balance_increase, &loss_bandwidth_balance_decrease,
99 &loss_bandwidth_balance_exponent, &allow_resets, &decrease_interval,
100 &loss_report_timeout},
101 trial_string);
102}
103LossBasedControlConfig::LossBasedControlConfig(const LossBasedControlConfig&) =
104 default;
105LossBasedControlConfig::~LossBasedControlConfig() = default;
106
107LossBasedBandwidthEstimation::LossBasedBandwidthEstimation()
108 : config_(LossBasedControlConfig()),
109 average_loss_(0),
110 average_loss_max_(0),
111 loss_based_bitrate_(DataRate::Zero()),
112 acknowledged_bitrate_max_(DataRate::Zero()),
113 acknowledged_bitrate_last_update_(Timestamp::MinusInfinity()),
114 time_last_decrease_(Timestamp::MinusInfinity()),
115 has_decreased_since_last_loss_report_(false),
116 last_loss_packet_report_(Timestamp::MinusInfinity()),
117 last_loss_ratio_(0) {}
118
119void LossBasedBandwidthEstimation::UpdateLossStatistics(
120 const std::vector<PacketResult>& packet_results,
121 Timestamp at_time) {
122 if (packet_results.empty()) {
123 RTC_DCHECK(false);
124 return;
125 }
126 int loss_count = 0;
Mirko Bonadei739baf02019-01-27 17:29:42 +0100127 for (const auto& pkt : packet_results) {
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100128 loss_count += pkt.receive_time.IsInfinite() ? 1 : 0;
129 }
130 last_loss_ratio_ = static_cast<double>(loss_count) / packet_results.size();
131 const TimeDelta time_passed = last_loss_packet_report_.IsFinite()
132 ? at_time - last_loss_packet_report_
133 : TimeDelta::seconds(1);
134 last_loss_packet_report_ = at_time;
135 has_decreased_since_last_loss_report_ = false;
136
137 average_loss_ += ExponentialUpdate(config_.loss_window, time_passed) *
138 (last_loss_ratio_ - average_loss_);
139 if (average_loss_ > average_loss_max_) {
140 average_loss_max_ = average_loss_;
141 } else {
142 average_loss_max_ +=
143 ExponentialUpdate(config_.loss_max_window, time_passed) *
144 (average_loss_ - average_loss_max_);
145 }
146}
147
148void LossBasedBandwidthEstimation::UpdateAcknowledgedBitrate(
149 DataRate acknowledged_bitrate,
150 Timestamp at_time) {
151 const TimeDelta time_passed =
152 acknowledged_bitrate_last_update_.IsFinite()
153 ? at_time - acknowledged_bitrate_last_update_
154 : TimeDelta::seconds(1);
155 acknowledged_bitrate_last_update_ = at_time;
156 if (acknowledged_bitrate > acknowledged_bitrate_max_) {
157 acknowledged_bitrate_max_ = acknowledged_bitrate;
158 } else {
Christoffer Rodbro5976bde2018-11-29 17:12:52 +0100159 acknowledged_bitrate_max_ -=
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100160 ExponentialUpdate(config_.acknowledged_rate_max_window, time_passed) *
Christoffer Rodbro5976bde2018-11-29 17:12:52 +0100161 (acknowledged_bitrate_max_ - acknowledged_bitrate);
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100162 }
163}
164
165void LossBasedBandwidthEstimation::Update(Timestamp at_time,
166 DataRate min_bitrate,
167 TimeDelta last_round_trip_time) {
168 // Only increase if loss has been low for some time.
169 const double loss_estimate_for_increase = average_loss_max_;
170 // Avoid multiple decreases from averaging over one loss spike.
171 const double loss_estimate_for_decrease =
172 std::min(average_loss_, last_loss_ratio_);
173
174 const double loss_increase_threshold = LossFromBitrate(
175 loss_based_bitrate_, config_.loss_bandwidth_balance_increase,
176 config_.loss_bandwidth_balance_exponent);
177 const double loss_decrease_threshold = LossFromBitrate(
178 loss_based_bitrate_, config_.loss_bandwidth_balance_decrease,
179 config_.loss_bandwidth_balance_exponent);
180 const bool allow_decrease =
181 !has_decreased_since_last_loss_report_ &&
182 (at_time - time_last_decrease_ >=
183 last_round_trip_time + config_.decrease_interval);
184
185 if (loss_estimate_for_increase < loss_increase_threshold) {
186 // Increase bitrate by RTT-adaptive ratio.
187 DataRate new_increased_bitrate =
188 min_bitrate * GetIncreaseFactor(config_, last_round_trip_time) +
189 config_.increase_offset;
190 // The bitrate that would make the loss "just high enough".
191 const DataRate new_increased_bitrate_cap = BitrateFromLoss(
192 loss_estimate_for_increase, config_.loss_bandwidth_balance_increase,
193 config_.loss_bandwidth_balance_exponent);
194 new_increased_bitrate =
195 std::min(new_increased_bitrate, new_increased_bitrate_cap);
196 loss_based_bitrate_ = std::max(new_increased_bitrate, loss_based_bitrate_);
197 } else if (loss_estimate_for_decrease > loss_decrease_threshold &&
198 allow_decrease) {
199 DataRate new_decreased_bitrate =
200 config_.decrease_factor * acknowledged_bitrate_max_;
201 // The bitrate that would make the loss "just acceptable".
202 const DataRate new_decreased_bitrate_floor = BitrateFromLoss(
203 loss_estimate_for_decrease, config_.loss_bandwidth_balance_decrease,
204 config_.loss_bandwidth_balance_exponent);
205 new_decreased_bitrate =
206 std::max(new_decreased_bitrate, new_decreased_bitrate_floor);
207 if (new_decreased_bitrate < loss_based_bitrate_) {
208 time_last_decrease_ = at_time;
209 has_decreased_since_last_loss_report_ = true;
210 loss_based_bitrate_ = new_decreased_bitrate;
211 }
212 }
213}
214
Christoffer Rodbro982639c2019-01-18 15:34:59 +0100215void LossBasedBandwidthEstimation::Reset(DataRate bitrate) {
216 loss_based_bitrate_ = bitrate;
217 average_loss_ = 0;
218 average_loss_max_ = 0;
219}
220
221void LossBasedBandwidthEstimation::MaybeReset(DataRate bitrate) {
222 if (config_.allow_resets)
223 Reset(bitrate);
224}
225
226void LossBasedBandwidthEstimation::SetInitialBitrate(DataRate bitrate) {
227 Reset(bitrate);
228}
229
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100230} // namespace webrtc