4556 lines
110 KiB
C
4556 lines
110 KiB
C
/*++
|
||
|
||
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 <limits.h>
|
||
#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;
|
||
}
|