windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/rrcm/rtcp/rtcpsess.cpp
2020-09-26 16:20:57 +08:00

1095 lines
29 KiB
C++
Raw Permalink Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/*----------------------------------------------------------------------------
* File: RTCPSESS.C
* Product: RTP/RTCP implementation
* Description: Provides RTCP session management.
*
* INTEL Corporation Proprietary Information
* This listing is supplied under the terms of a license agreement with
* Intel Corporation and may not be copied nor disclosed except in
* accordance with the terms of that agreement.
* Copyright (c) 1995 Intel Corporation.
*--------------------------------------------------------------------------*/
#include "rrcm.h"
/*---------------------------------------------------------------------------
/ Global Variables
/--------------------------------------------------------------------------*/
/*---------------------------------------------------------------------------
/ External Variables
/--------------------------------------------------------------------------*/
extern PRTCP_CONTEXT pRTCPContext;
extern PRTP_CONTEXT pRTPContext;
extern RRCM_WS RRCMws;
#ifdef ENABLE_ISDM2
extern ISDM2 Isdm2;
#endif
#ifdef _DEBUG
extern char debug_string[];
#endif
#if (defined(_DEBUG) || defined(PCS_COMPLIANCE))
//INTEROP
extern LPInteropLogger RTPLogger;
#endif
/*----------------------------------------------------------------------------
* Function : CreateRTCPSession
* Description: Creates an RTCP session.
*
* Input : RTPsd : RTP socket descriptor
* RTCPsd : RTCP socket descriptor
* lpTo : To address
* toLen : To address length
* pSdesInfo : -> to SDES information
* dwStreamClock : Stream clocking frequency
* pEncryptInfo : -> to encryption information
* ssrc : If set, user selected SSRC
* pSSRCcallback : Callback for user's selected SSRC
* dwCallbackInfo : User callback information
* miscInfo : Miscelleanous information:
* H.323Conf: 0x00000002
* Encrypt SR/RR: 0x00000004
* RTCPon: 0x00000008
* dwRtpSessionBw : RTP session bandwidth used for RTCP BW
* *pRTCPStatus : -> to status information
*
* Return: NULL : Couldn't create RTCP session
* !0 : RTCP session's address
---------------------------------------------------------------------------*/
PRTCP_SESSION CreateRTCPSession (SOCKET RTPsd,
SOCKET RTCPsd,
LPVOID lpTo,
DWORD toLen,
PSDES_DATA pSdesInfo,
DWORD dwStreamClock,
PENCRYPT_INFO pEncryptInfo,
DWORD ssrc,
PRRCM_EVENT_CALLBACK pRRCMcallback,
DWORD_PTR dwCallbackInfo,
DWORD miscInfo,
DWORD dwRtpSessionBw,
DWORD *pRTCPstatus)
{
PRTCP_SESSION pRTCPses = NULL;
PSSRC_ENTRY pSSRCentry = NULL;
DWORD dwStatus = RRCM_NoError;
DWORD startRtcp = FALSE;
char hName[256];
int tmpSize;
struct sockaddr_in *pSockAddr;
IN_OUT_STR ("RTCP: Enter CreateRTCPSession()\n");
// set status
*pRTCPstatus = RRCM_NoError;
// allocate all required resources for the RTCP session
dwStatus = allocateRTCPsessionResources (&pRTCPses,
&pSSRCentry);
if (dwStatus != RRCM_NoError)
{
RRCM_DBG_MSG ("RTCP: ERROR - Resource allocation failed", 0,
__FILE__, __LINE__, DBG_CRITICAL);
IN_OUT_STR ("RTCP: Exit CreateRTCPSession()\n");
*pRTCPstatus = dwStatus;
return (NULL);
}
// if this is the first session, create the RTCP thread, which
// will be killed when no more sessions exist.
if (pRTCPContext->RTCPSession.prev == NULL)
{
startRtcp = TRUE;
}
// save the parent RTCP session address in the SSRC entry
pSSRCentry->pRTCPses = pRTCPses;
// network destination address
if (toLen)
{
pRTCPses->dwSessionStatus = RTCP_DEST_LEARNED;
pRTCPses->toLen = toLen;
memcpy (&pRTCPses->toBfr, lpTo, toLen);
}
// mark the session as new for the benefit of the RTCP thread
pRTCPses->dwSessionStatus |= NEW_RTCP_SESSION;
#ifdef ENABLE_ISDM2
// initialize the session key in case ISDM is used
pRTCPses->hSessKey = NULL;
#endif
// number of SSRC for this RTCP session
pRTCPses->dwCurNumSSRCperSes = 1;
#ifdef MONITOR_STATS
pRTCPses->dwHiNumSSRCperSes = 1;
#endif
// SSRC entry related information
pSSRCentry->RTPsd = RTPsd;
pSSRCentry->RTCPsd = RTCPsd;
// get our own transport address -
// will be used for collision resolution when using multicast
tmpSize = sizeof (SOCKADDR);
dwStatus = RRCMws.getsockname (RTPsd, (PSOCKADDR)pSSRCentry->from, &tmpSize);
// only process when no error is reported. If the socket is not bound
// it won't cause any problem for unicast or multicast if the sender
// has not join the mcast group. If the sender joins the mcast group
// it's socket should be bound by now as specified in the EPS
if (dwStatus == 0)
{
// if bound to INADDR_ANY, address will be 0
pSockAddr = (PSOCKADDR_IN)&pSSRCentry->from;
if (pSockAddr->sin_addr.s_addr == 0)
{
// get the host name (to get the local IP address)
if ( ! RRCMws.gethostname (hName, sizeof(hName)))
{
LPHOSTENT lpHEnt;
// get the host by name infor
if ((lpHEnt = RRCMws.gethostbyname (hName)) != NULL)
{
// get the local IP address
pSockAddr->sin_addr.s_addr =
*((u_long *)lpHEnt->h_addr_list[0]);
}
}
}
}
// build session's SDES information
buildSDESinfo (pSSRCentry, pSdesInfo);
// link the SSRC to the RTCP session list of Xmt SSRCs entries
addToHeadOfList (&(pRTCPses->XmtSSRCList),
(PLINK_LIST)pSSRCentry,
&pRTCPses->critSect);
// initialize the number of stream for this session
pRTCPses->dwNumStreamPerSes = 1;
// get a unique SSRC for this session
if (ssrc)
pSSRCentry->SSRC = ssrc;
else
pSSRCentry->SSRC = getSSRC (pRTCPses->XmtSSRCList,
pRTCPses->RcvSSRCList);
// RRCM callback notification
pRTCPses->pRRCMcallback = pRRCMcallback;
pRTCPses->dwCallbackUserInfo = dwCallbackInfo;
// set operation flag
if (miscInfo & H323_CONFERENCE)
pRTCPses->dwSessionStatus |= H323_CONFERENCE;
if (miscInfo & ENCRYPT_SR_RR)
pRTCPses->dwSessionStatus |= ENCRYPT_SR_RR;
// estimate the initial session bandwidth
if (dwRtpSessionBw == 0)
{
pSSRCentry->xmtInfo.dwRtcpStreamMinBW = INITIAL_RTCP_BANDWIDTH;
}
else
{
// RTCP bandwidth is 5% of the RTP bandwidth
pSSRCentry->xmtInfo.dwRtcpStreamMinBW = (dwRtpSessionBw * 5) / 100;
}
// the stream clocking frequency
pSSRCentry->dwStreamClock = dwStreamClock;
// initialize 'dwLastReportRcvdTime' to now
pSSRCentry->dwLastReportRcvdTime = timeGetTime();
#ifdef _DEBUG
wsprintf(debug_string,
"RTCP: Add new RTCP session: Addr:x%lX", pRTCPses);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
wsprintf(debug_string,
"RTCP: Add SSRC entry (Addr:x%lX, SSRC=x%lX) to session (Addr:x%lX)",
pSSRCentry, pSSRCentry->SSRC, pRTCPses);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
pSSRCentry->dwPrvTime = timeGetTime();
#endif
// turn on RTCP or not
if (miscInfo & RTCP_ON)
{
// this session sends and receives RTCP reports
pRTCPses->dwSessionStatus |= RTCP_ON;
}
// link the RTCP session to the head of the list of RTCP sessions
addToHeadOfList (&(pRTCPContext->RTCPSession),
(PLINK_LIST)pRTCPses,
&pRTCPContext->critSect);
#ifdef ENABLE_ISDM2
// register to ISDM only if destination address is known
if (Isdm2.hISDMdll && (pRTCPses->dwSessionStatus & RTCP_DEST_LEARNED))
registerSessionToISDM (pSSRCentry, pRTCPses, &Isdm2);
#endif
// create the RTCP thread if needed
if (startRtcp == TRUE)
{
// No RTCP thread if this fail
dwStatus = CreateRTCPthread ();
if (dwStatus != RRCM_NoError)
{
RRCM_DBG_MSG ("RTCP: ERROR - Cannot create RTCP thread", 0,
__FILE__, __LINE__, DBG_CRITICAL);
IN_OUT_STR ("RTCP: Exit CreateRTCPSession()\n");
*pRTCPstatus = dwStatus;
return (NULL);
}
}
IN_OUT_STR ("RTCP: Exit CreateRTCPSession()\n");
return (pRTCPses);
}
/*----------------------------------------------------------------------------
* Function : allocateRTCPsessionResources
* Description: Allocate all required resources for an RTCP session.
*
* Input : *pRTCPses: ->(->) to the RTCP session's information
* *pSSRCentry: ->(->) to the SSRC's entry
*
* Return: OK: RRCM_NoError
* !0: Error code (see RRCM.H)
---------------------------------------------------------------------------*/
DWORD allocateRTCPsessionResources (PRTCP_SESSION *pRTCPses,
PSSRC_ENTRY *pSSRCentry)
{
DWORD dwStatus = RRCM_NoError;
IN_OUT_STR ("RTCP: Enter allocateRTCPsessionResources()\n");
// get an RTCP session
*pRTCPses = (PRTCP_SESSION)HeapAlloc (pRTCPContext->hHeapRTCPSes,
HEAP_ZERO_MEMORY,
sizeof(RTCP_SESSION));
if (*pRTCPses == NULL)
dwStatus = RRCMError_RTCPResources;
// 'defined' RTCP resources
if (dwStatus == RRCM_NoError)
{
(*pRTCPses)->dwInitNumFreeRcvBfr = NUM_FREE_RCV_BFR;
(*pRTCPses)->dwRcvBfrSize = pRTPContext->registry.RTCPrcvBfrSize;
(*pRTCPses)->dwXmtBfrSize = RRCM_XMT_BFR_SIZE;
// allocate the RTCP session's Rcv/Xmt heaps and Rcv/Xmt buffers
dwStatus = allocateRTCPSessionHeaps (pRTCPses);
}
if (dwStatus == RRCM_NoError)
{
// initialize this session's critical section
InitializeCriticalSection (&(*pRTCPses)->critSect);
// allocate free list of RTCP receive buffers
dwStatus = allocateRTCPBfrList (&(*pRTCPses)->RTCPrcvBfrList,
(*pRTCPses)->hHeapRcvBfrList,
(*pRTCPses)->hHeapRcvBfr,
&(*pRTCPses)->dwInitNumFreeRcvBfr,
(*pRTCPses)->dwRcvBfrSize,
&(*pRTCPses)->critSect);
}
if (dwStatus == RRCM_NoError)
{
(*pRTCPses)->XmtBfr.buf = (char *)LocalAlloc(0,(*pRTCPses)->dwXmtBfrSize);
if ((*pRTCPses)->XmtBfr.buf == NULL)
dwStatus = RRCMError_RTCPResources;
}
if (dwStatus == RRCM_NoError)
{
// get an SSRC entry
*pSSRCentry = getOneSSRCentry (&pRTCPContext->RRCMFreeStat,
pRTCPContext->hHeapRRCMStat,
&pRTCPContext->dwInitNumFreeRRCMStat,
&pRTCPContext->critSect);
if (*pSSRCentry == NULL)
dwStatus = RRCMError_RTCPResources;
}
if (dwStatus == RRCM_NoError)
{
// manual-reset event that will be used to signal the end of the
// RTCP session to all of the session's stream
(*pRTCPses)->hShutdownDone = CreateEvent (NULL, TRUE, FALSE, NULL);
if ((*pRTCPses)->hShutdownDone == NULL)
{
dwStatus = RRCMError_RTCPResources;
RRCM_DBG_MSG ("RTCP: ERROR - CreateEvent()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
}
// any resource allocation problem ?
if (dwStatus != RRCM_NoError)
{
if (*pSSRCentry)
addToHeadOfList (&pRTCPContext->RRCMFreeStat,
(PLINK_LIST)*pSSRCentry,
&pRTCPContext->critSect);
if ((*pSSRCentry)->hXmtThread)
{
if (TerminateThread ((*pSSRCentry)->hXmtThread,
(*pSSRCentry)->dwXmtThreadID) == FALSE)
{
RRCM_DBG_MSG ("RTCP: ERROR - TerminateThread()",
GetLastError(), __FILE__, __LINE__, DBG_ERROR);
}
}
if (*pRTCPses)
{
if (HeapFree (pRTCPContext->hHeapRTCPSes, 0, *pRTCPses) == FALSE)
{
RRCM_DBG_MSG ("RTCP: ERROR - HeapFree()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
}
}
IN_OUT_STR ("RTCP: Exit allocateRTCPsessionResources()\n");
return dwStatus;
}
/*----------------------------------------------------------------------------
* Function : buildSDESinfo
* Description: Build the session's SDES information
*
* Input : pRTCPses: -> to session's
* pSdesInfo: -> to SDES information
*
* Return: OK: RRCM_NoError
* !0: Error code (see RRCM.H)
---------------------------------------------------------------------------*/
DWORD buildSDESinfo (PSSRC_ENTRY pSSRCentry,
PSDES_DATA pSdesInfo)
{
PSDES_DATA pTmpSdes;
DWORD CnameOK = FALSE;
IN_OUT_STR ("RTCP: Enter buildSDESinfo()\n");
pTmpSdes = pSdesInfo;
while (pTmpSdes->dwSdesType)
{
switch (pTmpSdes->dwSdesType)
{
case RTCP_SDES_CNAME:
pSSRCentry->cnameInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->cnameInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->cnameInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->cnameInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
CnameOK = TRUE;
break;
case RTCP_SDES_NAME:
pSSRCentry->nameInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->nameInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->nameInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->nameInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
break;
case RTCP_SDES_EMAIL:
pSSRCentry->emailInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->emailInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->emailInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->emailInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
break;
case RTCP_SDES_PHONE:
pSSRCentry->phoneInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->phoneInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->phoneInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->phoneInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
break;
case RTCP_SDES_LOC:
pSSRCentry->locInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->locInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->locInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->locInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
break;
case RTCP_SDES_TOOL:
pSSRCentry->toolInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->toolInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->toolInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->toolInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
break;
case RTCP_SDES_TXT:
pSSRCentry->txtInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->txtInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->txtInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->txtInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
break;
case RTCP_SDES_PRIV:
pSSRCentry->privInfo.dwSdesLength = pTmpSdes->dwSdesLength;
memcpy (pSSRCentry->privInfo.sdesBfr, pTmpSdes->sdesBfr,
pTmpSdes->dwSdesLength);
pSSRCentry->privInfo.dwSdesFrequency =
frequencyToPckt (pTmpSdes->dwSdesFrequency);
pSSRCentry->privInfo.dwSdesEncrypted = pTmpSdes->dwSdesEncrypted;
break;
}
pTmpSdes++;
}
// default CNAME if none provided
if (CnameOK == FALSE)
{
pSSRCentry->cnameInfo.dwSdesLength = sizeof(szDfltCname);
memcpy (pSSRCentry->cnameInfo.sdesBfr, szDfltCname,
sizeof(szDfltCname));
pSSRCentry->cnameInfo.dwSdesFrequency = 1;
pSSRCentry->cnameInfo.dwSdesEncrypted = 0;
}
IN_OUT_STR ("RTCP: Exit buildSDESinfo()\n");
return (RRCM_NoError);
}
/*----------------------------------------------------------------------------
* Function : frequencyToPckt
* Description: Transform the required frequency to a number of packet. (To
* be used by a modulo function)
*
* Input : freq: Desired frequency from 0 to 100
*
* Return: X: Packet to skip, ie, one out of X
---------------------------------------------------------------------------*/
DWORD frequencyToPckt (DWORD freq)
{
if (freq <= 10)
return 9;
else if (freq <= 20)
return 5;
else if (freq <= 25)
return 4;
else if (freq <= 33)
return 3;
else if (freq <= 50)
return 2;
else
return 1;
}
/*----------------------------------------------------------------------------
* Function : deleteRTCPSession
* Description: Closes an RTCP session.
*
* Input : RTCPsd : RTCP socket descriptor
* byeReason : -> to the BYE reason
*
* Return: OK: RRCM_NoError
* !0: Error code (see RRCM.H)
---------------------------------------------------------------------------*/
DWORD deleteRTCPSession (SOCKET RTCPsd,
PCHAR byeReason)
{
PLINK_LIST pTmp;
PSSRC_ENTRY pSSRC;
PRTCP_SESSION pRTCP;
DWORD dwStatus = RRCM_NoError;
DWORD sessionFound = FALSE;
IN_OUT_STR ("RTCP: Enter deleteRTCPSEssion()\n");
// walk through the list from the tail
pTmp = pRTCPContext->RTCPSession.prev;
#ifdef _DEBUG
wsprintf(debug_string,
"RTCP: Deleting RTCP session: (Addr:x%lX) ...", pTmp);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
while (pTmp)
{
// get the right session to close by walking the transmit list
pSSRC = (PSSRC_ENTRY)((PRTCP_SESSION)pTmp)->XmtSSRCList.prev;
if (pSSRC->RTCPsd == RTCPsd)
{
sessionFound = TRUE;
// save a pointer to the RTCP session
pRTCP = pSSRC->pRTCPses;
// RTCP send BYE packet for this active stream
RTCPsendBYE (pSSRC, NULL);
// flush out any outstanding I/O
RTCPflushIO (pSSRC);
// if this is the only RTCP session left, terminate the RTCP
// timeout thread, so it doesn't access the session when it expires
if ((pRTCPContext->RTCPSession.prev)->next == NULL)
terminateRtcpThread ();
// lock out access to this RTCP session
EnterCriticalSection (&pRTCP->critSect);
// free all Rcv & Xmt SSRC entries used by this session
deleteSSRClist (pRTCP,
&pRTCPContext->RRCMFreeStat,
pRTCPContext);
#ifdef ENABLE_ISDM2
if (Isdm2.hISDMdll && pRTCP->hSessKey)
Isdm2.ISDMEntry.ISD_DeleteKey(pRTCP->hSessKey);
#endif
// release the RTCP session's heap
if (pRTCP->hHeapRcvBfrList)
{
if (HeapDestroy (pRTCP->hHeapRcvBfrList) == FALSE)
{
RRCM_DBG_MSG ("RTCP: ERROR - HeapDestroy()",
GetLastError(), __FILE__, __LINE__,
DBG_ERROR);
}
}
if (pRTCP->hHeapRcvBfr)
{
if (HeapDestroy (pRTCP->hHeapRcvBfr) == FALSE)
{
RRCM_DBG_MSG ("RTCP: ERROR - HeapDestroy()",
GetLastError(), __FILE__, __LINE__,
DBG_ERROR);
}
}
if (pRTCP->XmtBfr.buf)
LocalFree(pRTCP->XmtBfr.buf);
// remove the entry from the list of RTCP session
if (pTmp->next == NULL)
removePcktFromHead (&pRTCPContext->RTCPSession,
&pRTCPContext->critSect);
else if (pTmp->prev == NULL)
removePcktFromTail (&pRTCPContext->RTCPSession,
&pRTCPContext->critSect);
else
{
// in between, relink around
(pTmp->prev)->next = pTmp->next;
(pTmp->next)->prev = pTmp->prev;
}
// release the critical section
LeaveCriticalSection (&pRTCP->critSect);
DeleteCriticalSection (&pRTCP->critSect);
// put the RTCP session back on its heap
if (HeapFree (pRTCPContext->hHeapRTCPSes,
0,
pRTCP) == FALSE)
{
RRCM_DBG_MSG ("RTCP: ERROR - HeapFree()",
GetLastError(), __FILE__, __LINE__,
DBG_ERROR);
}
break;
}
pTmp = pTmp->next;
}
if (sessionFound != TRUE)
dwStatus = RRCMError_RTCPInvalidSession;
IN_OUT_STR ("RTCP: Exit deleteRTCPSEssion()\n");
return (dwStatus);
}
/*----------------------------------------------------------------------------
* Function : CreateRTCPthread
* Description: Create the RTCP thread / timeout thread depending on
* compilation flag.
*
* Input : None.
*
* Return: None
---------------------------------------------------------------------------*/
DWORD CreateRTCPthread (void)
{
DWORD dwStatus = RRCM_NoError;
IN_OUT_STR ("RTCP: Enter CreateRTCPthread()\n");
pRTCPContext->hTerminateRtcpEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
if (pRTCPContext->hTerminateRtcpEvent == NULL)
{
dwStatus = RRCMError_RTCPResources;
RRCM_DBG_MSG ("RTCP: ERROR - CreateEvent()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
pRTCPContext->hRtcpRptRequestEvent = CreateEvent (NULL, TRUE, FALSE, NULL);
if (pRTCPContext->hRtcpRptRequestEvent == NULL)
{
dwStatus = RRCMError_RTCPResources;
RRCM_DBG_MSG ("RTCP: ERROR - CreateEvent()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
if (pRTCPContext->hTerminateRtcpEvent)
{
// create RTCP thread
pRTCPContext->hRtcpThread = CreateThread (
NULL,
0,
(LPTHREAD_START_ROUTINE)RTCPThread,
pRTCPContext,
0,
&pRTCPContext->dwRtcpThreadID);
if (pRTCPContext->hRtcpThread == FALSE)
{
dwStatus = RRCMError_RTCPThreadCreation;
RRCM_DBG_MSG ("RTCP: ERROR - CreateThread()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
#ifdef _DEBUG
else
{
wsprintf(debug_string,
"RTCP: Create RTCP thread. Handle: x%lX - ID: x%lX",
pRTCPContext->hRtcpThread,
pRTCPContext->dwRtcpThreadID);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
}
#endif
}
IN_OUT_STR ("RTCP: Exit CreateRTCPthread()\n");
return dwStatus;
}
/*----------------------------------------------------------------------------
* Function : terminateRtcpThread
* Description: Terminate the RTCP thread.
*
* Input : None.
*
* Return: None
---------------------------------------------------------------------------*/
void terminateRtcpThread (void)
{
DWORD dwStatus;
IN_OUT_STR ("RTCP: Enter terminateRtcpThread()\n");
if (pRTCPContext->hRtcpThread)
{
// make sure the RTCP thread is running
RTCPThreadCtrl (RTCP_ON);
// signal the thread to terminate
SetEvent (pRTCPContext->hTerminateRtcpEvent);
// wait for the RTCP thread to be signaled
dwStatus = WaitForSingleObject (pRTCPContext->hRtcpThread, 500);
if (dwStatus == WAIT_OBJECT_0)
;
else if ((dwStatus == WAIT_TIMEOUT) || (dwStatus == WAIT_FAILED))
{
if (dwStatus == WAIT_TIMEOUT)
{
RRCM_DBG_MSG ("RTCP: Wait timed-out", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
else
{
RRCM_DBG_MSG ("RTCP: Wait failed", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
// Force ungraceful thread termination
dwStatus = TerminateThread (pRTCPContext->hRtcpThread, 1);
if (dwStatus == FALSE)
{
RRCM_DBG_MSG ("RTCP: ERROR - TerminateThread ()",
GetLastError(), __FILE__, __LINE__,
DBG_ERROR);
}
}
// close the thread handle
dwStatus = CloseHandle (pRTCPContext->hRtcpThread);
if (dwStatus == TRUE)
pRTCPContext->hRtcpThread = 0;
else
{
RRCM_DBG_MSG ("RTCP: ERROR - CloseHandle()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
// close the event handle
dwStatus = CloseHandle (pRTCPContext->hTerminateRtcpEvent);
if (dwStatus == TRUE)
pRTCPContext->hTerminateRtcpEvent = 0;
else
{
RRCM_DBG_MSG ("RTCP: ERROR - CloseHandle()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
// close the request handle
dwStatus = CloseHandle (pRTCPContext->hRtcpRptRequestEvent);
if (dwStatus == TRUE)
pRTCPContext->hRtcpRptRequestEvent = 0;
else
{
RRCM_DBG_MSG ("RTCP: ERROR - CloseHandle()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
}
IN_OUT_STR ("RTCP: Exit terminateRtcpThread()\n");
}
/*----------------------------------------------------------------------------
* Function : RTCPflushIO
* Description: Flush the receive queue.
*
* Input : pSSRC: -> to the SSRC entry
*
* Return: None
---------------------------------------------------------------------------*/
DWORD RTCPflushIO (PSSRC_ENTRY pSSRC)
{
DWORD dwStatus = RRCM_NoError;
int IoToFlush;
int waitForXmtTrials;
IN_OUT_STR ("RTCP: Enter RTCPflushIO()\n");
// set the flush flag
EnterCriticalSection (&pSSRC->pRTCPses->critSect);
pSSRC->pRTCPses->dwSessionStatus |= SHUTDOWN_IN_PROGRESS;
LeaveCriticalSection (&pSSRC->pRTCPses->critSect);
// check if need to flush or close the socket
if (pSSRC->dwSSRCStatus & CLOSE_RTCP_SOCKET)
{
// get the number of outstanding buffers
IoToFlush = pSSRC->pRTCPses->dwNumRcvIoPending;
#ifdef _DEBUG
wsprintf(debug_string,
"RTCPflushIO: closing socket(%d) dwNumRcvIoPending (%d)",
pSSRC->RTCPsd, pSSRC->pRTCPses->dwNumRcvIoPending);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
// make sure it's not < 0
if (IoToFlush < 0)
IoToFlush = pRTPContext->registry.NumRTCPPostedBfr;
dwStatus = RRCMws.closesocket (pSSRC->RTCPsd);
if (dwStatus != 0)
{
RRCM_DBG_MSG ("RTCP: ERROR - closesocket ()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
}
else
{
IoToFlush = flushIO (pSSRC);
}
// wait for the receive side to flush it's pending I/Os
if ((pSSRC->pRTCPses->dwSessionStatus & RTCP_ON) && IoToFlush)
{
// wait until the receiver signalled that the shutdown is done
dwStatus = WaitForSingleObject (pSSRC->pRTCPses->hShutdownDone, 2000);
if (dwStatus == WAIT_OBJECT_0)
;
else if (dwStatus == WAIT_TIMEOUT)
{
RRCM_DBG_MSG ("RTCP: Flush Wait timed-out", 0,
__FILE__, __LINE__, DBG_ERROR);
}
else if (dwStatus == WAIT_FAILED)
{
RRCM_DBG_MSG ("RTCP: Flush Wait failed", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
}
// make sure there is no buffers in transit on the transmit side
waitForXmtTrials = 3;
while (waitForXmtTrials--)
{
if (pSSRC->dwNumXmtIoPending == 0)
break;
RRCM_DBG_MSG ("RTCP: Xmt I/O Pending - Waiting",
0, NULL, 0, DBG_TRACE);
// wait in an alertable wait-state
SleepEx (200, TRUE);
}
// close the shutdown handle
dwStatus = CloseHandle (pSSRC->pRTCPses->hShutdownDone);
if (dwStatus == TRUE)
pSSRC->pRTCPses->hShutdownDone = 0;
else
{
RRCM_DBG_MSG ("RTCP: ERROR - CloseHandle()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
IN_OUT_STR ("RTCP: Exit RTCPflushIO()\n");
return (dwStatus);
}
/*----------------------------------------------------------------------------
* Function : flushIO
* Description: Flush the receive queue.
*
* Input : pSSRC: -> to the SSRC entry
*
* Return: None
---------------------------------------------------------------------------*/
DWORD flushIO (PSSRC_ENTRY pSSRC)
{
SOCKET tSocket;
SOCKADDR_IN tAddr;
char msg[16];
WSABUF msgBuf;
DWORD BytesSent;
int tmpSize;
DWORD dwStatus = RRCM_NoError;
int outstanding;
int IoToFlush;
RTCP_COMMON_T *pRTCPhdr;
IN_OUT_STR ("RTCP: Enter flushIO()\n");
// target socket
tSocket = pSSRC->RTCPsd;
// RTCP common header
pRTCPhdr = (RTCP_COMMON_T *)msg;
// RTP protocol version
pRTCPhdr->type = RTP_TYPE;
pRTCPhdr->pt = FLUSH_RTP_PAYLOAD_TYPE;
msgBuf.len = sizeof(msg);
msgBuf.buf = msg;
// get the address of the socket we are cleaning up
tmpSize = sizeof(tAddr);
if (RRCMws.getsockname (tSocket, (PSOCKADDR)&tAddr, &tmpSize))
{
dwStatus = GetLastError();
RRCM_DBG_MSG ("RTCP: ERROR - getsockname()",
dwStatus, __FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTP : Exit flushIO()\n");
// (was: return dwStatus;)
// Since this function is supposed to return number of pending I/O requests
// to this socket, returning a non-zero error here is BOGUS!
// Just return zero because if there is an error (socket has been freed)
// then just say there's no i/o pending.
return 0;
}
if (tAddr.sin_addr.s_addr == 0)
{
// send to the local address
tAddr.sin_addr.S_un.S_un_b.s_b1 = 127;
tAddr.sin_addr.S_un.S_un_b.s_b2 = 0;
tAddr.sin_addr.S_un.S_un_b.s_b3 = 0;
tAddr.sin_addr.S_un.S_un_b.s_b4 = 1;
}
// get the number of outstanding buffers
outstanding = pSSRC->pRTCPses->dwNumRcvIoPending;
// make sure it's not < 0
if (outstanding < 0)
outstanding = pRTPContext->registry.NumRTCPPostedBfr;
// save number of pending I/Os
IoToFlush = outstanding;
#if _DEBUG
wsprintf(debug_string,
"RTCP: Flushing %d outstanding RCV buffers", outstanding);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
// send datagrams to the RTCP socket
while (outstanding--)
{
#if (defined(_DEBUG) || defined(PCS_COMPLIANCE))
if (RTPLogger)
{
//INTEROP
InteropOutput (RTPLogger,
(BYTE FAR*)msgBuf.buf,
(int)msgBuf.len,
RTPLOG_SENT_PDU | RTCP_PDU);
}
#endif
dwStatus = RRCMws.sendTo (tSocket,
&msgBuf,
1,
&BytesSent,
0,
(SOCKADDR *)&tAddr,
sizeof(tAddr),
NULL,
#if 1
NULL);
#else
RTCPflushCallback);
#endif
if (dwStatus == SOCKET_ERROR)
{
// If serious error, undo all our work
dwStatus = GetLastError();
if (dwStatus != WSA_IO_PENDING)
{
RRCM_DBG_MSG ("RTCP: ERROR - sendTo()", dwStatus,
__FILE__, __LINE__, DBG_ERROR);
}
}
}
IN_OUT_STR ("RTCP: Exit flushIO()\n");
return IoToFlush;
}
/*----------------------------------------------------------------------------
* Function : RTCPflushCallback
* Description: Flush callback routine
*
* Input : dwError: I/O completion status
* cbTransferred: Number of bytes received
* lpOverlapped: -> to overlapped structure
* dwFlags: Flags
*
*
* Return: None
---------------------------------------------------------------------------*/
void CALLBACK RTCPflushCallback (DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags)
{
IN_OUT_STR ("RTCP: Enter RTCPflushCallback\n");
// check Winsock callback error status
if (dwError)
{
RRCM_DBG_MSG ("RTCP: ERROR - Rcv Callback", dwError,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP: Exit RTCPflushCallback\n");
return;
}
IN_OUT_STR ("RTCP: Exit RTCPflushCallback\n");
}
// [EOF]