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
|
GraphFrameColor ( Qt::black ), // frame
|
||||||
GraphGridColor ( Qt::gray ), // grid
|
GraphGridColor ( Qt::gray ), // grid
|
||||||
LineColor ( Qt::blue ),
|
LineColor ( Qt::blue ),
|
||||||
LineLimitColor ( Qt::red )
|
LineLimitColor ( Qt::green ),
|
||||||
|
LineMaxUpLimitColor ( Qt::red )
|
||||||
{
|
{
|
||||||
// set the window icon and title text
|
// set the window icon and title text
|
||||||
const QIcon icon = QIcon ( QString::fromUtf8 ( ":/png/main/res/mainicon.png" ) );
|
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
|
// get the network buffer error rates to be displayed
|
||||||
CVector<double> vecButErrorRates;
|
CVector<double> vecButErrorRates;
|
||||||
double dLimit;
|
double dLimit;
|
||||||
|
double dMaxUpLimit;
|
||||||
|
|
||||||
pClient->GetBufErrorRates ( vecButErrorRates, dLimit );
|
pClient->GetBufErrorRates ( vecButErrorRates, dLimit, dMaxUpLimit );
|
||||||
|
|
||||||
// get the number of data elements
|
// get the number of data elements
|
||||||
const int iNumBuffers = vecButErrorRates.Size();
|
const int iNumBuffers = vecButErrorRates.Size();
|
||||||
|
|
||||||
// convert the limit in the log domain
|
// convert the limits in the log domain
|
||||||
const double dLogLimit = log10 ( dLimit );
|
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
|
// use fixed y-axis scale where the limit line is in the middle of the graph
|
||||||
const double dMax = 0;
|
const double dMax = 0;
|
||||||
|
@ -154,6 +157,17 @@ void CAnalyzerConsole::DrawErrorRateTrace()
|
||||||
QPoint ( GraphGridFrame.x() +
|
QPoint ( GraphGridFrame.x() +
|
||||||
GraphGridFrame.width(), dYValLimitInGraph ) );
|
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
|
// plot the data
|
||||||
for ( int i = 0; i < iNumBuffers; i++ )
|
for ( int i = 0; i < iNumBuffers; i++ )
|
||||||
{
|
{
|
||||||
|
|
|
@ -82,6 +82,7 @@ protected:
|
||||||
QColor GraphGridColor;
|
QColor GraphGridColor;
|
||||||
QColor LineColor;
|
QColor LineColor;
|
||||||
QColor LineLimitColor;
|
QColor LineLimitColor;
|
||||||
|
QColor LineMaxUpLimitColor;
|
||||||
|
|
||||||
QTimer TimerErrRateUpdate;
|
QTimer TimerErrRateUpdate;
|
||||||
|
|
||||||
|
|
|
@ -115,7 +115,8 @@ CNetBufWithStats::CNetBufWithStats() :
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNetBufWithStats::GetErrorRates ( CVector<double>& vecErrRates,
|
void CNetBufWithStats::GetErrorRates ( CVector<double>& vecErrRates,
|
||||||
double& dLimit )
|
double& dLimit,
|
||||||
|
double& dMaxUpLimit )
|
||||||
{
|
{
|
||||||
// get all the averages of the error statistic
|
// get all the averages of the error statistic
|
||||||
vecErrRates.Init ( NUM_STAT_SIMULATION_BUFFERS );
|
vecErrRates.Init ( NUM_STAT_SIMULATION_BUFFERS );
|
||||||
|
@ -125,8 +126,9 @@ void CNetBufWithStats::GetErrorRates ( CVector<double>& vecErrRates,
|
||||||
vecErrRates[i] = ErrorRateStatistic[i].GetAverage();
|
vecErrRates[i] = ErrorRateStatistic[i].GetAverage();
|
||||||
}
|
}
|
||||||
|
|
||||||
// get the limit for decision
|
// get the limits for the decisions
|
||||||
dLimit = ERROR_RATE_BOUND;
|
dLimit = ERROR_RATE_BOUND;
|
||||||
|
dMaxUpLimit = UP_MAX_ERROR_BOUND;
|
||||||
}
|
}
|
||||||
|
|
||||||
void CNetBufWithStats::Init ( const int iNewBlockSize,
|
void CNetBufWithStats::Init ( const int iNewBlockSize,
|
||||||
|
@ -199,14 +201,17 @@ bool CNetBufWithStats::Get ( CVector<uint8_t>& vecbyData,
|
||||||
|
|
||||||
void CNetBufWithStats::UpdateAutoSetting()
|
void CNetBufWithStats::UpdateAutoSetting()
|
||||||
{
|
{
|
||||||
int iCurDecision = 0; // dummy initialization
|
int iCurDecision = 0; // dummy initialization
|
||||||
bool bDecisionFound = false;
|
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
|
// Use a specified error bound to identify the best buffer size for the
|
||||||
// current network situation. Start with the smallest buffer and
|
// current network situation. Start with the smallest buffer and
|
||||||
// test for the error rate until the rate is below the bound.
|
// 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++ )
|
for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS - 1; i++ )
|
||||||
{
|
{
|
||||||
if ( ( !bDecisionFound ) &&
|
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) --------------------------------------------
|
// Post calculation (filtering) --------------------------------------------
|
||||||
// Define different weigths for up and down direction. Up direction
|
// Define different weigths for up and down direction. Up direction
|
||||||
// filtering shall be slower than for down direction since we assume
|
// filtering shall be slower than for down direction since we assume
|
||||||
|
@ -235,9 +264,10 @@ void CNetBufWithStats::UpdateAutoSetting()
|
||||||
// adaptation.
|
// adaptation.
|
||||||
// Note that the following definitions of the weigh constants assume a block
|
// Note that the following definitions of the weigh constants assume a block
|
||||||
// size of 128 samples at a sampling rate of 48 kHz.
|
// size of 128 samples at a sampling rate of 48 kHz.
|
||||||
double dWeightUp = 0.999995;
|
double dWeightUp = 0.999995;
|
||||||
double dWeightDown = 0.9999;
|
double dWeightDown = 0.9999;
|
||||||
const double dHysteresisValue = 0.1;
|
const double dHysteresisValue = 0.1;
|
||||||
|
bool bUseFastAdaptation = false;
|
||||||
|
|
||||||
// check for initialization phase
|
// check for initialization phase
|
||||||
if ( iInitCounter > 0 )
|
if ( iInitCounter > 0 )
|
||||||
|
@ -245,6 +275,20 @@ void CNetBufWithStats::UpdateAutoSetting()
|
||||||
// decrease init counter
|
// decrease init counter
|
||||||
iInitCounter--;
|
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
|
// overwrite weigth values with lower values
|
||||||
dWeightUp = 0.9995;
|
dWeightUp = 0.9995;
|
||||||
dWeightDown = 0.999;
|
dWeightDown = 0.999;
|
||||||
|
@ -256,6 +300,22 @@ void CNetBufWithStats::UpdateAutoSetting()
|
||||||
dWeightUp,
|
dWeightUp,
|
||||||
dWeightDown );
|
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
|
// apply a hysteresis
|
||||||
iCurAutoBufferSizeSetting =
|
iCurAutoBufferSizeSetting =
|
||||||
MathUtils().DecideWithHysteresis ( dCurIIRFilterResult,
|
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
|
// blocks we have 15 s / 2.66 ms * 2 = approx. 11000
|
||||||
#define MAX_STATISTIC_COUNT 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
|
#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
|
// number of simulation network jitter buffers for evaluating the statistic
|
||||||
#define NUM_STAT_SIMULATION_BUFFERS 11
|
#define NUM_STAT_SIMULATION_BUFFERS 11
|
||||||
|
|
||||||
|
@ -407,7 +412,9 @@ public:
|
||||||
virtual bool Get ( CVector<uint8_t>& vecbyData, const int iOutSize );
|
virtual bool Get ( CVector<uint8_t>& vecbyData, const int iOutSize );
|
||||||
|
|
||||||
int GetAutoSetting() { return iCurAutoBufferSizeSetting; }
|
int GetAutoSetting() { return iCurAutoBufferSizeSetting; }
|
||||||
void GetErrorRates ( CVector<double>& vecErrRates, double& dLimit );
|
void GetErrorRates ( CVector<double>& vecErrRates,
|
||||||
|
double& dLimit,
|
||||||
|
double& dMaxUpLimit );
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
void UpdateAutoSetting();
|
void UpdateAutoSetting();
|
||||||
|
|
|
@ -136,8 +136,8 @@ Protocol.CreateChanNameMes ( ChInfo.strName );
|
||||||
int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; }
|
int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; }
|
||||||
int GetNetwFrameSize() const { return iNetwFrameSize; }
|
int GetNetwFrameSize() const { return iNetwFrameSize; }
|
||||||
|
|
||||||
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit )
|
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit, double& dMaxUpLimit )
|
||||||
{ SockBuf.GetErrorRates ( vecErrRates, dLimit ); }
|
{ SockBuf.GetErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); }
|
||||||
|
|
||||||
EAudComprType GetAudioCompressionType() { return eAudioCompressionType; }
|
EAudComprType GetAudioCompressionType() { return eAudioCompressionType; }
|
||||||
int GetNumAudioChannels() const { return iNumAudioChannels; }
|
int GetNumAudioChannels() const { return iNumAudioChannels; }
|
||||||
|
|
|
@ -275,8 +275,8 @@ public:
|
||||||
|
|
||||||
int EstimatedOverallDelay ( const int iPingTimeMs );
|
int EstimatedOverallDelay ( const int iPingTimeMs );
|
||||||
|
|
||||||
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit )
|
void GetBufErrorRates ( CVector<double>& vecErrRates, double& dLimit, double& dMaxUpLimit )
|
||||||
{ Channel.GetBufErrorRates ( vecErrRates, dLimit ); }
|
{ Channel.GetBufErrorRates ( vecErrRates, dLimit, dMaxUpLimit ); }
|
||||||
|
|
||||||
// settings
|
// settings
|
||||||
CVector<QString> vstrIPAddress;
|
CVector<QString> vstrIPAddress;
|
||||||
|
|
Loading…
Reference in a new issue