improvement of auto jitter buffer detection in bad network conditions
This commit is contained in:
parent
af4e972bfd
commit
16f170c030
6 changed files with 101 additions and 19 deletions
|
@ -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++ )
|
||||
{
|
||||
|
|
|
@ -82,6 +82,7 @@ protected:
|
|||
QColor GraphGridColor;
|
||||
QColor LineColor;
|
||||
QColor LineLimitColor;
|
||||
QColor LineMaxUpLimitColor;
|
||||
|
||||
QTimer TimerErrRateUpdate;
|
||||
|
||||
|
|
|
@ -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,
|
||||
|
|
11
src/buffer.h
11
src/buffer.h
|
@ -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();
|
||||
|
|
|
@ -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; }
|
||||
|
|
|
@ -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;
|
||||
|
|
Loading…
Add table
Reference in a new issue