improvement of auto jitter buffer detection in bad network conditions

This commit is contained in:
Volker Fischer 2015-03-14 16:54:36 +00:00
parent af4e972bfd
commit 16f170c030
6 changed files with 101 additions and 19 deletions

View File

@ -41,7 +41,8 @@ CAnalyzerConsole::CAnalyzerConsole ( CClient* pNCliP,
GraphFrameColor ( Qt::black ), // frame
GraphGridColor ( Qt::gray ), // grid
LineColor ( Qt::blue ),
LineLimitColor ( Qt::red )
LineLimitColor ( Qt::green ),
LineMaxUpLimitColor ( Qt::red )
{
// set the window icon and title text
const QIcon icon = QIcon ( QString::fromUtf8 ( ":/png/main/res/mainicon.png" ) );
@ -126,14 +127,16 @@ void CAnalyzerConsole::DrawErrorRateTrace()
// get the network buffer error rates to be displayed
CVector<double> vecButErrorRates;
double dLimit;
double dMaxUpLimit;
pClient->GetBufErrorRates ( vecButErrorRates, dLimit );
pClient->GetBufErrorRates ( vecButErrorRates, dLimit, dMaxUpLimit );
// get the number of data elements
const int iNumBuffers = vecButErrorRates.Size();
// convert the limit in the log domain
const double dLogLimit = log10 ( dLimit );
// convert the limits in the log domain
const double dLogLimit = log10 ( dLimit );
const double dLogMaxUpLimit = log10 ( dMaxUpLimit );
// use fixed y-axis scale where the limit line is in the middle of the graph
const double dMax = 0;
@ -154,6 +157,17 @@ void CAnalyzerConsole::DrawErrorRateTrace()
QPoint ( GraphGridFrame.x() +
GraphGridFrame.width(), dYValLimitInGraph ) );
// plot the maximum upper limit line as a dashed line
const double dYValMaxUpLimitInGraph = CalcYPosInGraph ( dMin, dMax, dLogMaxUpLimit );
GraphPainter.setPen ( QPen ( QBrush ( LineMaxUpLimitColor ),
iLineWidth,
Qt::DashLine ) );
GraphPainter.drawLine ( QPoint ( GraphGridFrame.x(), dYValMaxUpLimitInGraph ),
QPoint ( GraphGridFrame.x() +
GraphGridFrame.width(), dYValMaxUpLimitInGraph ) );
// plot the data
for ( int i = 0; i < iNumBuffers; i++ )
{

View File

@ -82,6 +82,7 @@ protected:
QColor GraphGridColor;
QColor LineColor;
QColor LineLimitColor;
QColor LineMaxUpLimitColor;
QTimer TimerErrRateUpdate;

View File

@ -115,7 +115,8 @@ CNetBufWithStats::CNetBufWithStats() :
}
void CNetBufWithStats::GetErrorRates ( CVector<double>& vecErrRates,
double& dLimit )
double& dLimit,
double& dMaxUpLimit )
{
// get all the averages of the error statistic
vecErrRates.Init ( NUM_STAT_SIMULATION_BUFFERS );
@ -125,8 +126,9 @@ void CNetBufWithStats::GetErrorRates ( CVector<double>& vecErrRates,
vecErrRates[i] = ErrorRateStatistic[i].GetAverage();
}
// get the limit for decision
dLimit = ERROR_RATE_BOUND;
// get the limits for the decisions
dLimit = ERROR_RATE_BOUND;
dMaxUpLimit = UP_MAX_ERROR_BOUND;
}
void CNetBufWithStats::Init ( const int iNewBlockSize,
@ -199,14 +201,17 @@ bool CNetBufWithStats::Get ( CVector<uint8_t>& vecbyData,
void CNetBufWithStats::UpdateAutoSetting()
{
int iCurDecision = 0; // dummy initialization
bool bDecisionFound = false;
int iCurDecision = 0; // dummy initialization
int iCurMaxUpDecision = 0; // dummy initialization
bool bDecisionFound;
// Get error rate decision -------------------------------------------------
// Get regular error rate decision -----------------------------------------
// Use a specified error bound to identify the best buffer size for the
// current network situation. Start with the smallest buffer and
// test for the error rate until the rate is below the bound.
bDecisionFound = false;
for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS - 1; i++ )
{
if ( ( !bDecisionFound ) &&
@ -224,6 +229,30 @@ void CNetBufWithStats::UpdateAutoSetting()
}
// Get maximum upper error rate decision -----------------------------------
// Use a specified error bound to identify the maximum upper error rate
// to identify if we have a too low buffer setting which gives a very
// bad performance constantly. Start with the smallest buffer and
// test for the error rate until the rate is below the bound.
bDecisionFound = false;
for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS - 1; i++ )
{
if ( ( !bDecisionFound ) &&
( ErrorRateStatistic[i].GetAverage() <= UP_MAX_ERROR_BOUND ) )
{
iCurMaxUpDecision = viBufSizesForSim[i];
bDecisionFound = true;
}
}
if ( !bDecisionFound )
{
// in case no buffer is below bound, use largest buffer size
iCurMaxUpDecision = viBufSizesForSim[NUM_STAT_SIMULATION_BUFFERS - 1];
}
// Post calculation (filtering) --------------------------------------------
// Define different weigths for up and down direction. Up direction
// filtering shall be slower than for down direction since we assume
@ -235,9 +264,10 @@ void CNetBufWithStats::UpdateAutoSetting()
// adaptation.
// Note that the following definitions of the weigh constants assume a block
// size of 128 samples at a sampling rate of 48 kHz.
double dWeightUp = 0.999995;
double dWeightDown = 0.9999;
const double dHysteresisValue = 0.1;
double dWeightUp = 0.999995;
double dWeightDown = 0.9999;
const double dHysteresisValue = 0.1;
bool bUseFastAdaptation = false;
// check for initialization phase
if ( iInitCounter > 0 )
@ -245,6 +275,20 @@ void CNetBufWithStats::UpdateAutoSetting()
// decrease init counter
iInitCounter--;
// use the fast adaptation
bUseFastAdaptation = true;
}
// if the current detected buffer setting is below the maximum upper bound
// decision, then we enable a booster to go up to the minimum required
// number of buffer blocks (i.e. we use weights for fast adaptation)
if ( iCurAutoBufferSizeSetting < iCurMaxUpDecision )
{
bUseFastAdaptation = true;
}
if ( bUseFastAdaptation )
{
// overwrite weigth values with lower values
dWeightUp = 0.9995;
dWeightDown = 0.999;
@ -256,6 +300,22 @@ void CNetBufWithStats::UpdateAutoSetting()
dWeightUp,
dWeightDown );
/*
// TEST store important detection parameters in file for debugging
static FILE* pFile = fopen ( "test.dat", "w" );
static int icnt = 0;
if ( icnt == 50 )
{
fprintf ( pFile, "%d %e\n", iCurDecision, dCurIIRFilterResult );
fflush ( pFile );
icnt = 0;
}
else
{
icnt++;
}
*/
// apply a hysteresis
iCurAutoBufferSizeSetting =
MathUtils().DecideWithHysteresis ( dCurIIRFilterResult,

View File

@ -34,9 +34,14 @@
// blocks we have 15 s / 2.66 ms * 2 = approx. 11000
#define MAX_STATISTIC_COUNT 11000
// definition of the error bound
// definition of the upper error bound of the jitter buffers
#define ERROR_RATE_BOUND 0.001
// definition of the upper jitter buffer error bound, if that one is reached we
// have to speed up the filtering to quickly get out of a incorrect buffer
// size state
#define UP_MAX_ERROR_BOUND 0.01
// number of simulation network jitter buffers for evaluating the statistic
#define NUM_STAT_SIMULATION_BUFFERS 11
@ -407,7 +412,9 @@ public:
virtual bool Get ( CVector<uint8_t>& vecbyData, const int iOutSize );
int GetAutoSetting() { return iCurAutoBufferSizeSetting; }
void GetErrorRates ( CVector<double>& vecErrRates, double& dLimit );
void GetErrorRates ( CVector<double>& vecErrRates,
double& dLimit,
double& dMaxUpLimit );
protected:
void UpdateAutoSetting();

View File

@ -136,8 +136,8 @@ Protocol.CreateChanNameMes ( ChInfo.strName );
int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; }
int GetNetwFrameSize() const { return iNetwFrameSize; }
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit )
{ SockBuf.GetErrorRates ( vecErrRates, dLimit ); }
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit, double& dMaxUpLimit )
{ SockBuf.GetErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); }
EAudComprType GetAudioCompressionType() { return eAudioCompressionType; }
int GetNumAudioChannels() const { return iNumAudioChannels; }

View File

@ -275,8 +275,8 @@ public:
int EstimatedOverallDelay ( const int iPingTimeMs );
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit )
{ Channel.GetBufErrorRates ( vecErrRates, dLimit ); }
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit, double& dMaxUpLimit )
{ Channel.GetBufErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); }
// settings
CVector<QString> vstrIPAddress;