webrtc/modules/rtp_rtcp/source/bandwidth_management.cc

310 lines
9.2 KiB
C++

/*
* Copyright (c) 2011 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 "bandwidth_management.h"
#include "trace.h"
#include "rtp_utility.h"
#include "rtp_rtcp_config.h"
#include <math.h> // sqrt()
namespace webrtc {
BandwidthManagement::BandwidthManagement(const WebRtc_Word32 id) :
_id(id),
_critsect(*CriticalSectionWrapper::CreateCriticalSection()),
_lastPacketLossExtendedHighSeqNum(0),
_lastReportAllLost(false),
_lastLoss(0),
_accumulateLostPacketsQ8(0),
_accumulateExpectedPackets(0),
_bitRate(0),
_minBitRateConfigured(0),
_maxBitRateConfigured(0),
_last_fraction_loss(0),
_last_round_trip_time(0),
// bandwidth estimate
_bwEstimateIncoming(0),
_bwEstimateIncomingMax(0),
_smoothedFractionLostQ4(-1), // indicate uninitialized
_sFLFactorQ4(14) // 0.875 in Q4
{
}
BandwidthManagement::~BandwidthManagement()
{
delete &_critsect;
}
WebRtc_Word32
BandwidthManagement::SetSendBitrate(const WebRtc_UWord32 startBitrate,
const WebRtc_UWord16 minBitrateKbit,
const WebRtc_UWord16 maxBitrateKbit)
{
CriticalSectionScoped cs(_critsect);
_bitRate = startBitrate;
_minBitRateConfigured = minBitrateKbit*1000;
if(maxBitrateKbit == 0)
{
// no max configured use 1Gbit/s
_maxBitRateConfigured = 1000000000;
} else
{
_maxBitRateConfigured = maxBitrateKbit*1000;
}
return 0;
}
WebRtc_Word32
BandwidthManagement::MaxConfiguredBitrate(WebRtc_UWord16& maxBitrateKbit)
{
CriticalSectionScoped cs(_critsect);
if(_maxBitRateConfigured == 0)
{
return -1;
}
maxBitrateKbit = (WebRtc_UWord16)(_maxBitRateConfigured/1000);
return 0;
}
WebRtc_Word32
BandwidthManagement::UpdateBandwidthEstimate(const WebRtc_UWord16 bandWidthMinKbit,
const WebRtc_UWord16 bandWidthMaxKbit,
WebRtc_UWord32& newBitrate,
WebRtc_UWord8& fractionLost,
WebRtc_UWord16& roundTripTime)
{
newBitrate = 0;
CriticalSectionScoped cs(_critsect);
_bwEstimateIncoming = bandWidthMinKbit*1000;
_bwEstimateIncomingMax = bandWidthMaxKbit*1000;
if(_bitRate == 0)
{
// BandwidthManagement off
return -1;
}
if (_bwEstimateIncoming > 0 && _bitRate > _bwEstimateIncoming)
{
_bitRate = _bwEstimateIncoming;
}
else
{
return -1;
}
newBitrate = _bitRate;
fractionLost = _last_fraction_loss;
roundTripTime = _last_round_trip_time;
return 0;
}
WebRtc_Word32
BandwidthManagement::UpdatePacketLoss(const WebRtc_UWord32 lastReceivedExtendedHighSeqNum,
const bool defaultCodec,
const WebRtc_UWord8 lossInput,
const WebRtc_UWord16 rtt,
WebRtc_UWord32& newBitrate,
WebRtc_UWord16& bwEstimateKbitMin,
WebRtc_UWord16& bwEstimateKbitMax)
{
CriticalSectionScoped cs(_critsect);
WebRtc_UWord8 loss = lossInput; // Local copy to modify.
_last_fraction_loss = loss;
_last_round_trip_time = rtt;
if(_bitRate == 0)
{
// BandwidthManagement off
return -1;
}
// Check sequence number diff and weight loss report
if (_lastPacketLossExtendedHighSeqNum > 0 &&
(lastReceivedExtendedHighSeqNum >= _lastPacketLossExtendedHighSeqNum))
{
// This is not the first loss report and the sequence number is
// non-decreasing. Calculate sequence number diff.
WebRtc_UWord32 seqNumDiff = lastReceivedExtendedHighSeqNum
- _lastPacketLossExtendedHighSeqNum;
// Check if this report and the last was 100% loss, then report
// 100% loss even though seqNumDiff is small.
// If not, go on with the checks.
if (!(_lastReportAllLost && loss == 255))
{
_lastReportAllLost = (loss == 255);
// Calculate number of lost packets.
// loss = 256 * numLostPackets / expectedPackets.
const int numLostPacketsQ8 = loss * seqNumDiff;
// Accumulate reports.
_accumulateLostPacketsQ8 += numLostPacketsQ8;
_accumulateExpectedPackets += seqNumDiff;
// Report loss if the total report is based on sufficiently
// many packets.
const int limitNumPackets = 10;
if (_accumulateExpectedPackets >= limitNumPackets)
{
loss = _accumulateLostPacketsQ8 / _accumulateExpectedPackets;
// Reset accumulators
_accumulateLostPacketsQ8 = 0;
_accumulateExpectedPackets = 0;
}
else
{
// Report same loss as before and keep the accumulators until
// the next report.
loss = _lastLoss;
}
}
}
// Keep for next time.
_lastLoss = loss;
// Remember the sequence number until next time
_lastPacketLossExtendedHighSeqNum = lastReceivedExtendedHighSeqNum;
bwEstimateKbitMax = static_cast<WebRtc_UWord16>(_bwEstimateIncomingMax / 1000);
bwEstimateKbitMin = static_cast<WebRtc_UWord16>(_bwEstimateIncoming / 1000);
newBitrate = 0;
if (defaultCodec)
{
return 0;
}
WebRtc_UWord32 bitRate = ShapeSimple(loss, rtt);
if(bitRate == 0)
{
// no change
return -1;
}
_bitRate = bitRate;
newBitrate = bitRate;
return 0;
}
/* Calculate the rate that TCP-Friendly Rate Control (TFRC) would apply.
* The formula in RFC 3448, Section 3.1, is used.
*/
// protected
WebRtc_Word32
BandwidthManagement::CalcTFRCbps(WebRtc_Word16 avgPackSizeBytes, WebRtc_Word32 rttMs, WebRtc_Word32 packetLoss)
{
if (avgPackSizeBytes <= 0 || rttMs <= 0 || packetLoss <= 0)
{
// input variables out of range; return -1
return -1;
}
double R = static_cast<double>(rttMs)/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>(packetLoss)/255; // packet loss rate in [0, 1)
double s = static_cast<double>(avgPackSizeBytes);
// 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<WebRtc_Word32>(X*8)); // bits/second
}
/*
* Simple bandwidth estimation. Depends a lot on bwEstimateIncoming and packetLoss.
*/
// protected
WebRtc_UWord32
BandwidthManagement::ShapeSimple(WebRtc_Word32 packetLoss, WebRtc_Word32 rtt)
{
WebRtc_UWord32 newBitRate = 0;
bool reducing = false;
if (packetLoss > 5 && packetLoss <= 26)
{
// 2% - 10%
newBitRate = _bitRate;
}
else if (packetLoss > 26)
{
// 26/256 ~= 10%
// reduce rate: newRate = rate * (1 - 0.5*lossRate)
// packetLoss = 256*lossRate
newBitRate = (_bitRate * (512 - packetLoss)) / 512;
reducing = true;
}
else
{
// increase rate by 5%
newBitRate = static_cast<WebRtc_UWord32>(_bitRate * 1.05 + 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)
newBitRate += 1000;
}
// Calculate smoothed loss number
if (_smoothedFractionLostQ4 < 0)
{
// startup
_smoothedFractionLostQ4 = static_cast<WebRtc_UWord16>(packetLoss);
}
else
{
_smoothedFractionLostQ4 = ((_sFLFactorQ4 * _smoothedFractionLostQ4 + 8) >> 4) // Q4*Q4 = Q8; down to Q4 again with proper rounding
+ (16 - _sFLFactorQ4) * static_cast<WebRtc_UWord16>(packetLoss); // Q4 * Q0 = Q4
}
// Calculate what rate TFRC would apply in this situation
//WebRtc_Word32 tfrcRate = CalcTFRCbps(1000, rtt, _smoothedFractionLostQ4 >> 4); // scale loss to Q0 (back to [0, 255])
WebRtc_Word32 tfrcRate = CalcTFRCbps(1000, rtt, packetLoss); // scale loss to Q0 (back to [0, 255])
if (reducing &&
tfrcRate > 0 &&
static_cast<WebRtc_UWord32>(tfrcRate) > newBitRate)
{
// do not reduce further if rate is below TFRC rate
newBitRate = _bitRate;
}
if (_bwEstimateIncoming > 0 && newBitRate > _bwEstimateIncoming)
{
newBitRate = _bwEstimateIncoming;
}
if (newBitRate > _maxBitRateConfigured)
{
newBitRate = _maxBitRateConfigured;
}
if (newBitRate < _minBitRateConfigured)
{
newBitRate = _minBitRateConfigured;
}
return newBitRate;
}
} // namespace webrtc