1611 lines
49 KiB
C
1611 lines
49 KiB
C
/****************************************************************************
|
|
*
|
|
* $Archive: S:/STURGEON/SRC/CALLCONT/VCS/confman.c_v $
|
|
*
|
|
* INTEL Corporation Prorietary Information
|
|
*
|
|
* This listing is supplied under the terms of a license agreement
|
|
* with INTEL Corporation and may not be copied nor disclosed except
|
|
* in accordance with the terms of that agreement.
|
|
*
|
|
* Copyright (c) 1993-1994 Intel Corporation.
|
|
*
|
|
* $Revision: 1.91 $
|
|
* $Date: 04 Mar 1997 17:35:06 $
|
|
* $Author: MANDREWS $
|
|
*
|
|
* Deliverable:
|
|
*
|
|
* Abstract:
|
|
*
|
|
*
|
|
* Notes:
|
|
*
|
|
***************************************************************************/
|
|
|
|
#pragma warning ( disable : 4115 4201 4214 )
|
|
#include <nt.h>
|
|
#include <ntrtl.h>
|
|
#include <nturtl.h>
|
|
#include <windows.h>
|
|
#pragma warning ( default : 4115 4201 4214 )
|
|
#include "apierror.h"
|
|
#include "incommon.h"
|
|
#include "callcont.h"
|
|
#include "q931.h"
|
|
#include "ccmain.h"
|
|
#include "confman.h"
|
|
#include "ccutils.h"
|
|
#include "chanman.h"
|
|
#include "callman.h"
|
|
|
|
|
|
static BOOL bConferenceInited = FALSE;
|
|
|
|
static struct {
|
|
PCONFERENCE pHead;
|
|
LOCK Lock;
|
|
} ConferenceTable;
|
|
|
|
static struct {
|
|
CC_HCONFERENCE hConference;
|
|
LOCK Lock;
|
|
} ConferenceHandle;
|
|
|
|
|
|
CC_CONFERENCEID InvalidConferenceID;
|
|
|
|
|
|
|
|
HRESULT InitConferenceManager()
|
|
{
|
|
ASSERT(bConferenceInited == FALSE);
|
|
|
|
ConferenceTable.pHead = NULL;
|
|
InitializeLock(&ConferenceTable.Lock);
|
|
|
|
ConferenceHandle.hConference = CC_INVALID_HANDLE + 1;
|
|
InitializeLock(&ConferenceHandle.Lock);
|
|
|
|
memset(&InvalidConferenceID, 0, sizeof(InvalidConferenceID));
|
|
|
|
bConferenceInited = TRUE;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT DeInitConferenceManager()
|
|
{
|
|
PCONFERENCE pConference;
|
|
PCONFERENCE pNextConference;
|
|
|
|
if (bConferenceInited == FALSE)
|
|
return CC_OK;
|
|
|
|
pConference = ConferenceTable.pHead;
|
|
while (pConference != NULL) {
|
|
AcquireLock(&pConference->Lock);
|
|
pNextConference = pConference->pNextInTable;
|
|
FreeConference(pConference);
|
|
pConference = pNextConference;
|
|
}
|
|
|
|
DeleteLock(&ConferenceHandle.Lock);
|
|
DeleteLock(&ConferenceTable.Lock);
|
|
bConferenceInited = FALSE;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT _CreateLocalH245H2250MuxCapability(
|
|
PCONFERENCE pConference)
|
|
{
|
|
HRESULT status;
|
|
CC_TERMCAP TermCap;
|
|
struct MultipointCapability_mediaDistributionCapability RXMediaDistributionCapability;
|
|
struct MultipointCapability_mediaDistributionCapability TXMediaDistributionCapability;
|
|
struct MultipointCapability_mediaDistributionCapability RXTXMediaDistributionCapability;
|
|
|
|
ASSERT(pConference != NULL);
|
|
|
|
if (pConference->pLocalH245H2250MuxCapability != NULL)
|
|
H245FreeCap(pConference->pLocalH245H2250MuxCapability);
|
|
|
|
TermCap.Dir = H245_CAPDIR_LCLRXTX;
|
|
TermCap.DataType = H245_DATA_MUX;
|
|
TermCap.ClientType = H245_CLIENT_MUX_H2250;
|
|
TermCap.CapId = 0; // CapId = 0 is a special case for mux capabilities
|
|
TermCap.Cap.H245Mux_H2250.maximumAudioDelayJitter = 60;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.multicastCapability = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.multiUniCastConference = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability = &RXMediaDistributionCapability;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->next = NULL;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.bit_mask = 0;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.centralizedControl = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.distributedControl = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.centralizedAudio = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.distributedAudio = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.centralizedVideo = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.distributedVideo = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.centralizedData = NULL;
|
|
TermCap.Cap.H245Mux_H2250.receiveMultipointCapability.mediaDistributionCapability->value.distributedData = NULL;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.multicastCapability = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.multiUniCastConference = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability = &TXMediaDistributionCapability;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->next = NULL;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.bit_mask = 0;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.centralizedControl = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.distributedControl = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.centralizedAudio = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.distributedAudio = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.centralizedVideo = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.distributedVideo = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.centralizedData = NULL;
|
|
TermCap.Cap.H245Mux_H2250.transmitMultipointCapability.mediaDistributionCapability->value.distributedData = NULL;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.multicastCapability = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.multiUniCastConference = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability = &RXTXMediaDistributionCapability;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->next = NULL;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.bit_mask = 0;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.centralizedControl = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.distributedControl = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.centralizedAudio = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.distributedAudio = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.centralizedVideo = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.distributedVideo = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.centralizedData = NULL;
|
|
TermCap.Cap.H245Mux_H2250.rcvAndTrnsmtMltpntCpblty.mediaDistributionCapability->value.distributedData = NULL;
|
|
TermCap.Cap.H245Mux_H2250.mcCapability.centralizedConferenceMC = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.mcCapability.decentralizedConferenceMC = (char)pConference->bMultipointCapable;
|
|
TermCap.Cap.H245Mux_H2250.rtcpVideoControlCapability = FALSE;
|
|
TermCap.Cap.H245Mux_H2250.mediaPacketizationCapability.h261aVideoPacketization = FALSE;
|
|
|
|
status = H245CopyCap(&pConference->pLocalH245H2250MuxCapability,
|
|
&TermCap);
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
HRESULT _AddConferenceToTable( PCONFERENCE pConference)
|
|
{
|
|
PCONFERENCE pCurrent;
|
|
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(pConference->hConference != CC_INVALID_HANDLE);
|
|
ASSERT(pConference->bInTable == FALSE);
|
|
|
|
AcquireLock(&ConferenceTable.Lock);
|
|
|
|
// If a valid non-zero conference ID was specified, make sure
|
|
// there's not a duplicate in the conference table
|
|
if (!EqualConferenceIDs(&pConference->ConferenceID, &InvalidConferenceID)) {
|
|
pCurrent = ConferenceTable.pHead;
|
|
while (pCurrent != NULL) {
|
|
if (EqualConferenceIDs(&pCurrent->ConferenceID,
|
|
&pConference->ConferenceID)) {
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
return CC_DUPLICATE_CONFERENCE_ID;
|
|
}
|
|
pCurrent = pCurrent->pNextInTable;
|
|
}
|
|
}
|
|
|
|
pConference->pNextInTable = ConferenceTable.pHead;
|
|
pConference->pPrevInTable = NULL;
|
|
if (ConferenceTable.pHead != NULL)
|
|
ConferenceTable.pHead->pPrevInTable = pConference;
|
|
ConferenceTable.pHead = pConference;
|
|
|
|
pConference->bInTable = TRUE;
|
|
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT _RemoveConferenceFromTable( PCONFERENCE pConference)
|
|
{
|
|
CC_HCONFERENCE hConference;
|
|
BOOL bTimedOut;
|
|
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(pConference->bInTable == TRUE);
|
|
|
|
// Caller must have a lock on the conference object;
|
|
// in order to avoid deadlock, we must:
|
|
// 1. unlock the conference object,
|
|
// 2. lock the ConferenceTable,
|
|
// 3. locate the conference object in the ConferenceTable (note that
|
|
// after step 2, the conference object may be deleted from the
|
|
// ConferenceTable by another thread),
|
|
// 4. lock the conference object (someone else may have the lock)
|
|
// 5. remove the conference object from the ConferenceTable,
|
|
// 6. unlock the ConferenceTable
|
|
//
|
|
// The caller can now safely unlock and destroy the conference object,
|
|
// since no other thread will be able to find the object (its been
|
|
// removed from the ConferenceTable), and therefore no other thread will
|
|
// be able to lock it.
|
|
|
|
// Save the conference handle; its the only way to look up
|
|
// the conference object in the ConferenceTable. Note that we
|
|
// can't use pConference to find the conference object, since
|
|
// pConference may be free'd up, and another conference object
|
|
// allocated at the same address
|
|
hConference = pConference->hConference;
|
|
|
|
// step 1
|
|
RelinquishLock(&pConference->Lock);
|
|
|
|
step2:
|
|
// step 2
|
|
AcquireLock(&ConferenceTable.Lock);
|
|
|
|
// step 3
|
|
pConference = ConferenceTable.pHead;
|
|
while ((pConference != NULL) && (pConference->hConference != hConference))
|
|
pConference = pConference->pNextInTable;
|
|
|
|
if (pConference != NULL) {
|
|
// step 4
|
|
AcquireTimedLock(&pConference->Lock,10,&bTimedOut);
|
|
if (bTimedOut) {
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
Sleep(0);
|
|
goto step2;
|
|
}
|
|
// step 5
|
|
if (pConference->pPrevInTable == NULL)
|
|
ConferenceTable.pHead = pConference->pNextInTable;
|
|
else
|
|
pConference->pPrevInTable->pNextInTable = pConference->pNextInTable;
|
|
|
|
if (pConference->pNextInTable != NULL)
|
|
pConference->pNextInTable->pPrevInTable = pConference->pPrevInTable;
|
|
|
|
pConference->pNextInTable = NULL;
|
|
pConference->pPrevInTable = NULL;
|
|
pConference->bInTable = FALSE;
|
|
}
|
|
|
|
// step 6
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
|
|
if (pConference == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT _MakeConferenceHandle( PCC_HCONFERENCE phConference)
|
|
{
|
|
AcquireLock(&ConferenceHandle.Lock);
|
|
*phConference = ConferenceHandle.hConference++;
|
|
RelinquishLock(&ConferenceHandle.Lock);
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AllocateTerminalNumber( PCONFERENCE pConference,
|
|
H245_TERMINAL_LABEL_T *pH245TerminalLabel)
|
|
{
|
|
unsigned i, j;
|
|
BYTE bMask;
|
|
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(pH245TerminalLabel != NULL);
|
|
ASSERT(pConference->ConferenceMode == MULTIPOINT_MODE);
|
|
ASSERT(pConference->tsMultipointController == TS_TRUE);
|
|
|
|
for (i = 0; i < NUM_TERMINAL_ALLOCATION_SLOTS; i++) {
|
|
bMask = 0x01;
|
|
if (pConference->TerminalNumberAllocation[i] != 0xFF) {
|
|
for (j = 0; j < 8; j++) {
|
|
if ((pConference->TerminalNumberAllocation[i] & bMask) == 0) {
|
|
pConference->TerminalNumberAllocation[i] |= bMask;
|
|
pH245TerminalLabel->mcuNumber = pConference->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bMCUNumber;
|
|
pH245TerminalLabel->terminalNumber = (TerminalNumber)((i * 8) + j + 1);
|
|
return CC_OK;
|
|
}
|
|
bMask *= 2;
|
|
}
|
|
}
|
|
}
|
|
// No more terminal numbers are available for this conference
|
|
return CC_BAD_PARAM;
|
|
}
|
|
|
|
|
|
|
|
HRESULT FreeTerminalNumber( PCONFERENCE pConference,
|
|
BYTE bTerminalNumber)
|
|
{
|
|
unsigned i, j;
|
|
BYTE bMask;
|
|
|
|
ASSERT(pConference != NULL);
|
|
|
|
if (bTerminalNumber > NUM_TERMINAL_ALLOCATION_SLOTS * 8)
|
|
return CC_BAD_PARAM;
|
|
|
|
--bTerminalNumber;
|
|
i = bTerminalNumber / 8;
|
|
j = bTerminalNumber % 8;
|
|
bMask = (BYTE)(0x01 << j);
|
|
if ((pConference->TerminalNumberAllocation[i] & bMask) == 0)
|
|
return CC_BAD_PARAM;
|
|
pConference->TerminalNumberAllocation[i] &= ~bMask;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AllocateChannelNumber( PCONFERENCE pConference,
|
|
WORD *pwChannelNumber)
|
|
{
|
|
unsigned i, j;
|
|
BYTE bMask;
|
|
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(pwChannelNumber != NULL);
|
|
|
|
for (i = 0; i < NUM_CHANNEL_ALLOCATION_SLOTS; i++) {
|
|
bMask = 0x01;
|
|
if (pConference->ChannelNumberAllocation[i] != 0xFF) {
|
|
for (j = 0; j < 8; j++) {
|
|
if ((pConference->ChannelNumberAllocation[i] & bMask) == 0) {
|
|
pConference->ChannelNumberAllocation[i] |= bMask;
|
|
*pwChannelNumber = (WORD) (((i * 8) + j) +
|
|
(pConference->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bMCUNumber << 8));
|
|
return CC_OK;
|
|
}
|
|
bMask *= 2;
|
|
}
|
|
}
|
|
}
|
|
// No more channel numbers are available for this conference
|
|
*pwChannelNumber = 0;
|
|
return CC_BAD_PARAM;
|
|
}
|
|
|
|
|
|
|
|
HRESULT FreeChannelNumber( PCONFERENCE pConference,
|
|
WORD wChannelNumber)
|
|
{
|
|
unsigned i, j;
|
|
BYTE bMask;
|
|
|
|
ASSERT(pConference != NULL);
|
|
|
|
wChannelNumber &= 0xFF;
|
|
|
|
if ((wChannelNumber > NUM_CHANNEL_ALLOCATION_SLOTS * 8) ||
|
|
(wChannelNumber == 0))
|
|
return CC_BAD_PARAM;
|
|
|
|
i = wChannelNumber / 8;
|
|
j = wChannelNumber % 8;
|
|
bMask = (BYTE)(0x01 << j);
|
|
if ((pConference->ChannelNumberAllocation[i] & bMask) == 0)
|
|
return CC_BAD_PARAM;
|
|
pConference->ChannelNumberAllocation[i] &= ~bMask;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AllocAndLockConference( PCC_HCONFERENCE phConference,
|
|
PCC_CONFERENCEID pConferenceID,
|
|
BOOL bMultipointCapable,
|
|
BOOL bForceMultipointController,
|
|
PCC_TERMCAPLIST pLocalTermCapList,
|
|
PCC_TERMCAPDESCRIPTORS pLocalTermCapDescriptors,
|
|
PCC_VENDORINFO pVendorInfo,
|
|
PCC_OCTETSTRING pTerminalID,
|
|
DWORD dwConferenceToken,
|
|
CC_SESSIONTABLE_CONSTRUCTOR SessionTableConstructor,
|
|
CC_TERMCAP_CONSTRUCTOR TermCapConstructor,
|
|
CC_CONFERENCE_CALLBACK ConferenceCallback,
|
|
PPCONFERENCE ppConference)
|
|
|
|
{
|
|
WORD i;
|
|
HRESULT status;
|
|
TRISTATE tsMultipointController;
|
|
|
|
ASSERT(bConferenceInited == TRUE);
|
|
|
|
// all parameters should have been validated by the caller
|
|
ASSERT(phConference != NULL);
|
|
ASSERT(pLocalTermCapList != NULL);
|
|
#ifdef DBG
|
|
if (pLocalTermCapList->wLength != 0)
|
|
ASSERT(pLocalTermCapList->pTermCapArray != NULL);
|
|
|
|
for (i = 0; i < pLocalTermCapList->wLength; i++)
|
|
ASSERT(pLocalTermCapList->pTermCapArray[i] != NULL);
|
|
|
|
if (pLocalTermCapDescriptors != NULL) {
|
|
ASSERT(pLocalTermCapDescriptors->pTermCapDescriptorArray != NULL);
|
|
for (i = 0; i < pLocalTermCapDescriptors->wLength; i++)
|
|
ASSERT(pLocalTermCapDescriptors->pTermCapDescriptorArray[i] != NULL);
|
|
}
|
|
#endif
|
|
ASSERT(pVendorInfo != NULL);
|
|
ASSERT(SessionTableConstructor != NULL);
|
|
ASSERT(TermCapConstructor != NULL);
|
|
ASSERT(ConferenceCallback != NULL);
|
|
ASSERT(ppConference != NULL);
|
|
|
|
// set phConference now, in case we encounter an error
|
|
*phConference = CC_INVALID_HANDLE;
|
|
|
|
*ppConference = (PCONFERENCE)Malloc(sizeof(CONFERENCE));
|
|
if (*ppConference == NULL)
|
|
return CC_NO_MEMORY;
|
|
|
|
if (bForceMultipointController == TRUE)
|
|
tsMultipointController = TS_TRUE;
|
|
else if (bMultipointCapable == TRUE)
|
|
tsMultipointController = TS_UNKNOWN;
|
|
else
|
|
tsMultipointController = TS_FALSE;
|
|
|
|
(*ppConference)->bInTable = FALSE;
|
|
(*ppConference)->LocalParticipantInfo.TerminalIDState = TERMINAL_ID_INVALID;
|
|
(*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bMCUNumber = 1;
|
|
(*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bTerminalNumber = 255;
|
|
(*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString = NULL;
|
|
(*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalID.wOctetStringLength = 0;
|
|
(*ppConference)->LocalParticipantInfo.pEnqueuedRequestsForTerminalID = NULL;
|
|
|
|
for (i = 0; i < NUM_TERMINAL_ALLOCATION_SLOTS; i++)
|
|
(*ppConference)->TerminalNumberAllocation[i] = 0;
|
|
// Channel 0 is reserved for the H.245 control channel
|
|
(*ppConference)->ChannelNumberAllocation[0] = 0x01;
|
|
for (i = 1; i < NUM_CHANNEL_ALLOCATION_SLOTS; i++)
|
|
(*ppConference)->ChannelNumberAllocation[i] = 0;
|
|
(*ppConference)->bMultipointCapable = bMultipointCapable;
|
|
(*ppConference)->bForceMC = bForceMultipointController;
|
|
(*ppConference)->SessionTableConstructor = SessionTableConstructor;
|
|
(*ppConference)->TermCapConstructor = TermCapConstructor;
|
|
(*ppConference)->dwConferenceToken = dwConferenceToken;
|
|
(*ppConference)->bDeferredDelete = FALSE;
|
|
(*ppConference)->bAutoAccept = FALSE; // ignored unless ConferenceCallback is NULL
|
|
(*ppConference)->LocalEndpointAttached = NEVER_ATTACHED;
|
|
(*ppConference)->ConferenceCallback = ConferenceCallback;
|
|
(*ppConference)->SaveConferenceCallback = ConferenceCallback;
|
|
(*ppConference)->bSessionTableInternallyConstructed = FALSE;
|
|
(*ppConference)->pSessionTable = NULL;
|
|
(*ppConference)->pConferenceH245H2250MuxCapability = NULL;
|
|
(*ppConference)->pConferenceTermCapList = NULL;
|
|
(*ppConference)->pConferenceTermCapDescriptors = NULL;
|
|
(*ppConference)->pLocalH245H2250MuxCapability = NULL;
|
|
(*ppConference)->pLocalH245TermCapList = NULL;
|
|
(*ppConference)->pLocalH245TermCapDescriptors = NULL;
|
|
(*ppConference)->pEnqueuedCalls = NULL;
|
|
(*ppConference)->pPlacedCalls = NULL;
|
|
(*ppConference)->pEstablishedCalls = NULL;
|
|
(*ppConference)->pVirtualCalls = NULL;
|
|
(*ppConference)->pChannels = NULL;
|
|
(*ppConference)->tsMultipointController = tsMultipointController;
|
|
(*ppConference)->tsMaster = TS_UNKNOWN;
|
|
(*ppConference)->pMultipointControllerAddr = NULL;
|
|
(*ppConference)->ConferenceMode = UNCONNECTED_MODE;
|
|
(*ppConference)->pVendorInfo = NULL;
|
|
(*ppConference)->pEnqueuedRequestModeCalls = NULL;
|
|
(*ppConference)->pNextInTable = NULL;
|
|
(*ppConference)->pPrevInTable = NULL;
|
|
|
|
if (pConferenceID == NULL) {
|
|
pConferenceID = &InvalidConferenceID;
|
|
(*ppConference)->bDynamicConferenceID = TRUE;
|
|
} else
|
|
(*ppConference)->bDynamicConferenceID = FALSE;
|
|
|
|
(*ppConference)->ConferenceID = *pConferenceID;
|
|
|
|
InitializeLock(&(*ppConference)->Lock);
|
|
AcquireLock(&(*ppConference)->Lock);
|
|
|
|
status = _MakeConferenceHandle(&(*ppConference)->hConference);
|
|
if (status != CC_OK) {
|
|
FreeConference(*ppConference);
|
|
return status;
|
|
}
|
|
|
|
if (pTerminalID != NULL) {
|
|
(*ppConference)->bDynamicTerminalID = FALSE;
|
|
(*ppConference)->LocalParticipantInfo.TerminalIDState = TERMINAL_ID_VALID;
|
|
(*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalID.wOctetStringLength =
|
|
pTerminalID->wOctetStringLength;
|
|
(*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString =
|
|
(BYTE *)Malloc(pTerminalID->wOctetStringLength);
|
|
if ((*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString == NULL) {
|
|
FreeConference(*ppConference);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
memcpy((*ppConference)->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString,
|
|
pTerminalID->pOctetString,
|
|
pTerminalID->wOctetStringLength);
|
|
} else {
|
|
(*ppConference)->bDynamicTerminalID = TRUE;
|
|
}
|
|
|
|
status = _CreateLocalH245H2250MuxCapability(*ppConference);
|
|
if (status != CC_OK) {
|
|
FreeConference(*ppConference);
|
|
return status;
|
|
}
|
|
|
|
// make a local copy of pTermCapList
|
|
status = CopyH245TermCapList(&(*ppConference)->pLocalH245TermCapList,
|
|
pLocalTermCapList);
|
|
if (status != CC_OK) {
|
|
FreeConference(*ppConference);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
|
|
// create a new descriptor list if one was not supplied
|
|
if (pLocalTermCapDescriptors == NULL)
|
|
status = CreateH245DefaultTermCapDescriptors(&(*ppConference)->pLocalH245TermCapDescriptors,
|
|
(*ppConference)->pLocalH245TermCapList);
|
|
else
|
|
// make a local copy of pTermCapDescriptors
|
|
status = CopyH245TermCapDescriptors(&(*ppConference)->pLocalH245TermCapDescriptors,
|
|
pLocalTermCapDescriptors);
|
|
|
|
if (status != CC_OK) {
|
|
FreeConference(*ppConference);
|
|
return CC_NO_MEMORY;
|
|
}
|
|
|
|
status = CopyVendorInfo(&((*ppConference)->pVendorInfo), pVendorInfo);
|
|
if (status != CC_OK) {
|
|
FreeConference(*ppConference);
|
|
return status;
|
|
}
|
|
|
|
*phConference = (*ppConference)->hConference;
|
|
|
|
// add the conference to the conference table
|
|
status = _AddConferenceToTable(*ppConference);
|
|
if (status != CC_OK)
|
|
FreeConference(*ppConference);
|
|
|
|
// CreateConferenceTermCaps() must be called after _AddConferenceToTable(),
|
|
// since it will re-lock the conference object
|
|
if ((*ppConference)->tsMultipointController == TS_TRUE) {
|
|
status = CreateConferenceTermCaps(*ppConference, NULL);
|
|
if (status != CC_OK) {
|
|
FreeConference(*ppConference);
|
|
return status;
|
|
}
|
|
}
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
|
|
HRESULT RemoveCallFromConference( PCALL pCall,
|
|
PCONFERENCE pConference)
|
|
{
|
|
ASSERT(pCall != NULL);
|
|
ASSERT(pConference != NULL);
|
|
// The call object must have been removed from the call table
|
|
// prior to removing it from the associated conference object.
|
|
// This assures us that no other thread is waiting for a lock on it.
|
|
ASSERT(pCall->bInTable == FALSE);
|
|
|
|
if (pCall->pPrev == NULL) {
|
|
// the call object is either at the head of the enqueued call list,
|
|
// the head of the placed call list, the head of the established
|
|
// call list, the head of the virtual call list, or is detached
|
|
// from the conference
|
|
if (pConference->pEnqueuedCalls == pCall)
|
|
// The call is on the enqueued call list
|
|
pConference->pEnqueuedCalls = pCall->pNext;
|
|
else if (pConference->pPlacedCalls == pCall)
|
|
// the call is on the placed call list
|
|
pConference->pPlacedCalls = pCall->pNext;
|
|
else if (pConference->pEstablishedCalls == pCall)
|
|
// the call is on the established call list
|
|
pConference->pEstablishedCalls = pCall->pNext;
|
|
else if (pConference->pVirtualCalls == pCall)
|
|
pConference->pVirtualCalls = pCall->pNext;
|
|
} else
|
|
pCall->pPrev->pNext = pCall->pNext;
|
|
|
|
if (pCall->pNext != NULL)
|
|
pCall->pNext->pPrev = pCall->pPrev;
|
|
|
|
pCall->pNext = NULL;
|
|
pCall->pPrev = NULL;
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT RemoveEnqueuedCallFromConference(
|
|
PCONFERENCE pConference,
|
|
PCC_HCALL phCall)
|
|
{
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(phCall != NULL);
|
|
|
|
if (pConference->pEnqueuedCalls == NULL) {
|
|
// No enqueued calls; this is not an error, since the caller can't tell
|
|
// whether there are any enqueued calls in this conference
|
|
*phCall = CC_INVALID_HANDLE;
|
|
return CC_OK;
|
|
}
|
|
|
|
// Move the call object from the enqueued call list to the placed
|
|
// call list.
|
|
|
|
// Note that another thread may have a lock on the enqueued call
|
|
// object, and may be trying to delete it; they will first need to
|
|
// lock the conference object (which this thread has locked), remove
|
|
// the call object from the enqueued call list, then free the call object.
|
|
// We are therefore safe in creating a pointer to the call object, although
|
|
// we may not examine or change any of its contents other than hCall (read-only),
|
|
// pNext and pPrev.
|
|
|
|
*phCall = pConference->pEnqueuedCalls->hCall;
|
|
pConference->pEnqueuedCalls = pConference->pEnqueuedCalls->pNext;
|
|
if (pConference->pEnqueuedCalls != NULL)
|
|
pConference->pEnqueuedCalls->pPrev = NULL;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT RemoveChannelFromConference(PCHANNEL pChannel,
|
|
PCONFERENCE pConference)
|
|
{
|
|
ASSERT(pChannel != NULL);
|
|
ASSERT(pConference != NULL);
|
|
|
|
// The channel object must have been removed from the channel table
|
|
// prior to removing it from the associated conference object.
|
|
// This assures us that no other thread is waiting for a lock on it.
|
|
ASSERT(pChannel->bInTable == FALSE);
|
|
|
|
if (pChannel->pPrev == NULL) {
|
|
// the channel object is at the head of the channel list,
|
|
// or has been detached from the conference
|
|
if (pConference->pChannels == pChannel)
|
|
pConference->pChannels = pChannel->pNext;
|
|
} else
|
|
pChannel->pPrev->pNext = pChannel->pNext;
|
|
|
|
if (pChannel->pNext != NULL)
|
|
pChannel->pNext->pPrev = pChannel->pPrev;
|
|
|
|
pChannel->pNext = NULL;
|
|
pChannel->pPrev = NULL;
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AddEnqueuedCallToConference(PCALL pCall,
|
|
PCONFERENCE pConference)
|
|
{
|
|
ASSERT(pCall != NULL);
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(EqualConferenceIDs(&pCall->ConferenceID, &InvalidConferenceID));
|
|
ASSERT(EqualConferenceIDs(&pConference->ConferenceID, &InvalidConferenceID));
|
|
ASSERT(pConference->pPlacedCalls != NULL);
|
|
// Call cannot already be associated with the conference
|
|
ASSERT(pCall->pNext == NULL);
|
|
ASSERT(pCall->pPrev == NULL);
|
|
|
|
pCall->hConference = pConference->hConference;
|
|
|
|
pCall->pNext = pConference->pEnqueuedCalls;
|
|
pCall->pPrev = NULL;
|
|
if (pConference->pEnqueuedCalls != NULL) {
|
|
ASSERT(pConference->pEnqueuedCalls->pPrev == NULL);
|
|
pConference->pEnqueuedCalls->pPrev = pCall;
|
|
}
|
|
pConference->pEnqueuedCalls = pCall;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AddPlacedCallToConference( PCALL pCall,
|
|
PCONFERENCE pConference)
|
|
{
|
|
ASSERT(pCall != NULL);
|
|
ASSERT(pConference != NULL);
|
|
|
|
if (EqualConferenceIDs(&pConference->ConferenceID,
|
|
&InvalidConferenceID)) {
|
|
// If a conference ID has not been assigned, but there are
|
|
// placed or enqueued calls on the conference, the conference ID
|
|
// will be assigned by a callee when the first of these calls completes.
|
|
// Since pCall has an assigned conference ID (which will differ from
|
|
// the ID to be assigned to this conference), we cannot assign pCall
|
|
// to this conference.
|
|
ASSERT(pConference->pEstablishedCalls == NULL);
|
|
if (pConference->pPlacedCalls != NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
pConference->ConferenceID = pCall->ConferenceID;
|
|
} else
|
|
if (!EqualConferenceIDs(&pConference->ConferenceID,
|
|
&pCall->ConferenceID))
|
|
return CC_BAD_PARAM;
|
|
|
|
pCall->hConference = pConference->hConference;
|
|
|
|
// Unlink pCall from pConference, if necessary
|
|
if (pCall->pPrev == NULL) {
|
|
// pCall is at the head of either the enqueued call list,
|
|
// the placed call list, or the established call list, or
|
|
// is not yet associated with the conference object
|
|
if (pConference->pEnqueuedCalls == pCall)
|
|
pConference->pEnqueuedCalls = pCall->pNext;
|
|
else if (pConference->pPlacedCalls == pCall)
|
|
pConference->pPlacedCalls = pCall->pNext;
|
|
else if (pConference->pEstablishedCalls == pCall)
|
|
pConference->pEstablishedCalls = pCall->pNext;
|
|
} else
|
|
pCall->pPrev->pNext = pCall->pNext;
|
|
|
|
if (pCall->pNext != NULL)
|
|
pCall->pNext->pPrev = pCall->pPrev;
|
|
|
|
// Now link pCall into the placed call list
|
|
pCall->pNext = pConference->pPlacedCalls;
|
|
pCall->pPrev = NULL;
|
|
if (pConference->pPlacedCalls != NULL) {
|
|
ASSERT(pConference->pPlacedCalls->pPrev == NULL);
|
|
pConference->pPlacedCalls->pPrev = pCall;
|
|
}
|
|
pConference->pPlacedCalls = pCall;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AddEstablishedCallToConference(
|
|
PCALL pCall,
|
|
PCONFERENCE pConference)
|
|
{
|
|
ASSERT(pCall != NULL);
|
|
ASSERT(pConference != NULL);
|
|
ASSERT((EqualConferenceIDs(&pConference->ConferenceID, &InvalidConferenceID)) ||
|
|
(EqualConferenceIDs(&pCall->ConferenceID, &pConference->ConferenceID)));
|
|
|
|
if (EqualConferenceIDs(&pConference->ConferenceID, &InvalidConferenceID)) {
|
|
// If a conference ID has not been assigned, but there are
|
|
// placed or enqueued calls on the conference, the conference ID
|
|
// will be assigned by a callee when the first of these calls completes.
|
|
// Since pCall has an assigned conference ID (which will differ from
|
|
// the ID to be assigned to this conference), we cannot assign pCall
|
|
// to this conference.
|
|
ASSERT(pConference->pEstablishedCalls == NULL);
|
|
pConference->ConferenceID = pCall->ConferenceID;
|
|
} else if (!EqualConferenceIDs(&pConference->ConferenceID, &pCall->ConferenceID))
|
|
return CC_BAD_PARAM;
|
|
|
|
pCall->hConference = pConference->hConference;
|
|
|
|
// Unlink pCall from pConference, if necessary
|
|
if (pCall->pPrev == NULL) {
|
|
// pCall is at the head of either the enqueued call list,
|
|
// the placed call list, or the established call list, or
|
|
// is not yet associated with the conference object
|
|
if (pConference->pEnqueuedCalls == pCall)
|
|
pConference->pEnqueuedCalls = pCall->pNext;
|
|
else if (pConference->pPlacedCalls == pCall)
|
|
pConference->pPlacedCalls = pCall->pNext;
|
|
else if (pConference->pEstablishedCalls == pCall)
|
|
pConference->pEstablishedCalls = pCall->pNext;
|
|
} else
|
|
pCall->pPrev->pNext = pCall->pNext;
|
|
|
|
if (pCall->pNext != NULL)
|
|
pCall->pNext->pPrev = pCall->pPrev;
|
|
|
|
// Now link pCall into the established call list
|
|
pCall->pNext = pConference->pEstablishedCalls;
|
|
pCall->pPrev = NULL;
|
|
if (pConference->pEstablishedCalls != NULL) {
|
|
ASSERT(pConference->pEstablishedCalls->pPrev == NULL);
|
|
pConference->pEstablishedCalls->pPrev = pCall;
|
|
}
|
|
pConference->pEstablishedCalls = pCall;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AddVirtualCallToConference( PCALL pCall,
|
|
PCONFERENCE pConference)
|
|
{
|
|
ASSERT(pCall != NULL);
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(pConference->ConferenceMode == MULTIPOINT_MODE);
|
|
ASSERT(pConference->tsMultipointController == TS_FALSE);
|
|
// Call cannot already be associated with the conference
|
|
ASSERT(pCall->pNext == NULL);
|
|
ASSERT(pCall->pPrev == NULL);
|
|
|
|
pCall->hConference = pConference->hConference;
|
|
|
|
pCall->pNext = pConference->pVirtualCalls;
|
|
pCall->pPrev = NULL;
|
|
if (pConference->pVirtualCalls != NULL) {
|
|
ASSERT(pConference->pVirtualCalls->pPrev == NULL);
|
|
pConference->pVirtualCalls->pPrev = pCall;
|
|
}
|
|
pConference->pVirtualCalls = pCall;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AddChannelToConference( PCHANNEL pChannel,
|
|
PCONFERENCE pConference)
|
|
{
|
|
PPCHANNEL ppChannel;
|
|
|
|
ASSERT(pChannel != NULL);
|
|
ASSERT((pChannel->bChannelType == TX_CHANNEL) ||
|
|
(pChannel->bChannelType == RX_CHANNEL) ||
|
|
(pChannel->bChannelType == TXRX_CHANNEL) ||
|
|
(pChannel->bChannelType == PROXY_CHANNEL));
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(pChannel->hConference == pConference->hConference);
|
|
ASSERT(pChannel->pNext == NULL);
|
|
ASSERT(pChannel->pPrev == NULL);
|
|
ASSERT(pConference->ConferenceMode != UNCONNECTED_MODE);
|
|
|
|
if (pConference->pEstablishedCalls == NULL)
|
|
// Can't open a channel unless we have at least one established call
|
|
return CC_BAD_PARAM;
|
|
|
|
ppChannel = &pConference->pChannels;
|
|
|
|
pChannel->pNext = *ppChannel;
|
|
pChannel->pPrev = NULL;
|
|
if (*ppChannel != NULL) {
|
|
ASSERT((*ppChannel)->pPrev == NULL);
|
|
(*ppChannel)->pPrev = pChannel;
|
|
}
|
|
*ppChannel = pChannel;
|
|
if (pConference->ConferenceMode == POINT_TO_POINT_MODE)
|
|
pChannel->bMultipointChannel = FALSE;
|
|
else
|
|
pChannel->bMultipointChannel = TRUE;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
// Caller must have a lock on the conference object
|
|
// There must be no calls on this conference object
|
|
// (previous calls must have been cleared by calling Hangup())
|
|
HRESULT FreeConference( PCONFERENCE pConference)
|
|
{
|
|
CC_HCONFERENCE hConference;
|
|
PCALL pVirtualCall;
|
|
WORD wNumCalls;
|
|
WORD i;
|
|
PCC_HCALL CallList;
|
|
WORD wNumChannels;
|
|
PCC_HCHANNEL ChannelList;
|
|
PCHANNEL pChannel;
|
|
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(pConference->pEnqueuedCalls == NULL);
|
|
ASSERT(pConference->pPlacedCalls == NULL);
|
|
ASSERT(pConference->pEstablishedCalls == NULL);
|
|
|
|
// caller must have a lock on the conference object,
|
|
// so there's no need to re-lock it
|
|
|
|
hConference = pConference->hConference;
|
|
|
|
if (pConference->bInTable == TRUE)
|
|
if (_RemoveConferenceFromTable(pConference) == CC_BAD_PARAM)
|
|
// the conference object was deleted by another thread,
|
|
// so just return CC_OK
|
|
return CC_OK;
|
|
|
|
if (pConference->pLocalH245H2250MuxCapability != NULL)
|
|
H245FreeCap(pConference->pLocalH245H2250MuxCapability);
|
|
|
|
// free up the LocalTermCapList elements
|
|
DestroyH245TermCapList(&pConference->pLocalH245TermCapList);
|
|
|
|
// free up the local terminal capability descriptors
|
|
DestroyH245TermCapDescriptors(&pConference->pLocalH245TermCapDescriptors);
|
|
|
|
if (pConference->pMultipointControllerAddr != NULL)
|
|
Free(pConference->pMultipointControllerAddr);
|
|
|
|
if (pConference->pVendorInfo != NULL)
|
|
FreeVendorInfo(pConference->pVendorInfo);
|
|
|
|
if (pConference->pSessionTable != NULL)
|
|
FreeConferenceSessionTable(pConference);
|
|
|
|
if (pConference->pConferenceH245H2250MuxCapability != NULL)
|
|
H245FreeCap(pConference->pConferenceH245H2250MuxCapability);
|
|
|
|
if ((pConference->pConferenceTermCapList != NULL) ||
|
|
(pConference->pConferenceTermCapDescriptors != NULL))
|
|
FreeConferenceTermCaps(pConference);
|
|
|
|
if (pConference->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString != NULL)
|
|
Free(pConference->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString);
|
|
|
|
while (DequeueRequest(&pConference->LocalParticipantInfo.pEnqueuedRequestsForTerminalID, NULL) == CC_OK);
|
|
|
|
EnumerateCallsInConference(&wNumCalls, &CallList, pConference, VIRTUAL_CALL);
|
|
for (i = 0; i < wNumCalls; i++)
|
|
if (LockCall(CallList[i], &pVirtualCall) == CC_OK)
|
|
FreeCall(pVirtualCall);
|
|
if (CallList != NULL)
|
|
Free(CallList);
|
|
|
|
EnumerateChannelsInConference(&wNumChannels, &ChannelList, pConference, ALL_CHANNELS);
|
|
for (i = 0; i < wNumChannels; i++)
|
|
if (LockChannel(ChannelList[i], &pChannel) == CC_OK)
|
|
FreeChannel(pChannel);
|
|
if (ChannelList != NULL)
|
|
Free(ChannelList);
|
|
|
|
while (DequeueRequest(&pConference->pEnqueuedRequestModeCalls, NULL) == CC_OK);
|
|
|
|
// since the conference object has been removed from the ConferenceTable,
|
|
// no other thread will be able to find the conference object and obtain
|
|
// a lock, so its safe to unlock the conference object and delete it here
|
|
RelinquishLock(&pConference->Lock);
|
|
DeleteLock(&pConference->Lock);
|
|
Free(pConference);
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT LockConference( CC_HCONFERENCE hConference,
|
|
PPCONFERENCE ppConference)
|
|
{
|
|
BOOL bTimedOut;
|
|
|
|
ASSERT(hConference != CC_INVALID_HANDLE);
|
|
ASSERT(ppConference != NULL);
|
|
|
|
step1:
|
|
AcquireLock(&ConferenceTable.Lock);
|
|
|
|
*ppConference = ConferenceTable.pHead;
|
|
while ((*ppConference != NULL) && ((*ppConference)->hConference != hConference))
|
|
*ppConference = (*ppConference)->pNextInTable;
|
|
|
|
if (*ppConference != NULL) {
|
|
AcquireTimedLock(&(*ppConference)->Lock,10,&bTimedOut);
|
|
if (bTimedOut) {
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
Sleep(0);
|
|
goto step1;
|
|
}
|
|
}
|
|
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
|
|
if (*ppConference == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT LockConferenceEx( CC_HCONFERENCE hConference,
|
|
PPCONFERENCE ppConference,
|
|
TRISTATE tsDeferredDelete)
|
|
{
|
|
BOOL bTimedOut;
|
|
|
|
ASSERT(hConference != CC_INVALID_HANDLE);
|
|
ASSERT(ppConference != NULL);
|
|
|
|
step1:
|
|
AcquireLock(&ConferenceTable.Lock);
|
|
|
|
*ppConference = ConferenceTable.pHead;
|
|
while ((*ppConference != NULL) && ((*ppConference)->hConference != hConference))
|
|
*ppConference = (*ppConference)->pNextInTable;
|
|
|
|
if (*ppConference != NULL) {
|
|
AcquireTimedLock(&(*ppConference)->Lock,10,&bTimedOut);
|
|
if (bTimedOut) {
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
Sleep(0);
|
|
goto step1;
|
|
}
|
|
if (tsDeferredDelete == TS_TRUE) {
|
|
if ((*ppConference)->bDeferredDelete != TRUE) {
|
|
RelinquishLock(&(*ppConference)->Lock);
|
|
*ppConference = NULL;
|
|
}
|
|
} else if (tsDeferredDelete == TS_FALSE) {
|
|
if ((*ppConference)->bDeferredDelete != FALSE) {
|
|
RelinquishLock(&(*ppConference)->Lock);
|
|
*ppConference = NULL;
|
|
}
|
|
}
|
|
}
|
|
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
|
|
if (*ppConference == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT ValidateConference( CC_HCONFERENCE hConference)
|
|
{
|
|
PCONFERENCE pConference;
|
|
|
|
ASSERT(hConference != CC_INVALID_HANDLE);
|
|
|
|
AcquireLock(&ConferenceTable.Lock);
|
|
|
|
pConference = ConferenceTable.pHead;
|
|
while ((pConference != NULL) && (pConference->hConference != hConference))
|
|
pConference = pConference->pNextInTable;
|
|
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
|
|
if (pConference == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT LockConferenceID( PCC_CONFERENCEID pConferenceID,
|
|
PPCONFERENCE ppConference)
|
|
{
|
|
BOOL bTimedOut;
|
|
|
|
ASSERT(!EqualConferenceIDs(pConferenceID, &InvalidConferenceID));
|
|
ASSERT(ppConference != NULL);
|
|
// There may be many conference objects in the table with unassigned
|
|
// conference IDs (ConferenceID = InvalidConferenceID). The caller may
|
|
// never ask us to search for an unassigned conference ID.
|
|
|
|
step1:
|
|
AcquireLock(&ConferenceTable.Lock);
|
|
|
|
*ppConference = ConferenceTable.pHead;
|
|
while ((*ppConference != NULL) &&
|
|
(!EqualConferenceIDs(&(*ppConference)->ConferenceID, pConferenceID)))
|
|
*ppConference = (*ppConference)->pNextInTable;
|
|
|
|
if (*ppConference != NULL) {
|
|
AcquireTimedLock(&(*ppConference)->Lock,10,&bTimedOut);
|
|
if (bTimedOut) {
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
Sleep(0);
|
|
goto step1;
|
|
}
|
|
}
|
|
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
|
|
if (*ppConference == NULL)
|
|
return CC_BAD_PARAM;
|
|
else
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT FindChannelInConference( WORD wChannel,
|
|
BOOL bLocalChannel,
|
|
BYTE bChannelType,
|
|
CC_HCALL hCall,
|
|
PCC_HCHANNEL phChannel,
|
|
PCONFERENCE pConference)
|
|
{
|
|
PCHANNEL pChannel;
|
|
WORD wChannelNumber;
|
|
|
|
ASSERT(wChannel != 0);
|
|
ASSERT(phChannel != NULL);
|
|
ASSERT(pConference != NULL);
|
|
|
|
*phChannel = CC_INVALID_HANDLE;
|
|
|
|
pChannel = pConference->pChannels;
|
|
while (pChannel != NULL) {
|
|
if (bLocalChannel)
|
|
wChannelNumber = pChannel->wLocalChannelNumber;
|
|
else
|
|
wChannelNumber = pChannel->wRemoteChannelNumber;
|
|
if ((wChannelNumber == wChannel) &&
|
|
((pChannel->bChannelType & bChannelType) != 0) &&
|
|
((hCall == CC_INVALID_HANDLE) ||
|
|
(pChannel->hCall == hCall)))
|
|
break;
|
|
pChannel = pChannel->pNext;
|
|
}
|
|
if (pChannel == NULL)
|
|
return CC_BAD_PARAM;
|
|
*phChannel = pChannel->hChannel;
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT EnumerateConferences( PWORD pwNumConferences,
|
|
CC_HCONFERENCE ConferenceList[])
|
|
{
|
|
WORD wIndexLimit;
|
|
PCONFERENCE pConference;
|
|
|
|
|
|
if ((*pwNumConferences != 0) && (ConferenceList == NULL))
|
|
return CC_BAD_PARAM;
|
|
if ((*pwNumConferences == 0) && (ConferenceList != NULL))
|
|
return CC_BAD_PARAM;
|
|
|
|
wIndexLimit = *pwNumConferences;
|
|
*pwNumConferences = 0;
|
|
|
|
AcquireLock(&ConferenceTable.Lock);
|
|
|
|
pConference = ConferenceTable.pHead;
|
|
while (pConference != NULL) {
|
|
if (*pwNumConferences < wIndexLimit)
|
|
ConferenceList[*pwNumConferences] = pConference->hConference;
|
|
(*pwNumConferences)++;
|
|
pConference = pConference->pNextInTable;
|
|
}
|
|
|
|
RelinquishLock(&ConferenceTable.Lock);
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT EnumerateCallsInConference( WORD *pwNumCalls,
|
|
PCC_HCALL pCallList[],
|
|
PCONFERENCE pConference,
|
|
BYTE bCallType)
|
|
{
|
|
WORD i;
|
|
PCALL pCall;
|
|
|
|
ASSERT(pwNumCalls != NULL);
|
|
ASSERT(pConference != NULL);
|
|
|
|
*pwNumCalls = 0;
|
|
|
|
if (bCallType & ENQUEUED_CALL) {
|
|
pCall = pConference->pEnqueuedCalls;
|
|
while (pCall != NULL) {
|
|
(*pwNumCalls)++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
|
|
if (bCallType & PLACED_CALL) {
|
|
pCall = pConference->pPlacedCalls;
|
|
while (pCall != NULL) {
|
|
(*pwNumCalls)++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
|
|
if (bCallType & ESTABLISHED_CALL) {
|
|
pCall = pConference->pEstablishedCalls;
|
|
while (pCall != NULL) {
|
|
(*pwNumCalls)++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
|
|
if (bCallType & VIRTUAL_CALL) {
|
|
pCall = pConference->pVirtualCalls;
|
|
while (pCall != NULL) {
|
|
(*pwNumCalls)++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
|
|
if (pCallList == NULL)
|
|
return CC_OK;
|
|
|
|
if (*pwNumCalls == 0) {
|
|
*pCallList = NULL;
|
|
return CC_OK;
|
|
}
|
|
|
|
*pCallList = (PCC_HCALL)Malloc(sizeof(CC_HCALL) * (*pwNumCalls));
|
|
if (*pCallList == NULL)
|
|
return CC_NO_MEMORY;
|
|
|
|
i = 0;
|
|
|
|
if (bCallType & ENQUEUED_CALL) {
|
|
pCall = pConference->pEnqueuedCalls;
|
|
while (pCall != NULL) {
|
|
(*pCallList)[i] = pCall->hCall;
|
|
i++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
|
|
if (bCallType & PLACED_CALL) {
|
|
pCall = pConference->pPlacedCalls;
|
|
while (pCall != NULL) {
|
|
(*pCallList)[i] = pCall->hCall;
|
|
i++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
|
|
if (bCallType & ESTABLISHED_CALL) {
|
|
pCall = pConference->pEstablishedCalls;
|
|
while (pCall != NULL) {
|
|
(*pCallList)[i] = pCall->hCall;
|
|
i++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
|
|
if (bCallType & VIRTUAL_CALL) {
|
|
pCall = pConference->pVirtualCalls;
|
|
while (pCall != NULL) {
|
|
(*pCallList)[i] = pCall->hCall;
|
|
i++;
|
|
pCall = pCall->pNext;
|
|
}
|
|
}
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT EnumerateChannelsInConference(
|
|
WORD *pwNumChannels,
|
|
PCC_HCHANNEL pChannelList[],
|
|
PCONFERENCE pConference,
|
|
BYTE bChannelType)
|
|
{
|
|
WORD i;
|
|
PCHANNEL pChannel;
|
|
|
|
ASSERT(pwNumChannels != NULL);
|
|
ASSERT(pConference != NULL);
|
|
|
|
*pwNumChannels = 0;
|
|
|
|
pChannel = pConference->pChannels;
|
|
|
|
while (pChannel != NULL) {
|
|
if (pChannel->bChannelType & bChannelType)
|
|
(*pwNumChannels)++;
|
|
pChannel = pChannel->pNext;
|
|
}
|
|
|
|
if (pChannelList == NULL)
|
|
return CC_OK;
|
|
|
|
if (*pwNumChannels == 0) {
|
|
*pChannelList = NULL;
|
|
return CC_OK;
|
|
}
|
|
|
|
*pChannelList = (PCC_HCHANNEL)Malloc(sizeof(CC_HCHANNEL) * (*pwNumChannels));
|
|
if (*pChannelList == NULL)
|
|
return CC_NO_MEMORY;
|
|
|
|
i = 0;
|
|
pChannel = pConference->pChannels;
|
|
|
|
while (pChannel != NULL) {
|
|
if (pChannel->bChannelType & bChannelType) {
|
|
(*pChannelList)[i] = pChannel->hChannel;
|
|
i++;
|
|
}
|
|
pChannel = pChannel->pNext;
|
|
}
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT EnumerateTerminalLabelsInConference(
|
|
WORD *pwNumTerminalLabels,
|
|
H245_TERMINAL_LABEL_T *pH245TerminalLabelList[],
|
|
PCONFERENCE pConference)
|
|
{
|
|
WORD i, j;
|
|
WORD wIndex;
|
|
BYTE bMask;
|
|
|
|
ASSERT(pwNumTerminalLabels != NULL);
|
|
ASSERT(pConference != NULL);
|
|
|
|
// First count the number of known terminals
|
|
*pwNumTerminalLabels = 0;
|
|
for (i = 0; i < NUM_TERMINAL_ALLOCATION_SLOTS; i++) {
|
|
if (pConference->TerminalNumberAllocation[i] != 0) {
|
|
bMask = 0x01;
|
|
for (j = 0; j < 8; j++) {
|
|
if ((pConference->TerminalNumberAllocation[i] & bMask) != 0)
|
|
(*pwNumTerminalLabels)++;
|
|
bMask *= 2;
|
|
}
|
|
}
|
|
}
|
|
if (pConference->LocalEndpointAttached == ATTACHED)
|
|
(*pwNumTerminalLabels)++;
|
|
|
|
if (pH245TerminalLabelList == NULL)
|
|
return CC_OK;
|
|
|
|
if (*pwNumTerminalLabels == 0)
|
|
*pH245TerminalLabelList = NULL;
|
|
|
|
*pH245TerminalLabelList = (H245_TERMINAL_LABEL_T *)Malloc(sizeof(H245_TERMINAL_LABEL_T) *
|
|
(*pwNumTerminalLabels));
|
|
if (*pH245TerminalLabelList == NULL)
|
|
return CC_NO_MEMORY;
|
|
|
|
wIndex = 0;
|
|
for (i = 0; i < NUM_TERMINAL_ALLOCATION_SLOTS; i++) {
|
|
if (pConference->TerminalNumberAllocation[i] != 0) {
|
|
bMask = 0x01;
|
|
for (j = 0; j < 8; j++) {
|
|
if ((pConference->TerminalNumberAllocation[i] & bMask) != 0) {
|
|
(*pH245TerminalLabelList)[wIndex].mcuNumber = pConference->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bMCUNumber;
|
|
(*pH245TerminalLabelList)[wIndex].terminalNumber = (TerminalNumber) ((i * 8) + j + 1);
|
|
wIndex++;
|
|
}
|
|
bMask *= 2;
|
|
}
|
|
}
|
|
}
|
|
if (pConference->LocalEndpointAttached == ATTACHED) {
|
|
(*pH245TerminalLabelList)[wIndex].mcuNumber = pConference->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bMCUNumber;
|
|
(*pH245TerminalLabelList)[wIndex].terminalNumber = 0;
|
|
}
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT UnlockConference( PCONFERENCE pConference)
|
|
{
|
|
ASSERT(pConference != NULL);
|
|
|
|
RelinquishLock(&pConference->Lock);
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT AsynchronousDestroyConference(
|
|
CC_HCONFERENCE hConference,
|
|
BOOL bAutoAccept)
|
|
{
|
|
HRESULT status;
|
|
PCONFERENCE pConference;
|
|
WORD wNumCalls;
|
|
WORD wNumChannels;
|
|
WORD i;
|
|
PCHANNEL pChannel;
|
|
PCC_HCHANNEL ChannelList;
|
|
|
|
status = LockConference(hConference, &pConference);
|
|
if (status != CC_OK)
|
|
return status;
|
|
|
|
status = EnumerateCallsInConference(&wNumCalls, NULL, pConference, REAL_CALLS);
|
|
if (status != CC_OK) {
|
|
UnlockConference(pConference);
|
|
return status;
|
|
}
|
|
|
|
// This is an illegal call if:
|
|
// 1. The local endpoint is currently attached;
|
|
// 2. The local endpoint has never been attached, but is in the
|
|
// process of placing a call
|
|
if ((pConference->LocalEndpointAttached == ATTACHED) ||
|
|
((pConference->LocalEndpointAttached == NEVER_ATTACHED) &&
|
|
(wNumCalls > 0))) {
|
|
UnlockConference(pConference);
|
|
return CC_BAD_PARAM;
|
|
}
|
|
|
|
pConference->ConferenceCallback = NULL;
|
|
|
|
// can't destroy a conference if there are active calls
|
|
if (wNumCalls != 0) {
|
|
pConference->bDeferredDelete = TRUE;
|
|
pConference->bAutoAccept = bAutoAccept;
|
|
UnlockConference(pConference);
|
|
return CC_OK;
|
|
}
|
|
|
|
status = EnumerateChannelsInConference(&wNumChannels,
|
|
&ChannelList,
|
|
pConference,
|
|
ALL_CHANNELS);
|
|
if (status != CC_OK) {
|
|
UnlockConference(pConference);
|
|
return status;
|
|
}
|
|
|
|
// free all the channels
|
|
for (i = 0; i < wNumChannels; i++) {
|
|
if (LockChannel(ChannelList[i], &pChannel) == CC_OK)
|
|
// Notice that since we're going to hangup, we don't need to
|
|
// close any channels
|
|
FreeChannel(pChannel);
|
|
}
|
|
|
|
if (ChannelList != NULL)
|
|
Free(ChannelList);
|
|
|
|
FreeConference(pConference);
|
|
|
|
return CC_OK;
|
|
}
|
|
|
|
|
|
|
|
HRESULT FindPeerParticipantInfo( H245_TERMINAL_LABEL_T H245TerminalLabel,
|
|
PCONFERENCE pConference,
|
|
BYTE bCallType,
|
|
PCALL *ppCall)
|
|
{
|
|
WORD wNumCalls;
|
|
PCC_HCALL CallList;
|
|
WORD i;
|
|
HRESULT status;
|
|
|
|
ASSERT(pConference != NULL);
|
|
ASSERT(ppCall != NULL);
|
|
|
|
status = EnumerateCallsInConference(&wNumCalls,
|
|
&CallList,
|
|
pConference,
|
|
bCallType);
|
|
if (status != CC_OK)
|
|
return status;
|
|
|
|
for (i = 0; i < wNumCalls; i++) {
|
|
if (LockCall(CallList[i], ppCall) == CC_OK) {
|
|
if ((*ppCall)->pPeerParticipantInfo != NULL)
|
|
if (((*ppCall)->pPeerParticipantInfo->ParticipantInfo.TerminalLabel.bMCUNumber ==
|
|
H245TerminalLabel.mcuNumber) &&
|
|
((*ppCall)->pPeerParticipantInfo->ParticipantInfo.TerminalLabel.bTerminalNumber ==
|
|
H245TerminalLabel.terminalNumber)) {
|
|
Free(CallList);
|
|
return CC_OK;
|
|
}
|
|
UnlockCall(*ppCall);
|
|
}
|
|
}
|
|
if (CallList != NULL)
|
|
Free(CallList);
|
|
*ppCall = NULL;
|
|
return CC_BAD_PARAM;
|
|
}
|
|
|
|
|
|
|
|
HRESULT ReInitializeConference( PCONFERENCE pConference)
|
|
{
|
|
PCALL pCall;
|
|
WORD wNumCalls;
|
|
WORD i;
|
|
PCC_HCALL CallList;
|
|
PCHANNEL pChannel;
|
|
WORD wNumChannels;
|
|
PCC_HCHANNEL ChannelList;
|
|
HRESULT status;
|
|
|
|
ASSERT(pConference != NULL);
|
|
|
|
if (pConference->bDynamicConferenceID == TRUE)
|
|
pConference->ConferenceID = InvalidConferenceID;
|
|
|
|
pConference->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bMCUNumber = 1;
|
|
pConference->LocalParticipantInfo.ParticipantInfo.TerminalLabel.bTerminalNumber = 255;
|
|
if (pConference->bDynamicTerminalID == TRUE) {
|
|
pConference->LocalParticipantInfo.TerminalIDState = TERMINAL_ID_INVALID;
|
|
Free(pConference->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString);
|
|
pConference->LocalParticipantInfo.ParticipantInfo.TerminalID.pOctetString = NULL;
|
|
pConference->LocalParticipantInfo.ParticipantInfo.TerminalID.wOctetStringLength = 0;
|
|
}
|
|
while (DequeueRequest(&pConference->LocalParticipantInfo.pEnqueuedRequestsForTerminalID, NULL) == CC_OK);
|
|
for (i = 0; i < NUM_TERMINAL_ALLOCATION_SLOTS; i++)
|
|
pConference->TerminalNumberAllocation[i] = 0;
|
|
// Channel 0 is reserved for the H.245 control channel
|
|
pConference->ChannelNumberAllocation[0] = 0x01;
|
|
for (i = 1; i < NUM_CHANNEL_ALLOCATION_SLOTS; i++)
|
|
pConference->ChannelNumberAllocation[i] = 0;
|
|
pConference->bDeferredDelete = FALSE;
|
|
pConference->bAutoAccept = FALSE; // ignored unless ConferenceCallback is NULL
|
|
pConference->LocalEndpointAttached = NEVER_ATTACHED;
|
|
if (pConference->pSessionTable != NULL)
|
|
FreeConferenceSessionTable(pConference);
|
|
_CreateLocalH245H2250MuxCapability(pConference);
|
|
if (pConference->pConferenceH245H2250MuxCapability != NULL) {
|
|
H245FreeCap(pConference->pConferenceH245H2250MuxCapability);
|
|
pConference->pConferenceH245H2250MuxCapability = NULL;
|
|
}
|
|
if ((pConference->pConferenceTermCapList != NULL) ||
|
|
(pConference->pConferenceTermCapDescriptors != NULL))
|
|
FreeConferenceTermCaps(pConference);
|
|
pConference->bSessionTableInternallyConstructed = FALSE;
|
|
pConference->ConferenceCallback = pConference->SaveConferenceCallback;
|
|
|
|
EnumerateCallsInConference(&wNumCalls, &CallList, pConference, ALL_CALLS);
|
|
for (i = 0; i < wNumCalls; i++)
|
|
if (LockCall(CallList[i], &pCall) == CC_OK)
|
|
FreeCall(pCall);
|
|
if (CallList != NULL)
|
|
Free(CallList);
|
|
|
|
EnumerateChannelsInConference(&wNumChannels, &ChannelList, pConference, ALL_CHANNELS);
|
|
for (i = 0; i < wNumChannels; i++)
|
|
if (LockChannel(ChannelList[i], &pChannel) == CC_OK)
|
|
FreeChannel(pChannel);
|
|
if (ChannelList != NULL)
|
|
Free(ChannelList);
|
|
|
|
if (pConference->bForceMC == TRUE)
|
|
pConference->tsMultipointController = TS_TRUE;
|
|
else if (pConference->bMultipointCapable == TRUE)
|
|
pConference->tsMultipointController = TS_UNKNOWN;
|
|
else
|
|
pConference->tsMultipointController = TS_FALSE;
|
|
pConference->tsMaster = TS_UNKNOWN;
|
|
pConference->ConferenceMode = UNCONNECTED_MODE;
|
|
if (pConference->pMultipointControllerAddr != NULL) {
|
|
Free(pConference->pMultipointControllerAddr);
|
|
pConference->pMultipointControllerAddr = NULL;
|
|
}
|
|
|
|
while (DequeueRequest(&pConference->pEnqueuedRequestModeCalls, NULL) == CC_OK);
|
|
|
|
if (pConference->tsMultipointController == TS_TRUE)
|
|
status = CreateConferenceTermCaps(pConference, NULL);
|
|
else
|
|
status = CC_OK;
|
|
|
|
return status;
|
|
}
|