mirror of
https://github.com/Mauler125/r5sdk.git
synced 2025-02-09 19:15:03 +01:00
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
4041 lines
158 KiB
C
4041 lines
158 KiB
C
/*H********************************************************************************/
|
|
/*!
|
|
\File voipconnection.c
|
|
|
|
\Description
|
|
VoIP virtual connection manager.
|
|
|
|
\Copyright
|
|
Copyright (c) Electronic Arts 2004. ALL RIGHTS RESERVED.
|
|
|
|
\Notes
|
|
\verbatim
|
|
|
|
None of the packet types used by DirtySDK's voip implementation is reliable, i.e. delivery to the other end of the connection is not guaranteed.
|
|
There exists some sort of reliability during the connection establishment handshaking: voip connect packets (VoipConnPacketT) are being re-sent
|
|
until packets are detected as received by the other end of the connection. This mechanism is specific to the connection establishment and is
|
|
not generic enough for re-use after connection establishment.
|
|
|
|
MLU VoIP join-in-progress (local user added to voip console-to-console connection after connection established) requires some
|
|
post-initial-handshaking reliability. For that purpose specifically, a reliability scheme was implemented with protocol components
|
|
piggy-backed on voip micr packets and voip ping packets. Those protocol components are: DATA, ACK and ACKCNF.
|
|
|
|
DATA (producer->consumer): data to be delivered reliably (tagged with a sequence number)
|
|
ACK (consumer->producer): acked sequence number
|
|
ACKCNF (producer->consumer): acked sequence number confirmation (can be leveraged by the consumer to stop “acking” and eliminate the associated “acking” overhead)
|
|
|
|
From the perspective of a game console producing reliable data, an "outbound reliable data flow" on a given connection looks like this:
|
|
|
|
Reliable data producer (local) Reliable Data Consumer (remote)
|
|
|
|
------ DATA ----->
|
|
<------ ACK -----
|
|
------ ACKCNF ----->
|
|
|
|
From the perspective of a game console consuming reliable data, an "inbound reliable data flow" on a given connection looks like this:
|
|
|
|
Reliable data consumer (local) Reliable Data Producer (remote)
|
|
|
|
<------ DATA -----
|
|
------ ACK ----->
|
|
<------ ACKCNF -----
|
|
|
|
ACK and ACKCNF protocol components are added, when necessary, as a pair to the beginning of any ping or voip packet. When the packet
|
|
is flagged with VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF, then it contains at least one such entry. The exact number of entries is
|
|
captured in the first byte of the packet payload. Then each entry consists of a 4-byte destination client id and a 1-byte field where:
|
|
bit 7 = ACKCNF (1 means "continue acking", 0 means "you can stop acking")
|
|
bits 0-6 = ACK (acked seq number in the 1 to 127 range - 0 means "unused" or "invalid")
|
|
|
|
DATA protocol component is also added at the beginning of the voip packet payload (after ACK+ACkNF if any). When the packet
|
|
is flagged with VOIP_PACKET_RELIABLE_FLAG_DATA, then it contains at least one such entry. The exact number of entries is
|
|
captured in the first byte of the packet payload (or in the first byte following the ACK+ACKCNF portion). When a DATA entry
|
|
is created, it is queued with the SAME sequence number in the outbound queue of all connections. The use of the SAME
|
|
sequence number is key in not having to specifiy what destination console the entry is for. For P2P connectivity this is not
|
|
really a concern as packets are exchanged directly between consoles. But for voipserver-based connectivity, the voip micr
|
|
packets are sent once to the voipserver and then rebroadcasted from there to all other peers. Consequently it is important that
|
|
the sequence number that the DATA entries are tagged with are meaningful to all remote peers.
|
|
For voipserver-based voip:
|
|
Have a look at addition comments in _VoipReliableDataOutProcess() describing the logics used to add DATA entries
|
|
to voip micr packets sent to the voipserver.
|
|
\endverbatim
|
|
|
|
\Version 1.0 03/17/2004 (jbrookes) First Version
|
|
\Version 1.1 01/06/2008 (mclouatre) Created VoipConnectionAddRef(), VoipConnectionRemoveRef(), VoipConnectionResetRef(), VoipConnectionGetFallbackAddr()
|
|
\Version 1.2 10/26/2009 (mclouatre) Renamed from xbox/voipconnection.c to xenon/voipconnectionxenon.c
|
|
\Version 1.3 11/13/2009 (jbrookes) Merged Xenon back into common version
|
|
\Version 1.4 09/29/2014 (mclouatre) Addes support for voip reliable data
|
|
*/
|
|
/********************************************************************************H*/
|
|
|
|
/*** Include files ****************************************************************/
|
|
|
|
#ifdef _XBOX
|
|
#include <xtl.h>
|
|
#include <xonline.h>
|
|
#endif
|
|
|
|
#include <stdio.h>
|
|
#include <string.h>
|
|
#include <wchar.h> // for wcslen()
|
|
|
|
#include "DirtySDK/platform.h"
|
|
#include "DirtySDK/dirtysock.h"
|
|
#include "DirtySDK/dirtysock/dirtymem.h"
|
|
|
|
#include "DirtySDK/voip/voipdef.h"
|
|
#include "voippriv.h"
|
|
#include "voipcommon.h"
|
|
|
|
#include "voipconnection.h"
|
|
|
|
/*** Defines **********************************************************************/
|
|
|
|
#define VOIP_PACKET_VERSION ('j') //!< current packet version
|
|
#define VOIP_PING_RATE (500) //!< send ping packets twice a second
|
|
#define VOIP_TIMEOUT (15*1000) //!< default voip data timeout
|
|
#define VOIP_CONNTIMEOUT (10*1000) //!< connection timeout
|
|
#define VOIP_MSPERPACKET (100) //!< number of ms per network packet (100ms = 10hz)
|
|
|
|
//! enable for verbose debugging
|
|
#define VOIP_CONNECTION_DEBUG (DIRTYCODE_DEBUG && FALSE)
|
|
#define VOIP_RELIABLE_DEBUG (DIRTYCODE_DEBUG && FALSE)
|
|
|
|
#define VOIP_IPPROTO (IPPROTO_IP)
|
|
|
|
/*** Macros ***********************************************************************/
|
|
|
|
//! compare packet types
|
|
#define VOIP_SamePacketType(_packetHead1, _packetHead2) \
|
|
(!memcmp((_packetHead1)->aType, (_packetHead2)->aType, sizeof((_packetHead1)->aType)))
|
|
|
|
//! get connection ID
|
|
#define VOIP_ConnID(_pConnectionlist, _pConnection) ((uint32_t)((_pConnection)-(_pConnectionlist)->pConnections))
|
|
|
|
/*** Type Definitions *************************************************************/
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
const char *_strReliableType[] =
|
|
{
|
|
"USERADD",
|
|
"USERREM",
|
|
"OPAQUE ",
|
|
"TEXT "
|
|
};
|
|
#endif
|
|
|
|
/*** Function Prototypes **********************************************************/
|
|
|
|
static void _VoipConnectionStop(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnID, int32_t bSendDiscMsg);
|
|
static void _VoipEncodeU16(uint8_t *pValue, uint16_t uValue);
|
|
static uint16_t _VoipDecodeU16(const uint8_t *pValue);
|
|
|
|
/*** Variables ********************************************************************/
|
|
|
|
//! VoIP conn packet header
|
|
static VoipPacketHeadT _Voip_ConnPacket =
|
|
{
|
|
{ 'C', 'O', VOIP_PACKET_VERSION }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0}
|
|
};
|
|
|
|
//! VoIP disc packet header
|
|
static VoipPacketHeadT _Voip_DiscPacket =
|
|
{
|
|
{ 'D', 'S', 'C' }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0}
|
|
};
|
|
|
|
//! VoIP ping packet header
|
|
static VoipPacketHeadT _Voip_PingPacket =
|
|
{
|
|
{ 'P', 'N', 'G' }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0}
|
|
};
|
|
|
|
//! VoIP mic data packet header
|
|
static VoipPacketHeadT _Voip_MicrPacket =
|
|
{
|
|
{ 'M', 'I', 'C' }, 0, { 0, 0, 0, 0 }, { 0, 0, 0, 0 }, {0, 0}
|
|
};
|
|
|
|
/*** Private Functions ************************************************************/
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipEncodeLocalHeadsetStatus
|
|
|
|
\Description
|
|
Encode the uLocalUserStatus fields in the packet header
|
|
|
|
\Input *pConnectionlist - information read from here to be written to the packet header
|
|
\Input *pHead - packet header to write to
|
|
|
|
\Version 11/20/2008 (cvienneau)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipEncodeLocalHeadsetStatus(VoipConnectionlistT *pConnectionlist, VoipPacketHeadT *pHead)
|
|
{
|
|
uint16_t uFlags = 0;
|
|
int32_t i = 0;
|
|
for (i = 0; i < VOIP_MAXLOCALUSERS; ++i)
|
|
{
|
|
if (pConnectionlist->uLocalUserStatus[i] & VOIP_LOCAL_USER_HEADSETOK)
|
|
{
|
|
uFlags |= (1 << i);
|
|
}
|
|
}
|
|
|
|
// Encode 16 for byte ordering
|
|
_VoipEncodeU16(pHead->aHeadsetStat, uFlags);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipDecodeRemoteHeadsetStatus
|
|
|
|
\Description
|
|
Reads aHeadsetStat field from an incoming packet and sets appropriate state
|
|
about the remote connection.
|
|
|
|
\Input *pConnection - connection to write state to
|
|
\Input *pHead - packet header to read information from
|
|
|
|
\Version 11/20/2008 (cvienneau)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipDecodeRemoteHeadsetStatus(VoipConnectionT *pConnection, VoipPacketHeadT *pHead)
|
|
{
|
|
uint32_t iIndex;
|
|
uint16_t uFlags = _VoipDecodeU16(pHead->aHeadsetStat);
|
|
|
|
for (iIndex = 0; iIndex < VOIP_MAXLOCALUSERS; ++iIndex)
|
|
{
|
|
uint16_t uMask = 1 << iIndex;
|
|
if (uFlags & uMask)
|
|
{
|
|
pConnection->uRemoteUserStatus[iIndex] |= VOIP_REMOTE_USER_HEADSETOK; //on
|
|
}
|
|
else
|
|
{
|
|
pConnection->uRemoteUserStatus[iIndex] &= ~VOIP_REMOTE_USER_HEADSETOK; //off
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipDecodeU64
|
|
|
|
\Description
|
|
Decode uint64_t from given packet structure.
|
|
|
|
\Input *pValue - network-order data field
|
|
|
|
\Output
|
|
uint64_t - decoded value
|
|
|
|
\Version 07/03/2019 (tcho)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint64_t _VoipDecodeU64(const uint8_t *pValue)
|
|
{
|
|
uint64_t uValue = (uint64_t)pValue[0] << 56;
|
|
uValue |= (uint64_t)pValue[1] << 48;
|
|
uValue |= (uint64_t)pValue[2] << 40;
|
|
uValue |= (uint64_t)pValue[3] << 32;
|
|
uValue |= (uint64_t)pValue[4] << 24;
|
|
uValue |= (uint64_t)pValue[5] << 16;
|
|
uValue |= (uint64_t)pValue[6] << 8;
|
|
uValue |= (uint64_t)pValue[7];
|
|
return(uValue);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipDecodeU32
|
|
|
|
\Description
|
|
Decode uint32_t from given packet structure.
|
|
|
|
\Input *pValue - network-order data field
|
|
|
|
\Output
|
|
uint32_t - decoded value
|
|
|
|
\Version 06/02/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipDecodeU32(const uint8_t *pValue)
|
|
{
|
|
uint32_t uValue = pValue[0] << 24;
|
|
uValue |= pValue[1] << 16;
|
|
uValue |= pValue[2] << 8;
|
|
uValue |= pValue[3];
|
|
return(uValue);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipDecodeU16
|
|
|
|
\Description
|
|
Decode uint16_t from given packet structure.
|
|
|
|
\Input *pValue - network-order data field
|
|
|
|
\Output
|
|
uint16_t - decoded value
|
|
|
|
\Version 05/02/2014 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint16_t _VoipDecodeU16(const uint8_t *pValue)
|
|
{
|
|
uint16_t uValue = pValue[0] << 8;
|
|
uValue |= pValue[1];
|
|
return(uValue);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipDecodeVoipUser
|
|
|
|
\Description
|
|
Decode a voip user to a voip user packet
|
|
|
|
\Input *pUserPacket - input voip user packet to decode
|
|
\Input *pUser - voip user to storge the decoded result
|
|
|
|
\Version 07/03/2019 (tcho)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipDecodeVoipUser(VoipUserPacketT *pUserPacket, VoipUserT *pUser)
|
|
{
|
|
pUser->AccountInfo.iAccountId = (int64_t)_VoipDecodeU64(pUserPacket->aAccountId);
|
|
pUser->AccountInfo.iPersonaId = (int64_t)_VoipDecodeU64(pUserPacket->aPersonaId);
|
|
pUser->uFlags = _VoipDecodeU32(pUserPacket->aFlags);
|
|
pUser->ePlatform = _VoipDecodeU32(pUserPacket->aPlatform);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipEncodeU64
|
|
|
|
\Description
|
|
Encode given uint64_t in network order
|
|
|
|
\Input *pValue - storage for network-order u64
|
|
\Input uValue - u64 value to encode
|
|
|
|
\Version 07/03/2019 (tcho)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipEncodeU64(uint8_t *pValue, uint64_t uValue)
|
|
{
|
|
pValue[0] = (uint8_t)(uValue >> 56);
|
|
pValue[1] = (uint8_t)(uValue >> 48);
|
|
pValue[2] = (uint8_t)(uValue >> 40);
|
|
pValue[3] = (uint8_t)(uValue >> 32);
|
|
pValue[4] = (uint8_t)(uValue >> 24);
|
|
pValue[5] = (uint8_t)(uValue >> 16);
|
|
pValue[6] = (uint8_t)(uValue >> 8);
|
|
pValue[7] = (uint8_t)(uValue);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipEncodeU32
|
|
|
|
\Description
|
|
Encode given uint32_t in network order
|
|
|
|
\Input *pValue - storage for network-order u32
|
|
\Input uValue - u32 value to encode
|
|
|
|
\Version 06/02/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipEncodeU32(uint8_t *pValue, uint32_t uValue)
|
|
{
|
|
pValue[0] = (uint8_t)(uValue >> 24);
|
|
pValue[1] = (uint8_t)(uValue >> 16);
|
|
pValue[2] = (uint8_t)(uValue >> 8);
|
|
pValue[3] = (uint8_t)uValue;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipEncodeU16
|
|
|
|
\Description
|
|
Encode given uint16_t in network order
|
|
|
|
\Input *pValue - storage for network-order u32
|
|
\Input uValue - u16 value to encode
|
|
|
|
\Version 05/02/2014 (amakoukji)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipEncodeU16(uint8_t *pValue, uint16_t uValue)
|
|
{
|
|
pValue[0] = (uint8_t)(uValue >> 8);
|
|
pValue[1] = (uint8_t)uValue;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipEncodeVoipUser
|
|
|
|
\Description
|
|
Encodes a voip user to a voip user packet
|
|
|
|
\Input *pUserPacket - output voip user packet
|
|
\Input *pUser - user to encode
|
|
|
|
\Version 07/03/2019 (tcho)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipEncodeVoipUser(VoipUserPacketT *pUserPacket, VoipUserT *pUser)
|
|
{
|
|
_VoipEncodeU64(pUserPacket->aAccountId, (uint64_t) pUser->AccountInfo.iAccountId);
|
|
_VoipEncodeU64(pUserPacket->aPersonaId, (uint64_t) pUser->AccountInfo.iPersonaId);
|
|
_VoipEncodeU32(pUserPacket->aFlags, pUser->uFlags);
|
|
_VoipEncodeU32(pUserPacket->aPlatform, pUser->ePlatform);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionIncrementReliableSeqNb
|
|
|
|
\Description
|
|
Return the next seq nb following the specified seq nb.
|
|
Sequence number validity range: [1,127] (0 = invalid)
|
|
|
|
\Input uSeqNb - input sequence number
|
|
|
|
\Output
|
|
uint8_t - output sequence number
|
|
|
|
\Version 09/18/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t _VoipConnectionIncrementReliableSeqNb(uint8_t uSeqNb)
|
|
{
|
|
if (uSeqNb == 127)
|
|
{
|
|
return(1);
|
|
}
|
|
else
|
|
{
|
|
return(uSeqNb + 1);
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataEnqueue
|
|
|
|
\Description
|
|
Add a reliable data entry to the tail of the specified linked list.
|
|
|
|
\Input **pListHead - pointe to head of list
|
|
\Input *pNewEntry - entry to be added
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipReliableDataEnqueue(LinkedReliableDataT **pListHead, LinkedReliableDataT *pNewEntry)
|
|
{
|
|
if (*pListHead == NULL)
|
|
{
|
|
*pListHead = pNewEntry;
|
|
}
|
|
else
|
|
{
|
|
// find tail of list and append
|
|
LinkedReliableDataT *pCurrent = *pListHead;
|
|
while (pCurrent->pNext != NULL)
|
|
{
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
pCurrent->pNext = pNewEntry;
|
|
}
|
|
|
|
pNewEntry->pNext = NULL;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataDequeue
|
|
|
|
\Description
|
|
Remove a reliable data entry from the tail of the specified linked list.
|
|
|
|
\Input **pListHead - pointer to head of list
|
|
|
|
\Output
|
|
LinkedReliableDataT * - pointer to returned buffer; NULL if error
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static LinkedReliableDataT *_VoipReliableDataDequeue(LinkedReliableDataT **pListHead)
|
|
{
|
|
LinkedReliableDataT *pRemovedEntry = NULL;
|
|
|
|
// return head of list to caller
|
|
if (*pListHead)
|
|
{
|
|
pRemovedEntry = *pListHead;
|
|
*pListHead = (*pListHead)->pNext;
|
|
pRemovedEntry->pNext = NULL;
|
|
}
|
|
|
|
return(pRemovedEntry);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipGetReliableDataBufferFromFreePool
|
|
|
|
\Description
|
|
Obtain a free reliable data buffer from the free pool.
|
|
(Extract from head of linked list)
|
|
|
|
\Input *pConnectionlist - connection list
|
|
\Input bAllocIfEmpty - enable/disable allocating buffer in pool is empty
|
|
|
|
\Output
|
|
LinkedReliableDataT * - pointer to returned buffer; NULL if error
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static LinkedReliableDataT *_VoipGetReliableDataBufferFromFreePool(VoipConnectionlistT *pConnectionlist, uint8_t bAllocIfEmpty)
|
|
{
|
|
LinkedReliableDataT *pReliableDataBuffer;
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserData;
|
|
|
|
// query current mem group data
|
|
VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData);
|
|
|
|
// get a buffer from the pool of free buffer
|
|
pReliableDataBuffer = _VoipReliableDataDequeue(&pConnectionlist->pFreeReliableDataPool);
|
|
|
|
// if pool is empty, allocate a new entry
|
|
if ((pReliableDataBuffer == NULL) && (bAllocIfEmpty != FALSE))
|
|
{
|
|
if ((pReliableDataBuffer = (LinkedReliableDataT *)DirtyMemAlloc(sizeof(*pReliableDataBuffer), VOIP_MEMID, iMemGroup, pMemGroupUserData)) != NULL)
|
|
{
|
|
ds_memclr(pReliableDataBuffer, sizeof(*pReliableDataBuffer));
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: new free reliable data buffer (%p) added to free pool\n", pReliableDataBuffer));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: error, unable to allocate reliable data buffer\n"));
|
|
}
|
|
}
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
if (pReliableDataBuffer)
|
|
{
|
|
NetPrintf(("voipconnection: reliable data buffer (%p) obtained from free pool\n", pReliableDataBuffer));
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: free pool of reliable data buffers is now empty\n"));
|
|
}
|
|
#endif
|
|
|
|
return(pReliableDataBuffer);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReturnReliableDataBufferToFreePool
|
|
|
|
\Description
|
|
Return buffer to free pool. (Append to tail of linked list)
|
|
|
|
\Input *pConnectionlist - connection list
|
|
\Input *pFreeBuffer - buffer to be returned to pool
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipReturnReliableDataBufferToFreePool(VoipConnectionlistT *pConnectionlist, LinkedReliableDataT *pFreeBuffer)
|
|
{
|
|
_VoipReliableDataEnqueue(&pConnectionlist->pFreeReliableDataPool, pFreeBuffer);
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: reliable data buffer (%p) returned to free pool\n", pFreeBuffer));
|
|
#endif
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipPacketGetWritePtr
|
|
|
|
\Description
|
|
Calculate next write position in the specified voip packet
|
|
|
|
\Input *pMicrPacket - packet pointer
|
|
|
|
\Output
|
|
uint8_t * - next write position in the packet
|
|
|
|
\Version 07/18/2013 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t *_VoipPacketGetWritePtr(VoipMicrPacketT *pMicrPacket)
|
|
{
|
|
uint8_t *pWrite = &pMicrPacket->aData[0];
|
|
|
|
// is the packet buffer already filled with ACK+ACKCNF protocol components used for the reliability mechanism?
|
|
if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF)
|
|
{
|
|
int32_t iReliableAckEntriesCount = *pWrite++;
|
|
pWrite += (iReliableAckEntriesCount * sizeof(ReliableAckT));
|
|
}
|
|
|
|
// is the packet buffer already filled with the DATA protocol component used for the reliability mechanism?
|
|
if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA)
|
|
{
|
|
int32_t iReliableDataEntriesCount = *pWrite++;
|
|
int32_t iReliableDataEntryIndex;
|
|
|
|
for (iReliableDataEntryIndex = 0; iReliableDataEntryIndex < iReliableDataEntriesCount; iReliableDataEntryIndex++)
|
|
{
|
|
ReliableDataT *pCurrent = (ReliableDataT *)pWrite;
|
|
uint32_t uSize = _VoipDecodeU16(&pCurrent->info.uSize[0]);
|
|
|
|
pWrite += sizeof(pCurrent->info) + uSize;
|
|
}
|
|
}
|
|
|
|
// is the packet buffer already filled with some metadata?
|
|
if (pMicrPacket->Head.uFlags & VOIP_PACKET_STATUS_FLAG_METADATA)
|
|
{
|
|
int32_t iMetaDataSize;
|
|
iMetaDataSize = *pWrite++;
|
|
pWrite += iMetaDataSize;
|
|
}
|
|
|
|
// is the packet buffer already filled with some sub-pkts?
|
|
if (pMicrPacket->MicrInfo.uNumSubPackets != 0)
|
|
{
|
|
uint8_t bVariableSubPktLen = ((pMicrPacket->MicrInfo.uSubPacketSize == 0xFF) ? TRUE : FALSE); // check if sub-pkts in this packet are fixed length or variable length
|
|
|
|
// if necessary, jump over sub-pkts already packed in the packet buffer
|
|
if (bVariableSubPktLen)
|
|
{
|
|
int32_t iSubPktIndex, iSubPacketSize;
|
|
|
|
// find where we need to write; each sub-packet is prepended (one byte) with its length
|
|
for (iSubPktIndex = 0; iSubPktIndex < pMicrPacket->MicrInfo.uNumSubPackets; iSubPktIndex++)
|
|
{
|
|
iSubPacketSize = *pWrite;
|
|
pWrite += (iSubPacketSize + 1);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWrite += (pMicrPacket->MicrInfo.uNumSubPackets * pMicrPacket->MicrInfo.uSubPacketSize);
|
|
}
|
|
}
|
|
|
|
return(pWrite);
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipPacketGetMetaDataPtr
|
|
|
|
\Description
|
|
Find position of metadata in the specified voip packet
|
|
|
|
\Input *pMicrPacket - packet pointer
|
|
|
|
\Output
|
|
uint8_t * - ptr to metadata
|
|
|
|
\Version 09/25/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t *_VoipPacketGetMetaDataPtr(VoipMicrPacketT *pMicrPacket)
|
|
{
|
|
uint8_t *pMetaData = &pMicrPacket->aData[0];
|
|
|
|
// is the packet buffer already filled with ACK+ACKCNF protocol components used for the reliability mechanism?
|
|
if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF)
|
|
{
|
|
int32_t iReliableAckEntriesCount = *pMetaData++;
|
|
pMetaData += (iReliableAckEntriesCount * sizeof(ReliableAckT));
|
|
}
|
|
|
|
// is the packet buffer already filled with the DATA protocol component used for the reliability mechanism?
|
|
if (pMicrPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA)
|
|
{
|
|
int32_t iReliableDataEntriesCount = *pMetaData++;
|
|
int32_t iReliableDataEntryIndex;
|
|
|
|
for (iReliableDataEntryIndex = 0; iReliableDataEntryIndex < iReliableDataEntriesCount; iReliableDataEntryIndex++)
|
|
{
|
|
ReliableDataT *pCurrent = (ReliableDataT *)pMetaData;
|
|
uint32_t uSize = _VoipDecodeU16(&pCurrent->info.uSize[0]);
|
|
|
|
pMetaData += sizeof(pCurrent->info) + uSize;
|
|
}
|
|
}
|
|
|
|
return(pMetaData);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipPrepareVoipServerSendMask
|
|
|
|
\Description
|
|
Convert local send mask to voip-server ready send mask
|
|
|
|
\Input *pConnectionlist - connection list
|
|
|
|
\Output
|
|
uint32_t - send mask to be packed in voip packet sent to voip server
|
|
|
|
\Version 10/27/2006 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipPrepareVoipServerSendMask(VoipConnectionlistT *pConnectionlist)
|
|
{
|
|
int32_t iLocalConnId, iVoipServerConnId;
|
|
uint32_t uLocalSendMask = pConnectionlist->uSendMask;
|
|
uint32_t uVoipServerSendMask = 0;
|
|
|
|
for (iLocalConnId = 0; iLocalConnId < pConnectionlist->iMaxConnections; iLocalConnId++)
|
|
{
|
|
iVoipServerConnId = pConnectionlist->pConnections[iLocalConnId].iVoipServerConnId;
|
|
|
|
// make sure connection is used by voip server
|
|
if (iVoipServerConnId != VOIP_CONNID_NONE)
|
|
{
|
|
// make sure local connection has send bit set
|
|
if (uLocalSendMask & (1 << iLocalConnId))
|
|
{
|
|
// set the proper bit in the send mask for the voip server
|
|
uVoipServerSendMask |= (1 << iVoipServerConnId);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(uVoipServerSendMask);
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionIsTranscribedTextRequested
|
|
|
|
\Description
|
|
Returns TRUE if at least one local user is requesting text
|
|
transcription from remote talkers.
|
|
|
|
\Input *pConnectionlist - pointer to connectionlist
|
|
|
|
\Output
|
|
uint32_t - TRUE or FALSE
|
|
|
|
\Version 05/05/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipConnectionIsTranscribedTextRequested(VoipConnectionlistT *pConnectionlist)
|
|
{
|
|
|
|
int32_t iUserIndex;
|
|
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; iUserIndex++)
|
|
{
|
|
if (pConnectionlist->aIsParticipating[iUserIndex] != FALSE)
|
|
{
|
|
if (pConnectionlist->bTranscribedTextRequested[iUserIndex] != FALSE)
|
|
{
|
|
return(TRUE);
|
|
}
|
|
}
|
|
}
|
|
|
|
return(FALSE);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionAllocate
|
|
|
|
\Description
|
|
Allocate an empty connection for use.
|
|
|
|
\Input *pConnectionlist - pointer to connectionlist
|
|
\Input iConnID - connection to allocate (or VOIP_CONNID_*)
|
|
|
|
\Output
|
|
VoipConnectionT * - pointer to connection to use or NULL
|
|
|
|
\Version 03/17/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static VoipConnectionT *_VoipConnectionAllocate(VoipConnectionlistT *pConnectionlist, int32_t iConnID)
|
|
{
|
|
VoipConnectionT *pConnection = NULL;
|
|
|
|
// get a connection ID
|
|
pConnection = NULL;
|
|
if (iConnID == VOIP_CONNID_NONE)
|
|
{
|
|
// find a free connection slot
|
|
for (iConnID = 0; iConnID < pConnectionlist->iMaxConnections; iConnID++)
|
|
{
|
|
// if this is an unallocated connection
|
|
if (pConnectionlist->pConnections[iConnID].eState == ST_DISC)
|
|
{
|
|
pConnection = &pConnectionlist->pConnections[iConnID];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// make sure we found a connection
|
|
if (pConnection == NULL)
|
|
{
|
|
NetPrintf(("voipconnection: out of connections\n"));
|
|
}
|
|
}
|
|
else if ((iConnID >= 0) && (iConnID < pConnectionlist->iMaxConnections))
|
|
{
|
|
// if we're currently connected, complain
|
|
if (pConnectionlist->pConnections[iConnID].eState != ST_DISC)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] connection not available, currently used for clientId=%d\n", iConnID, pConnectionlist->pConnections[iConnID].uRemoteClientId));
|
|
return(NULL);
|
|
}
|
|
|
|
// ref connection
|
|
pConnection = &pConnectionlist->pConnections[iConnID];
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: %d is an invalid connection id\n", iConnID));
|
|
}
|
|
|
|
// return connection ref to caller
|
|
return(pConnection);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipSocketSendto
|
|
|
|
\Description
|
|
Send data with the given socket.
|
|
|
|
\Input *pSocket - socket to send on
|
|
\Input uClientId - client identifier
|
|
\Input *pBuf - data to send
|
|
\Input iLen - size of data to send
|
|
\Input iFlags - send flags
|
|
\Input *pTo - address to send data to
|
|
\Input iToLen - length of address structure
|
|
|
|
\Output
|
|
int32_t - amount of data sent
|
|
|
|
\Version 11/30/2005 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipSocketSendto(SocketT *pSocket, uint32_t uClientId, VoipPacketHeadT *pBuf, int32_t iLen, int32_t iFlags, struct sockaddr *pTo, int32_t iToLen)
|
|
{
|
|
// make sure socket exists
|
|
if (pSocket == NULL)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// set connection identifier (goes in all packets)
|
|
_VoipEncodeU32(pBuf->aClientId, uClientId);
|
|
|
|
// send the packet
|
|
return(SocketSendto(pSocket, (char *)pBuf, iLen, iFlags, pTo, iToLen));
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionMatchSessionId
|
|
|
|
\Description
|
|
Check whether specified session ID matchs the voip connection.
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input iConnId - connection ID
|
|
\Input uSessionId - session ID
|
|
|
|
\Output
|
|
uint8_t - TRUE for match, FALSE for no match
|
|
|
|
\Version 08/30/2011 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t _VoipConnectionMatchSessionId(VoipConnectionlistT *pConnectionlist, int32_t iConnId, uint32_t uSessionId)
|
|
{
|
|
int32_t iSessionIndex;
|
|
int32_t bMatch = FALSE;
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId];
|
|
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
for (iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++)
|
|
{
|
|
if (pConnection->uSessionId[iSessionIndex] == uSessionId)
|
|
{
|
|
bMatch = TRUE;
|
|
}
|
|
}
|
|
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
|
|
return(bMatch);
|
|
}
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionPrintRemoteUsers
|
|
|
|
\Description
|
|
Print remote users
|
|
|
|
\Input *pConnection - connection to transition
|
|
\Input iConnID - index of connection
|
|
|
|
\Version 03/11/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionPrintRemoteUsers(VoipConnectionT *pConnection, int32_t iConnID)
|
|
{
|
|
int32_t iIndex;
|
|
|
|
for (iIndex = 0; iIndex < pConnection->iMaxRemoteUsers; iIndex++)
|
|
{
|
|
if (!VOIP_NullUser((VoipUserT *)(&pConnection->RemoteUsers[iIndex])))
|
|
{
|
|
NetPrintf(("voipconnection: [%d] remote user #%d --> %lld\n", iConnID, iIndex, ((VoipUserT *)&pConnection->RemoteUsers[iIndex])->AccountInfo.iPersonaId));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionPrintSessionIds
|
|
|
|
\Description
|
|
Prints all session IDs sharing this voip connection
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input iConnId - connection id
|
|
|
|
\Version 08/30/2011 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionPrintSessionIds(VoipConnectionlistT *pConnectionlist, int32_t iConnId)
|
|
{
|
|
int32_t iSessionIndex;
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId];
|
|
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
if (pConnection->uSessionId[0] != 0)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] IDs of sessions sharing this voip connection are: ", iConnId));
|
|
for (iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++)
|
|
{
|
|
if (pConnection->uSessionId[iSessionIndex] != 0)
|
|
{
|
|
NetPrintf(("0x%08x\n", pConnection->uSessionId[iSessionIndex]));
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] there is no session ID associated with this connection\n", iConnId));
|
|
}
|
|
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
}
|
|
#endif // DIRTYCODE_LOGGING
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionReliableDataEnqueue
|
|
|
|
\Description
|
|
Add a reliable data entry to the outbound queue of the
|
|
specified connection. (Append to tail of linked list)
|
|
|
|
\Input *pConnectionlist - connection list
|
|
\Input iConnID - connection identifier (VOIP_CONNID_ALL for all)
|
|
\Input *pReliableData - pointer to reliable data to be enqueued
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionReliableDataEnqueue(VoipConnectionlistT *pConnectionlist, int32_t iConnID, ReliableDataT *pReliableData)
|
|
{
|
|
int32_t iConnectionIndex;
|
|
uint32_t uCurrentTick = NetTick();
|
|
|
|
if (iConnID == VOIP_CONNID_ALL)
|
|
{
|
|
iConnectionIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
iConnectionIndex = iConnID;
|
|
}
|
|
|
|
while (iConnectionIndex < pConnectionlist->iMaxConnections)
|
|
{
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex];
|
|
|
|
if (pConnection->eState == ST_ACTV || pConnection->eState == ST_CONN)
|
|
{
|
|
LinkedReliableDataT *pNewEntry = _VoipGetReliableDataBufferFromFreePool(pConnectionlist, TRUE);
|
|
|
|
if (pNewEntry)
|
|
{
|
|
// fill with input data
|
|
ds_memcpy_s(&pNewEntry->data, sizeof(pNewEntry->data), pReliableData, sizeof(*pReliableData));
|
|
|
|
// fill sequence number
|
|
pNewEntry->data.info.uSeq = pConnection->uOutSeqNb;
|
|
pConnection->uOutSeqNb = _VoipConnectionIncrementReliableSeqNb(pConnection->uOutSeqNb);
|
|
|
|
// set remote peer id
|
|
_VoipEncodeU32(&pNewEntry->data.info.aRemoteClientId[0], pConnection->uRemoteClientId);
|
|
|
|
// fill enqueue tick
|
|
pNewEntry->uEnqueueTick = uCurrentTick;
|
|
|
|
// add to oubtound queue
|
|
_VoipReliableDataEnqueue(&pConnection->pOutboundReliableDataQueue, pNewEntry);
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] reliable data buffer (%p) added to outbound queue (seq# %03d, %s, tick=%u)\n", iConnectionIndex, pNewEntry,
|
|
pNewEntry->data.info.uSeq, _strReliableType[pNewEntry->data.info.uType], pNewEntry->uEnqueueTick));
|
|
#endif
|
|
|
|
/*
|
|
In the case of voipserver-based topology, to improve responsiveness and increase chances of reliable data
|
|
making it quick to other end, we alter the last send tick on the connection. This should result in a
|
|
ping packets being sent immediately, and help with reliable data reaching other end for voip connections
|
|
that see their voip traffic squelched by the voip server.
|
|
*/
|
|
if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE)
|
|
{
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] last send tick altered to speed up sending of reliable data via ping packet\n", iConnectionIndex));
|
|
#endif
|
|
pConnection->uLastSend = pConnection->uLastSend - VOIP_PING_RATE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] failed to obtain resources for enqueueing new outbound reliable data\n", iConnectionIndex));
|
|
}
|
|
}
|
|
|
|
// break out of while loop if we are dealing with a single connection only
|
|
if (iConnID == VOIP_CONNID_ALL)
|
|
{
|
|
iConnectionIndex++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionReliableDataDequeue
|
|
|
|
\Description
|
|
Remove reliable data entry from the outbound queue of the
|
|
specified connection. (Remove entry at head of linked list)
|
|
|
|
\Input *pConnectionlist - connection list
|
|
\Input iConnID - connection identifier (VOIP_CONNID_ALL for all)
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionReliableDataDequeue(VoipConnectionlistT *pConnectionlist, int32_t iConnID)
|
|
{
|
|
LinkedReliableDataT *pRemovedEntry = _VoipReliableDataDequeue(&pConnectionlist->pConnections[iConnID].pOutboundReliableDataQueue);
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] reliable data buffer (%p) removed from outbound queue (seq# %03d, %s)\n", iConnID, pRemovedEntry,
|
|
pRemovedEntry->data.info.uSeq, _strReliableType[pRemovedEntry->data.info.uType]));
|
|
#endif
|
|
|
|
_VoipReturnReliableDataBufferToFreePool(pConnectionlist, pRemovedEntry);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataPackACKandACKCNF
|
|
|
|
\Description
|
|
Outbound processing of reliablity data - deals with ACK+ACKCNF protocol
|
|
components specifically.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input iConnID - id of connection to be acked; VOIP_CONNID_ALL to ack all connections
|
|
\Input *pFlags - pointer to uFlags field of the outbound packet
|
|
\Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet
|
|
\Input *pWrite - pointer to next byte to be written in packet payload
|
|
\Input iSpaceLeftInPkt - amount of space left in packet payload
|
|
|
|
\Output
|
|
uint8_t * - pointer to first byte after the newly added ACK
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t * _VoipReliableDataPackACKandACKCNF(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint8_t *pFlags, uint32_t bPing, uint8_t *pWrite, int32_t iSpaceLeftInPkt)
|
|
{
|
|
uint8_t uEntryCount = 0;
|
|
uint8_t *pEntryCount = pWrite; // remember where the entry count needs to be written if any entry added to packet buffer
|
|
uint32_t uSpaceLeftInPkt = (uint32_t)iSpaceLeftInPkt; // amount of space still available in packet for reliable data
|
|
int32_t iConnectionIndex;
|
|
|
|
// skip the byte where the entry count is supposed to be written
|
|
pWrite++;
|
|
|
|
if (iConnID == VOIP_CONNID_ALL)
|
|
{
|
|
iConnectionIndex = 0;
|
|
}
|
|
else
|
|
{
|
|
iConnectionIndex = iConnID;
|
|
}
|
|
|
|
while (iConnectionIndex < pConnectionlist->iMaxConnections)
|
|
{
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex];
|
|
uint32_t bAckOrAckCnfNeeded = FALSE;
|
|
|
|
// if our outbound queue is not empty or if the peer at the remote end of the connection is still acking us, then
|
|
// add ACK+ACKCNF protocol component to packet.
|
|
if ((pConnectionlist->pConnections[iConnectionIndex].pOutboundReliableDataQueue != NULL) || pConnection->bPeerIsAcking)
|
|
{
|
|
bAckOrAckCnfNeeded = TRUE;
|
|
}
|
|
|
|
// make sure there is enough space left in destination packet buffer
|
|
if (sizeof(ReliableAckT) < uSpaceLeftInPkt)
|
|
{
|
|
// do we need to send ACK or ACKCNF to this remote client
|
|
if (pConnection->bPeerNeedsAck || bAckOrAckCnfNeeded)
|
|
{
|
|
ReliableAckT ackEntry;
|
|
|
|
// set remote peer id
|
|
_VoipEncodeU32(&ackEntry.aRemoteClientId[0], pConnection->uRemoteClientId);
|
|
|
|
// Fill in ACK value
|
|
if (pConnection->bPeerNeedsAck)
|
|
{
|
|
/* validty range: [1,127]
|
|
In a scenario where we have no yet received inbound reliable traffic from remote peer, pConnection->uInSeqNb
|
|
is 0 and we end up initializing ackEntry.uAckedSeq with 0 which means 'ignore this field, no sequence number acked' */
|
|
ackEntry.uAckedSeq = pConnection->uInSeqNb;
|
|
}
|
|
else
|
|
{
|
|
// means: 'ignore this field, no sequence number acked'
|
|
ackEntry.uAckedSeq = 0;
|
|
}
|
|
|
|
// set ACKCNF
|
|
if (bAckOrAckCnfNeeded)
|
|
{
|
|
// if our outbound queue is not empty, flag that we are still expecting acks back
|
|
if (pConnection->pOutboundReliableDataQueue != NULL)
|
|
{
|
|
ackEntry.uAckedSeq |= VOIP_RELIABLE_ACKCNF_MASK;
|
|
}
|
|
}
|
|
|
|
// add entry in the packet
|
|
ds_memcpy_s(pWrite, uSpaceLeftInPkt, &ackEntry, sizeof(ackEntry));
|
|
pWrite += sizeof(ackEntry);
|
|
|
|
// removed space used from space left
|
|
uSpaceLeftInPkt -= sizeof(ackEntry);
|
|
|
|
// increment entry count
|
|
uEntryCount += 1;
|
|
|
|
// flag that packet contains at least one ACK+ACKCNF protocol component entry
|
|
*pFlags |= VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF;
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
if (pConnection->bPeerNeedsAck)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# %03d, %s) added to outbound %s packet for clientId=0x%08x\n", iConnectionIndex,
|
|
(ackEntry.uAckedSeq & 0x7F), ((pConnection->pOutboundReliableDataQueue != NULL)?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), pConnection->uRemoteClientId));
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# INV, %s) added to outbound %s packet for clientId=0x%08x\n", iConnectionIndex,
|
|
((pConnection->pOutboundReliableDataQueue != NULL) ?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), pConnection->uRemoteClientId));
|
|
}
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: warning - failed to fit all ACK entries in outbound %s packet\n", (bPing?"PNG":"MIC")));
|
|
break; // no space left in packet, force while loop to exit
|
|
}
|
|
|
|
// break out of while loop if we are dealing with a single connection only
|
|
if (iConnID == VOIP_CONNID_ALL)
|
|
{
|
|
iConnectionIndex++;
|
|
}
|
|
else
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (uEntryCount == 0)
|
|
{
|
|
// reposition pWrite to its initial value
|
|
pWrite = pEntryCount;
|
|
}
|
|
else
|
|
{
|
|
// write the entry count in the first byte that the input pWrite was pointing to
|
|
*pEntryCount = uEntryCount;
|
|
}
|
|
|
|
return(pWrite);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataPackDATA
|
|
|
|
\Description
|
|
Outbound processing of reliablity data - deals with DATA protocol component specifically.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input iConnID - id of connection on which the outbound packet is being sent; CANNOT be VOIP_CONNID_ALL
|
|
\Input *pFlags - pointer to uFlags field of the outbound packet
|
|
\Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet
|
|
\Input *pWrite - pointer to next byte to be written in packet payload
|
|
\Input iSpaceLeftInPkt - amount of space left in packet payload
|
|
|
|
\Output
|
|
uint8_t * - pointer to first byte after the newly added DATA
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t * _VoipReliableDataPackDATA(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint8_t *pFlags, uint32_t bPing, uint8_t *pWrite, int32_t iSpaceLeftInPkt)
|
|
{
|
|
uint8_t uEntryCount = 0;
|
|
uint8_t *pEntryCount = pWrite; // remember where the entry count needs to be written if any entry added to packet buffer
|
|
uint32_t uSpaceLeftInPkt = (uint32_t)iSpaceLeftInPkt; // amount of space still available in packet for reliable data
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID];
|
|
LinkedReliableDataT *pCurrent = pConnection->pOutboundReliableDataQueue;
|
|
|
|
// skip the byte where the entry count is supposed to be written
|
|
pWrite++;
|
|
|
|
// find how many entries from the outbound reliable data queue fit in the outbound packet
|
|
while (pCurrent != NULL)
|
|
{
|
|
uint16_t uSize = _VoipDecodeU16(&pCurrent->data.info.uSize[0]);
|
|
|
|
if (uSize < uSpaceLeftInPkt)
|
|
{
|
|
// pack reliable data info
|
|
ds_memcpy(pWrite, &pCurrent->data.info, sizeof(pCurrent->data.info));
|
|
pWrite += sizeof(pCurrent->data.info);
|
|
|
|
// pack reliable data payload
|
|
ds_memcpy(pWrite, pCurrent->data.aData, uSize);
|
|
pWrite += uSize;
|
|
|
|
// removed space used from space left
|
|
uSpaceLeftInPkt -= uSize;
|
|
|
|
// increment the entry count in the packet
|
|
uEntryCount += 1;
|
|
|
|
// flag that packet contains at least one DATA protocol component entry
|
|
*pFlags |= VOIP_PACKET_RELIABLE_FLAG_DATA;
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
{
|
|
int32_t uClientId = _VoipDecodeU32(&pCurrent->data.info.aRemoteClientId[0]);
|
|
NetPrintf(("voipconnection: [%d] reliable DATA (target: 0x%08x, seq# %03d, %s, %d bytes) added to outbound %s packet\n", iConnID, uClientId, pCurrent->data.info.uSeq,
|
|
_strReliableType[pCurrent->data.info.uType], uSize, (bPing?"PNG":"MIC")));
|
|
}
|
|
#endif
|
|
|
|
// move to next pending entry
|
|
pCurrent = pCurrent->pNext;
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] warning - failed to fit all DATA entries in outbound %s packet (last entry size = %d)\n",
|
|
iConnID, (bPing?"PNG":"MIC"), uSize));
|
|
pCurrent = NULL; // no space left in packet, force while loop to exit
|
|
}
|
|
}
|
|
|
|
if (uEntryCount == 0)
|
|
{
|
|
// reposition pWrite to its initial value
|
|
pWrite = pEntryCount;
|
|
}
|
|
else
|
|
{
|
|
// write the entry count in the first byte that the input pWrite was pointing to
|
|
*pEntryCount = uEntryCount;
|
|
}
|
|
|
|
return(pWrite);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataOutProcess
|
|
|
|
\Description
|
|
Outbound processing of reliablity data. To be invoked every time a
|
|
ping packet or a micr packet is sent. Reliability protocol components
|
|
(ACK, ACKCNF, DATA) are going to be added to the packet if necessary.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input iConnID - connection id; VOIP_CONNID_ALL if packet is being sent to voipserver
|
|
\Input *pPacketHeader - pointer to header of the outbound packet
|
|
\Input *pPacketPayload - pointer to first payload byte of outbound packet
|
|
\Input iPayloadCapacity - amount of bytes we can fit in the payload
|
|
|
|
\Output
|
|
uint8_t * - pointer to first byte after reliable data in packet payload
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t * _VoipReliableDataOutProcess(VoipConnectionlistT *pConnectionlist, int32_t iConnID, VoipPacketHeadT *pPacketHeader, uint8_t *pPacketPayload, int32_t iPayloadCapacity)
|
|
{
|
|
int32_t iSpaceLeftInPkt = iPayloadCapacity;
|
|
uint32_t bPing = ((pPacketHeader->aType[0] == 'P' && pPacketHeader->aType[1] == 'N' && pPacketHeader->aType[2] == 'G') ? TRUE : FALSE);
|
|
uint8_t *pWrite;
|
|
|
|
// add ACK and ACKCNF to packet as necessary
|
|
pWrite = _VoipReliableDataPackACKandACKCNF(pConnectionlist, iConnID, &pPacketHeader->uFlags, bPing, pPacketPayload, iSpaceLeftInPkt);
|
|
iSpaceLeftInPkt -= (pWrite - pPacketPayload);
|
|
|
|
if (iConnID == VOIP_CONNID_ALL)
|
|
{
|
|
int32_t iConnectionIndex;
|
|
int32_t iLargestTickDiff = 0;
|
|
|
|
/*
|
|
When voip traffic flows through the voip server, we need to pick a local voip connection
|
|
to pack the outbound reliable data entries from. Each connection have its own outbound
|
|
reliable queue. They are all populated with same entries tagged with same sequence numbers.
|
|
But the acking progress on each queue may progress differently. For instance, queue X may
|
|
have entry seq nb 10 acked, but queue Y may still be waiting for entry seq nb 8 to be acked.
|
|
|
|
The best we can do is pick the connection that has the oldest pending reliable data. Other
|
|
connections will benefit of this too if there are still waiting for ack of that entry, or
|
|
they will rely on the next ping packet to go out.
|
|
*/
|
|
|
|
for (iConnectionIndex = 0; iConnectionIndex < pConnectionlist->iMaxConnections; iConnectionIndex++)
|
|
{
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex];
|
|
|
|
if ((pConnection->eState == ST_ACTV) && (pConnection->pOutboundReliableDataQueue != NULL))
|
|
{
|
|
int32_t iTickDiff = NetTickDiff(NetTick(), pConnection->pOutboundReliableDataQueue->uEnqueueTick);
|
|
|
|
if (iLargestTickDiff < iTickDiff)
|
|
{
|
|
iLargestTickDiff = iTickDiff;
|
|
iConnID = iConnectionIndex;
|
|
}
|
|
}
|
|
}
|
|
// if above for loop did not match on any connection, fallback to connection index 0
|
|
if (iConnID == VOIP_CONNID_ALL)
|
|
{
|
|
iConnID = 0;
|
|
}
|
|
}
|
|
|
|
pWrite = _VoipReliableDataPackDATA(pConnectionlist, iConnID, &pPacketHeader->uFlags, bPing, pWrite, iSpaceLeftInPkt);
|
|
|
|
return(pWrite);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataInboundUserAddProcess
|
|
|
|
\Description
|
|
Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_USERADD.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection on which the packet was received
|
|
\Input *pReliableData - pointer to inbound reliable data entry
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipReliableDataInboundUserAddProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData)
|
|
{
|
|
uint8_t uUserIndex = pReliableData->aData[0]; // first byte is user index
|
|
VoipUserT *pRemoteUser = (VoipUserT *)(&pReliableData->aData[0] + 1);
|
|
|
|
if (VOIP_NullUser((VoipUserT *)&pConnection->RemoteUsers[uUserIndex]))
|
|
{
|
|
int32_t iConnID = pConnection - &pConnectionlist->pConnections[0];
|
|
|
|
// update collection of remote users for this connection
|
|
VOIP_CopyUser(&pConnection->RemoteUsers[uUserIndex], pRemoteUser);
|
|
|
|
// register the user
|
|
pConnectionlist->pRegUserCb((VoipUserT *)&pConnection->RemoteUsers[uUserIndex], (uint32_t)iConnID, TRUE, pConnectionlist->pRegUserUserData);
|
|
|
|
// force re-application of channel config since we have a new remote player
|
|
((VoipCommonRefT *)VoipGetRef())->bApplyChannelConfig = TRUE;
|
|
|
|
#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK)
|
|
// reapply player-to-player xone comm relationships
|
|
pConnectionlist->bApplyRelFromMuting = TRUE;
|
|
#endif
|
|
// set mute list to be updated
|
|
pConnectionlist->bUpdateMute = TRUE;
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
_VoipConnectionPrintRemoteUsers(pConnection, (pConnection - &pConnectionlist->pConnections[0]));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] join-in-progress for remote user %lld failed because index %d already used by %lld\n", (pConnection - &pConnectionlist->pConnections[0]),
|
|
((VoipUserT *)pRemoteUser)->AccountInfo.iPersonaId, uUserIndex, ((VoipUserT *)&pConnection->RemoteUsers[uUserIndex])->AccountInfo.iPersonaId));
|
|
}
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataInboundUserRemProcess
|
|
|
|
\Description
|
|
Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_USERREM.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection on which the packet was received
|
|
\Input *pReliableData - pointer to inbound reliable data entry
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipReliableDataInboundUserRemProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData)
|
|
{
|
|
uint8_t uUserIndex = pReliableData->aData[0]; // first byte is user index
|
|
VoipUserT *pRemoteUser = (VoipUserT *)(&pReliableData->aData[0] + 1);
|
|
|
|
if (!VOIP_NullUser((VoipUserT *)&pConnection->RemoteUsers[uUserIndex]))
|
|
{
|
|
if (memcmp(pRemoteUser, &pConnection->RemoteUsers[uUserIndex], sizeof(*pRemoteUser)) == 0)
|
|
{
|
|
int32_t iConnID = pConnection - &pConnectionlist->pConnections[0];
|
|
|
|
// unregister the user
|
|
pConnectionlist->pRegUserCb((VoipUserT *)&pConnection->RemoteUsers[uUserIndex], (uint32_t)iConnID, FALSE, pConnectionlist->pRegUserUserData);
|
|
|
|
// force re-application of channel config since we lost a remote player
|
|
((VoipCommonRefT *)VoipGetRef())->bApplyChannelConfig = TRUE;
|
|
|
|
#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK)
|
|
// reapply player-to-player xone comm relationships
|
|
pConnectionlist->bApplyRelFromMuting = TRUE;
|
|
#endif
|
|
|
|
// set mute list to be updated
|
|
pConnectionlist->bUpdateMute = TRUE;
|
|
|
|
// clear the user
|
|
VOIP_ClearUser((VoipUserT *)&pConnection->RemoteUsers[uUserIndex]);
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
_VoipConnectionPrintRemoteUsers(pConnection, (pConnection - &pConnectionlist->pConnections[0]));
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] leave-in-progress for remote user %lld failed because remote user at index %d rather is %lld\n", (pConnection - &pConnectionlist->pConnections[0]),
|
|
((VoipUserT *)pRemoteUser)->AccountInfo.iPersonaId, uUserIndex, ((VoipUserT *)&pConnection->RemoteUsers[uUserIndex])->AccountInfo.iPersonaId));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] leave-in-progress for remote user %lld failed because no remote user at index %d\n", (pConnection - &pConnectionlist->pConnections[0]),
|
|
((VoipUserT *)pRemoteUser)->AccountInfo.iPersonaId, uUserIndex));
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataInboundOpaqueProcess
|
|
|
|
\Description
|
|
Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_OPAQUE.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection on which the packet was received
|
|
\Input *pReliableData - pointer to inbound reliable data entry
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipReliableDataInboundOpaqueProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData)
|
|
{
|
|
uint16_t uDataSize = _VoipDecodeU16(&pReliableData->info.uSize[0]);
|
|
int32_t iConnID = pConnection - &pConnectionlist->pConnections[0];
|
|
pConnectionlist->pOpaqueCb(iConnID, &pReliableData->aData[0], uDataSize, pConnectionlist->pOpaqueUserData);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataInboundTextProcess
|
|
|
|
\Description
|
|
Inbound processing of reliablity data of type VOIP_RELIABLE_TYPE_TEXT.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection on which the packet was received
|
|
\Input *pReliableData - pointer to inbound reliable data entry
|
|
|
|
\Version 05/02/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipReliableDataInboundTextProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, ReliableDataT *pReliableData)
|
|
{
|
|
uint8_t uUserIndex = pReliableData->aData[0]; // first byte is user index
|
|
uint16_t uTextSize = _VoipDecodeU16(&pReliableData->info.uSize[0]) - 1; // text size is payload size minus the byte used for the user index
|
|
|
|
if (uTextSize > 0)
|
|
{
|
|
if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist))
|
|
{
|
|
int32_t iConnID = pConnection - &pConnectionlist->pConnections[0];
|
|
|
|
if (pConnectionlist->uRecvMask & (1 << iConnID))
|
|
{
|
|
pConnectionlist->pTextCb(iConnID, uUserIndex, (char *)&pReliableData->aData[1], (void *)pConnectionlist->pTextUserData);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: inbound transcribed text dropped because connection (%d) is muted\n", iConnID));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: inbound transcribed text dropped because none of the local players requested for it (normal when voip is routed through DirytCast voipserver)\n"));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: inbound transcribed text dropped because decoding resulted in an empty string\n"));
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataUnpackACKandACKCNF
|
|
|
|
\Description
|
|
Inbound processing of reliablity data - deals with ACK+ACKCNF protocol
|
|
components specifically.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input uSrcClientId - client id of originator
|
|
\Input uFlags - uFlags from the inbound packet
|
|
\Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet
|
|
\Input *pRead - pointer to next byte to be read in packet payload
|
|
|
|
\Output
|
|
uint8_t * - pointer to first byte after ACK
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t * _VoipReliableDataUnpackACKandACKCNF(VoipConnectionlistT *pConnectionlist, uint32_t uSrcClientId, uint8_t uFlags, uint32_t bPing, uint8_t *pRead)
|
|
{
|
|
int32_t iConnectionIndex;
|
|
VoipConnectionT *pConnection = NULL;
|
|
uint32_t bPeerIsAckingUs = FALSE;
|
|
|
|
// find connection matching the source client id
|
|
for (iConnectionIndex = 0; iConnectionIndex < pConnectionlist->iMaxConnections; iConnectionIndex++)
|
|
{
|
|
if (pConnectionlist->pConnections[iConnectionIndex].uRemoteClientId == uSrcClientId)
|
|
{
|
|
pConnection = &pConnectionlist->pConnections[iConnectionIndex];
|
|
break;
|
|
}
|
|
}
|
|
|
|
// is the packet buffer already filled with ACK+ACKCNF protocol components used for the reliability mechanism?
|
|
if (uFlags & VOIP_PACKET_RELIABLE_FLAG_ACK_ACKCNF)
|
|
{
|
|
int32_t iReliableAckEntriesCount = *pRead++;
|
|
int32_t iReliableAckEntryIndex;
|
|
|
|
for (iReliableAckEntryIndex = 0; iReliableAckEntryIndex < iReliableAckEntriesCount; iReliableAckEntryIndex++)
|
|
{
|
|
uint32_t uClientId;
|
|
ReliableAckT *pReliableAck = (ReliableAckT *)pRead;
|
|
|
|
pRead += sizeof(ReliableAckT);
|
|
|
|
if (pConnection != NULL)
|
|
{
|
|
// determine our local client id from the remote client's point of view
|
|
uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId;
|
|
|
|
// extract client id from ACK+ACKCNF protocol component entry
|
|
uClientId = _VoipDecodeU32(&pReliableAck->aRemoteClientId[0]);
|
|
|
|
// ignore the entry if it is not for us
|
|
if (uClientId == uLocalClientId)
|
|
{
|
|
uint8_t uAckedSeq;
|
|
|
|
// deal with ACKCNF
|
|
pConnection->bPeerNeedsAck = ((pReliableAck->uAckedSeq & VOIP_RELIABLE_ACKCNF_MASK) ? TRUE : FALSE);
|
|
|
|
// deal with ACK
|
|
uAckedSeq = pReliableAck->uAckedSeq & ~VOIP_RELIABLE_ACKCNF_MASK;
|
|
if (uAckedSeq != 0)
|
|
{
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# %03d, %s) found in inbound %s packet from clientId=0x%08x\n",
|
|
iConnectionIndex, uAckedSeq, (pConnection->bPeerNeedsAck ?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), uSrcClientId));
|
|
#endif
|
|
bPeerIsAckingUs = TRUE;
|
|
|
|
while ( (pConnection->pOutboundReliableDataQueue != NULL) &&
|
|
(uAckedSeq >= pConnection->pOutboundReliableDataQueue->data.info.uSeq) )
|
|
{
|
|
_VoipConnectionReliableDataDequeue(pConnectionlist, iConnectionIndex);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] reliable ACK+ACKCNF (acked seq# INV, %s) found in inbound %s packet from clientId=0x%08x\n",
|
|
iConnectionIndex, (pConnection->bPeerNeedsAck ?"SendAck":"HaltAck"), (bPing?"PNG":"MIC"), uSrcClientId));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] dropped inbound ACK+ACKCNF - failed to find connection matching originating clientId=0x%08x\n", iConnectionIndex, uSrcClientId));
|
|
}
|
|
}
|
|
}
|
|
|
|
if (pConnection != NULL)
|
|
{
|
|
pConnection->bPeerIsAcking = bPeerIsAckingUs;
|
|
}
|
|
|
|
return(pRead);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataUnpackDATA
|
|
|
|
\Description
|
|
Inbound processing of reliablity data - deals with DATA protocol component specifically.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection on which the packet was received
|
|
\Input uFlags - uFlags from the inbound packet
|
|
\Input bPing - TRUE if packet is a ping packet, FALSE if packet is a micr packet
|
|
\Input *pRead - pointer to next byte to be read in packet payload
|
|
|
|
\Output
|
|
uint8_t * - pointer to first byte after DATA
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t * _VoipReliableDataUnpackDATA(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, uint8_t uFlags, uint32_t bPing, uint8_t *pRead)
|
|
{
|
|
// is the packet buffer already filled with the ACK protocol component used for the reliability mechanism?
|
|
if (uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA)
|
|
{
|
|
int32_t iReliableDataEntriesCount = *pRead++;
|
|
int32_t iReliableDataEntryIndex;
|
|
|
|
for (iReliableDataEntryIndex = 0; iReliableDataEntryIndex < iReliableDataEntriesCount; iReliableDataEntryIndex++)
|
|
{
|
|
ReliableDataT *pReliableData = (ReliableDataT *)pRead;
|
|
uint16_t uSize = _VoipDecodeU16(&pReliableData->info.uSize[0]);
|
|
uint32_t uClientId = _VoipDecodeU32(&pReliableData->info.aRemoteClientId[0]);
|
|
uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId;
|
|
|
|
// move read pointer ahead
|
|
pRead += sizeof(pReliableData->info) + uSize;
|
|
|
|
// make sure this is for us
|
|
if (uClientId == uLocalClientId)
|
|
{
|
|
if (pReliableData->info.uSeq == _VoipConnectionIncrementReliableSeqNb(pConnection->uInSeqNb))
|
|
{
|
|
// update next expected inbound reliable seq nb
|
|
pConnection->uInSeqNb = pReliableData->info.uSeq;
|
|
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] reliable DATA (seq# %03d, %s, %d bytes) found in inbound %s packet\n", (pConnection - &pConnectionlist->pConnections[0]),
|
|
pReliableData->info.uSeq, _strReliableType[pReliableData->info.uType], uSize, (bPing?"PNG":"MIC")));
|
|
#endif
|
|
|
|
if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_USERADD)
|
|
{
|
|
_VoipReliableDataInboundUserAddProcess(pConnectionlist, pConnection, pReliableData);
|
|
}
|
|
else if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_USERREM)
|
|
{
|
|
_VoipReliableDataInboundUserRemProcess(pConnectionlist, pConnection, pReliableData);
|
|
}
|
|
else if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_OPAQUE)
|
|
{
|
|
_VoipReliableDataInboundOpaqueProcess(pConnectionlist, pConnection, pReliableData);
|
|
}
|
|
else if (pReliableData->info.uType == VOIP_RELIABLE_TYPE_TEXT)
|
|
{
|
|
_VoipReliableDataInboundTextProcess(pConnectionlist, pConnection, pReliableData);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] unsupported inbound reliable data type (%d)\n", (pConnection - &pConnectionlist->pConnections[0]), pReliableData->info.uType));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] dropped inbound reliable data embedded in %s packet - seq nb mismatch (got: %d - expected: %d)\n",
|
|
(pConnection - &pConnectionlist->pConnections[0]), (bPing ? "PNG" : "MIC"), pReliableData->info.uSeq, _VoipConnectionIncrementReliableSeqNb(pConnection->uInSeqNb)));
|
|
#endif
|
|
}
|
|
}
|
|
else
|
|
{
|
|
#if VOIP_RELIABLE_DEBUG
|
|
NetPrintf(("voipconnection: [%d] dropped inbound reliable data embedded in %s packet - client id mismatch (target: 0x%08x - local: 0x%08x)\n",
|
|
(pConnection - &pConnectionlist->pConnections[0]), (bPing ? "PNG" : "MIC"), uClientId, uLocalClientId));
|
|
#endif
|
|
}
|
|
}
|
|
}
|
|
|
|
return(pRead);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipReliableDataInProcess
|
|
|
|
\Description
|
|
Inbound processing of reliablity data. To be invoked every time a
|
|
ping packet or a micr packet is received. Reliability protocol components
|
|
(ACK, ACKCNF, DATA) included in the packet are going to be processed appropriately.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection on which the packet was received
|
|
\Input *pPacketHeader - pointer to header of the inbound packet
|
|
\Input *pPacketPayload - pointer to first payload byte of inbound packet
|
|
|
|
\Output
|
|
uint8_t - first byte following reliable data in packet payload
|
|
|
|
\Version 09/14/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint8_t * _VoipReliableDataInProcess(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, VoipPacketHeadT *pPacketHeader, uint8_t *pPacketPayload)
|
|
{
|
|
// note: for voipserver-based topology, uSrcClientId may not be same as pConnection->uClientId
|
|
uint32_t uSrcClientId = _VoipDecodeU32(&pPacketHeader->aClientId[0]);
|
|
uint32_t bPing = ((pPacketHeader->aType[0] == 'P' && pPacketHeader->aType[1] == 'N' && pPacketHeader->aType[2] == 'G') ? TRUE : FALSE);
|
|
|
|
uint8_t *pRead = _VoipReliableDataUnpackACKandACKCNF(pConnectionlist, uSrcClientId, pPacketHeader->uFlags, bPing, pPacketPayload);
|
|
|
|
pRead = _VoipReliableDataUnpackDATA(pConnectionlist, pConnection, pPacketHeader->uFlags, bPing, pRead);
|
|
|
|
return(pRead);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipUpdateUserFlags
|
|
|
|
\Description
|
|
Update the flags field for each user just before serialization.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
|
|
\Version 10/02/2019 (cvienneau)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipUpdateUserFlags(VoipConnectionlistT *pConnectionlist)
|
|
{
|
|
int32_t iUserIndex;
|
|
uint8_t bCrossPlay = VoipStatus(VoipGetRef(), 'xply', 0, NULL, 0);
|
|
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++)
|
|
{
|
|
if (pConnectionlist->aIsParticipating[iUserIndex] == TRUE)
|
|
{
|
|
// is cross play setup for the local users
|
|
if (bCrossPlay)
|
|
{
|
|
pConnectionlist->LocalUsers[iUserIndex].uFlags |= VOIPUSER_FLAG_CROSSPLAY;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionConn
|
|
|
|
\Description
|
|
Send a connection packet to peer
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - pointer to connection to connect on
|
|
\Input uTick - current tick count
|
|
|
|
\Version 03/20/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionConn(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, uint32_t uTick)
|
|
{
|
|
VoipConnPacketT ConnPacket;
|
|
int32_t iUserIndex;
|
|
uint32_t uLocalClientId = 0;
|
|
uint32_t uSize = sizeof(ConnPacket) - sizeof(ConnPacket.LocalUsers) + (sizeof(*ConnPacket.LocalUsers) * VOIP_MAXLOCALUSERS_EXTENDED);
|
|
|
|
// output diagnostic info
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) sending connect packet to remote console (%a:%d)\n",
|
|
VOIP_ConnID(pConnectionlist, pConnection), pConnection->uSessionId[0],
|
|
SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port)));
|
|
|
|
// send a connection packet
|
|
ds_memclr(&ConnPacket, sizeof(VoipConnPacketT));
|
|
ds_memcpy_s(&ConnPacket.Head, sizeof(ConnPacket.Head), &_Voip_ConnPacket, sizeof(_Voip_ConnPacket));
|
|
_VoipEncodeU32(ConnPacket.aRemoteClientId, pConnection->uRemoteClientId);
|
|
_VoipEncodeU32(ConnPacket.Head.aSessionId, pConnection->uSessionId[0]);
|
|
_VoipEncodeLocalHeadsetStatus(pConnectionlist, &(ConnPacket.Head));
|
|
if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist))
|
|
{
|
|
ConnPacket.Head.uFlags |= VOIP_PACKET_STATUS_FLAG_STT;
|
|
}
|
|
_VoipUpdateUserFlags(pConnectionlist);
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++)
|
|
{
|
|
if (pConnectionlist->aIsParticipating[iUserIndex] != FALSE)
|
|
{
|
|
_VoipEncodeVoipUser(&ConnPacket.LocalUsers[iUserIndex], &pConnectionlist->LocalUsers[iUserIndex]);
|
|
}
|
|
}
|
|
|
|
ConnPacket.bConnected = pConnection->bConnPktRecv;
|
|
|
|
// extended local users always have an extra slot allocated and are not the same as maxlocalusers
|
|
ConnPacket.uNumLocalUsers = VOIP_MAXLOCALUSERS_EXTENDED;
|
|
|
|
#if VOIP_CONNECTION_DEBUG
|
|
NetPrintMem(&ConnPacket, sizeof(ConnPacket), "connection packet data");
|
|
#endif
|
|
uLocalClientId = pConnection->uLocalClientId == 0 ? pConnectionlist->uClientId : pConnection->uLocalClientId;
|
|
pConnection->iSendResult = _VoipSocketSendto(pConnectionlist->pSocket, uLocalClientId, &ConnPacket.Head, uSize, 0, (struct sockaddr *)&pConnection->SendAddr, sizeof(pConnection->SendAddr));
|
|
|
|
// update timestamp
|
|
pConnection->uLastSend = uTick;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionDisc
|
|
|
|
\Description
|
|
Send a disconnection request to peer.
|
|
|
|
\Input iConnId - connection id
|
|
\Input uClientId - client id
|
|
\Input uRemoteClientId - remote client id
|
|
\Input uSessionId - session identifier
|
|
\Input *pSendAddr - address to send disconnect to
|
|
\Input *pSocket - socket to send request with
|
|
|
|
\Version 03/21/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionDisc(int32_t iConnId, uint32_t uClientId, uint32_t uRemoteClientId, uint32_t uSessionId, struct sockaddr_in *pSendAddr, SocketT *pSocket)
|
|
{
|
|
VoipDiscPacketT DiscPacket;
|
|
|
|
// send a disconnect packet
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) sending disconnect packet to %a:%d\n",
|
|
iConnId, uSessionId, SocketNtohl(pSendAddr->sin_addr.s_addr), SocketNtohs(pSendAddr->sin_port)));
|
|
|
|
// set up disc packet
|
|
ds_memcpy_s(&DiscPacket.Head, sizeof(DiscPacket.Head), &_Voip_DiscPacket, sizeof(_Voip_DiscPacket));
|
|
_VoipEncodeU32(DiscPacket.aRemoteClientId, uRemoteClientId);
|
|
_VoipEncodeU32(DiscPacket.Head.aSessionId, uSessionId);
|
|
|
|
// send disc packet
|
|
_VoipSocketSendto(pSocket, uClientId, &DiscPacket.Head, sizeof(DiscPacket), 0, (struct sockaddr *)pSendAddr, sizeof(*pSendAddr));
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionInit
|
|
|
|
\Description
|
|
Transition from connecting to connected.
|
|
|
|
\Input *pConnectionlist - connectionlist connection is a part of
|
|
\Input *pConnection - connection to transition
|
|
\Input iConnID - index of connection
|
|
|
|
\Version 03/21/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionInit(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnID)
|
|
{
|
|
int32_t iLocalUserIndex;
|
|
|
|
NetPrintf(("voipconnection: [%d] connection established; talking to remote user(s)\n", iConnID));
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
_VoipConnectionPrintRemoteUsers(pConnection, iConnID);
|
|
#endif
|
|
|
|
// register the new remote users with voipheadset
|
|
VoipConnectionRegisterRemoteTalkers(pConnectionlist, iConnID, TRUE);
|
|
|
|
// we want to call this before the connection goes to state ST_ACTV
|
|
// to avoid race conditions where the voipthread would start working with this conn before the channel config is updated
|
|
VoipCommonApplyChannelConfig((VoipCommonRefT *)VoipGetRef());
|
|
|
|
// update connection flags
|
|
pConnection->uRemoteConnStatus = VOIP_CONN_CONNECTED;
|
|
|
|
// flag that this connection shall now be considered when updating friend status bitmask
|
|
if (pConnectionlist->iFriendConnId == VOIP_CONNID_NONE)
|
|
{
|
|
pConnectionlist->iFriendConnId = iConnID;
|
|
}
|
|
else
|
|
{
|
|
pConnectionlist->iFriendConnId = VOIP_CONNID_ALL;
|
|
}
|
|
|
|
// initialize timer used for sending at a fixed 10hz rate
|
|
for (iLocalUserIndex = 0; iLocalUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iLocalUserIndex++)
|
|
{
|
|
pConnection->aVoiceSendTimer[iLocalUserIndex] = NetTick();
|
|
}
|
|
|
|
/*
|
|
To avoid multi-threading race conditions, it is important to only move the connection state to ST_ACTV at the end of
|
|
the function (after initializing all connection vars and updating channel config) because some code exercised by the
|
|
_VoipThread (like _VoipUpdateRemoteStatus()) may start to use these fields concurrently as soon as the connection state
|
|
becomes ST_ACTV
|
|
*/
|
|
pConnection->eState = ST_ACTV;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionPing
|
|
|
|
\Description
|
|
Ping a connected remote peer.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - pointer to connection to send on
|
|
\Input uTick - current tick count
|
|
|
|
\Version 03/20/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionPing(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, uint32_t uTick)
|
|
{
|
|
VoipPingPacketT PingPacket;
|
|
int32_t iUserIndex;
|
|
uint32_t uChannelUserIndices = 0;
|
|
uint8_t *pWrite = PingPacket.aData;
|
|
const uint8_t *pEnd = pWrite+sizeof(PingPacket.aData);
|
|
uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId;
|
|
|
|
#if VOIP_CONNECTION_DEBUG
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) pinging %a:%d\n",
|
|
VOIP_ConnID(pConnectionlist, pConnection), pConnection->uSessionId[0],
|
|
SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port)));
|
|
#endif
|
|
|
|
// set up ping packet
|
|
ds_memcpy_s(&PingPacket.Head, sizeof(PingPacket.Head), &_Voip_PingPacket, sizeof(_Voip_PingPacket));
|
|
_VoipEncodeLocalHeadsetStatus(pConnectionlist, &(PingPacket.Head));
|
|
_VoipEncodeU32(PingPacket.aRemoteClientId, pConnection->uRemoteClientId);
|
|
_VoipEncodeU32(PingPacket.Head.aSessionId, pConnection->uSessionId[0]);
|
|
if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist))
|
|
{
|
|
PingPacket.Head.uFlags |= VOIP_PACKET_STATUS_FLAG_STT;
|
|
}
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++)
|
|
{
|
|
/* write the channel information for the participating user indices and
|
|
set the bit so the receiving side knows which index it corresponds to */
|
|
if (pConnectionlist->aIsParticipating[iUserIndex])
|
|
{
|
|
uChannelUserIndices |= 1 << iUserIndex;
|
|
_VoipEncodeU32(pWrite, pConnectionlist->aChannels[iUserIndex]);
|
|
pWrite += 4;
|
|
}
|
|
}
|
|
_VoipEncodeU32(PingPacket.aChannelUserIndices, uChannelUserIndices);
|
|
|
|
// if needed, add reliable protocol components to this ping packet
|
|
pWrite = _VoipReliableDataOutProcess(pConnectionlist, (pConnection - &pConnectionlist->pConnections[0]), &PingPacket.Head, pWrite, pEnd - pWrite);
|
|
|
|
// send a 'ping' packet
|
|
pConnection->iSendResult = _VoipSocketSendto(pConnectionlist->pSocket, uLocalClientId, &PingPacket.Head, (pWrite - (uint8_t *)&PingPacket), 0, (struct sockaddr *)&pConnection->SendAddr, sizeof(pConnection->SendAddr));
|
|
|
|
// update send timestamp and local status flag
|
|
pConnection->uLastSend = uTick;
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; ++iUserIndex)
|
|
{
|
|
if (NetTickDiff(uTick, pConnectionlist->uLocalUserLastSend[iUserIndex]) > VOIP_PING_RATE)
|
|
{
|
|
pConnectionlist->uLocalUserStatus[iUserIndex] &= ~VOIP_LOCAL_USER_SENDVOICE;
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionIsSendPktReady
|
|
|
|
\Description
|
|
Determine if we should send the current packet.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection pointer
|
|
\Input iUserIndex - index of local user that generated the voice to be sent
|
|
\Input uTick - current tick count
|
|
|
|
\Output
|
|
uint32_t - TRUE if ready, FALSE otherwise.
|
|
|
|
\Version 10/01/2011 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipConnectionIsSendPktReady(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iUserIndex, uint32_t uTick)
|
|
{
|
|
VoipMicrPacketT *pVoipPkt = &pConnection->VoipMicrPacket[iUserIndex];
|
|
uint8_t *pWrite = _VoipPacketGetWritePtr(pVoipPkt);
|
|
uint8_t bVariableSubPktLen = ((pVoipPkt->MicrInfo.uSubPacketSize == 0xFF) ? TRUE : FALSE); // check if sub-pkts in this packet are fixed length or variable length
|
|
int32_t iEstimatedResultingPacketPayloadSize;
|
|
|
|
// if an additional sub-packet would cause us to overflow the sub-packet buffer, send packet
|
|
if (bVariableSubPktLen)
|
|
{
|
|
// is next packet going to overflow? 1 byte for prepended size + estimated subpkt size
|
|
iEstimatedResultingPacketPayloadSize = (pWrite + 1 + pConnection->iMaxSubPktSize) - pVoipPkt->aData;
|
|
}
|
|
else
|
|
{
|
|
iEstimatedResultingPacketPayloadSize = (pWrite + pVoipPkt->MicrInfo.uSubPacketSize) - pVoipPkt->aData;
|
|
}
|
|
|
|
if (iEstimatedResultingPacketPayloadSize > (signed)sizeof(pVoipPkt->aData))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
// if 10hz timer has elapsed, send packet
|
|
if ((pVoipPkt->MicrInfo.uNumSubPackets > 0) && (NetTickDiff(uTick, pConnection->aVoiceSendTimer[iUserIndex]) >= 0))
|
|
{
|
|
return(TRUE);
|
|
}
|
|
|
|
// not yet ready to send
|
|
return(FALSE);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionSendSingle
|
|
|
|
\Description
|
|
Send a single buffered voice packet, if it is time to send it.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - pointer to connection to send on
|
|
\Input iUserIndex - index of the local user that generated the voice data to be sent
|
|
\Input uTick - current tick count
|
|
\Input bFlush - flush data
|
|
|
|
\Version 03/20/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionSendSingle(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iUserIndex, uint32_t uTick, uint32_t bFlush)
|
|
{
|
|
// ref current buffered packet
|
|
VoipMicrPacketT *pPacket = &pConnection->VoipMicrPacket[iUserIndex];
|
|
uint32_t bSendPacket = FALSE;
|
|
int32_t iParticipatingUserIndex;
|
|
uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId;
|
|
|
|
// should we send?
|
|
if (_VoipConnectionIsSendPktReady(pConnectionlist, pConnection, iUserIndex, uTick) || (bFlush == TRUE))
|
|
{
|
|
bSendPacket = TRUE;
|
|
}
|
|
|
|
// send packet?
|
|
if (bSendPacket == TRUE)
|
|
{
|
|
int32_t iPacketSize, iValidPayloadSize;
|
|
uint32_t uVoipServerReadySendMask = 0;
|
|
|
|
// set packet sequence
|
|
pPacket->MicrInfo.uSeqn = pConnection->uSendSeqn++;
|
|
|
|
// set the user index
|
|
if (iUserIndex == VOIP_SHARED_USER_INDEX)
|
|
{
|
|
pPacket->MicrInfo.uUserIndex = VOIP_SHARED_REMOTE_INDEX;
|
|
}
|
|
else
|
|
{
|
|
pPacket->MicrInfo.uUserIndex = iUserIndex;
|
|
}
|
|
|
|
// set the channels
|
|
_VoipEncodeU32(pPacket->MicrInfo.channels.aChannelId, pConnectionlist->aChannels[iUserIndex]);
|
|
|
|
// set the session id
|
|
_VoipEncodeU32(pPacket->Head.aSessionId, pConnection->uSessionId[0]);
|
|
|
|
// flag whether one of the local users is requesting transcribed text
|
|
if (_VoipConnectionIsTranscribedTextRequested(pConnectionlist))
|
|
{
|
|
pPacket->Head.uFlags |= VOIP_PACKET_STATUS_FLAG_STT;
|
|
}
|
|
|
|
// set send mask for VoipServer use
|
|
if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE)
|
|
{
|
|
uVoipServerReadySendMask = _VoipPrepareVoipServerSendMask(pConnectionlist);
|
|
}
|
|
_VoipEncodeU32(pPacket->MicrInfo.aSendMask, uVoipServerReadySendMask);
|
|
_VoipEncodeLocalHeadsetStatus(pConnectionlist, &pPacket->Head);
|
|
|
|
#if VOIP_CONNECTION_DEBUG
|
|
// display verbose status (metered to about once every three seconds)
|
|
if ((pConnection->uSendSeqn % 30) == 0)
|
|
{
|
|
char strSubPktSize[32];
|
|
sprintf(strSubPktSize, "size %d (bytes)", pPacket->MicrInfo.uSubPacketSize);
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) local usr %d sending pkt (seq#:%d) with %d sub-pkt(s) of %s to %a:%d | voip channels:0x%08x\n",
|
|
VOIP_ConnID(pConnectionlist, pConnection), pConnection->uSessionId[0], iUserIndex, pPacket->MicrInfo.uSeqn, pPacket->MicrInfo.uNumSubPackets,
|
|
(pPacket->MicrInfo.uSubPacketSize == 0xFF ? "variable-length" : strSubPktSize), SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port),
|
|
pConnectionlist->aChannels[iUserIndex]));
|
|
}
|
|
#endif
|
|
|
|
// send voice packet
|
|
iValidPayloadSize = _VoipPacketGetWritePtr(&pConnection->VoipMicrPacket[iUserIndex]) - &pPacket->aData[0];
|
|
iPacketSize = sizeof(pConnection->VoipMicrPacket[iUserIndex]) - sizeof(pPacket->aData) + iValidPayloadSize;
|
|
pConnection->iSendResult = _VoipSocketSendto(pConnectionlist->pSocket, uLocalClientId, &pPacket->Head,
|
|
iPacketSize, 0, (struct sockaddr *)&pConnection->SendAddr, sizeof(pConnection->SendAddr));
|
|
|
|
// update send info -- don't update uLastSend in server mode, to force pinging even when voice is being sent
|
|
if (pConnection->iVoipServerConnId == VOIP_CONNID_NONE)
|
|
{
|
|
pConnection->uLastSend = uTick;
|
|
}
|
|
|
|
// if packet contains reliable data, don't update aVoiceSendTimer to make sure we retry sending voip sub-packets soon (in case reliable data took all the place in the packet payload)
|
|
if ((pPacket->Head.uFlags & VOIP_PACKET_RELIABLE_FLAG_DATA) == 0)
|
|
{
|
|
pConnection->aVoiceSendTimer[iUserIndex] = uTick + VOIP_MSPERPACKET;
|
|
}
|
|
|
|
// if the voice packet is from a shared user set all the participating user's status
|
|
if (iUserIndex == VOIP_SHARED_USER_INDEX)
|
|
{
|
|
for (iParticipatingUserIndex = 0; iParticipatingUserIndex < VOIP_MAXLOCALUSERS; ++iParticipatingUserIndex)
|
|
{
|
|
if (pConnectionlist->aIsParticipating[iParticipatingUserIndex] == TRUE)
|
|
{
|
|
#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK)
|
|
/* For xboxone, opaque traffic is always sent as if it was originated from the
|
|
"shared user". We need to query VoipHeadset (via VoipCommon) to find out which
|
|
local user is exactly detected as talking by the MS game chat 2's chat manager
|
|
and consequently involved in generating the current outbound flow of voip packets. */
|
|
if (VoipCommonStatus((VoipCommonRefT *)VoipGetRef(), 'talk', iParticipatingUserIndex, NULL, 0))
|
|
#endif
|
|
{
|
|
pConnectionlist->uLocalUserStatus[iParticipatingUserIndex] |= VOIP_LOCAL_USER_SENDVOICE;
|
|
pConnectionlist->uLocalUserLastSend[iParticipatingUserIndex] = uTick;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pConnectionlist->uLocalUserStatus[iUserIndex] |= VOIP_LOCAL_USER_SENDVOICE;
|
|
pConnectionlist->uLocalUserLastSend[iUserIndex] = uTick;
|
|
}
|
|
|
|
// reset buffer used to build voip micr packet
|
|
ds_memclr(&pPacket->MicrInfo, sizeof(pPacket->MicrInfo)); // clear micr packet info
|
|
pPacket->Head.uFlags = 0; // clear flags in packet header
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionRecvSingle
|
|
|
|
\Description
|
|
Handle data incoming on a connection.
|
|
|
|
\Input *pConnectionlist - connectionlist connection belongs to
|
|
\Input *pConnection - pointer to connection to receive on
|
|
\Input *pSendAddr - remote address data was received from
|
|
\Input *pPacket - packet data that was received
|
|
\Input iSize - size of data received
|
|
|
|
\Version 03/21/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionRecvSingle(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, struct sockaddr_in *pSendAddr, VoipPacketBufferT *pPacket, int32_t iSize)
|
|
{
|
|
int32_t iConnID = VOIP_ConnID(pConnectionlist, pConnection);
|
|
uint32_t uSessionId = _VoipDecodeU32(pPacket->VoipPacketHead.aSessionId);
|
|
#if DIRTYCODE_LOGGING
|
|
uint32_t uClientId = _VoipDecodeU32(pPacket->VoipPacketHead.aClientId);
|
|
const char *pStrPktType;
|
|
#endif
|
|
uint32_t uTick;
|
|
uint8_t uPktType;
|
|
uint32_t uIndex;
|
|
|
|
// note time of arrival
|
|
uTick = NetTick();
|
|
|
|
// count the packet
|
|
pConnection->iRecvPackets += 1;
|
|
pConnection->iRecvData += iSize;
|
|
|
|
// did port change?
|
|
if (pConnection->SendAddr.sin_port != pSendAddr->sin_port)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] port remap #%d: %d bytes from %d instead of %d\n", iConnID,
|
|
pConnection->iChangePort++, iSize,
|
|
SocketNtohs(pSendAddr->sin_port),
|
|
SocketNtohs(pConnection->SendAddr.sin_port)));
|
|
|
|
// send future to remapped port
|
|
pConnection->SendAddr.sin_port = pSendAddr->sin_port;
|
|
|
|
// reset counter of packets received
|
|
pConnection->iRecvPackets = 0;
|
|
}
|
|
|
|
if (pPacket->VoipPacketHead.uFlags & VOIP_PACKET_STATUS_FLAG_STT)
|
|
{
|
|
#if DIRTYCODE_LOGGING
|
|
if (pConnection->bTranscribedTextRequested == FALSE)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] one or several of the remote peers on that connection are requesting transcribed text\n", iConnID));
|
|
}
|
|
#endif
|
|
pConnection->bTranscribedTextRequested = TRUE;
|
|
}
|
|
else
|
|
{
|
|
#if DIRTYCODE_LOGGING
|
|
if (pConnection->bTranscribedTextRequested != FALSE)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] remote peer(s) are no longer requesting transcribed text\n", iConnID));
|
|
}
|
|
#endif
|
|
pConnection->bTranscribedTextRequested = FALSE;
|
|
}
|
|
|
|
// identify type of incoming packet (connect, disconnect, ping, mic or unknown)
|
|
if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_ConnPacket))
|
|
{
|
|
uPktType = 'C'; // connection packet
|
|
#if DIRTYCODE_LOGGING
|
|
pStrPktType = "connect";
|
|
#endif
|
|
}
|
|
else if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_PingPacket))
|
|
{
|
|
uPktType = 'P'; // ping packet
|
|
#if DIRTYCODE_LOGGING
|
|
pStrPktType = "ping";
|
|
#endif
|
|
}
|
|
else if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_MicrPacket))
|
|
{
|
|
uPktType = 'M'; // data packet
|
|
#if DIRTYCODE_LOGGING
|
|
pStrPktType = "data";
|
|
#endif
|
|
}
|
|
else if (VOIP_SamePacketType(&pPacket->VoipPacketHead, &_Voip_DiscPacket))
|
|
{
|
|
uPktType = 'D'; // disconnect packet
|
|
#if DIRTYCODE_LOGGING
|
|
pStrPktType = "disconnect";
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] received unknown packet type 0x%02x%02x%02x\n", iConnID,
|
|
pPacket->VoipPacketHead.aType[0], pPacket->VoipPacketHead.aType[1], pPacket->VoipPacketHead.aType[2]));
|
|
return;
|
|
}
|
|
|
|
// ignore packet if session id does not match
|
|
if (_VoipConnectionMatchSessionId(pConnectionlist, iConnID, uSessionId) == FALSE)
|
|
{
|
|
#if DIRTYCODE_LOGGING
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) ignoring packet because of session IDs mismatch\n",
|
|
iConnID, uSessionId, pStrPktType));
|
|
|
|
// print set of sessions sharing this connection
|
|
_VoipConnectionPrintSessionIds(pConnectionlist, iConnID);
|
|
|
|
NetPrintMem(pPacket, iSize, "packet data");
|
|
#endif
|
|
|
|
return;
|
|
}
|
|
|
|
// handle incoming packet
|
|
switch (uPktType)
|
|
{
|
|
// connect packet
|
|
case 'C':
|
|
{
|
|
VoipConnPacketT *pConnPacket = &pPacket->VoipConnPacket;
|
|
int32_t iRemoteUserIndex;
|
|
|
|
// got a connection packet
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) received conn packet bConnected=%d eState=%d uClientId=0x%08x\n",
|
|
iConnID, uSessionId, pConnPacket->bConnected, pConnection->eState, uClientId));
|
|
|
|
// copy remote user's info
|
|
pConnection->iMaxRemoteUsers = pConnPacket->uNumLocalUsers - 1;
|
|
pConnection->iMaxRemoteUsersExt = pConnection->iMaxRemoteUsers;
|
|
|
|
for (iRemoteUserIndex = 0; iRemoteUserIndex < pConnection->iMaxRemoteUsersExt; iRemoteUserIndex++)
|
|
{
|
|
_VoipDecodeVoipUser(&pConnPacket->LocalUsers[iRemoteUserIndex], &pConnection->RemoteUsers[iRemoteUserIndex]);
|
|
}
|
|
|
|
_VoipDecodeRemoteHeadsetStatus(pConnection, &pConnPacket->Head);
|
|
|
|
// flag that we received Conn packet from remote party
|
|
pConnection->bConnPktRecv = TRUE;
|
|
|
|
// if they've received data from us, consider the connection established
|
|
if ((pConnPacket->bConnected) && (pConnection->eState == ST_CONN))
|
|
{
|
|
_VoipConnectionInit(pConnectionlist, pConnection, iConnID);
|
|
}
|
|
|
|
// if peer no more sees us as connected... go back to ST_CONN state and hope for connection to go back up
|
|
if ((!pConnPacket->bConnected) && (pConnection->eState == ST_ACTV))
|
|
{
|
|
NetPrintf(("voipconnection: [%d] voip connection lost! try to re-establish. connection back to ST_CONN state (from ST_ACTV)... \n", iConnID));
|
|
pConnection->eState = ST_CONN;
|
|
}
|
|
|
|
// update last received time
|
|
pConnection->uLastRecv = uTick;
|
|
}
|
|
break;
|
|
|
|
// ping packet
|
|
case 'P':
|
|
{
|
|
VoipPingPacketT *pPingPacket = &pPacket->VoipPingPacket;
|
|
int32_t iUserIndex;
|
|
uint32_t uChannelUserIndices;
|
|
const uint8_t *pRead = pPingPacket->aData;
|
|
|
|
#if VOIP_CONNECTION_DEBUG
|
|
char strGlobalVoipChannelConfig[80];
|
|
char strUserVoipChannelConfig[20];
|
|
ds_memclr(strGlobalVoipChannelConfig, sizeof(strGlobalVoipChannelConfig));
|
|
ds_memclr(strUserVoipChannelConfig, sizeof(strUserVoipChannelConfig));
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) received ping packet from clientId=0x%08x\n", iConnID, uSessionId, uClientId));
|
|
#endif
|
|
|
|
if (pConnection->bConnPktRecv)
|
|
{
|
|
VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef();
|
|
|
|
// if we receive a ping in connection state, that means we're connected
|
|
if (pConnection->eState == ST_CONN)
|
|
{
|
|
_VoipConnectionInit(pConnectionlist, pConnection, iConnID);
|
|
}
|
|
|
|
// update connection status flags
|
|
_VoipDecodeRemoteHeadsetStatus(pConnection, &pPingPacket->Head);
|
|
|
|
// check to see if VOIP_REMOTE_USER_RECVVOICE flag should be deactivated for other users
|
|
for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; ++uIndex)
|
|
{
|
|
if (NetTickDiff(uTick, pConnection->uUserLastRecv[uIndex]) > VOIP_PING_RATE)
|
|
{
|
|
pConnection->uRemoteUserStatus[uIndex] &= ~VOIP_REMOTE_USER_RECVVOICE;
|
|
}
|
|
}
|
|
|
|
pConnection->uRemoteConnStatus |= VOIP_CONN_BROADCONN;
|
|
pConnection->uLastRecv = uTick;
|
|
|
|
// read the bitset to figure which channel configurations need to be updated
|
|
uChannelUserIndices = _VoipDecodeU32(pPingPacket->aChannelUserIndices);
|
|
|
|
// update channel config for each remote user and remote shared user
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++)
|
|
{
|
|
if ((uChannelUserIndices >> iUserIndex) & 1)
|
|
{
|
|
pConnection->aRecvChannels[iUserIndex] = _VoipDecodeU32(pRead);
|
|
pRead += 4;
|
|
#if VOIP_CONNECTION_DEBUG
|
|
ds_snzprintf(strUserVoipChannelConfig, sizeof(strUserVoipChannelConfig), "user#%d:0x%08x ", iUserIndex, pConnection->aRecvChannels[iUserIndex]);
|
|
ds_strnzcat(strGlobalVoipChannelConfig, strUserVoipChannelConfig, sizeof(strGlobalVoipChannelConfig));
|
|
#endif
|
|
}
|
|
}
|
|
#if VOIP_CONNECTION_DEBUG
|
|
NetPrintf((" voip channels: %s\n", strGlobalVoipChannelConfig));
|
|
#endif
|
|
|
|
// process any reliable protocol components that may be included in this ping packet
|
|
_VoipReliableDataInProcess(pConnectionlist, pConnection, &pPingPacket->Head, (uint8_t *)pRead);
|
|
|
|
// update the channel
|
|
if (memcmp(&pConnection->aRecvChannels, &pConnection->aLastRecvChannels, sizeof(pConnection->aRecvChannels)))
|
|
{
|
|
VoipCommonProcessChannelChange(pVoipCommon, iConnID);
|
|
ds_memcpy_s(&pConnection->aLastRecvChannels, sizeof(pConnection->aLastRecvChannels), &pConnection->aRecvChannels, sizeof(pConnection->aRecvChannels));
|
|
VoipCommonUpdateRemoteStatus(pVoipCommon);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) ping packet from clientId=0x%08x ignored because conn packet not yet received\n",
|
|
iConnID, uSessionId, uClientId));
|
|
}
|
|
}
|
|
break;
|
|
|
|
// data packet
|
|
case 'M':
|
|
{
|
|
VoipMicrPacketT *pMicrPacket = &pPacket->VoipMicrPacket;
|
|
VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef();
|
|
|
|
#if VOIP_CONNECTION_DEBUG
|
|
if ((pConnection->iRecvPackets % 30) == 0)
|
|
{
|
|
char strSubPktSize[32];
|
|
sprintf(strSubPktSize, "size %d (bytes)", pMicrPacket->MicrInfo.uSubPacketSize);
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) received pkt (seq#:%d) with %d sub-pkt(s) of %s from remote usr idx %d | voip channels:0x%08x\n",
|
|
iConnID, uSessionId, pMicrPacket->MicrInfo.uSeqn, pMicrPacket->MicrInfo.uNumSubPackets, (pMicrPacket->MicrInfo.uSubPacketSize == 0xFF ? "variable-length" : strSubPktSize),
|
|
pMicrPacket->MicrInfo.uUserIndex, _VoipDecodeU32(pMicrPacket->MicrInfo.channels.aChannelId)));
|
|
NetPrintMem(pMicrPacket, iSize, "mic packet data");
|
|
}
|
|
#endif
|
|
|
|
if (pConnection->bConnPktRecv)
|
|
{
|
|
uint8_t *pRead;
|
|
int32_t iRemoteUserIndex;
|
|
uint32_t uLocalHeadsetStatusAggregate = 0;
|
|
|
|
// if we receive voice data in connection state, that means we're connected
|
|
if (pConnection->eState == ST_CONN)
|
|
{
|
|
_VoipConnectionInit(pConnectionlist, pConnection, iConnID);
|
|
}
|
|
|
|
// update connection status flags
|
|
_VoipDecodeRemoteHeadsetStatus(pConnection, &pMicrPacket->Head);
|
|
pConnection->uRemoteConnStatus |= (VOIP_CONN_ACTIVE|VOIP_CONN_BROADCONN);
|
|
if (pMicrPacket->MicrInfo.uUserIndex == VOIP_SHARED_REMOTE_INDEX)
|
|
{
|
|
pConnection->aRecvChannels[VOIP_SHARED_USER_INDEX] = _VoipDecodeU32(pMicrPacket->MicrInfo.channels.aChannelId);
|
|
}
|
|
else
|
|
{
|
|
pConnection->aRecvChannels[pMicrPacket->MicrInfo.uUserIndex] = _VoipDecodeU32(pMicrPacket->MicrInfo.channels.aChannelId);
|
|
}
|
|
|
|
// update the channel
|
|
if (memcmp(&pConnection->aRecvChannels, &pConnection->aLastRecvChannels, sizeof(pConnection->aRecvChannels)))
|
|
{
|
|
VoipCommonProcessChannelChange(pVoipCommon, iConnID);
|
|
ds_memcpy_s(&pConnection->aLastRecvChannels, sizeof(pConnection->aLastRecvChannels), &pConnection->aRecvChannels, sizeof(pConnection->aRecvChannels));
|
|
VoipCommonUpdateRemoteStatus(pVoipCommon);
|
|
}
|
|
|
|
pConnection->uLastRecv = uTick;
|
|
|
|
// validate and update packet sequence number
|
|
if (pMicrPacket->MicrInfo.uSeqn != (uint8_t)(pConnection->uRecvSeqn+1))
|
|
{
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) got packet seq # %d while expecting packet seq # %d\n",
|
|
iConnID, uSessionId, pMicrPacket->MicrInfo.uSeqn, (uint8_t)pConnection->uRecvSeqn+1));
|
|
}
|
|
pConnection->uRecvSeqn = pMicrPacket->MicrInfo.uSeqn;
|
|
|
|
// process any reliable protocol components that may be included in this MIC packet
|
|
pRead = _VoipReliableDataInProcess(pConnectionlist, pConnection, &pMicrPacket->Head, &pMicrPacket->aData[0]);
|
|
|
|
/* is the connection active, and are we sending to it?
|
|
xboxone: After rebasing VoipHeadsetXboxOne to MS game chat 2, per-connection muting
|
|
is handled by VoipHeadsetXboxOne (See usage of +snd, -snd, +rcv, -rcv).
|
|
But for crossplay we want the muting to be applied here just like other plaforms.
|
|
Testing has shown that apply te send mask and +snd, -snd, +rcv, -rcv has no negative effect.*/
|
|
if (pConnectionlist->uRecvMask & (1 << iConnID))
|
|
{
|
|
// if the voice packet is from a remote shared user update all the remote user's status
|
|
if (pMicrPacket->MicrInfo.uUserIndex == VOIP_SHARED_REMOTE_INDEX)
|
|
{
|
|
for (iRemoteUserIndex = 0; iRemoteUserIndex < pConnection->iMaxRemoteUsers; ++iRemoteUserIndex)
|
|
{
|
|
VoipUserT *pVoipUser = (VoipUserT *)&pConnection->RemoteUsers[iRemoteUserIndex];
|
|
|
|
if (!VOIP_NullUser(pVoipUser))
|
|
{
|
|
#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK)
|
|
/* For xboxone, opaque traffic is always received as if it was originated from the
|
|
"shared user". We need to query VoipHeadset (via VoipCommon) to find out which
|
|
remote user is exactly detected as talking by the MS game chat 2's chat manager
|
|
and consequently involved in generating the current inbound flow of voip packets. */
|
|
if (VoipCommonStatus((VoipCommonRefT *)VoipGetRef(), 'talk', 0, pVoipUser, sizeof(*pVoipUser)))
|
|
#endif
|
|
{
|
|
pConnection->uRemoteUserStatus[iRemoteUserIndex] |= VOIP_REMOTE_USER_RECVVOICE;
|
|
pConnection->uUserLastRecv[iRemoteUserIndex] = uTick;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pConnection->uUserLastRecv[pMicrPacket->MicrInfo.uUserIndex] = uTick;
|
|
pConnection->uRemoteUserStatus[pMicrPacket->MicrInfo.uUserIndex] |= VOIP_REMOTE_USER_RECVVOICE;
|
|
}
|
|
|
|
// if there is at least one headset capable of outputting voice data, forward the packet along, ignore otherwise
|
|
// we do this to ignore spamming on PC (ticket GOS-30000)
|
|
for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; ++uIndex)
|
|
{
|
|
uLocalHeadsetStatusAggregate |= pVoipCommon->Connectionlist.uLocalUserStatus[uIndex];
|
|
}
|
|
|
|
if (uLocalHeadsetStatusAggregate & VOIP_LOCAL_USER_OUTPUTDEVICEOK)
|
|
{
|
|
pConnectionlist->pVoiceCb((VoipUserT *)&pConnection->RemoteUsers[0], pConnection->iMaxRemoteUsers, iConnID, &pMicrPacket->MicrInfo, pRead, pConnectionlist->pVoiceUserData);
|
|
}
|
|
}
|
|
|
|
// check to see if VOIP_REMOTE_USER_RECVVOICE flag should be deactivated for other users
|
|
for (uIndex = 0; uIndex < VOIP_MAXLOCALUSERS; ++uIndex)
|
|
{
|
|
if (NetTickDiff(uTick, pConnection->uUserLastRecv[uIndex]) > VOIP_PING_RATE)
|
|
{
|
|
pConnection->uRemoteUserStatus[uIndex] &= ~VOIP_REMOTE_USER_RECVVOICE;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) voip data packet from clientId=0x%08x ignored because conn packet not yet received\n",
|
|
iConnID, uSessionId, uClientId));
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 'D':
|
|
{
|
|
VoipDiscPacketT *pDiscPacket = &pPacket->VoipDiscPacket;
|
|
uint32_t uRemoteClientId = _VoipDecodeU32(pDiscPacket->aRemoteClientId);
|
|
uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId;
|
|
if (uRemoteClientId == uLocalClientId)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) remote peer 0x%08x has disconnected from 0x%08x\n",
|
|
iConnID, uSessionId, uClientId, uRemoteClientId));
|
|
|
|
// In this specific case, we know that remote peer is already in 'disconnected' state.
|
|
// No need to send back a DISC msg. Therefore, set bSendDiscMsg parameter to FALSE.
|
|
_VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnID], iConnID, FALSE);
|
|
}
|
|
else
|
|
{
|
|
// this should only occurs with multiple machines on the same PC and voip being routed to the incorrect client
|
|
NetPrintf(("voipconnection: [%d] (sess id 0x%08x) remote peer 0x%08x has disconnected from 0x%08x, but we are 0x%08x\n",
|
|
iConnID, uSessionId, uClientId, uRemoteClientId, pConnectionlist->uClientId));
|
|
}
|
|
}
|
|
break;
|
|
|
|
default:
|
|
{
|
|
NetPrintf(("voipconnection: [%d] critical error! this path can't be taken in _VoipConnectionRecvSing()\n"));
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionUpdateSingle
|
|
|
|
\Description
|
|
Update a virtual connection.
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input *pConnection - pointer to connection to update
|
|
\Input iConnId - index of connection to update
|
|
\Input uTick - current tick count
|
|
|
|
\Output
|
|
uint32_t - nonzero=receiving voice, zero=not receiving voice
|
|
|
|
\Version 03/20/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static uint32_t _VoipConnectionUpdateSingle(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnId, uint32_t uTick)
|
|
{
|
|
int32_t iTimeout;
|
|
int32_t iUserIndex;
|
|
uint8_t bIsReceivingVoice = FALSE;
|
|
|
|
// make sure it's a valid connection
|
|
if (pConnection->eState == ST_DISC)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// see if we need to send a conn or ping packet
|
|
if ((uTick - pConnection->uLastSend) > VOIP_PING_RATE)
|
|
{
|
|
// if we're connecting, send connection packet
|
|
if (pConnection->eState == ST_CONN)
|
|
{
|
|
_VoipConnectionConn(pConnectionlist, pConnection, uTick);
|
|
}
|
|
else
|
|
{
|
|
_VoipConnectionPing(pConnectionlist, pConnection, uTick);
|
|
}
|
|
}
|
|
|
|
// see if we need to send any buffered voice data
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++)
|
|
{
|
|
_VoipConnectionSendSingle(pConnectionlist, pConnection, iUserIndex, uTick, FALSE);
|
|
}
|
|
|
|
// see if we need to time out the connection
|
|
iTimeout = (pConnection->eState == ST_ACTV) ? pConnectionlist->iDataTimeout : VOIP_CONNTIMEOUT;
|
|
if (NetTickDiff(uTick, pConnection->uLastRecv) > iTimeout)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] timing out connection due to inactivity\n", iConnId));
|
|
_VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnId], iConnId, TRUE);
|
|
}
|
|
|
|
// return if we are receiving voice or not
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; iUserIndex++)
|
|
{
|
|
if (pConnection->uRemoteUserStatus[iUserIndex] & VOIP_REMOTE_USER_RECVVOICE)
|
|
{
|
|
bIsReceivingVoice = TRUE;
|
|
break;
|
|
}
|
|
}
|
|
return((bIsReceivingVoice == TRUE) ? (1 << iConnId) : 0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionRecv
|
|
|
|
\Description
|
|
Receive data and forward it to the appropriate connection.
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
|
|
\Output
|
|
int32_t - amount of data received
|
|
|
|
\Version 03/21/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipConnectionRecv(VoipConnectionlistT *pConnectionlist)
|
|
{
|
|
struct sockaddr_in RecvAddr;
|
|
int32_t iAddrLen, iRecvSize;
|
|
|
|
VoipPacketBufferT RecvPacket;
|
|
|
|
// no socket? no way to receive.
|
|
if (pConnectionlist->pSocket == NULL)
|
|
{
|
|
return(0);
|
|
}
|
|
|
|
// try and receive from a peer
|
|
if ((iRecvSize = SocketRecvfrom(pConnectionlist->pSocket, (char *)&RecvPacket, sizeof(RecvPacket), 0, (struct sockaddr *)&RecvAddr, (iAddrLen=sizeof(RecvAddr),&iAddrLen))) > 0)
|
|
{
|
|
VoipConnectionT *pConnection;
|
|
int32_t iConnID;
|
|
|
|
// extract client identifier
|
|
uint32_t uClientId = _VoipDecodeU32(RecvPacket.VoipPacketHead.aClientId);
|
|
|
|
// find the matching connection
|
|
for (iConnID = 0, pConnection = NULL; iConnID < pConnectionlist->iMaxConnections; iConnID++)
|
|
{
|
|
pConnection = &pConnectionlist->pConnections[iConnID];
|
|
|
|
// if the clientID matches, this is the connection
|
|
if (uClientId == pConnection->uRemoteClientId)
|
|
{
|
|
break;
|
|
}
|
|
}
|
|
|
|
// if we found a matching connection
|
|
if (iConnID < pConnectionlist->iMaxConnections)
|
|
{
|
|
// give the data to the connection
|
|
_VoipConnectionRecvSingle(pConnectionlist, pConnection, &RecvAddr, &RecvPacket, iRecvSize);
|
|
}
|
|
else if (!VOIP_SamePacketType(&RecvPacket.VoipPacketHead, &_Voip_ConnPacket) && !VOIP_SamePacketType(&RecvPacket.VoipPacketHead, &_Voip_DiscPacket))
|
|
{
|
|
NetPrintf(("voipconnection: ignoring 0x%02x%02x%02x packet from address %a:%d clientId=0x%08x\n",
|
|
RecvPacket.VoipPacketHead.aType[0], RecvPacket.VoipPacketHead.aType[1], RecvPacket.VoipPacketHead.aType[2],
|
|
SocketNtohl(RecvAddr.sin_addr.s_addr), SocketNtohs(RecvAddr.sin_port),
|
|
uClientId));
|
|
NetPrintMem(&RecvPacket, iRecvSize, "packet data");
|
|
}
|
|
}
|
|
|
|
// return data length
|
|
return(iRecvSize);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionRecvCallback
|
|
|
|
\Description
|
|
Voip socket recv event callback.
|
|
|
|
\Input *pSocket - voip socket
|
|
\Input iFlags - unused
|
|
\Input *pRef - connectionlist ref
|
|
|
|
\Output
|
|
int32_t - zero
|
|
|
|
\Version 12/16/2005 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static int32_t _VoipConnectionRecvCallback(SocketT *pSocket, int32_t iFlags, void *pRef)
|
|
{
|
|
VoipConnectionlistT *pConnectionlist = (VoipConnectionlistT *)pRef;
|
|
|
|
// see if we have exclusive access
|
|
if (NetCritTry(&pConnectionlist->NetCrit))
|
|
{
|
|
// try and receive data
|
|
_VoipConnectionRecv(pConnectionlist);
|
|
|
|
// free access
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: _VoipConnectionRecvCallback() could not acquire connectionlist critical section\n"));
|
|
}
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionTrySocketClose
|
|
|
|
\Description
|
|
Check if the voip socket can be closed. The close will occur when all the
|
|
connections are closed. Moved from the voip loop, to also be done immediately
|
|
upon connection stop.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
|
|
\Output
|
|
int32_t - zero
|
|
|
|
\Version 06/18/2009 (jrainy)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionTrySocketClose(VoipConnectionlistT *pConnectionlist)
|
|
{
|
|
int32_t iConnId, iNumConnections;
|
|
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
// count number of active connections
|
|
for (iConnId = 0, iNumConnections = 0; iConnId < pConnectionlist->iMaxConnections; iConnId++)
|
|
{
|
|
if (pConnectionlist->pConnections[iConnId].eState != ST_DISC)
|
|
{
|
|
iNumConnections++;
|
|
}
|
|
}
|
|
|
|
// if no connections left, kill socket
|
|
if ((iNumConnections == 0) && (pConnectionlist->pSocket != NULL))
|
|
{
|
|
NetPrintf(("voipconnection: closing socket in _VoipConnectionTrySocketClose()\n"));
|
|
SocketClose(pConnectionlist->pSocket);
|
|
pConnectionlist->pSocket = NULL;
|
|
pConnectionlist->uBindPort = 0;
|
|
}
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipConnectionStop
|
|
|
|
\Description
|
|
Stop a connection.
|
|
|
|
\Input *pConnectionlist - connectionlist ref
|
|
\Input *pConnection - connection to stop
|
|
\Input iConnID - index of connection to stop, or VOIP_CONNID_ALL to stop all connections
|
|
\Input bSendDiscMsg - TRUE - send DISC msg to peer; FALSE - do not send DISC msg to peer
|
|
|
|
\Notes
|
|
Loss of the single unreliable disconnect packet sent in this function
|
|
is not critical, as the connection manager will respond to unexpected
|
|
packets that are not connect/disconnect packets (ie pings and voice
|
|
packets) with disconnection requests.
|
|
|
|
\Version 05/10/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipConnectionStop(VoipConnectionlistT *pConnectionlist, VoipConnectionT *pConnection, int32_t iConnID, int32_t bSendDiscMsg)
|
|
{
|
|
VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef();
|
|
int32_t i = 0;
|
|
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
// make sure we're not already disconnected
|
|
if (pConnection->eState == ST_DISC)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] disconnection attempt canceled because connection already in state ST_DISC!\n", iConnID));
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
return;
|
|
}
|
|
|
|
if (bSendDiscMsg)
|
|
{
|
|
// send a disconnection packet
|
|
uint32_t uLocalClientId = pConnection->bIsLocalClientIdValid ? pConnection->uLocalClientId : pConnectionlist->uClientId;
|
|
_VoipConnectionDisc(iConnID, uLocalClientId, pConnection->uRemoteClientId, pConnection->uSessionId[0], &pConnection->SendAddr, pConnectionlist->pSocket);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: [%d] no need to send disconnect message to %a:%d\n", iConnID,
|
|
SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port)));
|
|
}
|
|
|
|
// set to disconnected before unregister
|
|
pConnection->eState = ST_DISC;
|
|
pConnection->bConnPktRecv = FALSE;
|
|
|
|
// subtract connection identifier from send/recv masks
|
|
VoipConnectionSetSendMask(pConnectionlist, pConnectionlist->uSendMask & ~(1 << iConnID));
|
|
VoipConnectionSetRecvMask(pConnectionlist, pConnectionlist->uRecvMask & ~(1 << iConnID));
|
|
|
|
// unregister all the remote users on this connection with voipheadset
|
|
VoipConnectionRegisterRemoteTalkers(pConnectionlist, iConnID, FALSE);
|
|
|
|
// clear outbound reliable queue
|
|
while (pConnection->pOutboundReliableDataQueue != NULL)
|
|
{
|
|
_VoipConnectionReliableDataDequeue(pConnectionlist, iConnID);
|
|
}
|
|
|
|
// clear connection and mark as disconnected
|
|
ds_memclr(pConnection, sizeof(*pConnection));
|
|
pConnection->uRemoteConnStatus = VOIP_CONN_STOPPED;
|
|
pConnection->iVoipServerConnId = VOIP_CONNID_NONE;
|
|
for ( i = 0; i < VOIP_MAXLOCALUSERS; ++i)
|
|
{
|
|
pConnectionlist->uLocalUserStatus[i] &= ~VOIP_LOCAL_USER_SENDVOICE;
|
|
pConnectionlist->uLocalUserLastSend[i] = 0;
|
|
}
|
|
|
|
// clear channel config maintained by VoipCommon
|
|
ds_memclr(&pVoipCommon->uRemoteChannelSelection[iConnID], sizeof(pVoipCommon->uRemoteChannelSelection[iConnID]));
|
|
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
}
|
|
|
|
|
|
/*** Public functions *************************************************************/
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionStartup
|
|
|
|
\Description
|
|
Startup a connectionlist.
|
|
|
|
\Input *pConnectionlist - connectionlist to init
|
|
\Input iMaxPeers - max number of connections to support
|
|
|
|
\Output
|
|
int32_t - zero=success, negative=error
|
|
|
|
\Version 03/20/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipConnectionStartup(VoipConnectionlistT *pConnectionlist, int32_t iMaxPeers)
|
|
{
|
|
int32_t iConnId;
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserData;
|
|
|
|
//Check to see if Max Peer has exceeded VOIP_MAXCONNECT
|
|
if (iMaxPeers > VOIP_MAXCONNECT)
|
|
{
|
|
NetPrintf(("voipconnection: error, max peer exceeded VOIP_MAXCONNECT.\n"));
|
|
return(-2);
|
|
}
|
|
|
|
// Query current mem group data
|
|
VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData);
|
|
|
|
// allocate and init connectionlist
|
|
if ((pConnectionlist->pConnections = (VoipConnectionT *)DirtyMemAlloc(sizeof(VoipConnectionT) * iMaxPeers, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL)
|
|
{
|
|
NetPrintf(("voipconnection: error, unable to allocate connectionlist\n"));
|
|
return(-1);
|
|
}
|
|
ds_memclr(pConnectionlist->pConnections, sizeof(VoipConnectionT) * iMaxPeers);
|
|
|
|
pConnectionlist->iFriendConnId = VOIP_CONNID_NONE;
|
|
|
|
// reset local-to-server conn id mappings
|
|
for (iConnId = 0; iConnId < iMaxPeers; iConnId++)
|
|
{
|
|
pConnectionlist->pConnections[iConnId].iVoipServerConnId = VOIP_CONNID_NONE;
|
|
}
|
|
|
|
// init critical section
|
|
NetCritInit(&pConnectionlist->NetCrit, "voipconnection");
|
|
|
|
// set default timeout
|
|
pConnectionlist->iDataTimeout = VOIP_TIMEOUT;
|
|
|
|
// set max peers
|
|
pConnectionlist->iMaxConnections = iMaxPeers;
|
|
|
|
NetPrintf(("voipconnection: max connections = %d max reliable data size = %d max ping payload size = %d\n",
|
|
pConnectionlist->iMaxConnections, VOIP_MAXRELIABLEDATA, VOIP_MAXPINGPKTSIZE));
|
|
|
|
return(0);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionSetCallbacks
|
|
|
|
\Description
|
|
Set required recv voice and reg user callbacks.
|
|
|
|
\Input *pConnectionlist - connectionlist to register callbacks for
|
|
\Input *pVoiceCb - voice callback to register
|
|
\Input *pVoiceUserData - user data to be passed to the voice callback
|
|
\Input *pTextCb - text callback to register
|
|
\Input *pTextUserData - user data to be passed to the text callback
|
|
\Input *pRegUserCb - user reg callback to register
|
|
\Input *pRegUserUserData - user data to be passed to the reguser callback
|
|
\Input *pOpaqueCb - opaque data callback to register
|
|
\Input *pOpaqueUserData - user data to be passed to the opaquedata callback
|
|
|
|
\Version 03/20/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionSetCallbacks(VoipConnectionlistT *pConnectionlist, VoipConnectRecvVoiceCbT *pVoiceCb, void *pVoiceUserData,
|
|
VoipConnectRecvTextCbT *pTextCb, void *pTextUserData,
|
|
VoipConnectRegUserCbT *pRegUserCb, void *pRegUserUserData,
|
|
VoipConnectRecvOpaqueCbT *pOpaqueCb, void *pOpaqueUserData)
|
|
{
|
|
// save callbacks and callback user data
|
|
pConnectionlist->pRegUserCb = pRegUserCb;
|
|
pConnectionlist->pRegUserUserData = pRegUserUserData;
|
|
pConnectionlist->pVoiceCb = pVoiceCb;
|
|
pConnectionlist->pVoiceUserData = pVoiceUserData;
|
|
pConnectionlist->pTextCb = pTextCb;
|
|
pConnectionlist->pTextUserData = pTextUserData;
|
|
pConnectionlist->pOpaqueCb = pOpaqueCb;
|
|
pConnectionlist->pOpaqueUserData = pOpaqueUserData;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionSetTextCallback
|
|
|
|
\Description
|
|
Set required text callbacks for X1 crossplay asscessibility
|
|
Should be set to null for native mode
|
|
|
|
\Input *pConnectionlist - connectionlist to register callbacks for
|
|
\Input *pTextCb - text callback to register
|
|
\Input *pTextUserData - user data to be passed to the text callback
|
|
|
|
\Version 04/10/2019 (tcho)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionSetTextCallback(VoipConnectionlistT *pConnectionlist, VoipConnectRecvTextCbT *pTextCb, void *pTextUserData)
|
|
{
|
|
// save callbacks and callback user data
|
|
pConnectionlist->pTextCb = pTextCb;
|
|
pConnectionlist->pTextUserData = pTextUserData;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionShutdown
|
|
|
|
\Description
|
|
Shutdown a connectionlist
|
|
|
|
\Input *pConnectionlist - connectionlist to close
|
|
|
|
\Version 03/20/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionShutdown(VoipConnectionlistT *pConnectionlist)
|
|
{
|
|
int32_t iMemGroup;
|
|
void *pMemGroupUserData;
|
|
LinkedReliableDataT *pEntry;
|
|
|
|
// query current mem group data
|
|
VoipCommonMemGroupQuery(&iMemGroup, &pMemGroupUserData);
|
|
|
|
// clear free pool of reliable data entries
|
|
while ((pEntry = _VoipGetReliableDataBufferFromFreePool(pConnectionlist, FALSE)) != NULL)
|
|
{
|
|
DirtyMemFree(pEntry, VOIP_MEMID, iMemGroup, pMemGroupUserData);
|
|
}
|
|
|
|
// dispose of connectionlist
|
|
DirtyMemFree(pConnectionlist->pConnections, VOIP_MEMID, iMemGroup, pMemGroupUserData);
|
|
|
|
// dispose of socket
|
|
if (pConnectionlist->pSocket)
|
|
{
|
|
NetPrintf(("voipconnection: closing socket in VoipConnectionShutdown()\n"));
|
|
SocketClose(pConnectionlist->pSocket);
|
|
}
|
|
|
|
// release critical section
|
|
NetCritKill(&pConnectionlist->NetCrit);
|
|
|
|
// clear connectionlist
|
|
ds_memclr(pConnectionlist, sizeof(*pConnectionlist));
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionCanAllocate
|
|
|
|
\Description
|
|
Check whether a given Connection ID can be allocated in this Connection List
|
|
|
|
\Input *pConnectionlist - connection list ref
|
|
\Input iConnID - connection index
|
|
|
|
\Output
|
|
uint8_t - Whether a connection can be allocated with given ConnID
|
|
|
|
\Version 02/19/2008 (jrainy)
|
|
*/
|
|
/********************************************************************************F*/
|
|
uint8_t VoipConnectionCanAllocate(VoipConnectionlistT *pConnectionlist, int32_t iConnID)
|
|
{
|
|
if ((iConnID < 0) || (iConnID >= pConnectionlist->iMaxConnections))
|
|
{
|
|
return(FALSE);
|
|
}
|
|
|
|
return(pConnectionlist->pConnections[iConnID].eState == ST_DISC);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionStart
|
|
|
|
\Description
|
|
Start a connection to a peer.
|
|
|
|
\Input *pConnectionlist - connection list ref
|
|
\Input iConnID - connection index
|
|
\Input uAddr - address to connect to
|
|
\Input uConnPort - connection port
|
|
\Input uBindPort - local bind port
|
|
\Input uClientId - id of client to connect to
|
|
\Input uSessionId - session id (cannot be 0)
|
|
|
|
\Output
|
|
int32_t - connection identifier on success, negative=failure
|
|
|
|
\Version 03/18/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipConnectionStart(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint32_t uAddr, uint32_t uConnPort, uint32_t uBindPort, uint32_t uClientId, uint32_t uSessionId)
|
|
{
|
|
VoipConnectionT *pConnection;
|
|
struct sockaddr BindAddr;
|
|
int32_t iUserIndex;
|
|
|
|
// sanity check: make sure local and remote clientIDs are different
|
|
if (pConnectionlist->uClientId == uClientId)
|
|
{
|
|
NetPrintf(("voipconnection: local client id (%d) and remote client id (%d) can't be same\n", pConnectionlist->uClientId, uClientId));
|
|
return(-1);
|
|
}
|
|
|
|
// if we haven't allocated a socket yet, do so now
|
|
if (pConnectionlist->pSocket == NULL)
|
|
{
|
|
int32_t iResult;
|
|
|
|
// open the socket
|
|
if ((pConnectionlist->pSocket = SocketOpen(AF_INET, SOCK_DGRAM, VOIP_IPPROTO)) == NULL)
|
|
{
|
|
NetPrintf(("voipconnection: error creating socket\n"));
|
|
return(-2);
|
|
}
|
|
|
|
// bind the socket
|
|
SockaddrInit(&BindAddr, AF_INET);
|
|
SockaddrInSetPort(&BindAddr, uBindPort);
|
|
if ((iResult = SocketBind(pConnectionlist->pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE)
|
|
{
|
|
NetPrintf(("voipconnection: error %d binding socket to port %d, trying random\n", iResult, uBindPort));
|
|
SockaddrInSetPort(&BindAddr, 0);
|
|
if ((iResult = SocketBind(pConnectionlist->pSocket, &BindAddr, sizeof(BindAddr))) != SOCKERR_NONE)
|
|
{
|
|
NetPrintf(("voipconnection: error %d binding socket\n", iResult));
|
|
SocketClose(pConnectionlist->pSocket);
|
|
pConnectionlist->pSocket = NULL;
|
|
return(-3);
|
|
}
|
|
}
|
|
|
|
// retrieve bound port
|
|
SocketInfo(pConnectionlist->pSocket, 'bind', 0, &BindAddr, sizeof(BindAddr));
|
|
uBindPort = SockaddrInGetPort(&BindAddr);
|
|
NetPrintf(("voipconnection: bound socket to port %d\n", uBindPort));
|
|
|
|
// save local port
|
|
pConnectionlist->uBindPort = uBindPort;
|
|
|
|
// setup for socket events
|
|
SocketCallback(pConnectionlist->pSocket, CALLB_RECV, 5000, pConnectionlist, &_VoipConnectionRecvCallback);
|
|
}
|
|
|
|
// make sure bind matches our previous bind
|
|
if (uBindPort != pConnectionlist->uBindPort)
|
|
{
|
|
NetPrintf(("voipconnection: warning, only one global bind port is currently supported, using previously specified port\n"));
|
|
}
|
|
|
|
// convert address and ports to network form
|
|
uAddr = SocketHtonl(uAddr);
|
|
uConnPort = SocketHtons(uConnPort);
|
|
|
|
/*
|
|
Guarding the following code with NetCrit proved to be required to protect against
|
|
_VoipConnectionStop() being invoked from the socket async recv thread upon reception
|
|
of a DISC packet (typically belonging to a previous session) on that connection.
|
|
|
|
We did not include the above portion of this function within this critical
|
|
usage function, because atomic access to it is guaranteed by the threadcrit
|
|
being lock externally.
|
|
*/
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
// allocate a connection
|
|
if ((pConnection = _VoipConnectionAllocate(pConnectionlist, iConnID)) == NULL)
|
|
{
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
|
|
NetPrintf(("voipconnection: alloc failed\n"));
|
|
return(-4);
|
|
}
|
|
|
|
iConnID = VOIP_ConnID(pConnectionlist, pConnection);
|
|
|
|
// init the connection
|
|
ds_memclr(pConnection, sizeof(*pConnection));
|
|
pConnection->iVoipServerConnId = VOIP_CONNID_NONE;
|
|
pConnection->SendAddr.sin_family = AF_INET;
|
|
pConnection->SendAddr.sin_addr.s_addr = uAddr;
|
|
pConnection->SendAddr.sin_port = uConnPort;
|
|
pConnection->uRemoteClientId = uClientId;
|
|
|
|
// sequence number for reliable data ranges from 1 to 127
|
|
pConnection->uOutSeqNb = 1; // use 1 as the first outbound sequence number
|
|
pConnection->uInSeqNb = 0; // seq nb of last received inbound message. validity range : [1,127]. 0 means no inbound traffic received yet
|
|
|
|
// add specified session ID to the set of sessions sharing this connection
|
|
VoipConnectionAddSessionId(pConnectionlist, iConnID, uSessionId);
|
|
|
|
pConnection->uRecvSeqn = 0xFFFFFFFF; // intialize last receive sequence number with -1.
|
|
pConnection->uRemoteConnStatus = 0;
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS; ++iUserIndex)
|
|
{
|
|
pConnection->uRemoteUserStatus[iUserIndex] = 0;
|
|
}
|
|
pConnection->eState = ST_CONN;
|
|
pConnection->uLastRecv = NetTick();
|
|
|
|
// set up to transmit mic data
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++)
|
|
{
|
|
ds_memcpy_s(&pConnection->VoipMicrPacket[iUserIndex].Head, sizeof(pConnection->VoipMicrPacket[iUserIndex].Head), &_Voip_MicrPacket, sizeof(_Voip_MicrPacket));
|
|
}
|
|
|
|
// output connect message
|
|
NetPrintf(("voipconnection: [%d] connecting to %a:%d localId=0x%08x remoteId=0x%08x\n", iConnID,
|
|
SocketNtohl(pConnection->SendAddr.sin_addr.s_addr), SocketNtohs(pConnection->SendAddr.sin_port),
|
|
pConnection->uLocalClientId, pConnection->uRemoteClientId));
|
|
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
|
|
// return connection ID to caller
|
|
return(iConnID);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionUpdate
|
|
|
|
\Description
|
|
Update all connections
|
|
|
|
\Input *pConnectionlist - connection list
|
|
|
|
\Version 03/18/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionUpdate(VoipConnectionlistT *pConnectionlist)
|
|
{
|
|
int32_t iConnId;
|
|
uint32_t uTick;
|
|
|
|
// get sole access
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
// receive any incoming data
|
|
while(_VoipConnectionRecv(pConnectionlist) > 0)
|
|
;
|
|
|
|
_VoipConnectionTrySocketClose(pConnectionlist);
|
|
|
|
// relinquish sole access
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
|
|
// update connection status for all connections, keeping track of who we are receiving voice from
|
|
for (iConnId = 0, uTick = NetTick(), pConnectionlist->uRecvVoice = 0; iConnId < pConnectionlist->iMaxConnections; iConnId++)
|
|
{
|
|
pConnectionlist->uRecvVoice |= _VoipConnectionUpdateSingle(pConnectionlist, &pConnectionlist->pConnections[iConnId], iConnId, uTick);
|
|
}
|
|
|
|
#if !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK)
|
|
/* check if we need to time out talking status
|
|
on xbox one this is handled in the voipxbxone _VoipUpdateLocalStatus()
|
|
*/
|
|
for (int32_t i = 0; i < VOIP_MAXLOCALUSERS; ++i)
|
|
{
|
|
if ((pConnectionlist->uLocalUserStatus[i] & VOIP_LOCAL_USER_TALKING) && (NetTickDiff(uTick, pConnectionlist->uLastVoiceTime[i]) > VOIP_TALKTIMEOUT))
|
|
{
|
|
pConnectionlist->uLocalUserStatus[i] &= ~VOIP_LOCAL_USER_TALKING;
|
|
}
|
|
}
|
|
#endif
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionStop
|
|
|
|
\Description
|
|
Stop a connection with a peer.
|
|
|
|
\Input *pConnectionlist - connection list ref
|
|
\Input iConnID - connection to stop, or VOIP_CONNID_ALL to stop all connections
|
|
\Input bSendDiscMsg - TRUE - send DISC msg to peer; FALSE - do not send DISC msg to peer
|
|
|
|
\Version 03/18/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionStop(VoipConnectionlistT *pConnectionlist, int32_t iConnID, int32_t bSendDiscMsg)
|
|
{
|
|
if (iConnID == VOIP_CONNID_ALL)
|
|
{
|
|
// disconnect from all current connections
|
|
for (iConnID = 0; iConnID < pConnectionlist->iMaxConnections; iConnID++)
|
|
{
|
|
_VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnID], iConnID, bSendDiscMsg);
|
|
}
|
|
}
|
|
else if ((iConnID >= 0) && (iConnID < pConnectionlist->iMaxConnections))
|
|
{
|
|
// disconnect from the given connection
|
|
_VoipConnectionStop(pConnectionlist, &pConnectionlist->pConnections[iConnID], iConnID, bSendDiscMsg);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipconnection: disconnect with iConnID=%d is invalid\n", iConnID));
|
|
}
|
|
|
|
_VoipConnectionTrySocketClose(pConnectionlist);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionSend
|
|
|
|
\Description
|
|
Send data to peer.
|
|
|
|
\Input *pConnectionlist - connectionlist to send to
|
|
\Input uSendMask - mask of connections to send to
|
|
\Input *pVoiceData - pointer to data to send
|
|
\Input iDataSize - size of data to send
|
|
\Input *pMetaData - pointer to metadata to be added to voip packet
|
|
\Input iMetaDataSize - size of metadata
|
|
\Input uUserIndex - local user index
|
|
\Input uSendSeqn - seq nb
|
|
|
|
\Version 03/17/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionSend(VoipConnectionlistT *pConnectionlist, uint32_t uSendMask, const uint8_t *pVoiceData, int32_t iDataSize, const uint8_t *pMetaData, int32_t iMetaDataSize, uint32_t uUserIndex, uint8_t uSendSeqn)
|
|
{
|
|
uint32_t uCurTick = NetTick();
|
|
int32_t iConnID;
|
|
int32_t iParticipatingUserIndex;
|
|
uint8_t bSentToVoipServer = FALSE;
|
|
|
|
// early exit if metadata is too big
|
|
if ((pMetaData != NULL) && (iMetaDataSize > 256))
|
|
{
|
|
NetPrintf(("voipconnection: critical error! metadata is too big\n"));
|
|
return;
|
|
}
|
|
|
|
// early exit if the user index is invalid
|
|
if (uUserIndex >= VOIP_MAXLOCALUSERS_EXTENDED)
|
|
{
|
|
return;
|
|
}
|
|
|
|
// if we are dealing with the remote user update all the user's last voice receive time
|
|
if (uUserIndex == VOIP_SHARED_USER_INDEX)
|
|
{
|
|
for (iParticipatingUserIndex = 0; iParticipatingUserIndex < VOIP_MAXLOCALUSERS; ++iParticipatingUserIndex)
|
|
{
|
|
if (pConnectionlist->aIsParticipating[iParticipatingUserIndex] == TRUE)
|
|
{
|
|
pConnectionlist->uLastVoiceTime[iParticipatingUserIndex] = uCurTick;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pConnectionlist->uLastVoiceTime[uUserIndex] = uCurTick;
|
|
}
|
|
|
|
// loop through all connections
|
|
for (iConnID = 0; iConnID < pConnectionlist->iMaxConnections; iConnID++)
|
|
{
|
|
// ref the connection
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID];
|
|
|
|
// skip connections that are not set in the sendmask parameter
|
|
if ((uSendMask & (1 << iConnID)) == 0)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
// for connections via voip server, send only to the first connection
|
|
// todo: amakoukji, for CCS phase 2 we will need to remember what voip server we already sent the packet to and skip
|
|
if ((pConnection->iVoipServerConnId != VOIP_CONNID_NONE) && (bSentToVoipServer != FALSE))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
/* is the connection active, and are we sending to it?
|
|
xboxone: After rebasing VoipHeadsetXboxOne to MS game chat 2, per-connection muting
|
|
is handled by VoipHeadsetXboxOne (See usage of +snd, -snd, +rcv, -rcv).
|
|
But for crossplay we want the muting to be applied here just like other plaforms.
|
|
Testing has shown that apply te send mask and +snd, -snd, +rcv, -rcv has no negative effect.*/
|
|
|
|
if ((pConnection->eState == ST_ACTV) && (pConnectionlist->uSendMask & (1 << iConnID)))
|
|
{
|
|
uint8_t *pWrite; // write position in voip packet
|
|
VoipMicrPacketT *pMicrPacket = &pConnection->VoipMicrPacket[uUserIndex];
|
|
uint8_t bVariableSubPktLen;
|
|
int32_t iResult;
|
|
|
|
// set talking flag
|
|
// if the voice packet is from a shared user set all the participating user status to be talking
|
|
if (uUserIndex == VOIP_SHARED_USER_INDEX)
|
|
{
|
|
for (iParticipatingUserIndex = 0; iParticipatingUserIndex < VOIP_MAXLOCALUSERS; ++iParticipatingUserIndex)
|
|
{
|
|
if (pConnectionlist->aIsParticipating[iParticipatingUserIndex] == TRUE)
|
|
{
|
|
pConnectionlist->uLocalUserStatus[iParticipatingUserIndex] |= VOIP_LOCAL_USER_TALKING;
|
|
}
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pConnectionlist->uLocalUserStatus[uUserIndex] |= VOIP_LOCAL_USER_TALKING;
|
|
}
|
|
|
|
// record maximum outgoing sub-pkt size on this connection
|
|
if (iDataSize > pConnection->iMaxSubPktSize)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] max recorded outbound sub-pkt size increased from %d to %d\n", iConnID, pConnection->iMaxSubPktSize, iDataSize));
|
|
pConnection->iMaxSubPktSize = iDataSize;
|
|
}
|
|
|
|
// is new metadata different than metadata already in packet
|
|
if ((pMicrPacket->MicrInfo.uNumSubPackets != 0) && (pMetaData != NULL))
|
|
{
|
|
uint8_t bDifferentMetaData = FALSE;
|
|
uint8_t *pPackedMetaData = _VoipPacketGetMetaDataPtr(pMicrPacket);
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
if (!(pMicrPacket->Head.uFlags & VOIP_PACKET_STATUS_FLAG_METADATA))
|
|
{
|
|
NetPrintf(("voipconnection: [%d] critical error - metadata expected in packet but missing\n", iConnID));
|
|
}
|
|
#endif
|
|
|
|
if (iMetaDataSize == *pPackedMetaData)
|
|
{
|
|
if (memcmp(pMetaData, (pPackedMetaData+1), iMetaDataSize) != 0)
|
|
{
|
|
bDifferentMetaData = TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
bDifferentMetaData = TRUE;
|
|
}
|
|
|
|
if (bDifferentMetaData)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] flushing pending voip packet because new sub-packet has different metadata\n", iConnID));
|
|
_VoipConnectionSendSingle(pConnectionlist, pConnection, uUserIndex, uCurTick, TRUE);
|
|
}
|
|
}
|
|
|
|
// if no voice sub-packets are queued yet
|
|
if (pMicrPacket->MicrInfo.uNumSubPackets == 0)
|
|
{
|
|
// if needed, add reliable protocol components to this voip packet
|
|
if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE)
|
|
{
|
|
// packet routed through voip server
|
|
pWrite = _VoipReliableDataOutProcess(pConnectionlist, VOIP_CONNID_ALL, &pMicrPacket->Head, &pMicrPacket->aData[0], sizeof(pMicrPacket->aData));
|
|
}
|
|
else
|
|
{
|
|
// p2p packet
|
|
pWrite = _VoipReliableDataOutProcess(pConnectionlist, iConnID, &pMicrPacket->Head, &pMicrPacket->aData[0], sizeof(pMicrPacket->aData));
|
|
}
|
|
|
|
// if the voice timer has elapsed, reset it
|
|
if (NetTickDiff(uCurTick, pConnection->aVoiceSendTimer[uUserIndex]) >= 0)
|
|
{
|
|
pConnection->aVoiceSendTimer[uUserIndex] = uCurTick + VOIP_MSPERPACKET;
|
|
}
|
|
|
|
// if necessary, add metadata before we start appending the first packet
|
|
if (pMetaData != NULL)
|
|
{
|
|
// store metadata length in packet buffer
|
|
*pWrite++ = (unsigned)iMetaDataSize;
|
|
|
|
// copy metadata into packet buffer
|
|
ds_memcpy(pWrite, pMetaData, iMetaDataSize);
|
|
pWrite += iMetaDataSize;
|
|
|
|
// set flag signaling that payload starts with metadata size/metadata pair
|
|
pMicrPacket->Head.uFlags |= VOIP_PACKET_STATUS_FLAG_METADATA;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pWrite = _VoipPacketGetWritePtr(pMicrPacket);
|
|
}
|
|
|
|
// does this platform work with variable length sub-pkts
|
|
if ((iResult = VoipStatus(VoipGetRef(), 'vlen', 0, &bVariableSubPktLen, sizeof(bVariableSubPktLen))) < 0)
|
|
{
|
|
// if selector is not supported, assume fixed length
|
|
bVariableSubPktLen = FALSE;
|
|
}
|
|
|
|
if (bVariableSubPktLen)
|
|
{
|
|
// MicrInfo.uSubPacketSize == 0xFF means that sub-pkts may have different sizes
|
|
pMicrPacket->MicrInfo.uSubPacketSize = 0xFF;
|
|
}
|
|
else
|
|
{
|
|
#if DIRTYCODE_LOGGING
|
|
if (pMicrPacket->MicrInfo.uSubPacketSize != 0 && pMicrPacket->MicrInfo.uSubPacketSize != iDataSize)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] critical error - sub-packets have different size -> %d vs %d\n", iConnID, pMicrPacket->MicrInfo.uSubPacketSize, iDataSize));
|
|
}
|
|
if (pMicrPacket->MicrInfo.uSubPacketSize == 0xFF)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] critical error - sub-pkt size conflicting with fixed-length sub-pkt mode\n", iConnID));
|
|
}
|
|
#endif
|
|
pMicrPacket->MicrInfo.uSubPacketSize = (unsigned)iDataSize;
|
|
}
|
|
|
|
if (bVariableSubPktLen)
|
|
{
|
|
// store sub-pkt length in packet buffer
|
|
*pWrite = (unsigned)iDataSize;
|
|
|
|
// copy data into packet buffer
|
|
ds_memcpy(pWrite+1, pVoiceData, iDataSize);
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
if ((pWrite + 1 + iDataSize) >= ((&pConnection->VoipMicrPacket[uUserIndex].aData[0]) + sizeof(pConnection->VoipMicrPacket[uUserIndex].aData)))
|
|
{
|
|
NetPrintf(("voipconnection: [%d] critical error - sub-packet packing overflowed!\n", iConnID));
|
|
}
|
|
#endif
|
|
}
|
|
else
|
|
{
|
|
// copy data into packet buffer
|
|
ds_memcpy(pWrite, pVoiceData, iDataSize);
|
|
}
|
|
|
|
// increment sub-packet count
|
|
pMicrPacket->MicrInfo.uNumSubPackets += 1;
|
|
|
|
// identify local user index that generated the voice data
|
|
pMicrPacket->MicrInfo.uUserIndex = uUserIndex;
|
|
|
|
// see if we need to send any buffered voice data
|
|
_VoipConnectionSendSingle(pConnectionlist, pConnection, uUserIndex, uCurTick, FALSE);
|
|
|
|
if (pConnection->iVoipServerConnId != VOIP_CONNID_NONE)
|
|
{
|
|
// mark game server as served
|
|
bSentToVoipServer = TRUE;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionFlush
|
|
|
|
\Description
|
|
Send currently queued voice data, if any.
|
|
|
|
\Input *pConnectionlist - connectionlist to send to
|
|
\Input iConnID - connection ident of connection to flush
|
|
|
|
\Output
|
|
int32_t - number of voice packets flushed
|
|
|
|
\Notes
|
|
Currently only useful on Xenon
|
|
|
|
\Version 01/04/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipConnectionFlush(VoipConnectionlistT *pConnectionlist, int32_t iConnID)
|
|
{
|
|
int32_t iUserIndex;
|
|
int32_t iNumSubPackets = 0;
|
|
int32_t iTotalNumSubPackets = 0;
|
|
|
|
// ref the connection
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID];
|
|
|
|
for (iUserIndex = 0; iUserIndex < VOIP_MAXLOCALUSERS_EXTENDED; iUserIndex++)
|
|
{
|
|
iNumSubPackets = (signed)pConnection->VoipMicrPacket[iUserIndex].MicrInfo.uNumSubPackets;
|
|
|
|
/* is the connection active, and are we sending to it?
|
|
xboxone: Never "mute" here, i.e. skip applying pConnectionlist->uSendMask.
|
|
Per-connection muting handling is rather deferred to VoipHeadsetXboxOne (See usage of +snd, -snd, +rcv, -rcv).
|
|
After rebasing VoipHeadsetXboxOne to MS game chat 2, we found out that the integration
|
|
with our per-connection muting here was not behaving properly during unmuting: resuming
|
|
submission of data frames to MS game chat 2 resulted in a ~ 5-sec delay before speech
|
|
would resume. */
|
|
if ((pConnection->eState == ST_ACTV) && (iNumSubPackets > 0)
|
|
#if !defined(DIRTYCODE_XBOXONE) && !defined(DIRTYCODE_GDK)
|
|
&& (pConnectionlist->uSendMask & (1 << iConnID))
|
|
#endif
|
|
)
|
|
{
|
|
// see if we need to send any buffered voice data
|
|
_VoipConnectionSendSingle(pConnectionlist, pConnection, iUserIndex, NetTick(), TRUE);
|
|
|
|
// increment total number of sub-packets flushed for the connection
|
|
iTotalNumSubPackets += iNumSubPackets;
|
|
}
|
|
}
|
|
|
|
// return total number of sub-packets flushed for the connection
|
|
return(iTotalNumSubPackets);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionRegisterRemoteTalkers
|
|
|
|
\Description
|
|
Register/unregister remote users associated with the given connection.
|
|
|
|
\Input *pConnectionlist - connectionlist to send to
|
|
\Input iConnID - connection ident of connection to flush
|
|
\Input bRegister - if TRUE register talkers, else unregister them
|
|
|
|
\Version 01/04/2006 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionRegisterRemoteTalkers(VoipConnectionlistT *pConnectionlist, int32_t iConnID, uint32_t bRegister)
|
|
{
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnID];
|
|
int32_t iUser;
|
|
|
|
// register any remote users that are part of the connection
|
|
for (iUser = 0; iUser < pConnection->iMaxRemoteUsersExt; iUser++)
|
|
{
|
|
VoipUserT* pRemoteUser = (VoipUserT *)(&pConnection->RemoteUsers[iUser]);
|
|
// if no user, don't register
|
|
if (VOIP_NullUser(pRemoteUser))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
// if the user shouldn't be joining us, complain
|
|
if (pRemoteUser->ePlatform != VOIP_LOCAL_PLATFORM) // a platform that doesn't match the local platform is joining us
|
|
{
|
|
// is cross play setup properly for the remote user
|
|
if ((pRemoteUser->uFlags & VOIPUSER_FLAG_CROSSPLAY) == FALSE)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] error, platform %d, persona %lld attempted to join, but cross play is not enabled remotely.\n", iConnID, pRemoteUser->ePlatform, pRemoteUser->AccountInfo.iPersonaId));
|
|
}
|
|
|
|
// is cross play setup properly for the local user
|
|
if (VoipStatus(VoipGetRef(), 'xply', 0, NULL, 0) == FALSE)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] error, platform %d, persona %lld attempted to join, but cross play is not enabled locally.\n", iConnID, pRemoteUser->ePlatform, pRemoteUser->AccountInfo.iPersonaId));
|
|
}
|
|
}
|
|
else if (((pRemoteUser->uFlags & VOIPUSER_FLAG_CROSSPLAY) == TRUE) != (VoipStatus(VoipGetRef(), 'xply', 0, NULL, 0) == TRUE)) // the platform matches, but the cross play settings should match too
|
|
{
|
|
NetPrintf(("voipconnection: [%d] error, local and remote user have mismatched cross play settings, platform %d, persona %lld.\n", iConnID, pRemoteUser->ePlatform, pRemoteUser->AccountInfo.iPersonaId));
|
|
}
|
|
#endif
|
|
|
|
// register the user
|
|
pConnectionlist->pRegUserCb((VoipUserT *)&pConnection->RemoteUsers[iUser], iConnID, bRegister, pConnectionlist->pRegUserUserData);
|
|
|
|
#if defined(DIRTYCODE_XBOXONE) || defined(DIRTYCODE_GDK)
|
|
// reapply player-to-player xone comm relationships
|
|
pConnectionlist->bApplyRelFromMuting = TRUE;
|
|
#endif
|
|
}
|
|
|
|
// set mute list to be updated
|
|
pConnectionlist->bUpdateMute = TRUE;
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionSetSendMask
|
|
|
|
\Description
|
|
Set connections to send to.
|
|
|
|
\Input *pConnectionlist - connectionlist to send to
|
|
\Input uSendMask - connection send mask
|
|
|
|
\Version 03/22/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionSetSendMask(VoipConnectionlistT *pConnectionlist, uint32_t uSendMask)
|
|
{
|
|
if (pConnectionlist->uSendMask != uSendMask)
|
|
{
|
|
VoipCommonSetMask(&pConnectionlist->uSendMask, uSendMask, "sendmask");
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionSetRecvMask
|
|
|
|
\Description
|
|
Set connections to receive from.
|
|
|
|
\Input *pConnectionlist - connectionlist to send to
|
|
\Input uRecvMask - connection receive mask
|
|
|
|
\Version 03/22/2004 (jbrookes)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionSetRecvMask(VoipConnectionlistT *pConnectionlist, uint32_t uRecvMask)
|
|
{
|
|
if (pConnectionlist->uRecvMask != uRecvMask)
|
|
{
|
|
VoipCommonSetMask(&pConnectionlist->uRecvMask, uRecvMask, "recvmask");
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionAddSessionId
|
|
|
|
\Description
|
|
Add a session ID the set of higher level sessions sharing the specified VoIP connection.
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input iConnId - connection ID
|
|
\Input uSessionId - session ID to be added
|
|
|
|
\Output
|
|
int32_t - 0 for success, negative for failure
|
|
|
|
\Notes
|
|
Maintaning a set of session IDs per voip connection is required to support cases
|
|
where two consoles are involved in a P2P game and a pg simultaneously. Both of these
|
|
constructs will have a different session ID over the same shared voip connection.
|
|
Under some specific race conditions affecting the order at which Blaze messages are
|
|
processed by each game client, it is very possible that the pg session id is setup
|
|
first on one side and second on the other side thus leading to VOIP connectivity
|
|
failures if the voip connection construct is not supporting multiple concurrent
|
|
session IDs.
|
|
|
|
\Version 08/30/2011 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipConnectionAddSessionId(VoipConnectionlistT *pConnectionlist, int32_t iConnId, uint32_t uSessionId)
|
|
{
|
|
int32_t iSessionIndex;
|
|
int32_t iRetCode = -1;
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId];
|
|
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
for (iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++)
|
|
{
|
|
if (pConnection->uSessionId[iSessionIndex] == 0)
|
|
{
|
|
// free spot, insert session ID here
|
|
pConnection->uSessionId[iSessionIndex] = uSessionId;
|
|
|
|
NetPrintf(("voipconnection: [%d] added 0x%08x to set of sessions sharing this voip connection\n", iConnId, uSessionId));
|
|
|
|
iRetCode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iRetCode != 0)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] warning - 0x%08x could not be added to set of sessions sharing this voip connection because the list is full.\n", iConnId, uSessionId));
|
|
}
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
// print set of sessions sharing this connection
|
|
_VoipConnectionPrintSessionIds(pConnectionlist, iConnId);
|
|
#endif
|
|
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
|
|
return(iRetCode);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionDeleteSessionId
|
|
|
|
\Description
|
|
Delete a session ID from the set of sessions sharing this voip connection.
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input iConnId - connection id
|
|
\Input uSessionId - session ID to be deleted
|
|
|
|
\Output
|
|
int32_t - 0 for success, negative for failure
|
|
|
|
\Version 08/30/2011 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipConnectionDeleteSessionId(VoipConnectionlistT *pConnectionlist, int32_t iConnId, uint32_t uSessionId)
|
|
{
|
|
int32_t iSessionIndex;
|
|
int32_t iRetCode = -1;
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnId];
|
|
|
|
NetCritEnter(&pConnectionlist->NetCrit);
|
|
|
|
// now delete this address from the list of fallbacks
|
|
for(iSessionIndex = 0; iSessionIndex < VOIP_MAXSESSIONIDS; iSessionIndex++)
|
|
{
|
|
if (pConnection->uSessionId[iSessionIndex] == uSessionId)
|
|
{
|
|
int32_t iSessionIndex2;
|
|
|
|
// move all following session IDs one cell backward in the array
|
|
for(iSessionIndex2 = iSessionIndex; iSessionIndex2 < VOIP_MAXSESSIONIDS; iSessionIndex2++)
|
|
{
|
|
if (iSessionIndex2 == VOIP_MAXSESSIONIDS-1)
|
|
{
|
|
// last entry, reset to 0
|
|
pConnection->uSessionId[iSessionIndex2] = 0;
|
|
}
|
|
else
|
|
{
|
|
pConnection->uSessionId[iSessionIndex2] = pConnection->uSessionId[iSessionIndex2+1];
|
|
}
|
|
}
|
|
|
|
NetPrintf(("voipconnection: [%d] removed 0x%08x from set of sessions sharing this voip connection\n", iConnId, uSessionId));
|
|
|
|
iRetCode = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
if (iRetCode != 0)
|
|
{
|
|
NetPrintf(("voipconnection: [%d] warning - 0x%08x not deleted because not found in set of sessions sharing this voip connection\n", iConnId, uSessionId));
|
|
}
|
|
|
|
#if DIRTYCODE_LOGGING
|
|
// print set of sessions sharing this connection
|
|
_VoipConnectionPrintSessionIds(pConnectionlist, iConnId);
|
|
#endif
|
|
|
|
NetCritLeave(&pConnectionlist->NetCrit);
|
|
|
|
return(iRetCode);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionReliableBroadcastUser
|
|
|
|
\Description
|
|
Use this function to broadcast local user join-in-progress/leave-in-progress
|
|
data that needs to be sent reliably on all active connections (or connections in ST_CONN state).
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input uLocalUserIndex - local user index
|
|
\Input bParticipating - TRUE if user joined-in-progress, FALSE if user left-in-progress
|
|
|
|
\Version 09/18/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionReliableBroadcastUser(VoipConnectionlistT *pConnectionlist, uint8_t uLocalUserIndex, uint32_t bParticipating)
|
|
{
|
|
ReliableDataT ReliableData;
|
|
uint8_t *pWrite = &ReliableData.aData[0];
|
|
|
|
// fill type
|
|
ReliableData.info.uType = (bParticipating ? VOIP_RELIABLE_TYPE_USERADD : VOIP_RELIABLE_TYPE_USERREM);
|
|
|
|
// fill payload with user index first
|
|
*pWrite++ = uLocalUserIndex;
|
|
|
|
// then append voip user
|
|
ds_memcpy_s(pWrite, sizeof(ReliableData.aData), &pConnectionlist->LocalUsers[uLocalUserIndex], sizeof(pConnectionlist->LocalUsers[uLocalUserIndex]));
|
|
pWrite = pWrite + sizeof(pConnectionlist->LocalUsers[uLocalUserIndex]);
|
|
|
|
// fill size
|
|
_VoipEncodeU16(&ReliableData.info.uSize[0], (pWrite - &ReliableData.aData[0]));
|
|
|
|
// enqueue for transmission on all connections (will also fill in seq number)
|
|
_VoipConnectionReliableDataEnqueue(pConnectionlist, VOIP_CONNID_ALL, &ReliableData);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionReliableTranscribedTextMessage
|
|
|
|
\Description
|
|
Use this function to reliably broadcast a transcribed text message
|
|
(originated from a local user) on all connections that requested for it.
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input uLocalUserIndex - local user index
|
|
\Input *pStrUtf8 - pointer to text message to be sent
|
|
|
|
\Version 09/18/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionReliableTranscribedTextMessage(VoipConnectionlistT *pConnectionlist, uint8_t uLocalUserIndex, const char *pStrUtf8)
|
|
{
|
|
int32_t iConnectionIndex;
|
|
ReliableDataT ReliableData;
|
|
uint8_t *pWrite = &ReliableData.aData[0];
|
|
|
|
// fill type
|
|
ReliableData.info.uType = VOIP_RELIABLE_TYPE_TEXT;
|
|
|
|
// fill payload with user index first
|
|
*pWrite++ = uLocalUserIndex;
|
|
ds_strnzcpy((char *)pWrite, pStrUtf8, VOIP_MAXRELIABLEDATA - 1);
|
|
pWrite += strlen((const char *)pWrite)+1;
|
|
|
|
// fill size of payload
|
|
_VoipEncodeU16(&ReliableData.info.uSize[0], (pWrite - &ReliableData.aData[0]));
|
|
|
|
// enqueue for transmission only on connections with users that requested for transcribed text
|
|
for (iConnectionIndex = 0; iConnectionIndex < pConnectionlist->iMaxConnections; iConnectionIndex++)
|
|
{
|
|
VoipConnectionT *pConnection = &pConnectionlist->pConnections[iConnectionIndex];
|
|
|
|
if ((pConnection->eState == ST_ACTV) && pConnection->bTranscribedTextRequested && (pConnectionlist->uSendMask & (1 << iConnectionIndex)))
|
|
{
|
|
_VoipConnectionReliableDataEnqueue(pConnectionlist, iConnectionIndex, &ReliableData);
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipConnectionReliableSendOpaque
|
|
|
|
\Description
|
|
Use this function to send opaque data reliably over specified connection.
|
|
|
|
\Input *pConnectionlist - connectionlist
|
|
\Input iConnID - connection to send the data over
|
|
\Input *pData - pointer to buffer filled with reliable data to be broadcasted
|
|
\Input uDataSize - reliable data size (in bytes)
|
|
|
|
\Version 09/18/2014 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipConnectionReliableSendOpaque(VoipConnectionlistT *pConnectionlist, int32_t iConnID, const uint8_t *pData, uint16_t uDataSize)
|
|
{
|
|
ReliableDataT ReliableData;
|
|
|
|
// fill type
|
|
ReliableData.info.uType = VOIP_RELIABLE_TYPE_OPAQUE;
|
|
|
|
// fill size
|
|
_VoipEncodeU16(&ReliableData.info.uSize[0], uDataSize);
|
|
|
|
// fill payload
|
|
ds_memcpy_s(&ReliableData.aData[0], sizeof(ReliableData.aData), pData, uDataSize);
|
|
|
|
// enqueue for transmission on all connections (will also fill in seq number)
|
|
_VoipConnectionReliableDataEnqueue(pConnectionlist, iConnID, &ReliableData);
|
|
}
|
|
|