blob: dc3a49fe95c02e4c383f6c4f61e73b94da89507f [file] [log] [blame]
/*
* 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.
*/
#if _WIN32
#include <windows.h>
#endif
#include "remote_rate_control.h"
#include "trace.h"
#include <math.h>
#include <string.h>
#ifdef MATLAB
extern MatlabEngine eng; // global variable defined elsewhere
#endif
namespace webrtc {
RemoteRateControl::RemoteRateControl()
:
_minConfiguredBitRate(30000),
_maxConfiguredBitRate(30000000),
_currentBitRate(_maxConfiguredBitRate),
_maxHoldRate(0),
_avgMaxBitRate(-1.0f),
_varMaxBitRate(0.4f),
_rcState(kRcHold),
_cameFromState(kRcDecrease),
_rcRegion(kRcMaxUnknown),
_lastBitRateChange(-1),
_currentInput(kBwNormal, 0, 1.0),
_updated(false),
_timeFirstIncomingEstimate(-1),
_initializedBitRate(false),
_avgChangePeriod(1000.0f),
_lastChangeMs(-1),
_beta(0.9f)
#ifdef MATLAB
,_plot1(NULL),
_plot2(NULL)
#endif
{
}
RemoteRateControl::~RemoteRateControl()
{
#ifdef MATLAB
eng.DeletePlot(_plot1);
eng.DeletePlot(_plot2);
#endif
}
void RemoteRateControl::Reset()
{
_minConfiguredBitRate = 30000;
_maxConfiguredBitRate = 30000000;
_currentBitRate = _maxConfiguredBitRate;
_maxHoldRate = 0;
_avgMaxBitRate = -1.0f;
_varMaxBitRate = 0.4f;
_rcState = kRcHold;
_cameFromState = kRcHold;
_rcRegion = kRcMaxUnknown;
_lastBitRateChange = -1;
_avgChangePeriod = 1000.0f;
_lastChangeMs = -1;
_beta = 0.9f;
_currentInput._bwState = kBwNormal;
_currentInput._incomingBitRate = 0;
_currentInput._noiseVar = 1.0;
_updated = false;
_timeFirstIncomingEstimate = -1;
_initializedBitRate = false;
}
bool RemoteRateControl::ValidEstimate() {
return _initializedBitRate;
}
WebRtc_Word32 RemoteRateControl::SetConfiguredBitRates(WebRtc_UWord32 minBitRateBps, WebRtc_UWord32 maxBitRateBps)
{
if (minBitRateBps > maxBitRateBps)
{
return -1;
}
_minConfiguredBitRate = minBitRateBps;
_maxConfiguredBitRate = maxBitRateBps;
_currentBitRate = BWE_MIN(BWE_MAX(minBitRateBps, _currentBitRate), maxBitRateBps);
return 0;
}
WebRtc_UWord32 RemoteRateControl::TargetBitRate(WebRtc_UWord32 RTT,
WebRtc_Word64 nowMS)
{
_currentBitRate = ChangeBitRate(_currentBitRate, _currentInput._incomingBitRate,
_currentInput._noiseVar, RTT, nowMS);
return _currentBitRate;
}
RateControlRegion RemoteRateControl::Update(const RateControlInput& input,
bool& firstOverUse,
WebRtc_Word64 nowMS)
{
#ifdef MATLAB
// Create plots
if (_plot1 == NULL)
{
_plot1 = eng.NewPlot(new MatlabPlot());
_plot1->AddTimeLine(30, "b", "current");
_plot1->AddTimeLine(30, "r-", "avgMax");
_plot1->AddTimeLine(30, "r--", "pStdMax");
_plot1->AddTimeLine(30, "r--", "nStdMax");
_plot1->AddTimeLine(30, "r+", "max");
_plot1->AddTimeLine(30, "g", "incoming");
_plot1->AddTimeLine(30, "b+", "recovery");
}
if (_plot2 == NULL)
{
_plot2 = eng.NewPlot(new MatlabPlot());
_plot2->AddTimeLine(30, "b", "alpha");
}
#endif
firstOverUse = (_currentInput._bwState != kBwOverusing &&
input._bwState == kBwOverusing);
// Set the initial bit rate value to what we're receiving the first second
if (!_initializedBitRate)
{
if (_timeFirstIncomingEstimate < 0)
{
if (input._incomingBitRate > 0)
{
_timeFirstIncomingEstimate = nowMS;
}
}
else if (nowMS - _timeFirstIncomingEstimate > 1000 &&
input._incomingBitRate > 0)
{
_currentBitRate = input._incomingBitRate;
_initializedBitRate = true;
}
}
if (_updated && _currentInput._bwState == kBwOverusing)
{
// Only update delay factor and incoming bit rate. We always want to react on an over-use.
_currentInput._noiseVar = input._noiseVar;
_currentInput._incomingBitRate = input._incomingBitRate;
return _rcRegion;
}
_updated = true;
_currentInput = input;
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: Incoming rate = %u kbps", input._incomingBitRate/1000);
return _rcRegion;
}
WebRtc_UWord32 RemoteRateControl::ChangeBitRate(WebRtc_UWord32 currentBitRate,
WebRtc_UWord32 incomingBitRate,
double noiseVar,
WebRtc_UWord32 RTT,
WebRtc_Word64 nowMS)
{
if (!_updated)
{
return _currentBitRate;
}
_updated = false;
UpdateChangePeriod(nowMS);
ChangeState(_currentInput, nowMS);
// calculated here because it's used in multiple places
const float incomingBitRateKbps = incomingBitRate / 1000.0f;
// Calculate the max bit rate std dev given the normalized
// variance and the current incoming bit rate.
const float stdMaxBitRate = sqrt(_varMaxBitRate * _avgMaxBitRate);
bool recovery = false;
switch (_rcState)
{
case kRcHold:
{
_maxHoldRate = BWE_MAX(_maxHoldRate, incomingBitRate);
break;
}
case kRcIncrease:
{
if (_avgMaxBitRate >= 0)
{
if (incomingBitRateKbps > _avgMaxBitRate + 3 * stdMaxBitRate)
{
ChangeRegion(kRcMaxUnknown);
_avgMaxBitRate = -1.0;
}
else if (incomingBitRateKbps > _avgMaxBitRate + 2.5 * stdMaxBitRate)
{
ChangeRegion(kRcAboveMax);
}
}
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"BWE: Response time: %f + %i + 10*33\n",
_avgChangePeriod, RTT);
const WebRtc_UWord32 responseTime = static_cast<WebRtc_UWord32>(_avgChangePeriod + 0.5f) + RTT + 300;
double alpha = RateIncreaseFactor(nowMS, _lastBitRateChange,
responseTime, noiseVar);
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"BWE: _avgChangePeriod = %f ms; RTT = %u ms", _avgChangePeriod, RTT);
currentBitRate = static_cast<WebRtc_UWord32>(currentBitRate * alpha) + 1000;
if (_maxHoldRate > 0 && _beta * _maxHoldRate > currentBitRate)
{
currentBitRate = static_cast<WebRtc_UWord32>(_beta * _maxHoldRate);
_avgMaxBitRate = _beta * _maxHoldRate / 1000.0f;
ChangeRegion(kRcNearMax);
recovery = true;
#ifdef MATLAB
_plot1->Append("recovery", _maxHoldRate/1000);
#endif
}
_maxHoldRate = 0;
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"BWE: Increase rate to currentBitRate = %u kbps", currentBitRate/1000);
_lastBitRateChange = nowMS;
break;
}
case kRcDecrease:
{
if (incomingBitRate < _minConfiguredBitRate)
{
currentBitRate = _minConfiguredBitRate;
}
else
{
// Set bit rate to something slightly lower than max
// to get rid of any self-induced delay.
currentBitRate = static_cast<WebRtc_UWord32>(_beta * incomingBitRate + 0.5);
if (currentBitRate > _currentBitRate)
{
// Avoid increasing the rate when over-using.
if (_rcRegion != kRcMaxUnknown)
{
currentBitRate = static_cast<WebRtc_UWord32>(_beta * _avgMaxBitRate * 1000 + 0.5f);
}
currentBitRate = BWE_MIN(currentBitRate, _currentBitRate);
}
ChangeRegion(kRcNearMax);
if (incomingBitRateKbps < _avgMaxBitRate - 3 * stdMaxBitRate)
{
_avgMaxBitRate = -1.0f;
}
UpdateMaxBitRateEstimate(incomingBitRateKbps);
#ifdef MATLAB
_plot1->Append("max", incomingBitRateKbps);
#endif
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: Decrease rate to currentBitRate = %u kbps", currentBitRate/1000);
}
// Stay on hold until the pipes are cleared.
ChangeState(kRcHold);
_lastBitRateChange = nowMS;
break;
}
}
if (!recovery && (incomingBitRate > 100000 || currentBitRate > 150000) &&
currentBitRate > 1.5 * incomingBitRate)
{
// Allow changing the bit rate if we are operating at very low rates
// Don't change the bit rate if the send side is too far off
currentBitRate = _currentBitRate;
_lastBitRateChange = nowMS;
}
#ifdef MATLAB
if (_avgMaxBitRate >= 0.0f)
{
_plot1->Append("avgMax", _avgMaxBitRate);
_plot1->Append("pStdMax", _avgMaxBitRate + 3*stdMaxBitRate);
_plot1->Append("nStdMax", _avgMaxBitRate - 3*stdMaxBitRate);
}
_plot1->Append("incoming", incomingBitRate/1000);
_plot1->Append("current", currentBitRate/1000);
_plot1->Plot();
#endif
return currentBitRate;
}
double RemoteRateControl::RateIncreaseFactor(WebRtc_Word64 nowMs, WebRtc_Word64 lastMs, WebRtc_UWord32 reactionTimeMs, double noiseVar) const
{
// alpha = 1.02 + B ./ (1 + exp(b*(tr - (c1*s2 + c2))))
// Parameters
const double B = 0.0407;
const double b = 0.0025;
const double c1 = -6700.0 / (33 * 33);
const double c2 = 800.0;
const double d = 0.85;
double alpha = 1.005 + B / (1 + exp( b * (d * reactionTimeMs - (c1 * noiseVar + c2))));
if (alpha < 1.005)
{
alpha = 1.005;
}
else if (alpha > 1.3)
{
alpha = 1.3;
}
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"BWE: alpha = %f", alpha);
#ifdef MATLAB
_plot2->Append("alpha", alpha);
_plot2->Plot();
#endif
if (lastMs > -1)
{
alpha = pow(alpha, (nowMs - lastMs) / 1000.0);
}
if (_rcRegion == kRcNearMax)
{
// We're close to our previous maximum. Try to stabilize the
// bit rate in this region, by increasing in smaller steps.
alpha = alpha - (alpha - 1.0) / 2.0;
}
else if (_rcRegion == kRcMaxUnknown)
{
alpha = alpha + (alpha - 1.0) * 2.0;
}
return alpha;
}
void RemoteRateControl::UpdateChangePeriod(WebRtc_Word64 nowMs)
{
WebRtc_Word64 changePeriod = 0;
if (_lastChangeMs > -1)
{
changePeriod = nowMs - _lastChangeMs;
}
_lastChangeMs = nowMs;
_avgChangePeriod = 0.9f * _avgChangePeriod + 0.1f * changePeriod;
}
void RemoteRateControl::UpdateMaxBitRateEstimate(float incomingBitRateKbps)
{
const float alpha = 0.05f;
if (_avgMaxBitRate == -1.0f)
{
_avgMaxBitRate = incomingBitRateKbps;
}
else
{
_avgMaxBitRate = (1 - alpha) * _avgMaxBitRate +
alpha * incomingBitRateKbps;
}
// Estimate the max bit rate variance and normalize the variance
// with the average max bit rate.
const float norm = BWE_MAX(_avgMaxBitRate, 1.0f);
_varMaxBitRate = (1 - alpha) * _varMaxBitRate +
alpha * (_avgMaxBitRate - incomingBitRateKbps) *
(_avgMaxBitRate - incomingBitRateKbps) /
norm;
// 0.4 ~= 14 kbit/s at 500 kbit/s
if (_varMaxBitRate < 0.4f)
{
_varMaxBitRate = 0.4f;
}
// 2.5f ~= 35 kbit/s at 500 kbit/s
if (_varMaxBitRate > 2.5f)
{
_varMaxBitRate = 2.5f;
}
}
void RemoteRateControl::ChangeState(const RateControlInput& input, WebRtc_Word64 nowMs)
{
switch (_currentInput._bwState)
{
case kBwNormal:
{
if (_rcState == kRcHold)
{
_lastBitRateChange = nowMs;
ChangeState(kRcIncrease);
}
break;
}
case kBwOverusing:
{
if (_rcState != kRcDecrease)
{
ChangeState(kRcDecrease);
}
break;
}
case kBwUnderUsing:
{
ChangeState(kRcHold);
break;
}
}
}
void RemoteRateControl::ChangeRegion(RateControlRegion region)
{
_rcRegion = region;
switch (_rcRegion)
{
case kRcAboveMax:
case kRcMaxUnknown:
{
_beta = 0.9f;
break;
}
case kRcNearMax:
{
_beta = 0.95f;
break;
}
}
}
void RemoteRateControl::ChangeState(RateControlState newState)
{
_cameFromState = _rcState;
_rcState = newState;
char state1[15];
char state2[15];
char state3[15];
StateStr(_cameFromState, state1);
StateStr(_rcState, state2);
StateStr(_currentInput._bwState, state3);
WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1,
"\t%s => %s due to %s\n", state1, state2, state3);
}
void RemoteRateControl::StateStr(RateControlState state, char* str)
{
switch (state)
{
case kRcDecrease:
strncpy(str, "DECREASE", 9);
break;
case kRcHold:
strncpy(str, "HOLD", 5);
break;
case kRcIncrease:
strncpy(str, "INCREASE", 9);
break;
}
}
void RemoteRateControl::StateStr(BandwidthUsage state, char* str)
{
switch (state)
{
case kBwNormal:
strncpy(str, "NORMAL", 7);
break;
case kBwOverusing:
strncpy(str, "OVER USING", 11);
break;
case kBwUnderUsing:
strncpy(str, "UNDER USING", 12);
break;
}
}
} // namespace webrtc