/* * 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 // sqrt() #include "webrtc/system_wrappers/interface/trace.h" namespace webrtc { SendSideBandwidthEstimation::SendSideBandwidthEstimation() : critsect_(CriticalSectionWrapper::CreateCriticalSection()), accumulate_lost_packets_Q8_(0), accumulate_expected_packets_(0), bitrate_(0), min_bitrate_configured_(0), max_bitrate_configured_(0), last_fraction_loss_(0), last_round_trip_time_(0), bwe_incoming_(0), time_last_increase_(0), time_last_decrease_(0) { } SendSideBandwidthEstimation::~SendSideBandwidthEstimation() { delete critsect_; } void SendSideBandwidthEstimation::SetSendBitrate(const uint32_t bitrate) { CriticalSectionScoped cs(critsect_); bitrate_ = bitrate; } void SendSideBandwidthEstimation::SetMinMaxBitrate(const uint32_t min_bitrate, const uint32_t max_bitrate) { CriticalSectionScoped cs(critsect_); min_bitrate_configured_ = min_bitrate; if (max_bitrate == 0) { // no max configured use 1Gbit/s max_bitrate_configured_ = 1000000000; } else { max_bitrate_configured_ = max_bitrate; } } bool SendSideBandwidthEstimation::UpdateBandwidthEstimate( const uint32_t bandwidth, uint32_t* new_bitrate, uint8_t* fraction_lost, uint16_t* rtt) { *new_bitrate = 0; CriticalSectionScoped cs(critsect_); bwe_incoming_ = bandwidth; if (bitrate_ == 0) { // SendSideBandwidthEstimation off return false; } if (bwe_incoming_ > 0 && bitrate_ > bwe_incoming_) { bitrate_ = bwe_incoming_; *new_bitrate = bitrate_; *fraction_lost = last_fraction_loss_; *rtt = last_round_trip_time_; return true; } return false; } bool SendSideBandwidthEstimation::UpdatePacketLoss( const int number_of_packets, const uint32_t rtt, const uint32_t now_ms, uint8_t* loss, uint32_t* new_bitrate) { CriticalSectionScoped cs(critsect_); if (bitrate_ == 0) { // SendSideBandwidthEstimation off return false; } // Update RTT. last_round_trip_time_ = 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 = *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) { *loss = accumulate_lost_packets_Q8_ / accumulate_expected_packets_; // Reset accumulators accumulate_lost_packets_Q8_ = 0; accumulate_expected_packets_ = 0; } else { // Report zero loss until we have enough data to estimate // the loss rate. return false; } } // Keep for next time. last_fraction_loss_ = *loss; uint32_t bitrate = 0; if (!ShapeSimple(*loss, rtt, now_ms, &bitrate)) { // No change. return false; } bitrate_ = bitrate; *new_bitrate = bitrate; return true; } bool SendSideBandwidthEstimation::AvailableBandwidth( uint32_t* bandwidth) const { CriticalSectionScoped cs(critsect_); if (bitrate_ == 0) { return false; } *bandwidth = bitrate_; return true; } /* * Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply. * The formula in RFC 3448, Section 3.1, is used. */ uint32_t SendSideBandwidthEstimation::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 * sqrt(2 * b * p / 3) + (t_RTO * (3 * sqrt(3 * b * p / 8) * p * (1 + 32 * p * p)))); return (static_cast(X * 8)); // bits/second } bool SendSideBandwidthEstimation::ShapeSimple(const uint8_t loss, const uint32_t rtt, const uint32_t now_ms, uint32_t* bitrate) { uint32_t new_bitrate = 0; bool reducing = false; // Limit the rate increases to once a kBWEIncreaseIntervalMs. if (loss <= 5) { if ((now_ms - time_last_increase_) < kBWEIncreaseIntervalMs) { return false; } time_last_increase_ = now_ms; } // Limit the rate decreases to once a kBWEDecreaseIntervalMs + rtt. if (loss > 26) { if ((now_ms - time_last_decrease_) < kBWEDecreaseIntervalMs + rtt) { return false; } time_last_decrease_ = now_ms; } if (loss > 5 && loss <= 26) { // 2% - 10% new_bitrate = bitrate_; } else if (loss > 26) { // 26/256 ~= 10% // reduce rate: newRate = rate * (1 - 0.5*lossRate) // packetLoss = 256*lossRate new_bitrate = static_cast((bitrate_ * static_cast(512 - loss)) / 512.0); reducing = true; } else { // increase rate by 8% new_bitrate = static_cast(bitrate_ * 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) new_bitrate += 1000; } if (reducing) { // Calculate what rate TFRC would apply in this situation // scale loss to Q0 (back to [0, 255]) uint32_t tfrc_bitrate = CalcTFRCbps(rtt, loss); if (tfrc_bitrate > new_bitrate) { // do not reduce further if rate is below TFRC rate new_bitrate = tfrc_bitrate; } } if (bwe_incoming_ > 0 && new_bitrate > bwe_incoming_) { new_bitrate = bwe_incoming_; } if (new_bitrate > max_bitrate_configured_) { new_bitrate = max_bitrate_configured_; } if (new_bitrate < min_bitrate_configured_) { WEBRTC_TRACE(kTraceWarning, kTraceRtpRtcp, -1, "The configured min bitrate (%u kbps) is greater than the " "estimated available bandwidth (%u kbps).\n", min_bitrate_configured_ / 1000, new_bitrate / 1000); new_bitrate = min_bitrate_configured_; } *bitrate = new_bitrate; return true; } } // namespace webrtc