diff --git a/src/channel.cpp b/src/channel.cpp index c4842159..ec5f8cc8 100755 --- a/src/channel.cpp +++ b/src/channel.cpp @@ -1,517 +1,516 @@ -/******************************************************************************\ - * 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" - - -/* Implementation *************************************************************/ -CChannel::CChannel ( const bool bNIsServer ) : - bIsServer ( bNIsServer ), - sName ( "" ), - vecdGains ( USED_NUM_CHANNELS, (double) 1.0 ), - bIsEnabled ( false ), - iNetwFrameSizeFact ( FRAME_SIZE_FACTOR_DEFAULT ), - iNetwFrameSize ( 20 ) // must be > 0 and should be close to a valid size -{ - // 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 - SetSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ); - - // initialize cycle time variance measurement with defaults - CycleTimeVariance.Init ( SYSTEM_FRAME_SIZE_SAMPLES, - SYSTEM_SAMPLE_RATE, TIME_MOV_AV_RESPONSE ); - - - // connections ------------------------------------------------------------- - QObject::connect ( &Protocol, - SIGNAL ( MessReadyForSending ( CVector ) ), - this, SLOT ( OnSendProtMessage ( CVector ) ) ); - - 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 ) ), - SIGNAL ( ConClientListMesReceived ( CVector ) ) ); - - 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() ) ); - - QObject::connect ( &Protocol, SIGNAL ( Disconnection() ), - this, SLOT ( OnDisconnection() ) ); -} - -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::SetNetwFrameSizeAndFact ( const int iNewNetwFrameSize, - const int iNewNetwFrameSizeFact ) -{ - // this function is intended for the server (not the client) - QMutexLocker locker ( &Mutex ); - - // store new values - iNetwFrameSize = iNewNetwFrameSize; - iNetwFrameSizeFact = iNewNetwFrameSizeFact; - - // init conversion buffer - ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); - - // initialize and reset cycle time variance measurement - CycleTimeVariance.Init ( iNetwFrameSizeFact * SYSTEM_FRAME_SIZE_SAMPLES, - SYSTEM_SAMPLE_RATE, TIME_MOV_AV_RESPONSE ); - - CycleTimeVariance.Reset(); - - // tell the server that audio coding has changed - CreateNetTranspPropsMessFromCurrentSettings(); -} - -bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames ) -{ - QMutexLocker locker ( &Mutex ); // this opperation must be done with mutex - - // first check for valid input parameter range - if ( ( iNewNumFrames >= MIN_NET_BUF_SIZE_NUM_BL ) && - ( iNewNumFrames <= MAX_NET_BUF_SIZE_NUM_BL ) ) - { - // store new value - iCurSockBufNumFrames = iNewNumFrames; - - // the network block size is a multiple of the minimum network - // block size - SockBuf.Init ( iNetwFrameSize, iNewNumFrames ); - - return false; // -> no error - } - - return true; // set error flag -} - -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 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 ) -{ - SetSockBufNumFrames ( 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 ) -{ - // only the server shall act on network transport properties message - if ( bIsServer ) - { - QMutexLocker locker ( &Mutex ); - - // store received parameters - iNetwFrameSizeFact = NetworkTransportProps.iBlockSizeFact; - iNetwFrameSize = - NetworkTransportProps.iNetworkPacketSize / - NetworkTransportProps.iBlockSizeFact; - - // update socket buffer (the network block size is a multiple of the - // minimum network frame size - SockBuf.Init ( iNetwFrameSize, iCurSockBufNumFrames ); - - // init conversion buffer - ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); - } -} - -void CChannel::OnReqNetTranspProps() -{ - CreateNetTranspPropsMessFromCurrentSettings(); -} - -void CChannel::CreateNetTranspPropsMessFromCurrentSettings() -{ - CNetworkTransportProps NetworkTransportProps ( - iNetwFrameSize * iNetwFrameSizeFact, - iNetwFrameSizeFact, - 1, // right now we only use mono - SYSTEM_SAMPLE_RATE, - CT_CELT, // always CELT coding - 0, - 0 ); - - // send current network transport properties - Protocol.CreateNetwTranspPropsMes ( NetworkTransportProps ); -} - -void CChannel::OnDisconnection() -{ - // set time out counter to a small value > 0 so that the next time a - // received audio block is queried, the disconnection is performed - // (assuming that no audio packet is received in the meantime) - iConTimeOut = 1; // a small number > 0 -} - -EPutDataStat CChannel::PutData ( const CVector& vecbyData, - int iNumBytes ) -{ - EPutDataStat eRet = PS_GEN_ERROR; - - // init flags - bool bIsProtocolPacket = false; - bool bIsAudioPacket = false; - bool bNewConnection = false; - - 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(); - { - - -// TODO only process data if network properties protocol message has been arrived - - - // only process audio if packet has correct size - if ( iNumBytes == ( iNetwFrameSize * iNetwFrameSizeFact ) ) - { - // set audio packet flag - bIsAudioPacket = true; - - // store new packet in jitter buffer - if ( SockBuf.Put ( vecbyData, iNumBytes ) ) - { - eRet = PS_AUDIO_OK; - } - else - { - eRet = PS_AUDIO_ERR; - } - - // update cycle time variance measurement (this is only - // used by the client so do not update for server channel) - if ( !bIsServer ) - { - -// 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 - // (this is only required for server since we defined that the - // server has to send with the same properties as sent by - // the client) - if ( bIsServer && ( !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& vecbyData ) -{ - QMutexLocker locker ( &Mutex ); - - EGetDataStat eGetStatus; - - const bool bSockBufState = SockBuf.Get ( vecbyData ); - - // 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 (definition: - // always one atomic block is get by using the GetData() function - // where the atomic block size is "SYSTEM_FRAME_SIZE_SAMPLES") - -// TODO this code only works with the above assumption -> better -// implementation so that we are not depending on assumptions - - iConTimeOut -= SYSTEM_FRAME_SIZE_SAMPLES; - - 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 CChannel::PrepSendPacket ( const CVector& vecbyNPacket ) -{ - 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 vecbySendBuf ( 0 ); - - // use conversion buffer to convert sound card block size in network - // block size - if ( ConvBuf.Put ( vecbyNPacket ) ) - { - // a packet is ready - vecbySendBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); - vecbySendBuf = ConvBuf.Get(); - } - - return vecbySendBuf; -} - -int CChannel::GetUploadRateKbps() -{ - const int iAudioSizeOut = iNetwFrameSizeFact * SYSTEM_FRAME_SIZE_SAMPLES; - - // 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 ( iNetwFrameSize * iNetwFrameSizeFact + 28 /* header */ ) * - 8 /* bits per byte */ * - SYSTEM_SAMPLE_RATE / iAudioSizeOut / 1000; -} +/******************************************************************************\ + * 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" + + +/* Implementation *************************************************************/ +CChannel::CChannel ( const bool bNIsServer ) : + bIsServer ( bNIsServer ), + sName ( "" ), + vecdGains ( USED_NUM_CHANNELS, (double) 1.0 ), + bIsEnabled ( false ), + iNetwFrameSizeFact ( FRAME_SIZE_FACTOR_DEFAULT ), + iNetwFrameSize ( 20 ) // must be > 0 and should be close to a valid size +{ + // 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 + SetSockBufNumFrames ( DEF_NET_BUF_SIZE_NUM_BL ); + + // initialize cycle time variance measurement with defaults + CycleTimeVariance.Init ( SYSTEM_FRAME_SIZE_SAMPLES, + SYSTEM_SAMPLE_RATE, TIME_MOV_AV_RESPONSE ); + + + // connections ------------------------------------------------------------- + QObject::connect ( &Protocol, + SIGNAL ( MessReadyForSending ( CVector ) ), + this, SLOT ( OnSendProtMessage ( CVector ) ) ); + + 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 ) ), + SIGNAL ( ConClientListMesReceived ( CVector ) ) ); + + 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() ) ); + + QObject::connect ( &Protocol, SIGNAL ( Disconnection() ), + this, SLOT ( OnDisconnection() ) ); +} + +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::SetNetwFrameSizeAndFact ( const int iNewNetwFrameSize, + const int iNewNetwFrameSizeFact ) +{ + // this function is intended for the server (not the client) + QMutexLocker locker ( &Mutex ); + + // store new values + iNetwFrameSize = iNewNetwFrameSize; + iNetwFrameSizeFact = iNewNetwFrameSizeFact; + + // init conversion buffer + ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); + + // initialize and reset cycle time variance measurement + CycleTimeVariance.Init ( iNetwFrameSizeFact * SYSTEM_FRAME_SIZE_SAMPLES, + SYSTEM_SAMPLE_RATE, TIME_MOV_AV_RESPONSE ); + + CycleTimeVariance.Reset(); + + // tell the server that audio coding has changed + CreateNetTranspPropsMessFromCurrentSettings(); +} + +bool CChannel::SetSockBufNumFrames ( const int iNewNumFrames ) +{ + QMutexLocker locker ( &Mutex ); // this opperation must be done with mutex + + // first check for valid input parameter range + if ( ( iNewNumFrames >= MIN_NET_BUF_SIZE_NUM_BL ) && + ( iNewNumFrames <= MAX_NET_BUF_SIZE_NUM_BL ) ) + { + // store new value + iCurSockBufNumFrames = iNewNumFrames; + + // the network block size is a multiple of the minimum network + // block size + SockBuf.Init ( iNetwFrameSize, iNewNumFrames ); + + return false; // -> no error + } + + return true; // set error flag +} + +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 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 ) +{ + SetSockBufNumFrames ( 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 ) +{ + // only the server shall act on network transport properties message + if ( bIsServer ) + { + QMutexLocker locker ( &Mutex ); + + // store received parameters + iNetwFrameSizeFact = NetworkTransportProps.iBlockSizeFact; + iNetwFrameSize = + NetworkTransportProps.iBaseNetworkPacketSize; + + // update socket buffer (the network block size is a multiple of the + // minimum network frame size + SockBuf.Init ( iNetwFrameSize, iCurSockBufNumFrames ); + + // init conversion buffer + ConvBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); + } +} + +void CChannel::OnReqNetTranspProps() +{ + CreateNetTranspPropsMessFromCurrentSettings(); +} + +void CChannel::CreateNetTranspPropsMessFromCurrentSettings() +{ + CNetworkTransportProps NetworkTransportProps ( + iNetwFrameSize, + iNetwFrameSizeFact, + 1, // right now we only use mono + SYSTEM_SAMPLE_RATE, + CT_CELT, // always CELT coding + 0, + 0 ); + + // send current network transport properties + Protocol.CreateNetwTranspPropsMes ( NetworkTransportProps ); +} + +void CChannel::OnDisconnection() +{ + // set time out counter to a small value > 0 so that the next time a + // received audio block is queried, the disconnection is performed + // (assuming that no audio packet is received in the meantime) + iConTimeOut = 1; // a small number > 0 +} + +EPutDataStat CChannel::PutData ( const CVector& vecbyData, + int iNumBytes ) +{ + EPutDataStat eRet = PS_GEN_ERROR; + + // init flags + bool bIsProtocolPacket = false; + bool bIsAudioPacket = false; + bool bNewConnection = false; + + 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(); + { + + +// TODO only process data if network properties protocol message has been arrived + + + // only process audio if packet has correct size + if ( iNumBytes == ( iNetwFrameSize * iNetwFrameSizeFact ) ) + { + // set audio packet flag + bIsAudioPacket = true; + + // store new packet in jitter buffer + if ( SockBuf.Put ( vecbyData, iNumBytes ) ) + { + eRet = PS_AUDIO_OK; + } + else + { + eRet = PS_AUDIO_ERR; + } + + // update cycle time variance measurement (this is only + // used by the client so do not update for server channel) + if ( !bIsServer ) + { + +// 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 + // (this is only required for server since we defined that the + // server has to send with the same properties as sent by + // the client) + if ( bIsServer && ( !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& vecbyData ) +{ + QMutexLocker locker ( &Mutex ); + + EGetDataStat eGetStatus; + + const bool bSockBufState = SockBuf.Get ( vecbyData ); + + // 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 (definition: + // always one atomic block is get by using the GetData() function + // where the atomic block size is "SYSTEM_FRAME_SIZE_SAMPLES") + +// TODO this code only works with the above assumption -> better +// implementation so that we are not depending on assumptions + + iConTimeOut -= SYSTEM_FRAME_SIZE_SAMPLES; + + 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 CChannel::PrepSendPacket ( const CVector& vecbyNPacket ) +{ + 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 vecbySendBuf ( 0 ); + + // use conversion buffer to convert sound card block size in network + // block size + if ( ConvBuf.Put ( vecbyNPacket ) ) + { + // a packet is ready + vecbySendBuf.Init ( iNetwFrameSize * iNetwFrameSizeFact ); + vecbySendBuf = ConvBuf.Get(); + } + + return vecbySendBuf; +} + +int CChannel::GetUploadRateKbps() +{ + const int iAudioSizeOut = iNetwFrameSizeFact * SYSTEM_FRAME_SIZE_SAMPLES; + + // 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 ( iNetwFrameSize * iNetwFrameSizeFact + 28 /* header */ ) * + 8 /* bits per byte */ * + SYSTEM_SAMPLE_RATE / iAudioSizeOut / 1000; +} diff --git a/src/protocol.cpp b/src/protocol.cpp index 7ebd2ced..66e731e8 100755 --- a/src/protocol.cpp +++ b/src/protocol.cpp @@ -1,1142 +1,1142 @@ -/******************************************************************************\ - * Copyright (c) 2004-2009 - * - * Author(s): - * Volker Fischer - * - -Protocol message definition ---------------------------- - -- All messages received need to be acknowledged by an acknowledge packet - - - -MAIN FRAME ----------- - - +-------------+------------+------------+------------------+ ... - | 2 bytes TAG | 2 bytes ID | 1 byte cnt | 2 bytes length n | ... - +-------------+------------+------------+------------------+ ... - ... --------------+-------------+ - ... n bytes data | 2 bytes CRC | - ... --------------+-------------+ - -- TAG is an all zero bit word to identify protocol messages -- message ID defined by the defines PROTMESSID_x -- cnt: counter which is increment for each message and wraps around at 255 -- length n in bytes of the data -- actual data, dependent on message type -- 16 bits CRC, calculating over the entire message, is transmitted inverted - Generator polynom: G_16(x) = x^16 + x^12 + x^5 + 1, initial state: all ones - - -MESSAGES --------- - -- Acknowledgement message: PROTMESSID_ACKN - - +-----------------------------------+ - | 2 bytes ID of message to be ackn. | - +-----------------------------------+ - - note: the cnt value is the same as of the message to be acknowledged - - -- Jitter buffer size: PROTMESSID_JITT_BUF_SIZE - - +--------------------------+ - | 2 bytes number of blocks | - +--------------------------+ - - -- Request jitter buffer size: PROTMESSID_REQ_JITT_BUF_SIZE - - note: does not have any data -> n = 0 - - -- Server full message: PROTMESSID_SERVER_FULL - - note: does not have any data -> n = 0 - - -- Network buffer block size factor: PROTMESSID_NET_BLSI_FACTOR - - note: size, relative to minimum block size - - +----------------+ - | 2 bytes factor | - +----------------+ - - -- Gain of channel: PROTMESSID_CHANNEL_GAIN - - +-------------------+--------------+ - | 1 byte channel ID | 2 bytes gain | - +-------------------+--------------+ - - -- IP number and name of connected clients: PROTMESSID_CONN_CLIENTS_LIST - - for each connected client append following data: - - +-------------------+--------------------+------------------+ ... - | 1 byte channel ID | 4 bytes IP address | 2 bytes number n | ... - +-------------------+--------------------+------------------+ ... - ... ----------------------+ - ... n bytes UTF-8 string | - ... ----------------------+ - -- Request connected clients list: PROTMESSID_REQ_CONN_CLIENTS_LIST - - note: does not have any data -> n = 0 - - -- Name of channel: PROTMESSID_CHANNEL_NAME - - for each connected client append following data: - - +------------------+----------------------+ - | 2 bytes number n | n bytes UTF-8 string | - +------------------+----------------------+ - - -- Chat text: PROTMESSID_CHAT_TEXT - - +------------------+----------------------+ - | 2 bytes number n | n bytes UTF-8 string | - +------------------+----------------------+ - - -- Ping message (for measuring the ping time): PROTMESSID_PING_MS - - +-----------------------------+ - | 4 bytes transmit time in ms | - +-----------------------------+ - - -- Properties for network transport: PROTMESSID_NETW_TRANSPORT_PROPS - - +-------------------+-------------------------+-----------------+ ... - | 4 bytes netw size | 2 bytes block size fact | 1 byte num chan | ... - +-------------------+-------------------------+-----------------+ ... - ... ------------------+-----------------------+ ... - ... 4 bytes sam rate | 2 bytes audiocod type | ... - ... ------------------+-----------------------+ ... - ... -----------------+----------------------+ - ... 2 bytes version | 4 bytes audiocod arg | - ... -----------------+----------------------+ - - - "netw size": length of the network packet in bytes - - "block size fact": block size factor - - "num chan": number of channels of the audio signal, e.g. "2" is stereo - - "sam rate": sample rate of the audio stream - - "audiocod type": audio coding type, the following types are supported: - - 0: none, no audio coding applied - - 1: CELT - - "version": version of the audio coder, if not used this value shall be set to 0 - - "audiocod arg": argument for the audio coder, if not used this value shall be set to 0 - - -- Request properties for network transport: PROTMESSID_REQ_NETW_TRANSPORT_PROPS - - note: does not have any data -> n = 0 - - -- Disconnect message: PROTMESSID_DISCONNECTION - - note: does not have any data -> n = 0 - - - ****************************************************************************** - * - * 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 "protocol.h" - - -/* Implementation *************************************************************/ -CProtocol::CProtocol() -{ - Reset(); - - // connections - QObject::connect ( &TimerSendMess, SIGNAL ( timeout() ), - this, SLOT ( OnTimerSendMess() ) ); -} - -void CProtocol::Reset() -{ - QMutexLocker locker ( &Mutex ); - - // prepare internal variables for initial protocol transfer - iCounter = 0; - iOldRecID = PROTMESSID_ILLEGAL; - iOldRecCnt = 0; - - // delete complete "send message queue" - SendMessQueue.clear(); -} - -void CProtocol::EnqueueMessage ( CVector& vecMessage, - const int iCnt, - const int iID ) -{ - bool bListWasEmpty; - - Mutex.lock(); - { - // check if list is empty so that we have to initiate a send process - bListWasEmpty = SendMessQueue.empty(); - - // create send message object for the queue - CSendMessage SendMessageObj ( vecMessage, iCnt, iID ); - - // we want to have a FIFO: we add at the end and take from the beginning - SendMessQueue.push_back ( SendMessageObj ); - } - Mutex.unlock(); - - // if list was empty, initiate send process - if ( bListWasEmpty ) - { - SendMessage(); - } -} - -void CProtocol::SendMessage() -{ - CVector vecMessage; - bool bSendMess = false; - - Mutex.lock(); - { - // we have to check that list is not empty, since in another thread the - // last element of the list might have been erased - if ( !SendMessQueue.empty() ) - { - vecMessage.Init ( SendMessQueue.front().vecMessage.Size() ); - vecMessage = SendMessQueue.front().vecMessage; - - bSendMess = true; - } - } - Mutex.unlock(); - - if ( bSendMess ) - { - // send message - emit MessReadyForSending ( vecMessage ); - - // start time-out timer if not active - if ( !TimerSendMess.isActive() ) - { - TimerSendMess.start ( SEND_MESS_TIMEOUT_MS ); - } - } - else - { - // no message to send, stop timer - TimerSendMess.stop(); - } -} - -void CProtocol::CreateAndSendMessage ( const int iID, - const CVector& vecData ) -{ - CVector vecNewMessage; - int iCurCounter; - - Mutex.lock(); - { - // store current counter value - iCurCounter = iCounter; - - // increase counter (wraps around automatically) - iCounter++; - } - Mutex.unlock(); - - // build complete message - GenMessageFrame ( vecNewMessage, iCurCounter, iID, vecData ); - - // enqueue message - EnqueueMessage ( vecNewMessage, iCurCounter, iID ); -} - -void CProtocol::CreateAndSendAcknMess ( const int& iID, const int& iCnt ) -{ - CVector vecAcknMessage; - CVector vecData ( 2 ); // 2 bytes of data - unsigned int iPos = 0; // init position pointer - - // build data vector - PutValOnStream ( vecData, iPos, static_cast ( iID ), 2 ); - - // build complete message - GenMessageFrame ( vecAcknMessage, iCnt, PROTMESSID_ACKN, vecData ); - - // immediately send acknowledge message - emit MessReadyForSending ( vecAcknMessage ); -} - -bool CProtocol::ParseMessage ( const CVector& vecbyData, - const int iNumBytes ) -{ -/* - return code: false -> ok; true -> error -*/ - bool bRet = false; - bool bSendNextMess; - int iRecCounter, iRecID; - CVector vecData; - - if ( !ParseMessageFrame ( vecbyData, iNumBytes, iRecCounter, iRecID, vecData ) ) - { - // In case we received a message and returned an answer but our answer - // did not make it to the receiver, he will resend his message. We check - // here if the message is the same as the old one, and if this is the - // case, just resend our old answer again - if ( ( iOldRecID == iRecID ) && ( iOldRecCnt == iRecCounter ) ) - { - // acknowledgments are not acknowledged - if ( iRecID != PROTMESSID_ACKN ) - { - // resend acknowledgement - CreateAndSendAcknMess ( iRecID, iRecCounter ); - } - } - else - { - // special treatment for acknowledge messages - if ( iRecID == PROTMESSID_ACKN ) - { - // extract data from stream and emit signal for received value - unsigned int iPos = 0; - const int iData = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - Mutex.lock(); - { - // check if this is the correct acknowledgment - bSendNextMess = false; - if ( !SendMessQueue.empty() ) - { - if ( ( SendMessQueue.front().iCnt == iRecCounter ) && - ( SendMessQueue.front().iID == iData ) ) - { - // message acknowledged, remove from queue - SendMessQueue.pop_front(); - - // send next message in queue - bSendNextMess = true; - } - } - } - Mutex.unlock(); - - if ( bSendNextMess ) - { - SendMessage(); - } - } - else - { - // check which type of message we received and do action - switch ( iRecID ) - { - case PROTMESSID_JITT_BUF_SIZE: - bRet = EvaluateJitBufMes ( vecData ); - break; - - case PROTMESSID_REQ_JITT_BUF_SIZE: - bRet = EvaluateReqJitBufMes ( vecData ); - break; - - case PROTMESSID_SERVER_FULL: - bRet = EvaluateServerFullMes ( vecData ); - break; - - case PROTMESSID_NET_BLSI_FACTOR: - bRet = EvaluateNetwBlSiFactMes ( vecData ); - break; - - case PROTMESSID_CHANNEL_GAIN: - bRet = EvaluateChanGainMes ( vecData ); - break; - - case PROTMESSID_CONN_CLIENTS_LIST: - bRet = EvaluateConClientListMes ( vecData ); - break; - - case PROTMESSID_REQ_CONN_CLIENTS_LIST: - bRet = EvaluateReqConnClientsList ( vecData ); - break; - - case PROTMESSID_CHANNEL_NAME: - bRet = EvaluateChanNameMes ( vecData ); - break; - - case PROTMESSID_CHAT_TEXT: - bRet = EvaluateChatTextMes ( vecData ); - break; - - case PROTMESSID_PING_MS: - bRet = EvaluatePingMes ( vecData ); - break; - - case PROTMESSID_NETW_TRANSPORT_PROPS: - bRet = EvaluateNetwTranspPropsMes ( vecData ); - break; - - case PROTMESSID_REQ_NETW_TRANSPORT_PROPS: - bRet = EvaluateReqNetwTranspPropsMes ( vecData ); - break; - - case PROTMESSID_DISCONNECTION: - bRet = EvaluateDisconnectionMes ( vecData ); - break; - } - - // send acknowledge message - CreateAndSendAcknMess ( iRecID, iRecCounter ); - } - } - - // save current message ID and counter to find out if message was resent - iOldRecID = iRecID; - iOldRecCnt = iRecCounter; - } - else - { - bRet = true; // return error code - } - - return bRet; -} - - -// Access-functions for creating and parsing messages -------------------------- -void CProtocol::CreateJitBufMes ( const int iJitBufSize ) -{ - CVector vecData ( 2 ); // 2 bytes of data - unsigned int iPos = 0; // init position pointer - - // build data vector - PutValOnStream ( vecData, iPos, static_cast ( iJitBufSize ), 2 ); - - CreateAndSendMessage ( PROTMESSID_JITT_BUF_SIZE, vecData ); -} - -bool CProtocol::EvaluateJitBufMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 2 ) - { - return true; - } - - // extract jitter buffer size - const int iData = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // invoke message action - emit ChangeJittBufSize ( iData ); - - return false; // no error -} - -void CProtocol::CreateReqJitBufMes() -{ - CreateAndSendMessage ( PROTMESSID_REQ_JITT_BUF_SIZE, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateReqJitBufMes ( const CVector& vecData ) -{ - // invoke message action - emit ReqJittBufSize(); - - return false; // no error -} - -void CProtocol::CreateServerFullMes() -{ - CreateAndSendMessage ( PROTMESSID_SERVER_FULL, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateServerFullMes ( const CVector& vecData ) -{ - // invoke message action - emit ServerFull(); - - return false; // no error -} - -void CProtocol::CreateNetwBlSiFactMes ( const int iNetwBlSiFact ) -{ - CVector vecData ( 2 ); // 2 bytes of data - unsigned int iPos = 0; // init position pointer - - // build data vector - PutValOnStream ( vecData, iPos, static_cast ( iNetwBlSiFact ), 2 ); - - CreateAndSendMessage ( PROTMESSID_NET_BLSI_FACTOR, vecData ); -} - -bool CProtocol::EvaluateNetwBlSiFactMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 2 ) - { - return true; - } - - const int iData = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // invoke message action - emit ChangeNetwBlSiFact ( iData ); - - return false; // no error -} - -void CProtocol::CreateChanGainMes ( const int iChanID, const double dGain ) -{ - CVector vecData ( 3 ); // 3 bytes of data - unsigned int iPos = 0; // init position pointer - - // build data vector - // channel ID - PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); - - // actual gain, we convert from double with range 0..1 to integer - const int iCurGain = static_cast ( dGain * ( 1 << 15 ) ); - PutValOnStream ( vecData, iPos, static_cast ( iCurGain ), 2 ); - - CreateAndSendMessage ( PROTMESSID_CHANNEL_GAIN, vecData ); -} - -bool CProtocol::EvaluateChanGainMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 3 ) - { - return true; - } - - // channel ID - const int iCurID = - static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - - // actual gain, we convert from integer to double with range 0..1 - const int iData = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - const double dNewGain = static_cast ( iData ) / ( 1 << 15 ); - - // invoke message action - emit ChangeChanGain ( iCurID, dNewGain ); - - return false; // no error -} - -void CProtocol::CreateConClientListMes ( const CVector& vecChanInfo ) -{ - const int iNumClients = vecChanInfo.Size(); - - // build data vector - CVector vecData ( 0 ); - unsigned int iPos = 0; // init position pointer - - for ( int i = 0; i < iNumClients; i++ ) - { - // current string size - const int iCurStrLen = vecChanInfo[i].strName.size(); - - // size of current list entry - const int iCurListEntrLen = - 1 /* chan ID */ + 4 /* IP addr. */ + 2 /* str. size */ + iCurStrLen; - - // make space for new data - vecData.Enlarge ( iCurListEntrLen ); - - // channel ID - PutValOnStream ( vecData, iPos, - static_cast ( vecChanInfo[i].iChanID ), 1 ); - - // IP address (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( vecChanInfo[i].iIpAddr ), 4 ); - - // number of bytes for name string (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( iCurStrLen ), 2 ); - - // name string (n bytes) - for ( int j = 0; j < iCurStrLen; j++ ) - { - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, - static_cast ( vecChanInfo[i].strName[j].toAscii() ), 1 ); - } - } - - CreateAndSendMessage ( PROTMESSID_CONN_CLIENTS_LIST, vecData ); -} - -bool CProtocol::EvaluateConClientListMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - int iData; - const unsigned int iDataLen = vecData.Size(); - CVector vecChanInfo ( 0 ); - - while ( iPos < iDataLen ) - { - // check size (the first 7 bytes) - if ( iDataLen - iPos < 7 ) - { - return true; - } - - // channel ID (1 byte) - const int iChanID = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - - // IP address (4 bytes) - const int iIpAddr = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - // number of bytes for name string (2 bytes) - const unsigned int iStringLen = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // check size - if ( iDataLen - iPos < iStringLen ) - { - return true; - } - - // name string (n bytes) - QString strCurStr = ""; - for ( unsigned int j = 0; j < iStringLen; j++ ) - { - // byte-by-byte copying of the string data - iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - strCurStr += QString ( (char*) &iData ); - } - - // add channel information to vector - vecChanInfo.Add ( CChannelShortInfo ( iChanID, iIpAddr, strCurStr ) ); - } - - // invoke message action - emit ConClientListMesReceived ( vecChanInfo ); - - return false; // no error -} - -void CProtocol::CreateReqConnClientsList() -{ - CreateAndSendMessage ( PROTMESSID_REQ_CONN_CLIENTS_LIST, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateReqConnClientsList ( const CVector& vecData ) -{ - // invoke message action - emit ReqConnClientsList(); - - return false; // no error -} - -void CProtocol::CreateChanNameMes ( const QString strName ) -{ - unsigned int iPos = 0; // init position pointer - const int iStrLen = strName.size(); // get string size - - // size of current list entry - const int iEntrLen = 2 /* string size */ + iStrLen; - - // build data vector - CVector vecData ( iEntrLen ); - - // number of bytes for name string (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); - - // name string (n bytes) - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, - static_cast ( strName[j].toAscii() ), 1 ); - } - - CreateAndSendMessage ( PROTMESSID_CHANNEL_NAME, vecData ); -} - -bool CProtocol::EvaluateChanNameMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size (the first 2 bytes) - if ( vecData.Size() < 2 ) - { - return true; - } - - // number of bytes for name string (2 bytes) - const int iStrLen = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // check size - if ( vecData.Size() - 2 != iStrLen ) - { - return true; - } - - // name string (n bytes) - QString strName = ""; - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - strName += QString ( (char*) &iData ); - } - - // invoke message action - emit ChangeChanName ( strName ); - - return false; // no error -} - -void CProtocol::CreateChatTextMes ( const QString strChatText ) -{ - unsigned int iPos = 0; // init position pointer - const int iStrLen = strChatText.size(); // get string size - - // size of message body - const int iEntrLen = 2 /* string size */ + iStrLen; - - // build data vector - CVector vecData ( iEntrLen ); - - // number of bytes for name string (2 bytes) - PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); - - // name string (n bytes) - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, - static_cast ( strChatText[j].toAscii() ), 1 ); - } - - CreateAndSendMessage ( PROTMESSID_CHAT_TEXT, vecData ); -} - -bool CProtocol::EvaluateChatTextMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size (the first 2 bytes) - if ( vecData.Size() < 2 ) - { - return true; - } - - // number of bytes for name string (2 bytes) - const int iStrLen = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // check size - if ( vecData.Size() - 2 != iStrLen ) - { - return true; - } - - // name string (n bytes) - QString strChatText = ""; - for ( int j = 0; j < iStrLen; j++ ) - { - // byte-by-byte copying of the string data - int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - strChatText += QString ( (char*) &iData ); - } - - // invoke message action - emit ChatTextReceived ( strChatText ); - - return false; // no error -} - -void CProtocol::CreatePingMes ( const int iMs ) -{ - unsigned int iPos = 0; // init position pointer - - // build data vector (4 bytes long) - CVector vecData ( 4 ); - - // byte-by-byte copying of the string data - PutValOnStream ( vecData, iPos, static_cast ( iMs ), 4 ); - - CreateAndSendMessage ( PROTMESSID_PING_MS, vecData ); -} - -bool CProtocol::EvaluatePingMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - - // check size - if ( vecData.Size() != 4 ) - { - return true; - } - - emit PingReceived ( static_cast ( GetValFromStream ( vecData, iPos, 4 ) ) ); - - return false; // no error -} - -void CProtocol::CreateNetwTranspPropsMes ( const CNetworkTransportProps& NetTrProps ) -{ - unsigned int iPos = 0; // init position pointer - - // size of current message body - const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + - 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + - 2 /* version */ + 4 /* audiocod arg */; - - // build data vector - CVector vecData ( iEntrLen ); - - // length of the network packet in bytes (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iNetworkPacketSize ), 4 ); - - // block size factor (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iBlockSizeFact ), 2 ); - - // number of channels of the audio signal, e.g. "2" is stereo (1 byte) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iNumAudioChannels ), 1 ); - - // sample rate of the audio stream (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iSampleRate ), 4 ); - - // audio coding type (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.eAudioCodingType ), 2 ); - - // version (2 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iVersion ), 2 ); - - // argument for the audio coder (4 bytes) - PutValOnStream ( vecData, iPos, - static_cast ( NetTrProps.iAudioCodingArg ), 4 ); - - CreateAndSendMessage ( PROTMESSID_NETW_TRANSPORT_PROPS, vecData ); -} - -bool CProtocol::EvaluateNetwTranspPropsMes ( const CVector& vecData ) -{ - unsigned int iPos = 0; // init position pointer - CNetworkTransportProps ReceivedNetwTranspProps; - - // size of current message body - const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + - 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + - 2 /* version */ + 4 /* audiocod arg */; - - // check size - if ( vecData.Size() != iEntrLen ) - { - return true; - } - - // length of the network packet in bytes (4 bytes) - ReceivedNetwTranspProps.iNetworkPacketSize = - static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - if ( ( ReceivedNetwTranspProps.iNetworkPacketSize < 1 ) || - ( ReceivedNetwTranspProps.iNetworkPacketSize > MAX_SIZE_BYTES_NETW_BUF ) ) - { - return true; - } - - // block size factor (2 bytes) - ReceivedNetwTranspProps.iBlockSizeFact = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - if ( ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_PREFERRED ) && - ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_DEFAULT ) && - ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_SAFE ) ) - { - return true; - } - - // number of channels of the audio signal, e.g. "2" is stereo (1 byte) - ReceivedNetwTranspProps.iNumAudioChannels = - static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); - - if ( ( ReceivedNetwTranspProps.iNumAudioChannels != 1 ) && - ( ReceivedNetwTranspProps.iNumAudioChannels != 2 ) ) - { - return true; - } - - // sample rate of the audio stream (4 bytes) - ReceivedNetwTranspProps.iSampleRate = - static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - // audio coding type (2 bytes) with error check - const int iRecCodingType = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - if ( ( iRecCodingType != CT_NONE ) && - ( iRecCodingType != CT_CELT ) ) - { - return true; - } - - ReceivedNetwTranspProps.eAudioCodingType = - static_cast ( iRecCodingType ); - - // version (2 bytes) - ReceivedNetwTranspProps.iVersion = - static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); - - // argument for the audio coder (4 bytes) - ReceivedNetwTranspProps.iAudioCodingArg = - static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); - - // invoke message action - emit NetTranspPropsReceived ( ReceivedNetwTranspProps ); - - return false; // no error -} - -void CProtocol::CreateReqNetwTranspPropsMes() -{ - CreateAndSendMessage ( PROTMESSID_REQ_NETW_TRANSPORT_PROPS, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateReqNetwTranspPropsMes ( const CVector& vecData ) -{ - // invoke message action - emit ReqNetTranspProps(); - - return false; // no error -} - -void CProtocol::CreateDisconnectionMes() -{ - CreateAndSendMessage ( PROTMESSID_DISCONNECTION, CVector ( 0 ) ); -} - -bool CProtocol::EvaluateDisconnectionMes ( const CVector& vecData ) -{ - // invoke message action - emit Disconnection(); - - return false; // no error -} - - -/******************************************************************************\ -* Message generation (parsing) * -\******************************************************************************/ -bool CProtocol::ParseMessageFrame ( const CVector& vecIn, - const int iNumBytesIn, - int& iCnt, - int& iID, - CVector& vecData ) -{ -/* - return code: true -> ok; false -> error -*/ - int iLenBy, i; - unsigned int iCurPos; - - // vector must be at least "MESS_LEN_WITHOUT_DATA_BYTE" bytes long - if ( iNumBytesIn < MESS_LEN_WITHOUT_DATA_BYTE ) - { - return true; // return error code - } - - - // decode header ----- - iCurPos = 0; // start from beginning - - // 2 bytes TAG - const int iTag = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); - - // check if tag is correct - if ( iTag != 0 ) - { - return true; // return error code - } - - /* 2 bytes ID */ - iID = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); - - /* 1 byte cnt */ - iCnt = static_cast ( GetValFromStream ( vecIn, iCurPos, 1 ) ); - - /* 2 bytes length */ - iLenBy = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); - - // make sure the length is correct - if ( iLenBy != iNumBytesIn - MESS_LEN_WITHOUT_DATA_BYTE ) - { - return true; // return error code - } - - - // now check CRC ----- - CCRC CRCObj; - const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iLenBy; - - iCurPos = 0; // start from beginning - for ( i = 0; i < iLenCRCCalc; i++ ) - { - CRCObj.AddByte ( static_cast ( - GetValFromStream ( vecIn, iCurPos, 1 ) ) ); - } - - if ( CRCObj.GetCRC () != GetValFromStream ( vecIn, iCurPos, 2 ) ) - { - return true; // return error code - } - - - // extract actual data ----- - vecData.Init ( iLenBy ); - iCurPos = MESS_HEADER_LENGTH_BYTE; // start from beginning of data - for ( i = 0; i < iLenBy; i++ ) - { - vecData[i] = static_cast ( - GetValFromStream ( vecIn, iCurPos, 1 ) ); - } - - return false; // everything was ok -} - -uint32_t CProtocol::GetValFromStream ( const CVector& vecIn, - unsigned int& iPos, - const unsigned int iNumOfBytes ) -{ -/* - note: iPos is automatically incremented in this function -*/ - // 4 bytes maximum since we return uint32 - Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); - Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); - - uint32_t iRet = 0; - for ( unsigned int i = 0; i < iNumOfBytes; i++ ) - { - iRet |= vecIn[iPos] << ( i * 8 /* size of byte */ ); - iPos++; - } - - return iRet; -} - -void CProtocol::GenMessageFrame ( CVector& vecOut, - const int iCnt, - const int iID, - const CVector& vecData ) -{ - int i; - - // query length of data vector - const int iDataLenByte = vecData.Size(); - - // total length of message - const int iTotLenByte = MESS_LEN_WITHOUT_DATA_BYTE + iDataLenByte; - - // init message vector - vecOut.Init ( iTotLenByte ); - - // encode header ----- - unsigned int iCurPos = 0; // init position pointer - - // 2 bytes TAG (all zero bits) - PutValOnStream ( vecOut, iCurPos, - static_cast ( 0 ), 2 ); - - // 2 bytes ID - PutValOnStream ( vecOut, iCurPos, - static_cast ( iID ), 2 ); - - // 1 byte cnt - PutValOnStream ( vecOut, iCurPos, - static_cast ( iCnt ), 1 ); - - // 2 bytes length - PutValOnStream ( vecOut, iCurPos, - static_cast ( iDataLenByte ), 2 ); - - // encode data ----- - for ( i = 0; i < iDataLenByte; i++ ) - { - PutValOnStream ( vecOut, iCurPos, - static_cast ( vecData[i] ), 1 ); - } - - // encode CRC ----- - CCRC CRCObj; - iCurPos = 0; // start from beginning - - const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iDataLenByte; - for ( i = 0; i < iLenCRCCalc; i++ ) - { - CRCObj.AddByte ( static_cast ( - GetValFromStream ( vecOut, iCurPos, 1 ) ) ); - } - - PutValOnStream ( vecOut, iCurPos, - static_cast ( CRCObj.GetCRC() ), 2 ); -} - -void CProtocol::PutValOnStream ( CVector& vecIn, - unsigned int& iPos, - const uint32_t iVal, - const unsigned int iNumOfBytes ) -{ -/* - note: iPos is automatically incremented in this function -*/ - // 4 bytes maximum since we use uint32 - Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); - Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); - - for ( unsigned int i = 0; i < iNumOfBytes; i++ ) - { - vecIn[iPos] = - ( iVal >> ( i * 8 /* size of byte */ ) ) & 255 /* 11111111 */; - - iPos++; - } -} +/******************************************************************************\ + * Copyright (c) 2004-2009 + * + * Author(s): + * Volker Fischer + * + +Protocol message definition +--------------------------- + +- All messages received need to be acknowledged by an acknowledge packet + + + +MAIN FRAME +---------- + + +-------------+------------+------------+------------------+ ... + | 2 bytes TAG | 2 bytes ID | 1 byte cnt | 2 bytes length n | ... + +-------------+------------+------------+------------------+ ... + ... --------------+-------------+ + ... n bytes data | 2 bytes CRC | + ... --------------+-------------+ + +- TAG is an all zero bit word to identify protocol messages +- message ID defined by the defines PROTMESSID_x +- cnt: counter which is increment for each message and wraps around at 255 +- length n in bytes of the data +- actual data, dependent on message type +- 16 bits CRC, calculating over the entire message, is transmitted inverted + Generator polynom: G_16(x) = x^16 + x^12 + x^5 + 1, initial state: all ones + + +MESSAGES +-------- + +- Acknowledgement message: PROTMESSID_ACKN + + +-----------------------------------+ + | 2 bytes ID of message to be ackn. | + +-----------------------------------+ + + note: the cnt value is the same as of the message to be acknowledged + + +- Jitter buffer size: PROTMESSID_JITT_BUF_SIZE + + +--------------------------+ + | 2 bytes number of blocks | + +--------------------------+ + + +- Request jitter buffer size: PROTMESSID_REQ_JITT_BUF_SIZE + + note: does not have any data -> n = 0 + + +- Server full message: PROTMESSID_SERVER_FULL + + note: does not have any data -> n = 0 + + +- Network buffer block size factor: PROTMESSID_NET_BLSI_FACTOR + + note: size, relative to minimum block size + + +----------------+ + | 2 bytes factor | + +----------------+ + + +- Gain of channel: PROTMESSID_CHANNEL_GAIN + + +-------------------+--------------+ + | 1 byte channel ID | 2 bytes gain | + +-------------------+--------------+ + + +- IP number and name of connected clients: PROTMESSID_CONN_CLIENTS_LIST + + for each connected client append following data: + + +-------------------+--------------------+------------------+ ... + | 1 byte channel ID | 4 bytes IP address | 2 bytes number n | ... + +-------------------+--------------------+------------------+ ... + ... ----------------------+ + ... n bytes UTF-8 string | + ... ----------------------+ + +- Request connected clients list: PROTMESSID_REQ_CONN_CLIENTS_LIST + + note: does not have any data -> n = 0 + + +- Name of channel: PROTMESSID_CHANNEL_NAME + + for each connected client append following data: + + +------------------+----------------------+ + | 2 bytes number n | n bytes UTF-8 string | + +------------------+----------------------+ + + +- Chat text: PROTMESSID_CHAT_TEXT + + +------------------+----------------------+ + | 2 bytes number n | n bytes UTF-8 string | + +------------------+----------------------+ + + +- Ping message (for measuring the ping time): PROTMESSID_PING_MS + + +-----------------------------+ + | 4 bytes transmit time in ms | + +-----------------------------+ + + +- Properties for network transport: PROTMESSID_NETW_TRANSPORT_PROPS + + +------------------------+-------------------------+-----------------+ ... + | 4 bytes base netw size | 2 bytes block size fact | 1 byte num chan | ... + +------------------------+-------------------------+-----------------+ ... + ... ------------------+-----------------------+ ... + ... 4 bytes sam rate | 2 bytes audiocod type | ... + ... ------------------+-----------------------+ ... + ... -----------------+----------------------+ + ... 2 bytes version | 4 bytes audiocod arg | + ... -----------------+----------------------+ + + - "base netw size": length of the base network packet (frame) in bytes + - "block size fact": block size factor + - "num chan": number of channels of the audio signal, e.g. "2" is stereo + - "sam rate": sample rate of the audio stream + - "audiocod type": audio coding type, the following types are supported: + - 0: none, no audio coding applied + - 1: CELT + - "version": version of the audio coder, if not used this value shall be set to 0 + - "audiocod arg": argument for the audio coder, if not used this value shall be set to 0 + + +- Request properties for network transport: PROTMESSID_REQ_NETW_TRANSPORT_PROPS + + note: does not have any data -> n = 0 + + +- Disconnect message: PROTMESSID_DISCONNECTION + + note: does not have any data -> n = 0 + + + ****************************************************************************** + * + * 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 "protocol.h" + + +/* Implementation *************************************************************/ +CProtocol::CProtocol() +{ + Reset(); + + // connections + QObject::connect ( &TimerSendMess, SIGNAL ( timeout() ), + this, SLOT ( OnTimerSendMess() ) ); +} + +void CProtocol::Reset() +{ + QMutexLocker locker ( &Mutex ); + + // prepare internal variables for initial protocol transfer + iCounter = 0; + iOldRecID = PROTMESSID_ILLEGAL; + iOldRecCnt = 0; + + // delete complete "send message queue" + SendMessQueue.clear(); +} + +void CProtocol::EnqueueMessage ( CVector& vecMessage, + const int iCnt, + const int iID ) +{ + bool bListWasEmpty; + + Mutex.lock(); + { + // check if list is empty so that we have to initiate a send process + bListWasEmpty = SendMessQueue.empty(); + + // create send message object for the queue + CSendMessage SendMessageObj ( vecMessage, iCnt, iID ); + + // we want to have a FIFO: we add at the end and take from the beginning + SendMessQueue.push_back ( SendMessageObj ); + } + Mutex.unlock(); + + // if list was empty, initiate send process + if ( bListWasEmpty ) + { + SendMessage(); + } +} + +void CProtocol::SendMessage() +{ + CVector vecMessage; + bool bSendMess = false; + + Mutex.lock(); + { + // we have to check that list is not empty, since in another thread the + // last element of the list might have been erased + if ( !SendMessQueue.empty() ) + { + vecMessage.Init ( SendMessQueue.front().vecMessage.Size() ); + vecMessage = SendMessQueue.front().vecMessage; + + bSendMess = true; + } + } + Mutex.unlock(); + + if ( bSendMess ) + { + // send message + emit MessReadyForSending ( vecMessage ); + + // start time-out timer if not active + if ( !TimerSendMess.isActive() ) + { + TimerSendMess.start ( SEND_MESS_TIMEOUT_MS ); + } + } + else + { + // no message to send, stop timer + TimerSendMess.stop(); + } +} + +void CProtocol::CreateAndSendMessage ( const int iID, + const CVector& vecData ) +{ + CVector vecNewMessage; + int iCurCounter; + + Mutex.lock(); + { + // store current counter value + iCurCounter = iCounter; + + // increase counter (wraps around automatically) + iCounter++; + } + Mutex.unlock(); + + // build complete message + GenMessageFrame ( vecNewMessage, iCurCounter, iID, vecData ); + + // enqueue message + EnqueueMessage ( vecNewMessage, iCurCounter, iID ); +} + +void CProtocol::CreateAndSendAcknMess ( const int& iID, const int& iCnt ) +{ + CVector vecAcknMessage; + CVector vecData ( 2 ); // 2 bytes of data + unsigned int iPos = 0; // init position pointer + + // build data vector + PutValOnStream ( vecData, iPos, static_cast ( iID ), 2 ); + + // build complete message + GenMessageFrame ( vecAcknMessage, iCnt, PROTMESSID_ACKN, vecData ); + + // immediately send acknowledge message + emit MessReadyForSending ( vecAcknMessage ); +} + +bool CProtocol::ParseMessage ( const CVector& vecbyData, + const int iNumBytes ) +{ +/* + return code: false -> ok; true -> error +*/ + bool bRet = false; + bool bSendNextMess; + int iRecCounter, iRecID; + CVector vecData; + + if ( !ParseMessageFrame ( vecbyData, iNumBytes, iRecCounter, iRecID, vecData ) ) + { + // In case we received a message and returned an answer but our answer + // did not make it to the receiver, he will resend his message. We check + // here if the message is the same as the old one, and if this is the + // case, just resend our old answer again + if ( ( iOldRecID == iRecID ) && ( iOldRecCnt == iRecCounter ) ) + { + // acknowledgments are not acknowledged + if ( iRecID != PROTMESSID_ACKN ) + { + // resend acknowledgement + CreateAndSendAcknMess ( iRecID, iRecCounter ); + } + } + else + { + // special treatment for acknowledge messages + if ( iRecID == PROTMESSID_ACKN ) + { + // extract data from stream and emit signal for received value + unsigned int iPos = 0; + const int iData = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + Mutex.lock(); + { + // check if this is the correct acknowledgment + bSendNextMess = false; + if ( !SendMessQueue.empty() ) + { + if ( ( SendMessQueue.front().iCnt == iRecCounter ) && + ( SendMessQueue.front().iID == iData ) ) + { + // message acknowledged, remove from queue + SendMessQueue.pop_front(); + + // send next message in queue + bSendNextMess = true; + } + } + } + Mutex.unlock(); + + if ( bSendNextMess ) + { + SendMessage(); + } + } + else + { + // check which type of message we received and do action + switch ( iRecID ) + { + case PROTMESSID_JITT_BUF_SIZE: + bRet = EvaluateJitBufMes ( vecData ); + break; + + case PROTMESSID_REQ_JITT_BUF_SIZE: + bRet = EvaluateReqJitBufMes ( vecData ); + break; + + case PROTMESSID_SERVER_FULL: + bRet = EvaluateServerFullMes ( vecData ); + break; + + case PROTMESSID_NET_BLSI_FACTOR: + bRet = EvaluateNetwBlSiFactMes ( vecData ); + break; + + case PROTMESSID_CHANNEL_GAIN: + bRet = EvaluateChanGainMes ( vecData ); + break; + + case PROTMESSID_CONN_CLIENTS_LIST: + bRet = EvaluateConClientListMes ( vecData ); + break; + + case PROTMESSID_REQ_CONN_CLIENTS_LIST: + bRet = EvaluateReqConnClientsList ( vecData ); + break; + + case PROTMESSID_CHANNEL_NAME: + bRet = EvaluateChanNameMes ( vecData ); + break; + + case PROTMESSID_CHAT_TEXT: + bRet = EvaluateChatTextMes ( vecData ); + break; + + case PROTMESSID_PING_MS: + bRet = EvaluatePingMes ( vecData ); + break; + + case PROTMESSID_NETW_TRANSPORT_PROPS: + bRet = EvaluateNetwTranspPropsMes ( vecData ); + break; + + case PROTMESSID_REQ_NETW_TRANSPORT_PROPS: + bRet = EvaluateReqNetwTranspPropsMes ( vecData ); + break; + + case PROTMESSID_DISCONNECTION: + bRet = EvaluateDisconnectionMes ( vecData ); + break; + } + + // send acknowledge message + CreateAndSendAcknMess ( iRecID, iRecCounter ); + } + } + + // save current message ID and counter to find out if message was resent + iOldRecID = iRecID; + iOldRecCnt = iRecCounter; + } + else + { + bRet = true; // return error code + } + + return bRet; +} + + +// Access-functions for creating and parsing messages -------------------------- +void CProtocol::CreateJitBufMes ( const int iJitBufSize ) +{ + CVector vecData ( 2 ); // 2 bytes of data + unsigned int iPos = 0; // init position pointer + + // build data vector + PutValOnStream ( vecData, iPos, static_cast ( iJitBufSize ), 2 ); + + CreateAndSendMessage ( PROTMESSID_JITT_BUF_SIZE, vecData ); +} + +bool CProtocol::EvaluateJitBufMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 2 ) + { + return true; + } + + // extract jitter buffer size + const int iData = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // invoke message action + emit ChangeJittBufSize ( iData ); + + return false; // no error +} + +void CProtocol::CreateReqJitBufMes() +{ + CreateAndSendMessage ( PROTMESSID_REQ_JITT_BUF_SIZE, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqJitBufMes ( const CVector& vecData ) +{ + // invoke message action + emit ReqJittBufSize(); + + return false; // no error +} + +void CProtocol::CreateServerFullMes() +{ + CreateAndSendMessage ( PROTMESSID_SERVER_FULL, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateServerFullMes ( const CVector& vecData ) +{ + // invoke message action + emit ServerFull(); + + return false; // no error +} + +void CProtocol::CreateNetwBlSiFactMes ( const int iNetwBlSiFact ) +{ + CVector vecData ( 2 ); // 2 bytes of data + unsigned int iPos = 0; // init position pointer + + // build data vector + PutValOnStream ( vecData, iPos, static_cast ( iNetwBlSiFact ), 2 ); + + CreateAndSendMessage ( PROTMESSID_NET_BLSI_FACTOR, vecData ); +} + +bool CProtocol::EvaluateNetwBlSiFactMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 2 ) + { + return true; + } + + const int iData = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // invoke message action + emit ChangeNetwBlSiFact ( iData ); + + return false; // no error +} + +void CProtocol::CreateChanGainMes ( const int iChanID, const double dGain ) +{ + CVector vecData ( 3 ); // 3 bytes of data + unsigned int iPos = 0; // init position pointer + + // build data vector + // channel ID + PutValOnStream ( vecData, iPos, static_cast ( iChanID ), 1 ); + + // actual gain, we convert from double with range 0..1 to integer + const int iCurGain = static_cast ( dGain * ( 1 << 15 ) ); + PutValOnStream ( vecData, iPos, static_cast ( iCurGain ), 2 ); + + CreateAndSendMessage ( PROTMESSID_CHANNEL_GAIN, vecData ); +} + +bool CProtocol::EvaluateChanGainMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 3 ) + { + return true; + } + + // channel ID + const int iCurID = + static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // actual gain, we convert from integer to double with range 0..1 + const int iData = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + const double dNewGain = static_cast ( iData ) / ( 1 << 15 ); + + // invoke message action + emit ChangeChanGain ( iCurID, dNewGain ); + + return false; // no error +} + +void CProtocol::CreateConClientListMes ( const CVector& vecChanInfo ) +{ + const int iNumClients = vecChanInfo.Size(); + + // build data vector + CVector vecData ( 0 ); + unsigned int iPos = 0; // init position pointer + + for ( int i = 0; i < iNumClients; i++ ) + { + // current string size + const int iCurStrLen = vecChanInfo[i].strName.size(); + + // size of current list entry + const int iCurListEntrLen = + 1 /* chan ID */ + 4 /* IP addr. */ + 2 /* str. size */ + iCurStrLen; + + // make space for new data + vecData.Enlarge ( iCurListEntrLen ); + + // channel ID + PutValOnStream ( vecData, iPos, + static_cast ( vecChanInfo[i].iChanID ), 1 ); + + // IP address (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( vecChanInfo[i].iIpAddr ), 4 ); + + // number of bytes for name string (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( iCurStrLen ), 2 ); + + // name string (n bytes) + for ( int j = 0; j < iCurStrLen; j++ ) + { + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, + static_cast ( vecChanInfo[i].strName[j].toAscii() ), 1 ); + } + } + + CreateAndSendMessage ( PROTMESSID_CONN_CLIENTS_LIST, vecData ); +} + +bool CProtocol::EvaluateConClientListMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + int iData; + const unsigned int iDataLen = vecData.Size(); + CVector vecChanInfo ( 0 ); + + while ( iPos < iDataLen ) + { + // check size (the first 7 bytes) + if ( iDataLen - iPos < 7 ) + { + return true; + } + + // channel ID (1 byte) + const int iChanID = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + // IP address (4 bytes) + const int iIpAddr = static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + // number of bytes for name string (2 bytes) + const unsigned int iStringLen = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // check size + if ( iDataLen - iPos < iStringLen ) + { + return true; + } + + // name string (n bytes) + QString strCurStr = ""; + for ( unsigned int j = 0; j < iStringLen; j++ ) + { + // byte-by-byte copying of the string data + iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + strCurStr += QString ( (char*) &iData ); + } + + // add channel information to vector + vecChanInfo.Add ( CChannelShortInfo ( iChanID, iIpAddr, strCurStr ) ); + } + + // invoke message action + emit ConClientListMesReceived ( vecChanInfo ); + + return false; // no error +} + +void CProtocol::CreateReqConnClientsList() +{ + CreateAndSendMessage ( PROTMESSID_REQ_CONN_CLIENTS_LIST, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqConnClientsList ( const CVector& vecData ) +{ + // invoke message action + emit ReqConnClientsList(); + + return false; // no error +} + +void CProtocol::CreateChanNameMes ( const QString strName ) +{ + unsigned int iPos = 0; // init position pointer + const int iStrLen = strName.size(); // get string size + + // size of current list entry + const int iEntrLen = 2 /* string size */ + iStrLen; + + // build data vector + CVector vecData ( iEntrLen ); + + // number of bytes for name string (2 bytes) + PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); + + // name string (n bytes) + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, + static_cast ( strName[j].toAscii() ), 1 ); + } + + CreateAndSendMessage ( PROTMESSID_CHANNEL_NAME, vecData ); +} + +bool CProtocol::EvaluateChanNameMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size (the first 2 bytes) + if ( vecData.Size() < 2 ) + { + return true; + } + + // number of bytes for name string (2 bytes) + const int iStrLen = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // check size + if ( vecData.Size() - 2 != iStrLen ) + { + return true; + } + + // name string (n bytes) + QString strName = ""; + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + strName += QString ( (char*) &iData ); + } + + // invoke message action + emit ChangeChanName ( strName ); + + return false; // no error +} + +void CProtocol::CreateChatTextMes ( const QString strChatText ) +{ + unsigned int iPos = 0; // init position pointer + const int iStrLen = strChatText.size(); // get string size + + // size of message body + const int iEntrLen = 2 /* string size */ + iStrLen; + + // build data vector + CVector vecData ( iEntrLen ); + + // number of bytes for name string (2 bytes) + PutValOnStream ( vecData, iPos, static_cast ( iStrLen ), 2 ); + + // name string (n bytes) + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, + static_cast ( strChatText[j].toAscii() ), 1 ); + } + + CreateAndSendMessage ( PROTMESSID_CHAT_TEXT, vecData ); +} + +bool CProtocol::EvaluateChatTextMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size (the first 2 bytes) + if ( vecData.Size() < 2 ) + { + return true; + } + + // number of bytes for name string (2 bytes) + const int iStrLen = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // check size + if ( vecData.Size() - 2 != iStrLen ) + { + return true; + } + + // name string (n bytes) + QString strChatText = ""; + for ( int j = 0; j < iStrLen; j++ ) + { + // byte-by-byte copying of the string data + int iData = static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + strChatText += QString ( (char*) &iData ); + } + + // invoke message action + emit ChatTextReceived ( strChatText ); + + return false; // no error +} + +void CProtocol::CreatePingMes ( const int iMs ) +{ + unsigned int iPos = 0; // init position pointer + + // build data vector (4 bytes long) + CVector vecData ( 4 ); + + // byte-by-byte copying of the string data + PutValOnStream ( vecData, iPos, static_cast ( iMs ), 4 ); + + CreateAndSendMessage ( PROTMESSID_PING_MS, vecData ); +} + +bool CProtocol::EvaluatePingMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + + // check size + if ( vecData.Size() != 4 ) + { + return true; + } + + emit PingReceived ( static_cast ( GetValFromStream ( vecData, iPos, 4 ) ) ); + + return false; // no error +} + +void CProtocol::CreateNetwTranspPropsMes ( const CNetworkTransportProps& NetTrProps ) +{ + unsigned int iPos = 0; // init position pointer + + // size of current message body + const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + + 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + + 2 /* version */ + 4 /* audiocod arg */; + + // build data vector + CVector vecData ( iEntrLen ); + + // length of the base network packet (frame) in bytes (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iBaseNetworkPacketSize ), 4 ); + + // block size factor (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iBlockSizeFact ), 2 ); + + // number of channels of the audio signal, e.g. "2" is stereo (1 byte) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iNumAudioChannels ), 1 ); + + // sample rate of the audio stream (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iSampleRate ), 4 ); + + // audio coding type (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.eAudioCodingType ), 2 ); + + // version (2 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iVersion ), 2 ); + + // argument for the audio coder (4 bytes) + PutValOnStream ( vecData, iPos, + static_cast ( NetTrProps.iAudioCodingArg ), 4 ); + + CreateAndSendMessage ( PROTMESSID_NETW_TRANSPORT_PROPS, vecData ); +} + +bool CProtocol::EvaluateNetwTranspPropsMes ( const CVector& vecData ) +{ + unsigned int iPos = 0; // init position pointer + CNetworkTransportProps ReceivedNetwTranspProps; + + // size of current message body + const int iEntrLen = 4 /* netw size */ + 2 /* block size fact */ + + 1 /* num chan */ + 4 /* sam rate */ + 2 /* audiocod type */ + + 2 /* version */ + 4 /* audiocod arg */; + + // check size + if ( vecData.Size() != iEntrLen ) + { + return true; + } + + // length of the base network packet (frame) in bytes (4 bytes) + ReceivedNetwTranspProps.iBaseNetworkPacketSize = + static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + if ( ( ReceivedNetwTranspProps.iBaseNetworkPacketSize < 1 ) || + ( ReceivedNetwTranspProps.iBaseNetworkPacketSize > MAX_SIZE_BYTES_NETW_BUF ) ) + { + return true; + } + + // block size factor (2 bytes) + ReceivedNetwTranspProps.iBlockSizeFact = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + if ( ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_PREFERRED ) && + ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_DEFAULT ) && + ( ReceivedNetwTranspProps.iBlockSizeFact != FRAME_SIZE_FACTOR_SAFE ) ) + { + return true; + } + + // number of channels of the audio signal, e.g. "2" is stereo (1 byte) + ReceivedNetwTranspProps.iNumAudioChannels = + static_cast ( GetValFromStream ( vecData, iPos, 1 ) ); + + if ( ( ReceivedNetwTranspProps.iNumAudioChannels != 1 ) && + ( ReceivedNetwTranspProps.iNumAudioChannels != 2 ) ) + { + return true; + } + + // sample rate of the audio stream (4 bytes) + ReceivedNetwTranspProps.iSampleRate = + static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + // audio coding type (2 bytes) with error check + const int iRecCodingType = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + if ( ( iRecCodingType != CT_NONE ) && + ( iRecCodingType != CT_CELT ) ) + { + return true; + } + + ReceivedNetwTranspProps.eAudioCodingType = + static_cast ( iRecCodingType ); + + // version (2 bytes) + ReceivedNetwTranspProps.iVersion = + static_cast ( GetValFromStream ( vecData, iPos, 2 ) ); + + // argument for the audio coder (4 bytes) + ReceivedNetwTranspProps.iAudioCodingArg = + static_cast ( GetValFromStream ( vecData, iPos, 4 ) ); + + // invoke message action + emit NetTranspPropsReceived ( ReceivedNetwTranspProps ); + + return false; // no error +} + +void CProtocol::CreateReqNetwTranspPropsMes() +{ + CreateAndSendMessage ( PROTMESSID_REQ_NETW_TRANSPORT_PROPS, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateReqNetwTranspPropsMes ( const CVector& vecData ) +{ + // invoke message action + emit ReqNetTranspProps(); + + return false; // no error +} + +void CProtocol::CreateDisconnectionMes() +{ + CreateAndSendMessage ( PROTMESSID_DISCONNECTION, CVector ( 0 ) ); +} + +bool CProtocol::EvaluateDisconnectionMes ( const CVector& vecData ) +{ + // invoke message action + emit Disconnection(); + + return false; // no error +} + + +/******************************************************************************\ +* Message generation (parsing) * +\******************************************************************************/ +bool CProtocol::ParseMessageFrame ( const CVector& vecIn, + const int iNumBytesIn, + int& iCnt, + int& iID, + CVector& vecData ) +{ +/* + return code: true -> ok; false -> error +*/ + int iLenBy, i; + unsigned int iCurPos; + + // vector must be at least "MESS_LEN_WITHOUT_DATA_BYTE" bytes long + if ( iNumBytesIn < MESS_LEN_WITHOUT_DATA_BYTE ) + { + return true; // return error code + } + + + // decode header ----- + iCurPos = 0; // start from beginning + + // 2 bytes TAG + const int iTag = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); + + // check if tag is correct + if ( iTag != 0 ) + { + return true; // return error code + } + + /* 2 bytes ID */ + iID = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); + + /* 1 byte cnt */ + iCnt = static_cast ( GetValFromStream ( vecIn, iCurPos, 1 ) ); + + /* 2 bytes length */ + iLenBy = static_cast ( GetValFromStream ( vecIn, iCurPos, 2 ) ); + + // make sure the length is correct + if ( iLenBy != iNumBytesIn - MESS_LEN_WITHOUT_DATA_BYTE ) + { + return true; // return error code + } + + + // now check CRC ----- + CCRC CRCObj; + const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iLenBy; + + iCurPos = 0; // start from beginning + for ( i = 0; i < iLenCRCCalc; i++ ) + { + CRCObj.AddByte ( static_cast ( + GetValFromStream ( vecIn, iCurPos, 1 ) ) ); + } + + if ( CRCObj.GetCRC () != GetValFromStream ( vecIn, iCurPos, 2 ) ) + { + return true; // return error code + } + + + // extract actual data ----- + vecData.Init ( iLenBy ); + iCurPos = MESS_HEADER_LENGTH_BYTE; // start from beginning of data + for ( i = 0; i < iLenBy; i++ ) + { + vecData[i] = static_cast ( + GetValFromStream ( vecIn, iCurPos, 1 ) ); + } + + return false; // everything was ok +} + +uint32_t CProtocol::GetValFromStream ( const CVector& vecIn, + unsigned int& iPos, + const unsigned int iNumOfBytes ) +{ +/* + note: iPos is automatically incremented in this function +*/ + // 4 bytes maximum since we return uint32 + Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); + Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); + + uint32_t iRet = 0; + for ( unsigned int i = 0; i < iNumOfBytes; i++ ) + { + iRet |= vecIn[iPos] << ( i * 8 /* size of byte */ ); + iPos++; + } + + return iRet; +} + +void CProtocol::GenMessageFrame ( CVector& vecOut, + const int iCnt, + const int iID, + const CVector& vecData ) +{ + int i; + + // query length of data vector + const int iDataLenByte = vecData.Size(); + + // total length of message + const int iTotLenByte = MESS_LEN_WITHOUT_DATA_BYTE + iDataLenByte; + + // init message vector + vecOut.Init ( iTotLenByte ); + + // encode header ----- + unsigned int iCurPos = 0; // init position pointer + + // 2 bytes TAG (all zero bits) + PutValOnStream ( vecOut, iCurPos, + static_cast ( 0 ), 2 ); + + // 2 bytes ID + PutValOnStream ( vecOut, iCurPos, + static_cast ( iID ), 2 ); + + // 1 byte cnt + PutValOnStream ( vecOut, iCurPos, + static_cast ( iCnt ), 1 ); + + // 2 bytes length + PutValOnStream ( vecOut, iCurPos, + static_cast ( iDataLenByte ), 2 ); + + // encode data ----- + for ( i = 0; i < iDataLenByte; i++ ) + { + PutValOnStream ( vecOut, iCurPos, + static_cast ( vecData[i] ), 1 ); + } + + // encode CRC ----- + CCRC CRCObj; + iCurPos = 0; // start from beginning + + const int iLenCRCCalc = MESS_HEADER_LENGTH_BYTE + iDataLenByte; + for ( i = 0; i < iLenCRCCalc; i++ ) + { + CRCObj.AddByte ( static_cast ( + GetValFromStream ( vecOut, iCurPos, 1 ) ) ); + } + + PutValOnStream ( vecOut, iCurPos, + static_cast ( CRCObj.GetCRC() ), 2 ); +} + +void CProtocol::PutValOnStream ( CVector& vecIn, + unsigned int& iPos, + const uint32_t iVal, + const unsigned int iNumOfBytes ) +{ +/* + note: iPos is automatically incremented in this function +*/ + // 4 bytes maximum since we use uint32 + Q_ASSERT ( ( iNumOfBytes > 0 ) && ( iNumOfBytes <= 4 ) ); + Q_ASSERT ( static_cast ( vecIn.Size() ) >= iPos + iNumOfBytes ); + + for ( unsigned int i = 0; i < iNumOfBytes; i++ ) + { + vecIn[iPos] = + ( iVal >> ( i * 8 /* size of byte */ ) ) & 255 /* 11111111 */; + + iPos++; + } +} diff --git a/src/util.h b/src/util.h index fc676a3c..82c1bc49 100755 --- a/src/util.h +++ b/src/util.h @@ -447,29 +447,29 @@ class CNetworkTransportProps { public: CNetworkTransportProps() : - iNetworkPacketSize ( 0 ), - iBlockSizeFact ( 0 ), - iNumAudioChannels ( 0 ), - iSampleRate ( 0 ), - eAudioCodingType ( CT_NONE ), - iAudioCodingArg ( 0 ) {} + iBaseNetworkPacketSize ( 0 ), + iBlockSizeFact ( 0 ), + iNumAudioChannels ( 0 ), + iSampleRate ( 0 ), + eAudioCodingType ( CT_NONE ), + iAudioCodingArg ( 0 ) {} - CNetworkTransportProps ( const uint32_t iNNPS, + CNetworkTransportProps ( const uint32_t iNBNPS, const uint16_t iNBSF, const uint32_t iNNACH, const uint32_t iNSR, const EAudComprType eNACT, const uint32_t iNVers, const int32_t iNACA ) : - iNetworkPacketSize ( iNNPS ), - iBlockSizeFact ( iNBSF ), - iNumAudioChannels ( iNNACH ), - iSampleRate ( iNSR ), - eAudioCodingType ( eNACT ), - iVersion ( iNVers ), - iAudioCodingArg ( iNACA ) {} + iBaseNetworkPacketSize ( iNBNPS ), + iBlockSizeFact ( iNBSF ), + iNumAudioChannels ( iNNACH ), + iSampleRate ( iNSR ), + eAudioCodingType ( eNACT ), + iVersion ( iNVers ), + iAudioCodingArg ( iNACA ) {} - uint32_t iNetworkPacketSize; + uint32_t iBaseNetworkPacketSize; uint16_t iBlockSizeFact; uint32_t iNumAudioChannels; uint32_t iSampleRate;