Source code of Windows XP (NT5)
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.

614 lines
21 KiB

  1. /****************************************************************************
  2. *
  3. * $Archive: S:/STURGEON/SRC/CALLCONT/VCS/callcon2.c_v $
  4. *
  5. * INTEL Corporation Prorietary Information
  6. *
  7. * This listing is supplied under the terms of a license agreement
  8. * with INTEL Corporation and may not be copied nor disclosed except
  9. * in accordance with the terms of that agreement.
  10. *
  11. * Copyright (c) 1996 Intel Corporation.
  12. *
  13. * $Revision: 1.35 $
  14. * $Date: 03 Mar 1997 09:08:16 $
  15. * $Author: MANDREWS $
  16. *
  17. * Deliverable:
  18. *
  19. * Abstract:
  20. *
  21. * Notes:
  22. *
  23. ***************************************************************************/
  24. #ifdef GATEKEEPER
  25. #pragma warning ( disable : 4057 4115 4201 4214 )
  26. #include <nt.h>
  27. #include <ntrtl.h>
  28. #include <nturtl.h>
  29. #include <windows.h>
  30. #pragma warning ( default : 4115 4201 4214 )
  31. #include "apierror.h"
  32. #include "incommon.h"
  33. #include "callcont.h"
  34. #include "q931.h"
  35. #include "ccmain.h"
  36. #include "confman.h"
  37. #include "listman.h"
  38. #include "q931man.h"
  39. #include "h245man.h"
  40. #include "callman.h"
  41. #include "userman.h"
  42. #include "chanman.h"
  43. #include "hangman.h"
  44. #include "linkapi.h"
  45. #include "h245api.h"
  46. #include "ccutils.h"
  47. #include "callman2.h"
  48. #define HResultLeave(x) return x
  49. extern CC_CONFERENCEID InvalidConferenceID;
  50. //
  51. // Complete CC_xxx Operations
  52. //
  53. HRESULT ListenReject (DWORD hListen, HRESULT Reason)
  54. {
  55. HRESULT status;
  56. PLISTEN pListen;
  57. CC_LISTEN_CALLBACK_PARAMS ListenCallbackParams;
  58. status = LockListen(hListen, &pListen);
  59. if (status == CC_OK) {
  60. ListenCallbackParams.hCall = CC_INVALID_HANDLE;
  61. ListenCallbackParams.pCallerAliasNames = NULL;
  62. ListenCallbackParams.pCalleeAliasNames = NULL;
  63. ListenCallbackParams.pNonStandardData = NULL;
  64. ListenCallbackParams.pszDisplay = NULL;
  65. ListenCallbackParams.pVendorInfo = NULL;
  66. ListenCallbackParams.ConferenceID = InvalidConferenceID;
  67. ListenCallbackParams.pCallerAddr = NULL;
  68. ListenCallbackParams.pCalleeAddr = NULL;
  69. ListenCallbackParams.dwListenToken = pListen->dwListenToken;
  70. // Invoke the user callback -- the listen object is locked during the callback,
  71. // but the associated call object is unlocked (to prevent deadlock if
  72. // CC_AcceptCall() or CC_RejectCall() is called during the callback from a
  73. // different thread, and the callback thread blocks pending completion of
  74. // CC_AcceptCall() or CC_RejectCall())
  75. InvokeUserListenCallback(pListen,
  76. Reason,
  77. &ListenCallbackParams);
  78. // Need to validate the listen handle; the associated object may have been
  79. // deleted during the user callback by this thread
  80. if (ValidateListen(hListen) == CC_OK) {
  81. HQ931LISTEN hQ931Listen = pListen->hQ931Listen;
  82. UnlockListen(pListen);
  83. status = Q931CancelListen(hQ931Listen);
  84. if (LockListen(hListen, &pListen) == CC_OK) {
  85. FreeListen(pListen);
  86. }
  87. }
  88. }
  89. HResultLeave(status);
  90. } // ListenReject()
  91. HRESULT PlaceCallConfirm (void *pCallVoid, void *pConferenceVoid)
  92. {
  93. register PCALL pCall = (PCALL) pCallVoid;
  94. HRESULT status;
  95. // Free Alias lists
  96. if (pCall->GkiCall.pCalleeAliasNames != NULL) {
  97. Q931FreeAliasNames(pCall->GkiCall.pCalleeAliasNames);
  98. pCall->GkiCall.pCalleeAliasNames = NULL;
  99. }
  100. if (pCall->GkiCall.pCalleeExtraAliasNames != NULL) {
  101. Q931FreeAliasNames(pCall->GkiCall.pCalleeExtraAliasNames);
  102. pCall->GkiCall.pCalleeExtraAliasNames = NULL;
  103. }
  104. if (pCall->pQ931PeerConnectAddr == NULL) {
  105. pCall->pQ931PeerConnectAddr = (PCC_ADDR)Malloc(sizeof(CC_ADDR));
  106. if (pCall->pQ931PeerConnectAddr == NULL)
  107. return PlaceCallReject(pCallVoid, pConferenceVoid, CC_NO_MEMORY);
  108. }
  109. pCall->pQ931PeerConnectAddr->nAddrType = CC_IP_BINARY;
  110. pCall->pQ931PeerConnectAddr->bMulticast = FALSE;
  111. pCall->pQ931PeerConnectAddr->Addr.IP_Binary.wPort = pCall->GkiCall.wPort;
  112. pCall->pQ931PeerConnectAddr->Addr.IP_Binary.dwAddr = ntohl(pCall->GkiCall.dwIpAddress);
  113. status = PlaceCall(pCall, (PCONFERENCE)pConferenceVoid);
  114. if (status != CC_OK)
  115. PlaceCallReject(pCallVoid, pConferenceVoid, status);
  116. return status;
  117. } // PlaceCallConfirm()
  118. HRESULT PlaceCallReject (void *pCallVoid, void *pConferenceVoid, HRESULT Reason)
  119. {
  120. register PCALL pCall = (PCALL) pCallVoid;
  121. register PCONFERENCE pConference = (PCONFERENCE) pConferenceVoid;
  122. CC_HCONFERENCE hConference;
  123. HRESULT status = CC_OK;
  124. CC_CONNECT_CALLBACK_PARAMS ConnectCallbackParams = {0};
  125. CC_HCALL hCall;
  126. PCALL pCall2;
  127. ASSERT(pCall != NULL);
  128. ASSERT(pConference != NULL);
  129. // Free Alias lists
  130. if (pCall->GkiCall.pCalleeAliasNames != NULL) {
  131. Q931FreeAliasNames(pCall->GkiCall.pCalleeAliasNames);
  132. pCall->GkiCall.pCalleeAliasNames = NULL;
  133. }
  134. if (pCall->GkiCall.pCalleeExtraAliasNames != NULL) {
  135. Q931FreeAliasNames(pCall->GkiCall.pCalleeExtraAliasNames);
  136. pCall->GkiCall.pCalleeExtraAliasNames = NULL;
  137. }
  138. // Inform Call Control client of failure
  139. ConnectCallbackParams.pNonStandardData = pCall->pPeerNonStandardData;
  140. ConnectCallbackParams.pszPeerDisplay = pCall->pszPeerDisplay;
  141. ConnectCallbackParams.bRejectReason = 0;
  142. ConnectCallbackParams.pTermCapList = pCall->pPeerH245TermCapList;
  143. ConnectCallbackParams.pH2250MuxCapability = pCall->pPeerH245H2250MuxCapability;
  144. ConnectCallbackParams.pTermCapDescriptors = pCall->pPeerH245TermCapDescriptors;
  145. ConnectCallbackParams.pLocalAddr = pCall->pQ931LocalConnectAddr;
  146. if (pCall->pQ931DestinationAddr == NULL)
  147. ConnectCallbackParams.pPeerAddr = pCall->pQ931PeerConnectAddr;
  148. else
  149. ConnectCallbackParams.pPeerAddr = pCall->pQ931DestinationAddr;
  150. ConnectCallbackParams.pVendorInfo = pCall->pPeerVendorInfo;
  151. if (pConference->ConferenceMode == MULTIPOINT_MODE)
  152. ConnectCallbackParams.bMultipointConference = TRUE;
  153. else
  154. ConnectCallbackParams.bMultipointConference = FALSE;
  155. ConnectCallbackParams.pConferenceID = &pConference->ConferenceID;
  156. ConnectCallbackParams.pMCAddress = pConference->pMultipointControllerAddr;
  157. ConnectCallbackParams.pAlternateAddress = NULL;
  158. ConnectCallbackParams.dwUserToken = pCall->dwUserToken;
  159. hConference = pConference->hConference;
  160. InvokeUserConferenceCallback(pConference,
  161. CC_CONNECT_INDICATION,
  162. Reason,
  163. &ConnectCallbackParams);
  164. if (ValidateConference(hConference) == CC_OK) {
  165. // Start up an enqueued call, if one exists
  166. for ( ; ; ) {
  167. status = RemoveEnqueuedCallFromConference(pConference, &hCall);
  168. if ((status != CC_OK) || (hCall == CC_INVALID_HANDLE))
  169. break;
  170. status = LockCall(hCall, &pCall2);
  171. if (status == CC_OK) {
  172. pCall2->CallState = PLACED;
  173. status = PlaceCall(pCall2, pConference);
  174. UnlockCall(pCall2);
  175. if (status == CC_OK)
  176. break;
  177. }
  178. }
  179. }
  180. HResultLeave(status);
  181. } // PlaceCallReject()
  182. HRESULT AcceptCallConfirm (void *pCallVoid, void *pConferenceVoid)
  183. {
  184. CC_HCALL hCall = ((PCALL)pCallVoid)->hCall;
  185. CC_HCONFERENCE hConference = ((PCONFERENCE)pConferenceVoid)->hConference;
  186. HRESULT status;
  187. status = AcceptCall((PCALL)pCallVoid, (PCONFERENCE)pConferenceVoid);
  188. LockConference(hConference, (PPCONFERENCE)&pConferenceVoid);
  189. LockCall(hCall, (PPCALL)&pCallVoid);
  190. if (status != CC_OK && pCallVoid != NULL && pConferenceVoid != NULL)
  191. AcceptCallReject(pCallVoid, pConferenceVoid, status);
  192. return status;
  193. } // AcceptCallConfirm()
  194. HRESULT AcceptCallReject (void *pCallVoid, void *pConferenceVoid, HRESULT Reason)
  195. {
  196. register PCALL pCall = (PCALL) pCallVoid;
  197. register PCONFERENCE pConference = (PCONFERENCE) pConferenceVoid;
  198. HRESULT status = CC_OK;
  199. CC_CONNECT_CALLBACK_PARAMS ConnectCallbackParams = {0};
  200. status = Q931RejectCall(pCall->hQ931Call, // Q931 call handle
  201. CC_REJECT_GATEKEEPER_RESOURCES,
  202. &pCall->ConferenceID, // Conference Identifier
  203. NULL, // alternate address
  204. pCall->pLocalNonStandardData);
  205. ConnectCallbackParams.pNonStandardData = pCall->pPeerNonStandardData;
  206. ConnectCallbackParams.pszPeerDisplay = pCall->pszPeerDisplay;
  207. ConnectCallbackParams.bRejectReason = 0;
  208. ConnectCallbackParams.pTermCapList = pCall->pPeerH245TermCapList;
  209. ConnectCallbackParams.pH2250MuxCapability = pCall->pPeerH245H2250MuxCapability;
  210. ConnectCallbackParams.pTermCapDescriptors = pCall->pPeerH245TermCapDescriptors;
  211. ConnectCallbackParams.pLocalAddr = pCall->pQ931LocalConnectAddr;
  212. if (pCall->pQ931DestinationAddr == NULL)
  213. ConnectCallbackParams.pPeerAddr = pCall->pQ931PeerConnectAddr;
  214. else
  215. ConnectCallbackParams.pPeerAddr = pCall->pQ931DestinationAddr;
  216. if (pConference->ConferenceMode == MULTIPOINT_MODE)
  217. ConnectCallbackParams.bMultipointConference = TRUE;
  218. else
  219. ConnectCallbackParams.bMultipointConference = FALSE;
  220. ConnectCallbackParams.pVendorInfo = pCall->pPeerVendorInfo;
  221. ConnectCallbackParams.pConferenceID = &pConference->ConferenceID;
  222. ConnectCallbackParams.pMCAddress = pConference->pMultipointControllerAddr;
  223. ConnectCallbackParams.pAlternateAddress = NULL;
  224. ConnectCallbackParams.dwUserToken = pCall->dwUserToken;
  225. InvokeUserConferenceCallback(pConference,
  226. CC_CONNECT_INDICATION,
  227. Reason,
  228. &ConnectCallbackParams);
  229. HResultLeave(status);
  230. } // AcceptCallReject()
  231. #if 0
  232. HRESULT CancelCallConfirm (void *pCallVoid, void *pConferenceVoid)
  233. {
  234. PCALL pCall = (PCALL) pCallVoid;
  235. PCONFERENCE pConference = (PCONFERENCE) pConferenceVoid;
  236. HRESULT status;
  237. H245_INST_T H245Instance;
  238. HQ931CALL hQ931Call;
  239. CC_HCONFERENCE hConference;
  240. HRESULT SaveStatus;
  241. CC_HCALL hCall;
  242. H245Instance = pCall->H245Instance;
  243. hQ931Call = pCall->hQ931Call;
  244. hConference = pCall->hConference;
  245. FreeCall(pCall);
  246. if (H245Instance != H245_INVALID_ID)
  247. SaveStatus = H245ShutDown(H245Instance);
  248. else
  249. SaveStatus = H245_ERROR_OK;
  250. if (SaveStatus == H245_ERROR_OK) {
  251. SaveStatus = Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
  252. // Q931Hangup may legitimately return CS_BAD_PARAM, because the Q.931 call object
  253. // may have been deleted at this point
  254. if (SaveStatus == CS_BAD_PARAM)
  255. SaveStatus = CC_OK;
  256. } else
  257. Q931Hangup(hQ931Call, CC_REJECT_UNDEFINED_REASON);
  258. // Start up an enqueued call, if one exists
  259. for ( ; ; ) {
  260. status = RemoveEnqueuedCallFromConference(pConference, &hCall);
  261. if ((status != CC_OK) || (hCall == CC_INVALID_HANDLE))
  262. break;
  263. status = LockCall(hCall, &pCall);
  264. if (status == CC_OK) {
  265. pCall->CallState = PLACED;
  266. status = PlaceCall(pCall, pConference);
  267. UnlockCall(pCall);
  268. if (status == CC_OK)
  269. break;
  270. }
  271. }
  272. UnlockConference(pConference);
  273. if (SaveStatus != CC_OK)
  274. status = SaveStatus;
  275. HResultLeave(status);
  276. } // CancelCallConfirm()
  277. HRESULT CancelCallReject (void *pCallVoid, void *pConferenceVoid)
  278. {
  279. // I don't care what the Gatekeeper says; I'm shutting down the call!
  280. return CancelCallConfirm(pCallVoid, pConferenceVoid);
  281. } // CancelCallReject()
  282. #endif
  283. HRESULT OpenChannelConfirm (DWORD hChannel)
  284. {
  285. HRESULT status;
  286. PCHANNEL pChannel;
  287. PCONFERENCE pConference;
  288. WORD wNumCalls;
  289. PCC_HCALL CallList;
  290. HRESULT SaveStatus;
  291. unsigned i;
  292. PCALL pCall;
  293. status = LockChannelAndConference(hChannel, &pChannel, &pConference);
  294. if (status == CC_OK) {
  295. // Open a logical channel for each established call
  296. status = EnumerateCallsInConference(&wNumCalls, &CallList, pConference, ESTABLISHED_CALL);
  297. if (status == CC_OK) {
  298. SaveStatus = CC_OK;
  299. for (i = 0; i < wNumCalls; ++i) {
  300. if (LockCall(CallList[i], &pCall) == CC_OK) {
  301. status = H245OpenChannel(pCall->H245Instance, // H245 instance
  302. pChannel->hChannel, // dwTransId
  303. pChannel->wLocalChannelNumber,
  304. pChannel->pTxH245TermCap, // TxMode
  305. pChannel->pTxMuxTable, // TxMux
  306. H245_INVALID_PORT_NUMBER, // TxPort
  307. pChannel->pRxH245TermCap, // RxMode
  308. pChannel->pRxMuxTable, // RxMux
  309. pChannel->pSeparateStack);
  310. if (status == H245_ERROR_OK)
  311. (pChannel->wNumOutstandingRequests)++;
  312. else
  313. SaveStatus = status;
  314. UnlockCall(pCall);
  315. }
  316. }
  317. if (CallList != NULL)
  318. Free(CallList);
  319. if (pChannel->wNumOutstandingRequests == 0) {
  320. // all open channel requests failed
  321. FreeChannel(pChannel);
  322. }
  323. else {
  324. UnlockChannel(pChannel);
  325. }
  326. if (SaveStatus != CC_OK)
  327. status = SaveStatus;
  328. }
  329. else {
  330. FreeChannel(pChannel);
  331. }
  332. UnlockConference(pConference);
  333. }
  334. HResultLeave(status);
  335. } // OpenChannelConfirm()
  336. HRESULT OpenChannelReject (DWORD hChannel, HRESULT Reason)
  337. {
  338. PCHANNEL pChannel;
  339. PCONFERENCE pConference;
  340. CC_HCONFERENCE hConference;
  341. HRESULT status;
  342. CC_TX_CHANNEL_OPEN_CALLBACK_PARAMS Params = {0};
  343. status = LockChannelAndConference(hChannel, &pChannel, &pConference);
  344. if (status == CC_OK) {
  345. // Inform Call Control client of failure
  346. Params.hChannel = hChannel;
  347. Params.pPeerRTPAddr = pChannel->pPeerRTPAddr;
  348. Params.pPeerRTCPAddr = pChannel->pPeerRTCPAddr;
  349. Params.dwRejectReason = 0;
  350. Params.dwUserToken = pChannel->dwUserToken;
  351. hConference = pConference->hConference;
  352. InvokeUserConferenceCallback(pConference,
  353. CC_TX_CHANNEL_OPEN_INDICATION,
  354. Reason,
  355. &Params);
  356. if (ValidateChannel(hChannel) == CC_OK)
  357. FreeChannel(pChannel);
  358. if (ValidateConference(hConference) == CC_OK)
  359. UnlockConference(pConference);
  360. }
  361. HResultLeave(status);
  362. } // OpenChannelReject()
  363. HRESULT AcceptChannelConfirm(DWORD hChannel)
  364. {
  365. HRESULT status;
  366. PCHANNEL pChannel;
  367. PCONFERENCE pConference;
  368. CC_HCONFERENCE hConference;
  369. PCALL pCall;
  370. unsigned i;
  371. H245_MUX_T H245MuxTable;
  372. CC_ACCEPT_CHANNEL_CALLBACK_PARAMS Params;
  373. status = LockChannelAndConference(hChannel, &pChannel, &pConference);
  374. if (status != CC_OK)
  375. HResultLeave(status);
  376. status = LockCall(pChannel->hCall, &pCall);
  377. if (status != CC_OK) {
  378. UnlockChannel(pChannel);
  379. UnlockConference(pConference);
  380. HResultLeave(status);
  381. }
  382. if (pChannel->wNumOutstandingRequests != 0) {
  383. PCC_ADDR pRTPAddr = pChannel->pLocalRTPAddr;
  384. PCC_ADDR pRTCPAddr = pChannel->pLocalRTCPAddr;
  385. if ((pChannel->bMultipointChannel) &&
  386. (pConference->tsMultipointController == TS_TRUE)) {
  387. // Supply the RTP and RTCP addresses in the OpenLogicalChannelAck
  388. if (pConference->pSessionTable != NULL) {
  389. for (i = 0; i < pConference->pSessionTable->wLength; ++i) {
  390. if (pConference->pSessionTable->SessionInfoArray[i].bSessionID ==
  391. pChannel->bSessionID) {
  392. pRTPAddr = pConference->pSessionTable->SessionInfoArray[i].pRTPAddr;
  393. pRTCPAddr = pConference->pSessionTable->SessionInfoArray[i].pRTCPAddr;
  394. break;
  395. }
  396. }
  397. }
  398. }
  399. H245MuxTable.Kind = H245_H2250ACK;
  400. H245MuxTable.u.H2250ACK.nonStandardList = NULL;
  401. if (pRTPAddr != NULL) {
  402. if (pRTPAddr->bMulticast)
  403. H245MuxTable.u.H2250ACK.mediaChannel.type = H245_IP_MULTICAST;
  404. else
  405. H245MuxTable.u.H2250ACK.mediaChannel.type = H245_IP_UNICAST;
  406. H245MuxTable.u.H2250ACK.mediaChannel.u.ip.tsapIdentifier =
  407. pRTPAddr->Addr.IP_Binary.wPort;
  408. HostToH245IPNetwork(H245MuxTable.u.H2250ACK.mediaChannel.u.ip.network,
  409. pRTPAddr->Addr.IP_Binary.dwAddr);
  410. H245MuxTable.u.H2250ACK.mediaChannelPresent = TRUE;
  411. } else
  412. H245MuxTable.u.H2250ACK.mediaChannelPresent = FALSE;
  413. if (pRTCPAddr != NULL) {
  414. if (pRTCPAddr->bMulticast)
  415. H245MuxTable.u.H2250ACK.mediaControlChannel.type = H245_IP_MULTICAST;
  416. else
  417. H245MuxTable.u.H2250ACK.mediaControlChannel.type = H245_IP_UNICAST;
  418. H245MuxTable.u.H2250ACK.mediaControlChannel.u.ip.tsapIdentifier =
  419. pRTCPAddr->Addr.IP_Binary.wPort;
  420. HostToH245IPNetwork(H245MuxTable.u.H2250ACK.mediaControlChannel.u.ip.network,
  421. pRTCPAddr->Addr.IP_Binary.dwAddr);
  422. H245MuxTable.u.H2250ACK.mediaControlChannelPresent = TRUE;
  423. } else
  424. H245MuxTable.u.H2250ACK.mediaControlChannelPresent = FALSE;
  425. H245MuxTable.u.H2250ACK.dynamicRTPPayloadTypePresent = FALSE;
  426. H245MuxTable.u.H2250ACK.sessionIDPresent = TRUE;
  427. H245MuxTable.u.H2250ACK.sessionID = pChannel->bSessionID;
  428. status = H245OpenChannelAccept(pCall->H245Instance,
  429. 0, // dwTransId
  430. pChannel->wRemoteChannelNumber, // Rx channel
  431. &H245MuxTable,
  432. 0, // Tx channel
  433. NULL, // Tx mux
  434. H245_INVALID_PORT_NUMBER,// Port
  435. pChannel->pSeparateStack);
  436. if (status == CC_OK)
  437. pChannel->wNumOutstandingRequests = 0;
  438. else
  439. --(pChannel->wNumOutstandingRequests);
  440. }
  441. pChannel->tsAccepted = TS_TRUE;
  442. Params.hChannel = hChannel;
  443. if (status == CC_OK)
  444. UnlockChannel(pChannel);
  445. else
  446. FreeChannel(pChannel);
  447. UnlockCall(pCall);
  448. hConference = pConference->hConference;
  449. InvokeUserConferenceCallback(pConference,
  450. CC_ACCEPT_CHANNEL_INDICATION,
  451. status,
  452. &Params);
  453. if (ValidateConference(hConference) == CC_OK)
  454. UnlockConference(pConference);
  455. HResultLeave(status);
  456. } // AcceptChannelConfirm(void()
  457. HRESULT AcceptChannelReject (DWORD hChannel, HRESULT Reason)
  458. {
  459. HRESULT status;
  460. PCHANNEL pChannel;
  461. PCONFERENCE pConference;
  462. CC_HCONFERENCE hConference;
  463. CC_ACCEPT_CHANNEL_CALLBACK_PARAMS Params;
  464. status = LockChannelAndConference(hChannel, &pChannel, &pConference);
  465. if (status == CC_OK) {
  466. Params.hChannel = hChannel;
  467. FreeChannel(pChannel);
  468. hConference = pConference->hConference;
  469. InvokeUserConferenceCallback(pConference,
  470. CC_ACCEPT_CHANNEL_INDICATION,
  471. Reason,
  472. &Params);
  473. if (ValidateConference(hConference) == CC_OK)
  474. UnlockConference(pConference);
  475. }
  476. HResultLeave(status);
  477. } // AcceptChannelReject()
  478. //
  479. // Handle gratuitous messages from Gatekeeper
  480. //
  481. // Note: pCall assumed locked when called!
  482. HRESULT Disengage(void *pCallVoid)
  483. {
  484. CC_HCALL hCall = ((PCALL)pCallVoid)->hCall;
  485. HRESULT status;
  486. UnlockCall((PCALL)pCallVoid);
  487. status = ProcessRemoteHangup(hCall, CC_INVALID_HANDLE, CC_REJECT_GATEKEEPER_TERMINATED);
  488. HResultLeave(status);
  489. } // Disengage()
  490. // Note: pCall assumed locked when called!
  491. HRESULT BandwidthShrunk(void *pCallVoid,
  492. void *pConferenceVoid,
  493. unsigned uBandwidthAllocated,
  494. long lBandwidthChange)
  495. {
  496. PCALL pCall = (PCALL) pCallVoid;
  497. PCONFERENCE pConference = (PCONFERENCE)pConferenceVoid;
  498. CC_BANDWIDTH_CALLBACK_PARAMS Params;
  499. Params.hCall = pCall->hCall;
  500. Params.dwBandwidthTotal = uBandwidthAllocated;
  501. Params.lBandwidthChange = lBandwidthChange;
  502. InvokeUserConferenceCallback(pConference,
  503. CC_BANDWIDTH_CHANGED_INDICATION,
  504. CC_OK,
  505. &Params);
  506. HResultLeave(CC_OK);
  507. } // BandwidthShrunk()
  508. #else // GATEKEEPER
  509. static char ch; // Kludge around warning C4206: nonstandard extension used : translation unit is empty
  510. #endif // GATEKEEPER