moved the socket buffer size update from the client in the channel because it shall be also used in the server in the future, bug fix with connect/disconnect

This commit is contained in:
Volker Fischer 2011-05-22 20:40:29 +00:00
parent a770b75a06
commit b7ce6c3193
6 changed files with 159 additions and 99 deletions

View file

@ -27,12 +27,13 @@
// CChannel implementation ***************************************************** // CChannel implementation *****************************************************
CChannel::CChannel ( const bool bNIsServer ) : CChannel::CChannel ( const bool bNIsServer ) :
vecdGains ( MAX_NUM_CHANNELS, (double) 1.0 ), vecdGains ( MAX_NUM_CHANNELS, (double) 1.0 ),
bIsEnabled ( false ), bIsEnabled ( false ),
bIsServer ( bNIsServer ), bIsServer ( bNIsServer ),
bDoAutoSockBufSize ( true ),
iNetwFrameSizeFact ( FRAME_SIZE_FACTOR_PREFERRED ), iNetwFrameSizeFact ( FRAME_SIZE_FACTOR_PREFERRED ),
iNetwFrameSize ( 20 ), // must be > 0 and should be close to a valid size iNetwFrameSize ( 20 ), // must be > 0 and should be close to a valid size
iNumAudioChannels ( 1 ) // mono iNumAudioChannels ( 1 ) // mono
{ {
// initial value for connection time out counter, we calculate the total // initial value for connection time out counter, we calculate the total
// number of samples here and subtract the number of samples of the block // number of samples here and subtract the number of samples of the block
@ -172,14 +173,31 @@ bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames,
if ( ( iNewNumFrames >= MIN_NET_BUF_SIZE_NUM_BL ) && if ( ( iNewNumFrames >= MIN_NET_BUF_SIZE_NUM_BL ) &&
( iNewNumFrames <= MAX_NET_BUF_SIZE_NUM_BL ) ) ( iNewNumFrames <= MAX_NET_BUF_SIZE_NUM_BL ) )
{ {
// store new value // only apply parameter if new parameter is different from current one
iCurSockBufNumFrames = iNewNumFrames; if ( iCurSockBufNumFrames != iNewNumFrames )
{
// store new value
iCurSockBufNumFrames = 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, bPreserve ); SockBuf.Init ( iNetwFrameSize, iNewNumFrames, bPreserve );
return false; // -> no error
// TEST in case we are the client, tell the server that the size has changed
if ( !bIsServer && bPreserve )
{
// TODO this function call causes the lock up of the protocol mechanism when the
// SetSockBufNumFrames is called directly from UpdateSocketBufferSize
// -> find and fix the problem!
CreateJitBufMes ( iNewNumFrames );
}
return false; // -> no error
}
} }
return true; // set error flag return true; // set error flag
@ -549,3 +567,81 @@ int CChannel::GetUploadRateKbps()
8 /* bits per byte */ * 8 /* bits per byte */ *
SYSTEM_SAMPLE_RATE_HZ / iAudioSizeOut / 1000; SYSTEM_SAMPLE_RATE_HZ / iAudioSizeOut / 1000;
} }
// TEST
void CChannel::UpdateSocketBufferSize ( const double dAudioBufferDurationMs,
const double dLocalStdDev )
{
// just update the socket buffer size if auto setting is enabled, otherwise
// do nothing
if ( bDoAutoSockBufSize )
{
// We use the time response measurement for the automatic setting.
// Assumptions:
// - the audio interface/network jitter is assumed to be Gaussian
// - the buffer size is set to 3.3 times the standard deviation of
// the jitter (~98% of the jitter should be fit in the
// buffer)
// - introduce a hysteresis to avoid switching the buffer sizes all the
// time in case the time response measurement is close to a bound
// - only use time response measurement results if averaging buffer is
// completely filled
const double dHysteresis = 0.3;
// jitter introduced in the server by the timer implementation
// TODO remove this!
const double dServerJitterMs = 0.666666; // ms
// accumulate the standard deviations of input network stream and
// internal timer,
// add 0.5 to "round up" -> ceil,
// divide by MIN_SERVER_BLOCK_DURATION_MS because this is the size of
// one block in the jitter buffer
const double dEstCurBufSet = ( dAudioBufferDurationMs + dServerJitterMs +
3.3 * ( GetTimingStdDev() + dLocalStdDev ) ) /
SYSTEM_BLOCK_DURATION_MS_FLOAT + 0.5;
// upper/lower hysteresis decision
const int iUpperHystDec = LlconMath().round ( dEstCurBufSet - dHysteresis );
const int iLowerHystDec = LlconMath().round ( dEstCurBufSet + dHysteresis );
// if both decisions are equal than use the result
if ( iUpperHystDec == iLowerHystDec )
{
// set the socket buffer via the main window thread since somehow
// it gives a protocol deadlock if we call the SetSocketBufSize()
// function directly
// TEST
PostWinMessage ( MS_SET_JIT_BUF_SIZE, iUpperHystDec );
//SetSockBufNumFrames ( iUpperHystDec, true );
}
else
{
// we are in the middle of the decision region, use
// previous setting for determing the new decision
if ( !( ( GetSockBufNumFrames() == iUpperHystDec ) ||
( GetSockBufNumFrames() == iLowerHystDec ) ) )
{
// The old result is not near the new decision,
// use per definition the upper decision.
// Set the socket buffer via the main window thread since somehow
// it gives a protocol deadlock if we call the SetSocketBufSize()
// function directly.
// TEST
PostWinMessage ( MS_SET_JIT_BUF_SIZE, iUpperHystDec );
//SetSockBufNumFrames ( iUpperHystDec, true );
}
}
}
}

View file

@ -96,6 +96,12 @@ public:
const bool bPreserve = false ); const bool bPreserve = false );
int GetSockBufNumFrames() const { return iCurSockBufNumFrames; } int GetSockBufNumFrames() const { return iCurSockBufNumFrames; }
// TEST
void UpdateSocketBufferSize ( const double dAudioBufferDurationMs,
const double dLocalStdDev );
int GetUploadRateKbps(); int GetUploadRateKbps();
double GetTimingStdDev() { return CycleTimeVariance.GetStdDev(); } double GetTimingStdDev() { return CycleTimeVariance.GetStdDev(); }
@ -104,8 +110,13 @@ public:
void SetAudioStreamProperties ( const int iNewNetwFrameSize, void SetAudioStreamProperties ( const int iNewNetwFrameSize,
const int iNewNetwFrameSizeFact, const int iNewNetwFrameSizeFact,
const int iNewNumAudioChannels ); const int iNewNumAudioChannels );
void SetDoAutoSockBufSize ( const bool bValue ) { bDoAutoSockBufSize = bValue; }
bool GetDoAutoSockBufSize() const { return bDoAutoSockBufSize; }
int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; } int GetNetwFrameSizeFact() const { return iNetwFrameSizeFact; }
int GetNetwFrameSize() const { return iNetwFrameSize; } int GetNetwFrameSize() const { return iNetwFrameSize; }
int GetNumAudioChannels() const { return iNumAudioChannels; } int GetNumAudioChannels() const { return iNumAudioChannels; }
// network protocol interface // network protocol interface
@ -160,6 +171,7 @@ protected:
bool bIsEnabled; bool bIsEnabled;
bool bIsServer; bool bIsServer;
bool bDoAutoSockBufSize;
int iNetwFrameSizeFact; int iNetwFrameSizeFact;
int iNetwFrameSize; int iNetwFrameSize;

View file

@ -30,7 +30,6 @@ CClient::CClient ( const quint16 iPortNumber ) :
vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ), vstrIPAddress ( MAX_NUM_SERVER_ADDR_ITEMS, "" ),
strName ( "" ), strName ( "" ),
Channel ( false ), /* we need a client channel -> "false" */ Channel ( false ), /* we need a client channel -> "false" */
bDoAutoSockBufSize ( true ),
iCeltNumCodedBytes ( CELT_NUM_BYTES_MONO_NORMAL_QUALITY ), iCeltNumCodedBytes ( CELT_NUM_BYTES_MONO_NORMAL_QUALITY ),
bCeltDoHighQuality ( false ), bCeltDoHighQuality ( false ),
bUseStereo ( false ), bUseStereo ( false ),
@ -428,7 +427,12 @@ void CClient::Stop()
QTime DieTime = QTime::currentTime().addMSecs ( 100 ); QTime DieTime = QTime::currentTime().addMSecs ( 100 );
while ( QTime::currentTime() < DieTime ) while ( QTime::currentTime() < DieTime )
{ {
QCoreApplication::processEvents ( QEventLoop::AllEvents, 100 ); // exclude user input events because if we use AllEvents, it happens
// that if the user initiates a connection and disconnection quickly
// (e.g. quickly pressing enter five times), the software can get into
// an unknown state
QCoreApplication::processEvents (
QEventLoop::ExcludeUserInputEvents, 100 );
} }
// Send disconnect message to server (Since we disable our protocol // Send disconnect message to server (Since we disable our protocol
@ -877,75 +881,18 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
vecsStereoSndCrd.Reset ( 0 ); vecsStereoSndCrd.Reset ( 0 );
} }
// update response time measurement and socket buffer size // update response time measurement
CycleTimeVariance.Update(); CycleTimeVariance.Update();
UpdateSocketBufferSize();
}
void CClient::UpdateSocketBufferSize() // calculate current buffer setting
{ const double dAudioBufferDurationMs =
// just update the socket buffer size if auto setting is enabled, otherwise ( GetSndCrdActualMonoBlSize() +
// do nothing GetSndCrdConvBufAdditionalDelayMonoBlSize() ) *
if ( bDoAutoSockBufSize ) 1000 / SYSTEM_SAMPLE_RATE_HZ;
{
// We use the time response measurement for the automatic setting.
// Assumptions:
// - the audio interface/network jitter is assumed to be Gaussian
// - the buffer size is set to 3.3 times the standard deviation of
// the jitter (~98% of the jitter should be fit in the
// buffer)
// - introduce a hysteresis to avoid switching the buffer sizes all the
// time in case the time response measurement is close to a bound
// - only use time response measurement results if averaging buffer is
// completely filled
const double dHysteresis = 0.3;
// calculate current buffer setting // update and socket buffer size
const double dAudioBufferDurationMs = Channel.UpdateSocketBufferSize ( dAudioBufferDurationMs,
( GetSndCrdActualMonoBlSize() + CycleTimeVariance.GetStdDev() );
GetSndCrdConvBufAdditionalDelayMonoBlSize() ) *
1000 / SYSTEM_SAMPLE_RATE_HZ;
// jitter introduced in the server by the timer implementation
const double dServerJitterMs = 0.666666; // ms
// accumulate the standard deviations of input network stream and
// internal timer,
// add 0.5 to "round up" -> ceil,
// divide by MIN_SERVER_BLOCK_DURATION_MS because this is the size of
// one block in the jitter buffer
const double dEstCurBufSet = ( dAudioBufferDurationMs + dServerJitterMs +
3.3 * ( Channel.GetTimingStdDev() + CycleTimeVariance.GetStdDev() ) ) /
SYSTEM_BLOCK_DURATION_MS_FLOAT + 0.5;
// upper/lower hysteresis decision
const int iUpperHystDec = LlconMath().round ( dEstCurBufSet - dHysteresis );
const int iLowerHystDec = LlconMath().round ( dEstCurBufSet + dHysteresis );
// if both decisions are equal than use the result
if ( iUpperHystDec == iLowerHystDec )
{
// set the socket buffer via the main window thread since somehow
// it gives a protocol deadlock if we call the SetSocketBufSize()
// function directly
PostWinMessage ( MS_SET_JIT_BUF_SIZE, iUpperHystDec );
}
else
{
// we are in the middle of the decision region, use
// previous setting for determing the new decision
if ( !( ( GetSockBufNumFrames() == iUpperHystDec ) ||
( GetSockBufNumFrames() == iLowerHystDec ) ) )
{
// The old result is not near the new decision,
// use per definition the upper decision.
// Set the socket buffer via the main window thread since somehow
// it gives a protocol deadlock if we call the SetSocketBufSize()
// function directly.
PostWinMessage ( MS_SET_JIT_BUF_SIZE, iUpperHystDec );
}
}
}
} }
int CClient::EstimatedOverallDelay ( const int iPingTimeMs ) int CClient::EstimatedOverallDelay ( const int iPingTimeMs )

View file

@ -132,21 +132,24 @@ public:
AudioReverbR.Clear(); AudioReverbR.Clear();
} }
void SetDoAutoSockBufSize ( const bool bValue ) { bDoAutoSockBufSize = bValue; } void SetDoAutoSockBufSize ( const bool bValue )
bool GetDoAutoSockBufSize() const { return bDoAutoSockBufSize; } { Channel.SetDoAutoSockBufSize ( bValue ); }
bool GetDoAutoSockBufSize() const { return Channel.GetDoAutoSockBufSize(); }
void SetSockBufNumFrames ( const int iNumBlocks, void SetSockBufNumFrames ( const int iNumBlocks,
const bool bPreserve = false ) const bool bPreserve = false )
{ {
// only change parameter if new parameter is different from current one // set the new socket size (number of frames)
if ( Channel.GetSockBufNumFrames() != iNumBlocks ) if ( !Channel.SetSockBufNumFrames ( iNumBlocks, bPreserve ) )
{ {
// set the new socket size (number of frames) // if setting of socket buffer size was successful,
if ( !Channel.SetSockBufNumFrames ( iNumBlocks, bPreserve ) ) // tell the server that the size has changed
{
// if setting of socket buffer size was successful, // TEST is done in channel now
// tell the server that size has changed // Channel.CreateJitBufMes ( iNumBlocks );
Channel.CreateJitBufMes ( iNumBlocks );
}
} }
} }
int GetSockBufNumFrames() { return Channel.GetSockBufNumFrames(); } int GetSockBufNumFrames() { return Channel.GetSockBufNumFrames(); }
@ -245,20 +248,18 @@ public:
protected: protected:
// callback function must be static, otherwise it does not work // callback function must be static, otherwise it does not work
static void AudioCallback ( CVector<short>& psData, void* arg ); static void AudioCallback ( CVector<short>& psData, void* arg );
void Init(); void Init();
void ProcessSndCrdAudioData ( CVector<short>& vecsStereoSndCrd ); void ProcessSndCrdAudioData ( CVector<short>& vecsStereoSndCrd );
void ProcessAudioDataIntern ( CVector<short>& vecsStereoSndCrd ); void ProcessAudioDataIntern ( CVector<short>& vecsStereoSndCrd );
void UpdateSocketBufferSize();
int PreparePingMessage(); int PreparePingMessage();
int EvaluatePingMessage ( const int iMs ); int EvaluatePingMessage ( const int iMs );
// only one channel is needed for client application // only one channel is needed for client application
CChannel Channel; CChannel Channel;
CProtocol ConnLessProtocol; CProtocol ConnLessProtocol;
bool bDoAutoSockBufSize;
// audio encoder/decoder // audio encoder/decoder
CELTMode* CeltModeMono; CELTMode* CeltModeMono;

View file

@ -79,6 +79,9 @@ CConnectDlg::CConnectDlg ( QWidget* parent, Qt::WindowFlags f )
lvwServers->setColumnWidth ( 3, 130 ); lvwServers->setColumnWidth ( 3, 130 );
lvwServers->clear(); lvwServers->clear();
// make sure the connect button has the focus
butConnect->setFocus();
// Connections ------------------------------------------------------------- // Connections -------------------------------------------------------------
// list view // list view

View file

@ -140,10 +140,11 @@ void CSocket::OnDataReceived()
// TEST old code -> to be removed because this is not working!!! // TEST old code -> to be removed because this is not working!!!
/*
pChannel->SetEnable ( true ); pChannel->SetEnable ( true );
pChannel->CreateAndImmSendDisconnectionMes(); pChannel->CreateAndImmSendDisconnectionMes();
pChannel->SetEnable ( false ); pChannel->SetEnable ( false );
*/
// TODO this does not work because for a connected channel at the server, no // TODO this does not work because for a connected channel at the server, no
// connection less protocol messages are accepted // connection less protocol messages are accepted