bug fix in server, added simulation mode in buffer base class, added deactivated test code for simulation buffer statistics, avoid audio drop outs when the jitter buffer size is changed

This commit is contained in:
Volker Fischer 2011-05-17 15:39:33 +00:00
parent 1a99b76365
commit 56f528b13e
11 changed files with 326 additions and 128 deletions

View file

@ -30,16 +30,20 @@
/* Network buffer implementation **********************************************/ /* Network buffer implementation **********************************************/
void CNetBuf::Init ( const int iNewBlockSize, void CNetBuf::Init ( const int iNewBlockSize,
const int iNewNumBlocks ) const int iNewNumBlocks,
const bool bPreserve )
{ {
// store block size value // store block size value
iBlockSize = iNewBlockSize; iBlockSize = iNewBlockSize;
// total size -> size of one block times the number of blocks // total size -> size of one block times the number of blocks
CBufferBase<uint8_t>::Init ( iNewBlockSize * iNewNumBlocks ); CBufferBase<uint8_t>::Init ( iNewBlockSize * iNewNumBlocks, bPreserve );
// use the "get" flag to make sure the buffer is cleared // use the "get" flag to make sure the buffer is cleared
if ( !bPreserve )
{
Clear ( CT_GET ); Clear ( CT_GET );
}
} }
bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData, bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
@ -180,7 +184,10 @@ void CNetBuf::Clear ( const EClearType eClearType )
if ( eClearType == CT_GET ) if ( eClearType == CT_GET )
{ {
// clear buffer since we had a buffer underrun // clear buffer since we had a buffer underrun
if ( !bIsSimulation )
{
vecMemory.Reset ( 0 ); vecMemory.Reset ( 0 );
}
// reset buffer pointers so that they are at maximum distance after // reset buffer pointers so that they are at maximum distance after
// the get operation (assign new fill level value to the get pointer) // the get operation (assign new fill level value to the get pointer)
@ -233,14 +240,48 @@ void CNetBuf::Clear ( const EClearType eClearType )
/* Network buffer with statistic calculations implementation ******************/ /* Network buffer with statistic calculations implementation ******************/
/*
CNetBufWithStats::CNetBufWithStats() :
CNetBuf ( false ) // base class init: no simulation mode
{
// define the sizes of the simulation buffers,
// must be NUM_STAT_SIMULATION_BUFFERS elements!
viBufSizesForSim[0] = 2;
viBufSizesForSim[1] = 3;
viBufSizesForSim[2] = 4;
viBufSizesForSim[3] = 5;
viBufSizesForSim[4] = 6;
viBufSizesForSim[5] = 7;
viBufSizesForSim[6] = 8;
viBufSizesForSim[7] = 10;
viBufSizesForSim[8] = 12;
// set all simulation buffers in simulation mode
for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
{
SimulationBuffer[i].SetIsSimulation ( true );
}
}
void CNetBufWithStats::Init ( const int iNewBlockSize, void CNetBufWithStats::Init ( const int iNewBlockSize,
const int iNewNumBlocks ) const int iNewNumBlocks,
const bool bPreserve )
{ {
// call base class Init // call base class Init
CNetBuf::Init ( iNewBlockSize, iNewNumBlocks ); CNetBuf::Init ( iNewBlockSize, iNewNumBlocks, bPreserve );
// init statistic // inits for statistics calculation
ErrorRateStatistic.Init ( MAX_STATISTIC_COUNT ); if ( !bPreserve )
{
for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
{
// init simulation buffers with the correct size
SimulationBuffer[i].Init ( iNewBlockSize, viBufSizesForSim[i] );
// init statistics
ErrorRateStatistic[i].Init ( MAX_STATISTIC_COUNT );
}
}
} }
bool CNetBufWithStats::Put ( const CVector<uint8_t>& vecbyData, bool CNetBufWithStats::Put ( const CVector<uint8_t>& vecbyData,
@ -249,8 +290,12 @@ bool CNetBufWithStats::Put ( const CVector<uint8_t>& vecbyData,
// call base class Put // call base class Put
const bool bPutOK = CNetBuf::Put ( vecbyData, iInSize ); const bool bPutOK = CNetBuf::Put ( vecbyData, iInSize );
// update statistic // update statistics calculations
ErrorRateStatistic.Update ( !bPutOK ); for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
{
ErrorRateStatistic[i].Update (
!SimulationBuffer[i].Put ( vecbyData, iInSize ) );
}
return bPutOK; return bPutOK;
} }
@ -260,8 +305,31 @@ bool CNetBufWithStats::Get ( CVector<uint8_t>& vecbyData )
// call base class Get // call base class Get
const bool bGetOK = CNetBuf::Get ( vecbyData ); const bool bGetOK = CNetBuf::Get ( vecbyData );
// update statistic // update statistics calculations
ErrorRateStatistic.Update ( !bGetOK ); for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
{
ErrorRateStatistic[i].Update (
!SimulationBuffer[i].Get ( vecbyData ) );
}
return bGetOK; return bGetOK;
} }
// TEST for debugging
void CNetBufWithStats::StoreAllSimAverages()
{
FILE* pFile = fopen ( "c:\\temp\\test.dat", "w" );
for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS - 1; i++ )
{
fprintf ( pFile, "%e, ", ErrorRateStatistic[i].GetAverage() );
}
fprintf ( pFile, "%e", ErrorRateStatistic[NUM_STAT_SIMULATION_BUFFERS - 1].GetAverage() );
fprintf ( pFile, "\n" );
fclose ( pFile );
// scilab:
// close;x=read('c:/temp/test.dat',-1,9);plot2d( [2, 3, 4, 5, 6, 7, 8, 10, 12], x, style=-1 , logflag = 'nl');plot2d([2 12],[1 1]*0.01);plot2d([2 12],[1 1]*0.005);x
}
*/

View file

@ -34,28 +34,161 @@
// blocks we have 30 s / 2.33 ms * 2 = 25714 // blocks we have 30 s / 2.33 ms * 2 = 25714
#define MAX_STATISTIC_COUNT 25714 #define MAX_STATISTIC_COUNT 25714
// number of simulation network jitter buffers for evaluating the statistic
#define NUM_STAT_SIMULATION_BUFFERS 9
/* Classes ********************************************************************/ /* Classes ********************************************************************/
// Buffer base class ----------------------------------------------------------- // Buffer base class -----------------------------------------------------------
template<class TData> class CBufferBase template<class TData> class CBufferBase
{ {
public: public:
virtual void Init ( const int iNewMemSize ) CBufferBase ( const bool bNIsSim = false ) :
{ bIsSimulation ( bNIsSim ), bIsInitialized ( false ) {}
// store total memory size value
iMemSize = iNewMemSize;
// allocate memory for actual data buffer void SetIsSimulation ( const bool bNIsSim ) { bIsSimulation = bNIsSim; }
virtual void Init ( const int iNewMemSize,
const bool bPreserve = false )
{
// in simulation mode the size is not changed during operation -> we do
// not have to implement special code for this case
// only enter the "preserve" branch, if object was already initialized
if ( bPreserve && ( !bIsSimulation ) && bIsInitialized )
{
// copy old data in new vector using get pointer as zero per
// definition
int iCurPos;
// copy current data in temporary vector
CVector<TData> vecTempMemory ( vecMemory );
// resize actual buffer memory
vecMemory.Init ( iNewMemSize ); vecMemory.Init ( iNewMemSize );
// get maximum number of data to be copied
int iCopyLen = GetAvailData();
if ( iCopyLen > iNewMemSize )
{
iCopyLen = iNewMemSize;
}
// set correct buffer state
if ( iCopyLen >= iNewMemSize )
{
eBufState = CBufferBase<TData>::BS_FULL;
}
else
{
if ( iCopyLen == 0 )
{
eBufState = CBufferBase<TData>::BS_EMPTY;
}
else
{
eBufState = CBufferBase<TData>::BS_OK;
}
}
if ( iGetPos < iPutPos )
{
// "get" position is before "put" position -> no wrap around
for ( iCurPos = 0; iCurPos < iCopyLen; iCurPos++ )
{
vecMemory[iCurPos] = vecTempMemory[iGetPos + iCurPos];
}
// update put pointer
if ( eBufState == CBufferBase<TData>::BS_FULL )
{
iPutPos = 0;
}
else
{
iPutPos -= iGetPos;
}
}
else
{
// "put" position is before "get" position -> wrap around
bool bEnoughSpaceForSecondPart = true;
int iFirstPartLen = iMemSize - iGetPos;
// check that first copy length is not larger then new memory
if ( iFirstPartLen > iCopyLen )
{
iFirstPartLen = iCopyLen;
bEnoughSpaceForSecondPart = false;
}
for ( iCurPos = 0; iCurPos < iFirstPartLen; iCurPos++ )
{
vecMemory[iCurPos] = vecTempMemory[iGetPos + iCurPos];
}
if ( bEnoughSpaceForSecondPart )
{
// calculate remaining copy length
const int iRemainingCopyLen = iCopyLen - iFirstPartLen;
// perform copying of second part
for ( iCurPos = 0; iCurPos < iRemainingCopyLen; iCurPos++ )
{
vecMemory[iCurPos + iFirstPartLen] =
vecTempMemory[iCurPos];
}
}
// update put pointer
if ( eBufState == CBufferBase<TData>::BS_FULL )
{
iPutPos = 0;
}
else
{
iPutPos += iFirstPartLen;
}
}
// update get position -> zero per definition
iGetPos = 0;
}
else
{
// allocate memory for actual data buffer
if ( !bIsSimulation )
{
vecMemory.Init ( iNewMemSize );
}
// init buffer pointers and buffer state (empty buffer) // init buffer pointers and buffer state (empty buffer)
iGetPos = 0; iGetPos = 0;
iPutPos = 0; iPutPos = 0;
eBufState = CBufferBase<TData>::BS_EMPTY; eBufState = CBufferBase<TData>::BS_EMPTY;
} }
// store total memory size value
iMemSize = iNewMemSize;
// set initialized flag
bIsInitialized = true;
}
virtual bool Put ( const CVector<TData>& vecData, virtual bool Put ( const CVector<TData>& vecData,
const int iInSize ) const int iInSize )
{
if ( bIsSimulation )
{
// in this simulation only the buffer pointers and the buffer state
// is updated, no actual data is transferred
iPutPos += iInSize;
if ( iPutPos >= iMemSize )
{
iPutPos -= iMemSize;
}
}
else
{ {
// copy new data in internal buffer // copy new data in internal buffer
int iCurPos = 0; int iCurPos = 0;
@ -84,6 +217,7 @@ public:
vecMemory[iPutPos++] = vecData[iCurPos++]; vecMemory[iPutPos++] = vecData[iCurPos++];
} }
} }
}
// set buffer state flag // set buffer state flag
if ( iPutPos == iGetPos ) if ( iPutPos == iGetPos )
@ -103,6 +237,18 @@ public:
// get size of data to be get from the buffer // get size of data to be get from the buffer
const int iInSize = vecData.Size(); const int iInSize = vecData.Size();
if ( bIsSimulation )
{
// in this simulation only the buffer pointers and the buffer state
// is updated, no actual data is transferred
iGetPos += iInSize;
if ( iGetPos >= iMemSize )
{
iGetPos -= iMemSize;
}
}
else
{
// copy data from internal buffer in output buffer // copy data from internal buffer in output buffer
int iCurPos = 0; int iCurPos = 0;
if ( iGetPos + iInSize > iMemSize ) if ( iGetPos + iInSize > iMemSize )
@ -130,6 +276,7 @@ public:
vecData[iCurPos++] = vecMemory[iGetPos++]; vecData[iCurPos++] = vecMemory[iGetPos++];
} }
} }
}
// set buffer state flag // set buffer state flag
if ( iPutPos == iGetPos ) if ( iPutPos == iGetPos )
@ -189,52 +336,13 @@ public:
protected: protected:
enum EBufState { BS_OK, BS_FULL, BS_EMPTY }; enum EBufState { BS_OK, BS_FULL, BS_EMPTY };
void PutSimulation ( const int iInSize )
{
// in this simulation only the buffer pointers and the buffer state
// is updated, no actual data is transferred
iPutPos += iInSize;
if ( iPutPos >= iMemSize )
{
iPutPos -= iMemSize;
}
// set buffer state flag
if ( iPutPos == iGetPos )
{
eBufState = CBufferBase<TData>::BS_FULL;
}
else
{
eBufState = CBufferBase<TData>::BS_OK;
}
}
void GetSimulation ( const int iInSize )
{
// in this simulation only the buffer pointers and the buffer state
// is updated, no actual data is transferred
iGetPos += iInSize;
if ( iGetPos >= iMemSize )
{
iGetPos -= iMemSize;
}
// set buffer state flag
if ( iPutPos == iGetPos )
{
eBufState = CBufferBase<TData>::BS_EMPTY;
}
else
{
eBufState = CBufferBase<TData>::BS_OK;
}
}
CVector<TData> vecMemory; CVector<TData> vecMemory;
int iMemSize; int iMemSize;
int iGetPos, iPutPos; int iGetPos;
int iPutPos;
EBufState eBufState; EBufState eBufState;
bool bIsSimulation;
bool bIsInitialized;
}; };
@ -242,7 +350,13 @@ protected:
class CNetBuf : public CBufferBase<uint8_t> class CNetBuf : public CBufferBase<uint8_t>
{ {
public: public:
virtual void Init ( const int iNewBlockSize, const int iNewNumBlocks ); CNetBuf ( const bool bNewIsSim = false ) :
CBufferBase<uint8_t> ( bNewIsSim ) {}
virtual void Init ( const int iNewBlockSize,
const int iNewNumBlocks,
const bool bPreserve = false );
int GetSize() { return iMemSize / iBlockSize; } int GetSize() { return iMemSize / iBlockSize; }
virtual bool Put ( const CVector<uint8_t>& vecbyData, const int iInSize ); virtual bool Put ( const CVector<uint8_t>& vecbyData, const int iInSize );
@ -258,21 +372,37 @@ protected:
}; };
/*
// This is a test class which provides statistic for a certain number of
// simulation buffers -> was intended to improve the auto jitter buffer
// setting but we stick to the old auto mechanism because of some draw backs
// of the simulated jitter buffers like that if burst errors occur, we would
// have to exclude it to not to effect the error rate too much and others...
// Network buffer (jitter buffer) with statistic calculations ------------------ // Network buffer (jitter buffer) with statistic calculations ------------------
class CNetBufWithStats : public CNetBuf class CNetBufWithStats : public CNetBuf
{ {
public: public:
virtual void Init ( const int iNewBlockSize, const int iNewNumBlocks ); CNetBufWithStats();
virtual void Init ( const int iNewBlockSize,
const int iNewNumBlocks,
const bool bPreserve = false );
virtual bool Put ( const CVector<uint8_t>& vecbyData, const int iInSize ); virtual bool Put ( const CVector<uint8_t>& vecbyData, const int iInSize );
virtual bool Get ( CVector<uint8_t>& vecbyData ); virtual bool Get ( CVector<uint8_t>& vecbyData );
double GetErrorRate() { return ErrorRateStatistic.GetAverage(); } // TEST
void StoreAllSimAverages();
protected: protected:
// statistic // statistic (do not use the vector class since the classes do not have
CErrorRate ErrorRateStatistic; // appropriate copy constructor/operator)
CErrorRate ErrorRateStatistic[NUM_STAT_SIMULATION_BUFFERS];
CNetBuf SimulationBuffer[NUM_STAT_SIMULATION_BUFFERS];
int viBufSizesForSim[NUM_STAT_SIMULATION_BUFFERS];
}; };
*/
// Conversion buffer (very simple buffer) -------------------------------------- // Conversion buffer (very simple buffer) --------------------------------------

View file

@ -163,7 +163,8 @@ void CChannel::SetAudioStreamProperties ( const int iNewNetwFrameSize,
CreateNetTranspPropsMessFromCurrentSettings(); CreateNetTranspPropsMessFromCurrentSettings();
} }
bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames ) bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames,
const bool bPreserve )
{ {
QMutexLocker locker ( &Mutex ); // this operation must be done with mutex QMutexLocker locker ( &Mutex ); // this operation must be done with mutex
@ -176,7 +177,7 @@ bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames )
// the network block size is a multiple of the minimum network // the network block size is a multiple of the minimum network
// block size // block size
SockBuf.Init ( iNetwFrameSize, iNewNumFrames ); SockBuf.Init ( iNetwFrameSize, iNewNumFrames, bPreserve );
return false; // -> no error return false; // -> no error
} }
@ -259,7 +260,7 @@ void CChannel::OnSendProtMessage ( CVector<uint8_t> vecMessage )
void CChannel::OnJittBufSizeChange ( int iNewJitBufSize ) void CChannel::OnJittBufSizeChange ( int iNewJitBufSize )
{ {
SetSockBufNumFrames ( iNewJitBufSize ); SetSockBufNumFrames ( iNewJitBufSize, true );
} }
void CChannel::OnChangeChanGain ( int iChanID, double dNewGain ) void CChannel::OnChangeChanGain ( int iChanID, double dNewGain )

View file

@ -91,7 +91,8 @@ public:
void SetRemoteChanGain ( const int iId, const double dGain ) void SetRemoteChanGain ( const int iId, const double dGain )
{ Protocol.CreateChanGainMes ( iId, dGain ); } { Protocol.CreateChanGainMes ( iId, dGain ); }
bool SetSockBufNumFrames ( const int iNewNumFrames ); bool SetSockBufNumFrames ( const int iNewNumFrames,
const bool bPreserve = false );
int GetSockBufNumFrames() const { return iCurSockBufNumFrames; } int GetSockBufNumFrames() const { return iCurSockBufNumFrames; }
int GetUploadRateKbps(); int GetUploadRateKbps();
@ -106,8 +107,6 @@ public:
int GetNetwFrameSize() const { return iNetwFrameSize; } int GetNetwFrameSize() const { return iNetwFrameSize; }
int GetNumAudioChannels() const { return iNumAudioChannels; } int GetNumAudioChannels() const { return iNumAudioChannels; }
double GetJitterBufferErrorRate() { return SockBuf.GetErrorRate(); }
// network protocol interface // network protocol interface
void CreateJitBufMes ( const int iJitBufSize ) void CreateJitBufMes ( const int iJitBufSize )
{ {
@ -143,7 +142,7 @@ protected:
CVector<double> vecdGains; CVector<double> vecdGains;
// network jitter-buffer // network jitter-buffer
CNetBufWithStats SockBuf; CNetBuf SockBuf;
int iCurSockBufNumFrames; int iCurSockBufNumFrames;
CCycleTimeVariance CycleTimeVariance; CCycleTimeVariance CycleTimeVariance;

View file

@ -421,11 +421,11 @@ void CClient::Stop()
// stop audio interface // stop audio interface
Sound.Stop(); Sound.Stop();
// wait for approx. 300 ms to make sure no audio packet is still in the // wait for approx. 100 ms to make sure no audio packet is still in the
// network queue causing the channel to be reconnected right after having // network queue causing the channel to be reconnected right after having
// received the disconnect message (seems not to gain much, disconnect is // received the disconnect message (seems not to gain much, disconnect is
// still not working reliably) // still not working reliably)
QTime dieTime = QTime::currentTime().addMSecs ( 300 ); QTime dieTime = QTime::currentTime().addMSecs ( 100 );
while ( QTime::currentTime() < dieTime ) while ( QTime::currentTime() < dieTime )
{ {
QCoreApplication::processEvents ( QEventLoop::AllEvents, 100 ); QCoreApplication::processEvents ( QEventLoop::AllEvents, 100 );

View file

@ -134,13 +134,14 @@ public:
void SetDoAutoSockBufSize ( const bool bValue ) { bDoAutoSockBufSize = bValue; } void SetDoAutoSockBufSize ( const bool bValue ) { bDoAutoSockBufSize = bValue; }
bool GetDoAutoSockBufSize() const { return bDoAutoSockBufSize; } bool GetDoAutoSockBufSize() const { return bDoAutoSockBufSize; }
void SetSockBufNumFrames ( const int iNumBlocks ) void SetSockBufNumFrames ( const int iNumBlocks,
const bool bPreserve = false )
{ {
// only change parameter if new parameter is different from current one // only change parameter if new parameter is different from current one
if ( Channel.GetSockBufNumFrames() != iNumBlocks ) if ( Channel.GetSockBufNumFrames() != iNumBlocks )
{ {
// set the new socket size (number of frames) // set the new socket size (number of frames)
if ( !Channel.SetSockBufNumFrames ( iNumBlocks ) ) if ( !Channel.SetSockBufNumFrames ( iNumBlocks, bPreserve ) )
{ {
// if setting of socket buffer size was successful, // if setting of socket buffer size was successful,
// tell the server that size has changed // tell the server that size has changed

View file

@ -546,7 +546,7 @@ void CClientSettingsDlg::OnDriverSetupClicked()
void CClientSettingsDlg::OnNetBufValueChanged ( int value ) void CClientSettingsDlg::OnNetBufValueChanged ( int value )
{ {
pClient->SetSockBufNumFrames ( value ); pClient->SetSockBufNumFrames ( value, true );
UpdateJitterBufferFrame(); UpdateJitterBufferFrame();
} }

View file

@ -957,7 +957,7 @@ void CLlconClientDlg::customEvent ( QEvent* Event )
break; break;
case MS_SET_JIT_BUF_SIZE: case MS_SET_JIT_BUF_SIZE:
pClient->SetSockBufNumFrames ( iStatus ); pClient->SetSockBufNumFrames ( iStatus, true );
break; break;
} }

View file

@ -225,8 +225,6 @@ CServer::CServer ( const int iNewNumChan,
vstrChatColors[4] = "maroon"; vstrChatColors[4] = "maroon";
vstrChatColors[5] = "coral"; vstrChatColors[5] = "coral";
vecsSendData.Init ( SYSTEM_FRAME_SIZE_SAMPLES );
// init moving average buffer for response time evaluation // init moving average buffer for response time evaluation
CycleTimeVariance.Init ( SYSTEM_FRAME_SIZE_SAMPLES, CycleTimeVariance.Init ( SYSTEM_FRAME_SIZE_SAMPLES,
SYSTEM_SAMPLE_RATE_HZ, TIME_MOV_AV_RESPONSE_SECONDS ); SYSTEM_SAMPLE_RATE_HZ, TIME_MOV_AV_RESPONSE_SECONDS );
@ -598,10 +596,10 @@ void CServer::OnTimer()
// generate a sparate mix for each channel // generate a sparate mix for each channel
// actual processing of audio data -> mix // actual processing of audio data -> mix
vecsSendData = ProcessData ( i, CVector<short> vecsSendData ( ProcessData ( i,
vecvecsData, vecvecsData,
vecvecdGains[i], vecvecdGains[i],
vecNumAudioChannels ); vecNumAudioChannels ) );
// get current number of CELT coded bytes // get current number of CELT coded bytes
const int iCeltNumCodedBytes = const int iCeltNumCodedBytes =

View file

@ -228,7 +228,6 @@ protected:
QString strServerNameWithPort; QString strServerNameWithPort;
CHighPrecisionTimer HighPrecisionTimer; CHighPrecisionTimer HighPrecisionTimer;
CVector<short> vecsSendData;
// server list // server list
CServerListManager ServerListManager; CServerListManager ServerListManager;

View file

@ -467,8 +467,10 @@ bool LlconNetwUtil::ParseNetworkAddress ( QString strAddress,
/******************************************************************************\ /******************************************************************************\
* Global Functions Implementation * * Global Functions Implementation *
\******************************************************************************/ \******************************************************************************/
void DebugError ( const QString& pchErDescr, const QString& pchPar1Descr, void DebugError ( const QString& pchErDescr,
const double dPar1, const QString& pchPar2Descr, const QString& pchPar1Descr,
const double dPar1,
const QString& pchPar2Descr,
const double dPar2 ) const double dPar2 )
{ {
QFile File ( "DebugError.dat" ); QFile File ( "DebugError.dat" );