implemented auto output settings for server depending on maximum upload rate, bug fix in server, some code cleanup, new entry in server dialog table

This commit is contained in:
Volker Fischer 2009-03-10 10:47:55 +00:00
parent 4220b492d6
commit c61b9d593a
7 changed files with 174 additions and 77 deletions

View file

@ -29,7 +29,8 @@
* CChannelSet * * CChannelSet *
\******************************************************************************/ \******************************************************************************/
CChannelSet::CChannelSet ( const bool bForceLowUploadRate ) : CChannelSet::CChannelSet ( const bool bForceLowUploadRate ) :
bWriteStatusHTMLFile ( false ) bWriteStatusHTMLFile ( false ),
iUploadRateLimit ( DEF_MAX_UPLOAD_RATE_KBPS )
{ {
// enable all channels and set server flag // enable all channels and set server flag
for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
@ -244,6 +245,75 @@ int CChannelSet::GetFreeChan()
return INVALID_CHANNEL_ID; return INVALID_CHANNEL_ID;
} }
void CChannelSet::SetOutputParameters()
{
// The strategy is as follows: Change the parameters for each channel
// until the total upload rate is lower than the limit. We first set the
// audio compression from None to MS-ADPCM for each channel and if this
// is not enough, we start to increase the buffer size factor out.
bool bUploadRateIsBelowLimit = false;
const int iNumTrials = 4;
EAudComprType eCurAudComprType;
int iCurBlockSizeFact;
for ( int iCurTrialIdx = 0; iCurTrialIdx < iNumTrials; iCurTrialIdx++ )
{
switch ( iCurTrialIdx )
{
case 0:
// highest data rate
eCurAudComprType = CT_NONE;
iCurBlockSizeFact = 1;
break;
case 1:
// using other audio compression gives most reduction
eCurAudComprType = CT_MSADPCM;
iCurBlockSizeFact = 1;
break;
case 2:
// trying to use larger block size factor to further reduce rate
eCurAudComprType = CT_MSADPCM;
iCurBlockSizeFact = 2;
break;
case 3:
// trying to use larger block size factor to further reduce rate
eCurAudComprType = CT_MSADPCM;
iCurBlockSizeFact = 3;
break;
}
int iCurCh = 0;
while ( ( iCurCh < USED_NUM_CHANNELS ) && ( !bUploadRateIsBelowLimit ) )
{
if ( vecChannels[iCurCh].IsConnected() )
{
// set new parameters
vecChannels[iCurCh].SetNetwBufSizeFactOut ( iCurBlockSizeFact );
vecChannels[iCurCh].SetAudioCompressionOut ( eCurAudComprType );
// calculate and check total upload rate
int iTotalUploadRate = 0;
for ( int j = 0; j < USED_NUM_CHANNELS; j++ )
{
if ( vecChannels[j].IsConnected() )
{
// accumulate the upload rates from all channels
iTotalUploadRate += vecChannels[j].GetUploadRateKbps();
}
}
bUploadRateIsBelowLimit = ( iTotalUploadRate <= iUploadRateLimit );
}
// next channel
iCurCh++;
}
}
}
int CChannelSet::CheckAddr ( const CHostAddress& Addr ) int CChannelSet::CheckAddr ( const CHostAddress& Addr )
{ {
CHostAddress InetAddr; CHostAddress InetAddr;
@ -251,12 +321,15 @@ int CChannelSet::CheckAddr ( const CHostAddress& Addr )
// check for all possible channels if IP is already in use // check for all possible channels if IP is already in use
for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
{ {
if ( vecChannels[i].GetAddress ( InetAddr ) ) if ( vecChannels[i].IsConnected() )
{ {
// IP found, return channel number if ( vecChannels[i].GetAddress ( InetAddr ) )
if ( InetAddr == Addr )
{ {
return i; // IP found, return channel number
if ( InetAddr == Addr )
{
return i;
}
} }
} }
} }
@ -348,6 +421,9 @@ bAudioOK = true;
// requested // requested
if ( bNewChannelReserved && bAudioOK ) if ( bNewChannelReserved && bAudioOK )
{ {
// update output network parameters for all connected clients
SetOutputParameters();
// send message about new channel // send message about new channel
emit ChannelConnected ( HostAdr ); emit ChannelConnected ( HostAdr );
@ -439,9 +515,12 @@ void CChannelSet::GetBlockAllConC ( CVector<int>& vecChanID,
} }
} }
// create channel list message if requested // a channel is now disconnected, take action on it
if ( bChannelIsNowDisconnected ) if ( bChannelIsNowDisconnected )
{ {
// update output network parameters for all connected clients
SetOutputParameters();
// update channel list for all currently connected clients // update channel list for all currently connected clients
CreateAndSendChanListForAllConChannels(); CreateAndSendChanListForAllConChannels();
} }
@ -449,10 +528,11 @@ void CChannelSet::GetBlockAllConC ( CVector<int>& vecChanID,
Mutex.unlock(); // release mutex Mutex.unlock(); // release mutex
} }
void CChannelSet::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses, void CChannelSet::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
CVector<QString>& vecsName, CVector<QString>& vecsName,
CVector<int>& veciJitBufSize, CVector<int>& veciJitBufSize,
CVector<int>& veciNetwOutBlSiFact ) CVector<int>& veciNetwOutBlSiFact,
CVector<EAudComprType>& veceAudComprType )
{ {
CHostAddress InetAddr; CHostAddress InetAddr;
@ -461,6 +541,7 @@ void CChannelSet::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
vecsName.Init ( USED_NUM_CHANNELS ); vecsName.Init ( USED_NUM_CHANNELS );
veciJitBufSize.Init ( USED_NUM_CHANNELS ); veciJitBufSize.Init ( USED_NUM_CHANNELS );
veciNetwOutBlSiFact.Init ( USED_NUM_CHANNELS ); veciNetwOutBlSiFact.Init ( USED_NUM_CHANNELS );
veceAudComprType.Init ( USED_NUM_CHANNELS );
// check all possible channels // check all possible channels
for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
@ -472,6 +553,7 @@ void CChannelSet::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
vecsName[i] = vecChannels[i].GetName(); vecsName[i] = vecChannels[i].GetName();
veciJitBufSize[i] = vecChannels[i].GetSockBufSize(); veciJitBufSize[i] = vecChannels[i].GetSockBufSize();
veciNetwOutBlSiFact[i] = vecChannels[i].GetNetwBufSizeFactOut(); veciNetwOutBlSiFact[i] = vecChannels[i].GetNetwBufSizeFactOut();
veceAudComprType[i] = vecChannels[i].GetAudioCompressionOut();
} }
} }
} }
@ -948,7 +1030,6 @@ EPutDataStat CChannel::PutData ( const CVector<unsigned char>& vecbyData,
bool bIsAudioPacket = false; bool bIsAudioPacket = false;
bool bNewConnection = false; bool bNewConnection = false;
bool bReinitializeIn = false; bool bReinitializeIn = false;
bool bReinitializeOut = false;
// intermediate storage for new parameters // intermediate storage for new parameters
int iNewAudioBlockSize; int iNewAudioBlockSize;
@ -996,35 +1077,18 @@ EPutDataStat CChannel::PutData ( const CVector<unsigned char>& vecbyData,
{ {
bReinitializeIn = true; bReinitializeIn = true;
} }
// in case of a server channel, use the same audio
// compression for output as for the input
if ( bIsServer )
{
if ( GetAudioCompressionOut() != eNewAudComprType )
{
bReinitializeOut = true;
}
}
} }
} }
Mutex.unlock(); Mutex.unlock();
// actual initialization calls have to be made // actual initialization call has to be made
// outside the mutex region since they internally // outside the mutex region since it internally
// use the same mutex, too // usees the same mutex, too
if ( bReinitializeIn ) if ( bReinitializeIn )
{ {
// re-initialize to new value // re-initialize to new value
SetAudioBlockSizeAndComprIn ( SetAudioBlockSizeAndComprIn (
iNewAudioBlockSize, eNewAudComprType ); iNewAudioBlockSize, eNewAudComprType );
}
if ( bReinitializeOut )
{
SetAudioCompressionOut ( eNewAudComprType );
} }
} }

View file

@ -247,7 +247,8 @@ public:
void GetConCliParam ( CVector<CHostAddress>& vecHostAddresses, void GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
CVector<QString>& vecsName, CVector<QString>& vecsName,
CVector<int>& veciJitBufSize, CVector<int>& veciJitBufSize,
CVector<int>& veciNetwOutBlSiFact ); CVector<int>& veciNetwOutBlSiFact,
CVector<EAudComprType>& veceAudComprType );
// access functions for actual channels // access functions for actual channels
bool IsConnected ( const int iChanNum ) bool IsConnected ( const int iChanNum )
@ -270,6 +271,7 @@ protected:
void CreateAndSendChanListForThisChan ( const int iCurChanID ); void CreateAndSendChanListForThisChan ( const int iCurChanID );
void CreateAndSendChatTextForAllConChannels ( const int iCurChanID, const QString& strChatText ); void CreateAndSendChatTextForAllConChannels ( const int iCurChanID, const QString& strChatText );
void WriteHTMLChannelList(); void WriteHTMLChannelList();
void SetOutputParameters();
/* do not use the vector class since CChannel does not have appropriate /* do not use the vector class since CChannel does not have appropriate
copy constructor/operator */ copy constructor/operator */
@ -278,6 +280,8 @@ protected:
CVector<QString> vstrChatColors; CVector<QString> vstrChatColors;
int iUploadRateLimit;
// HTML file server status // HTML file server status
bool bWriteStatusHTMLFile; bool bWriteStatusHTMLFile;
QString strServerHTMLFileListName; QString strServerHTMLFileListName;

View file

@ -432,56 +432,52 @@ void CClient::UpdateSocketBufferSize()
// completely filled // completely filled
const double dHysteresis = 0.3; const double dHysteresis = 0.3;
// it seems that it is better to update the jitter buffer as soon as possible // calculate current buffer setting
// even if the value is not optimal right from the beginning
if ( 1 ) // previously -> CycleTimeVariance.IsInitialized()
{
// calculate current buffer setting
// TODO 2* seems not give optimal results, maybe use 3*? // TODO 2* seems not give optimal results, maybe use 3*?
// add .5 to "round up" -> ceil // add .5 to "round up" -> ceil
// divide by MIN_SERVER_BLOCK_DURATION_MS because this is the size of // divide by MIN_SERVER_BLOCK_DURATION_MS because this is the size of
// one block in the jitter buffer // one block in the jitter buffer
// add one block for actual network jitter
// Use worst case scenario: We add the block size of input and // Use worst case scenario: We add the block size of input and
// output. This is not required if the smaller block size is a // output. This is not required if the smaller block size is a
// multiple of the bigger size, but in the general case where // multiple of the bigger size, but in the general case where
// the block sizes do not have this relation, we require to have // the block sizes do not have this relation, we require to have
// a minimum buffer size of the sum of both sizes // a minimum buffer size of the sum of both sizes
const double dAudioBufferDurationMs = const double dAudioBufferDurationMs =
( iMonoBlockSizeSam + Channel.GetAudioBlockSizeIn() ) * 1000 / ( iMonoBlockSizeSam + Channel.GetAudioBlockSizeIn() ) * 1000 /
SYSTEM_SAMPLE_RATE; SYSTEM_SAMPLE_RATE;
const double dEstCurBufSet = ( dAudioBufferDurationMs + const double dEstCurBufSet = 1 + ( dAudioBufferDurationMs +
2 * ( CycleTimeVariance.GetStdDev() + 0.5 ) ) / 2 * ( CycleTimeVariance.GetStdDev() + 0.5 ) ) /
MIN_SERVER_BLOCK_DURATION_MS; MIN_SERVER_BLOCK_DURATION_MS;
// upper/lower hysteresis decision // upper/lower hysteresis decision
const int iUpperHystDec = LlconMath().round ( dEstCurBufSet - dHysteresis ); const int iUpperHystDec = LlconMath().round ( dEstCurBufSet - dHysteresis );
const int iLowerHystDec = LlconMath().round ( dEstCurBufSet + dHysteresis ); const int iLowerHystDec = LlconMath().round ( dEstCurBufSet + dHysteresis );
// if both decisions are equal than use the result // if both decisions are equal than use the result
if ( iUpperHystDec == iLowerHystDec ) 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 ( !( ( GetSockBufSize() == iUpperHystDec ) ||
( GetSockBufSize() == iLowerHystDec ) ) )
{ {
// set the socket buffer via the main window thread since somehow // 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() // it gives a protocol deadlock if we call the SetSocketBufSize()
// function directly // function directly.
PostWinMessage ( MS_SET_JIT_BUF_SIZE, iUpperHystDec ); 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 ( !( ( GetSockBufSize() == iUpperHystDec ) ||
( GetSockBufSize() == 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 );
}
}
} }
} }
} }

View file

@ -88,6 +88,9 @@
// default network buffer size // default network buffer size
#define DEF_NET_BUF_SIZE_NUM_BL 10 // number of blocks #define DEF_NET_BUF_SIZE_NUM_BL 10 // number of blocks
// default maximum upload rate at server (typical DSL upload for good DSL)
#define DEF_MAX_UPLOAD_RATE_KBPS 800 // kbps
// maximum number of recognized sound cards installed in the system, // maximum number of recognized sound cards installed in the system,
// definition for "no device" // definition for "no device"
#define MAX_NUMBER_SOUND_CARDS 10 #define MAX_NUMBER_SOUND_CARDS 10

View file

@ -45,7 +45,9 @@ CLlconServerDlg::CLlconServerDlg ( CServer* pNServP, QWidget* parent )
// set up list view for connected clients // set up list view for connected clients
ListViewClients->setColumnWidth ( 0, 170 ); ListViewClients->setColumnWidth ( 0, 170 );
ListViewClients->setColumnWidth ( 1, 150 ); ListViewClients->setColumnWidth ( 1, 130 );
ListViewClients->setColumnWidth ( 2, 40 );
ListViewClients->setColumnWidth ( 3, 40 );
ListViewClients->clear(); ListViewClients->clear();
// insert items in reverse order because in Windows all of them are // insert items in reverse order because in Windows all of them are
@ -85,12 +87,13 @@ void CLlconServerDlg::OnTimer()
CVector<QString> vecsName; CVector<QString> vecsName;
CVector<int> veciJitBufSize; CVector<int> veciJitBufSize;
CVector<int> veciNetwOutBlSiFact; CVector<int> veciNetwOutBlSiFact;
CVector<EAudComprType> veceAudComprType;
double dCurTiStdDev; double dCurTiStdDev;
ListViewMutex.lock(); ListViewMutex.lock();
pServer->GetConCliParam ( vecHostAddresses, vecsName, veciJitBufSize, pServer->GetConCliParam ( vecHostAddresses, vecsName, veciJitBufSize,
veciNetwOutBlSiFact ); veciNetwOutBlSiFact, veceAudComprType );
// fill list with connected clients // fill list with connected clients
for ( int i = 0; i < USED_NUM_CHANNELS; i++ ) for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
@ -114,6 +117,26 @@ void CLlconServerDlg::OnTimer()
QString().setNum ( QString().setNum (
double ( veciNetwOutBlSiFact[i] * MIN_SERVER_BLOCK_DURATION_MS ), 'f', 2 ) ); double ( veciNetwOutBlSiFact[i] * MIN_SERVER_BLOCK_DURATION_MS ), 'f', 2 ) );
// output audio compression
switch ( veceAudComprType[i] )
{
case CT_NONE:
vecpListViewItems[i]->setText ( 6, "None" );
break;
case CT_IMAADPCM:
vecpListViewItems[i]->setText ( 6, "IMA-ADPCM" );
break;
case CT_MSADPCM:
vecpListViewItems[i]->setText ( 6, "MS-ADPCM" );
break;
default:
vecpListViewItems[i]->setText ( 6, "Unknown" );
break;
}
vecpListViewItems[i]->setHidden ( false ); vecpListViewItems[i]->setHidden ( false );
} }
else else

View file

@ -5,8 +5,8 @@
<rect> <rect>
<x>0</x> <x>0</x>
<y>0</y> <y>0</y>
<width>767</width> <width>765</width>
<height>280</height> <height>315</height>
</rect> </rect>
</property> </property>
<property name="windowTitle" > <property name="windowTitle" >
@ -69,6 +69,11 @@
<string>Block Size Out</string> <string>Block Size Out</string>
</property> </property>
</column> </column>
<column>
<property name="text" >
<string>Audio Compr. Out</string>
</property>
</column>
</widget> </widget>
</item> </item>
<item> <item>

View file

@ -54,10 +54,12 @@ public:
void GetConCliParam ( CVector<CHostAddress>& vecHostAddresses, void GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
CVector<QString>& vecsName, CVector<QString>& vecsName,
CVector<int>& veciJitBufSize, CVector<int>& veciNetwOutBlSiFact ) CVector<int>& veciJitBufSize,
CVector<int>& veciNetwOutBlSiFact,
CVector<EAudComprType>& veceAudComprType )
{ {
ChannelSet.GetConCliParam ( vecHostAddresses, vecsName, ChannelSet.GetConCliParam ( vecHostAddresses, vecsName,
veciJitBufSize, veciNetwOutBlSiFact ); veciJitBufSize, veciNetwOutBlSiFact, veceAudComprType );
} }
bool GetTimingStdDev ( double& dCurTiStdDev ); bool GetTimingStdDev ( double& dCurTiStdDev );