Kawe Mazidjatari b3a68ed095 Add EABase, EAThread and DirtySDK to R5sdk
DirtySDK (EA's Dirty Sockets library) will be used for the LiveAPI implementation, and depends on: EABase, EAThread.
2024-04-05 18:29:03 +02:00

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));
}