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.
414 lines
14 KiB
C
414 lines
14 KiB
C
/*H********************************************************************************/
|
|
/*!
|
|
\File voipunix.c
|
|
|
|
\Description
|
|
voip interface (not production ready - for DirtyCast voip stress testing client)
|
|
|
|
\Copyright
|
|
Copyright (c) Electronic Arts 2017. ALL RIGHTS RESERVED.
|
|
|
|
\Version 06/08/2017 (mclouatre) First Version
|
|
*/
|
|
/********************************************************************************H*/
|
|
|
|
/*** Include files ****************************************************************/
|
|
|
|
// dirtysock includes
|
|
#include "DirtySDK/platform.h"
|
|
#include "DirtySDK/dirtysock.h"
|
|
#include "DirtySDK/dirtysock/netconn.h"
|
|
#include "DirtySDK/dirtysock/dirtylib.h"
|
|
#include "DirtySDK/dirtysock/dirtymem.h"
|
|
|
|
// voip includes
|
|
#include "DirtySDK/voip/voipdef.h"
|
|
#include "voippriv.h"
|
|
#include "voipcommon.h"
|
|
#include "DirtySDK/voip/voip.h"
|
|
|
|
|
|
/*** Defines **********************************************************************/
|
|
#define VOIP_THREAD_SLEEP_DURATION (20)
|
|
|
|
/*** Macros ***********************************************************************/
|
|
|
|
/*** Type Definitions *************************************************************/
|
|
|
|
//! VoIP module state
|
|
struct VoipRefT
|
|
{
|
|
VoipCommonRefT Common; //!< cross-platform voip data (must come first!)
|
|
|
|
// dummy traffic injection config
|
|
uint32_t uSendSeqn; //!< send sequence number
|
|
int32_t iDummyVoipSubPacketSize; //!< size of test packets (in bytes)
|
|
uint32_t uDummyVoipSendPeriod; //!< rate of artificially injected subpackets 0: disabled; [1,1024]: 0% to 100% of voip bandwidth produced over a 20-sec window
|
|
uint32_t uDummyVoipWrappingCounter; //!< 32-bit wrapping counter used to implement the right dummy send period
|
|
};
|
|
|
|
/*** Function Prototypes **********************************************************/
|
|
|
|
/*** Variables ********************************************************************/
|
|
|
|
/*** Private Functions ************************************************************/
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipStatusCb
|
|
|
|
\Description
|
|
Callback to handle change of headset status.
|
|
|
|
\Input iLocalUserIndex - headset that has changed
|
|
\Input bStatus - if TRUE the headset was inserted, else if FALSE it was removed
|
|
\Input eUpdate - functionality of the headset (input, output or both)
|
|
\Input *pUserData - pointer to callback user data
|
|
|
|
\Notes
|
|
It is assumed that eUpdate will always be VOIP_HEADSET_STATUS_INOUT for
|
|
this platform.
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipStatusCb(int32_t iLocalUserIndex, uint32_t bStatus, VoipHeadsetStatusUpdateE eUpdate, void *pUserData)
|
|
{
|
|
VoipRefT *pRef = (VoipRefT *)pUserData;
|
|
|
|
if (eUpdate != VOIP_HEADSET_STATUS_INOUT)
|
|
{
|
|
NetPrintf(("voipunix: warning, unexpected headset event for this platform!\n"));
|
|
}
|
|
|
|
if (bStatus)
|
|
{
|
|
NetPrintf(("voipunix: [%d] headset inserted\n", iLocalUserIndex));
|
|
pRef->Common.uPortHeadsetStatus |= 1 << iLocalUserIndex;
|
|
pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] |= (VOIP_LOCAL_USER_HEADSETOK|VOIP_LOCAL_USER_INPUTDEVICEOK|VOIP_LOCAL_USER_OUTPUTDEVICEOK);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipunix: [%d] headset removed\n", iLocalUserIndex));
|
|
pRef->Common.uPortHeadsetStatus &= ~(1 << iLocalUserIndex);
|
|
pRef->Common.Connectionlist.uLocalUserStatus[iLocalUserIndex] &= ~(VOIP_LOCAL_USER_HEADSETOK|VOIP_LOCAL_USER_INPUTDEVICEOK|VOIP_LOCAL_USER_OUTPUTDEVICEOK);
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipInjectDummyTraffic
|
|
|
|
\Description
|
|
Inject dummy voip traffic on all active voip connections
|
|
|
|
\Input *pVoip - module state
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipInjectDummyTraffic(VoipRefT *pVoip)
|
|
{
|
|
static uint8_t DummyVoipSubPacket[VOIP_MAXMICRPKTSIZE];
|
|
VoipConnectionlistT *pConnectionlist = &pVoip->Common.Connectionlist;
|
|
|
|
// only inject dummy sub-packets if the 1024-large counter is in between [0, pVoip->uDummyVoipSendPeriod[
|
|
if ((++pVoip->uDummyVoipWrappingCounter % 1024) < pVoip->uDummyVoipSendPeriod)
|
|
{
|
|
// send dummy traffic on all active connections
|
|
VoipConnectionSend(pConnectionlist, 0xFFFFFFFF, &DummyVoipSubPacket[0], pVoip->iDummyVoipSubPacketSize, NULL, 0, 0, pVoip->uSendSeqn++);
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function _VoipUpdate
|
|
|
|
\Description
|
|
Update function to be used in a single-threaded environment
|
|
|
|
\Input *pVoip - voip ref
|
|
|
|
\Version 01/18/2018 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
static void _VoipUpdate(VoipRefT *pVoip)
|
|
{
|
|
uint32_t uTime0, uTime1;
|
|
int32_t iSleep;
|
|
|
|
uTime0 = NetTick();
|
|
|
|
if (pVoip->Common.bApplyChannelConfig)
|
|
{
|
|
pVoip->Common.bApplyChannelConfig = FALSE;
|
|
VoipCommonApplyChannelConfig((VoipCommonRefT *)pVoip);
|
|
}
|
|
|
|
// update status of remote users
|
|
VoipCommonUpdateRemoteStatus(&pVoip->Common);
|
|
VoipConnectionUpdate(&pVoip->Common.Connectionlist);
|
|
|
|
// inject dummy voip traffic for stress test purposes
|
|
_VoipInjectDummyTraffic(pVoip);
|
|
|
|
uTime1 = NetTick();
|
|
|
|
iSleep = VOIP_THREAD_SLEEP_DURATION - NetTickDiff(uTime1, uTime0);
|
|
|
|
// wait for next tick
|
|
if (iSleep >= 0)
|
|
{
|
|
NetConnSleep(iSleep);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipunix: [WARNING] Overall voip update took %d ms\n", NetTickDiff(uTime1, uTime0)));
|
|
}
|
|
}
|
|
|
|
/*** Public Functions *************************************************************/
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipStartup
|
|
|
|
\Description
|
|
Prepare VoIP for use.
|
|
|
|
\Input iMaxPeers - maximum number of peers supported (up to VOIP_MAXCONNECT)
|
|
\Input iData - platform-specific - unused on Unix
|
|
|
|
\Output
|
|
VoipRefT - state reference that is passed to all other functions
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
VoipRefT *VoipStartup(int32_t iMaxPeers, int32_t iData)
|
|
{
|
|
VoipRefT *pVoip;
|
|
|
|
NetPrintf(("voipunix: WARNING - VoipUnix is not for production. Usage reserved for DirtyCast voip stress testing only!\n"));
|
|
|
|
// common startup
|
|
if ((pVoip = VoipCommonStartup(iMaxPeers, sizeof(*pVoip), _VoipStatusCb, iData)) == NULL)
|
|
{
|
|
return(NULL);
|
|
}
|
|
|
|
// injection of dummy traffic is disabled by default
|
|
pVoip->iDummyVoipSubPacketSize = 0;
|
|
pVoip->uDummyVoipSendPeriod = 0;
|
|
|
|
// bPrivileged is always TRUE on unix
|
|
pVoip->Common.bPrivileged = TRUE;
|
|
|
|
// return to caller
|
|
return(pVoip);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipShutdown
|
|
|
|
\Description
|
|
Release all VoIP resources.
|
|
|
|
\Input *pVoip - module state from VoipStartup
|
|
\Input uShutdownFlags - NET_SHUTDOWN_* flags
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipShutdown(VoipRefT *pVoip, uint32_t uShutdownFlags)
|
|
{
|
|
// make sure we're really started up
|
|
if (VoipGetRef() == NULL)
|
|
{
|
|
NetPrintf(("voipunix: module shutdown called when not in a started state\n"));
|
|
return;
|
|
}
|
|
|
|
// common shutdown (must come last as this frees the memory)
|
|
VoipCommonShutdown(&pVoip->Common);
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipSetLocalUser
|
|
|
|
\Description
|
|
Register/unregister specified local user with the voip sub-system.
|
|
|
|
\Input *pVoip - module state from VoipStartup
|
|
\Input iLocalUserIndex - local user index (ignored because MLU not supported on PC)
|
|
\Input bRegister - ignored
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipSetLocalUser(VoipRefT *pVoip, int32_t iLocalUserIndex, uint32_t bRegister)
|
|
{
|
|
if (bRegister)
|
|
{
|
|
if (VOIP_NullUser(&pVoip->Common.Connectionlist.LocalUsers[0]))
|
|
{
|
|
VoipUserT voipUser;
|
|
ds_memclr(&voipUser, sizeof(voipUser));
|
|
|
|
voipUser.ePlatform = VOIP_PLATFORM_LINUX;
|
|
NetConnStatus('acid', iLocalUserIndex, &voipUser.AccountInfo.iAccountId, sizeof(voipUser.AccountInfo.iAccountId));
|
|
NetConnStatus('peid', iLocalUserIndex, &voipUser.AccountInfo.iPersonaId, sizeof(voipUser.AccountInfo.iPersonaId));
|
|
|
|
NetPrintf(("voipunix: registering a local user %llx at index %d\n", voipUser.AccountInfo.iPersonaId, iLocalUserIndex));
|
|
VOIP_CopyUser(&pVoip->Common.Connectionlist.LocalUsers[0], &voipUser);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipunix: warning -- ignoring attempt to register local user because user %llx is already registered\n", pVoip->Common.Connectionlist.LocalUsers[0].AccountInfo.iPersonaId));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if (!VOIP_NullUser(&pVoip->Common.Connectionlist.LocalUsers[0]))
|
|
{
|
|
// if a participating user demote him first
|
|
if (pVoip->Common.Connectionlist.aIsParticipating[0] == TRUE)
|
|
{
|
|
VoipCommonActivateLocalUser(&pVoip->Common, 0, FALSE);
|
|
}
|
|
|
|
NetPrintf(("voipunix: unregistering local user %llx\n", pVoip->Common.Connectionlist.LocalUsers[0].AccountInfo.iPersonaId));
|
|
ds_memclr(&pVoip->Common.Connectionlist.LocalUsers[0], sizeof(pVoip->Common.Connectionlist.LocalUsers[0]));
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipunix: warning -- ignoring attempt to unregister local user because it is not yet registered\n"));
|
|
}
|
|
}
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipCommonActivateLocalUser
|
|
|
|
\Description
|
|
Promote/demote specified local user to/from "participating" state
|
|
|
|
\Input *pVoipCommon - voip common state
|
|
\Input iLocalUserIndex - local user index (ignored because MLU not supported on PC)
|
|
\Input bActivate - TRUE to activate, FALSE to deactivate
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
void VoipCommonActivateLocalUser(VoipCommonRefT *pVoipCommon, int32_t iLocalUserIndex, uint8_t bActivate)
|
|
{
|
|
if (!VOIP_NullUser(&pVoipCommon->Connectionlist.LocalUsers[0]))
|
|
{
|
|
if (bActivate == TRUE)
|
|
{
|
|
NetPrintf(("voipunix: promoting user %llx to participating state\n", &pVoipCommon->Connectionlist.LocalUsers[0].AccountInfo.iPersonaId));
|
|
pVoipCommon->Connectionlist.aIsParticipating[0] = TRUE;
|
|
|
|
// reapply playback muting config based on the channel configuration only if this is a participating user
|
|
pVoipCommon->bApplyChannelConfig = TRUE;
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipunix: demoting user %llx from participating state\n", &pVoipCommon->Connectionlist.LocalUsers[0].AccountInfo.iPersonaId));
|
|
pVoipCommon->Connectionlist.aIsParticipating[0] = FALSE;
|
|
}
|
|
VoipHeadsetControl(pVoipCommon->pHeadset, 'aloc', 0, bActivate, &pVoipCommon->Connectionlist.LocalUsers[0]);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipunix: VoipCommonActivateLocalUser() failed because no local user registered\n"));
|
|
}
|
|
}
|
|
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipStatus
|
|
|
|
\Description
|
|
Return status.
|
|
|
|
\Input *pVoip - module state from VoipStartup
|
|
\Input iSelect - status selector
|
|
\Input iValue - selector-specific
|
|
\Input *pBuf - [out] storage for selector-specific output
|
|
\Input iBufSize - size of output buffer
|
|
|
|
\Output
|
|
int32_t - selector-specific data
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipStatus(VoipRefT *pVoip, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize)
|
|
{
|
|
return(VoipCommonStatus(&pVoip->Common, iSelect, iValue, pBuf, iBufSize));
|
|
}
|
|
|
|
/*F********************************************************************************/
|
|
/*!
|
|
\Function VoipControl
|
|
|
|
\Description
|
|
Set control options.
|
|
|
|
\Input *pVoip - module state from VoipStartup
|
|
\Input iControl - control selector
|
|
\Input iValue - selector-specific
|
|
\Input *pValue - selector-specific
|
|
|
|
\Notes
|
|
iControl can be one of the following:
|
|
|
|
\verbatim
|
|
'psiz' - set size of artificially injected test voip sub-packets
|
|
'prat' - artificially injected voip sub-packets period --> 0: disabled; [1,1024]: 0% to 100% of voip bandwidth produced over a 20-sec window
|
|
'updt' - to pump the voip sub-system in a single-threaded context
|
|
\endverbatim
|
|
|
|
Unhandled selectors are passed through to VoipCommonControl(), and
|
|
further to VoipHeadsetControl() if unhandled there.
|
|
|
|
\Version 06/12/2017 (mclouatre)
|
|
*/
|
|
/********************************************************************************F*/
|
|
int32_t VoipControl(VoipRefT *pVoip, int32_t iControl, int32_t iValue, void *pValue)
|
|
{
|
|
if (iControl == 'psiz')
|
|
{
|
|
NetPrintf(("voipunix: size of artificially injected test voip sub-packets changed from %d bytes to %d bytes\n", pVoip->iDummyVoipSubPacketSize, iValue));
|
|
pVoip->iDummyVoipSubPacketSize = iValue;
|
|
return(0);
|
|
}
|
|
if (iControl == 'prat')
|
|
{
|
|
NetPrintf(("voipunix: artificially injected voip sub-packets period changed from %d/1024 to %d/1024\n", pVoip->uDummyVoipSendPeriod, iValue));
|
|
pVoip->uDummyVoipSendPeriod = iValue;
|
|
return(0);
|
|
}
|
|
if (iControl == 'updt')
|
|
{
|
|
if (VoipGetRef())
|
|
{
|
|
_VoipUpdate(VoipGetRef());
|
|
return(0);
|
|
}
|
|
else
|
|
{
|
|
NetPrintf(("voipunix: module update called when not in a started state\n"));
|
|
return(-1);
|
|
}
|
|
}
|
|
return(VoipCommonControl(&pVoip->Common, iControl, iValue, pValue));
|
|
}
|
|
|
|
|