diff --git a/src/buffer.cpp b/src/buffer.cpp index 3e329deb..d515d138 100755 --- a/src/buffer.cpp +++ b/src/buffer.cpp @@ -64,28 +64,26 @@ bool CNetBuf::Put ( const CVector& vecbyData, return bPutOK; } -bool CNetBuf::Get ( CVector& vecbyData ) +bool CNetBuf::Get ( CVector& 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::Get ( vecbyData ); + CBufferBase::Get ( vecbyData, iOutSize ); return bGetOK; } @@ -180,16 +178,17 @@ bool CNetBufWithStats::Put ( const CVector& vecbyData, return bPutOK; } -bool CNetBufWithStats::Get ( CVector& vecbyData ) +bool CNetBufWithStats::Get ( CVector& 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 diff --git a/src/buffer.h b/src/buffer.h index 06f2cf35..6cce18dc 100755 --- a/src/buffer.h +++ b/src/buffer.h @@ -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& vecData ) + virtual bool Get ( CVector& 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& vecbyData, const int iInSize ); - virtual bool Get ( CVector& vecbyData ); + virtual bool Get ( CVector& vecbyData, const int iOutSize ); protected: int iBlockSize; @@ -401,7 +404,7 @@ public: const bool bPreserve = false ); virtual bool Put ( const CVector& vecbyData, const int iInSize ); - virtual bool Get ( CVector& vecbyData ); + virtual bool Get ( CVector& vecbyData, const int iOutSize ); int GetAutoSetting() { return iCurAutoBufferSizeSetting; } void GetErrorRates ( CVector& vecErrRates, double& dLimit ); @@ -444,11 +447,11 @@ public: int GetSize() const { return iMemSize; } - bool Put ( const CVector& vecsData ) + bool Put ( const CVector& 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 ) diff --git a/src/channel.cpp b/src/channel.cpp index b19e0364..7529d404 100755 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -51,6 +51,9 @@ CChannel::CChannel ( const bool bNIsServer ) : // Connections ------------------------------------------------------------- + qRegisterMetaType > ( "CVector" ); + qRegisterMetaType ( "CHostAddress" ); + QObject::connect ( &Protocol, SIGNAL ( MessReadyForSending ( CVector ) ), this, SLOT ( OnSendProtMessage ( CVector ) ) ); @@ -433,7 +436,7 @@ void CChannel::Disconnect() } EPutDataStat CChannel::PutData ( const CVector& 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& vecbyData, return eRet; } -EGetDataStat CChannel::GetData ( CVector& vecbyData ) +EGetDataStat CChannel::GetData ( CVector& 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& vecbyData ) } void CChannel::PrepAndSendPacket ( CSocket* pSocket, - const CVector& vecbyNPacket ) + const CVector& 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() ); } diff --git a/src/channel.h b/src/channel.h index f68870d0..994a75b3 100755 --- a/src/channel.h +++ b/src/channel.h @@ -64,11 +64,13 @@ public: CChannel ( const bool bNIsServer = true ); EPutDataStat PutData ( const CVector& vecbyData, - int iNumBytes ); - EGetDataStat GetData ( CVector& vecbyData ); + const int iNumBytes ); + EGetDataStat GetData ( CVector& vecbyData, + const int iNumBytes ); void PrepAndSendPacket ( CSocket* pSocket, - const CVector& vecbyNPacket ); + const CVector& vecbyNPacket, + const int iNPacketLen ); void ResetTimeOutCounter() { iConTimeOut = iConTimeOutStartVal; } bool IsConnected() const { return iConTimeOut > 0; } diff --git a/src/client.cpp b/src/client.cpp index f233fb48..dc4cea11 100755 --- a/src/client.cpp +++ b/src/client.cpp @@ -896,16 +896,16 @@ void CClient::ProcessSndCrdAudioData ( CVector& 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& vecsStereoSndCrd ) } // send coded audio through the network - Channel.PrepAndSendPacket ( &Socket, vecCeltData ); + Channel.PrepAndSendPacket ( &Socket, + vecCeltData, + iCeltNumCodedBytes ); } @@ -1119,7 +1121,7 @@ void CClient::ProcessAudioDataIntern ( CVector& 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 ) diff --git a/src/server.cpp b/src/server.cpp index 302709f8..90db8b8c 100755 --- a/src/server.cpp +++ b/src/server.cpp @@ -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 > vecvecdGains; -CVector > vecvecsData; -CVector vecNumAudioChannels; - // Get data from all connected clients ------------------------------------- // some inits @@ -675,18 +693,6 @@ CVector 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 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 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 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 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 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 vecCeltData ( iCeltNumCodedBytes ); - + // OPUS/CELT encoding if ( vecChannels[iCurChanID].GetNumAudioChannels() == 1 ) { // mono: @@ -879,7 +868,7 @@ CVector 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 >& vecvecsData, +void CServer::ProcessData ( const CVector >& vecvecsData, const CVector& vecdGains, const CVector& vecNumAudioChannels, - CVector& vecsOutData ) + CVector& 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 ); diff --git a/src/server.h b/src/server.h index 75af5018..98e87ab3 100755 --- a/src/server.h +++ b/src/server.h @@ -203,58 +203,65 @@ protected: const QString& strChatText ); void WriteHTMLChannelList(); - void ProcessData ( const int iCurNumAudChan, - const CVector >& vecvecsData, + void ProcessData ( const CVector >& vecvecsData, const CVector& vecdGains, const CVector& vecNumAudioChannels, - CVector& vecsOutData ); + CVector& 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 vstrChatColors; - CVector vecChanIDsCurConChan; + CVector vstrChatColors; + CVector vecChanIDsCurConChan; + + CVector > vecvecdGains; + CVector > vecvecsData; + CVector vecNumAudioChannels; + CVector vecsSendData; + CVector 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();