avoid allocating memory in the server real-time processing routine

This commit is contained in:
Volker Fischer 2014-01-12 09:48:49 +00:00
parent 7ee9fc2a67
commit 2c443c7aef
7 changed files with 133 additions and 125 deletions

View File

@ -64,28 +64,26 @@ bool CNetBuf::Put ( const CVector<uint8_t>& vecbyData,
return bPutOK;
}
bool CNetBuf::Get ( CVector<uint8_t>& vecbyData )
bool CNetBuf::Get ( CVector<uint8_t>& vecbyData,
const int iOutSize )
{
bool bGetOK = true; // init return value
// get size of data to be get from the buffer
const int iInSize = vecbyData.Size();
// check size
if ( ( iInSize == 0 ) || ( iInSize != iBlockSize ) )
if ( ( iOutSize == 0 ) || ( iOutSize != iBlockSize ) )
{
return false;
}
// check if there is not enough data available
if ( GetAvailData() < iInSize )
if ( GetAvailData() < iOutSize )
{
return false;
}
// copy data from internal buffer in output buffer (implemented in base
// class)
CBufferBase<uint8_t>::Get ( vecbyData );
CBufferBase<uint8_t>::Get ( vecbyData, iOutSize );
return bGetOK;
}
@ -180,16 +178,17 @@ bool CNetBufWithStats::Put ( const CVector<uint8_t>& vecbyData,
return bPutOK;
}
bool CNetBufWithStats::Get ( CVector<uint8_t>& vecbyData )
bool CNetBufWithStats::Get ( CVector<uint8_t>& vecbyData,
const int iOutSize )
{
// call base class Get
const bool bGetOK = CNetBuf::Get ( vecbyData );
const bool bGetOK = CNetBuf::Get ( vecbyData, iOutSize );
// update statistics calculations
for ( int i = 0; i < NUM_STAT_SIMULATION_BUFFERS; i++ )
{
ErrorRateStatistic[i].Update (
!SimulationBuffer[i].Get ( vecbyData ) );
!SimulationBuffer[i].Get ( vecbyData, iOutSize ) );
}
// update auto setting

View File

@ -72,6 +72,7 @@ public:
// get maximum number of data to be copied
int iCopyLen = GetAvailData();
if ( iCopyLen > iNewMemSize )
{
iCopyLen = iNewMemSize;
@ -176,6 +177,7 @@ public:
// 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;
@ -185,6 +187,7 @@ public:
{
// copy new data in internal buffer
int iCurPos = 0;
if ( iPutPos + iInSize > iMemSize )
{
// remaining space size for second block
@ -233,16 +236,15 @@ public:
return true; // no error check in base class, alyways return ok
}
virtual bool Get ( CVector<TData>& vecData )
virtual bool Get ( CVector<TData>& vecData,
const int iOutSize )
{
// get size of data to be get from the buffer
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;
iGetPos += iOutSize;
if ( iGetPos >= iMemSize )
{
iGetPos -= iMemSize;
@ -252,10 +254,11 @@ public:
{
// copy data from internal buffer in output buffer
int iCurPos = 0;
if ( iGetPos + iInSize > iMemSize )
if ( iGetPos + iOutSize > iMemSize )
{
// remaining data size for second block
const int iRemData = iGetPos + iInSize - iMemSize;
const int iRemData = iGetPos + iOutSize - iMemSize;
// data must be read in two steps because of wrap around
while ( iGetPos < iMemSize )
@ -272,12 +275,12 @@ public:
{
// data can be read in one step
std::copy ( vecMemory.begin() + iGetPos,
vecMemory.begin() + iGetPos + iInSize,
vecMemory.begin() + iGetPos + iOutSize,
vecData.begin() );
// set the get position one block further (no wrap around needs
// to be considered here)
iGetPos += iInSize;
iGetPos += iOutSize;
}
}
@ -383,7 +386,7 @@ public:
int GetSize() { return iMemSize / iBlockSize; }
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, const int iOutSize );
protected:
int iBlockSize;
@ -401,7 +404,7 @@ public:
const bool bPreserve = false );
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, const int iOutSize );
int GetAutoSetting() { return iCurAutoBufferSizeSetting; }
void GetErrorRates ( CVector<double>& vecErrRates, double& dLimit );
@ -444,11 +447,11 @@ public:
int GetSize() const { return iMemSize; }
bool Put ( const CVector<TData>& vecsData )
bool Put ( const CVector<TData>& vecsData,
const int iVecSize )
{
// calculate the input size and the end position after copying
const int iVecSize = vecsData.Size();
const int iEnd = iPutPos + iVecSize;
const int iEnd = iPutPos + iVecSize;
// first check for buffer overrun
if ( iEnd <= iMemSize )

View File

@ -51,6 +51,9 @@ CChannel::CChannel ( const bool bNIsServer ) :
// Connections -------------------------------------------------------------
qRegisterMetaType<CVector<uint8_t> > ( "CVector<uint8_t>" );
qRegisterMetaType<CHostAddress> ( "CHostAddress" );
QObject::connect ( &Protocol,
SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ),
this, SLOT ( OnSendProtMessage ( CVector<uint8_t> ) ) );
@ -433,7 +436,7 @@ void CChannel::Disconnect()
}
EPutDataStat CChannel::PutData ( const CVector<uint8_t>& vecbyData,
int iNumBytes )
const int iNumBytes )
{
/*
Note that this function might be called from a different thread (separate
@ -558,14 +561,15 @@ EPutDataStat CChannel::PutData ( const CVector<uint8_t>& vecbyData,
return eRet;
}
EGetDataStat CChannel::GetData ( CVector<uint8_t>& vecbyData )
EGetDataStat CChannel::GetData ( CVector<uint8_t>& vecbyData,
const int iNumBytes )
{
EGetDataStat eGetStatus;
Mutex.lock();
{
// the socket access must be inside a mutex
const bool bSockBufState = SockBuf.Get ( vecbyData );
const bool bSockBufState = SockBuf.Get ( vecbyData, iNumBytes );
// decrease time-out counter
if ( iConTimeOut > 0 )
@ -622,13 +626,14 @@ EGetDataStat CChannel::GetData ( CVector<uint8_t>& vecbyData )
}
void CChannel::PrepAndSendPacket ( CSocket* pSocket,
const CVector<uint8_t>& vecbyNPacket )
const CVector<uint8_t>& vecbyNPacket,
const int iNPacketLen )
{
QMutexLocker locker ( &Mutex );
// use conversion buffer to convert sound card block size in network
// block size
if ( ConvBuf.Put ( vecbyNPacket ) )
if ( ConvBuf.Put ( vecbyNPacket, iNPacketLen ) )
{
pSocket->SendPacket ( ConvBuf.Get(), GetAddress() );
}

View File

@ -64,11 +64,13 @@ public:
CChannel ( const bool bNIsServer = true );
EPutDataStat PutData ( const CVector<uint8_t>& vecbyData,
int iNumBytes );
EGetDataStat GetData ( CVector<uint8_t>& vecbyData );
const int iNumBytes );
EGetDataStat GetData ( CVector<uint8_t>& vecbyData,
const int iNumBytes );
void PrepAndSendPacket ( CSocket* pSocket,
const CVector<uint8_t>& vecbyNPacket );
const CVector<uint8_t>& vecbyNPacket,
const int iNPacketLen );
void ResetTimeOutCounter() { iConTimeOut = iConTimeOutStartVal; }
bool IsConnected() const { return iConTimeOut > 0; }

View File

@ -896,16 +896,16 @@ void CClient::ProcessSndCrdAudioData ( CVector<int16_t>& vecsStereoSndCrd )
while ( SndCrdConversionBufferIn.GetAvailData() >= iStereoBlockSizeSam )
{
// get one block of data for processing
SndCrdConversionBufferIn.Get ( vecDataConvBuf );
SndCrdConversionBufferIn.Get ( vecDataConvBuf, iStereoBlockSizeSam );
// process audio data
ProcessAudioDataIntern ( vecDataConvBuf );
SndCrdConversionBufferOut.Put ( vecDataConvBuf, vecDataConvBuf.Size() );
SndCrdConversionBufferOut.Put ( vecDataConvBuf, iStereoBlockSizeSam );
}
// get processed sound card block out of the conversion buffer
SndCrdConversionBufferOut.Get ( vecsStereoSndCrd );
SndCrdConversionBufferOut.Get ( vecsStereoSndCrd, vecsStereoSndCrd.Size() );
}
else
{
@ -1092,7 +1092,9 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
}
// send coded audio through the network
Channel.PrepAndSendPacket ( &Socket, vecCeltData );
Channel.PrepAndSendPacket ( &Socket,
vecCeltData,
iCeltNumCodedBytes );
}
@ -1119,7 +1121,7 @@ void CClient::ProcessAudioDataIntern ( CVector<int16_t>& vecsStereoSndCrd )
{
// receive a new block
const bool bReceiveDataOk =
( Channel.GetData ( vecbyNetwData ) == GS_BUFFER_OK );
( Channel.GetData ( vecbyNetwData, iCeltNumCodedBytes ) == GS_BUFFER_OK );
// invalidate the buffer OK status flag if necessary
if ( !bReceiveDataOk )

View File

@ -311,10 +311,33 @@ CServer::CServer ( const int iNewMaxNumChan,
vstrChatColors[4] = "maroon";
vstrChatColors[5] = "coral";
// allocate memory for the channel IDs vector for the current connected
// channels where we assume the worst case that all possible channels are
// used
// To avoid audio clitches, in the entire realtime timer audio processing
// routine including the ProcessData no memory must be allocated. Since we
// do not know the required sizes for the vectors, we allocate memory for
// the worst case here:
// we always use stereo audio buffers (which is the worst case)
vecsSendData.Init ( 2 * SYSTEM_FRAME_SIZE_SAMPLES );
// allocate worst case memory for the temporary vectors
vecChanIDsCurConChan.Init ( iMaxNumChannels );
vecvecdGains.Init ( iMaxNumChannels );
vecvecsData.Init ( iMaxNumChannels );
vecNumAudioChannels.Init ( iMaxNumChannels );
for ( i = 0; i < iMaxNumChannels; i++ )
{
// init vectors storing information of all channels
vecvecdGains[i].Init ( iMaxNumChannels );
// we always use stereo audio buffers (see "vecsSendData")
vecvecsData[i].Init ( 2 * SYSTEM_FRAME_SIZE_SAMPLES );
}
// allocate worst case memory for the coded data
vecbyCodedData.Init ( MAX_SIZE_BYTES_NETW_BUF );
// enable history graph (if requested)
if ( !strHistoryFileName.isEmpty() )
@ -646,11 +669,6 @@ void CServer::OnTimer()
{
int i, j;
// TODO avoid allocating memory in the time critical processing routine
CVector<CVector<double> > vecvecdGains;
CVector<CVector<int16_t> > vecvecsData;
CVector<int> vecNumAudioChannels;
// Get data from all connected clients -------------------------------------
// some inits
@ -675,18 +693,6 @@ CVector<int> vecNumAudioChannels;
}
}
// init temporary vectors
// TODO in the entire realtime timer routine including the ProcessData no
// memory must be allocated -> use member variables for the vectors and
// only change the size on changing the number of connected clients at
// the server
// TODO avoid allocating memory in the time critical processing routine
vecvecdGains.Init ( iNumClients );
vecvecsData.Init ( iNumClients );
vecNumAudioChannels.Init ( iNumClients );
// process connected channels
for ( i = 0; i < iNumClients; i++ )
{
@ -699,10 +705,6 @@ vecNumAudioChannels.Init ( iNumClients );
vecNumAudioChannels[i] = iCurNumAudChan;
// init vectors storing information of all channels
vecvecdGains[i].Init ( iNumClients );
vecvecsData[i].Init ( iCurNumAudChan * SYSTEM_FRAME_SIZE_SAMPLES );
// get gains of all connected channels
for ( j = 0; j < iNumClients; j++ )
{
@ -718,13 +720,10 @@ vecNumAudioChannels.Init ( iNumClients );
const int iCeltNumCodedBytes =
vecChannels[iCurChanID].GetNetwFrameSize();
// init temporal data vector and clear input buffers
// TODO avoid allocating memory in the time critical processing routine
CVector<uint8_t> vecbyData ( iCeltNumCodedBytes );
// get data
const EGetDataStat eGetStat =
vecChannels[iCurChanID].GetData ( vecbyData );
vecChannels[iCurChanID].GetData ( vecbyCodedData,
iCeltNumCodedBytes );
// if channel was just disconnected, set flag that connected
// client list is sent to all other clients
@ -743,14 +742,14 @@ CVector<uint8_t> vecbyData ( iCeltNumCodedBytes );
if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_CELT )
{
cc6_celt_decode ( CeltDecoderMono[iCurChanID],
&vecbyData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes,
&vecvecsData[i][0] );
}
else
{
opus_custom_decode ( OpusDecoderMono[iCurChanID],
&vecbyData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes,
&vecvecsData[i][0],
SYSTEM_FRAME_SIZE_SAMPLES );
@ -763,14 +762,14 @@ CVector<uint8_t> vecbyData ( iCeltNumCodedBytes );
if ( vecChannels[iCurChanID].GetAudioCompressionType() == CT_CELT )
{
cc6_celt_decode ( CeltDecoderStereo[iCurChanID],
&vecbyData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes,
&vecvecsData[i][0] );
}
else
{
opus_custom_decode ( OpusDecoderStereo[iCurChanID],
&vecbyData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes,
&vecvecsData[i][0],
SYSTEM_FRAME_SIZE_SAMPLES );
@ -846,30 +845,20 @@ CVector<uint8_t> vecbyData ( iCeltNumCodedBytes );
// get number of audio channels of current channel
const int iCurNumAudChan = vecNumAudioChannels[i];
// calculate the number of samples for output vector
const int iNumOutSamples =
iCurNumAudChan * SYSTEM_FRAME_SIZE_SAMPLES;
// allocate memory for the send data vector
// TODO avoid allocating memory in the time critical processing routine
CVector<int16_t> vecsSendData ( iNumOutSamples );
// generate a sparate mix for each channel
// actual processing of audio data -> mix
ProcessData ( iCurNumAudChan,
vecvecsData,
ProcessData ( vecvecsData,
vecvecdGains[i],
vecNumAudioChannels,
vecsSendData );
vecsSendData,
iCurNumAudChan,
iNumClients );
// get current number of CELT coded bytes
const int iCeltNumCodedBytes =
vecChannels[iCurChanID].GetNetwFrameSize();
// CELT encoding
// TODO avoid allocating memory in the time critical processing routine
CVector<unsigned char> vecCeltData ( iCeltNumCodedBytes );
// OPUS/CELT encoding
if ( vecChannels[iCurChanID].GetNumAudioChannels() == 1 )
{
// mono:
@ -879,7 +868,7 @@ CVector<unsigned char> vecCeltData ( iCeltNumCodedBytes );
cc6_celt_encode ( CeltEncoderMono[iCurChanID],
&vecsSendData[0],
NULL,
&vecCeltData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes );
}
else
@ -894,7 +883,7 @@ opus_custom_encoder_ctl ( OpusEncoderMono[iCurChanID],
opus_custom_encode ( OpusEncoderMono[iCurChanID],
&vecsSendData[0],
SYSTEM_FRAME_SIZE_SAMPLES,
&vecCeltData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes );
}
}
@ -907,11 +896,12 @@ opus_custom_encoder_ctl ( OpusEncoderMono[iCurChanID],
cc6_celt_encode ( CeltEncoderStereo[iCurChanID],
&vecsSendData[0],
NULL,
&vecCeltData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes );
}
else
{
// TODO find a better place than this: the setting does not change all the time
// so for speed optimization it would be better to set it only if the network
// frame size is changed
@ -921,13 +911,15 @@ opus_custom_encoder_ctl ( OpusEncoderStereo[iCurChanID],
opus_custom_encode ( OpusEncoderStereo[iCurChanID],
&vecsSendData[0],
SYSTEM_FRAME_SIZE_SAMPLES,
&vecCeltData[0],
&vecbyCodedData[0],
iCeltNumCodedBytes );
}
}
// send separate mix to current clients
vecChannels[iCurChanID].PrepAndSendPacket ( &Socket, vecCeltData );
vecChannels[iCurChanID].PrepAndSendPacket ( &Socket,
vecbyCodedData,
iCeltNumCodedBytes );
// update socket buffer size
vecChannels[iCurChanID].UpdateSocketBufferSize();
@ -942,17 +934,15 @@ opus_custom_encoder_ctl ( OpusEncoderStereo[iCurChanID],
}
/// @brief Mix all audio data from all clients together.
void CServer::ProcessData ( const int iCurNumAudChan,
const CVector<CVector<int16_t> >& vecvecsData,
void CServer::ProcessData ( const CVector<CVector<int16_t> >& vecvecsData,
const CVector<double>& vecdGains,
const CVector<int>& vecNumAudioChannels,
CVector<int16_t>& vecsOutData )
CVector<int16_t>& vecsOutData,
const int iCurNumAudChan,
const int iNumClients )
{
int i, j, k;
// get the number of clients
const int iNumClients = vecvecsData.Size();
// init return vector with zeros since we mix all channels on that vector
vecsOutData.Reset ( 0 );

View File

@ -203,58 +203,65 @@ protected:
const QString& strChatText );
void WriteHTMLChannelList();
void ProcessData ( const int iCurNumAudChan,
const CVector<CVector<int16_t> >& vecvecsData,
void ProcessData ( const CVector<CVector<int16_t> >& vecvecsData,
const CVector<double>& vecdGains,
const CVector<int>& vecNumAudioChannels,
CVector<int16_t>& vecsOutData );
CVector<int16_t>& vecsOutData,
const int iCurNumAudChan,
const int iNumClients );
virtual void customEvent ( QEvent* pEvent );
// do not use the vector class since CChannel does not have appropriate
// copy constructor/operator
CChannel vecChannels[MAX_NUM_CHANNELS];
int iMaxNumChannels;
CProtocol ConnLessProtocol;
QMutex Mutex;
CChannel vecChannels[MAX_NUM_CHANNELS];
int iMaxNumChannels;
CProtocol ConnLessProtocol;
QMutex Mutex;
// audio encoder/decoder
cc6_CELTMode* CeltModeMono[MAX_NUM_CHANNELS];
cc6_CELTEncoder* CeltEncoderMono[MAX_NUM_CHANNELS];
cc6_CELTDecoder* CeltDecoderMono[MAX_NUM_CHANNELS];
cc6_CELTMode* CeltModeStereo[MAX_NUM_CHANNELS];
cc6_CELTEncoder* CeltEncoderStereo[MAX_NUM_CHANNELS];
cc6_CELTDecoder* CeltDecoderStereo[MAX_NUM_CHANNELS];
OpusCustomMode* OpusMode[MAX_NUM_CHANNELS];
OpusCustomEncoder* OpusEncoderMono[MAX_NUM_CHANNELS];
OpusCustomDecoder* OpusDecoderMono[MAX_NUM_CHANNELS];
OpusCustomEncoder* OpusEncoderStereo[MAX_NUM_CHANNELS];
OpusCustomDecoder* OpusDecoderStereo[MAX_NUM_CHANNELS];
cc6_CELTMode* CeltModeMono[MAX_NUM_CHANNELS];
cc6_CELTEncoder* CeltEncoderMono[MAX_NUM_CHANNELS];
cc6_CELTDecoder* CeltDecoderMono[MAX_NUM_CHANNELS];
cc6_CELTMode* CeltModeStereo[MAX_NUM_CHANNELS];
cc6_CELTEncoder* CeltEncoderStereo[MAX_NUM_CHANNELS];
cc6_CELTDecoder* CeltDecoderStereo[MAX_NUM_CHANNELS];
OpusCustomMode* OpusMode[MAX_NUM_CHANNELS];
OpusCustomEncoder* OpusEncoderMono[MAX_NUM_CHANNELS];
OpusCustomDecoder* OpusDecoderMono[MAX_NUM_CHANNELS];
OpusCustomEncoder* OpusEncoderStereo[MAX_NUM_CHANNELS];
OpusCustomDecoder* OpusDecoderStereo[MAX_NUM_CHANNELS];
CVector<QString> vstrChatColors;
CVector<int> vecChanIDsCurConChan;
CVector<QString> vstrChatColors;
CVector<int> vecChanIDsCurConChan;
CVector<CVector<double> > vecvecdGains;
CVector<CVector<int16_t> > vecvecsData;
CVector<int> vecNumAudioChannels;
CVector<int16_t> vecsSendData;
CVector<uint8_t> vecbyCodedData;
// actual working objects
CSocket Socket;
CSocket Socket;
// logging
CServerLogging Logging;
CServerLogging Logging;
// HTML file server status
bool bWriteStatusHTMLFile;
QString strServerHTMLFileListName;
QString strServerNameWithPort;
bool bWriteStatusHTMLFile;
QString strServerHTMLFileListName;
QString strServerNameWithPort;
CHighPrecisionTimer HighPrecisionTimer;
CHighPrecisionTimer HighPrecisionTimer;
// server list
CServerListManager ServerListManager;
CServerListManager ServerListManager;
// GUI settings
bool bAutoRunMinimized;
bool bAutoRunMinimized;
// messaging
QString strWelcomeMessage;
QString strWelcomeMessage;
signals:
void Started();