blob: 6338f9646f998db7cd0b77585a949eed06f19d2e [file] [log] [blame]
philipel863a8262016-06-17 09:21:34 -07001/*
2 * Copyright (c) 2016 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/congestion_controller/delay_based_bwe.h"
12
philipel863a8262016-06-17 09:21:34 -070013#include <algorithm>
Stefan Holmer492ee282016-10-27 17:19:20 +020014#include <cmath>
tereliusafaef8b2016-11-17 03:48:18 -080015#include <string>
philipel863a8262016-06-17 09:21:34 -070016
17#include "webrtc/base/checks.h"
18#include "webrtc/base/constructormagic.h"
19#include "webrtc/base/logging.h"
20#include "webrtc/base/thread_annotations.h"
Irfan Sheriffb2540bb2016-09-12 12:28:54 -070021#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
philipel863a8262016-06-17 09:21:34 -070022#include "webrtc/modules/pacing/paced_sender.h"
23#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
terelius5a388362016-12-09 05:50:01 -080024#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
Stefan Holmer492ee282016-10-27 17:19:20 +020025#include "webrtc/system_wrappers/include/field_trial.h"
stefan64636dd2016-08-03 00:29:03 -070026#include "webrtc/system_wrappers/include/metrics.h"
philipel863a8262016-06-17 09:21:34 -070027#include "webrtc/typedefs.h"
28
29namespace {
philipel7522a282016-08-16 10:59:36 +020030constexpr int kTimestampGroupLengthMs = 5;
31constexpr int kAbsSendTimeFraction = 18;
32constexpr int kAbsSendTimeInterArrivalUpshift = 8;
33constexpr int kInterArrivalShift =
34 kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift;
35constexpr double kTimestampToMs =
philipel863a8262016-06-17 09:21:34 -070036 1000.0 / static_cast<double>(1 << kInterArrivalShift);
philipel7522a282016-08-16 10:59:36 +020037// This ssrc is used to fulfill the current API but will be removed
38// after the API has been changed.
39constexpr uint32_t kFixedSsrc = 0;
Stefan Holmer492ee282016-10-27 17:19:20 +020040constexpr int kInitialRateWindowMs = 500;
41constexpr int kRateWindowMs = 150;
42
terelius5a388362016-12-09 05:50:01 -080043// Parameters for linear least squares fit of regression line to noisy data.
tereliusafaef8b2016-11-17 03:48:18 -080044constexpr size_t kDefaultTrendlineWindowSize = 15;
45constexpr double kDefaultTrendlineSmoothingCoeff = 0.9;
46constexpr double kDefaultTrendlineThresholdGain = 4.0;
47
terelius5a388362016-12-09 05:50:01 -080048// Parameters for Theil-Sen robust fitting of line to noisy data.
49constexpr size_t kDefaultMedianSlopeWindowSize = 20;
50constexpr double kDefaultMedianSlopeThresholdGain = 4.0;
51
Stefan Holmer492ee282016-10-27 17:19:20 +020052const char kBitrateEstimateExperiment[] = "WebRTC-ImprovedBitrateEstimate";
tereliusafaef8b2016-11-17 03:48:18 -080053const char kBweTrendlineFilterExperiment[] = "WebRTC-BweTrendlineFilter";
terelius5a388362016-12-09 05:50:01 -080054const char kBweMedianSlopeFilterExperiment[] = "WebRTC-BweMedianSlopeFilter";
Stefan Holmer492ee282016-10-27 17:19:20 +020055
56bool BitrateEstimateExperimentIsEnabled() {
57 return webrtc::field_trial::FindFullName(kBitrateEstimateExperiment) ==
58 "Enabled";
59}
tereliusafaef8b2016-11-17 03:48:18 -080060
61bool TrendlineFilterExperimentIsEnabled() {
62 std::string experiment_string =
63 webrtc::field_trial::FindFullName(kBweTrendlineFilterExperiment);
64 // The experiment is enabled iff the field trial string begins with "Enabled".
65 return experiment_string.find("Enabled") == 0;
66}
67
terelius5a388362016-12-09 05:50:01 -080068bool MedianSlopeFilterExperimentIsEnabled() {
69 std::string experiment_string =
70 webrtc::field_trial::FindFullName(kBweMedianSlopeFilterExperiment);
71 // The experiment is enabled iff the field trial string begins with "Enabled".
72 return experiment_string.find("Enabled") == 0;
73}
74
75bool ReadTrendlineFilterExperimentParameters(size_t* window_size,
tereliusafaef8b2016-11-17 03:48:18 -080076 double* smoothing_coef,
77 double* threshold_gain) {
78 RTC_DCHECK(TrendlineFilterExperimentIsEnabled());
terelius5a388362016-12-09 05:50:01 -080079 RTC_DCHECK(!MedianSlopeFilterExperimentIsEnabled());
80 RTC_DCHECK(window_size != nullptr);
81 RTC_DCHECK(smoothing_coef != nullptr);
82 RTC_DCHECK(threshold_gain != nullptr);
tereliusafaef8b2016-11-17 03:48:18 -080083 std::string experiment_string =
84 webrtc::field_trial::FindFullName(kBweTrendlineFilterExperiment);
85 int parsed_values = sscanf(experiment_string.c_str(), "Enabled-%zu,%lf,%lf",
terelius5a388362016-12-09 05:50:01 -080086 window_size, smoothing_coef, threshold_gain);
tereliusafaef8b2016-11-17 03:48:18 -080087 if (parsed_values == 3) {
terelius5a388362016-12-09 05:50:01 -080088 RTC_CHECK_GT(*window_size, 1) << "Need at least 2 points to fit a line.";
tereliusafaef8b2016-11-17 03:48:18 -080089 RTC_CHECK(0 <= *smoothing_coef && *smoothing_coef <= 1)
90 << "Coefficient needs to be between 0 and 1 for weighted average.";
91 RTC_CHECK_GT(*threshold_gain, 0) << "Threshold gain needs to be positive.";
92 return true;
93 }
94 LOG(LS_WARNING) << "Failed to parse parameters for BweTrendlineFilter "
95 "experiment from field trial string. Using default.";
terelius5a388362016-12-09 05:50:01 -080096 *window_size = kDefaultTrendlineWindowSize;
tereliusafaef8b2016-11-17 03:48:18 -080097 *smoothing_coef = kDefaultTrendlineSmoothingCoeff;
98 *threshold_gain = kDefaultTrendlineThresholdGain;
99 return false;
100}
101
terelius5a388362016-12-09 05:50:01 -0800102bool ReadMedianSlopeFilterExperimentParameters(size_t* window_size,
103 double* threshold_gain) {
104 RTC_DCHECK(!TrendlineFilterExperimentIsEnabled());
105 RTC_DCHECK(MedianSlopeFilterExperimentIsEnabled());
106 RTC_DCHECK(window_size != nullptr);
107 RTC_DCHECK(threshold_gain != nullptr);
108 std::string experiment_string =
109 webrtc::field_trial::FindFullName(kBweMedianSlopeFilterExperiment);
110 int parsed_values = sscanf(experiment_string.c_str(), "Enabled-%zu,%lf",
111 window_size, threshold_gain);
112 if (parsed_values == 2) {
113 RTC_CHECK_GT(*window_size, 1) << "Need at least 2 points to fit a line.";
114 RTC_CHECK_GT(*threshold_gain, 0) << "Threshold gain needs to be positive.";
115 return true;
116 }
117 LOG(LS_WARNING) << "Failed to parse parameters for BweMedianSlopeFilter "
118 "experiment from field trial string. Using default.";
119 *window_size = kDefaultMedianSlopeWindowSize;
120 *threshold_gain = kDefaultMedianSlopeThresholdGain;
121 return false;
122}
philipel863a8262016-06-17 09:21:34 -0700123} // namespace
124
125namespace webrtc {
tereliusafaef8b2016-11-17 03:48:18 -0800126
Stefan Holmer492ee282016-10-27 17:19:20 +0200127DelayBasedBwe::BitrateEstimator::BitrateEstimator()
128 : sum_(0),
129 current_win_ms_(0),
130 prev_time_ms_(-1),
131 bitrate_estimate_(-1.0f),
132 bitrate_estimate_var_(50.0f),
133 old_estimator_(kBitrateWindowMs, 8000),
134 in_experiment_(BitrateEstimateExperimentIsEnabled()) {}
135
136void DelayBasedBwe::BitrateEstimator::Update(int64_t now_ms, int bytes) {
137 if (!in_experiment_) {
138 old_estimator_.Update(bytes, now_ms);
139 rtc::Optional<uint32_t> rate = old_estimator_.Rate(now_ms);
140 bitrate_estimate_ = -1.0f;
141 if (rate)
142 bitrate_estimate_ = *rate / 1000.0f;
143 return;
144 }
145 int rate_window_ms = kRateWindowMs;
146 // We use a larger window at the beginning to get a more stable sample that
147 // we can use to initialize the estimate.
148 if (bitrate_estimate_ < 0.f)
149 rate_window_ms = kInitialRateWindowMs;
150 float bitrate_sample = UpdateWindow(now_ms, bytes, rate_window_ms);
151 if (bitrate_sample < 0.0f)
152 return;
153 if (bitrate_estimate_ < 0.0f) {
154 // This is the very first sample we get. Use it to initialize the estimate.
155 bitrate_estimate_ = bitrate_sample;
156 return;
157 }
158 // Define the sample uncertainty as a function of how far away it is from the
159 // current estimate.
160 float sample_uncertainty =
161 10.0f * std::abs(bitrate_estimate_ - bitrate_sample) / bitrate_estimate_;
162 float sample_var = sample_uncertainty * sample_uncertainty;
163 // Update a bayesian estimate of the rate, weighting it lower if the sample
164 // uncertainty is large.
165 // The bitrate estimate uncertainty is increased with each update to model
166 // that the bitrate changes over time.
167 float pred_bitrate_estimate_var = bitrate_estimate_var_ + 5.f;
168 bitrate_estimate_ = (sample_var * bitrate_estimate_ +
169 pred_bitrate_estimate_var * bitrate_sample) /
170 (sample_var + pred_bitrate_estimate_var);
171 bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var /
172 (sample_var + pred_bitrate_estimate_var);
173}
174
175float DelayBasedBwe::BitrateEstimator::UpdateWindow(int64_t now_ms,
176 int bytes,
177 int rate_window_ms) {
178 // Reset if time moves backwards.
179 if (now_ms < prev_time_ms_) {
180 prev_time_ms_ = -1;
181 sum_ = 0;
182 current_win_ms_ = 0;
183 }
184 if (prev_time_ms_ >= 0) {
185 current_win_ms_ += now_ms - prev_time_ms_;
186 // Reset if nothing has been received for more than a full window.
187 if (now_ms - prev_time_ms_ > rate_window_ms) {
188 sum_ = 0;
189 current_win_ms_ %= rate_window_ms;
190 }
191 }
192 prev_time_ms_ = now_ms;
193 float bitrate_sample = -1.0f;
194 if (current_win_ms_ >= rate_window_ms) {
195 bitrate_sample = 8.0f * sum_ / static_cast<float>(rate_window_ms);
196 current_win_ms_ -= rate_window_ms;
197 sum_ = 0;
198 }
199 sum_ += bytes;
200 return bitrate_sample;
201}
202
203rtc::Optional<uint32_t> DelayBasedBwe::BitrateEstimator::bitrate_bps() const {
204 if (bitrate_estimate_ < 0.f)
205 return rtc::Optional<uint32_t>();
206 return rtc::Optional<uint32_t>(bitrate_estimate_ * 1000);
207}
philipel863a8262016-06-17 09:21:34 -0700208
Stefan Holmer280de9e2016-09-30 10:06:51 +0200209DelayBasedBwe::DelayBasedBwe(Clock* clock)
terelius5a388362016-12-09 05:50:01 -0800210 : in_trendline_experiment_(TrendlineFilterExperimentIsEnabled()),
211 in_median_slope_experiment_(MedianSlopeFilterExperimentIsEnabled()),
212 clock_(clock),
philipel863a8262016-06-17 09:21:34 -0700213 inter_arrival_(),
tereliusafaef8b2016-11-17 03:48:18 -0800214 kalman_estimator_(),
215 trendline_estimator_(),
terelius84f83f82016-12-27 10:43:01 -0800216 detector_(),
Stefan Holmer492ee282016-10-27 17:19:20 +0200217 receiver_incoming_bitrate_(),
philipel863a8262016-06-17 09:21:34 -0700218 last_update_ms_(-1),
philipel7522a282016-08-16 10:59:36 +0200219 last_seen_packet_ms_(-1),
tereliusafaef8b2016-11-17 03:48:18 -0800220 uma_recorded_(false),
221 trendline_window_size_(kDefaultTrendlineWindowSize),
222 trendline_smoothing_coeff_(kDefaultTrendlineSmoothingCoeff),
223 trendline_threshold_gain_(kDefaultTrendlineThresholdGain),
terelius5a388362016-12-09 05:50:01 -0800224 probing_interval_estimator_(&rate_control_),
225 median_slope_window_size_(kDefaultMedianSlopeWindowSize),
226 median_slope_threshold_gain_(kDefaultMedianSlopeThresholdGain) {
tereliusafaef8b2016-11-17 03:48:18 -0800227 if (in_trendline_experiment_) {
228 ReadTrendlineFilterExperimentParameters(&trendline_window_size_,
229 &trendline_smoothing_coeff_,
230 &trendline_threshold_gain_);
231 }
terelius5a388362016-12-09 05:50:01 -0800232 if (in_median_slope_experiment_) {
233 ReadMedianSlopeFilterExperimentParameters(&trendline_window_size_,
234 &trendline_threshold_gain_);
235 }
236
philipel863a8262016-06-17 09:21:34 -0700237 network_thread_.DetachFromThread();
238}
239
Stefan Holmer280de9e2016-09-30 10:06:51 +0200240DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
philipel863a8262016-06-17 09:21:34 -0700241 const std::vector<PacketInfo>& packet_feedback_vector) {
242 RTC_DCHECK(network_thread_.CalledOnValidThread());
stefan64636dd2016-08-03 00:29:03 -0700243 if (!uma_recorded_) {
asapersson1d02d3e2016-09-09 22:40:25 -0700244 RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram,
245 BweNames::kSendSideTransportSeqNum,
246 BweNames::kBweNamesMax);
stefan64636dd2016-08-03 00:29:03 -0700247 uma_recorded_ = true;
248 }
Stefan Holmer280de9e2016-09-30 10:06:51 +0200249 Result aggregated_result;
philipel863a8262016-06-17 09:21:34 -0700250 for (const auto& packet_info : packet_feedback_vector) {
Stefan Holmer280de9e2016-09-30 10:06:51 +0200251 Result result = IncomingPacketInfo(packet_info);
252 if (result.updated)
253 aggregated_result = result;
philipel863a8262016-06-17 09:21:34 -0700254 }
Stefan Holmer280de9e2016-09-30 10:06:51 +0200255 return aggregated_result;
philipel863a8262016-06-17 09:21:34 -0700256}
257
Stefan Holmer280de9e2016-09-30 10:06:51 +0200258DelayBasedBwe::Result DelayBasedBwe::IncomingPacketInfo(
259 const PacketInfo& info) {
stefan5e12d362016-07-11 01:44:02 -0700260 int64_t now_ms = clock_->TimeInMilliseconds();
philipel863a8262016-06-17 09:21:34 -0700261
Stefan Holmer492ee282016-10-27 17:19:20 +0200262 receiver_incoming_bitrate_.Update(info.arrival_time_ms, info.payload_size);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200263 Result result;
264 // Reset if the stream has timed out.
265 if (last_seen_packet_ms_ == -1 ||
266 now_ms - last_seen_packet_ms_ > kStreamTimeOutMs) {
267 inter_arrival_.reset(
268 new InterArrival((kTimestampGroupLengthMs << kInterArrivalShift) / 1000,
269 kTimestampToMs, true));
tereliusafaef8b2016-11-17 03:48:18 -0800270 kalman_estimator_.reset(new OveruseEstimator(OverUseDetectorOptions()));
271 trendline_estimator_.reset(new TrendlineEstimator(
272 trendline_window_size_, trendline_smoothing_coeff_,
273 trendline_threshold_gain_));
terelius5a388362016-12-09 05:50:01 -0800274 median_slope_estimator_.reset(new MedianSlopeEstimator(
275 median_slope_window_size_, median_slope_threshold_gain_));
Stefan Holmer280de9e2016-09-30 10:06:51 +0200276 }
277 last_seen_packet_ms_ = now_ms;
philipel863a8262016-06-17 09:21:34 -0700278
Stefan Holmer280de9e2016-09-30 10:06:51 +0200279 uint32_t send_time_24bits =
280 static_cast<uint32_t>(
281 ((static_cast<uint64_t>(info.send_time_ms) << kAbsSendTimeFraction) +
282 500) /
283 1000) &
284 0x00FFFFFF;
285 // Shift up send time to use the full 32 bits that inter_arrival works with,
286 // so wrapping works properly.
287 uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift;
stefanfd0d4262016-09-29 02:44:31 -0700288
Stefan Holmer280de9e2016-09-30 10:06:51 +0200289 uint32_t ts_delta = 0;
290 int64_t t_delta = 0;
291 int size_delta = 0;
292 if (inter_arrival_->ComputeDeltas(timestamp, info.arrival_time_ms, now_ms,
293 info.payload_size, &ts_delta, &t_delta,
294 &size_delta)) {
295 double ts_delta_ms = (1000.0 * ts_delta) / (1 << kInterArrivalShift);
tereliusafaef8b2016-11-17 03:48:18 -0800296 if (in_trendline_experiment_) {
297 trendline_estimator_->Update(t_delta, ts_delta_ms, info.arrival_time_ms);
298 detector_.Detect(trendline_estimator_->trendline_slope(), ts_delta_ms,
299 trendline_estimator_->num_of_deltas(),
Stefan Holmer280de9e2016-09-30 10:06:51 +0200300 info.arrival_time_ms);
terelius5a388362016-12-09 05:50:01 -0800301 } else if (in_median_slope_experiment_) {
302 median_slope_estimator_->Update(t_delta, ts_delta_ms,
303 info.arrival_time_ms);
304 detector_.Detect(median_slope_estimator_->trendline_slope(), ts_delta_ms,
305 median_slope_estimator_->num_of_deltas(),
306 info.arrival_time_ms);
tereliusafaef8b2016-11-17 03:48:18 -0800307 } else {
308 kalman_estimator_->Update(t_delta, ts_delta_ms, size_delta,
309 detector_.State(), info.arrival_time_ms);
310 detector_.Detect(kalman_estimator_->offset(), ts_delta_ms,
311 kalman_estimator_->num_of_deltas(),
312 info.arrival_time_ms);
313 }
stefan5ec85fb2016-09-29 04:19:38 -0700314 }
315
Stefan Holmer280de9e2016-09-30 10:06:51 +0200316 int probing_bps = 0;
317 if (info.probe_cluster_id != PacketInfo::kNotAProbe) {
318 probing_bps = probe_bitrate_estimator_.HandleProbeAndEstimateBitrate(info);
319 }
Stefan Holmer492ee282016-10-27 17:19:20 +0200320 rtc::Optional<uint32_t> acked_bitrate_bps =
321 receiver_incoming_bitrate_.bitrate_bps();
Stefan Holmer280de9e2016-09-30 10:06:51 +0200322 // Currently overusing the bandwidth.
323 if (detector_.State() == kBwOverusing) {
Stefan Holmer492ee282016-10-27 17:19:20 +0200324 if (acked_bitrate_bps &&
325 rate_control_.TimeToReduceFurther(now_ms, *acked_bitrate_bps)) {
326 result.updated =
327 UpdateEstimate(info.arrival_time_ms, now_ms, acked_bitrate_bps,
328 &result.target_bitrate_bps);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200329 }
330 } else if (probing_bps > 0) {
331 // No overuse, but probing measured a bitrate.
terelius6ed592d2016-10-18 05:55:30 -0700332 rate_control_.SetEstimate(probing_bps, info.arrival_time_ms);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200333 result.probe = true;
Stefan Holmer492ee282016-10-27 17:19:20 +0200334 result.updated =
335 UpdateEstimate(info.arrival_time_ms, now_ms, acked_bitrate_bps,
336 &result.target_bitrate_bps);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200337 }
Stefan Holmer280de9e2016-09-30 10:06:51 +0200338 if (!result.updated &&
339 (last_update_ms_ == -1 ||
terelius6ed592d2016-10-18 05:55:30 -0700340 now_ms - last_update_ms_ > rate_control_.GetFeedbackInterval())) {
Stefan Holmer492ee282016-10-27 17:19:20 +0200341 result.updated =
342 UpdateEstimate(info.arrival_time_ms, now_ms, acked_bitrate_bps,
343 &result.target_bitrate_bps);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200344 }
terelius5a388362016-12-09 05:50:01 -0800345 if (result.updated) {
stefan5ec85fb2016-09-29 04:19:38 -0700346 last_update_ms_ = now_ms;
terelius5a388362016-12-09 05:50:01 -0800347 BWE_TEST_LOGGING_PLOT(1, "target_bitrate_bps", now_ms,
348 result.target_bitrate_bps);
349 }
Stefan Holmer280de9e2016-09-30 10:06:51 +0200350
351 return result;
philipel863a8262016-06-17 09:21:34 -0700352}
353
Irfan Sheriffb2540bb2016-09-12 12:28:54 -0700354bool DelayBasedBwe::UpdateEstimate(int64_t arrival_time_ms,
355 int64_t now_ms,
Stefan Holmer492ee282016-10-27 17:19:20 +0200356 rtc::Optional<uint32_t> acked_bitrate_bps,
Irfan Sheriffb2540bb2016-09-12 12:28:54 -0700357 uint32_t* target_bitrate_bps) {
tereliusafaef8b2016-11-17 03:48:18 -0800358 // TODO(terelius): RateControlInput::noise_var is deprecated and will be
359 // removed. In the meantime, we set it to zero.
360 const RateControlInput input(detector_.State(), acked_bitrate_bps, 0);
terelius6ed592d2016-10-18 05:55:30 -0700361 rate_control_.Update(&input, now_ms);
362 *target_bitrate_bps = rate_control_.UpdateBandwidthEstimate(now_ms);
363 return rate_control_.ValidEstimate();
Irfan Sheriffb2540bb2016-09-12 12:28:54 -0700364}
365
philipel863a8262016-06-17 09:21:34 -0700366void DelayBasedBwe::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) {
terelius6ed592d2016-10-18 05:55:30 -0700367 rate_control_.SetRtt(avg_rtt_ms);
philipel863a8262016-06-17 09:21:34 -0700368}
369
philipel863a8262016-06-17 09:21:34 -0700370bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
371 uint32_t* bitrate_bps) const {
372 // Currently accessed from both the process thread (see
373 // ModuleRtpRtcpImpl::Process()) and the configuration thread (see
374 // Call::GetStats()). Should in the future only be accessed from a single
375 // thread.
376 RTC_DCHECK(ssrcs);
377 RTC_DCHECK(bitrate_bps);
terelius6ed592d2016-10-18 05:55:30 -0700378 if (!rate_control_.ValidEstimate())
philipel863a8262016-06-17 09:21:34 -0700379 return false;
philipel7522a282016-08-16 10:59:36 +0200380
381 *ssrcs = {kFixedSsrc};
terelius6ed592d2016-10-18 05:55:30 -0700382 *bitrate_bps = rate_control_.LatestEstimate();
philipel863a8262016-06-17 09:21:34 -0700383 return true;
384}
385
386void DelayBasedBwe::SetMinBitrate(int min_bitrate_bps) {
387 // Called from both the configuration thread and the network thread. Shouldn't
388 // be called from the network thread in the future.
terelius6ed592d2016-10-18 05:55:30 -0700389 rate_control_.SetMinBitrate(min_bitrate_bps);
philipel863a8262016-06-17 09:21:34 -0700390}
minyue78b4d562016-11-30 04:47:39 -0800391
392int64_t DelayBasedBwe::GetProbingIntervalMs() const {
393 return probing_interval_estimator_.GetIntervalMs();
394}
philipel863a8262016-06-17 09:21:34 -0700395} // namespace webrtc