/*H********************************************************************************/ /*! \File voipgroup.c \Description A module that handles logical grouping of voip connections. Each module that wants to deal with a block of voip connections uses a voipgroup. This reduces the singleton nature of voip.h \Copyright Copyright (c) 2007 Electronic Arts Inc. \Version 12/18/07 (jrainy) */ /********************************************************************************H*/ /*** Include files ****************************************************************/ #include // dirtysock includes #include "DirtySDK/platform.h" #include "DirtySDK/dirtysock.h" #include "DirtySDK/dirtysock/dirtymem.h" // voip includes #include "DirtySDK/voip/voip.h" #include "DirtySDK/voip/voipgroup.h" #include "voippriv.h" #include "voipcommon.h" /*** Defines **********************************************************************/ #define MAX_CLIENTS_PER_GROUP (32) #define MAX_REF_PER_LOW_LEVEL_CONNS (8) /*** Macros ***********************************************************************/ /*** Type Definitions *************************************************************/ //! high-level connection state typedef enum VoipGroupConnStateE { VOIPGROUP_CONNSTATE_ACTIVE, //iMaxGroups; iGroup++) { if ((pManager->aGroups[iGroup].bUsed) && (pManager->aGroups[iGroup].pGroupCallback)) { if (eCbType == VOIP_CBTYPE_FROMEVENT) { iNewValue = 0; for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { iMappedConnId = _VoipGroupMatch(&pManager->aGroups[iGroup], iConnId); if (iMappedConnId != VOIP_CONNID_NONE) { // lookup bit indexed by mapped value, and set the bit before mapping. iNewValue |= (((iValue & (1 << iMappedConnId)) ? 1 : 0) << iConnId); } } } else { iNewValue = iValue; } pManager->aGroups[iGroup].pGroupCallback(pVoip, eCbType, iNewValue, pManager->aGroups[iGroup].pGroupCallbackData); } } } /*F********************************************************************************/ /*! \Function _VoipGroupManagerDisplayTranscribedTextCallback \Description Callback registered with low-level voip transcribed text callback \Input iLowLevelConnId - connection index (-1 if originator is local) \Input iUserIndex - index of local/remote user from which the transcribed text is originated \Input *pStrUtf8 - received transcribed text \Input *pUserData - pointer to the VoipGroupManager \Output int32_t - TRUE \Version 05/03/2017 (mclouatre) */ /********************************************************************************F*/ static int32_t _VoipGroupManagerDisplayTranscribedTextCallback(int32_t iLowLevelConnId, int32_t iUserIndex, const char *pStrUtf8, void *pUserData) { VoipGroupManagerT *pManager = (VoipGroupManagerT *)pUserData; int32_t iGroup; uint8_t bDisplayed; for (iGroup = 0, bDisplayed = FALSE; (iGroup < pManager->iMaxGroups) && !bDisplayed; iGroup++) { if ((pManager->aGroups[iGroup].bUsed) && (pManager->aGroups[iGroup].pDisplayTranscribedTextCb)) { if (iLowLevelConnId != -1) { int32_t iHighLevelConnId; for (iHighLevelConnId = 0; iHighLevelConnId < MAX_CLIENTS_PER_GROUP; iHighLevelConnId++) { // is the entry valid and is it associated with the low level conn id we are interested in if ((pManager->aGroups[iGroup].aConnections[iHighLevelConnId].uClientId != 0) && (pManager->aGroups[iGroup].aConnections[iHighLevelConnId].iMappedLowLevelConnId == iLowLevelConnId)) { bDisplayed = pManager->aGroups[iGroup].pDisplayTranscribedTextCb(iHighLevelConnId, iUserIndex, pStrUtf8, pManager->aGroups[iGroup].pDisplayTranscribedTextUserData); break; } } } else { // display transcribed text from local user bDisplayed = pManager->aGroups[iGroup].pDisplayTranscribedTextCb(-1, iUserIndex, pStrUtf8, pManager->aGroups[iGroup].pDisplayTranscribedTextUserData); } } } return(TRUE); } /*F********************************************************************************/ /*! \Function _VoipGroupManagerCreate \Description Creates the single VoipGroupManager. \Input iMaxGroups - the maximum number of voip groups to allocate \Output VoipGroupManagerT * - The VoipGroup Manager \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ static VoipGroupManagerT *_VoipGroupManagerCreate(int8_t iMaxGroups) { if (_VoipGroupManager_pRef == NULL) { int32_t iMemGroup, iMemSize; void *pMemGroupUserData; // Query current mem group data DirtyMemGroupQuery(&iMemGroup, &pMemGroupUserData); // calculate size of state iMemSize = sizeof(*_VoipGroupManager_pRef) + (sizeof(_VoipGroupManager_pRef->aGroups[0]) * (iMaxGroups - 1)); if ((_VoipGroupManager_pRef = DirtyMemAlloc(iMemSize, VOIP_MEMID, iMemGroup, pMemGroupUserData)) == NULL) { NetPrintf(("voipgroup: could not allocate module state\n")); return(NULL); } ds_memclr(_VoipGroupManager_pRef, iMemSize); _VoipGroupManager_pRef->iMaxGroups = iMaxGroups; _VoipGroupManager_pRef->iMemGroup = iMemGroup; _VoipGroupManager_pRef->pMemGroupUserData = pMemGroupUserData; // register callback with low-level VOIP module if it exists if (VoipGetRef() != NULL) { VoipCommonSetEventCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerEventCallback, _VoipGroupManager_pRef); VoipCommonSetDisplayTranscribedTextCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerDisplayTranscribedTextCallback, _VoipGroupManager_pRef); } } return(_VoipGroupManager_pRef); } /*F********************************************************************************/ /*! \Function _VoipGroupFindFreeHighLevelConn \Description Find a free high level connection spot in the given voipgroup. \Input *pVoipGroup - the voipgroup to inspect for a free connection spot \Output int32_t - failure: VOIP_CONNID_NONE, success: conn id of high-level conn found \Version 10/04/2010 (mclouatre) */ /********************************************************************************F*/ static int32_t _VoipGroupFindFreeHighLevelConn(VoipGroupRefT *pVoipGroup) { int32_t iConnId; for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { if (pVoipGroup->aConnections[iConnId].uClientId == 0) { break; } } if (iConnId == MAX_CLIENTS_PER_GROUP) { NetPrintf(("voipgroup: [%p] warning - voipgroup is full\n", pVoipGroup)); return(VOIP_CONNID_NONE); } return(iConnId); } /*F********************************************************************************/ /*! \Function _VoipGroupFindUsedHighLevelConn \Description In the given voipgroiup, find the high level connection associated with specified client id. \Input *pVoipGroup - the voipgroup to inspect for a free connection spot \Input uClientId - client id to look for \Output int32_t - failure: VOIP_CONNID_NONE, success: conn id of high-level conn found \Version 10/04/2010 (mclouatre) */ /********************************************************************************F*/ static int32_t _VoipGroupFindUsedHighLevelConn(VoipGroupRefT *pVoipGroup, uint32_t uClientId) { int32_t iConnId; for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) { break; } } if (iConnId == MAX_CLIENTS_PER_GROUP) { return(VOIP_CONNID_NONE); } return(iConnId); } /*F********************************************************************************/ /*! \Function _VoipGroupLowLevelConnInUse \Description Tests whether a voip-level ConnId is in use. \Input iConnId - voip-level ConnId \Output uint8_t - TRUE or FALSE \Version 12/02/2009 (jrainy) */ /********************************************************************************F*/ static uint8_t _VoipGroupLowLevelConnInUse(int32_t iConnId) { return(_VoipGroupManager_pRef->aLowLevelConnReferences[iConnId][0] != NULL); } /*F********************************************************************************/ /*! \Function _VoipGroupManagerGetGroup \Description Allocates a new VoipGroup for the given manager \Input *pVoipGroupManager - The manager to allocate the group from \Output VoipGroupRefT* - The allocated VoipGroup \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ static VoipGroupRefT* _VoipGroupManagerGetGroup(VoipGroupManagerT *pVoipGroupManager) { int32_t iIndex; if (pVoipGroupManager->iNumGroups < pVoipGroupManager->iMaxGroups) { for (iIndex = 0; (iIndex < pVoipGroupManager->iMaxGroups); iIndex++) { if (!pVoipGroupManager->aGroups[iIndex].bUsed) { return(&pVoipGroupManager->aGroups[iIndex]); } } } NetPrintf(("voipgroup: no voipgroup available\n")); return(NULL); } #if DIRTYCODE_LOGGING /*F********************************************************************************/ /*! \Function _VoipGroupPrintRefsToLowLevelConn \Description Prints all references to a given low-level voip connection \Input iLowLevelConnId - id of low-level connection \Version 11/12/2009 (mclouatre) */ /********************************************************************************F*/ static void _VoipGroupPrintRefsToLowLevelConn(int32_t iLowLevelConnId) { int32_t iRefIndex; VoipGroupManagerT *pManager = _VoipGroupManager_pRef; if (_VoipGroupLowLevelConnInUse(iLowLevelConnId)) { NetPrintf(("voipgroup: references to low-level voip conn %d are: ", iLowLevelConnId)); for (iRefIndex = 0; iRefIndex < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex++) { if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] != NULL) { NetPrintf(("%p ", pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex])); } else { break; } } NetPrintf(("\n")); } else { NetPrintf(("voipgroup: there is no reference to low-level voip conn %d\n", iLowLevelConnId)); } } #endif /*F********************************************************************************/ /*! \Function _VoipGroupManagerShutdown \Description free the singleton VoipGroupManager. \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ static void _VoipGroupManagerShutdown(void) { if (_VoipGroupManager_pRef != NULL) { // remove callback registered with low-level VOIP module if (VoipGetRef() != NULL) { VoipCommonSetEventCallback((VoipCommonRefT *)VoipGetRef(), NULL, NULL); VoipCommonSetDisplayTranscribedTextCallback((VoipCommonRefT *)VoipGetRef(), NULL, NULL); } DirtyMemFree(_VoipGroupManager_pRef, VOIP_MEMID, _VoipGroupManager_pRef->iMemGroup, _VoipGroupManager_pRef->pMemGroupUserData); _VoipGroupManager_pRef = NULL; } } /*F********************************************************************************/ /*! \Function _VoipGroupManagerReleaseGroup \Description Frees a VoipGroup for the given manager \Input *pManager - The manager to allocate the group from \Input *pVoipGroup - The VoipGroup to free \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ static void _VoipGroupManagerReleaseGroup(VoipGroupManagerT *pManager, VoipGroupRefT* pVoipGroup) { int32_t iSlot; int32_t iUser; NetPrintf(("voipgroup: [%p] remove all connections and clear all mappings\n", pVoipGroup)); // remove all connections for(iSlot = 0; iSlot < MAX_CLIENTS_PER_GROUP; iSlot++) { if (pVoipGroup->aConnections[iSlot].uClientId != 0) { VoipGroupDisconnect(pVoipGroup, iSlot); } } // deactivate active refs for(iUser = 0; iUser < VOIP_MAXLOCALUSERS; iUser++) { if (pVoipGroup->aParticipatingUser[iUser] == TRUE) { VoipGroupActivateLocalUser(pVoipGroup, iUser, FALSE); } } // clear voipgroup entry ds_memclr(pVoipGroup, sizeof(VoipGroupRefT)); // shutdown the voipgroup manager if there are no groups left if ((--pManager->iNumGroups) == 0) { _VoipGroupManagerShutdown(); } return; } /*F********************************************************************************/ /*! \Function _VoipGroupMatch \Description Converts between VoipGroup ID and Voip ID. \Input *pVoipGroup - The VoipGroup the iConnId indexes in \Input iConnId - The VoipGroup ID \Output int32_t - The corresponding Voip ID \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ static int32_t _VoipGroupMatch(VoipGroupRefT *pVoipGroup, int32_t iConnId) { int32_t iIndex, iConn, iRetVal = VOIP_CONNID_NONE; uint32_t uClientId; VoipGroupManagerT *pManager = _VoipGroupManager_pRef; if ((iConnId < 0) || (iConnId >= MAX_CLIENTS_PER_GROUP)) { return(VOIP_CONNID_NONE); } uClientId = pVoipGroup->aConnections[iConnId].uClientId; if (uClientId == 0) { return(VOIP_CONNID_NONE); } // if this connection is suspended look for the active group // for this iConnId to get the proper low level conn id if (pVoipGroup->aConnections[iConnId].eConnState == VOIPGROUP_CONNSTATE_SUSPENDED) { for (iIndex = 0; iIndex < pManager->iMaxGroups; iIndex++) { for (iConn = 0; iConn < MAX_CLIENTS_PER_GROUP; iConn++) { if ( (pManager->aGroups[iIndex].aConnections[iConn].uClientId == uClientId) && (pManager->aGroups[iIndex].aConnections[iConn].eConnState == VOIPGROUP_CONNSTATE_ACTIVE) ) { iRetVal = pManager->aGroups[iIndex].aConnections[iConn].iMappedLowLevelConnId; break; } } } } else { iRetVal = pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId; } return(iRetVal); } /*F********************************************************************************/ /*! \Function _VoipGroupFindCollision \Description Looks for another group with a low-level connection to a given client id \Input *pVoipGroup - pointer to voipgroup looking for a colliding voipgroup \Input uClientId - the client id to match \Input eConnState - connection state to match \Input *pCollidingConnId - output parameter to be filled with id of colliding connection if applicable (invalid if return value is NULL) \Output VoipGroupRefT* - group reference if found, NULL if no collision \Version 011/11/2009 (mclouatre) */ /********************************************************************************F*/ static VoipGroupRefT* _VoipGroupFindCollision(VoipGroupRefT *pVoipGroup, uint32_t uClientId, VoipGroupConnStateE eConnState, int32_t *pCollidingConnId) { int32_t iIndex, iConnId; VoipGroupManagerT *pManager = _VoipGroupManager_pRef; for (iIndex = 0; iIndex < pManager->iMaxGroups; iIndex++) { for (iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { if ((pManager->aGroups[iIndex].aConnections[iConnId].uClientId == uClientId) && (pManager->aGroups[iIndex].aConnections[iConnId].eConnState == eConnState)) { // do not allow a voipgroup to collide with itself if (&pManager->aGroups[iIndex] == pVoipGroup) { continue; } NetPrintf(("voipgroup: [%p] collision found with voipgroup %p for client id 0x%08x\n", pVoipGroup, &pManager->aGroups[iIndex], uClientId)); *pCollidingConnId = iConnId; return(&pManager->aGroups[iIndex]); } } } *pCollidingConnId = VOIP_CONNID_NONE; return(NULL); } /*F********************************************************************************/ /*! \Function _VoipGroupAddRefToLowLevelConn \Description Add a reference to a low level connection \Input iLowLevelConnId - low level conn id \Input *pVoipGroup - voipgroup to be added as a reference to the low-level connection \Output int32_t - 0 for success, -1 for failure \Version 11/12/2009 (mclouatre) */ /********************************************************************************F*/ static int32_t _VoipGroupAddRefToLowLevelConn(int32_t iLowLevelConnId, VoipGroupRefT * pVoipGroup) { int32_t iRefIndex; int32_t iRetCode = 0; // default to succes VoipGroupManagerT *pManager = _VoipGroupManager_pRef; for (iRefIndex = 0; iRefIndex < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex++) { // if this ref already is in the table, fake a failure if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] == pVoipGroup) { iRetCode = -1; NetPrintf(("voipgroup: [%p] error - current voipgroup already exists as a reference for low-level voip conn %d\n", pVoipGroup, iLowLevelConnId)); break; } // look for a free spot to find our ref if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] == NULL) { pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] = pVoipGroup; NetPrintf(("voipgroup: [%p] added current voipgroup as a reference for low-level voip conn %d\n", pVoipGroup, iLowLevelConnId)); break; } } if (iRefIndex == MAX_REF_PER_LOW_LEVEL_CONNS) { iRetCode = -1; NetPrintf(("voipgroup: [%p] critical error - cannot add current voipgroup as a reference for low-level conn %d, max number of references exceeded\n", pVoipGroup, iLowLevelConnId)); } #if DIRTYCODE_LOGGING _VoipGroupPrintRefsToLowLevelConn(iLowLevelConnId); #endif return(iRetCode); } /*F********************************************************************************/ /*! \Function _VoipGroupRemoveRefToLowLevelConn \Description Remove a reference to a low level connection \Input iLowLevelConnId - low level conn id \Input *pVoipGroup - voipgroup to be added as a reference to the low-level connection \Version 11/12/2009 (mclouatre) */ /********************************************************************************F*/ static void _VoipGroupRemoveRefToLowLevelConn(int32_t iLowLevelConnId, VoipGroupRefT * pVoipGroup) { int32_t iRefIndex; VoipGroupManagerT *pManager = _VoipGroupManager_pRef; for(iRefIndex = 0; iRefIndex < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex++) { if (pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex] == pVoipGroup) { int32_t iRefIndex2; // move all following references one cell backward in the array for(iRefIndex2 = iRefIndex; iRefIndex2 < MAX_REF_PER_LOW_LEVEL_CONNS; iRefIndex2++) { if (iRefIndex2 == MAX_REF_PER_LOW_LEVEL_CONNS-1) { // last entry, reset to NULL pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex2] = NULL; } else { pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex2] = pManager->aLowLevelConnReferences[iLowLevelConnId][iRefIndex2+1]; } } NetPrintf(("voipgroup: [%p] current voipgroup no more referencing low-level voip conn %d\n", pVoipGroup, iLowLevelConnId)); #if DIRTYCODE_LOGGING _VoipGroupPrintRefsToLowLevelConn(iLowLevelConnId); #endif break; } } if (iRefIndex == MAX_REF_PER_LOW_LEVEL_CONNS) { NetPrintf(("voipgroup: [%p] warning - current voipgroup not found as a valid reference for low-level conn %d\n", pVoipGroup, iLowLevelConnId)); } } /*** Public Functions *************************************************************/ /*F********************************************************************************/ /*! \Function VoipGroupCreate \Description allocates a VoipGroup \Input iMaxGroups - the maximum number of groups we can have active at one time \Output VoipGroupRefT* - Allocated VoipGroup \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ VoipGroupRefT* VoipGroupCreate(int8_t iMaxGroups) { VoipGroupManagerT *pManager = _VoipGroupManagerCreate(iMaxGroups); VoipGroupRefT *pNewlyCreated; if ((pNewlyCreated = _VoipGroupManagerGetGroup(pManager)) != NULL) { pManager->iNumGroups++; pNewlyCreated->bUsed = TRUE; } return(pNewlyCreated); } /*F********************************************************************************/ /*! \Function VoipGroupDestroy \Description Deallocates a VoipGroup \Input *pVoipGroup - VoipGroup to Deallocate \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ void VoipGroupDestroy(VoipGroupRefT *pVoipGroup) { _VoipGroupManagerReleaseGroup(_VoipGroupManager_pRef, pVoipGroup); } /*F********************************************************************************/ /*! \Function VoipGroupSetEventCallback \Description Sets the callback and userdata for the specified group \Input *pVoipGroup - VoipGroup to use \Input *pCallback - pointer to the callback to use \Input *pUserData - pointer to the user specified data \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ void VoipGroupSetEventCallback(VoipGroupRefT *pVoipGroup, VoipCallbackT *pCallback, void *pUserData) { pVoipGroup->pGroupCallback = pCallback; pVoipGroup->pGroupCallbackData = pUserData; VoipCommonSetEventCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerEventCallback, _VoipGroupManager_pRef); } /*F********************************************************************************/ /*! \Function VoipGroupSetDisplayTranscribedTextCallback \Description Sets the callback and userdata for the specified group \Input *pVoipGroup - VoipGroup to use \Input *pCallback - pointer to the callback to use \Input *pUserData - pointer to the user specified data \Version 05/03/2017 (mclouatre) */ /********************************************************************************F*/ void VoipGroupSetDisplayTranscribedTextCallback(VoipGroupRefT *pVoipGroup, VoipDisplayTranscribedTextCallbackT *pCallback, void *pUserData) { pVoipGroup->pDisplayTranscribedTextCb = pCallback; pVoipGroup->pDisplayTranscribedTextUserData = pUserData; VoipCommonSetDisplayTranscribedTextCallback((VoipCommonRefT *)VoipGetRef(), _VoipGroupManagerDisplayTranscribedTextCallback, _VoipGroupManager_pRef); } /*F********************************************************************************/ /*! \Function VoipGroupSetConnSharingEventCallback \Description Sets the connection sharing callback and userdata for the specified group \Input *pVoipGroup - voipgroup ref \Input *pCallback - pointer to the callback to use \Input *pUserData - pointer to the user specified data \Notes The callback registered with this function must invoke VoipGroupSuspend() when eCbType=VOIPGROUP_CBTYPE_CONNSUSPEND, and it must call VoipGroupResume() when eCbType=VOIPGROUP_CBTYPE_CONNRESUME. \Version 11/11/2009 (mclouatre) */ /********************************************************************************F*/ void VoipGroupSetConnSharingEventCallback(VoipGroupRefT *pVoipGroup, ConnSharingCallbackT *pCallback, void *pUserData) { pVoipGroup->pConnSharingCallback = pCallback; pVoipGroup->pConnSharingCallbackData = pUserData; } /*F********************************************************************************/ /*! \Function VoipGroupConnect \Description Set up a connection, using VoipGroup \Input *pVoipGroup - The VoipGroup the flags apply to \Input iConnId - connection index \Input uAddress - address to connect to \Input uManglePort - port to connect on \Input uGamePort - port to connect on \Input uClientId - client identifier \Input uSessionId - session identifier (optional) \Input bIsConnectivityHosted - is the connection through a relay server \Input uLowLevelConnectivityId - low level remote client id \Output int32_t - the VoipGroup connid the connection was made with \Version 11/03/2015 (amakoukji) */ /********************************************************************************F*/ int32_t VoipGroupConnect(VoipGroupRefT *pVoipGroup, int32_t iConnId, uint32_t uAddress, uint32_t uManglePort, uint32_t uGamePort, uint32_t uClientId, uint32_t uSessionId, uint8_t bIsConnectivityHosted, uint32_t uLowLevelConnectivityId) { int32_t iMappedLowLevelConnId = VOIP_CONNID_NONE; int32_t iHighLevelConnIdInUse = VOIP_CONNID_NONE; int32_t iCollidingConnId; VoipGroupRefT *pCollidingVoipGroup; uint8_t bMuted = FALSE; VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); NetPrintf(("voipgroup: [%p] connecting high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, uSessionId)); // is the specified client id already known by the current voipgroup? // note: typically this occurs when the caller perform successive calls to VoipGroupConnect() while // trying different port obtained from the demangler. if ((iHighLevelConnIdInUse = _VoipGroupFindUsedHighLevelConn(pVoipGroup, uClientId)) != VOIP_CONNID_NONE) { if (iConnId == VOIP_CONNID_NONE) { iConnId = iHighLevelConnIdInUse; } if (iHighLevelConnIdInUse == iConnId) { NetPrintf(("voipgroup: [%p] current voipgroup already knows about client id 0x%08x (low level id 0x%08x) with high-level conn id %d\n", pVoipGroup, uClientId, uLowLevelConnectivityId, iConnId)); // if connection is "suspended", early exit faking a success if (pVoipGroup->aConnections[iConnId].eConnState == VOIPGROUP_CONNSTATE_SUSPENDED) { NetPrintf(("voipgroup: [%p] high-level conn %d (sess id 0x%08x) is suspended, assume low-level connectivity is up\n", pVoipGroup, iConnId, pVoipGroup->aConnections[iConnId].uSessionId)); return(iConnId); } } else { NetPrintf(("voipgroup: [%p] critical error - current voipgroup already knows about client id x%08x (low level id 0x%08x) but conn ids do not match (%d != %d)\n", pVoipGroup, uClientId, uLowLevelConnectivityId, iHighLevelConnIdInUse, iConnId)); return(-4); } } // if the user doesn't specify a conn id, use the first unmapped one. if (iConnId == VOIP_CONNID_NONE) { if ((iConnId = _VoipGroupFindFreeHighLevelConn(pVoipGroup)) == VOIP_CONNID_NONE) { NetPrintf(("voipgroup: [%p] warning - voipgroup is full\n", pVoipGroup)); return(-1); } } // check for collision: verify if another voipgroup deals with the same client if ( (pCollidingVoipGroup = _VoipGroupFindCollision(pVoipGroup, uClientId, VOIPGROUP_CONNSTATE_ACTIVE, &iCollidingConnId)) != NULL ) { // if there is a collision check the mute status of the colliding voipgroup's on the connId so we can reset the status if we suspend bMuted = VoipGroupIsMutedByConnId(pCollidingVoipGroup, iCollidingConnId); /* If we are re-establishing voip from a hosted voip conn to a p2p voip conn, and if we know that the original hosted voip conn was resulting from a failing p2p voip conn, then we know this new targeted p2p voip conn will fail. Consequently, to avoid an unnecessary 10-sec p2p connection handshaking delay (which results in novoip between the users during that time because the original hosted voip connection gets suspended), we just return an error. The assumption being that blaze, upon detecting the connection failure, will immediately resume connectivity but hosted this time. */ if ((pCollidingVoipGroup->iCcMode == VOIPGROUP_CCMODE_HOSTEDFALLBACK) && pCollidingVoipGroup->aConnections[iCollidingConnId].bIsConnectivityHosted && !bIsConnectivityHosted) { NetPrintf(("voipgroup: [%p] failing VoipGroupConnect() because we anticipate the target p2p conn to fail... we instead prefer waiting for hosted conn to hosted conn transition\n", pVoipGroup)); return(-2); } // if both voipgroups do not share same bServer / bTunnel attributes // OR if the previous or current connection is connectivity hosted, // then connection re-establishment kicks-in if ( (pCollidingVoipGroup->bServer != pVoipGroup->bServer) || (pCollidingVoipGroup->bTunnel != pVoipGroup->bTunnel) || bIsConnectivityHosted || pCollidingVoipGroup->aConnections[iCollidingConnId].bIsConnectivityHosted) { /* Note: Voipgroups with bServer attribute set have precedence over voipgroups with bServer attribute not set, i.e. voip connectivity via voipserver is always preferred over p2p voip connectivity. Therefore, a newly created voip connection goes straight to "suspended state" if there already exists a voip connection via the voipserver for the same client. Or, if the newly created voip connection is to be established through the voipserver, then any existing p2p voip connection to the same client is first moved to "suspended" state, and only then the voip connection through the voipserver is established. */ // is the colliding voipgroup already using the voipserver? // note: bServer will never be the true for connectivity hosted connection if (pCollidingVoipGroup->bServer) { // no need to proceed with current connection establishment because the colliding voipgroup // already has voip connectivity via the voipserver, so it has precedence. just mark current // connection as "SUSPENDED" such that it can be resumed later if needed. pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_SUSPENDED; pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = VOIP_CONNID_NONE; pVoipGroup->aConnections[iConnId].uClientId = uClientId; pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId = uLowLevelConnectivityId; pVoipGroup->aConnections[iConnId].uSessionId = uSessionId; pVoipGroup->aConnections[iConnId].bIsConnectivityHosted = bIsConnectivityHosted; NetPrintf(("voipgroup: [%p] early suspension of high-level conn %d (sess id 0x%08x) - there already exists a low-level voip conn through the voipserver for client id 0x%08x (low level id 0x%08x).\n", pVoipGroup, iConnId, uSessionId, uClientId, uLowLevelConnectivityId)); return(iConnId); // fake success } else { // apply crossplay flag VoipCommonControl(pVoipCommon, 'xply', pVoipGroup->bCrossplay, NULL); // let the colliding voipgroup entity know that it has to suspend its connection right away // because the current voipgroup will be creating a new one with new connection parameters pCollidingVoipGroup->pConnSharingCallback(pCollidingVoipGroup, VOIPGROUP_CBTYPE_CONNSUSPEND, iCollidingConnId, pCollidingVoipGroup->pConnSharingCallbackData, FALSE, FALSE); } } else { // both voipgroups can share the underlying low-level voip connection iMappedLowLevelConnId = pCollidingVoipGroup->aConnections[iCollidingConnId].iMappedLowLevelConnId; // add session ID to the set of sessions sharing the voip connection VoipCommonConnectionSharingAddSession(pVoipCommon, iMappedLowLevelConnId, uSessionId); } } if (iMappedLowLevelConnId < 0) { // apply crossplay flag VoipCommonControl(pVoipCommon, 'xply', pVoipGroup->bCrossplay, NULL); // only call VoipCommonConnect() with a valid conn id if any of the following two condidions is met: // 1- VoipCommonConnect() was already called for that low-level connection (i.e iHighLevelConnIdInUse is valid) // 2- The high-level conn id is directly available in voip and there is currently no reference to it // (there is indeed a small transition time during which a connection may go to DISC state and a voip goup still refers to it) if ( (iHighLevelConnIdInUse != VOIP_CONNID_NONE) || (VoipStatus(VoipGetRef(), 'avlb', iConnId, NULL, 0) && !_VoipGroupLowLevelConnInUse(iConnId)) ) { // conn id is available, use it! iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, iConnId, uAddress, uManglePort, uGamePort, uLowLevelConnectivityId, uSessionId); } else { // else, let the voip module select a low-level voip conn id for us. iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, VOIP_CONNID_NONE, uAddress, uManglePort, uGamePort, uLowLevelConnectivityId, uSessionId); } } if (iMappedLowLevelConnId < 0) { NetPrintf(("voipgroup: [%p] VoipGroupConnect() failed obtaining a valid low-level conn id\n", pVoipGroup)); return(-3); } NetPrintf(("voipgroup: [%p] high-level conn id %d (sess id 0x%08x) mapped to low-level conn id %d --> address %a, clientId=0x%08x (low level id 0x%08x).\n", pVoipGroup, iConnId, uSessionId, iMappedLowLevelConnId, uAddress, uClientId, uLowLevelConnectivityId)); // set the local client id for the mapping VoipCommonSetLocalClientId(pVoipCommon, iMappedLowLevelConnId, pVoipGroup->aLowLevelConnectivityId[iConnId]); // save the mapping. pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = iMappedLowLevelConnId; if (pVoipGroup->bServer == TRUE || bIsConnectivityHosted == TRUE) { // let the voip module know about the local-to-voipserver conn id mapping VoipCommonMapVoipServerId(pVoipCommon, pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId, iConnId); } // save other connection-specific data pVoipGroup->aConnections[iConnId].uClientId = uClientId; pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId = uLowLevelConnectivityId; pVoipGroup->aConnections[iConnId].uSessionId = uSessionId; pVoipGroup->aConnections[iConnId].bIsConnectivityHosted = bIsConnectivityHosted; // if this is the first attempt to connect, let's just add ourself as an additional reference for this connection if ((iHighLevelConnIdInUse == VOIP_CONNID_NONE) && (_VoipGroupAddRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup) < 0)) { NetPrintf(("voipgroup: [%p] VoipGroupConnect() failed to add a new reference to the low-level conn %d\n", pVoipGroup, iMappedLowLevelConnId)); VoipGroupDisconnect(pVoipGroup, iConnId); return(-4); } // don't initialize the user masks if there was a collision since they'll already be set if (!bMuted) { _VoipGroupManager_pRef->uUserRecvMask |= (1 << iMappedLowLevelConnId); VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserRecvMask); _VoipGroupManager_pRef->uUserSendMask |= (1 << iMappedLowLevelConnId); VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserSendMask); } // return the high-level connection id used return(iConnId); } /*F********************************************************************************/ /*! \Function VoipGroupResume \Description resume a connection \Input *pVoipGroup - voipgroup ref \Input iConnId - connection index \Input uAddress - address to connect to \Input uManglePort - port to connect on \Input uGamePort - port to connect on \Input uClientId - client identifier \Input uSessionId - session identifier (optional) \Input bIsConnectivityHosted - TRUE is connection is connectivity hosted \Notes Muting logic is intentionally excluded from the resume. It is currently handled by ConnApi. \Version 01/26/2016 (amakoukji) */ /********************************************************************************F*/ void VoipGroupResume(VoipGroupRefT *pVoipGroup, int32_t iConnId, uint32_t uAddress, uint32_t uManglePort, uint32_t uGamePort, uint32_t uClientId, uint32_t uSessionId, uint8_t bIsConnectivityHosted) { int32_t iMappedLowLevelConnId = -1; VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); NetPrintf(("voipgroup: [%p] resuming high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, uSessionId)); #if DIRTYCODE_LOGGING if (pVoipGroup->aConnections[iConnId].uClientId != uClientId) { NetPrintf(("voipgroup: [%p] warning connection being resumed has inconsistent client id: 0x%08x should be 0x%08x\n", pVoipGroup, uClientId, pVoipGroup->aConnections[iConnId].uClientId)); } if (pVoipGroup->aConnections[iConnId].uSessionId != uSessionId) { NetPrintf(("voipgroup: [%p] warning connection being resumed has inconsistent session id: 0x%08x should be 0x%08x\n", pVoipGroup, uSessionId, pVoipGroup->aConnections[iConnId].uSessionId)); } #endif // force connection back to active state pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_ACTIVE; // check if the high-level conn id is directly available in voip and if there is currently no reference to it // (there is indeed a small transition time during which a connection may go to DISC state and a voip goup still refers to it) if (VoipStatus(VoipGetRef(), 'avlb', iConnId, NULL, 0) && !_VoipGroupLowLevelConnInUse(iConnId)) { // conn id is available, use it! iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, iConnId, uAddress, uManglePort, uGamePort, pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId, uSessionId); } else { // else, let Voip allocate one. iMappedLowLevelConnId = VoipCommonConnect(pVoipCommon, VOIP_CONNID_NONE, uAddress, uManglePort, uGamePort, pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId, uSessionId); } if (iMappedLowLevelConnId < 0) { NetPrintf(("voipgroup: [%p] VoipGroupResume() failed obtaining a valid low-level conn id\n", pVoipGroup)); return; } NetPrintf(("voipgroup: [%p] high-level conn id %d (sess id 0x%08x) mapped to low-level conn id %d --> address %a, clientId=0x%08x (low level id 0x%08x).\n", pVoipGroup, iConnId, uSessionId, iMappedLowLevelConnId, uAddress, uClientId, pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId)); // set the local client id for the mapping VoipCommonSetLocalClientId(pVoipCommon, iMappedLowLevelConnId, pVoipGroup->aLowLevelConnectivityId[iConnId]); // save the mapping. pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = iMappedLowLevelConnId; if (pVoipGroup->bServer == TRUE || bIsConnectivityHosted == TRUE) { // let the voip module know about the local-to-voipserver conn id mapping VoipCommonMapVoipServerId(pVoipCommon, pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId, iConnId); } // let's just add ourself as an additional reference for this connection if ((_VoipGroupAddRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup)) < 0) { NetPrintf(("voipgroup: [%p] VoipGroupResume() failed adding a new reference\n", pVoipGroup)); return; } return; } /*F********************************************************************************/ /*! \Function VoipGroupDisconnect \Description Teardown a connection, using VoipGroups. \Input *pVoipGroup - The VoipGroup the flags apply to \Input iConnId - connection index \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ void VoipGroupDisconnect(VoipGroupRefT *pVoipGroup, int32_t iConnId) { int32_t iCollidingConnId; int32_t iMappedLowLevelConnId; VoipGroupRefT *pCollidingVoipGroup; VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); NetPrintf(("voipgroup: [%p] disconnecting high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, pVoipGroup->aConnections[iConnId].uSessionId)); // if high-level connection is already suspended, skip disconnection and just clean up corresponding data in the group if (pVoipGroup->aConnections[iConnId].eConnState != VOIPGROUP_CONNSTATE_SUSPENDED) { iMappedLowLevelConnId = _VoipGroupMatch(pVoipGroup, iConnId); if (iMappedLowLevelConnId != VOIP_CONNID_NONE) { // remove voipgroup from low-level connection's reference set _VoipGroupRemoveRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup); // only proceed if the ref count for this low-level conn is 0 if (!_VoipGroupLowLevelConnInUse(iMappedLowLevelConnId)) { uint8_t bMuted = VoipGroupIsMutedByConnId(pVoipGroup, iConnId); VoipCommonDisconnect(pVoipCommon, iMappedLowLevelConnId, TRUE); _VoipGroupManager_pRef->uUserRecvMask &= ~(1 << iMappedLowLevelConnId); VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserRecvMask); _VoipGroupManager_pRef->uUserSendMask &= ~(1 << iMappedLowLevelConnId); VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserSendMask); // scan other voipgroups and resume any suspended connection to the same client id if ( (pCollidingVoipGroup = _VoipGroupFindCollision(pVoipGroup, pVoipGroup->aConnections[iConnId].uClientId, VOIPGROUP_CONNSTATE_SUSPENDED, &iCollidingConnId)) != NULL ) { // apply colliding voip group crossplay flag VoipCommonControl(pVoipCommon, 'xply', pCollidingVoipGroup->bCrossplay, NULL); // let colliding voipgroup entity that it can resume its connection right away as the current // voipgroup has just disconnected a connection with different connection parameters pCollidingVoipGroup->pConnSharingCallback(pCollidingVoipGroup, VOIPGROUP_CBTYPE_CONNRESUME, iCollidingConnId, pCollidingVoipGroup->pConnSharingCallbackData, bMuted, bMuted); } } else { // remove session ID from the set of sessions sharing the low-level voip connection // note: don't call this function earlier in time (before VoipCommonDisconnect) because the session id is needed for building the disconnect message VoipCommonConnectionSharingDelSession(pVoipCommon, iMappedLowLevelConnId, pVoipGroup->aConnections[iConnId].uSessionId); NetPrintf(("voipgroup: [%p] skipped call to VoipCommonDisconnect() because low-level conn ref count has not reached 0\n", pVoipGroup)); } pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = VOIP_CONNID_NONE; } else { NetPrintf(("voipgroup: [%p] warning - VoipGroupDisconnect() dealing with a corrupted connection entry\n", pVoipGroup)); } } else { NetPrintf(("voipgroup: [%p] skipped call to VoipCommonDisconnect() because high-level conn %d is suspended\n", pVoipGroup, iConnId)); } // free the high-level connection entry pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_ACTIVE; pVoipGroup->aConnections[iConnId].uClientId = 0; pVoipGroup->aConnections[iConnId].uLowLevelConnectivityId = 0; pVoipGroup->aConnections[iConnId].uSessionId = 0; pVoipGroup->aConnections[iConnId].bIsConnectivityHosted = FALSE; return; } /*F********************************************************************************/ /*! \Function VoipGroupSuspend \Description suspend a connection \Input *pVoipGroup - voipgroup ref \Input iConnId - connection index \Version 11/11/2009 (mclouatre) */ /********************************************************************************F*/ void VoipGroupSuspend(VoipGroupRefT *pVoipGroup, int32_t iConnId) { int32_t iMappedLowLevelConnId; VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); NetPrintf(("voipgroup: [%p] suspending high-level conn %d (sess id 0x%08x)\n", pVoipGroup, iConnId, pVoipGroup->aConnections[iConnId].uSessionId)); // force connection state back to suspended iMappedLowLevelConnId = _VoipGroupMatch(pVoipGroup, iConnId); if (iMappedLowLevelConnId != VOIP_CONNID_NONE) { pVoipGroup->aConnections[iConnId].eConnState = VOIPGROUP_CONNSTATE_SUSPENDED; // remove voipgroup from low-level connection's reference set _VoipGroupRemoveRefToLowLevelConn(iMappedLowLevelConnId, pVoipGroup); if (_VoipGroupLowLevelConnInUse(iMappedLowLevelConnId)) { /* note: This scenario can occur if the low-level connection has already been disconnected by the other peer. In such a case, the voipgroup is not aware of the state of the low-level connection, and it still maintains the high-level connection entry as the corresponding connapi client is maintained in DISC state by the upper ConnApi layer. Now when asked to go to suspended state because of a conflict with another newly created voipgroup, it is very possible that the low-level connection has already been re-used and has other voipgroups referring to it. */ NetPrintf(("voipgroup: [%p] VoipGroupSuspend() skips call to VoipCommonDisconnect() because low-level conn ref count has not reached 0\n", pVoipGroup)); } else { /* note: bSendDiscMsg is FALSE here because we don't want the voip disc pkt to result in the other end of the connection having its connapi voip connection status moved to CONNAPI_STATUS_DISC state. */ VoipCommonDisconnect(pVoipCommon, iMappedLowLevelConnId, FALSE); _VoipGroupManager_pRef->uUserRecvMask &= ~(1 << iMappedLowLevelConnId); VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserRecvMask); _VoipGroupManager_pRef->uUserSendMask &= ~(1 << iMappedLowLevelConnId); VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : _VoipGroupManager_pRef->uUserSendMask); pVoipGroup->aConnections[iConnId].iMappedLowLevelConnId = VOIP_CONNID_NONE; } } else { NetPrintf(("voipgroup: [%p] low level conn id not found\n", pVoipGroup)); } return; } /*F********************************************************************************/ /*! \Function VoipGroupMuteByClientId3 \Description mutes a connection by the client id and mute flag \Input uClientId - client id \Input bMute - TRUE to mute FALSE to umute \Input uMuteFlag - mute flags (VOIPGROUP_INBOUND_MUTE, VOIPGROUP_OUTBOUND_MUTE, VOIPGROUP_TWOWAY_MUTE) \Output int32_t - negative=error, zero=success \Version 10/30/2017 (amakoukji) */ /********************************************************************************F*/ int32_t VoipGroupMuteByClientId3(uint32_t uClientId, uint8_t bMute, uint32_t uMuteFlag) { VoipGroupManagerT* pManager = _VoipGroupManager_pRef; VoipGroupRefT *pVoipGroup = NULL; int32_t iConnId = MAX_CLIENTS_PER_GROUP; int32_t iGroupIndex; if (uClientId == (uint32_t)VOIP_CONNID_ALL) { iConnId = (int32_t)VOIP_CONNID_ALL; } else { for (iGroupIndex = 0; iGroupIndex < pManager->iMaxGroups; ++iGroupIndex) { pVoipGroup = &pManager->aGroups[iGroupIndex]; // look for the client in this voip group if (pVoipGroup->bUsed) { for (iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) { break; } } // found a match in the inner for-loop, stop looking if (iConnId != MAX_CLIENTS_PER_GROUP) { break; } } } // if the client is not found in any voip group return an error if ((iGroupIndex == pManager->iMaxGroups) && (iConnId == MAX_CLIENTS_PER_GROUP)) { return(-1); } } return(VoipGroupMuteByConnId2(pVoipGroup, iConnId, bMute, uMuteFlag)); } /*F********************************************************************************/ /*! \Function VoipGroupMuteByClientId2 \Description mutes a connection by the client id and mute flag \Input *pVoipGroup - voipgroup ref \Input uClientId - client id \Input bMute - TRUE to mute FALSE to umute \Input uMuteFlag - mute flags (VOIPGROUP_INBOUND_MUTE, VOIPGROUP_OUTBOUND_MUTE, VOIPGROUP_TWOWAY_MUTE) \Output int32_t - negative=error, zero=success \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ int32_t VoipGroupMuteByClientId2(VoipGroupRefT *pVoipGroup, uint32_t uClientId, uint8_t bMute, uint32_t uMuteFlag) { int32_t iConnId; if (uClientId == (uint32_t)VOIP_CONNID_ALL) { iConnId = (int32_t)VOIP_CONNID_ALL; } else { for (iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) { break; } } if (iConnId == MAX_CLIENTS_PER_GROUP) { return(-1); } } return(VoipGroupMuteByConnId2(pVoipGroup, iConnId, bMute, uMuteFlag)); } /*F********************************************************************************/ /*! \Function VoipGroupMuteByClientId \Description 2-way mutes a connection by the client id \Input *pVoipGroup - voipgroup ref \Input iClientId - client id \Input uMute - TRUE to mute FALSE to umute \Output int32_t - negative=error, zero=success \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ int32_t VoipGroupMuteByClientId(VoipGroupRefT *pVoipGroup, uint32_t iClientId, uint8_t uMute) { return(VoipGroupMuteByClientId2(pVoipGroup, iClientId, uMute, VOIPGROUP_TWOWAY_MUTE)); } /*F********************************************************************************/ /*! \Function VoipGroupMuteByConnId2 \Description mutes a connection by the high level connection id and mute flag \Input *pVoipGroup - voipgroup ref \Input iConnId - connection index \Input bMute - TRUE to mute FALSE to umute \Input uMuteFlag - mute flags (VOIPGROUP_INBOUND_MUTE, VOIPGROUP_OUTBOUND_MUTE, VOIPGROUP_TWOWAY_MUTE) \Output int32_t - negative=error, zero=success \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ int32_t VoipGroupMuteByConnId2(VoipGroupRefT *pVoipGroup, uint32_t iConnId, uint8_t bMute, uint32_t uMuteFlag) { VoipGroupManagerT* pManager = _VoipGroupManager_pRef; VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); int32_t iMappedLowLevelConnId; int32_t iIndex; iMappedLowLevelConnId = ((iConnId == (uint32_t)VOIP_CONNID_ALL) ? (int32_t)VOIP_CONNID_ALL : _VoipGroupMatch(pVoipGroup, iConnId)); if (iMappedLowLevelConnId == (int32_t)VOIP_CONNID_NONE) { return(-1); } if (bMute) { if (iMappedLowLevelConnId == (int32_t)VOIP_CONNID_ALL) { // mute all if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) { pManager->uUserRecvMask = 0x00000000; } if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) { pManager->uUserSendMask = 0x00000000; } } else { if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) { pManager->uUserRecvMask &= ~(1 << iMappedLowLevelConnId); } if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) { pManager->uUserSendMask &= ~(1 << iMappedLowLevelConnId); } } } else { if (iMappedLowLevelConnId == VOIP_CONNID_ALL) { // unmute all connIds for (iIndex = 0; iIndex < MAX_CLIENTS_PER_GROUP; ++iIndex) { if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) { pManager->uUserRecvMask |= (1 << pVoipGroup->aConnections[iIndex].iMappedLowLevelConnId); } if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) { pManager->uUserSendMask |= (1 << pVoipGroup->aConnections[iIndex].iMappedLowLevelConnId); } } } else { if (uMuteFlag & VOIPGROUP_INBOUND_MUTE) { pManager->uUserRecvMask |= (1 << iMappedLowLevelConnId); } if (uMuteFlag & VOIPGROUP_OUTBOUND_MUTE) { pManager->uUserSendMask |= (1 << iMappedLowLevelConnId); } } } VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserRecvMask); VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserSendMask); return(0); } /*F********************************************************************************/ /*! \Function VoipGroupMuteByConnId \Description 2-way mutes a connection by the high level connection id \Input *pVoipGroup - voipgroup ref \Input iConnId - connection index \Input uMute - TRUE to mute FALSE to umute \Output int32_t - negative=error, zero=success \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ int32_t VoipGroupMuteByConnId(VoipGroupRefT *pVoipGroup, uint32_t iConnId, uint8_t uMute) { return (VoipGroupMuteByConnId2(pVoipGroup, iConnId, uMute, VOIPGROUP_TWOWAY_MUTE)); } /*F********************************************************************************/ /*! \Function VoipGroupIsMutedByClientId2 \Description reutrns the mute flag for the connection specified by the client id \Input *pVoipGroup - voipgroup ref \Input iClientId - connection index \Output uint8_t - mute flag \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ uint32_t VoipGroupIsMutedByClientId2(VoipGroupRefT *pVoipGroup, uint32_t iClientId) { int32_t iConnId; for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { if (pVoipGroup->aConnections[iConnId].uClientId == iClientId) { break; } } if (iConnId == MAX_CLIENTS_PER_GROUP) { return(FALSE); } return(VoipGroupIsMutedByConnId2(pVoipGroup, iConnId)); } /*F********************************************************************************/ /*! \Function VoipGroupIsMutedByClientId \Description Is the connection specified by the client id muted or not (1-way or 2-way) \Input *pVoipGroup - voipgroup ref \Input uClientId - connection index \Output int32_t - TRUE only if muted 2-ways \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ uint8_t VoipGroupIsMutedByClientId(VoipGroupRefT *pVoipGroup, uint32_t uClientId) { int32_t iConnId; for(iConnId = 0; iConnId < MAX_CLIENTS_PER_GROUP; iConnId++) { if (pVoipGroup->aConnections[iConnId].uClientId == uClientId) { break; } } if (iConnId == MAX_CLIENTS_PER_GROUP) { return(FALSE); } return(VoipGroupIsMutedByConnId(pVoipGroup, iConnId)); } /*F********************************************************************************/ /*! \Function VoipGroupIsMutedByConnId2 \Description reutrns the mute flag for the connection specified by the high level connection id \Input *pVoipGroup - voipgroup ref \Input iConnId - connection index \Output uint32_t - mute flag \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ uint32_t VoipGroupIsMutedByConnId2(VoipGroupRefT *pVoipGroup, uint32_t iConnId) { uint8_t uMuteFlag = 0; int32_t iMappedLowLevelConnId; VoipGroupManagerT* pManager = _VoipGroupManager_pRef; iMappedLowLevelConnId = _VoipGroupMatch(pVoipGroup, iConnId); if (iMappedLowLevelConnId == VOIP_CONNID_NONE) { return(0); } if ((pManager->uUserRecvMask & (1 << iMappedLowLevelConnId)) == 0) { uMuteFlag |= VOIPGROUP_INBOUND_MUTE; } if ((pManager->uUserSendMask & (1 << iMappedLowLevelConnId)) == 0) { uMuteFlag |= VOIPGROUP_OUTBOUND_MUTE; } return(uMuteFlag); } /*F********************************************************************************/ /*! \Function VoipGroupIsMutedByConnId \Description Is the connection specified by the conn id muted or not (1-way or 2-way) \Input *pVoipGroup - voipgroup ref \Input iConnId - connection index \Output int32_t - TRUE only if it is 2-way muted \Version 11/02/2015 (tcho) */ /********************************************************************************F*/ uint8_t VoipGroupIsMutedByConnId(VoipGroupRefT *pVoipGroup, uint32_t iConnId) { uint32_t uMuteFlag = VoipGroupIsMutedByConnId2(pVoipGroup, iConnId); if (uMuteFlag == VOIPGROUP_TWOWAY_MUTE) { return(TRUE); } else { return(FALSE); } } /*F********************************************************************************/ /*! \Function VoipGroupMuteAll \Description Mute all users or restore previous mute settings from before a previous call to VoipGroupMuteAll. This stacks with other VoipGroupMute functionality. It does not erase the previous muting state, and therefore is best used for short term muting of all users. \Input *pVoipGroup - voipgroup ref \Input bActive - TRUE to mute all users, FALSE to restore mute settings \Version 10/30/2017 (amakoukji) */ /********************************************************************************F*/ void VoipGroupMuteAll(VoipGroupRefT *pVoipGroup, uint8_t bActive) { VoipGroupManagerT* pManager = _VoipGroupManager_pRef; VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); if (pVoipGroup->bSilent != bActive) { pVoipGroup->bSilent = bActive; VoipCommonSpeaker(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserRecvMask); VoipCommonMicrophone(pVoipCommon, pVoipGroup->bSilent ? 0x00000000 : pManager->uUserSendMask); } } /*F********************************************************************************/ /*! \Function VoipGroupConnStatus \Description Passes through to VoipConnRemote, afer adjusting connection ID \Input *pVoipGroup - The VoipGroup \Input iConnId - The connection ID \Output int32_t - VOIP_REMOTE_* flags, or VOIP_FLAG_INVALID if iConnId is invalid \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ int32_t VoipGroupConnStatus(VoipGroupRefT *pVoipGroup, int32_t iConnId) { int32_t iMappedConnId = _VoipGroupMatch(pVoipGroup, iConnId); if (iMappedConnId == VOIP_CONNID_NONE) { return(VOIP_FLAG_INVALID); } return(VoipCommonConnStatus((VoipCommonRefT *)VoipGetRef(), iMappedConnId)); } /*F********************************************************************************/ /*! \Function VoipGroupRemoteUserStatus \Description Passes through to VoipUserRemote, afer adjusting connection ID \Input *pVoipGroup - The VoipGroup \Input iConnId - The connection ID \Input iRemoteUserIndex - User index \Output int32_t - VOIP_REMOTE_* flags, or VOIP_FLAG_INVALID if iConnId is invalid \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ int32_t VoipGroupRemoteUserStatus(VoipGroupRefT *pVoipGroup, int32_t iConnId, int32_t iRemoteUserIndex) { int32_t iMappedConnId = _VoipGroupMatch(pVoipGroup, iConnId); if (iMappedConnId == VOIP_CONNID_NONE) { return(VOIP_FLAG_INVALID); } return(VoipCommonRemoteUserStatus((VoipCommonRefT *)VoipGetRef(), iMappedConnId, iRemoteUserIndex)); } /*F********************************************************************************/ /*! \Function VoipGroupLocalUserStatus \Description return information about local hardware state \Input *pVoipGroup - The VoipGroup \Input iLocalUserIndex - User index \Output int32_t - VOIP_LOCAL_* flags \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ int32_t VoipGroupLocalUserStatus(VoipGroupRefT *pVoipGroup, int32_t iLocalUserIndex) { return(VoipLocalUserStatus(VoipGetRef(), iLocalUserIndex)); } /*F********************************************************************************/ /*! \Function VoipGroupStatus \Description Passes through to VoipStatus, for historical reasons \Input *pVoipGroup - The VoipGroup \Input iSelect - status selector \Input iValue - selector-specific \Input *pBuf - selector-specific \Input iBufSize - size of the pBuf input \Output int32_t - selector-specific data \Notes iSelect can be one of the following: \verbatim rchn: returns the active channel of the remote peer rcvu: returns the voipuser of the remote peer slnt: returns if the group is silent \endverbatim \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ int32_t VoipGroupStatus(VoipGroupRefT *pVoipGroup, int32_t iSelect, int32_t iValue, void *pBuf, int32_t iBufSize) { int32_t iRetCode = -1; // default to error if (iSelect == 'rchn') { // convert high-level id to low-level id int32_t iMappedConnId = _VoipGroupMatch(pVoipGroup, iValue); if (iMappedConnId != VOIP_CONNID_NONE) { iRetCode = VoipStatus(VoipGetRef(), iSelect, iMappedConnId, pBuf, iBufSize); } } else if (iSelect == 'rcvu') { int32_t iMappedConnId; int32_t iHighLevelConnId = iValue & 0xFFFF; int32_t iRemoteUserIndex = iValue >> 16; // convert high-level id to low-level id iMappedConnId = _VoipGroupMatch(pVoipGroup, iHighLevelConnId); if (iMappedConnId != VOIP_CONNID_NONE) { // rebuild input parameter with converted conn index iValue = iMappedConnId; iValue |= (iRemoteUserIndex << 16); iRetCode = VoipStatus(VoipGetRef(), iSelect, iValue, pBuf, iBufSize); } } else if (iSelect == 'slnt') { iRetCode = pVoipGroup->bSilent; } else { iRetCode = VoipStatus(VoipGetRef(), iSelect, iValue, pBuf, iBufSize); } return(iRetCode); } /*F********************************************************************************/ /*! \Function VoipGroupControl \Description Passes through to VoipControl, afer adjusting connection ID and flags. \Input *pVoipGroup - The VoipGroup \Input iControl - status selector \Input iValue - selector-specific \Input iValue2 - selector-specific \Input *pValue - selector-specific \Output int32_t - selector-specific data \Notes iControl can be one of the following: \verbatim ccmd: set the CC mode (VOIPGROUP_CCMODE_*) lusr: register local voip user serv: flag voipgroup as using voipserver tunl: flag voipgroup as using tunnel vcid: set the local id (pValue) for the voip connection with client (iValue) \endverbatim \Version 01/31/2007 (jrainy) */ /********************************************************************************F*/ int32_t VoipGroupControl(VoipGroupRefT *pVoipGroup, int32_t iControl, int32_t iValue, int32_t iValue2, void *pValue) { VoipGroupManagerT *pManager = _VoipGroupManager_pRef; if (iControl == 'ccmd') { #if DIRTYCODE_LOGGING static const char *_VoipGroupCcModeNames[] = { "VOIPGROUP_CCMODE_PEERONLY", "VOIPGROUP_CCMODE_HOSTEDONLY", "VOIPGROUP_CCMODE_HOSTEDFALLBACK" }; #endif NetPrintf(("voipgroup: [%p] CC mode = %d (%s)\n", pVoipGroup, iValue, _VoipGroupCcModeNames[iValue])); pVoipGroup->iCcMode = iValue; return(0); } if (iControl == 'lusr') { if (VoipGetRef() == NULL) { return(-1); } // only proceed with performing the operation for the first group created or // for the last group being destroyed as groups cannot have different voip // local users. if (pManager->iNumGroups != 1) { NetPrintf(("voipgroup: [%p] skipping %s of voip local user and %d, because multiple voipgroups exist\n", pVoipGroup, (iValue2 ? "registration" : "unregistration"), iValue)); return(-1); } VoipSetLocalUser(VoipGetRef(), iValue, iValue2); return(0); } if (iControl == 'serv') { pVoipGroup->bServer = iValue; return(0); } if (iControl == 'tunl') { pVoipGroup->bTunnel = iValue; return(0); } if (iControl == 'vcid') { NetPrintf(("voipgroup: [%p] set local client id 0x%08x for conn id %d\n", pVoipGroup, *(int32_t*)pValue, iValue)); pVoipGroup->aLowLevelConnectivityId[iValue] = *(uint32_t*)pValue; return(0); } if (iControl == 'xply') { pVoipGroup->bCrossplay = iValue ? TRUE : FALSE; return(0); } if (VoipGetRef() != NULL) { return(VoipControl(VoipGetRef(), iControl, iValue, pValue)); } return(-1); } /*F********************************************************************************/ /*! \Function VoipGroupActivateLocalUser \Description Refcounted access to VoipCommonActivateLocalUser() \Input *pVoipGroup - The VoipGroup (if voipgroup is null and bActivate is false we will deactivate the user from all the voipgroup) \Input iLocalUserIndex - local user index \Input bActivate - TRUE to activate, FALSE to deactivate \Version 05/12/2014 (mclouatre) */ /********************************************************************************F*/ void VoipGroupActivateLocalUser(VoipGroupRefT *pVoipGroup, int32_t iLocalUserIndex, uint8_t bActivate) { VoipCommonRefT *pVoipCommon = (VoipCommonRefT *)VoipGetRef(); if ((iLocalUserIndex >= 0) && (iLocalUserIndex < VOIP_MAXLOCALUSERS)) { if (bActivate && (pVoipGroup != NULL)) { // sanity check if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] < 0) { NetPrintf(("voipgroup: [%p] warning, VoipGroupActivateLocalUser invalid ref count for user %d, %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] = 0; } // increment ref count ++(_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex]); pVoipGroup->aParticipatingUser[iLocalUserIndex] = TRUE; NetPrintf(("voipgroup: [%p] VoipGroupActivateLocalUser incrementing ref count for user %d to %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); // activate underlying voip ref if required if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] <= 1) { VoipCommonActivateLocalUser(pVoipCommon, iLocalUserIndex, bActivate); } } else if (_VoipGroupManager_pRef != NULL) { // sanity check if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] <= 0) { NetPrintf(("voipgroup: [%p] warning, deactivating local user %d with ref count %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] = 0; } else { if (pVoipGroup == NULL) { int32_t iGroupIndex = 0; VoipGroupManagerT *pVoipGroupManager = _VoipGroupManager_pRef; for (iGroupIndex = 0; iGroupIndex < pVoipGroupManager->iMaxGroups; ++iGroupIndex) { if (pVoipGroupManager->aGroups[iGroupIndex].aParticipatingUser[iLocalUserIndex] == TRUE) { // decrement ref count --(pVoipGroupManager->aActiveRefCount[iLocalUserIndex]); pVoipGroupManager->aGroups[iGroupIndex].aParticipatingUser[iLocalUserIndex] = FALSE; } } NetPrintf(("voipgroup: VoipGroupActivateLocalUser deactivating user %d from all voipgroups\n", iLocalUserIndex)); } else { if (pVoipGroup->aParticipatingUser[iLocalUserIndex] == TRUE) { // decrement ref count --(_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex]); pVoipGroup->aParticipatingUser[iLocalUserIndex] = FALSE; NetPrintf(("voipgroup: [%p] VoipGroupActivateLocalUser decrementing ref count for user %d to %d\n", pVoipGroup, iLocalUserIndex, _VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex])); } } // deactivate underlying voip ref if required if (_VoipGroupManager_pRef->aActiveRefCount[iLocalUserIndex] <= 0) { VoipCommonActivateLocalUser(pVoipCommon, iLocalUserIndex, bActivate); } } } } }