/* * 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 "remote_rate_control.h" #include "tick_util.h" #include "trace.h" #include #include #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 DEBUG_DELAY_SAMPLES ,_delayFile(NULL) #endif #ifdef MATLAB ,_plot1(NULL), _plot2(NULL) #endif { #ifdef DEBUG_DELAY_SAMPLES _delayFile = fopen("delaySamples.m", "w"); fprintf(_delayFile, "delays=[\n"); #endif } RemoteRateControl::~RemoteRateControl() { #ifdef DEBUG_DELAY_SAMPLES fprintf(_delayFile, "];"); fflush(_delayFile); fclose(_delayFile); #endif #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; } 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) { _currentBitRate = ChangeBitRate(_currentBitRate, _currentInput._incomingBitRate, _currentInput._noiseVar, RTT); return _currentBitRate; } RateControlRegion RemoteRateControl::Update(const RateControlInput& input, bool& firstOverUse) { #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 = TickTime::MillisecondTimestamp(); } } else if (TickTime::MillisecondTimestamp() - _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) { const WebRtc_Word64 now = TickTime::MillisecondTimestamp(); if (!_updated) { return _currentBitRate; } _updated = false; UpdateChangePeriod(now); ChangeState(_currentInput, now); // 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); } } #ifdef _DEBUG char logStr[256]; #ifdef _WIN32 _snprintf(logStr,256, "Response time: %f + %i + 10*33\n", _avgChangePeriod, RTT); OutputDebugStringA(logStr); #else snprintf(logStr,256, "Response time: %f + %i + 10*33\n", _avgChangePeriod, RTT); //TODO #endif #endif const WebRtc_UWord32 responseTime = static_cast(_avgChangePeriod + 0.5f) + RTT + 300; double alpha = RateIncreaseFactor(now, _lastBitRateChange, responseTime, noiseVar); WEBRTC_TRACE(kTraceStream, kTraceRtpRtcp, -1, "BWE: _avgChangePeriod = %f ms; RTT = %u ms", _avgChangePeriod, RTT); currentBitRate = static_cast(currentBitRate * alpha) + 1000; if (_maxHoldRate > 0 && _beta * _maxHoldRate > currentBitRate) { currentBitRate = static_cast(_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); #ifdef _DEBUG //char logStr[256]; #ifdef _WIN32 _snprintf(logStr,256, "New bitRate: %lu\n", currentBitRate / 1000); OutputDebugStringA(logStr); #else snprintf(logStr,256, "New bitRate: %lu\n", currentBitRate / 1000); //TODO #endif #endif _lastBitRateChange = now; 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(_beta * incomingBitRate + 0.5); if (currentBitRate > _currentBitRate) { // Avoid increasing the rate when over-using. if (_rcRegion != kRcMaxUnknown) { currentBitRate = static_cast(_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 = now; 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 = now; } #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.001 + B / (1 + exp( b * (d * reactionTimeMs - (c1 * noiseVar + c2)))); if (alpha < 1.001) { alpha = 1.001; } 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; #ifdef _DEBUG char logStr[256]; char state1[15]; char state2[15]; char state3[15]; StateStr(_cameFromState, state1); StateStr(_rcState, state2); StateStr(_currentInput._bwState, state3); #ifdef _WIN32 _snprintf(logStr,256, "\t%s => %s due to %s\n", state1, state2, state3); OutputDebugStringA(logStr); #else snprintf(logStr,256, "\t%s => %s due to %s\n", state1, state2, state3); //TODO #endif #endif } #ifdef _DEBUG void RemoteRateControl::StateStr(RateControlState state, char* str) { switch (state) { case kRcDecrease: strncpy(str, "DECREASE", 8); str[8] = 0; break; case kRcHold: strncpy(str, "HOLD", 4); str[4] = 0; break; case kRcIncrease: strncpy(str, "INCREASE", 8); str[8] = 0; break; } } void RemoteRateControl::StateStr(BandwidthUsage state, char* str) { switch (state) { case kBwNormal: strncpy(str, "NORMAL", 6); str[6] = 0; break; case kBwOverusing: strncpy(str, "OVER USING", 10); str[10] = 0; break; case kBwUnderUsing: strncpy(str, "UNDER USING", 11); str[11] = 0; break; } } #endif } // namespace webrtc