only whenever a RTCP receiver block is received. Additionally: Add condition to only start rampup after a receiver block is received. This was same as old behaviour but now an explicit check is needed to verify process does not ramps up before the first block. Fix logic around capping max bitrate increase at 8% per second. Before it was only increasing once every 1 second and each increase would be as high as 8%. If receiver blocks had a different interval before it would lose an update or waste an update slot and not ramp up as much as a 8% (e.g. if RTCP received < 1 second). Did not touch decrease logic, however since it can be triggered more often it may decrease much faster and closer to the original written cap of once every 300ms + rtt. Note: rampup_tests.cc don't seem to be affected by this since there is no packet loss or REMB that go higher than expected cap. bitrate_controller_unittests.cc are don't really simulate a clock and the process thread, but trigger update by inserting an rtcp block. BUG=3065 R=stefan@webrtc.org, mflodman@webrtc.org Review URL: https://webrtc-codereview.appspot.com/10529004 git-svn-id: http://webrtc.googlecode.com/svn/trunk@5775 4adac7df-926f-26a2-2b94-8c16560cd09d
218 lines
7.6 KiB
C++
218 lines
7.6 KiB
C++
/*
|
|
* 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/trace.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_) {
|
|
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,
|
|
bitrate_ / 1000);
|
|
bitrate_ = min_bitrate_configured_;
|
|
}
|
|
}
|
|
|
|
} // namespace webrtc
|