| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276 |
- //
- // Created by Grishka on 25/02/2019.
- //
- #include <algorithm>
- #include <math.h>
- #include "ScreamCongestionController.h"
- #include "../logging.h"
- #include "../VoIPController.h"
- using namespace tgvoip;
- using namespace tgvoip::video;
- namespace{
- /*static*/ constexpr float QDELAY_TARGET_LO=0.1f; // seconds
- /*static*/ constexpr float QDELAY_TARGET_HI=0.4f; // seconds
- /*static*/ constexpr float QDELAY_WEIGHT=0.1f;
- /*static*/ constexpr float QDELAY_TREND_TH=0.2f;
- /*static*/ constexpr uint32_t MIN_CWND=3000; // bytes
- /*static*/ constexpr float MAX_BYTES_IN_FLIGHT_HEAD_ROOM=1.1f;
- /*static*/ constexpr float GAIN=1.0f;
- /*static*/ constexpr float BETA_LOSS=0.8f;
- /*static*/ constexpr float BETA_ECN=0.9f;
- /*static*/ constexpr float BETA_R=0.9f;
- /*static*/ constexpr uint32_t MSS=1024;
- /*static*/ constexpr float RATE_ADJUST_INTERVAL=0.2f;
- /*static*/ constexpr uint32_t TARGET_BITRATE_MIN=50*1024; // bps
- /*static*/ constexpr uint32_t TARGET_BITRATE_MAX=500*1024; // bps
- /*static*/ constexpr uint32_t RAMP_UP_SPEED=200000; // bps/s
- /*static*/ constexpr float PRE_CONGESTION_GUARD=0.1f;
- /*static*/ constexpr float TX_QUEUE_SIZE_FACTOR=1.0f;
- /*static*/ constexpr float RTP_QDELAY_TH=0.02f; // seconds
- /*static*/ constexpr float TARGET_RATE_SCALE_RTP_QDELAY=0.95f;
- /*static*/ constexpr float QDELAY_TREND_LO=0.2f;
- /*static*/ constexpr float T_RESUME_FAST_INCREASE=5.0f; // seconds
- /*static*/ constexpr uint32_t RATE_PACE_MIN=50000; // bps
- }
- ScreamCongestionController::ScreamCongestionController() : qdelayTarget(QDELAY_TARGET_LO), cwnd(MIN_CWND) {
- }
- void ScreamCongestionController::UpdateVariables(float qdelay){
- float qdelayFraction=qdelay/qdelayTarget;
- qdelayFractionAvg=(1.0f-QDELAY_WEIGHT)*qdelayFractionAvg+qdelayFraction*QDELAY_WEIGHT;
- qdelayFractionHist.Add(qdelayFraction);
- float avg=qdelayFractionHist.Average();
- float r1=0.0, r0=0.0;
- for(int i=(int)qdelayFractionHist.Size()-1;i>=0;i--){
- float v=qdelayFractionHist[i]-avg;
- r0+=v*v;
- }
- for(int i=(int)qdelayFractionHist.Size()-1;i>=1;i--){
- float v1=qdelayFractionHist[i]-avg;
- float v2=qdelayFractionHist[i-1]-avg;
- r1+=v1*v2;
- }
- float a=r1/r0;
- qdelayTrend=std::min(1.0f, std::max(0.0f, a*qdelayFractionAvg));
- qdelayTrendMem=std::max(0.99f*qdelayTrendMem, qdelayTrend);
- if(qdelayTrend>QDELAY_TREND_LO){
- lastTimeQDelayTrendWasGreaterThanLo=VoIPController::GetCurrentTime();
- }
- }
- void ScreamCongestionController::UpdateCWnd(float qdelay){
- if(inFastIncrease){
- if(qdelayTrend>=QDELAY_TREND_TH){
- inFastIncrease=false;
- }else{
- if((float)bytesInFlight*1.5f+bytesNewlyAcked>cwnd){
- LOGD("HERE");
- cwnd+=bytesNewlyAcked;
- }
- return;
- }
- }
- float offTarget=(qdelayTarget-qdelay)/qdelayTarget;
- float gain=GAIN;
- float cwndDelta=gain*offTarget*bytesNewlyAcked*MSS/(float)cwnd;
- if(offTarget>0 && (float)bytesInFlight*1.25f+bytesNewlyAcked<=cwnd){
- cwndDelta=0.0;
- }
- cwnd+=cwndDelta;
- cwnd=std::min(cwnd, (uint32_t)(maxBytesInFlight*MAX_BYTES_IN_FLIGHT_HEAD_ROOM));
- cwnd=std::max(cwnd, MIN_CWND);
- }
- void ScreamCongestionController::AdjustQDelayTarget(float qdelay){
- float qdelayNorm=qdelay/QDELAY_TARGET_LO;
- qdelayNormHist.Add(qdelayNorm);
- float qdelayNormAvg=qdelayNormHist.Average();
- float qdelayNormVar=0.0;
- for(uint32_t i=0;i<qdelayNormHist.Size();i++){
- float tmp=qdelayNormHist[i]-qdelayNormAvg;
- qdelayNormVar+=tmp*tmp;
- }
- qdelayNormVar/=qdelayNormHist.Size();
- float newTarget=qdelayNormAvg+sqrt(qdelayNormVar);
- newTarget*=QDELAY_TARGET_LO;
- if(lossEventRate>0.002f){
- qdelayTarget=1.5f*newTarget;
- }else{
- if(qdelayNormVar<0.2f){
- qdelayTarget=newTarget;
- }else{
- if(newTarget<QDELAY_TARGET_LO){
- qdelayTarget=std::max(qdelayTarget*0.5f, newTarget);
- }else{
- qdelayTarget*=0.9;
- }
- }
- }
- qdelayTarget=std::min(QDELAY_TARGET_HI, qdelayTarget);
- qdelayTarget=std::max(QDELAY_TARGET_LO, qdelayTarget);
- }
- void ScreamCongestionController::AdjustBitrate(){
- if(lossPending){
- lossPending=false;
- targetBitrate=std::max((uint32_t)(BETA_R*(float)targetBitrate), TARGET_BITRATE_MIN);
- return;
- }
- float rampUpSpeed=std::min(RAMP_UP_SPEED, targetBitrate/2);
- float scale=(float)(targetBitrate-targetBitrateLastMax)/(float)targetBitrateLastMax;
- scale=std::max(0.2f, std::min(1.0f, (scale*4)*(scale*4)));
- float currentRate=std::max(rateTransmit, rateAck);
- if(inFastIncrease){
- float increment=(rampUpSpeed*RATE_ADJUST_INTERVAL)*scale;
- targetBitrate+=increment;
- }else{
- float deltaRate=currentRate*(1.0f-PRE_CONGESTION_GUARD*qdelayTrend)-TX_QUEUE_SIZE_FACTOR*(float)rtpQueueSize;
- if(deltaRate>0.0f){
- deltaRate*=scale;
- deltaRate=std::min(deltaRate, rampUpSpeed*RATE_ADJUST_INTERVAL);
- }
- targetBitrate+=deltaRate;
- float rtpQueueDelay=(float)rtpQueueSize/currentRate;
- if(rtpQueueDelay>RTP_QDELAY_TH){
- targetBitrate=(uint32_t)((float)targetBitrate*TARGET_RATE_SCALE_RTP_QDELAY);
- }
- }
- float rateMediaLimit=std::max(currentRate, std::max(rateMedia, rateMediaMedian));
- rateMediaLimit*=(2.0-qdelayTrendMem);
- targetBitrate=std::min(targetBitrate, (uint32_t)rateMediaLimit);
- targetBitrate=std::min(TARGET_BITRATE_MAX, std::max(TARGET_BITRATE_MIN, targetBitrate));
- }
- void ScreamCongestionController::CalculateSendWindow(float qdelay){
- if(qdelay<=qdelayTarget)
- sendWnd=cwnd+MSS-bytesInFlight;
- else
- sendWnd=cwnd-bytesInFlight;
- }
- void ScreamCongestionController::ProcessAcks(float oneWayDelay, uint32_t bytesNewlyAcked, uint32_t lossCount, double rtt){
- if(prevOneWayDelay!=0.0f){
- double currentTime=VoIPController::GetCurrentTime();
- float qdelay=oneWayDelay-prevOneWayDelay;
- sRTT=(float)rtt;
- bytesInFlight-=bytesNewlyAcked;
- rtpQueueSize-=(bytesNewlyAcked*8);
- UpdateBytesInFlightHistory();
- bytesAcked+=bytesNewlyAcked;
- //LOGV("Scream: qdelay = %f, newly acked = %u, in flight = %u, losses = %u", qdelay, bytesNewlyAcked, bytesInFlight, lossCount);
- if(currentTime-lastVariablesUpdateTime>=0.050){
- lastVariablesUpdateTime=currentTime;
- UpdateVariables(qdelay);
- }
- if(currentTime-lastRateAdjustmentTime>=RATE_ADJUST_INTERVAL){
- lastRateAdjustmentTime=currentTime;
- AdjustBitrate();
- //LOGV("Scream: target bitrate = %u", targetBitrate);
- }
- if(lossCount>prevLossCount && currentTime>ignoreLossesUntil){
- LOGD("Scream: loss detected");
- ignoreLossesUntil=currentTime+rtt;
- inFastIncrease=false;
- cwnd=std::max(MIN_CWND, (uint32_t)(cwnd*BETA_LOSS));
- AdjustQDelayTarget(qdelay);
- CalculateSendWindow(qdelay);
- lossPending=true;
- prevLossCount=lossCount;
- lastTimeQDelayTrendWasGreaterThanLo=currentTime;
- }else{
- this->bytesNewlyAcked+=bytesNewlyAcked;
- if(currentTime-lastCWndUpdateTime>=0.15){
- lastCWndUpdateTime=currentTime;
- UpdateCWnd(qdelay);
- //LOGI("Scream: cwnd = %u", cwnd);
- this->bytesNewlyAcked=0;
- }
- AdjustQDelayTarget(qdelay);
- CalculateSendWindow(qdelay);
- if(!inFastIncrease){
- if(currentTime-lastTimeQDelayTrendWasGreaterThanLo>=T_RESUME_FAST_INCREASE){
- inFastIncrease=true;
- }
- }
- }
- }
- prevOneWayDelay=oneWayDelay;
- }
- void ScreamCongestionController::ProcessPacketSent(uint32_t size){
- bytesInFlight+=size;
- rtpQueueSize+=(size*8);
- bytesSent+=size;
- double currentTime=VoIPController::GetCurrentTime();
- if(currentTime-rateTransmitUpdateTime>=0.2){
- rateTransmit=(float)(bytesSent*8)/(float)(currentTime-rateTransmitUpdateTime);
- rateAck=(float)(bytesAcked*8)/(float)(currentTime-rateTransmitUpdateTime);
- rateTransmitUpdateTime=currentTime;
- bytesSent=0;
- bytesAcked=0;
- //LOGV("rateTransmit %f, rateAck %f", rateTransmit, rateAck);
- }
- UpdateBytesInFlightHistory();
- }
- void ScreamCongestionController::ProcessPacketLost(uint32_t size){
- bytesInFlight-=size;
- rtpQueueSize-=(size*8);
- UpdateBytesInFlightHistory();
- }
- double ScreamCongestionController::GetPacingInterval(){
- float paceBitrate=std::max((float)RATE_PACE_MIN, cwnd*8.0f/sRTT);
- //LOGV("RTT=%f cwnd=%u paceBitrate=%f fastIncrease=%d", sRTT, cwnd, paceBitrate, inFastIncrease);
- double pacingInterval=rtpSize*8.0f/paceBitrate;
- return std::min(0.010, pacingInterval);
- }
- void ScreamCongestionController::UpdateBytesInFlightHistory(){
- double currentTime=VoIPController::GetCurrentTime();
- ValueSample now={bytesInFlight, currentTime};
- bytesInFlightHistory.push_back(now);
- uint32_t max=0;
- for(std::vector<ValueSample>::iterator i=bytesInFlightHistory.begin();i!=bytesInFlightHistory.end();){
- if(currentTime-i->time>=5.0){
- i=bytesInFlightHistory.erase(i);
- }else{
- max=std::max(max, i->sample);
- ++i;
- }
- }
- maxBytesInFlight=max;
- }
- void ScreamCongestionController::UpdateMediaRate(uint32_t frameSize){
- bytesMedia+=frameSize;
- double currentTime=VoIPController::GetCurrentTime();
- if(currentTime-rateMediaUpdateTime>=0.5){
- rateMedia=(float)(bytesMedia*8)/(float)(currentTime-rateMediaUpdateTime);
- bytesMedia=0;
- rateMediaUpdateTime=currentTime;
- LOGV("rateMedia %f", rateMedia);
- rateMediaHistory.Add(rateMedia);
- rateMediaMedian=rateMediaHistory.NonZeroAverage();
- }
- }
- uint32_t ScreamCongestionController::GetBitrate(){
- return targetBitrate;
- }
|