1285 lines
48 KiB
C++
Executable file
1285 lines
48 KiB
C++
Executable file
/******************************************************************************\
|
|
* Copyright (c) 2004-2009
|
|
*
|
|
* Author(s):
|
|
* Volker Fischer
|
|
*
|
|
******************************************************************************
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify it under
|
|
* the terms of the GNU General Public License as published by the Free Software
|
|
* Foundation; either version 2 of the License, or (at your option) any later
|
|
* version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful, but WITHOUT
|
|
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
|
|
* FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
|
|
* details.
|
|
*
|
|
* You should have received a copy of the GNU General Public License along with
|
|
* this program; if not, write to the Free Software Foundation, Inc.,
|
|
* 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
|
|
*
|
|
\******************************************************************************/
|
|
|
|
#include "channel.h"
|
|
|
|
|
|
/******************************************************************************\
|
|
* CChannelSet *
|
|
\******************************************************************************/
|
|
CChannelSet::CChannelSet ( const int iNewUploadRateLimitKbps ) :
|
|
bWriteStatusHTMLFile ( false ),
|
|
iUploadRateLimitKbps ( iNewUploadRateLimitKbps )
|
|
{
|
|
// enable all channels
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
vecChannels[i].SetEnable ( true );
|
|
}
|
|
|
|
// define colors for chat window identifiers
|
|
vstrChatColors.Init ( 6 );
|
|
vstrChatColors[0] = "mediumblue";
|
|
vstrChatColors[1] = "red";
|
|
vstrChatColors[2] = "darkorchid";
|
|
vstrChatColors[3] = "green";
|
|
vstrChatColors[4] = "maroon";
|
|
vstrChatColors[5] = "coral";
|
|
|
|
// CODE TAG: MAX_NUM_CHANNELS_TAG
|
|
// make sure we have MAX_NUM_CHANNELS connections!!!
|
|
// send message
|
|
QObject::connect ( &vecChannels[0], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh0 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[1], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh1 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[2], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh2 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[3], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh3 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[4], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh4 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[5], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh5 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[6], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh6 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[7], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh7 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[8], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh8 ( CVector<uint8_t> ) ) );
|
|
QObject::connect ( &vecChannels[9], SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ), this, SLOT ( OnSendProtMessCh9 ( CVector<uint8_t> ) ) );
|
|
|
|
// request jitter buffer size
|
|
QObject::connect ( &vecChannels[0], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh0() ) );
|
|
QObject::connect ( &vecChannels[1], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh1() ) );
|
|
QObject::connect ( &vecChannels[2], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh2() ) );
|
|
QObject::connect ( &vecChannels[3], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh3() ) );
|
|
QObject::connect ( &vecChannels[4], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh4() ) );
|
|
QObject::connect ( &vecChannels[5], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh5() ) );
|
|
QObject::connect ( &vecChannels[6], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh6() ) );
|
|
QObject::connect ( &vecChannels[7], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh7() ) );
|
|
QObject::connect ( &vecChannels[8], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh8() ) );
|
|
QObject::connect ( &vecChannels[9], SIGNAL ( NewConnection() ), this, SLOT ( OnNewConnectionCh9() ) );
|
|
|
|
// request connected clients list
|
|
QObject::connect ( &vecChannels[0], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh0() ) );
|
|
QObject::connect ( &vecChannels[1], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh1() ) );
|
|
QObject::connect ( &vecChannels[2], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh2() ) );
|
|
QObject::connect ( &vecChannels[3], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh3() ) );
|
|
QObject::connect ( &vecChannels[4], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh4() ) );
|
|
QObject::connect ( &vecChannels[5], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh5() ) );
|
|
QObject::connect ( &vecChannels[6], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh6() ) );
|
|
QObject::connect ( &vecChannels[7], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh7() ) );
|
|
QObject::connect ( &vecChannels[8], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh8() ) );
|
|
QObject::connect ( &vecChannels[9], SIGNAL ( ReqConnClientsList() ), this, SLOT ( OnReqConnClientsListCh9() ) );
|
|
|
|
// channel name has changed
|
|
QObject::connect ( &vecChannels[0], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh0() ) );
|
|
QObject::connect ( &vecChannels[1], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh1() ) );
|
|
QObject::connect ( &vecChannels[2], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh2() ) );
|
|
QObject::connect ( &vecChannels[3], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh3() ) );
|
|
QObject::connect ( &vecChannels[4], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh4() ) );
|
|
QObject::connect ( &vecChannels[5], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh5() ) );
|
|
QObject::connect ( &vecChannels[6], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh6() ) );
|
|
QObject::connect ( &vecChannels[7], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh7() ) );
|
|
QObject::connect ( &vecChannels[8], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh8() ) );
|
|
QObject::connect ( &vecChannels[9], SIGNAL ( NameHasChanged() ), this, SLOT ( OnNameHasChangedCh9() ) );
|
|
|
|
// chat text received
|
|
QObject::connect ( &vecChannels[0], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh0 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[1], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh1 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[2], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh2 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[3], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh3 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[4], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh4 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[5], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh5 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[6], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh6 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[7], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh7 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[8], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh8 ( QString ) ) );
|
|
QObject::connect ( &vecChannels[9], SIGNAL ( ChatTextReceived ( QString ) ), this, SLOT ( OnChatTextReceivedCh9 ( QString ) ) );
|
|
|
|
// ping message received
|
|
QObject::connect ( &vecChannels[0], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh0 ( int ) ) );
|
|
QObject::connect ( &vecChannels[1], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh1 ( int ) ) );
|
|
QObject::connect ( &vecChannels[2], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh2 ( int ) ) );
|
|
QObject::connect ( &vecChannels[3], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh3 ( int ) ) );
|
|
QObject::connect ( &vecChannels[4], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh4 ( int ) ) );
|
|
QObject::connect ( &vecChannels[5], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh5 ( int ) ) );
|
|
QObject::connect ( &vecChannels[6], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh6 ( int ) ) );
|
|
QObject::connect ( &vecChannels[7], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh7 ( int ) ) );
|
|
QObject::connect ( &vecChannels[8], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh8 ( int ) ) );
|
|
QObject::connect ( &vecChannels[9], SIGNAL ( PingReceived ( int ) ), this, SLOT ( OnPingReceivedCh9 ( int ) ) );
|
|
}
|
|
|
|
CVector<CChannelShortInfo> CChannelSet::CreateChannelList()
|
|
{
|
|
CVector<CChannelShortInfo> vecChanInfo ( 0 );
|
|
|
|
// look for free channels
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
// append channel ID, IP address and channel name to storing vectors
|
|
vecChanInfo.Add ( CChannelShortInfo (
|
|
i, // ID
|
|
vecChannels[i].GetAddress().InetAddr.toIPv4Address(), // IP address
|
|
vecChannels[i].GetName() /* name */ ) );
|
|
}
|
|
}
|
|
|
|
return vecChanInfo;
|
|
}
|
|
|
|
void CChannelSet::CreateAndSendChanListForAllConChannels()
|
|
{
|
|
// create channel list
|
|
CVector<CChannelShortInfo> vecChanInfo ( CChannelSet::CreateChannelList() );
|
|
|
|
// now send connected channels list to all connected clients
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
// send message
|
|
vecChannels[i].CreateConClientListMes ( vecChanInfo );
|
|
}
|
|
}
|
|
|
|
// create status HTML file if enabled
|
|
if ( bWriteStatusHTMLFile )
|
|
{
|
|
WriteHTMLChannelList();
|
|
}
|
|
}
|
|
|
|
void CChannelSet::CreateAndSendChanListForAllExceptThisChan ( const int iCurChanID )
|
|
{
|
|
// create channel list
|
|
CVector<CChannelShortInfo> vecChanInfo ( CChannelSet::CreateChannelList() );
|
|
|
|
// now send connected channels list to all connected clients except for
|
|
// the channel with the ID "iCurChanID"
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( ( vecChannels[i].IsConnected() ) && ( i != iCurChanID ) )
|
|
{
|
|
// send message
|
|
vecChannels[i].CreateConClientListMes ( vecChanInfo );
|
|
}
|
|
}
|
|
|
|
// create status HTML file if enabled
|
|
if ( bWriteStatusHTMLFile )
|
|
{
|
|
WriteHTMLChannelList();
|
|
}
|
|
}
|
|
|
|
void CChannelSet::CreateAndSendChanListForThisChan ( const int iCurChanID )
|
|
{
|
|
// create channel list
|
|
CVector<CChannelShortInfo> vecChanInfo ( CChannelSet::CreateChannelList() );
|
|
|
|
// now send connected channels list to the channel with the ID "iCurChanID"
|
|
vecChannels[iCurChanID].CreateConClientListMes ( vecChanInfo );
|
|
}
|
|
|
|
void CChannelSet::CreateAndSendChatTextForAllConChannels ( const int iCurChanID,
|
|
const QString& strChatText )
|
|
{
|
|
// create message which is sent to all connected clients -------------------
|
|
// get client name, if name is empty, use IP address instead
|
|
QString ChanName = vecChannels[iCurChanID].GetName();
|
|
if ( ChanName.isEmpty() )
|
|
{
|
|
// convert IP address to text and show it
|
|
ChanName =
|
|
vecChannels[iCurChanID].GetAddress().GetIpAddressStringNoLastByte();
|
|
}
|
|
|
|
// add name of the client at the beginning of the message text and use
|
|
// different colors, to get correct HTML, the "<" and ">" signs must be
|
|
// replaced by "<" and ">"
|
|
QString sCurColor = vstrChatColors[iCurChanID % vstrChatColors.Size()];
|
|
const QString strActualMessageText =
|
|
"<font color=""" + sCurColor + """><b><" + ChanName +
|
|
"></b></font> " + strChatText;
|
|
|
|
|
|
// send chat text to all connected clients ---------------------------------
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
// send message
|
|
vecChannels[i].CreateChatTextMes ( strActualMessageText );
|
|
}
|
|
}
|
|
}
|
|
|
|
int CChannelSet::GetFreeChan()
|
|
{
|
|
// look for a free channel
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( !vecChannels[i].IsConnected() )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
|
|
// no free channel found, return invalid 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;
|
|
|
|
// first initialize all channels with the first parameter set (highest
|
|
// upload data rate)
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
// set new parameters
|
|
vecChannels[i].SetNetwBufSizeFactOut ( 1 );
|
|
vecChannels[i].SetAudioCompressionOut ( CT_NONE );
|
|
}
|
|
}
|
|
|
|
// calculate and check total upload rate (maybe the initialization already
|
|
// gives desired upload rate)
|
|
bUploadRateIsBelowLimit =
|
|
( CalculateTotalUploadRateKbps() <= iUploadRateLimitKbps );
|
|
|
|
// try other parameter sets if required
|
|
const int iNumTrials = 3;
|
|
EAudComprType eCurAudComprType;
|
|
int iCurBlockSizeFact;
|
|
|
|
int iCurTrialIdx = 0;
|
|
while ( ( iCurTrialIdx < iNumTrials ) && ( !bUploadRateIsBelowLimit ) )
|
|
{
|
|
switch ( iCurTrialIdx )
|
|
{
|
|
case 0:
|
|
// using other audio compression gives most reduction
|
|
eCurAudComprType = CT_MSADPCM;
|
|
iCurBlockSizeFact = 1;
|
|
break;
|
|
|
|
case 1:
|
|
// trying to use larger block size factor to further reduce rate
|
|
eCurAudComprType = CT_MSADPCM;
|
|
iCurBlockSizeFact = 2;
|
|
break;
|
|
|
|
case 2:
|
|
// trying to use larger block size factor to further reduce rate
|
|
eCurAudComprType = CT_MSADPCM;
|
|
iCurBlockSizeFact = 3;
|
|
break;
|
|
}
|
|
|
|
// we try to set the worst parameters to the clients which connected
|
|
// the latest to the server, therefore we apply the new parameters to
|
|
// the last channels first
|
|
int iCurCh = USED_NUM_CHANNELS - 1;
|
|
while ( ( iCurCh >= 0 ) && ( !bUploadRateIsBelowLimit ) )
|
|
{
|
|
if ( vecChannels[iCurCh].IsConnected() )
|
|
{
|
|
// set new parameters
|
|
vecChannels[iCurCh].SetNetwBufSizeFactOut ( iCurBlockSizeFact );
|
|
vecChannels[iCurCh].SetAudioCompressionOut ( eCurAudComprType );
|
|
|
|
// calculate and check total upload rate
|
|
bUploadRateIsBelowLimit =
|
|
( CalculateTotalUploadRateKbps() <= iUploadRateLimitKbps );
|
|
}
|
|
|
|
// next channel (backwards counting)
|
|
iCurCh--;
|
|
}
|
|
|
|
// next trial index
|
|
iCurTrialIdx++;
|
|
}
|
|
}
|
|
|
|
int CChannelSet::CalculateTotalUploadRateKbps()
|
|
{
|
|
// calculate total upload rate
|
|
int iTotalUploadRate = 0;
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
// accumulate the upload rates from all channels
|
|
iTotalUploadRate += vecChannels[i].GetUploadRateKbps();
|
|
}
|
|
}
|
|
return iTotalUploadRate;
|
|
}
|
|
|
|
int CChannelSet::CheckAddr ( const CHostAddress& Addr )
|
|
{
|
|
CHostAddress InetAddr;
|
|
|
|
// check for all possible channels if IP is already in use
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
if ( vecChannels[i].GetAddress ( InetAddr ) )
|
|
{
|
|
// IP found, return channel number
|
|
if ( InetAddr == Addr )
|
|
{
|
|
return i;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
// IP not found, return invalid ID
|
|
return INVALID_CHANNEL_ID;
|
|
}
|
|
|
|
bool CChannelSet::PutData ( const CVector<unsigned char>& vecbyRecBuf,
|
|
const int iNumBytesRead,
|
|
const CHostAddress& HostAdr )
|
|
{
|
|
bool bAudioOK = false;
|
|
bool bNewChannelReserved = false;
|
|
|
|
Mutex.lock();
|
|
{
|
|
bool bChanOK = true;
|
|
|
|
// Get channel ID ------------------------------------------------------
|
|
// check address
|
|
int iCurChanID = CheckAddr ( HostAdr );
|
|
|
|
if ( iCurChanID == INVALID_CHANNEL_ID )
|
|
{
|
|
// a new client is calling, look for free channel
|
|
iCurChanID = GetFreeChan();
|
|
|
|
if ( iCurChanID != INVALID_CHANNEL_ID )
|
|
{
|
|
// initialize current channel by storing the calling host
|
|
// address
|
|
vecChannels[iCurChanID].SetAddress ( HostAdr );
|
|
|
|
// reset the channel gains of current channel, at the same
|
|
// time reset gains of this channel ID for all other channels
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
vecChannels[iCurChanID].SetGain ( i, (double) 1.0 );
|
|
|
|
// other channels (we do not distinguish the case if
|
|
// i == iCurChanID for simplicity)
|
|
vecChannels[i].SetGain ( iCurChanID, (double) 1.0 );
|
|
}
|
|
|
|
// set flag for new reserved channel
|
|
bNewChannelReserved = true;
|
|
}
|
|
else
|
|
{
|
|
// no free channel available
|
|
bChanOK = false;
|
|
|
|
// create and send "server full" message
|
|
|
|
// TODO problem: if channel is not officially connected, we cannot send messages
|
|
|
|
}
|
|
}
|
|
|
|
|
|
// Put received data in jitter buffer ----------------------------------
|
|
if ( bChanOK )
|
|
{
|
|
// put packet in socket buffer
|
|
switch ( vecChannels[iCurChanID].PutData ( vecbyRecBuf, iNumBytesRead ) )
|
|
{
|
|
case PS_AUDIO_OK:
|
|
PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_GREEN, iCurChanID );
|
|
bAudioOK = true; // in case we have an audio packet, return true
|
|
break;
|
|
|
|
case PS_AUDIO_ERR:
|
|
PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_RED, iCurChanID );
|
|
bAudioOK = true; // in case we have an audio packet, return true
|
|
break;
|
|
|
|
case PS_PROT_ERR:
|
|
PostWinMessage ( MS_JIT_BUF_PUT, MUL_COL_LED_YELLOW, iCurChanID );
|
|
|
|
// TEST
|
|
bAudioOK = true;
|
|
|
|
break;
|
|
}
|
|
}
|
|
|
|
// after data is put in channel buffer, create channel list message if
|
|
// requested
|
|
if ( bNewChannelReserved && bAudioOK )
|
|
{
|
|
// update output network parameters for all connected clients
|
|
SetOutputParameters();
|
|
|
|
// send message about new channel
|
|
emit ChannelConnected ( HostAdr );
|
|
|
|
// A new client connected to the server, create and
|
|
// send all clients the updated channel list (the list has to
|
|
// be created after the received data has to be put to the
|
|
// channel first so that this channel is marked as connected)
|
|
//
|
|
// connected clients list is only send for new connected clients after
|
|
// request, only the already connected clients get the list
|
|
// automatically, because they don't know when new clients connect
|
|
CreateAndSendChanListForAllExceptThisChan ( iCurChanID );
|
|
}
|
|
}
|
|
Mutex.unlock();
|
|
|
|
return bAudioOK;
|
|
}
|
|
|
|
void CChannelSet::GetBlockAllConC ( CVector<int>& vecChanID,
|
|
CVector<CVector<double> >& vecvecdData,
|
|
CVector<CVector<double> >& vecvecdGains )
|
|
{
|
|
int i, j;
|
|
bool bChannelIsNowDisconnected = false;
|
|
|
|
// init temporal data vector and clear input buffers
|
|
CVector<double> vecdData ( MIN_SERVER_BLOCK_SIZE_SAMPLES );
|
|
|
|
vecChanID.Init ( 0 );
|
|
vecvecdData.Init ( 0 );
|
|
vecvecdGains.Init ( 0 );
|
|
|
|
// make put and get calls thread safe. Do not forget to unlock mutex
|
|
// afterwards!
|
|
Mutex.lock();
|
|
{
|
|
// check all possible channels
|
|
for ( i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
// read out all input buffers to decrease timeout counter on
|
|
// disconnected channels
|
|
const EGetDataStat eGetStat = vecChannels[i].GetData ( vecdData );
|
|
|
|
// if channel was just disconnected, set flag that connected
|
|
// client list is sent to all other clients
|
|
if ( eGetStat == GS_CHAN_NOW_DISCONNECTED )
|
|
{
|
|
bChannelIsNowDisconnected = true;
|
|
}
|
|
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
// add ID and data
|
|
vecChanID.Add ( i );
|
|
|
|
const int iOldSize = vecvecdData.Size();
|
|
vecvecdData.Enlarge ( 1 );
|
|
vecvecdData[iOldSize].Init ( vecdData.Size() );
|
|
vecvecdData[iOldSize] = vecdData;
|
|
|
|
// send message for get status (for GUI)
|
|
if ( eGetStat == GS_BUFFER_OK )
|
|
{
|
|
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_GREEN, i );
|
|
}
|
|
else
|
|
{
|
|
PostWinMessage ( MS_JIT_BUF_GET, MUL_COL_LED_RED, i );
|
|
}
|
|
}
|
|
}
|
|
|
|
// now that we know the IDs of the connected clients, get gains
|
|
const int iNumCurConnChan = vecChanID.Size();
|
|
vecvecdGains.Init ( iNumCurConnChan );
|
|
|
|
for ( i = 0; i < iNumCurConnChan; i++ )
|
|
{
|
|
vecvecdGains[i].Init ( iNumCurConnChan );
|
|
|
|
for ( j = 0; j < iNumCurConnChan; j++ )
|
|
{
|
|
// The second index of "vecvecdGains" does not represent
|
|
// the channel ID! Therefore we have to use "vecChanID" to
|
|
// query the IDs of the currently connected channels
|
|
vecvecdGains[i][j] =
|
|
vecChannels[vecChanID[i]].GetGain( vecChanID[j] );
|
|
}
|
|
}
|
|
|
|
// a channel is now disconnected, take action on it
|
|
if ( bChannelIsNowDisconnected )
|
|
{
|
|
// update output network parameters for all connected clients
|
|
SetOutputParameters();
|
|
|
|
// update channel list for all currently connected clients
|
|
CreateAndSendChanListForAllConChannels();
|
|
}
|
|
}
|
|
Mutex.unlock(); // release mutex
|
|
}
|
|
|
|
void CChannelSet::GetConCliParam ( CVector<CHostAddress>& vecHostAddresses,
|
|
CVector<QString>& vecsName,
|
|
CVector<int>& veciJitBufSize,
|
|
CVector<int>& veciNetwOutBlSiFact,
|
|
CVector<EAudComprType>& veceAudComprType )
|
|
{
|
|
CHostAddress InetAddr;
|
|
|
|
// init return values
|
|
vecHostAddresses.Init ( USED_NUM_CHANNELS );
|
|
vecsName.Init ( USED_NUM_CHANNELS );
|
|
veciJitBufSize.Init ( USED_NUM_CHANNELS );
|
|
veciNetwOutBlSiFact.Init ( USED_NUM_CHANNELS );
|
|
veceAudComprType.Init ( USED_NUM_CHANNELS );
|
|
|
|
// check all possible channels
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].GetAddress ( InetAddr ) )
|
|
{
|
|
// get requested data
|
|
vecHostAddresses[i] = InetAddr;
|
|
vecsName[i] = vecChannels[i].GetName();
|
|
veciJitBufSize[i] = vecChannels[i].GetSockBufSize();
|
|
veciNetwOutBlSiFact[i] = vecChannels[i].GetNetwBufSizeFactOut();
|
|
veceAudComprType[i] = vecChannels[i].GetAudioCompressionOut();
|
|
}
|
|
}
|
|
}
|
|
|
|
void CChannelSet::StartStatusHTMLFileWriting ( const QString& strNewFileName,
|
|
const QString& strNewServerNameWithPort )
|
|
{
|
|
// set important parameters
|
|
strServerHTMLFileListName = strNewFileName;
|
|
strServerNameWithPort = strNewServerNameWithPort;
|
|
|
|
// set flag
|
|
bWriteStatusHTMLFile = true;
|
|
|
|
// write initial file
|
|
WriteHTMLChannelList();
|
|
}
|
|
|
|
void CChannelSet::WriteHTMLChannelList()
|
|
{
|
|
// create channel list
|
|
CVector<CChannelShortInfo> vecChanInfo ( CChannelSet::CreateChannelList() );
|
|
|
|
// prepare file and stream
|
|
QFile serverFileListFile ( strServerHTMLFileListName );
|
|
if ( !serverFileListFile.open ( QIODevice::WriteOnly | QIODevice::Text ) )
|
|
{
|
|
return;
|
|
}
|
|
|
|
QTextStream streamFileOut ( &serverFileListFile );
|
|
streamFileOut << strServerNameWithPort << endl << "<ul>" << endl;
|
|
|
|
// get the number of connected clients
|
|
int iNumConnClients = 0;
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
iNumConnClients++;
|
|
}
|
|
}
|
|
|
|
// depending on number of connected clients write list
|
|
if ( iNumConnClients == 0 )
|
|
{
|
|
// no clients are connected -> empty server
|
|
streamFileOut << " No client connected" << endl;
|
|
}
|
|
else
|
|
{
|
|
// write entry for each connected client
|
|
for ( int i = 0; i < USED_NUM_CHANNELS; i++ )
|
|
{
|
|
if ( vecChannels[i].IsConnected() )
|
|
{
|
|
QString strCurChanName = vecChannels[i].GetName();
|
|
|
|
// if text is empty, show IP address instead
|
|
if ( strCurChanName.isEmpty() )
|
|
{
|
|
// convert IP address to text and show it, remove last
|
|
// digits
|
|
strCurChanName =
|
|
vecChannels[i].GetAddress().GetIpAddressStringNoLastByte();
|
|
}
|
|
|
|
streamFileOut << " <li>" << strCurChanName << "</li>" << endl;
|
|
}
|
|
}
|
|
}
|
|
|
|
// finish list
|
|
streamFileOut << "</ul>" << endl;
|
|
}
|
|
|
|
|
|
|
|
/******************************************************************************\
|
|
* CChannel *
|
|
\******************************************************************************/
|
|
CChannel::CChannel ( const bool bNIsServer ) :
|
|
bIsServer ( bNIsServer ),
|
|
sName ( "" ),
|
|
vecdGains ( USED_NUM_CHANNELS, (double) 1.0 ),
|
|
bIsEnabled ( false ),
|
|
iCurNetwOutBlSiFact ( DEF_NET_BLOCK_SIZE_FACTOR )
|
|
{
|
|
// query all possible network in buffer sizes for determining if an
|
|
// audio packet was received (the following code only works if all
|
|
// possible network buffer sizes are different!)
|
|
// we add a special entry for network modes which are managed via the
|
|
// protocol -> "+ 1"
|
|
const int iNumSupportedAudComprTypes = 3;
|
|
vecNetwBufferInProps.Init ( iNumSupportedAudComprTypes *
|
|
MAX_NET_BLOCK_SIZE_FACTOR + 1 );
|
|
|
|
// init special mode (with invalid data)
|
|
vecNetwBufferInProps[0].iAudioBlockSize = 0;
|
|
vecNetwBufferInProps[0].eAudComprType = CT_NONE;
|
|
vecNetwBufferInProps[0].iNetwInBufSize = 0;
|
|
|
|
for ( int i = 0; i < MAX_NET_BLOCK_SIZE_FACTOR; i++ )
|
|
{
|
|
// (consider the special mode -> "1 +")
|
|
const int iNoneIdx = 1 + iNumSupportedAudComprTypes * i;
|
|
const int iIMAIdx = 1 + iNumSupportedAudComprTypes * i + 1;
|
|
const int iMSIdx = 1 + iNumSupportedAudComprTypes * i + 2;
|
|
|
|
// network block size factor must start from 1 -> i + 1
|
|
const int iCurNetBlockSizeFact = i + 1;
|
|
vecNetwBufferInProps[iNoneIdx].iAudioBlockSize = iCurNetBlockSizeFact * MIN_SERVER_BLOCK_SIZE_SAMPLES;
|
|
vecNetwBufferInProps[iIMAIdx].iAudioBlockSize = iCurNetBlockSizeFact * MIN_SERVER_BLOCK_SIZE_SAMPLES;
|
|
vecNetwBufferInProps[iMSIdx].iAudioBlockSize = iCurNetBlockSizeFact * MIN_SERVER_BLOCK_SIZE_SAMPLES;
|
|
|
|
// None (no audio compression)
|
|
vecNetwBufferInProps[iNoneIdx].eAudComprType = CT_NONE;
|
|
vecNetwBufferInProps[iNoneIdx].iNetwInBufSize = AudioCompressionIn.Init (
|
|
vecNetwBufferInProps[iNoneIdx].iAudioBlockSize,
|
|
vecNetwBufferInProps[iNoneIdx].eAudComprType );
|
|
|
|
// IMA ADPCM
|
|
vecNetwBufferInProps[iIMAIdx].eAudComprType = CT_IMAADPCM;
|
|
vecNetwBufferInProps[iIMAIdx].iNetwInBufSize = AudioCompressionIn.Init (
|
|
vecNetwBufferInProps[iIMAIdx].iAudioBlockSize,
|
|
vecNetwBufferInProps[iIMAIdx].eAudComprType );
|
|
|
|
// MS ADPCM
|
|
vecNetwBufferInProps[iMSIdx].eAudComprType = CT_MSADPCM;
|
|
vecNetwBufferInProps[iMSIdx].iNetwInBufSize = AudioCompressionIn.Init (
|
|
vecNetwBufferInProps[iMSIdx].iAudioBlockSize,
|
|
vecNetwBufferInProps[iMSIdx].eAudComprType );
|
|
}
|
|
|
|
// initial value for connection time out counter, we calculate the total
|
|
// number of samples here and subtract the number of samples of the block
|
|
// which we take out of the buffer to be independent of block sizes
|
|
iConTimeOutStartVal = CON_TIME_OUT_SEC_MAX * SYSTEM_SAMPLE_RATE;
|
|
|
|
// init time-out for the buffer with zero -> no connection
|
|
iConTimeOut = 0;
|
|
|
|
// init the socket buffer
|
|
SetSockBufSize ( DEF_NET_BUF_SIZE_NUM_BL );
|
|
|
|
// set initial input and output block size factors
|
|
SetAudioBlockSizeAndComprIn (
|
|
MIN_SERVER_BLOCK_SIZE_SAMPLES * DEF_NET_BLOCK_SIZE_FACTOR,
|
|
CT_MSADPCM );
|
|
|
|
if ( bIsServer )
|
|
{
|
|
SetNetwBufSizeFactOut ( DEF_NET_BLOCK_SIZE_FACTOR );
|
|
}
|
|
else
|
|
{
|
|
SetNetwBufSizeOut ( MIN_SERVER_BLOCK_SIZE_SAMPLES );
|
|
}
|
|
|
|
// set initial audio compression format for output
|
|
SetAudioCompressionOut ( CT_MSADPCM );
|
|
|
|
|
|
// connections -------------------------------------------------------------
|
|
QObject::connect ( &Protocol,
|
|
SIGNAL ( MessReadyForSending ( CVector<uint8_t> ) ),
|
|
this, SLOT ( OnSendProtMessage ( CVector<uint8_t> ) ) );
|
|
|
|
QObject::connect ( &Protocol,
|
|
SIGNAL ( ChangeJittBufSize ( int ) ),
|
|
this, SLOT ( OnJittBufSizeChange ( int ) ) );
|
|
|
|
QObject::connect ( &Protocol,
|
|
SIGNAL ( ReqJittBufSize() ),
|
|
SIGNAL ( ReqJittBufSize() ) );
|
|
|
|
QObject::connect ( &Protocol,
|
|
SIGNAL ( ReqConnClientsList() ),
|
|
SIGNAL ( ReqConnClientsList() ) );
|
|
|
|
QObject::connect ( &Protocol,
|
|
SIGNAL ( ConClientListMesReceived ( CVector<CChannelShortInfo> ) ),
|
|
SIGNAL ( ConClientListMesReceived ( CVector<CChannelShortInfo> ) ) );
|
|
|
|
QObject::connect( &Protocol, SIGNAL ( ChangeChanGain ( int, double ) ),
|
|
this, SLOT ( OnChangeChanGain ( int, double ) ) );
|
|
|
|
QObject::connect( &Protocol, SIGNAL ( ChangeChanName ( QString ) ),
|
|
this, SLOT ( OnChangeChanName ( QString ) ) );
|
|
|
|
QObject::connect( &Protocol, SIGNAL ( ChatTextReceived ( QString ) ),
|
|
this, SIGNAL ( ChatTextReceived ( QString ) ) );
|
|
|
|
QObject::connect( &Protocol, SIGNAL ( PingReceived ( int ) ),
|
|
this, SIGNAL ( PingReceived ( int ) ) );
|
|
|
|
QObject::connect ( &Protocol,
|
|
SIGNAL ( NetTranspPropsReceived ( CNetworkTransportProps ) ),
|
|
this, SLOT ( OnNetTranspPropsReceived ( CNetworkTransportProps ) ) );
|
|
|
|
QObject::connect ( &Protocol,
|
|
SIGNAL ( ReqNetTranspProps() ),
|
|
this, SLOT ( OnReqNetTranspProps() ) );
|
|
}
|
|
|
|
bool CChannel::ProtocolIsEnabled()
|
|
{
|
|
// for the server, only enable protocol if the channel is connected, i.e.,
|
|
// successfully audio packets are received from a client
|
|
// for the client, enable protocol if the channel is enabled, i.e., the
|
|
// connection button was hit by the user
|
|
if ( bIsServer )
|
|
{
|
|
return IsConnected();
|
|
}
|
|
else
|
|
{
|
|
return bIsEnabled;
|
|
}
|
|
}
|
|
|
|
void CChannel::SetEnable ( const bool bNEnStat )
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// set internal parameter
|
|
bIsEnabled = bNEnStat;
|
|
|
|
// if channel is not enabled, reset time out count and protocol
|
|
if ( !bNEnStat )
|
|
{
|
|
iConTimeOut = 0;
|
|
Protocol.Reset();
|
|
}
|
|
}
|
|
|
|
void CChannel::SetAudioBlockSizeAndComprIn ( const int iNewBlockSize,
|
|
const EAudComprType eNewAudComprType )
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// store block size value
|
|
iCurAudioBlockSizeIn = iNewBlockSize;
|
|
|
|
// init audio compression unit
|
|
AudioCompressionIn.Init ( iNewBlockSize, eNewAudComprType );
|
|
}
|
|
|
|
void CChannel::SetNetwBufSizeOut ( const int iNewAudioBlockSizeOut )
|
|
{
|
|
// this function is intended for the client (not the server)
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// direct setting of audio buffer (without buffer size factor) is
|
|
// right now only intendet for the client, not the server
|
|
if ( !bIsServer )
|
|
{
|
|
// store new value
|
|
iCurAudioBlockSizeOut = iNewAudioBlockSizeOut;
|
|
|
|
iAudComprSizeOut =
|
|
AudioCompressionOut.Init ( iNewAudioBlockSizeOut, eAudComprTypeOut );
|
|
}
|
|
}
|
|
|
|
void CChannel::SetNetwBufSizeFactOut ( const int iNewNetwBlSiFactOut )
|
|
{
|
|
// this function is intended for the server (not the client)
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// use the network block size factor only for the server
|
|
if ( bIsServer )
|
|
{
|
|
// store new value
|
|
iCurNetwOutBlSiFact = iNewNetwBlSiFactOut;
|
|
|
|
// init audio compression and get audio compression block size
|
|
iAudComprSizeOut = AudioCompressionOut.Init (
|
|
iNewNetwBlSiFactOut * MIN_SERVER_BLOCK_SIZE_SAMPLES, eAudComprTypeOut );
|
|
|
|
// init conversion buffer
|
|
ConvBuf.Init ( iNewNetwBlSiFactOut * MIN_SERVER_BLOCK_SIZE_SAMPLES );
|
|
}
|
|
}
|
|
|
|
void CChannel::SetAudioCompressionOut ( const EAudComprType eNewAudComprTypeOut )
|
|
{
|
|
// store new value
|
|
eAudComprTypeOut = eNewAudComprTypeOut;
|
|
|
|
if ( bIsServer )
|
|
{
|
|
// call "set network buffer size factor" function because its
|
|
// initialization depends on the audio compression format and
|
|
// implicitely, the audio compression is initialized
|
|
SetNetwBufSizeFactOut ( iCurNetwOutBlSiFact );
|
|
}
|
|
else
|
|
{
|
|
// for client set arbitrary block size
|
|
SetNetwBufSizeOut ( iCurAudioBlockSizeOut );
|
|
}
|
|
}
|
|
|
|
void CChannel::SetSockBufSize ( const int iNumBlocks )
|
|
{
|
|
QMutexLocker locker ( &Mutex ); // this opperation must be done with mutex
|
|
|
|
iCurSockBufSize = iNumBlocks;
|
|
|
|
// the network block size is a multiple of the internal minimal
|
|
// block size
|
|
SockBuf.Init ( MIN_SERVER_BLOCK_SIZE_SAMPLES, iNumBlocks );
|
|
}
|
|
|
|
void CChannel::SetGain ( const int iChanID, const double dNewGain )
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// set value (make sure channel ID is in range)
|
|
if ( ( iChanID >= 0 ) && ( iChanID < USED_NUM_CHANNELS ) )
|
|
{
|
|
vecdGains[iChanID] = dNewGain;
|
|
}
|
|
}
|
|
|
|
double CChannel::GetGain ( const int iChanID )
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// get value (make sure channel ID is in range)
|
|
if ( ( iChanID >= 0 ) && ( iChanID < USED_NUM_CHANNELS ) )
|
|
{
|
|
return vecdGains[iChanID];
|
|
}
|
|
else
|
|
{
|
|
return 0;
|
|
}
|
|
}
|
|
|
|
void CChannel::SetName ( const QString strNewName )
|
|
{
|
|
bool bNameHasChanged = false;
|
|
|
|
Mutex.lock();
|
|
{
|
|
// apply value (if different from previous name)
|
|
if ( sName.compare ( strNewName ) )
|
|
{
|
|
sName = strNewName;
|
|
bNameHasChanged = true;
|
|
}
|
|
}
|
|
Mutex.unlock();
|
|
|
|
// fire message that name has changed
|
|
if ( bNameHasChanged )
|
|
{
|
|
// the "emit" has to be done outside the mutexed region
|
|
emit NameHasChanged();
|
|
}
|
|
}
|
|
QString CChannel::GetName()
|
|
{
|
|
// make sure the string is not written at the same time when it is
|
|
// read here -> use mutex to secure access
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
return sName;
|
|
}
|
|
|
|
void CChannel::OnSendProtMessage ( CVector<uint8_t> vecMessage )
|
|
{
|
|
// only send messages if protocol is enabled, otherwise delete complete
|
|
// queue
|
|
if ( ProtocolIsEnabled() )
|
|
{
|
|
// emit message to actually send the data
|
|
emit MessReadyForSending ( vecMessage );
|
|
}
|
|
else
|
|
{
|
|
// delete send message queue
|
|
Protocol.Reset();
|
|
}
|
|
}
|
|
|
|
void CChannel::OnJittBufSizeChange ( int iNewJitBufSize )
|
|
{
|
|
SetSockBufSize ( iNewJitBufSize );
|
|
}
|
|
|
|
void CChannel::OnChangeChanGain ( int iChanID, double dNewGain )
|
|
{
|
|
SetGain ( iChanID, dNewGain );
|
|
}
|
|
|
|
void CChannel::OnChangeChanName ( QString strName )
|
|
{
|
|
SetName ( strName );
|
|
}
|
|
|
|
bool CChannel::GetAddress(CHostAddress& RetAddr)
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
if ( IsConnected() )
|
|
{
|
|
RetAddr = InetAddr;
|
|
return true;
|
|
}
|
|
else
|
|
{
|
|
RetAddr = CHostAddress();
|
|
return false;
|
|
}
|
|
}
|
|
|
|
void CChannel::OnNetTranspPropsReceived ( CNetworkTransportProps NetworkTransportProps )
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// apply received parameters to internal data struct
|
|
vecNetwBufferInProps[0].iAudioBlockSize = NetworkTransportProps.iMonoAudioBlockSize;
|
|
vecNetwBufferInProps[0].eAudComprType = NetworkTransportProps.eAudioCodingType;
|
|
vecNetwBufferInProps[0].iNetwInBufSize = AudioCompressionIn.Init (
|
|
vecNetwBufferInProps[0].iAudioBlockSize,
|
|
vecNetwBufferInProps[0].eAudComprType );
|
|
}
|
|
|
|
void CChannel::OnReqNetTranspProps()
|
|
{
|
|
CreateNetTranspPropsMessFromCurrentSettings();
|
|
}
|
|
|
|
void CChannel::CreateNetTranspPropsMessFromCurrentSettings()
|
|
{
|
|
CNetworkTransportProps NetworkTransportProps (
|
|
iAudComprSizeOut,
|
|
iCurAudioBlockSizeOut,
|
|
1, // right now we only use mono
|
|
SYSTEM_SAMPLE_RATE, // right now only one sample rate is supported
|
|
AudioCompressionOut.GetType(),
|
|
0 );
|
|
|
|
// send current network transport properties
|
|
Protocol.CreateNetwTranspPropsMes ( NetworkTransportProps );
|
|
}
|
|
|
|
EPutDataStat CChannel::PutData ( const CVector<unsigned char>& vecbyData,
|
|
int iNumBytes )
|
|
{
|
|
EPutDataStat eRet = PS_GEN_ERROR;
|
|
|
|
// init flags
|
|
bool bIsProtocolPacket = false;
|
|
bool bIsAudioPacket = false;
|
|
bool bNewConnection = false;
|
|
bool bReinitializeIn = false;
|
|
|
|
// intermediate storage for new parameters
|
|
int iNewAudioBlockSize;
|
|
EAudComprType eNewAudComprType;
|
|
|
|
if ( bIsEnabled )
|
|
{
|
|
// first check if this is protocol data
|
|
// only use protocol data if protocol mechanism is enabled
|
|
if ( ProtocolIsEnabled() )
|
|
{
|
|
// parse the message assuming this is a protocol message
|
|
if ( !Protocol.ParseMessage ( vecbyData, iNumBytes ) )
|
|
{
|
|
// set status flags
|
|
eRet = PS_PROT_OK;
|
|
bIsProtocolPacket = true;
|
|
}
|
|
}
|
|
|
|
// only try to parse audio if it was not a protocol packet
|
|
if ( !bIsProtocolPacket )
|
|
{
|
|
Mutex.lock();
|
|
{
|
|
// check if this is an audio packet by checking all possible lengths
|
|
const int iPossNetwSizes = vecNetwBufferInProps.Size();
|
|
|
|
for ( int i = 0; i < iPossNetwSizes; i++ )
|
|
{
|
|
// check for low/high quality audio packets and set flags
|
|
if ( iNumBytes == vecNetwBufferInProps[i].iNetwInBufSize )
|
|
{
|
|
bIsAudioPacket = true;
|
|
|
|
// check if we are correctly initialized
|
|
iNewAudioBlockSize =
|
|
vecNetwBufferInProps[i].iAudioBlockSize;
|
|
|
|
eNewAudComprType =
|
|
vecNetwBufferInProps[i].eAudComprType;
|
|
|
|
if ( ( iNewAudioBlockSize != iCurAudioBlockSizeIn ) ||
|
|
( eNewAudComprType != AudioCompressionIn.GetType() ) )
|
|
{
|
|
bReinitializeIn = true;
|
|
}
|
|
}
|
|
}
|
|
Mutex.unlock();
|
|
|
|
// actual initialization call has to be made
|
|
// outside the mutex region since it internally
|
|
// usees the same mutex, too
|
|
if ( bReinitializeIn )
|
|
{
|
|
// re-initialize to new value
|
|
SetAudioBlockSizeAndComprIn (
|
|
iNewAudioBlockSize, eNewAudComprType );
|
|
}
|
|
}
|
|
|
|
Mutex.lock();
|
|
{
|
|
// only process audio if packet has correct size
|
|
if ( bIsAudioPacket )
|
|
{
|
|
// decompress audio
|
|
CVector<short> vecsDecomprAudio ( AudioCompressionIn.Decode ( vecbyData ) );
|
|
|
|
// convert received data from short to double
|
|
const int iAudioSize = vecsDecomprAudio.Size();
|
|
CVector<double> vecdDecomprAudio ( iAudioSize );
|
|
for ( int i = 0; i < iAudioSize; i++ )
|
|
{
|
|
vecdDecomprAudio[i] = static_cast<double> ( vecsDecomprAudio[i] );
|
|
}
|
|
|
|
if ( SockBuf.Put ( vecdDecomprAudio ) )
|
|
{
|
|
eRet = PS_AUDIO_OK;
|
|
}
|
|
else
|
|
{
|
|
eRet = PS_AUDIO_ERR;
|
|
}
|
|
|
|
// update cycle time variance measurement, take care of
|
|
// re-initialization, too, if necessary
|
|
if ( iAudioSize != CycleTimeVariance.GetBlockLength() )
|
|
{
|
|
// re-init (we have to use the buffer size which works
|
|
// on the system sample rate, therefore we use the
|
|
// decompressed audio buffer size instead of the network
|
|
// buffer size)
|
|
CycleTimeVariance.Init ( iAudioSize, TIME_MOV_AV_RESPONSE );
|
|
CycleTimeVariance.Reset();
|
|
}
|
|
else
|
|
{
|
|
|
|
// TODO only update if time difference of received packets is below
|
|
// a limit to avoid having short network troubles incorporated in the
|
|
// statistic
|
|
|
|
CycleTimeVariance.Update();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// the protocol parsing failed and this was no audio block,
|
|
// we treat this as protocol error (unkown packet)
|
|
eRet = PS_PROT_ERR;
|
|
}
|
|
|
|
// all network packets except of valid llcon protocol messages
|
|
// regardless if they are valid or invalid audio packets lead to
|
|
// a state change to a connected channel
|
|
// this is because protocol messages can only be sent on a
|
|
// connected channel and the client has to inform the server
|
|
// about the audio packet properties via the protocol
|
|
|
|
// check if channel was not connected, this is a new connection
|
|
bNewConnection = !IsConnected();
|
|
|
|
// reset time-out counter
|
|
iConTimeOut = iConTimeOutStartVal;
|
|
}
|
|
Mutex.unlock();
|
|
}
|
|
|
|
if ( bNewConnection )
|
|
{
|
|
// if this is a new connection and the current network packet is
|
|
// neither an audio or protocol packet, we have to query the
|
|
// network transport properties for the audio packets
|
|
if ( ( !bIsProtocolPacket ) && ( !bIsAudioPacket ) )
|
|
{
|
|
Protocol.CreateReqNetwTranspPropsMes();
|
|
}
|
|
|
|
// reset cycle time variance measurement
|
|
CycleTimeVariance.Reset();
|
|
|
|
// inform other objects that new connection was established
|
|
emit NewConnection();
|
|
}
|
|
}
|
|
|
|
return eRet;
|
|
}
|
|
|
|
EGetDataStat CChannel::GetData ( CVector<double>& vecdData )
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
EGetDataStat eGetStatus;
|
|
|
|
const bool bSockBufState = SockBuf.Get ( vecdData );
|
|
|
|
// decrease time-out counter
|
|
if ( iConTimeOut > 0 )
|
|
{
|
|
// subtract the number of samples of the current block since the
|
|
// time out counter is based on samples not on blocks
|
|
iConTimeOut -= vecdData.Size();
|
|
|
|
if ( iConTimeOut <= 0 )
|
|
{
|
|
// channel is just disconnected
|
|
eGetStatus = GS_CHAN_NOW_DISCONNECTED;
|
|
iConTimeOut = 0; // make sure we do not have negative values
|
|
}
|
|
else
|
|
{
|
|
if ( bSockBufState )
|
|
{
|
|
// everything is ok
|
|
eGetStatus = GS_BUFFER_OK;
|
|
}
|
|
else
|
|
{
|
|
// channel is not yet disconnected but no data in buffer
|
|
eGetStatus = GS_BUFFER_UNDERRUN;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// channel is disconnected
|
|
eGetStatus = GS_CHAN_NOT_CONNECTED;
|
|
}
|
|
|
|
return eGetStatus;
|
|
}
|
|
|
|
CVector<unsigned char> CChannel::PrepSendPacket ( const CVector<short>& vecsNPacket )
|
|
{
|
|
QMutexLocker locker ( &Mutex );
|
|
|
|
// if the block is not ready we have to initialize with zero length to
|
|
// tell the following network send routine that nothing should be sent
|
|
CVector<unsigned char> vecbySendBuf ( 0 );
|
|
|
|
if ( bIsServer )
|
|
{
|
|
// use conversion buffer to convert sound card block size in network
|
|
// block size
|
|
if ( ConvBuf.Put ( vecsNPacket ) )
|
|
{
|
|
// a packet is ready, compress audio
|
|
vecbySendBuf.Init ( iAudComprSizeOut );
|
|
vecbySendBuf = AudioCompressionOut.Encode ( ConvBuf.Get() );
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// a packet is ready, compress audio
|
|
vecbySendBuf.Init ( iAudComprSizeOut );
|
|
vecbySendBuf = AudioCompressionOut.Encode ( vecsNPacket );
|
|
}
|
|
|
|
return vecbySendBuf;
|
|
}
|
|
|
|
int CChannel::GetUploadRateKbps()
|
|
{
|
|
int iAudioSizeOut;
|
|
|
|
if ( bIsServer )
|
|
{
|
|
iAudioSizeOut = iCurNetwOutBlSiFact * MIN_SERVER_BLOCK_SIZE_SAMPLES;
|
|
}
|
|
else
|
|
{
|
|
iAudioSizeOut = iCurAudioBlockSizeOut;
|
|
}
|
|
|
|
// we assume that the UDP packet which is transported via IP has an
|
|
// additional header size of
|
|
// 8 (UDP) + 20 (IP without optional fields) = 28 bytes
|
|
return ( iAudComprSizeOut + 28 /* header */ ) * 8 /* bits per byte */ *
|
|
SYSTEM_SAMPLE_RATE / iAudioSizeOut / 1000;
|
|
}
|