/*++ Copyright (c) 1997 Microsoft Corporation Module Name: callback.c Abstract: Callback routines for Intel Call Control Module. Environment: User Mode - Win32 --*/ /////////////////////////////////////////////////////////////////////////////// // // // Include files // // // /////////////////////////////////////////////////////////////////////////////// #include "globals.h" #include #include "registry.h" #include "termcaps.h" #include "provider.h" #include "callback.h" #include "line.h" #include "apierror.h" /////////////////////////////////////////////////////////////////////////////// // // // Global variables // // // /////////////////////////////////////////////////////////////////////////////// HANDLE g_hCallbackThread = NULL; DWORD g_dwCallbackThreadID = UNINITIALIZED; HANDLE g_WaitableObjects[NUM_WAITABLE_OBJECTS]; /////////////////////////////////////////////////////////////////////////////// // // // Private procedures // // // /////////////////////////////////////////////////////////////////////////////// VOID H323ComputeVideoChannelBitRates( IN const DWORD dwReferenceMaxBitRate, IN const DWORD dwAudioBitRate, OUT DWORD * pdwFinalBitRate, OUT DWORD * pdwStartUpBitRate ) /*++ Routine Description: caculate the bit rate for opening the local channel and the initial bit rate for the MSP. Arguments: dwReferenceMaxBitRate - The max bit rate we got from capability exchange. (in 100 bps unit) dwAudioBitRate - The bit rate of the audio stream. (in 100 bps unit) pdwFinalBitRate - the bit rate we are going to use to open the channel. (in 100 bps unit) pdwStartUpBitRate - the bit rate the MSP should use to start the stream. It will adapt to the max bit rate if everything is fine. (in 1 bps unit) Return Values: Returns true if successful. --*/ { DWORD dwMaxBitRate = dwReferenceMaxBitRate; if (dwMaxBitRate < H323_UNADJ_VIDEORATE_THRESHOLD) { // if the max bit rate is too small, just use it. *pdwStartUpBitRate = dwMaxBitRate * 100; } else if (dwMaxBitRate < H323_TRUE_VIDEORATE_THRESHOLD) { // if the max bit rate is still smaller than our threshold, // the other side must mean . *pdwStartUpBitRate = dwMaxBitRate * 80; if (*pdwStartUpBitRate < H323_UNADJ_VIDEORATE_THRESHOLD * 100) { *pdwStartUpBitRate = H323_UNADJ_VIDEORATE_THRESHOLD * 100; } } else if (dwMaxBitRate < MAXIMUM_BITRATE_28800) { // We assume the MaxBitRate is the total bandwidth of // the pipe. We need to substract the bandwidth needed // by audio from this number. dwMaxBitRate -= dwAudioBitRate; // We don't want to use 100% of the bandwidth for RTP packets // So the video bandwidth is further adjusted. dwMaxBitRate = dwMaxBitRate * (100 - H323_BANDWIDTH_CUSHION_PERCENT) / 100; *pdwStartUpBitRate = dwMaxBitRate * 100; } else if (dwMaxBitRate < MAXIMUM_BITRATE_63000) { // We assume the MaxBitRate is the total bandwidth of // the pipe. We need to substract the bandwidth needed // by audio from this number. dwMaxBitRate -= dwAudioBitRate; // We don't want to use 100% of the bandwidth for RTP packets // So the video bandwidth is further adjusted. dwMaxBitRate = dwMaxBitRate * (100 - H323_BANDWIDTH_CUSHION_PERCENT) / 100; *pdwStartUpBitRate = dwMaxBitRate * 80; if (*pdwStartUpBitRate < H323_UNADJ_VIDEORATE_THRESHOLD * 100) { *pdwStartUpBitRate = H323_UNADJ_VIDEORATE_THRESHOLD * 100; } } else { *pdwStartUpBitRate = dwMaxBitRate * 80; } *pdwFinalBitRate = dwMaxBitRate; } BOOL H323SendNewCallIndication( PH323_CALL pCall ) /*++ Routine Description: Sends NEW_CALL_INDICATION from TSP to MSP. Arguments: pCall - Specifies a pointer to the associated call object. Return Values: Returns true if successful. --*/ { H323TSP_MESSAGE Message; // set the appropriate message type Message.Type = H323TSP_NEW_CALL_INDICATION; // send msp message (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_SENDMSPDATA, MSP_HANDLE_UNKNOWN, (DWORD_PTR)&Message, sizeof(Message) ); // success return TRUE; } BOOL H323SendVideoFastUpdatePictureCommand( PH323_CALL pCall, PH323_CHANNEL pChannel ) /*++ Routine Description: Sends VIDEO_FAST_UPDATE_PICTURE_COMMAND from TSP to MSP. Arguments: pCall - Specifies a pointer to the call object. pChannel - Specifies a pointer to the channel object to open. Return Values: Returns TRUE if successful. --*/ { H323TSP_MESSAGE Message; H323DBG(( DEBUG_LEVEL_TRACE, "TSP->MSP I-Frame request. hmChannel=0x%08lx.\n", pChannel->hmChannel )); // set the appropriate message type Message.Type = H323TSP_VIDEO_FAST_UPDATE_PICTURE_COMMAND; // initialize tsp channel handle Message.VideoFastUpdatePictureCommand.hChannel = pChannel->hmChannel; // send msp message (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_SENDMSPDATA, MSP_HANDLE_UNKNOWN, (DWORD_PTR)&Message, sizeof(Message) ); // success return TRUE; } BOOL H323SendFlowControlCommand( PH323_CALL pCall, PH323_CHANNEL pChannel, DWORD dwBitRate ) /*++ Routine Description: Sends FLOW_CONTROL_COMMAND from TSP to MSP. Arguments: pCall - Specifies a pointer to the call object. pChannel - Specifies a pointer to the channel object to open. dwBitRate - Specifies new media stream bit rate (in bps) for MSP Return Values: Returns true if successful. --*/ { H323TSP_MESSAGE Message; H323DBG(( DEBUG_LEVEL_TRACE, "TSP->MSP Flow Control request. hmChannel=0x%08lx. Req. rate=%08d.\n", pChannel->hmChannel, dwBitRate )); // set the appropriate message type Message.Type = H323TSP_FLOW_CONTROL_COMMAND; // initialize tsp channel handle Message.FlowControlCommand.hChannel = pChannel->hmChannel; // transfer stream settings Message.FlowControlCommand.dwBitRate = dwBitRate; // send msp message (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_SENDMSPDATA, MSP_HANDLE_UNKNOWN, (DWORD_PTR)&Message, sizeof(Message) ); // success return TRUE; } BOOL H323UpdateMediaModes( PH323_CALL pCall ) /*++ Routine Description: Updates media modes based on new channel table. Arguments: pCall - Pointer to call object to update. Return Values: Returns true if successful. --*/ { DWORD i; DWORD dwIncomingModesOld; DWORD dwOutgoingModesOld; PH323_CHANNEL_TABLE pChannelTable = pCall->pChannelTable; // save old media modes dwIncomingModesOld = pCall->dwIncomingModes; dwOutgoingModesOld = pCall->dwOutgoingModes; // clear modia modes pCall->dwIncomingModes = 0; pCall->dwOutgoingModes = 0; // loop through each object in table for (i = 0; i < pChannelTable->dwNumSlots; i++) { // see if there are any open channels in table if (H323IsChannelOpen(pChannelTable->pChannels[i])) { // see if open channel is incoming if (H323IsChannelInbound(pChannelTable->pChannels[i])) { // add media mode to list of media modes pCall->dwIncomingModes |= H323IsVideoPayloadType(pChannelTable->pChannels[i]->Settings.dwPayloadType) ? LINEMEDIAMODE_VIDEO : H323IsInteractiveVoiceRequested(pCall) ? LINEMEDIAMODE_INTERACTIVEVOICE : LINEMEDIAMODE_AUTOMATEDVOICE ; } else { // add media mode to list of media modes pCall->dwOutgoingModes |= H323IsVideoPayloadType(pChannelTable->pChannels[i]->Settings.dwPayloadType) ? LINEMEDIAMODE_VIDEO : H323IsInteractiveVoiceRequested(pCall) ? LINEMEDIAMODE_INTERACTIVEVOICE : LINEMEDIAMODE_AUTOMATEDVOICE ; } } } // see if media modes were modified if ((dwIncomingModesOld | dwOutgoingModesOld) != (pCall->dwIncomingModes | pCall->dwOutgoingModes)) { // announce media change (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_CALLINFO, LINECALLINFOSTATE_MEDIAMODE, 0, 0 ); } H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx incoming modes 0x%08x (old=0x%08lx).\n", pCall, pCall->dwIncomingModes, dwIncomingModesOld )); H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx outgoing modes 0x%08x (old=0x%08lx).\n", pCall, pCall->dwOutgoingModes, dwOutgoingModesOld )); // success return TRUE; } BOOL H323SendAcceptChannelRequest( PH323_CALL pCall, PH323_CHANNEL pChannel ) /*++ Routine Description: Sends ACCEPT_CHANNEL_REQUEST from TSP to MSP. Arguments: pCall - Specifies a pointer to the call object. pChannel - Specifies a pointer to the channel object to open. Return Values: Returns true if successful. --*/ { H323TSP_MESSAGE Message; H323DBG(( DEBUG_LEVEL_TRACE, "Accept channel request to MSP. htChannel=0x%08lx.\n", pChannel->hccChannel )); // set the appropriate message type Message.Type = H323TSP_ACCEPT_CHANNEL_REQUEST; // initialize tsp channel handle Message.AcceptChannelRequest.htChannel = (HANDLE)(DWORD)pChannel->hccChannel; // transfer stream settings Message.AcceptChannelRequest.Settings = pChannel->Settings; // send msp message (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_SENDMSPDATA, MSP_HANDLE_UNKNOWN, (DWORD_PTR)&Message, sizeof(Message) ); // success return TRUE; } BOOL H323SendOpenChannelResponse( PH323_CALL pCall, PH323_CHANNEL pChannel ) /*++ Routine Description: Sends OPEN_CHANNEL_RESPONSE from TSP to MSP. Arguments: pCall - Specifies a pointer to the call object. pChannel - Specifies a pointer to the channel object to open. Return Values: Returns true if successful. --*/ { H323TSP_MESSAGE Message; H323DBG(( DEBUG_LEVEL_TRACE, "Open channel response to MSP. hmChannel=0x%08lx.\n", pChannel->hmChannel )); // set the appropriate message type Message.Type = H323TSP_OPEN_CHANNEL_RESPONSE; // initialize channel handles Message.OpenChannelResponse.hmChannel = pChannel->hmChannel; Message.OpenChannelResponse.htChannel = (HANDLE)(DWORD)pChannel->hccChannel; // transfer stream settings Message.OpenChannelResponse.Settings = pChannel->Settings; // send msp message (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_SENDMSPDATA, MSP_HANDLE_UNKNOWN, (DWORD_PTR)&Message, sizeof(Message) ); // success return TRUE; } BOOL H323SendCloseChannelCommand( PH323_CALL pCall, HANDLE hmChannel, DWORD dwReason ) /*++ Routine Description: Sends CLOSE_CHANNEL_COMMAND from TSP to MSP. Arguments: pCall - Specifies a pointer to the call object. hmChannel - Specifies a handle to the channel to close. dwReason - Specifies reason for closing channel. Return Values: Returns true if successful. --*/ { H323TSP_MESSAGE Message; H323DBG(( DEBUG_LEVEL_TRACE, "Close channel command to MSP. hmChannel=0x%08lx.\n", hmChannel )); // set the appropriate message type Message.Type = H323TSP_CLOSE_CHANNEL_COMMAND; // transfer reason Message.CloseChannelCommand.dwReason = dwReason; // initialize channel handles Message.CloseChannelCommand.hChannel = hmChannel; // send msp message (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_SENDMSPDATA, MSP_HANDLE_UNKNOWN, (DWORD_PTR)&Message, sizeof(Message) ); // success return TRUE; } BOOL H323ProcessOpenChannelRequest( PH323_CALL pCall, PH323MSG_OPEN_CHANNEL_REQUEST pRequest ) /*++ Routine Description: Process command from MSP. Arguments: pCall - Specifies a pointer to the call object to process. pRequest - Specifies a pointer to the command block. Return Values: Returns true if successful. --*/ { BOOL fOpened = FALSE; PH323_CHANNEL pChannel = NULL; PH323_CHANNEL pAssociatedChannel = NULL; BYTE bSessionID; H323DBG(( DEBUG_LEVEL_TRACE, "open channel reqest from MSP. hmChannel=0x%08lx.\n", pRequest->hmChannel )); // determine session id from media type bSessionID = (pRequest->Settings.MediaType == MEDIA_AUDIO) ? H245_SESSIONID_AUDIO : H245_SESSIONID_VIDEO ; // look for existing session H323LookupChannelBySessionID( &pAssociatedChannel, pCall->pChannelTable, bSessionID ); // allocate new channel object if (!H323AllocChannelFromTable( &pChannel, &pCall->pChannelTable, pCall)) { H323DBG(( DEBUG_LEVEL_WARNING, "could not allocate channel hmChannel=0x%08lx.\n", pRequest->hmChannel )); // close the requested channel H323SendCloseChannelCommand( pCall, pRequest->hmChannel, ERROR_NOT_ENOUGH_MEMORY ); // failure return FALSE; } // save msp channel handle pChannel->hmChannel = pRequest->hmChannel; // initialize common information pChannel->bSessionID = bSessionID; pChannel->pCall = pCall; // examine existing session if (pAssociatedChannel != NULL) { H323DBG(( DEBUG_LEVEL_VERBOSE, "overriding default local RTCP port for session %d\n", pChannel->bSessionID )); // override default RTCP port with existing one pChannel->ccLocalRTCPAddr.Addr.IP_Binary.wPort = pAssociatedChannel->ccLocalRTCPAddr.Addr.IP_Binary.wPort; } // see if outgoing audio requested if (bSessionID == H245_SESSIONID_AUDIO) { // transfer capabilities from call object pChannel->ccTermCaps = pCall->ccRemoteAudioCaps; // check terminal capabilities for g723 if (pChannel->ccTermCaps.ClientType == H245_CLIENT_AUD_G723) { // complete stream description structure pChannel->Settings.MediaType = MEDIA_AUDIO; pChannel->Settings.dwPayloadType = G723_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = G723_RTP_PAYLOAD_TYPE; pChannel->Settings.Audio.dwMillisecondsPerPacket = G723_MILLISECONDS_PER_PACKET( pChannel->ccTermCaps.Cap.H245Aud_G723.maxAl_sduAudioFrames ); pChannel->Settings.Audio.G723Settings.bG723LowSpeed = H323IsSlowLink(pCall->dwLinkSpeed); H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing G723 stream (%d milliseconds).\n", pChannel->Settings.Audio.dwMillisecondsPerPacket )); // open outgoing channel fOpened = H323OpenChannel(pChannel); } else if (pChannel->ccTermCaps.ClientType == H245_CLIENT_AUD_G711_ULAW64) { // complete stream description structure pChannel->Settings.MediaType = MEDIA_AUDIO; pChannel->Settings.dwPayloadType = G711U_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = G711U_RTP_PAYLOAD_TYPE; pChannel->Settings.Audio.dwMillisecondsPerPacket = G711_MILLISECONDS_PER_PACKET( pChannel->ccTermCaps.Cap.H245Aud_G711_ULAW64 ); H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing G711U stream (%d milliseconds).\n", pChannel->Settings.Audio.dwMillisecondsPerPacket )); // open outgoing channel fOpened = H323OpenChannel(pChannel); } else if (pChannel->ccTermCaps.ClientType == H245_CLIENT_AUD_G711_ALAW64) { // complete stream description structure pChannel->Settings.MediaType = MEDIA_AUDIO; pChannel->Settings.dwPayloadType = G711A_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = G711A_RTP_PAYLOAD_TYPE; pChannel->Settings.Audio.dwMillisecondsPerPacket = G711_MILLISECONDS_PER_PACKET( pChannel->ccTermCaps.Cap.H245Aud_G711_ALAW64 ); H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing G711A stream (%d milliseconds).\n", pChannel->Settings.Audio.dwMillisecondsPerPacket )); // open outgoing channel fOpened = H323OpenChannel(pChannel); } } else if (bSessionID == H245_SESSIONID_VIDEO) { // transfer capabilities from call object pChannel->ccTermCaps = pCall->ccRemoteVideoCaps; // check terminal capabilities for h263 if (pChannel->ccTermCaps.ClientType == H245_CLIENT_VID_H263) { DWORD dwFinalMaxBitRate; DWORD dwStartUpBitRate; DWORD dwAudioBitRate; // complete stream description structure pChannel->Settings.MediaType = MEDIA_VIDEO; pChannel->Settings.dwPayloadType = H263_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = H263_RTP_PAYLOAD_TYPE; pChannel->Settings.Video.bCIF = FALSE; if (pCall->ccRemoteAudioCaps.ClientType == H245_CLIENT_AUD_G723) { DWORD dwFramesPerPacket = pCall->ccRemoteAudioCaps.Cap.H245Aud_G723.maxAl_sduAudioFrames; dwAudioBitRate = (TOTAL_HEADER_SIZE + dwFramesPerPacket * G723_BYTES_PER_FRAME) * CHAR_BIT * 1000 / (dwFramesPerPacket * G723_MILLISECONDS_PER_FRAME); } else { // Since for other types of audio encoding the // call to H323ComputeVideoChannelBitRates will have // no effect, we set the audio bit rate to a dummy // value. dwAudioBitRate = H323_MINIMUM_AUDIO_BANDWIDTH; } // ajust three percent to make QOS happy. dwAudioBitRate = dwAudioBitRate * 103 / 100; H323ComputeVideoChannelBitRates( pChannel->ccTermCaps.Cap.H245Vid_H263.maxBitRate, dwAudioBitRate / 100 + 1, &dwFinalMaxBitRate, &dwStartUpBitRate ); pChannel->ccTermCaps.Cap.H245Vid_H263.maxBitRate = dwFinalMaxBitRate; pChannel->Settings.Video.dwMaxBitRate = dwFinalMaxBitRate * 100; pChannel->Settings.Video.dwStartUpBitRate = dwStartUpBitRate; H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing H263 stream (%d bps).\n", pChannel->Settings.Video.dwMaxBitRate )); // open outgoing channel fOpened = H323OpenChannel(pChannel); } else if (pChannel->ccTermCaps.ClientType == H245_CLIENT_VID_H261) { DWORD dwFinalMaxBitRate; DWORD dwStartUpBitRate; DWORD dwAudioBitRate; // complete stream description structure pChannel->Settings.MediaType = MEDIA_VIDEO; pChannel->Settings.dwPayloadType = H261_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = H261_RTP_PAYLOAD_TYPE; pChannel->Settings.Video.bCIF = FALSE; if (pCall->ccRemoteAudioCaps.ClientType == H245_CLIENT_AUD_G723) { DWORD dwFramesPerPacket = pCall->ccRemoteAudioCaps.Cap.H245Aud_G723.maxAl_sduAudioFrames; dwAudioBitRate = (TOTAL_HEADER_SIZE + dwFramesPerPacket * G723_BYTES_PER_FRAME) * CHAR_BIT * 1000 / (dwFramesPerPacket * G723_MILLISECONDS_PER_FRAME); } else { // Since for other types of audio encoding the // call to H323ComputeVideoChannelBitRates will have // no effect, we set the audio bit rate to a dummy // value. dwAudioBitRate = H323_MINIMUM_AUDIO_BANDWIDTH; } // ajust three percent to make QOS happy. dwAudioBitRate = dwAudioBitRate * 103 / 100; H323ComputeVideoChannelBitRates( (DWORD)pChannel->ccTermCaps.Cap.H245Vid_H261.maxBitRate, dwAudioBitRate / 100 + 1, &dwFinalMaxBitRate, &dwStartUpBitRate ); pChannel->ccTermCaps.Cap.H245Vid_H261.maxBitRate = (WORD)dwFinalMaxBitRate; pChannel->Settings.Video.dwMaxBitRate = dwFinalMaxBitRate * 100; pChannel->Settings.Video.dwStartUpBitRate = dwStartUpBitRate; H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing H261 stream (%d bps).\n", pChannel->Settings.Video.dwMaxBitRate )); // open outgoing channel fOpened = H323OpenChannel(pChannel); } } // validate if (!fOpened) { H323DBG(( DEBUG_LEVEL_WARNING, "could not open channel hmChannel=0x%08lx.\n", pRequest->hmChannel )); // close the requested channel H323SendCloseChannelCommand( pCall, pRequest->hmChannel, ERROR_GEN_FAILURE ); // failure return FALSE; } // success return TRUE; } BOOL H323ProcessAcceptChannelResponse( PH323_CALL pCall, PH323MSG_ACCEPT_CHANNEL_RESPONSE pResponse ) /*++ Routine Description: Process command from MSP. Arguments: pCall - Specifies a pointer to the call object to process. pResponse - Specifies a pointer to the command block. Return Values: Returns true if successful. --*/ { PH323_CHANNEL pChannel = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "accept channel response from MSP. hmChannel=0x%08lx. htChannel=0x%08lx.\n", pResponse->hmChannel, pResponse->htChannel )); // retrieve channel given handle if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, (CC_HCHANNEL)PtrToUlong(pResponse->htChannel))) { H323DBG(( DEBUG_LEVEL_ERROR, "could not accept unknown htChannel 0x%08lx", pResponse->htChannel )); // done return TRUE; } // accept channel CC_AcceptChannel( pChannel->hccChannel, // hChannel &pChannel->ccLocalRTPAddr, // pRTPAddr &pChannel->ccLocalRTCPAddr, // pRTCPAddr 0 // dwChannelBitRate ); H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx accepted htChannel 0x%08lx.\n", pCall, pChannel->hccChannel )); // change state to opened pChannel->nState = H323_CHANNELSTATE_OPENED; // update media modes H323UpdateMediaModes(pCall); // success return TRUE; } BOOL H323ProcessVideoFastUpdatePictureCommand( PH323_CALL pCall, PH323MSG_VIDEO_FAST_UPDATE_PICTURE_COMMAND pCommand ) /*++ Routine Description: Process I-frame request command from MSP. Arguments: pCall - Specifies a pointer to the call object to process. pCommand - Specifies a pointer to the command block. Return Values: Returns TRUE if successful. --*/ { PH323_CHANNEL pChannel = NULL; MiscellaneousCommand h245miscellaneousCommand; H323DBG(( DEBUG_LEVEL_TRACE, "MSP->TSP I-frame request. hChannel=0x%08lx.\n", pCommand->hChannel )); h245miscellaneousCommand.type.choice = videoFastUpdatePicture_chosen; // retrieve channel given handle if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, (CC_HCHANNEL)PtrToUlong(pCommand->hChannel))) { H323DBG(( DEBUG_LEVEL_ERROR, "Could not process MSP->TSP I-frame request. Unknown hChannel 0x%08lx.\n", pCommand->hChannel )); // done return TRUE; } // send H245 Miscellaneous Command to the remote entity CC_H245MiscellaneousCommand( pCall->hccCall, // hCall pChannel->hccChannel, // hChannel &h245miscellaneousCommand // Command ); H323DBG(( DEBUG_LEVEL_VERBOSE, "MSP->TSP I-frame request processed. call 0x%08lx -- hccChannel 0x%08lx. \n", pCall, pChannel->hccChannel )); // success return TRUE; } BOOL H323ProcessFlowControlCommand( PH323_CALL pCall, PH323MSG_FLOW_CONTROL_COMMAND pCommand ) /*++ Routine Description: Process flow control command from MSP. Arguments: pCall - Specifies a pointer to the call object to process. pCommand - Specifies a pointer to the command block. Return Values: Returns true if successful. --*/ { PH323_CHANNEL pChannel = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "MSP->TSP Flow control cmd. hChannel=0x%08lx. Req. rate=%08d.\n", pCommand->hChannel, pCommand->dwBitRate )); // retrieve channel given handle if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, (CC_HCHANNEL)PtrToUlong(pCommand->hChannel))) { H323DBG(( DEBUG_LEVEL_ERROR, "Could not process MSP->TSP flow control cmd. Unknown hChannel 0x%08lx", pCommand->hChannel )); // done return TRUE; } // send flow control command to the remote entity CC_FlowControl( pChannel->hccChannel, // hChannel pCommand->dwBitRate // requested bit rate, bps ); H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx -- hccChannel 0x%08lx, MSP->TSP flow control cmd processed.\n", pCall, pChannel->hccChannel )); // success return TRUE; } BOOL H323ProcessQoSEventIndication( PH323_CALL pCall, PH323MSG_QOS_EVENT pCommand ) /*++ Routine Description: Process QoS event indication from MSP. Arguments: pCall - Specifies a pointer to the call object to process. pCommand - Specifies a pointer to the command block. Return Values: Returns true if successful. --*/ { PH323_CHANNEL pChannel = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "MSP->TSP QoS event. htChannel=0x%08lx. dwEvent=%08d.\n", pCommand->htChannel, pCommand->dwEvent )); // retrieve channel given handle if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, (CC_HCHANNEL)PtrToUlong(pCommand->htChannel))) { H323DBG(( DEBUG_LEVEL_ERROR, "Could not process MSP->TSP QoS event. Unknown htChannel 0x%08lx", pCommand->htChannel )); // done return TRUE; } // report qos event (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_QOSINFO, pCommand->dwEvent, (pChannel->Settings.MediaType == MEDIA_AUDIO) ? LINEMEDIAMODE_INTERACTIVEVOICE : LINEMEDIAMODE_VIDEO, 0 ); // success return TRUE; } BOOL H323ProcessCloseChannelCommand( PH323_CALL pCall, PH323MSG_CLOSE_CHANNEL_COMMAND pCommand ) /*++ Routine Description: Process command from MSP. Arguments: pCall - Specifies a pointer to the call object to process. pCommand - Specifies a pointer to the command block. Return Values: Returns true if successful. --*/ { PH323_CHANNEL pChannel = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "close channel command from MSP. htChannel=0x%08lx.\n", pCommand->hChannel )); // retrieve channel given handle if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, (CC_HCHANNEL)PtrToUlong(pCommand->hChannel))) { H323DBG(( DEBUG_LEVEL_ERROR, "could not close unknown htChannel 0x%08lx", pCommand->hChannel )); // done return TRUE; } H323DBG(( DEBUG_LEVEL_WARNING, "closing htChannel 0x%08lx.\n", pCommand->hChannel )); // close channel H323CloseChannel(pChannel); // release channel resources H323FreeChannelFromTable(pChannel,pCall->pChannelTable); // success return TRUE; } VOID H323ProcessPlaceCallMessage( HDRVCALL hdCall ) /*++ Routine Description: Processes async place call messages. Arguments: hdCall - Handle to call to be placed. Return Values: None. --*/ { PH323_CALL pCall = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "place call message received (hdCall=0x%08lx).\n", PtrToUlong(hdCall) )); // retrieve call pointer from handle if (H323GetCallAndLock(&pCall, hdCall)) { // place outgoing call if (!H323PlaceCall(pCall)) { // drop call using disconnect mode H323DropCall(pCall, LINEDISCONNECTMODE_TEMPFAILURE); } // unlock line device H323UnlockLine(pCall->pLine); } else { H323DBG(( DEBUG_LEVEL_ERROR, "invalid handle in place call message.\n" )); } } VOID H323ProcessAcceptCallMessage( HDRVCALL hdCall ) /*++ Routine Description: Processes async accept call messages. Arguments: hdCall - Handle to call to be placed. Return Values: None. --*/ { PH323_CALL pCall = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "accept call message received (hdCall=0x%08lx).\n", PtrToUlong(hdCall) )); // retrieve call pointer from handle if (H323GetCallAndLock(&pCall, hdCall)) { // place outgoing call if (!H323AcceptCall(pCall)) { // drop call using disconnect mode H323DropCall(pCall, LINEDISCONNECTMODE_TEMPFAILURE); } // unlock line device H323UnlockLine(pCall->pLine); } else { H323DBG(( DEBUG_LEVEL_ERROR, "invalid handle in accept call message.\n" )); } } VOID H323ProcessDropCallMessage( HDRVCALL hdCall, DWORD dwDisconnectMode ) /*++ Routine Description: Processes async drop call messages. Arguments: hdCall - Handle to call to be hung up. dwDisconnectMode - Status to be placed in disconnected message. Return Values: None. --*/ { PH323_CALL pCall = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "drop call message received (hdCall=0x%08lx).\n", PtrToUlong(hdCall) )); // retrieve call pointer from handle if (H323GetCallAndLock(&pCall, hdCall)) { // drop call using disconnect code H323DropCall(pCall, dwDisconnectMode); // unlock line device H323UnlockLine(pCall->pLine); } else { H323DBG(( DEBUG_LEVEL_ERROR, "invalid handle in place call message.\n" )); } } VOID H323ProcessCloseCallMessage( HDRVCALL hdCall ) /*++ Routine Description: Processes async close call messages. Arguments: hdCall - Handle to call to be closed. Return Values: None. --*/ { PH323_CALL pCall = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "close call message received (hdCall=0x%08lx).\n", PtrToUlong(hdCall) )); // retrieve call pointer from handle if (H323GetCallAndLock(&pCall, hdCall)) { // close call H323CloseCall(pCall); // unlock line device H323UnlockLine(pCall->pLine); } else { H323DBG(( DEBUG_LEVEL_ERROR, "invalid handle in place call message.\n" )); } } VOID H323ProcessCallListenMessage( HDRVLINE hdLine ) /*++ Routine Description: Processes async call listen messages. Arguments: hdLine - Line to be placed into listening state. Return Values: None. --*/ { PH323_LINE pLine = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "call listen message received (hdLine=0x%08lx).\n", PtrToUlong(hdLine) )); // retrieve line pointer from handle if (H323GetLineAndLock(&pLine, hdLine)) { // start listening for calls if (!H323CallListen(pLine)) { // change state to opened pLine->nState = H323_LINESTATE_OPENED; } // unlock line device H323UnlockLine(pLine); } else { H323DBG(( DEBUG_LEVEL_ERROR, "invalid handle in call listen message.\n" )); } } DWORD WINAPI H323CallbackThread( LPVOID pParam ) /*++ Routine Description: Worker thread to handle async operations. Arguments: pParam - Pointer to opaque thread parameter (unused). Return Values: Win32 error codes. --*/ { MSG msg; DWORD dwStatus; // associate event with key H323ListenForRegistryChanges( g_WaitableObjects[WAIT_OBJECT_REGISTRY_CHANGE] ); // loop... for (;;) { // wait for message or termination dwStatus = MsgWaitForMultipleObjectsEx( NUM_WAITABLE_OBJECTS, g_WaitableObjects, INFINITE, QS_ALLINPUT, MWMO_ALERTABLE ); // see if new message has arrived if (dwStatus == WAIT_OBJECT_INCOMING_MESSAGE) { // retrieve next item in thread message queue while (PeekMessage(&msg, NULL, 0, 0, PM_REMOVE)) { // handle service provider messages if (H323IsPlaceCallMessage(&msg)) { // process place call message H323ProcessPlaceCallMessage((HDRVCALL)msg.wParam); } else if (H323IsAcceptCallMessage(&msg)) { // process accept call message H323ProcessAcceptCallMessage((HDRVCALL)msg.wParam); } else if (H323IsDropCallMessage(&msg)) { // process drop call message H323ProcessDropCallMessage((HDRVCALL)msg.wParam,(DWORD)msg.lParam); } else if (H323IsCloseCallMessage(&msg)) { // process close call message H323ProcessCloseCallMessage((HDRVCALL)msg.wParam); } else if (H323IsCallListenMessage(&msg)) { // process call listen message H323ProcessCallListenMessage((HDRVLINE)msg.wParam); } else { // translate message TranslateMessage(&msg); // dispatch message DispatchMessage(&msg); } } } else if (dwStatus == WAIT_OBJECT_REGISTRY_CHANGE) { // lock provider H323LockProvider(); // refresh registry settings H323GetConfigFromRegistry(); // associate event with registry key H323ListenForRegistryChanges(g_WaitableObjects[WAIT_OBJECT_REGISTRY_CHANGE]); // unlock provider H323UnlockProvider(); } else if (dwStatus == WAIT_IO_COMPLETION) { H323DBG(( DEBUG_LEVEL_VERBOSE, "callback thread %x io completion.\n", g_dwCallbackThreadID )); } else if (dwStatus == WAIT_OBJECT_TERMINATE_EVENT) { H323DBG(( DEBUG_LEVEL_TRACE, "callback thread %x terminating on command.\n", g_dwCallbackThreadID )); break; // bail... } else { H323DBG(( DEBUG_LEVEL_TRACE, "callback thread %x terminating (dwStatus=0x%08lx).\n", g_dwCallbackThreadID, dwStatus )); break; // bail... } } // stop listening for registry changes H323StopListeningForRegistryChanges(); H323DBG(( DEBUG_LEVEL_VERBOSE, "callback thread %x exiting.\n", g_dwCallbackThreadID )); // success return NOERROR; } DWORD H323RejectReasonToDisconnectMode( BYTE bRejectReason ) /*++ Routine Description: Converts connect reject reason to tapi disconnect mode. Arguments: bRejectReason - Specifies reason peer rejected call. Return Values: Returns disconnect mode corresponding to reject reason. --*/ { H323DBG(( DEBUG_LEVEL_VERBOSE, "Reject Reason: %s.\n", CCRejectReasonToString((DWORD)bRejectReason) )); // determine reject reason switch (bRejectReason) { case CC_REJECT_NORMAL_CALL_CLEARING: // call was terminated normally return LINEDISCONNECTMODE_NORMAL; case CC_REJECT_UNREACHABLE_DESTINATION: // remote user could not be reached return LINEDISCONNECTMODE_UNREACHABLE; case CC_REJECT_DESTINATION_REJECTION: // remote user has rejected the call return LINEDISCONNECTMODE_REJECT; case CC_REJECT_USER_BUSY: // remote user's station is busy return LINEDISCONNECTMODE_BUSY; case CC_REJECT_NO_ANSWER: // remote user's station does not answer return LINEDISCONNECTMODE_NOANSWER; case CC_REJECT_BAD_FORMAT_ADDRESS: // destination address in invalid return LINEDISCONNECTMODE_BADADDRESS; default: // reason for the disconnect is unavailable return LINEDISCONNECTMODE_UNAVAIL; } } BOOL H323GetTermCapById( H245_CAPID_T CapId, PCC_TERMCAPLIST pTermCapList, PCC_TERMCAP * ppTermCap ) /*++ Routine Description: Retrieve pointer to termcap from list via id. Arguments: CapId - Id of termcap of interest. pTermCapList - Pointer to termcap list. ppTermCap - Pointer to place to copy termcap pointer. Return Values: Returns TRUE if successful. --*/ { WORD wIndex; PCC_TERMCAP pTermCap; // walk caps for (wIndex = 0; wIndex < pTermCapList->wLength; wIndex++) { // compare id with the next item in the list if (pTermCapList->pTermCapArray[wIndex]->CapId == CapId) { // return pointer *ppTermCap = pTermCapList->pTermCapArray[wIndex]; // success return TRUE; } } // failure return FALSE; } BOOL H323GetTermCapByType( H245_DATA_T DataType, H245_CLIENT_T ClientType, PCC_TERMCAPLIST pTermCapList, PCC_TERMCAP * ppTermCap ) /*++ Routine Description: Retrieve pointer to termcap from list via id. Arguments: DataType - Type of capability. ClientType - Type of media-specific cabability. pTermCapList - Pointer to termcap list. ppTermCap - Pointer to place to copy termcap pointer. Return Values: Returns TRUE if successful. --*/ { WORD wIndex; PCC_TERMCAP pTermCap; // walk caps for (wIndex = 0; wIndex < pTermCapList->wLength; wIndex++) { // compare id with the next item in the list if ((pTermCapList->pTermCapArray[wIndex]->DataType == DataType) && (pTermCapList->pTermCapArray[wIndex]->ClientType == ClientType)) { // return pointer *ppTermCap = pTermCapList->pTermCapArray[wIndex]; // success return TRUE; } } // failure return FALSE; } BOOL H323SavePreferredTermCap( PH323_CALL pCall, PCC_TERMCAP pLocalCap, PCC_TERMCAP pRemoteCap ) /*++ Routine Description: Save remote cap adjusted for outgoing settings. Arguments: pCall - Pointer to call object. pLocalCap - Pointer to local capability. pRemoteCap - Pointer to termcap to save. Return Values: Returns TRUE if both audio and video termcaps resolved. --*/ { PCC_TERMCAP pPreferredCap; WORD wMillisecondsPerPacket; // retrieve pointer to stored termcap preferences pPreferredCap = H323IsValidAudioClientType(pRemoteCap->ClientType) ? &pCall->ccRemoteAudioCaps : &pCall->ccRemoteVideoCaps ; // make sure we have not already saved preferred type if (H323IsValidClientType(pPreferredCap->ClientType)) { H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx ignoring remote cap %d\n", pCall, pRemoteCap->CapId )); // failure return FALSE; } H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx saving remote cap %d\n", pCall, pRemoteCap->CapId )); // modify preferred caps *pPreferredCap = *pLocalCap; // reverse capability direction pPreferredCap->Dir = H245_CAPDIR_LCLTX; // determine client type switch (pPreferredCap->ClientType) { case H245_CLIENT_AUD_G723: // determine packet size wMillisecondsPerPacket = H323IsSlowLink(pCall->dwLinkSpeed) ? G723_SLOWLNK_MILLISECONDS_PER_PACKET : G723_DEFAULT_MILLISECONDS_PER_PACKET ; // adjust default parameters for link speed pPreferredCap->Cap.H245Aud_G723.maxAl_sduAudioFrames = G723_FRAMES_PER_PACKET(wMillisecondsPerPacket) ; // see if remote maximum less than default if (pRemoteCap->Cap.H245Aud_G723.maxAl_sduAudioFrames < pPreferredCap->Cap.H245Aud_G723.maxAl_sduAudioFrames) { // use remote maximum instead of default pPreferredCap->Cap.H245Aud_G723.maxAl_sduAudioFrames = pRemoteCap->Cap.H245Aud_G723.maxAl_sduAudioFrames ; } break; case H245_CLIENT_AUD_G711_ULAW64: // store default parameters pPreferredCap->Cap.H245Aud_G711_ULAW64 = G711_FRAMES_PER_PACKET( G711_DEFAULT_MILLISECONDS_PER_PACKET ); // see if remote maximum less than default if (pRemoteCap->Cap.H245Aud_G711_ULAW64 < pPreferredCap->Cap.H245Aud_G711_ULAW64) { // use remote maximum instead of default pPreferredCap->Cap.H245Aud_G711_ULAW64 = pRemoteCap->Cap.H245Aud_G711_ULAW64 ; } break; case H245_CLIENT_AUD_G711_ALAW64: // store default parameters pPreferredCap->Cap.H245Aud_G711_ALAW64 = G711_FRAMES_PER_PACKET( G711_DEFAULT_MILLISECONDS_PER_PACKET ); // see if remote maximum less than default if (pRemoteCap->Cap.H245Aud_G711_ALAW64 < pPreferredCap->Cap.H245Aud_G711_ALAW64) { // use remote maximum instead of default pPreferredCap->Cap.H245Aud_G711_ALAW64 = pRemoteCap->Cap.H245Aud_G711_ALAW64 ; } break; case H245_CLIENT_VID_H263: // see if remote maximum less than local if (pRemoteCap->Cap.H245Vid_H263.maxBitRate < pPreferredCap->Cap.H245Vid_H263.maxBitRate) { // use remote maximum instead of local pPreferredCap->Cap.H245Vid_H263.maxBitRate = pRemoteCap->Cap.H245Vid_H263.maxBitRate ; } break; case H245_CLIENT_VID_H261: // see if remote maximum less than local if (pRemoteCap->Cap.H245Vid_H261.maxBitRate < pPreferredCap->Cap.H245Vid_H261.maxBitRate) { // use remote maximum instead of local pPreferredCap->Cap.H245Vid_H261.maxBitRate = pRemoteCap->Cap.H245Vid_H261.maxBitRate ; } break; } // return success if we have resolved both audio and video caps return (H323IsValidClientType(pCall->ccRemoteAudioCaps.ClientType) && H323IsValidClientType(pCall->ccRemoteVideoCaps.ClientType)); } VOID H323GetPreferedTransmitTypes( PCC_TERMCAPDESCRIPTORS pTermCapDescriptors, H245_CLIENT_T *pPreferredAudioType, H245_CLIENT_T *pPreferredVideoType ) /*++ Routine Description: Find the preferred audio and video format in the local terminal capability descriptors array if they exist. Arguments: pTermCapDescriptors - pointer to the local terminal capability descriptor. pPreferredAudioType - return the preferred audio type. pPreferredVideoType - return the preferred video type. Return Values: NONE --*/ { WORD wDescIndex; WORD wSimCapIndex; WORD wAltCapIndex; // process descriptors for (wDescIndex = 0; wDescIndex < pTermCapDescriptors->wLength; wDescIndex++) { H245_TOTCAPDESC_T * pTotCapDesc; // retrieve pointer to capability structure pTotCapDesc = pTermCapDescriptors->pTermCapDescriptorArray[wDescIndex]; // process simultaneous caps for (wSimCapIndex = 0; wSimCapIndex < pTotCapDesc->CapDesc.Length; wSimCapIndex++) { H245_SIMCAP_T * pSimCap; // retrieve pointer to simulateous cap pSimCap = &pTotCapDesc->CapDesc.SimCapArray[wSimCapIndex]; if (pSimCap->Length > 0) { // the first one in the altcaps array is the preferred one. switch (pSimCap->AltCaps[0]) { case H245_TERMCAPID_G723: *pPreferredAudioType = H245_CLIENT_AUD_G723; break; case H245_TERMCAPID_H263: *pPreferredVideoType = H245_CLIENT_VID_H263; break; case H245_TERMCAPID_G711_ULAW64: *pPreferredAudioType = H245_CLIENT_AUD_G711_ULAW64; break; case H245_TERMCAPID_G711_ALAW64: *pPreferredAudioType = H245_CLIENT_AUD_G711_ALAW64; break; case H245_TERMCAPID_H261: *pPreferredVideoType = H245_CLIENT_VID_H261; break; } } } } } BOOL H323ProcessRemoteTermCaps( PH323_CALL pCall, PCC_TERMCAPLIST pRemoteTermCapList, PCC_TERMCAPDESCRIPTORS pRemoteTermCapDescriptors ) /*++ Routine Description: Process remote capabilities and establish outgoing termcaps. Arguments: pCall - Pointer to call object. pRemoteTermCapList - Pointer to termcap list. pRemoteTermCapDescriptors - Pointer to descriptor list. Return Values: Returns TRUE if successful. --*/ { WORD wDescIndex; WORD wSimCapIndex; WORD wAltCapIndex; PCC_TERMCAP pLocalCap = NULL; PCC_TERMCAP pRemoteCap = NULL; CC_TERMCAPLIST LocalTermCapList; CC_TERMCAPDESCRIPTORS LocalTermCapDescriptors; CC_TERMCAP SavedAudioCap; CC_TERMCAP SavedVideoCap; H245_CLIENT_T PreferredAudioType = H245_CLIENT_DONTCARE; H245_CLIENT_T PreferredVideoType = H245_CLIENT_DONTCARE; // initialize cached termcaps memset(&SavedAudioCap,0,sizeof(CC_TERMCAP)); memset(&SavedVideoCap,0,sizeof(CC_TERMCAP)); // retrieve local caps H323GetTermCapList(pCall,&LocalTermCapList,&LocalTermCapDescriptors); H323GetPreferedTransmitTypes( &LocalTermCapDescriptors, &PreferredAudioType, &PreferredVideoType ); // look for our preferred audio format. if (PreferredAudioType != H245_CLIENT_DONTCARE) { // look for match if (H323GetTermCapByType(H245_DATA_AUDIO, PreferredAudioType, pRemoteTermCapList, &pRemoteCap ) && H323GetTermCapByType(H245_DATA_AUDIO, PreferredAudioType, &LocalTermCapList, &pLocalCap )) { // adjust termcaps and save if (H323SavePreferredTermCap( pCall, pLocalCap, pRemoteCap )) { // // The function above will only return // true when both audio and video caps // have been successfully resolved. // return TRUE; } } } // look for our preferred video format. if (PreferredVideoType != H245_CLIENT_DONTCARE) { // look for match if (H323GetTermCapByType(H245_DATA_VIDEO, PreferredVideoType, pRemoteTermCapList, &pRemoteCap ) && H323GetTermCapByType(H245_DATA_VIDEO, PreferredVideoType, &LocalTermCapList, &pLocalCap )) { // adjust termcaps and save if (H323SavePreferredTermCap( pCall, pLocalCap, pRemoteCap )) { // // The function above will only return // true when both audio and video caps // have been successfully resolved. // return TRUE; } } } // process descriptors for (wDescIndex = 0; wDescIndex < pRemoteTermCapDescriptors->wLength; wDescIndex++) { H245_TOTCAPDESC_T * pTotCapDesc; // retrieve pointer to capability structure pTotCapDesc = pRemoteTermCapDescriptors->pTermCapDescriptorArray[wDescIndex]; H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx processing CapDescId %d\n", pCall, pTotCapDesc->CapDescId )); // process simultaneous caps for (wSimCapIndex = 0; wSimCapIndex < pTotCapDesc->CapDesc.Length; wSimCapIndex++) { H245_SIMCAP_T * pSimCap; // retrieve pointer to simulateous cap pSimCap = &pTotCapDesc->CapDesc.SimCapArray[wSimCapIndex]; H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx processing SimCap %d\n", pCall, wSimCapIndex + 1 )); // process alternative caps for (wAltCapIndex = 0; wAltCapIndex < pSimCap->Length; wAltCapIndex++) { H245_CAPID_T CapId; // re-initialize pRemoteCap = NULL; // retrieve alternative capid CapId = pSimCap->AltCaps[wAltCapIndex]; H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx processing AltCapId %d\n", pCall, CapId )); // lookup termcap from id if (H323GetTermCapById( CapId, pRemoteTermCapList, &pRemoteCap )) { H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx examining remote cap %d:\n\t%s\n\t%s\n\t%s\n", pCall, pRemoteCap->CapId, H323DirToString(pRemoteCap->Dir), H323DataTypeToString(pRemoteCap->DataType), H323ClientTypeToString(pRemoteCap->ClientType) )); // validate remote termcap and check priority if (H323IsReceiveCapability(pRemoteCap->Dir)) { // re-initialize pLocalCap = NULL; // look for match if (H323GetTermCapByType( pRemoteCap->DataType, pRemoteCap->ClientType, &LocalTermCapList, &pLocalCap )) { // adjust termcaps and save if (H323SavePreferredTermCap( pCall, pLocalCap, pRemoteCap )) { // // The function above will only return // true when both audio and video caps // have been successfully resolved. // return TRUE; } } } } } } // see if we discovered any audio-only caps we would like to save if (H323IsValidClientType(pCall->ccRemoteAudioCaps.ClientType) && !H323IsValidClientType(SavedAudioCap.ClientType)) { // save discovered audio-only cap SavedAudioCap = pCall->ccRemoteAudioCaps; // reset video-only cap (prefer audio-only) memset(&SavedVideoCap,0,sizeof(CC_TERMCAP)); } // see if we discovered any video-only caps we would like to save if (H323IsValidClientType(pCall->ccRemoteVideoCaps.ClientType) && !H323IsValidClientType(SavedAudioCap.ClientType)) { // save video-only cap (only if no saved audio-only cap) SavedVideoCap = pCall->ccRemoteVideoCaps; } // reset capability stored in call for next iteration memset(&pCall->ccRemoteAudioCaps,0,sizeof(CC_TERMCAP)); memset(&pCall->ccRemoteVideoCaps,0,sizeof(CC_TERMCAP)); } // see if we saved any audio-only capabilities if (H323IsValidClientType(SavedAudioCap.ClientType)) { // restore saved audio-only capabilities pCall->ccRemoteAudioCaps = SavedAudioCap; // success return TRUE; } // see if we saved any video-only capabilities if (H323IsValidClientType(SavedVideoCap.ClientType)) { // restore saved video-only capabilities pCall->ccRemoteVideoCaps = SavedVideoCap; // success return TRUE; } // failure return FALSE; } HRESULT H323ConnectCallback( PH323_CALL pCall, HRESULT hrConf, PCC_CONNECT_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for connect indications. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { DWORD dwDisconnectMode; PH323_CHANNEL pChannel = NULL; BOOL bRemoteVideoSupported; DWORD dwRemoteVideoBitRate; WORD wRemoteMaxAl_sduAudioFrames; // validate status if (hrConf != CC_OK) { H323DBG(( DEBUG_LEVEL_ERROR, "error %s (0x%08lx) connecting call 0x%08lx.\n", H323StatusToString(hrConf), hrConf, pCall )); // see if we timed out if (hrConf == MAKE_WINSOCK_ERROR(WSAETIMEDOUT)) { // if so, report it as an unreachable destination dwDisconnectMode = LINEDISCONNECTMODE_UNREACHABLE; } else if (hrConf == CC_PEER_REJECT) { // translate reject code into failure dwDisconnectMode = H323RejectReasonToDisconnectMode( pCallbackParams->bRejectReason ); } else { // default to temp failure dwDisconnectMode = LINEDISCONNECTMODE_TEMPFAILURE; } // drop call using disconnect mode H323DropCall(pCall, dwDisconnectMode); // release line device H323UnlockLine(pCall->pLine); // processed return CC_OK; } // send msp new call indication H323SendNewCallIndication(pCall); // process remote terminal capabilities if ((pCallbackParams->pTermCapList != NULL) && (pCallbackParams->pTermCapDescriptors != NULL)) { // process capabilirties H323ProcessRemoteTermCaps( pCall, pCallbackParams->pTermCapList, pCallbackParams->pTermCapDescriptors ); } // By this time remote capabilities have been discovered. // Thus, we can classify the call as a 'FastLink' call, if // both local and remote links are LAN connections; or as a // 'SlowLink' call otherwise. // Using this classification we then determine the length of // the interval of audio signal to be packed into one IP // packet. bRemoteVideoSupported = FALSE; // Determine remote side video bit rate, in case it // supports video in one of the recognizable formats switch (pCall->ccRemoteVideoCaps.ClientType) { case H245_CLIENT_VID_H263: dwRemoteVideoBitRate = pCall->ccRemoteVideoCaps.Cap.H245Vid_H263.maxBitRate; bRemoteVideoSupported = TRUE; break; case H245_CLIENT_VID_H261: dwRemoteVideoBitRate = pCall->ccRemoteVideoCaps.Cap.H245Vid_H261.maxBitRate; bRemoteVideoSupported = TRUE; break; default: // Remote side either does not support video, or // supports unrecognizable video format bRemoteVideoSupported = FALSE; break; } // If the remote end supports video in one of the recognizable // formats, then we can guess, based on the remote video bit rate // capability, whether it sits on a slow link or not. if ( bRemoteVideoSupported && H323IsSlowLink(dwRemoteVideoBitRate * 100)){ // Remote end has slow link. Adjust the number of // milliseconds of audio signal per packet. // Note that if the local end has slow link, the // adjustment has already been made, but there is // no harm in resetting the number. if (pCall->ccRemoteAudioCaps.ClientType == H245_CLIENT_AUD_G723){ // recalculate new setting wRemoteMaxAl_sduAudioFrames = G723_FRAMES_PER_PACKET(G723_SLOWLNK_MILLISECONDS_PER_PACKET); // see if new setting less than current if (wRemoteMaxAl_sduAudioFrames < pCall->ccRemoteAudioCaps.Cap.H245Aud_G723.maxAl_sduAudioFrames){ // use new setting instead of current pCall->ccRemoteAudioCaps.Cap.H245Aud_G723.maxAl_sduAudioFrames = wRemoteMaxAl_sduAudioFrames; } } } // see if user user information specified if ((pCallbackParams->pNonStandardData != NULL) && H323IsValidU2U(pCallbackParams->pNonStandardData)) { // add user user info if (H323AddU2U( &pCall->IncomingU2U, pCallbackParams->pNonStandardData->sData.wOctetStringLength, pCallbackParams->pNonStandardData->sData.pOctetString )) { H323DBG(( DEBUG_LEVEL_VERBOSE, "user user info available in CONNECT PDU.\n" )); // signal incoming (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_CALLINFO, LINECALLINFOSTATE_USERUSERINFO, 0, 0 ); } else { H323DBG(( DEBUG_LEVEL_WARNING, "could not save incoming user user info.\n" )); } } H323DBG(( DEBUG_LEVEL_TRACE, "call 0x%08lx connected to %S.\n", pCall, pCallbackParams->pszPeerDisplay )); // no outgoing channels so connect H323ChangeCallState(pCall, LINECALLSTATE_CONNECTED, 0); // release line device H323UnlockLine(pCall->pLine); // processed return CC_OK; } HRESULT H323RingingCallback( PH323_CALL pCall ) /*++ Routine Description: Callback for ringing indications. Arguments: pCall - Pointer to call object. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08x ringing.\n", pCall )); // change state to ringback H323ChangeCallState(pCall, LINECALLSTATE_RINGBACK, 0); // release line device H323UnlockLine(pCall->pLine); // processed return CC_OK; } HRESULT H323TerminationCallback( PH323_CALL pCall, HRESULT hrConf, PCC_CONFERENCE_TERMINATION_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for termination indications. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { DWORD dwDisconnectMode; // validate status if (hrConf == CC_OK) { H323DBG(( DEBUG_LEVEL_ERROR, "call 0x%08lx is being terminated.\n", pCall )); // set disconnect mode to normal dwDisconnectMode = LINEDISCONNECTMODE_NORMAL; } else { H323DBG(( DEBUG_LEVEL_ERROR, "call 0x%08lx could not be terminated.\n", pCall )); // unable to determine disconnect mode dwDisconnectMode = LINEDISCONNECTMODE_UNKNOWN; } // change call state to disconnected before dropping call H323ChangeCallState(pCall, LINECALLSTATE_DISCONNECTED, dwDisconnectMode); // drop call using disconnect mode H323DropCall(pCall, dwDisconnectMode); // release line device H323UnlockLine(pCall->pLine); // processed return CC_OK; } HRESULT H323RxChannelRequestCallback( PH323_CALL pCall, HRESULT hrConf, PCC_RX_CHANNEL_REQUEST_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for channel request indications. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { HRESULT hr; DWORD dwIndex; DWORD dwRejectReason; CC_TERMCAPLIST TermCapList; CC_TERMCAPDESCRIPTORS TermCapDescriptors; PCC_TERMCAP pRemoteCap; PCC_TERMCAP pLocalCap = NULL; PH323_CHANNEL pChannel = NULL; PH323_CHANNEL pAssociatedChannel = NULL; #if DBG DWORD dwIPAddr; #endif // retrieve pointer to capabilities structure pRemoteCap = pCallbackParams->pChannelCapability; H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx incoming channel:\n\t%s\n\t%s\n\t%s\n", pCall, H323DirToString(pRemoteCap->Dir), H323DataTypeToString(pRemoteCap->DataType), H323ClientTypeToString(pRemoteCap->ClientType) )); // validate data and client type if (!H323IsValidDataType(pRemoteCap->DataType)) { H323DBG(( DEBUG_LEVEL_ERROR, "channel rejected invalid type(s).\n" )); // initialize reject reason dwRejectReason = H245_REJ_TYPE_UNKNOWN; // bail... goto reject; } // see whether incoming channel is available if (H323IsVideoDataType(pRemoteCap->DataType) && !H323IsVideoRequested(pCall)) { H323DBG(( DEBUG_LEVEL_ERROR, "channel rejected video not enabled.\n" )); // initialize reject reason dwRejectReason = H245_REJ_TYPE_NOTAVAIL; // bail... goto reject; } else if (H323IsAudioDataType(pRemoteCap->DataType) && !H323IsAudioRequested(pCall)) { H323DBG(( DEBUG_LEVEL_ERROR, "channel rejected audio not enabled.\n" )); // initialize reject reason dwRejectReason = H245_REJ_TYPE_NOTAVAIL; // bail... goto reject; } // retrieve local caps H323GetTermCapList(pCall,&TermCapList,&TermCapDescriptors); // search term cap list for incoming cap for (dwIndex = 0; dwIndex < TermCapList.wLength; dwIndex++) { // see if local cap matchs incoming channel cap if (TermCapList.pTermCapArray[dwIndex]->ClientType == pRemoteCap->ClientType) { // save pointer to termcap for later use pLocalCap = TermCapList.pTermCapArray[dwIndex]; // done break; } } // validate termcap if (pLocalCap == NULL) { H323DBG(( DEBUG_LEVEL_ERROR, "channel rejected unsupported termcap.\n" )); // initialize reject reason dwRejectReason = H245_REJ_TYPE_NOTSUPPORT; // bail... goto reject; } // determine client type switch (pRemoteCap->ClientType) { case H245_CLIENT_VID_H263: // see if incoming bitrate too large if (pLocalCap->Cap.H245Vid_H263.maxBitRate < pRemoteCap->Cap.H245Vid_H263.maxBitRate) { H323DBG(( DEBUG_LEVEL_ERROR, "incoming H263 bitrate too large (%d bps).\n", pRemoteCap->Cap.H245Vid_H263.maxBitRate * 100 )); // initialize reject reason dwRejectReason = H245_REJ_BANDWIDTH; // bail... goto reject; } break; case H245_CLIENT_VID_H261: // see if incoming bitrate too large if (pLocalCap->Cap.H245Vid_H261.maxBitRate < pRemoteCap->Cap.H245Vid_H261.maxBitRate) { H323DBG(( DEBUG_LEVEL_ERROR, "incoming H261 bitrate too large (%d bps).\n", pRemoteCap->Cap.H245Vid_H261.maxBitRate * 100 )); // initialize reject reason dwRejectReason = H245_REJ_BANDWIDTH; // bail... goto reject; } break; } // look for existing session H323LookupChannelBySessionID( &pAssociatedChannel, pCall->pChannelTable, pCallbackParams->bSessionID ); // allocate channel object if (!H323AllocChannelFromTable( &pChannel, &pCall->pChannelTable, pCall)) { H323DBG(( DEBUG_LEVEL_ERROR, "channel rejected could not allocate.\n" )); // initialize reject reason dwRejectReason = H245_REJ_TYPE_UNKNOWN; // bail... goto reject; } // transfer peer rtcp address from callback parameters // fail if the peer address was invalid if (NULL == pCallbackParams->pPeerRTCPAddr) { H323DBG(( DEBUG_LEVEL_ERROR, "channel rejected: empty peer RTCP address.\n" )); // initialize reject reason dwRejectReason = H245_REJ_TYPE_UNKNOWN; // bail... goto reject; } pChannel->ccRemoteRTCPAddr = *pCallbackParams->pPeerRTCPAddr; // transfer channel information from callback parameters pChannel->hccChannel = pCallbackParams->hChannel; pChannel->bSessionID = pCallbackParams->bSessionID; // transfer termcap to channel pChannel->ccTermCaps = *pRemoteCap; // initialize direction pChannel->fInbound = TRUE; // determine payload type from channel capability data type pChannel->Settings.MediaType = H323IsVideoDataType(pRemoteCap->DataType) ? MEDIA_VIDEO : MEDIA_AUDIO ; // examine existing session if (pAssociatedChannel != NULL) { H323DBG(( DEBUG_LEVEL_VERBOSE, "overriding default local RTCP port for session %d\n", pChannel->bSessionID )); // override default RTCP port with existing one pChannel->ccLocalRTCPAddr.Addr.IP_Binary.wPort = pAssociatedChannel->ccLocalRTCPAddr.Addr.IP_Binary.wPort; } // complete media stream address information pChannel->Settings.dwIPLocal = pChannel->ccLocalRTPAddr.Addr.IP_Binary.dwAddr; pChannel->Settings.wRTPPortLocal = pChannel->ccLocalRTPAddr.Addr.IP_Binary.wPort; pChannel->Settings.wRTCPPortLocal = pChannel->ccLocalRTCPAddr.Addr.IP_Binary.wPort; pChannel->Settings.dwIPRemote = pChannel->ccRemoteRTCPAddr.Addr.IP_Binary.dwAddr; pChannel->Settings.wRTPPortRemote = 0; pChannel->Settings.wRTCPPortRemote = pChannel->ccRemoteRTCPAddr.Addr.IP_Binary.wPort; #if DBG // convert local address to network order dwIPAddr = htonl(pChannel->Settings.dwIPLocal); H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming RTP stream %s:%d\n", H323AddrToString(dwIPAddr), pChannel->Settings.wRTPPortLocal )); H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming RTCP stream %s:%d\n", H323AddrToString(dwIPAddr), pChannel->Settings.wRTCPPortLocal )); // convert remote address to network order dwIPAddr = htonl(pChannel->Settings.dwIPRemote); H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing RTCP stream %s:%d\n", H323AddrToString(dwIPAddr), pChannel->Settings.wRTCPPortRemote )); #endif // check incoming stream type switch (pRemoteCap->ClientType) { case H245_CLIENT_AUD_G723: // initialize rtp payload type pChannel->Settings.dwPayloadType = G723_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = (pCallbackParams->bRTPPayloadType != 0) ? (DWORD)(BYTE)(pCallbackParams->bRTPPayloadType) : G723_RTP_PAYLOAD_TYPE ; pChannel->Settings.Audio.dwMillisecondsPerPacket = G723_MILLISECONDS_PER_PACKET( pRemoteCap->Cap.H245Aud_G723.maxAl_sduAudioFrames ); pChannel->Settings.Audio.G723Settings.bG723LowSpeed = H323IsSlowLink(pCall->dwLinkSpeed); H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming G723 stream (%d milliseconds).\n", pChannel->Settings.Audio.dwMillisecondsPerPacket )); break; case H245_CLIENT_AUD_G711_ULAW64: // complete audio information pChannel->Settings.dwPayloadType = G711U_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = (pCallbackParams->bRTPPayloadType != 0) ? (DWORD)(BYTE)(pCallbackParams->bRTPPayloadType) : G711U_RTP_PAYLOAD_TYPE ; pChannel->Settings.Audio.dwMillisecondsPerPacket = G711_MILLISECONDS_PER_PACKET( pRemoteCap->Cap.H245Aud_G711_ULAW64 ); H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming G711U stream (%d milliseconds).\n", pChannel->Settings.Audio.dwMillisecondsPerPacket )); break; case H245_CLIENT_AUD_G711_ALAW64: // complete audio information pChannel->Settings.dwPayloadType = G711A_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = (pCallbackParams->bRTPPayloadType != 0) ? (DWORD)(BYTE)(pCallbackParams->bRTPPayloadType) : G711A_RTP_PAYLOAD_TYPE ; pChannel->Settings.Audio.dwMillisecondsPerPacket = G711_MILLISECONDS_PER_PACKET( pRemoteCap->Cap.H245Aud_G711_ALAW64 ); H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming G711A stream (%d milliseconds).\n", pChannel->Settings.Audio.dwMillisecondsPerPacket )); break; case H245_CLIENT_VID_H263: // complete video information pChannel->Settings.dwPayloadType = H263_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = (pCallbackParams->bRTPPayloadType != 0) ? (DWORD)(BYTE)(pCallbackParams->bRTPPayloadType) : H263_RTP_PAYLOAD_TYPE ; pChannel->Settings.Video.bCIF = FALSE; pChannel->Settings.Video.dwMaxBitRate = pRemoteCap->Cap.H245Vid_H263.maxBitRate * 100 ; H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming H263 stream (%d bps).\n", pChannel->Settings.Video.dwMaxBitRate )); break; case H245_CLIENT_VID_H261: // complete video information pChannel->Settings.dwPayloadType = H261_RTP_PAYLOAD_TYPE; pChannel->Settings.dwDynamicType = (pCallbackParams->bRTPPayloadType != 0) ? (DWORD)(BYTE)(pCallbackParams->bRTPPayloadType) : H261_RTP_PAYLOAD_TYPE ; pChannel->Settings.Video.bCIF = FALSE; pChannel->Settings.Video.dwMaxBitRate = pRemoteCap->Cap.H245Vid_H261.maxBitRate * 100 ; H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming H261 stream (%d bps).\n", pChannel->Settings.Video.dwMaxBitRate )); break; } // save back pointer pChannel->pCall = pCall; // let msp accept incoming channel H323SendAcceptChannelRequest(pCall, pChannel); // release line device H323UnlockLine(pCall->pLine); // processed return CC_OK; reject: // reject channel CC_RejectChannel( pCallbackParams->hChannel, dwRejectReason ); H323DBG(( DEBUG_LEVEL_VERBOSE, "call 0x%08lx incoming channel rejected.\n", pCall )); // release line device H323UnlockLine(pCall->pLine); // processed return CC_OK; } HRESULT H323RxChannelCloseCallback( PH323_CALL pCall, HRESULT hrConf, PCC_RX_CHANNEL_CLOSE_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for channel close indications. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { PH323_CHANNEL pChannel = NULL; // attempt to retrieve channel if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, pCallbackParams->hChannel )) { H323DBG(( DEBUG_LEVEL_ERROR, "could not close unknown rx channel 0x%08lx.\n", pCallbackParams->hChannel )); // release line device H323UnlockLine(pCall->pLine); // could not find channel return CC_NOT_IMPLEMENTED; } // notify msp of channel closure H323SendCloseChannelCommand(pCall, pChannel->hmChannel,ERROR_SUCCESS); // release memory for logical channel H323FreeChannelFromTable(pChannel, pCall->pChannelTable); // update media modes H323UpdateMediaModes(pCall); // release line device H323UnlockLine(pCall->pLine); // success return CC_OK; } HRESULT H323TxChannelOpenCallback( PH323_CALL pCall, HRESULT hrConf, PCC_TX_CHANNEL_OPEN_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for channel open indications. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { PH323_CHANNEL pChannel = NULL; // attempt to retrieve channel if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, pCallbackParams->hChannel )) { H323DBG(( DEBUG_LEVEL_ERROR, "could not open unknown tx channel 0x%08lx.\n", pCallbackParams->hChannel )); // release line device H323UnlockLine(pCall->pLine); // could not find channel return CC_NOT_IMPLEMENTED; } // validate status if (hrConf != CC_OK) { // see if peer rejected if (hrConf == CC_PEER_REJECT) { H323DBG(( DEBUG_LEVEL_VERBOSE, "channel 0x%08lx rejected 0x%08lx.\n", pChannel, pCallbackParams->dwRejectReason )); } else { H323DBG(( DEBUG_LEVEL_VERBOSE, "channel 0x%08lx unable to be opened.\n", pChannel )); } // close channel (with error) H323SendCloseChannelCommand(pCall, pChannel->hmChannel,(DWORD)-1); // release channel object H323FreeChannelFromTable(pChannel,pCall->pChannelTable); } else { // transfer peer rtcp address from callback parameters pChannel->ccRemoteRTPAddr = *pCallbackParams->pPeerRTPAddr; // transfer peer rtcp address from callback parameters pChannel->ccRemoteRTCPAddr = *pCallbackParams->pPeerRTCPAddr; H323DBG(( DEBUG_LEVEL_VERBOSE, "channel 0x%08lx accepted by peer.\n", pChannel )); // complete media stream address information pChannel->Settings.dwIPLocal = pChannel->ccLocalRTCPAddr.Addr.IP_Binary.dwAddr; pChannel->Settings.wRTPPortLocal = 0; pChannel->Settings.wRTCPPortLocal = pChannel->ccLocalRTCPAddr.Addr.IP_Binary.wPort; pChannel->Settings.dwIPRemote = pChannel->ccRemoteRTPAddr.Addr.IP_Binary.dwAddr; pChannel->Settings.wRTPPortRemote = pChannel->ccRemoteRTPAddr.Addr.IP_Binary.wPort; pChannel->Settings.wRTCPPortRemote = pChannel->ccRemoteRTCPAddr.Addr.IP_Binary.wPort; #if DBG { DWORD dwIPAddr; // convert local address to network order dwIPAddr = htonl(pChannel->Settings.dwIPLocal); H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming RTCP stream %s:%d\n", H323AddrToString(dwIPAddr), pChannel->Settings.wRTCPPortLocal )); // convert remote address to network order dwIPAddr = htonl(pChannel->Settings.dwIPRemote); H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing RTP stream %s:%d\n", H323AddrToString(dwIPAddr), pChannel->Settings.wRTPPortRemote )); H323DBG(( DEBUG_LEVEL_VERBOSE, "outgoing RTCP stream %s:%d\n", H323AddrToString(dwIPAddr), pChannel->Settings.wRTCPPortRemote )); } #endif // change state to opened pChannel->nState = H323_CHANNELSTATE_OPENED; // notify msp channel is opened H323SendOpenChannelResponse(pCall, pChannel); } // update media modes H323UpdateMediaModes(pCall); // release line device H323UnlockLine(pCall->pLine); // processed return CC_OK; } HRESULT H323TxChannelCloseCallback( PH323_CALL pCall, HRESULT hrConf, PCC_TX_CHANNEL_CLOSE_REQUEST_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for channel close indications. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { PH323_CHANNEL pChannel = NULL; // attempt to retrieve channel if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, pCallbackParams->hChannel )) { H323DBG(( DEBUG_LEVEL_ERROR, "could not close unknown tx channel 0x%08lx.\n", pCallbackParams->hChannel )); // release line device H323UnlockLine(pCall->pLine); // could not find channel return CC_NOT_IMPLEMENTED; } // notify msp channel is closed H323SendCloseChannelCommand(pCall, pChannel->hmChannel,ERROR_SUCCESS); // release memory for logical channel H323FreeChannelFromTable(pChannel, pCall->pChannelTable); // update media modes H323UpdateMediaModes(pCall); // release line device H323UnlockLine(pCall->pLine); // success return CC_OK; } HRESULT H323FlowControlCallback( PH323_CALL pCall, HRESULT hrConf, PCC_FLOW_CONTROL_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for flow control commands. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns CC_OK. --*/ { PH323_CHANNEL pChannel = NULL; // attempt to retrieve channel if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, pCallbackParams->hChannel )) { H323DBG(( DEBUG_LEVEL_ERROR, "could not process flow control command for channel 0x%08lx.\n", pCallbackParams->hChannel )); // release line device H323UnlockLine(pCall->pLine); // could not find channel return CC_OK; } // notify msp that media stream bit rate is to be changed H323SendFlowControlCommand( pCall, pChannel, pCallbackParams->dwRate ); // release line device H323UnlockLine(pCall->pLine); // success return CC_OK; } HRESULT H323VideoFastUpdatePictureCallback( PH323_CALL pCall, HRESULT hrConf, PCC_H245_MISCELLANEOUS_COMMAND_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for Video Fast Update Picture command (a.k.a. I-frame request command) Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns CC_OK. --*/ { PH323_CHANNEL pChannel = NULL; // attempt to retrieve channel if (!H323LookupChannelByHandle( &pChannel, pCall->pChannelTable, pCallbackParams->hChannel )) { H323DBG(( DEBUG_LEVEL_ERROR, "could not process I-frame request cmd for channel 0x%08lx.\n", pCallbackParams->hChannel )); // release line device H323UnlockLine(pCall->pLine); // could not find channel return CC_OK; } // notify msp that media stream bit rate is to be changed H323SendVideoFastUpdatePictureCommand( pCall, pChannel ); // release line device H323UnlockLine(pCall->pLine); // success return CC_OK; } HRESULT H245MiscellaneousCommandCallback( PH323_CALL pCall, HRESULT hrConf, PCC_H245_MISCELLANEOUS_COMMAND_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for miscellaneous H.245 commands. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { // retrieve command structure from incoming callback parameters MiscellaneousCommand * pCommand = pCallbackParams->pMiscellaneousCommand; switch (pCommand->type.choice) { case videoFastUpdatePicture_chosen: // process I-frame request from remote entity return H323VideoFastUpdatePictureCallback( pCall, hrConf, (PVOID)pCallbackParams ); default: // intentionally left blank break; } H323DBG(( DEBUG_LEVEL_VERBOSE, "misc command %s ignored.\n", H323MiscCommandToString((DWORD)(USHORT)pCommand->type.choice) )); // release line device H323UnlockLine(pCall->pLine); // success return CC_NOT_IMPLEMENTED; } HRESULT H245RxNonStandardMessageCallback( PH323_CALL pCall, HRESULT hrConf, PCC_RX_NONSTANDARD_MESSAGE_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for miscellaneous H.245 commands. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { // validate status if (hrConf == CC_OK) { // validate incoming parameters if ((pCallbackParams->bH245MessageType == CC_H245_MESSAGE_COMMAND) && H323IsValidU2U(&pCallbackParams->NonStandardData)) { // add user user info if (H323AddU2U( &pCall->IncomingU2U, pCallbackParams->NonStandardData.sData.wOctetStringLength, pCallbackParams->NonStandardData.sData.pOctetString )) { H323DBG(( DEBUG_LEVEL_VERBOSE, "user user info available in NONSTANDARD MESSAGE.\n" )); // signal incoming (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_CALLINFO, LINECALLINFOSTATE_USERUSERINFO, 0, 0 ); } } } else { H323DBG(( DEBUG_LEVEL_WARNING, "error 0x%08lx receiving non-standard message.\n", hrConf )); } // release line device H323UnlockLine(pCall->pLine); // success return CC_OK; } HRESULT H245UserInputCallback( PH323_CALL pCall, HRESULT hrConf, PCC_USER_INPUT_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Callback for miscellaneous H.245 commands. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { // validate status if (hrConf == CC_OK) { // check monitoring mode if (pCall->fMonitoringDigits == TRUE) { WCHAR * pwch; H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming user input %S.\n", pCallbackParams->pUserInput )); // initialize string pointer pwch = pCallbackParams->pUserInput; // process each digit while (*pwch != L'\0') { // signal incoming (*g_pfnLineEventProc)( pCall->pLine->htLine, pCall->htCall, LINE_MONITORDIGITS, (DWORD_PTR)*pwch, LINEDIGITMODE_DTMF, GetTickCount() ); // next ++pwch; } } else { H323DBG(( DEBUG_LEVEL_VERBOSE, "ignoring incoming user input message.\n" )); } } else { H323DBG(( DEBUG_LEVEL_WARNING, "error 0x%08lx receiving user input message.\n", hrConf )); } // release line device H323UnlockLine(pCall->pLine); // success return CC_OK; } HRESULT H245T120ChannelRequestCallback( PH323_CALL pCall, HRESULT hrConf, PCC_T120_CHANNEL_REQUEST_CALLBACK_PARAMS pT120RequestParams ) /*++ Routine Description: Callback for a T120 open channel request. Arguments: pCall - Pointer to call object. hrConf - Current status of H.323 conference. pCallbackParams - Parameters returned by call control module. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { HRESULT hr; // validate status if (hrConf == CC_OK) { CC_ADDR ChannelAddr; ChannelAddr.nAddrType = CC_IP_BINARY; ChannelAddr.bMulticast = FALSE; ChannelAddr.Addr.IP_Binary.wPort = g_wPortT120; if (g_dwIPT120 != INADDR_ANY) { ChannelAddr.Addr.IP_Binary.dwAddr = g_dwIPT120; } else { ChannelAddr.Addr.IP_Binary.dwAddr = H323IsCallInbound(pCall) ? pCall->ccCalleeAddr.Addr.IP_Binary.dwAddr : pCall->ccCallerAddr.Addr.IP_Binary.dwAddr ; } hr = CC_AcceptT120Channel( pT120RequestParams->hChannel, FALSE, // BOOL bAssociateConference, NULL, // PCC_OCTETSTRING pExternalReference, &ChannelAddr ); if (hr != CC_OK) { H323DBG(( DEBUG_LEVEL_WARNING, "error 0x%08lx accepting T120 channel.\n", hr )); } } else { H323DBG(( DEBUG_LEVEL_WARNING, "error 0x%08lx receiving user input message.\n", hrConf )); } // release line device H323UnlockLine(pCall->pLine); // success return CC_OK; } /////////////////////////////////////////////////////////////////////////////// // // // Public procedures // // // /////////////////////////////////////////////////////////////////////////////// HRESULT H323ConferenceCallback( BYTE bIndication, HRESULT hrConf, CC_HCONFERENCE hConference, DWORD dwConferenceToken, PCC_CONFERENCE_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Conference callback for Intel Call Control module. Arguments: bIndication - indicates the reason for the callback. hrConf - indicates the asynchronous status of the call. hConference - conference handle associated with the callback. dwConferenceToken - conference token specified in the CC_CreateConference(). pCallbackParams - pointer to a structure containing callback parameters specific for the callback indication. Return Values: Returns either CC_OK or CC_NOT_IMPLEMENTED. --*/ { HRESULT hr; HDRVCALL hdCall; PH323_CALL pCall = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "%s %s (0x%08lx).\n", H323IndicationToString(bIndication), H323StatusToString((DWORD)hrConf), hrConf )); // retrieve call handle from token hdCall = (HDRVCALL)dwConferenceToken; // handle hangup indications separately if (bIndication == CC_HANGUP_INDICATION) { H323DBG(( DEBUG_LEVEL_VERBOSE, "hangup confirmed (hdCall=0x%08lx).\n", hdCall )); // success return CC_OK; } else if (bIndication == CC_ACCEPT_CHANNEL_INDICATION) { H323DBG(( DEBUG_LEVEL_VERBOSE, "ignoring accept channel indication (hdCall=0x%08lx).\n", hdCall )); // success return CC_OK; } // retrieve call pointer from handle if (!H323GetCallAndLock(&pCall, hdCall)) { H323DBG(( DEBUG_LEVEL_ERROR, "invalid call handle in callback.\n" )); // need to return error return CC_NOT_IMPLEMENTED; } // validate conference handle if (pCall->hccConf != hConference) { H323DBG(( DEBUG_LEVEL_ERROR, "conference handle mismatch.\n" )); // unlock line device H323UnlockLine(pCall->pLine); // need to return error return CC_NOT_IMPLEMENTED; } // determine message switch (bIndication) { case CC_CONNECT_INDICATION: // process connect indication return H323ConnectCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_RINGING_INDICATION: // process ringing indication return H323RingingCallback( pCall ); case CC_CONFERENCE_TERMINATION_INDICATION: // process termination indication return H323TerminationCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_RX_CHANNEL_REQUEST_INDICATION: // process termination indication return H323RxChannelRequestCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_RX_CHANNEL_CLOSE_INDICATION: // process channel close return H323RxChannelCloseCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_TX_CHANNEL_OPEN_INDICATION: // process channel open return H323TxChannelOpenCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_TX_CHANNEL_CLOSE_REQUEST_INDICATION: // process channel close return H323TxChannelCloseCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_FLOW_CONTROL_INDICATION: // process flow control command return H323FlowControlCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_H245_MISCELLANEOUS_COMMAND_INDICATION: // process miscellaneous commands return H245MiscellaneousCommandCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_RX_NONSTANDARD_MESSAGE_INDICATION: // process nonstandard commands return H245RxNonStandardMessageCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_USER_INPUT_INDICATION: // process nonstandard commands return H245UserInputCallback( pCall, hrConf, (PVOID)pCallbackParams ); case CC_T120_CHANNEL_REQUEST_INDICATION: // process T120 channel request return H245T120ChannelRequestCallback( pCall, hrConf, (PVOID)pCallbackParams ); } H323DBG(( DEBUG_LEVEL_WARNING, "conference callback indication not supported.\n" )); // unlock line device H323UnlockLine(pCall->pLine); // not yet supported return CC_NOT_IMPLEMENTED; } VOID H323ListenCallback( HRESULT hrListen, PCC_LISTEN_CALLBACK_PARAMS pCallbackParams ) /*++ Routine Description: Conference callback for Intel Call Control module. Arguments: hrListen - indicates the asynchronous status of the call. pCallbackParams - pointer to a structure containing callback parameters specific for the callback indication. Return Values: None. --*/ { HRESULT hr; HDRVLINE hdLine; PH323_LINE pLine = NULL; PH323_CALL pCall = NULL; H323DBG(( DEBUG_LEVEL_TRACE, "INCOMING CALL %s (0x%08lx).\n", H323StatusToString(hrListen), hrListen )); // retrieve line handle from token hdLine = (HDRVLINE)pCallbackParams->dwListenToken; // retrieve line pointer from handle if (!H323GetLineAndLock(&pLine, hdLine)) { H323DBG(( DEBUG_LEVEL_ERROR, "invalid line handle in listen callback.\n" )); // failure return ; } // validate status if (hrListen != CC_OK) { // check for active call if (H323GetCallByHCall( &pCall, pLine, pCallbackParams->hCall)) { // drop offering call H323DropCall(pCall,LINEDISCONNECTMODE_CANCELLED); } // release line device H323UnlockLine(pLine); // done return; } H323DBG(( DEBUG_LEVEL_TRACE, "line %d receiving call request from %S.\n", pLine->dwDeviceID, pCallbackParams->pszDisplay )); // allocate outgoing call from line call table if (!H323AllocCallFromTable(&pCall,&pLine->pCallTable,pLine)) { H323DBG(( DEBUG_LEVEL_ERROR, "could not allocate call object.\n" )); // release line device H323UnlockLine(pLine); // failure return; } // save back pointer pCall->pLine = pLine; // clear incoming modes pCall->dwIncomingModes = 0; // outgoing modes will be finalized during H.245 phase pCall->dwOutgoingModes = pLine->dwMediaModes | LINEMEDIAMODE_UNKNOWN; // save media modes specified pCall->dwRequestedModes = pLine->dwMediaModes; // specify incoming call direction pCall->dwOrigin = LINECALLORIGIN_INBOUND; // save incoming call handle pCall->hccCall = pCallbackParams->hCall; // save caller transport address pCall->ccCallerAddr = *pCallbackParams->pCallerAddr; // save callee transport address pCall->ccCalleeAddr = *pCallbackParams->pCalleeAddr; #if DBG { DWORD dwIPAddr; // retrieve ip address in network byte order dwIPAddr = htonl(pCall->ccCalleeAddr.Addr.IP_Binary.dwAddr); H323DBG(( DEBUG_LEVEL_VERBOSE, "callee address resolved to %s.\n", H323AddrToString(dwIPAddr) )); // retrieve ip address in network byte order dwIPAddr = htonl(pCall->ccCallerAddr.Addr.IP_Binary.dwAddr); H323DBG(( DEBUG_LEVEL_VERBOSE, "caller address resolved to %s.\n", H323AddrToString(dwIPAddr) )); } #endif // determine link speed for local interface pCall->dwLinkSpeed = H323DetermineLinkSpeed( pCall->ccCalleeAddr.Addr.IP_Binary.dwAddr ); // bind incoming call if (!H323BindCall(pCall,NULL)) { H323DBG(( DEBUG_LEVEL_ERROR, "could not bind call object.\n" )); // failure goto cleanup; } // see if caller alias was specified if ((pCallbackParams->pCallerAliasNames != NULL) && (pCallbackParams->pCallerAliasNames->wCount > 0)) { PCC_ALIASITEM pCallerAlias; // retrieve pointer to caller alias pCallerAlias = pCallbackParams->pCallerAliasNames->pItems; // validate alias type if ((pCallerAlias->wDataLength > 0) && ((pCallerAlias->wType == CC_ALIAS_H323_ID) || (pCallerAlias->wType == CC_ALIAS_H323_PHONE))) { // initialize alias pCall->ccCallerAlias.wType = pCallerAlias->wType; pCall->ccCallerAlias.wDataLength = pCallerAlias->wDataLength; pCall->ccCallerAlias.wPrefixLength = 0; pCall->ccCallerAlias.pPrefix = NULL; // allocate memory for caller string pCall->ccCallerAlias.pData = H323HeapAlloc( (pCallerAlias->wDataLength + 1) * sizeof(WCHAR) ); // validate pointer if (pCall->ccCallerAlias.pData == NULL) { H323DBG(( DEBUG_LEVEL_ERROR, "could not allocate caller name.\n" )); // failure goto cleanup; } // transfer string information memcpy(pCall->ccCallerAlias.pData, pCallerAlias->pData, pCallerAlias->wDataLength * sizeof(WCHAR) ); // terminate incoming string pCall->ccCallerAlias.pData[pCallerAlias->wDataLength] = L'\0'; H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming caller alias is %S.\n", pCall->ccCallerAlias.pData )); } } // see if callee alias was specified if ((pCallbackParams->pCalleeAliasNames != NULL) && (pCallbackParams->pCalleeAliasNames->wCount > 0)) { PCC_ALIASITEM pCalleeAlias; // retrieve pointer to callee alias pCalleeAlias = pCallbackParams->pCalleeAliasNames->pItems; // validate alias type if ((pCalleeAlias->wDataLength > 0) && ((pCalleeAlias->wType == CC_ALIAS_H323_ID) || (pCalleeAlias->wType == CC_ALIAS_H323_PHONE))) { // initialize alias pCall->ccCalleeAlias.wType = pCalleeAlias->wType; pCall->ccCalleeAlias.wDataLength = pCalleeAlias->wDataLength; pCall->ccCalleeAlias.wPrefixLength = 0; pCall->ccCalleeAlias.pPrefix = NULL; // allocate memory for caller string pCall->ccCalleeAlias.pData = H323HeapAlloc( (pCalleeAlias->wDataLength + 1) * sizeof(WCHAR) ); // validate pointer if (pCall->ccCalleeAlias.pData == NULL) { H323DBG(( DEBUG_LEVEL_ERROR, "could not allocate callee name.\n" )); // failure goto cleanup; } // transfer string information memcpy(pCall->ccCalleeAlias.pData, pCalleeAlias->pData, pCalleeAlias->wDataLength * sizeof(WCHAR) ); // terminate incoming string pCall->ccCalleeAlias.pData[pCalleeAlias->wDataLength] = L'\0'; H323DBG(( DEBUG_LEVEL_VERBOSE, "incoming callee alias is %S.\n", pCall->ccCalleeAlias.pData )); } } // validate user user info specified if ((pCallbackParams->pNonStandardData != NULL) && H323IsValidU2U(pCallbackParams->pNonStandardData)) { // add user user info if (!H323AddU2U( &pCall->IncomingU2U, pCallbackParams->pNonStandardData->sData.wOctetStringLength, pCallbackParams->pNonStandardData->sData.pOctetString )) { H323DBG(( DEBUG_LEVEL_ERROR, "could not save incoming user user info.\n" )); // failure goto cleanup; } } // signal incoming call (*g_pfnLineEventProc)( pLine->htLine, (HTAPICALL)NULL, LINE_NEWCALL, (DWORD_PTR)pCall->hdCall, (DWORD_PTR)&pCall->htCall, 0 ); // see if user user info specified if (!IsListEmpty(&pCall->IncomingU2U)) { H323DBG(( DEBUG_LEVEL_VERBOSE, "user user info available in SETUP PDU.\n" )); // signal incoming (*g_pfnLineEventProc)( pLine->htLine, pCall->htCall, LINE_CALLINFO, LINECALLINFOSTATE_USERUSERINFO, 0, 0 ); } // change state to offering H323ChangeCallState(pCall, LINECALLSTATE_OFFERING, 0); // release line device H323UnlockLine(pLine); // success return; cleanup: // unbind call H323UnbindCall(pCall); // release outgoing call from line call table H323FreeCallFromTable(pCall,pLine->pCallTable); // release line device H323UnlockLine(pLine); // failure return; } BOOL H323StartCallbackThread( ) /*++ Routine Description: Creates thread which handles async processing. Arguments: None. Return Values: Returns true if successful. --*/ { // transfer registry key change event to waitable object array g_WaitableObjects[WAIT_OBJECT_REGISTRY_CHANGE] = CreateEvent( NULL, // lpEventAttributes FALSE, // bManualReset FALSE, // bInitialState NULL // lpName ); // transfer termination event to waitable object array g_WaitableObjects[WAIT_OBJECT_TERMINATE_EVENT] = CreateEvent( NULL, // lpEventAttributes TRUE, // bManualReset FALSE, // bInitialState NULL // lpName ); // validate waitable object handles if ((g_WaitableObjects[WAIT_OBJECT_REGISTRY_CHANGE] == NULL) || (g_WaitableObjects[WAIT_OBJECT_TERMINATE_EVENT] == NULL)) { H323DBG(( DEBUG_LEVEL_ERROR, "could not allocate waitable objects.\n" )); // cleanup resources H323StopCallbackThread(); // failure return FALSE; } // attempt to start thread g_hCallbackThread = CreateThread( NULL, // lpThreadAttributes 0, // dwStackSize H323CallbackThread, NULL, // lpParameter 0, // dwCreationFlags &g_dwCallbackThreadID ); // validate thread handle if (g_hCallbackThread == NULL) { H323DBG(( DEBUG_LEVEL_ERROR, "error 0x%08lx creating callback thread.\n", GetLastError() )); // cleanup resources H323StopCallbackThread(); // failure return FALSE; } H323DBG(( DEBUG_LEVEL_VERBOSE, "callback thread %x started.\n", g_dwCallbackThreadID )); // success return TRUE; } BOOL H323StopCallbackThread( ) /*++ Routine Description: Destroys thread which handles async processing. Arguments: None. Return Values: Returns true if successful. --*/ { HRESULT hr; DWORD dwStatus; // validate thread handle if (g_hCallbackThread != NULL) { // signal termination event SetEvent(g_WaitableObjects[WAIT_OBJECT_TERMINATE_EVENT]); H323DBG(( DEBUG_LEVEL_VERBOSE, "callback thread %x stopping.\n", g_dwCallbackThreadID )); // unlock temporarily H323UnlockProvider(); // wait for callback thread to terminate dwStatus = WaitForSingleObject(g_hCallbackThread, INFINITE); // relock provider H323LockProvider(); H323DBG(( DEBUG_LEVEL_VERBOSE, "callback thread %x stopped (dwStatus=0x%08lx).\n", g_dwCallbackThreadID, dwStatus )); // close thread handle CloseHandle(g_hCallbackThread); // re-initialize callback thread id g_dwCallbackThreadID = UNINITIALIZED; g_hCallbackThread = NULL; } // validate waitable object handle if (g_WaitableObjects[WAIT_OBJECT_REGISTRY_CHANGE] != NULL) { // release waitable object handle CloseHandle(g_WaitableObjects[WAIT_OBJECT_REGISTRY_CHANGE]); // re-initialize waitable object handle g_WaitableObjects[WAIT_OBJECT_REGISTRY_CHANGE] = NULL; } // validate waitable object handle if (g_WaitableObjects[WAIT_OBJECT_TERMINATE_EVENT] != NULL) { // release waitable object handle CloseHandle(g_WaitableObjects[WAIT_OBJECT_TERMINATE_EVENT]); // re-initialize waitable object handle g_WaitableObjects[WAIT_OBJECT_TERMINATE_EVENT] = NULL; } // success return TRUE; }