blob: 64c0482edb5cc4b093bbcc248e7bfb46d5e7d5ef [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_);
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100173 const bool allow_decrease =
174 !has_decreased_since_last_loss_report_ &&
175 (at_time - time_last_decrease_ >=
176 last_round_trip_time + config_.decrease_interval);
177
Sebastian Jansson28aced52019-06-11 11:08:20 +0200178 if (loss_estimate_for_increase < loss_increase_threshold()) {
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100179 // Increase bitrate by RTT-adaptive ratio.
180 DataRate new_increased_bitrate =
181 min_bitrate * GetIncreaseFactor(config_, last_round_trip_time) +
182 config_.increase_offset;
183 // The bitrate that would make the loss "just high enough".
184 const DataRate new_increased_bitrate_cap = BitrateFromLoss(
185 loss_estimate_for_increase, config_.loss_bandwidth_balance_increase,
186 config_.loss_bandwidth_balance_exponent);
187 new_increased_bitrate =
188 std::min(new_increased_bitrate, new_increased_bitrate_cap);
189 loss_based_bitrate_ = std::max(new_increased_bitrate, loss_based_bitrate_);
Sebastian Jansson28aced52019-06-11 11:08:20 +0200190 } else if (loss_estimate_for_decrease > loss_decrease_threshold() &&
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100191 allow_decrease) {
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100192 // The bitrate that would make the loss "just acceptable".
193 const DataRate new_decreased_bitrate_floor = BitrateFromLoss(
194 loss_estimate_for_decrease, config_.loss_bandwidth_balance_decrease,
195 config_.loss_bandwidth_balance_exponent);
Sebastian Jansson28aced52019-06-11 11:08:20 +0200196 DataRate new_decreased_bitrate =
197 std::max(decreased_bitrate(), new_decreased_bitrate_floor);
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100198 if (new_decreased_bitrate < loss_based_bitrate_) {
199 time_last_decrease_ = at_time;
200 has_decreased_since_last_loss_report_ = true;
201 loss_based_bitrate_ = new_decreased_bitrate;
202 }
203 }
204}
205
Christoffer Rodbro982639c2019-01-18 15:34:59 +0100206void LossBasedBandwidthEstimation::Reset(DataRate bitrate) {
207 loss_based_bitrate_ = bitrate;
208 average_loss_ = 0;
209 average_loss_max_ = 0;
210}
211
Sebastian Jansson28aced52019-06-11 11:08:20 +0200212double LossBasedBandwidthEstimation::loss_increase_threshold() const {
213 return LossFromBitrate(loss_based_bitrate_,
214 config_.loss_bandwidth_balance_increase,
215 config_.loss_bandwidth_balance_exponent);
216}
217
218double LossBasedBandwidthEstimation::loss_decrease_threshold() const {
219 return LossFromBitrate(loss_based_bitrate_,
220 config_.loss_bandwidth_balance_decrease,
221 config_.loss_bandwidth_balance_exponent);
222}
223
224DataRate LossBasedBandwidthEstimation::decreased_bitrate() const {
225 return config_.decrease_factor * acknowledged_bitrate_max_;
226}
227
Christoffer Rodbro982639c2019-01-18 15:34:59 +0100228void LossBasedBandwidthEstimation::MaybeReset(DataRate bitrate) {
229 if (config_.allow_resets)
230 Reset(bitrate);
231}
232
233void LossBasedBandwidthEstimation::SetInitialBitrate(DataRate bitrate) {
234 Reset(bitrate);
235}
236
Christoffer Rodbro3a837482018-11-19 15:30:23 +0100237} // namespace webrtc