| /* |
| * 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 <cmath> |
| |
| #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<double>(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<double>(loss) / 255; // Packet loss rate in [0, 1). |
| double s = static_cast<double>(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<uint32_t>(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<uint32_t>( |
| 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<uint32_t>(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<uint32_t>( |
| (bitrate_ * static_cast<double>(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 |