/* * Copyright (c) 2012 The WebRTC project authors. All Rights Reserved. * * Use of this source code is governed by a BSD-style license * that can be found in the LICENSE file in the root of the source * tree. An additional intellectual property rights grant can be found * in the file PATENTS. All contributing project authors may * be found in the AUTHORS file in the root of the source tree. */ #include "webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.h" #include #include "webrtc/system_wrappers/interface/logging.h" namespace webrtc { namespace { enum { kBweIncreaseIntervalMs = 1000 }; enum { kBweDecreaseIntervalMs = 300 }; enum { kLimitNumPackets = 20 }; enum { kAvgPacketSizeBytes = 1000 }; // Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply. // The formula in RFC 3448, Section 3.1, is used. uint32_t CalcTfrcBps(uint16_t rtt, uint8_t loss) { if (rtt == 0 || loss == 0) { // Input variables out of range. return 0; } double R = static_cast(rtt) / 1000; // RTT in seconds. int b = 1; // Number of packets acknowledged by a single TCP acknowledgement: // recommended = 1. double t_RTO = 4.0 * R; // TCP retransmission timeout value in seconds // recommended = 4*R. double p = static_cast(loss) / 255; // Packet loss rate in [0, 1). double s = static_cast(kAvgPacketSizeBytes); // Calculate send rate in bytes/second. double X = s / (R * std::sqrt(2 * b * p / 3) + (t_RTO * (3 * std::sqrt(3 * b * p / 8) * p * (1 + 32 * p * p)))); // Convert to bits/second. return (static_cast(X * 8)); } } SendSideBandwidthEstimation::SendSideBandwidthEstimation() : accumulate_lost_packets_Q8_(0), accumulate_expected_packets_(0), bitrate_(0), min_bitrate_configured_(0), max_bitrate_configured_(0), time_last_receiver_block_ms_(0), last_fraction_loss_(0), last_round_trip_time_ms_(0), bwe_incoming_(0), time_last_decrease_ms_(0) {} SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {} void SendSideBandwidthEstimation::SetSendBitrate(uint32_t bitrate) { bitrate_ = bitrate; // Clear last sent bitrate history so the new value can be used directly // and not capped. min_bitrate_history_.clear(); } void SendSideBandwidthEstimation::SetMinMaxBitrate(uint32_t min_bitrate, uint32_t max_bitrate) { min_bitrate_configured_ = min_bitrate; max_bitrate_configured_ = max_bitrate; } void SendSideBandwidthEstimation::SetMinBitrate(uint32_t min_bitrate) { min_bitrate_configured_ = min_bitrate; } void SendSideBandwidthEstimation::CurrentEstimate(uint32_t* bitrate, uint8_t* loss, uint32_t* rtt) const { *bitrate = bitrate_; *loss = last_fraction_loss_; *rtt = last_round_trip_time_ms_; } void SendSideBandwidthEstimation::UpdateReceiverEstimate(uint32_t bandwidth) { bwe_incoming_ = bandwidth; CapBitrateToThresholds(); } void SendSideBandwidthEstimation::UpdateReceiverBlock(uint8_t fraction_loss, uint32_t rtt, int number_of_packets, uint32_t now_ms) { // Update RTT. last_round_trip_time_ms_ = rtt; // Check sequence number diff and weight loss report if (number_of_packets > 0) { // Calculate number of lost packets. const int num_lost_packets_Q8 = fraction_loss * number_of_packets; // Accumulate reports. accumulate_lost_packets_Q8_ += num_lost_packets_Q8; accumulate_expected_packets_ += number_of_packets; // Report loss if the total report is based on sufficiently many packets. if (accumulate_expected_packets_ >= kLimitNumPackets) { last_fraction_loss_ = accumulate_lost_packets_Q8_ / accumulate_expected_packets_; // Reset accumulators. accumulate_lost_packets_Q8_ = 0; accumulate_expected_packets_ = 0; } else { // Early return without updating estimate. return; } } time_last_receiver_block_ms_ = now_ms; UpdateEstimate(now_ms); } void SendSideBandwidthEstimation::UpdateEstimate(uint32_t now_ms) { UpdateMinHistory(now_ms); // Only start updating bitrate when receiving receiver blocks. if (time_last_receiver_block_ms_ != 0) { if (last_fraction_loss_ <= 5) { // Loss < 2%: Increase rate by 8% of the min bitrate in the last // kBweIncreaseIntervalMs. // Note that by remembering the bitrate over the last second one can // rampup up one second faster than if only allowed to start ramping // at 8% per second rate now. E.g.: // If sending a constant 100kbps it can rampup immediatly to 108kbps // whenever a receiver report is received with lower packet loss. // If instead one would do: bitrate_ *= 1.08^(delta time), it would // take over one second since the lower packet loss to achieve 108kbps. bitrate_ = static_cast( min_bitrate_history_.front().second * 1.08 + 0.5); // Add 1 kbps extra, just to make sure that we do not get stuck // (gives a little extra increase at low rates, negligible at higher // rates). bitrate_ += 1000; } else if (last_fraction_loss_ <= 26) { // Loss between 2% - 10%: Do nothing. } else { // Loss > 10%: Limit the rate decreases to once a kBweDecreaseIntervalMs + // rtt. if ((now_ms - time_last_decrease_ms_) >= static_cast(kBweDecreaseIntervalMs + last_round_trip_time_ms_)) { time_last_decrease_ms_ = now_ms; // Reduce rate: // newRate = rate * (1 - 0.5*lossRate); // where packetLoss = 256*lossRate; bitrate_ = static_cast( (bitrate_ * static_cast(512 - last_fraction_loss_)) / 512.0); // Calculate what rate TFRC would apply in this situation and to not // reduce further than it. bitrate_ = std::max( bitrate_, CalcTfrcBps(last_round_trip_time_ms_, last_fraction_loss_)); } } } CapBitrateToThresholds(); } void SendSideBandwidthEstimation::UpdateMinHistory(uint32_t now_ms) { // Remove old data points from history. // Since history precision is in ms, add one so it is able to increase // bitrate if it is off by as little as 0.5ms. while (!min_bitrate_history_.empty() && now_ms - min_bitrate_history_.front().first + 1 > kBweIncreaseIntervalMs) { min_bitrate_history_.pop_front(); } // Typical minimum sliding-window algorithm: Pop values higher than current // bitrate before pushing it. while (!min_bitrate_history_.empty() && bitrate_ <= min_bitrate_history_.back().second) { min_bitrate_history_.pop_back(); } min_bitrate_history_.push_back(std::make_pair(now_ms, bitrate_)); } void SendSideBandwidthEstimation::CapBitrateToThresholds() { if (bwe_incoming_ > 0 && bitrate_ > bwe_incoming_) { bitrate_ = bwe_incoming_; } if (bitrate_ > max_bitrate_configured_) { bitrate_ = max_bitrate_configured_; } if (bitrate_ < min_bitrate_configured_) { LOG(LS_WARNING) << "Estimated available bandwidth " << bitrate_ / 1000 << " kbps is below configured min bitrate " << min_bitrate_configured_ / 1000 << " kbps."; bitrate_ = min_bitrate_configured_; } } } // namespace webrtc