Files
platform-external-webrtc/webrtc/modules/bitrate_controller/send_side_bandwidth_estimation.cc
Stefan Holmer e590416722 Moving the pacer and the pacer thread to ChannelGroup.
This means all channels within the same group will share the same pacing queue and scheduler. It also means padding will be computed and sent by a single pacer. To accomplish this I also introduce a PacketRouter which finds the RTP module which owns the packet to be paced out.

BUG=4323
R=mflodman@webrtc.org, pbos@webrtc.org

Review URL: https://webrtc-codereview.appspot.com/45549004

Cr-Commit-Position: refs/heads/master@{#8864}
2015-03-26 10:11:22 +00:00

298 lines
11 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/base/checks.h"
#include "webrtc/system_wrappers/interface/field_trial.h"
#include "webrtc/system_wrappers/interface/logging.h"
#include "webrtc/system_wrappers/interface/metrics.h"
namespace webrtc {
namespace {
const int64_t kBweIncreaseIntervalMs = 1000;
const int64_t kBweDecreaseIntervalMs = 300;
const int64_t kStartPhaseMs = 2000;
const int64_t kBweConverganceTimeMs = 20000;
const int kLimitNumPackets = 20;
const int kAvgPacketSizeBytes = 1000;
const int kDefaultMinBitrateBps = 10000;
const int kDefaultMaxBitrateBps = 1000000000;
struct UmaRampUpMetric {
const char* metric_name;
int bitrate_kbps;
};
const UmaRampUpMetric kUmaRampupMetrics[] = {
{"WebRTC.BWE.RampUpTimeTo500kbpsInMs", 500},
{"WebRTC.BWE.RampUpTimeTo1000kbpsInMs", 1000},
{"WebRTC.BWE.RampUpTimeTo2000kbpsInMs", 2000}};
const size_t kNumUmaRampupMetrics =
sizeof(kUmaRampupMetrics) / sizeof(kUmaRampupMetrics[0]);
// Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply.
// The formula in RFC 3448, Section 3.1, is used.
uint32_t CalcTfrcBps(int64_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_(kDefaultMinBitrateBps),
max_bitrate_configured_(kDefaultMaxBitrateBps),
time_last_receiver_block_ms_(0),
last_fraction_loss_(0),
last_round_trip_time_ms_(0),
bwe_incoming_(0),
time_last_decrease_ms_(0),
first_report_time_ms_(-1),
initially_lost_packets_(0),
bitrate_at_2_seconds_kbps_(0),
uma_update_state_(kNoUpdate),
rampup_uma_stats_updated_(kNumUmaRampupMetrics, false) {
}
SendSideBandwidthEstimation::~SendSideBandwidthEstimation() {}
void SendSideBandwidthEstimation::SetSendBitrate(int bitrate) {
DCHECK_GT(bitrate, 0);
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(int min_bitrate,
int max_bitrate) {
DCHECK_GE(min_bitrate, 0);
min_bitrate_configured_ = std::max(min_bitrate, kDefaultMinBitrateBps);
if (max_bitrate > 0) {
max_bitrate_configured_ =
std::max<uint32_t>(min_bitrate_configured_, max_bitrate);
} else {
max_bitrate_configured_ = kDefaultMaxBitrateBps;
}
}
int SendSideBandwidthEstimation::GetMinBitrate() const {
return min_bitrate_configured_;
}
void SendSideBandwidthEstimation::CurrentEstimate(int* bitrate,
uint8_t* loss,
int64_t* rtt) const {
*bitrate = bitrate_;
*loss = last_fraction_loss_;
*rtt = last_round_trip_time_ms_;
}
void SendSideBandwidthEstimation::UpdateReceiverEstimate(uint32_t bandwidth) {
bwe_incoming_ = bandwidth;
bitrate_ = CapBitrateToThresholds(bitrate_);
}
void SendSideBandwidthEstimation::UpdateReceiverBlock(uint8_t fraction_loss,
int64_t rtt,
int number_of_packets,
int64_t now_ms) {
if (first_report_time_ms_ == -1)
first_report_time_ms_ = 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);
UpdateUmaStats(now_ms, rtt, (fraction_loss * number_of_packets) >> 8);
}
void SendSideBandwidthEstimation::UpdateUmaStats(int64_t now_ms,
int64_t rtt,
int lost_packets) {
int bitrate_kbps = static_cast<int>((bitrate_ + 500) / 1000);
for (size_t i = 0; i < kNumUmaRampupMetrics; ++i) {
if (!rampup_uma_stats_updated_[i] &&
bitrate_kbps >= kUmaRampupMetrics[i].bitrate_kbps) {
RTC_HISTOGRAM_COUNTS_100000(kUmaRampupMetrics[i].metric_name,
now_ms - first_report_time_ms_);
rampup_uma_stats_updated_[i] = true;
}
}
if (IsInStartPhase(now_ms)) {
initially_lost_packets_ += lost_packets;
} else if (uma_update_state_ == kNoUpdate) {
uma_update_state_ = kFirstDone;
bitrate_at_2_seconds_kbps_ = bitrate_kbps;
RTC_HISTOGRAM_COUNTS(
"WebRTC.BWE.InitiallyLostPackets", initially_lost_packets_, 0, 100, 50);
RTC_HISTOGRAM_COUNTS(
"WebRTC.BWE.InitialRtt", static_cast<int>(rtt), 0, 2000, 50);
RTC_HISTOGRAM_COUNTS("WebRTC.BWE.InitialBandwidthEstimate",
bitrate_at_2_seconds_kbps_,
0,
2000,
50);
} else if (uma_update_state_ == kFirstDone &&
now_ms - first_report_time_ms_ >= kBweConverganceTimeMs) {
uma_update_state_ = kDone;
int bitrate_diff_kbps =
std::max(bitrate_at_2_seconds_kbps_ - bitrate_kbps, 0);
RTC_HISTOGRAM_COUNTS(
"WebRTC.BWE.InitialVsConvergedDiff", bitrate_diff_kbps, 0, 2000, 50);
}
}
void SendSideBandwidthEstimation::UpdateEstimate(int64_t now_ms) {
// We trust the REMB during the first 2 seconds if we haven't had any
// packet loss reported, to allow startup bitrate probing.
if (last_fraction_loss_ == 0 && IsInStartPhase(now_ms) &&
bwe_incoming_ > bitrate_) {
bitrate_ = CapBitrateToThresholds(bwe_incoming_);
min_bitrate_history_.clear();
min_bitrate_history_.push_back(std::make_pair(now_ms, bitrate_));
return;
}
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_) >=
(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_));
}
}
}
bitrate_ = CapBitrateToThresholds(bitrate_);
}
bool SendSideBandwidthEstimation::IsInStartPhase(int64_t now_ms) const {
return first_report_time_ms_ == -1 ||
now_ms - first_report_time_ms_ < kStartPhaseMs;
}
void SendSideBandwidthEstimation::UpdateMinHistory(int64_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_));
}
uint32_t SendSideBandwidthEstimation::CapBitrateToThresholds(uint32_t bitrate) {
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_;
}
return bitrate;
}
} // namespace webrtc