blob: 2e66e73a28a185f2218b5e42ace2952677f9bba8 [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"
terelius0baf55d2017-02-17 03:38:28 -080021#include "webrtc/logging/rtc_event_log/rtc_event_log.h"
Irfan Sheriffb2540bb2016-09-12 12:28:54 -070022#include "webrtc/modules/congestion_controller/include/congestion_controller.h"
philipel863a8262016-06-17 09:21:34 -070023#include "webrtc/modules/pacing/paced_sender.h"
24#include "webrtc/modules/remote_bitrate_estimator/include/remote_bitrate_estimator.h"
terelius5a388362016-12-09 05:50:01 -080025#include "webrtc/modules/remote_bitrate_estimator/test/bwe_test_logging.h"
Stefan Holmer492ee282016-10-27 17:19:20 +020026#include "webrtc/system_wrappers/include/field_trial.h"
stefan64636dd2016-08-03 00:29:03 -070027#include "webrtc/system_wrappers/include/metrics.h"
philipel863a8262016-06-17 09:21:34 -070028#include "webrtc/typedefs.h"
29
30namespace {
philipel7522a282016-08-16 10:59:36 +020031constexpr int kTimestampGroupLengthMs = 5;
32constexpr int kAbsSendTimeFraction = 18;
33constexpr int kAbsSendTimeInterArrivalUpshift = 8;
34constexpr int kInterArrivalShift =
35 kAbsSendTimeFraction + kAbsSendTimeInterArrivalUpshift;
36constexpr double kTimestampToMs =
philipel863a8262016-06-17 09:21:34 -070037 1000.0 / static_cast<double>(1 << kInterArrivalShift);
philipel7522a282016-08-16 10:59:36 +020038// This ssrc is used to fulfill the current API but will be removed
39// after the API has been changed.
40constexpr uint32_t kFixedSsrc = 0;
Stefan Holmer492ee282016-10-27 17:19:20 +020041constexpr int kInitialRateWindowMs = 500;
42constexpr int kRateWindowMs = 150;
43
terelius5a388362016-12-09 05:50:01 -080044// Parameters for linear least squares fit of regression line to noisy data.
stefan76d9c9c2017-04-01 06:51:09 -070045constexpr size_t kDefaultTrendlineWindowSize = 20;
tereliusafaef8b2016-11-17 03:48:18 -080046constexpr double kDefaultTrendlineSmoothingCoeff = 0.9;
47constexpr double kDefaultTrendlineThresholdGain = 4.0;
48
stefane3a55672017-02-13 09:08:22 -080049constexpr int kMaxConsecutiveFailedLookups = 5;
50
tereliusbf2c0492017-05-02 01:04:26 -070051const char kBweSparseUpdateExperiment[] = "WebRTC-BweSparseUpdateExperiment";
52
53bool BweSparseUpdateExperimentIsEnabled() {
54 std::string experiment_string =
55 webrtc::field_trial::FindFullName(kBweSparseUpdateExperiment);
56 return experiment_string == "Enabled";
57}
elad.alonec304f92017-03-08 05:03:53 -080058
59class PacketFeedbackComparator {
60 public:
61 inline bool operator()(const webrtc::PacketFeedback& lhs,
62 const webrtc::PacketFeedback& rhs) {
63 if (lhs.arrival_time_ms != rhs.arrival_time_ms)
64 return lhs.arrival_time_ms < rhs.arrival_time_ms;
65 if (lhs.send_time_ms != rhs.send_time_ms)
66 return lhs.send_time_ms < rhs.send_time_ms;
67 return lhs.sequence_number < rhs.sequence_number;
68 }
69};
70
71void SortPacketFeedbackVector(const std::vector<webrtc::PacketFeedback>& input,
72 std::vector<webrtc::PacketFeedback>* output) {
73 auto pred = [](const webrtc::PacketFeedback& packet_feedback) {
74 return packet_feedback.arrival_time_ms !=
75 webrtc::PacketFeedback::kNotReceived;
76 };
77 std::copy_if(input.begin(), input.end(), std::back_inserter(*output), pred);
78 std::sort(output->begin(), output->end(), PacketFeedbackComparator());
79}
philipel863a8262016-06-17 09:21:34 -070080} // namespace
81
82namespace webrtc {
tereliusafaef8b2016-11-17 03:48:18 -080083
Stefan Holmer492ee282016-10-27 17:19:20 +020084DelayBasedBwe::BitrateEstimator::BitrateEstimator()
85 : sum_(0),
86 current_win_ms_(0),
87 prev_time_ms_(-1),
88 bitrate_estimate_(-1.0f),
stefan76d9c9c2017-04-01 06:51:09 -070089 bitrate_estimate_var_(50.0f) {}
Stefan Holmer492ee282016-10-27 17:19:20 +020090
91void DelayBasedBwe::BitrateEstimator::Update(int64_t now_ms, int bytes) {
Stefan Holmer492ee282016-10-27 17:19:20 +020092 int rate_window_ms = kRateWindowMs;
93 // We use a larger window at the beginning to get a more stable sample that
94 // we can use to initialize the estimate.
95 if (bitrate_estimate_ < 0.f)
96 rate_window_ms = kInitialRateWindowMs;
97 float bitrate_sample = UpdateWindow(now_ms, bytes, rate_window_ms);
98 if (bitrate_sample < 0.0f)
99 return;
100 if (bitrate_estimate_ < 0.0f) {
101 // This is the very first sample we get. Use it to initialize the estimate.
102 bitrate_estimate_ = bitrate_sample;
103 return;
104 }
105 // Define the sample uncertainty as a function of how far away it is from the
106 // current estimate.
107 float sample_uncertainty =
108 10.0f * std::abs(bitrate_estimate_ - bitrate_sample) / bitrate_estimate_;
109 float sample_var = sample_uncertainty * sample_uncertainty;
110 // Update a bayesian estimate of the rate, weighting it lower if the sample
111 // uncertainty is large.
112 // The bitrate estimate uncertainty is increased with each update to model
113 // that the bitrate changes over time.
114 float pred_bitrate_estimate_var = bitrate_estimate_var_ + 5.f;
115 bitrate_estimate_ = (sample_var * bitrate_estimate_ +
116 pred_bitrate_estimate_var * bitrate_sample) /
117 (sample_var + pred_bitrate_estimate_var);
118 bitrate_estimate_var_ = sample_var * pred_bitrate_estimate_var /
119 (sample_var + pred_bitrate_estimate_var);
120}
121
122float DelayBasedBwe::BitrateEstimator::UpdateWindow(int64_t now_ms,
123 int bytes,
124 int rate_window_ms) {
125 // Reset if time moves backwards.
126 if (now_ms < prev_time_ms_) {
127 prev_time_ms_ = -1;
128 sum_ = 0;
129 current_win_ms_ = 0;
130 }
131 if (prev_time_ms_ >= 0) {
132 current_win_ms_ += now_ms - prev_time_ms_;
133 // Reset if nothing has been received for more than a full window.
134 if (now_ms - prev_time_ms_ > rate_window_ms) {
135 sum_ = 0;
136 current_win_ms_ %= rate_window_ms;
137 }
138 }
139 prev_time_ms_ = now_ms;
140 float bitrate_sample = -1.0f;
141 if (current_win_ms_ >= rate_window_ms) {
142 bitrate_sample = 8.0f * sum_ / static_cast<float>(rate_window_ms);
143 current_win_ms_ -= rate_window_ms;
144 sum_ = 0;
145 }
146 sum_ += bytes;
147 return bitrate_sample;
148}
149
150rtc::Optional<uint32_t> DelayBasedBwe::BitrateEstimator::bitrate_bps() const {
151 if (bitrate_estimate_ < 0.f)
152 return rtc::Optional<uint32_t>();
153 return rtc::Optional<uint32_t>(bitrate_estimate_ * 1000);
154}
philipel863a8262016-06-17 09:21:34 -0700155
elad.alon61ce37e2017-03-09 07:09:31 -0800156DelayBasedBwe::DelayBasedBwe(RtcEventLog* event_log, const Clock* clock)
stefan76d9c9c2017-04-01 06:51:09 -0700157 : event_log_(event_log),
terelius5a388362016-12-09 05:50:01 -0800158 clock_(clock),
philipel863a8262016-06-17 09:21:34 -0700159 inter_arrival_(),
tereliusafaef8b2016-11-17 03:48:18 -0800160 trendline_estimator_(),
terelius84f83f82016-12-27 10:43:01 -0800161 detector_(),
Stefan Holmer492ee282016-10-27 17:19:20 +0200162 receiver_incoming_bitrate_(),
philipel7522a282016-08-16 10:59:36 +0200163 last_seen_packet_ms_(-1),
tereliusafaef8b2016-11-17 03:48:18 -0800164 uma_recorded_(false),
philipelf4238f92017-03-28 04:18:02 -0700165 probe_bitrate_estimator_(event_log),
tereliusafaef8b2016-11-17 03:48:18 -0800166 trendline_window_size_(kDefaultTrendlineWindowSize),
167 trendline_smoothing_coeff_(kDefaultTrendlineSmoothingCoeff),
168 trendline_threshold_gain_(kDefaultTrendlineThresholdGain),
terelius0baf55d2017-02-17 03:38:28 -0800169 consecutive_delayed_feedbacks_(0),
170 last_logged_bitrate_(0),
tereliusbf2c0492017-05-02 01:04:26 -0700171 last_logged_state_(BandwidthUsage::kBwNormal),
172 in_sparse_update_experiment_(BweSparseUpdateExperimentIsEnabled()) {
stefan76d9c9c2017-04-01 06:51:09 -0700173 LOG(LS_INFO) << "Using Trendline filter for delay change estimation.";
philipel863a8262016-06-17 09:21:34 -0700174 network_thread_.DetachFromThread();
175}
176
Stefan Holmer280de9e2016-09-30 10:06:51 +0200177DelayBasedBwe::Result DelayBasedBwe::IncomingPacketFeedbackVector(
elad.alonf9490002017-03-06 05:32:21 -0800178 const std::vector<PacketFeedback>& packet_feedback_vector) {
philipel863a8262016-06-17 09:21:34 -0700179 RTC_DCHECK(network_thread_.CalledOnValidThread());
elad.alonec304f92017-03-08 05:03:53 -0800180
181 std::vector<PacketFeedback> sorted_packet_feedback_vector;
182 SortPacketFeedbackVector(packet_feedback_vector,
183 &sorted_packet_feedback_vector);
stefan80fba252017-04-18 06:45:12 -0700184 // TOOD(holmer): An empty feedback vector here likely means that
185 // all acks were too late and that the send time history had
186 // timed out. We should reduce the rate when this occurs.
187 if (sorted_packet_feedback_vector.empty()) {
188 LOG(LS_WARNING) << "Very late feedback received.";
189 return DelayBasedBwe::Result();
190 }
elad.alonec304f92017-03-08 05:03:53 -0800191
stefan64636dd2016-08-03 00:29:03 -0700192 if (!uma_recorded_) {
asapersson1d02d3e2016-09-09 22:40:25 -0700193 RTC_HISTOGRAM_ENUMERATION(kBweTypeHistogram,
194 BweNames::kSendSideTransportSeqNum,
195 BweNames::kBweNamesMax);
stefan64636dd2016-08-03 00:29:03 -0700196 uma_recorded_ = true;
197 }
michaelt8490f8a2017-04-20 10:10:10 -0700198 bool overusing = false;
stefane3a55672017-02-13 09:08:22 -0800199 bool delayed_feedback = true;
elad.alonec304f92017-03-08 05:03:53 -0800200 for (const auto& packet_feedback : sorted_packet_feedback_vector) {
elad.alonf9490002017-03-06 05:32:21 -0800201 if (packet_feedback.send_time_ms < 0)
stefane3a55672017-02-13 09:08:22 -0800202 continue;
203 delayed_feedback = false;
michaelt8490f8a2017-04-20 10:10:10 -0700204 IncomingPacketFeedback(packet_feedback);
tereliusbf2c0492017-05-02 01:04:26 -0700205 if (!in_sparse_update_experiment_)
206 overusing |= (detector_.State() == BandwidthUsage::kBwOverusing);
philipel863a8262016-06-17 09:21:34 -0700207 }
tereliusbf2c0492017-05-02 01:04:26 -0700208 if (in_sparse_update_experiment_)
209 overusing = (detector_.State() == BandwidthUsage::kBwOverusing);
stefane3a55672017-02-13 09:08:22 -0800210 if (delayed_feedback) {
211 ++consecutive_delayed_feedbacks_;
michaelt8490f8a2017-04-20 10:10:10 -0700212 if (consecutive_delayed_feedbacks_ >= kMaxConsecutiveFailedLookups) {
213 consecutive_delayed_feedbacks_ = 0;
214 return OnLongFeedbackDelay(
215 sorted_packet_feedback_vector.back().arrival_time_ms);
216 }
stefane3a55672017-02-13 09:08:22 -0800217 } else {
218 consecutive_delayed_feedbacks_ = 0;
michaelt8490f8a2017-04-20 10:10:10 -0700219 return MaybeUpdateEstimate(overusing);
stefane3a55672017-02-13 09:08:22 -0800220 }
michaelt8490f8a2017-04-20 10:10:10 -0700221 return Result();
philipel863a8262016-06-17 09:21:34 -0700222}
223
stefane3a55672017-02-13 09:08:22 -0800224DelayBasedBwe::Result DelayBasedBwe::OnLongFeedbackDelay(
225 int64_t arrival_time_ms) {
226 // Estimate should always be valid since a start bitrate always is set in the
227 // Call constructor. An alternative would be to return an empty Result here,
228 // or to estimate the throughput based on the feedback we received.
229 RTC_DCHECK(rate_control_.ValidEstimate());
230 rate_control_.SetEstimate(rate_control_.LatestEstimate() / 2,
231 arrival_time_ms);
232 Result result;
233 result.updated = true;
234 result.probe = false;
235 result.target_bitrate_bps = rate_control_.LatestEstimate();
236 LOG(LS_WARNING) << "Long feedback delay detected, reducing BWE to "
237 << result.target_bitrate_bps;
238 return result;
239}
240
michaelt8490f8a2017-04-20 10:10:10 -0700241void DelayBasedBwe::IncomingPacketFeedback(
elad.alonf9490002017-03-06 05:32:21 -0800242 const PacketFeedback& packet_feedback) {
stefan5e12d362016-07-11 01:44:02 -0700243 int64_t now_ms = clock_->TimeInMilliseconds();
philipel863a8262016-06-17 09:21:34 -0700244
elad.alonf9490002017-03-06 05:32:21 -0800245 receiver_incoming_bitrate_.Update(packet_feedback.arrival_time_ms,
246 packet_feedback.payload_size);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200247 Result result;
248 // Reset if the stream has timed out.
249 if (last_seen_packet_ms_ == -1 ||
250 now_ms - last_seen_packet_ms_ > kStreamTimeOutMs) {
251 inter_arrival_.reset(
252 new InterArrival((kTimestampGroupLengthMs << kInterArrivalShift) / 1000,
253 kTimestampToMs, true));
tereliusafaef8b2016-11-17 03:48:18 -0800254 trendline_estimator_.reset(new TrendlineEstimator(
255 trendline_window_size_, trendline_smoothing_coeff_,
256 trendline_threshold_gain_));
Stefan Holmer280de9e2016-09-30 10:06:51 +0200257 }
258 last_seen_packet_ms_ = now_ms;
philipel863a8262016-06-17 09:21:34 -0700259
Stefan Holmer280de9e2016-09-30 10:06:51 +0200260 uint32_t send_time_24bits =
261 static_cast<uint32_t>(
elad.alonf9490002017-03-06 05:32:21 -0800262 ((static_cast<uint64_t>(packet_feedback.send_time_ms)
263 << kAbsSendTimeFraction) +
Stefan Holmer280de9e2016-09-30 10:06:51 +0200264 500) /
265 1000) &
266 0x00FFFFFF;
267 // Shift up send time to use the full 32 bits that inter_arrival works with,
268 // so wrapping works properly.
269 uint32_t timestamp = send_time_24bits << kAbsSendTimeInterArrivalUpshift;
stefanfd0d4262016-09-29 02:44:31 -0700270
Stefan Holmer280de9e2016-09-30 10:06:51 +0200271 uint32_t ts_delta = 0;
272 int64_t t_delta = 0;
273 int size_delta = 0;
elad.alonf9490002017-03-06 05:32:21 -0800274 if (inter_arrival_->ComputeDeltas(timestamp, packet_feedback.arrival_time_ms,
275 now_ms, packet_feedback.payload_size,
276 &ts_delta, &t_delta, &size_delta)) {
Stefan Holmer280de9e2016-09-30 10:06:51 +0200277 double ts_delta_ms = (1000.0 * ts_delta) / (1 << kInterArrivalShift);
stefan76d9c9c2017-04-01 06:51:09 -0700278 trendline_estimator_->Update(t_delta, ts_delta_ms,
279 packet_feedback.arrival_time_ms);
280 detector_.Detect(trendline_estimator_->trendline_slope(), ts_delta_ms,
281 trendline_estimator_->num_of_deltas(),
282 packet_feedback.arrival_time_ms);
stefan5ec85fb2016-09-29 04:19:38 -0700283 }
elad.alonf9490002017-03-06 05:32:21 -0800284 if (packet_feedback.pacing_info.probe_cluster_id !=
285 PacedPacketInfo::kNotAProbe) {
michaelt8490f8a2017-04-20 10:10:10 -0700286 probe_bitrate_estimator_.HandleProbeAndEstimateBitrate(packet_feedback);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200287 }
michaelt8490f8a2017-04-20 10:10:10 -0700288}
289
290DelayBasedBwe::Result DelayBasedBwe::MaybeUpdateEstimate(bool overusing) {
291 Result result;
292 int64_t now_ms = clock_->TimeInMilliseconds();
293
Stefan Holmer492ee282016-10-27 17:19:20 +0200294 rtc::Optional<uint32_t> acked_bitrate_bps =
295 receiver_incoming_bitrate_.bitrate_bps();
michaelt8490f8a2017-04-20 10:10:10 -0700296 rtc::Optional<int> probe_bitrate_bps =
297 probe_bitrate_estimator_.FetchAndResetLastEstimatedBitrateBps();
Stefan Holmer280de9e2016-09-30 10:06:51 +0200298 // Currently overusing the bandwidth.
michaelt8490f8a2017-04-20 10:10:10 -0700299 if (overusing) {
Stefan Holmer492ee282016-10-27 17:19:20 +0200300 if (acked_bitrate_bps &&
301 rate_control_.TimeToReduceFurther(now_ms, *acked_bitrate_bps)) {
michaelt8490f8a2017-04-20 10:10:10 -0700302 result.updated = UpdateEstimate(now_ms, acked_bitrate_bps, overusing,
303 &result.target_bitrate_bps);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200304 }
michaelt8490f8a2017-04-20 10:10:10 -0700305 } else {
306 if (probe_bitrate_bps) {
307 rate_control_.SetEstimate(*probe_bitrate_bps, now_ms);
308 result.probe = true;
309 }
310 result.updated = UpdateEstimate(now_ms, acked_bitrate_bps, overusing,
311 &result.target_bitrate_bps);
Stefan Holmer280de9e2016-09-30 10:06:51 +0200312 }
terelius5a388362016-12-09 05:50:01 -0800313 if (result.updated) {
terelius5a388362016-12-09 05:50:01 -0800314 BWE_TEST_LOGGING_PLOT(1, "target_bitrate_bps", now_ms,
315 result.target_bitrate_bps);
terelius0baf55d2017-02-17 03:38:28 -0800316 if (event_log_ && (result.target_bitrate_bps != last_logged_bitrate_ ||
317 detector_.State() != last_logged_state_)) {
terelius424e6cf2017-02-20 05:14:41 -0800318 event_log_->LogDelayBasedBweUpdate(result.target_bitrate_bps,
terelius0baf55d2017-02-17 03:38:28 -0800319 detector_.State());
320 last_logged_bitrate_ = result.target_bitrate_bps;
321 last_logged_state_ = detector_.State();
322 }
terelius5a388362016-12-09 05:50:01 -0800323 }
Stefan Holmer280de9e2016-09-30 10:06:51 +0200324 return result;
philipel863a8262016-06-17 09:21:34 -0700325}
326
michaelt8490f8a2017-04-20 10:10:10 -0700327bool DelayBasedBwe::UpdateEstimate(int64_t now_ms,
Stefan Holmer492ee282016-10-27 17:19:20 +0200328 rtc::Optional<uint32_t> acked_bitrate_bps,
michaelt8490f8a2017-04-20 10:10:10 -0700329 bool overusing,
Irfan Sheriffb2540bb2016-09-12 12:28:54 -0700330 uint32_t* target_bitrate_bps) {
tereliusafaef8b2016-11-17 03:48:18 -0800331 // TODO(terelius): RateControlInput::noise_var is deprecated and will be
332 // removed. In the meantime, we set it to zero.
michaelt8490f8a2017-04-20 10:10:10 -0700333 const RateControlInput input(
334 overusing ? BandwidthUsage::kBwOverusing : detector_.State(),
335 acked_bitrate_bps, 0);
tereliusd1b0e0e2017-04-03 02:27:08 -0700336 *target_bitrate_bps = rate_control_.Update(&input, now_ms);
terelius6ed592d2016-10-18 05:55:30 -0700337 return rate_control_.ValidEstimate();
Irfan Sheriffb2540bb2016-09-12 12:28:54 -0700338}
339
philipel863a8262016-06-17 09:21:34 -0700340void DelayBasedBwe::OnRttUpdate(int64_t avg_rtt_ms, int64_t max_rtt_ms) {
terelius6ed592d2016-10-18 05:55:30 -0700341 rate_control_.SetRtt(avg_rtt_ms);
philipel863a8262016-06-17 09:21:34 -0700342}
343
philipel863a8262016-06-17 09:21:34 -0700344bool DelayBasedBwe::LatestEstimate(std::vector<uint32_t>* ssrcs,
345 uint32_t* bitrate_bps) const {
346 // Currently accessed from both the process thread (see
347 // ModuleRtpRtcpImpl::Process()) and the configuration thread (see
348 // Call::GetStats()). Should in the future only be accessed from a single
349 // thread.
350 RTC_DCHECK(ssrcs);
351 RTC_DCHECK(bitrate_bps);
terelius6ed592d2016-10-18 05:55:30 -0700352 if (!rate_control_.ValidEstimate())
philipel863a8262016-06-17 09:21:34 -0700353 return false;
philipel7522a282016-08-16 10:59:36 +0200354
355 *ssrcs = {kFixedSsrc};
terelius6ed592d2016-10-18 05:55:30 -0700356 *bitrate_bps = rate_control_.LatestEstimate();
philipel863a8262016-06-17 09:21:34 -0700357 return true;
358}
359
stefan5a2c5062017-01-27 06:43:18 -0800360void DelayBasedBwe::SetStartBitrate(int start_bitrate_bps) {
361 LOG(LS_WARNING) << "BWE Setting start bitrate to: " << start_bitrate_bps;
362 rate_control_.SetStartBitrate(start_bitrate_bps);
363}
364
philipel863a8262016-06-17 09:21:34 -0700365void DelayBasedBwe::SetMinBitrate(int min_bitrate_bps) {
366 // Called from both the configuration thread and the network thread. Shouldn't
367 // be called from the network thread in the future.
terelius6ed592d2016-10-18 05:55:30 -0700368 rate_control_.SetMinBitrate(min_bitrate_bps);
philipel863a8262016-06-17 09:21:34 -0700369}
minyue78b4d562016-11-30 04:47:39 -0800370
terelius67370452017-04-19 09:15:04 -0700371int64_t DelayBasedBwe::GetExpectedBwePeriodMs() const {
372 return rate_control_.GetExpectedBandwidthPeriodMs();
minyue78b4d562016-11-30 04:47:39 -0800373}
philipel863a8262016-06-17 09:21:34 -0700374} // namespace webrtc