mirror of https://github.com/tongzx/nt5src
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
4555 lines
110 KiB
4555 lines
110 KiB
/*++
|
|
|
|
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;
|
|
}
|