Windows2003-3790/enduser/netmeeting/av/rrcm/rtcp/rtcpthrd.cpp
2020-09-30 16:53:55 +02:00

422 lines
12 KiB
C++

/*----------------------------------------------------------------------------
* File: RTCPIO.C
* Product: RTP/RTCP implementation
* Description: Provides the RTCP network I/O.
*
* 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"
/*---------------------------------------------------------------------------
/ External Variables
/--------------------------------------------------------------------------*/
extern PRTCP_CONTEXT pRTCPContext;
extern RRCM_WS RRCMws;
#ifdef _DEBUG
extern char debug_string[];
#endif
#if (defined(_DEBUG) || defined(PCS_COMPLIANCE))
//INTEROP
extern LPInteropLogger RTPLogger;
#endif
/*----------------------------------------------------------------------------
* Function : RTCPThread
* Description: RTCP thread
*
* Input : pRTCPctxt: -> to RTCP context
*
* Return: None.
---------------------------------------------------------------------------*/
void RTCPThread (PRTCP_CONTEXT pRTCPctxt)
{
PSSRC_ENTRY pSSRC;
PSSRC_ENTRY pRecvSSRC;
PRTCP_SESSION pRTCP;
long timerPeriod;
long minTimeInterval;
long prvTimeoutChkTime = 0;
DWORD initTime;
long deltaTime;
int dwStatus;
DWORD curTime;
DWORD dwNumBytesXfr;
HANDLE bfrHandle[2];
DWORD dwHandleCnt;
RRCM_DBG_MSG ("RTCP: RTCP thread running ...", 0, NULL, 0, DBG_NOTIFY);
// setup buffer Events
bfrHandle[0] = pRTCPctxt->hTerminateRtcpEvent;
bfrHandle[1] = pRTCPctxt->hRtcpRptRequestEvent;
dwHandleCnt = 2;
// loop as long as there are sessions in the RTCP session list
//
while (1)
{
//LOOK: Claim global critical section?
// walk through the RTCP session list from the tail and check which
// SSRC entry timed out if any
curTime = timeGetTime();
minTimeInterval = TIMEOUT_CHK_FREQ; // 30 seconds
for (pRTCP = (PRTCP_SESSION)pRTCPctxt->RTCPSession.prev;
pRTCP;
pRTCP = (PRTCP_SESSION)(pRTCP->RTCPList.next))
{
// if RTCP is disabled or shutdown is in progress, ignore
// this session and move on.
if (!(pRTCP->dwSessionStatus & RTCP_ON)
|| (pRTCP->dwSessionStatus & SHUTDOWN_IN_PROGRESS))
continue;
// lock out access to this RTCP session
EnterCriticalSection (&pRTCP->critSect);
// NOTE: this assumes only one SSRC in the transmit list but
// that assumption has been made elsewhere too
pSSRC = (PSSRC_ENTRY)pRTCP->XmtSSRCList.prev;
// if its a new session, post RECVs
if (pRTCP->dwSessionStatus & NEW_RTCP_SESSION)
{
// post RTCP receive buffers
dwStatus = RTCPrcvInit(pSSRC);
#ifdef _DEBUG
if (dwStatus == FALSE)
{
RRCM_DBG_MSG ("RTCP: Couldn't initialize RTCP receive", 0,
__FILE__, __LINE__, DBG_TRACE);
}
#endif
// get initial transmit time
timerPeriod = (long)RTCPxmitInterval (1, 0,
pSSRC->xmtInfo.dwRtcpStreamMinBW,
0, 100,
&pRTCP->avgRTCPpktSizeRcvd,
1);
pSSRC->dwNextReportSendTime = curTime + timerPeriod;
pRTCP->dwSessionStatus &= ~NEW_RTCP_SESSION;
}
// check if it has any expired SSRCs
if ((curTime - prvTimeoutChkTime) > TIMEOUT_CHK_FREQ)
{
while (pRecvSSRC = SSRCTimeoutCheck (pRTCP, curTime))
{
// notify application if interested
// NOTE: may be do this outside the loop?
RRCMnotification (RRCM_TIMEOUT_EVENT, pRecvSSRC,
pRecvSSRC->SSRC, 0);
// remove this entry from the list
deleteSSRCEntry (pRecvSSRC->SSRC, pRTCP);
}
prvTimeoutChkTime = curTime;
}
if ( ! (pRTCP->dwSessionStatus & RTCP_DEST_LEARNED))
{
// cant send yet because we dont know who to
// send to. Delay for 3 seconds
pSSRC->dwNextReportSendTime = curTime + 3000;
}
// if its time to send RTCP reports on this session
// then break out of the loop and send it (cannot
// send with the global critsect held)
//
timerPeriod = (pSSRC->dwNextReportSendTime - curTime);
if (timerPeriod <= RTCP_TIMEOUT_WITHIN_RANGE
&& FormatRTCPReport(pRTCP, pSSRC, curTime))
{
// increment Xmt count in anticipation. This will prevent
// the session from being deleted while the send is in progress.
InterlockedIncrement ((long *)&pSSRC->dwNumXmtIoPending);
InterlockedIncrement ((long *)&pSSRC->dwNumRptSent);
LeaveCriticalSection(&pRTCP->critSect);
break;
}
// if not then check how long before the next scheduled
// transmission and save the minimum. We will sleep
// for this much time and then start again.
if (minTimeInterval > timerPeriod)
minTimeInterval = timerPeriod;
LeaveCriticalSection(&pRTCP->critSect);
}
if (pRTCP)
{
#if (defined(_DEBUG) || defined(PCS_COMPLIANCE))
if (RTPLogger)
{
//INTEROP
InteropOutput (RTPLogger,
(BYTE FAR*)(pRTCP->XmtBfr.buf),
(int)pRTCP->XmtBfr.len,
RTPLOG_SENT_PDU | RTCP_PDU);
}
#endif
// send the RTCP packet
dwStatus = RRCMws.sendTo (pSSRC->RTCPsd,
&pRTCP->XmtBfr,
1,
&dwNumBytesXfr,
0,
(PSOCKADDR)pRTCP->toBfr,
pRTCP->toLen,
NULL,
NULL);
// check SendTo status
if (dwStatus == SOCKET_ERROR)
{
RRCM_DBG_MSG ("RTCP: ERROR - WSASendTo()", dwStatus,
__FILE__, __LINE__, DBG_ERROR);
//If dwStatus is WSAENOTSOCK (or worse, a fault)
//We're likely shutting down, and the RTCP session
//is going away, don't touch it and let the normal
//shutdown code take over
if (dwStatus != WSAENOTSOCK && dwStatus != WSAEFAULT) {
// notify application if interested
RRCMnotification (RRCM_RTCP_WS_XMT_ERROR, pSSRC,
pSSRC->SSRC, dwStatus);
InterlockedDecrement ((long *)&pSSRC->dwNumRptSent);
}
}
InterlockedDecrement ((long *)&pSSRC->dwNumXmtIoPending);
// run through the session list again
continue;
}
// grab an initial timestamp so we can reset WaitForSingleObjectEx
initTime = timeGetTime();
// now we've gone through all the RTCP sessions and
// verified that none have pending reports to be sent
// We also know the earliest scheduled timeout so
// lets sleep till then.
while (1)
{
dwStatus = WaitForMultipleObjectsEx (dwHandleCnt,
bfrHandle,
FALSE,
(DWORD)minTimeInterval,
TRUE);
if (dwStatus == WAIT_OBJECT_0)
{
// Exit event was signalled
#ifdef _DEBUG
wsprintf(debug_string,
"RTCP: Exit RTCP thread - Handle: x%p - ID: x%lX",
pRTCPctxt->hRtcpThread, pRTCPctxt->dwRtcpThreadID);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
ExitThread (0);
}
else if (dwStatus == WAIT_OBJECT_0+1)
{
// the application requested a non-periodic control
// of the RTCP report frequency
break;
}
else if (dwStatus == WAIT_IO_COMPLETION)
{
// decrement the timerPeriod so the WaitForSingleObjectEx
// can continue but if we're less than 250 milliseconds from
// the original timeout go ahead and call it close enough.
curTime = timeGetTime();
deltaTime = curTime - initTime;
if (deltaTime < 0)
break;
else
{
if (minTimeInterval >
(deltaTime + (RTCP_TIMEOUT_WITHIN_RANGE * 2)))
{
minTimeInterval -= deltaTime;
}
else
break;
}
}
else if (dwStatus == WAIT_TIMEOUT)
{
// the expected completion status
break;
}
else if (dwStatus == WAIT_FAILED)
{
RRCM_DBG_MSG ("RTCP: Wait() Error", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
break;
}
}
}
}
/*----------------------------------------------------------------------------
* Function : RTCPThreadCtrl
* Description: RTCP thread ON / OFF
*
* Input : dwState: ON / OFF
*
* Return: 0 (success) / 0xFFFFFFFF (failure)
---------------------------------------------------------------------------*/
DWORD WINAPI RTCPThreadCtrl (DWORD dwState)
{
IN_OUT_STR ("RTCP : Enter RTCPThreadCtrl()\n");
DWORD dwStatus = RRCM_NoError;
DWORD dwSuspendCnt;
DWORD idx;
if (pRTCPContext->hRtcpThread == 0)
{
IN_OUT_STR ("RTCP : Exit RTCPThreadCtrl()\n");
return dwStatus;
}
if (dwState == RTCP_ON)
{
idx = MAXIMUM_SUSPEND_COUNT;
while (idx--)
{
dwSuspendCnt = ResumeThread (pRTCPContext->hRtcpThread);
if (dwSuspendCnt <= 1)
{
break;
}
else if (dwSuspendCnt == 0xFFFFFFFF)
{
dwStatus = RRCM_NoError;
break;
}
}
}
else if (dwState == RTCP_OFF)
{
if (SuspendThread (pRTCPContext->hRtcpThread) == 0xFFFFFFFF)
{
RRCM_DBG_MSG ("RTCP: SuspendThread() Error", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
}
IN_OUT_STR ("RTCP : Exit RTCPThreadCtrl()\n");
return dwStatus;
}
/*----------------------------------------------------------------------------
* Function : RTCPSendSessionCtrl
* Description: Gives RTCP control to the application if the application
* desire to do so. The application is now responsible to comply
* with the RTP specification.
*
* Input : hRtpSession: Handle of the RTP session
* dwTimeout: RTCP send message timeout
* 0x0 -> RRCM control
* 0x7FFFFFFF -> RTCP xmt disabled
* value -> selected timeout
* (periodic or not)
*
* Return: 0 (success) / 0xFFFFFFFF (failure)
---------------------------------------------------------------------------*/
HRESULT WINAPI RTCPSendSessionCtrl (DWORD_PTR RTPSession,
DWORD dwTimeOut)
{
IN_OUT_STR ("RTCP : Enter RTCPSendSessionCtrl()\n");
PRTP_SESSION pSession;
PSSRC_ENTRY pSSRC;
DWORD dwStatus = RRCM_NoError;
// Cast Session ID to obtain the session pointer.
pSession = (PRTP_SESSION)RTPSession;
if (pSession == NULL)
{
RRCM_DBG_MSG ("RTCP : ERROR - Invalid RTP session", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTP : Exit RTCPSendSessionCtrl()\n");
return (MAKE_RRCM_ERROR (RRCMError_RTPSessResources));
}
// Get this RTP session's transmit SSRC
pSSRC = (PSSRC_ENTRY)pSession->pRTCPSession->XmtSSRCList.prev;
if (pSSRC == NULL)
{
RRCM_DBG_MSG ("RTP : ERROR - No SSRC entry on the Xmt list", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP : Exit RTCPSendSessionCtrl()\n");
return (MAKE_RRCM_ERROR (RRCMError_RTCPInvalidSSRCentry));
}
// set the new RTCP control timeout value
if (dwTimeOut == RRCM_CTRL_RTCP)
pSSRC->dwSSRCStatus &= ~RTCP_XMT_USER_CTRL;
else if (dwTimeOut & RTCP_ONE_SEND_ONLY)
{
pSSRC->dwNextReportSendTime = RTCP_TIMEOUT_WITHIN_RANGE;
// report are then turned off
pSSRC->dwUserXmtTimeoutCtrl = RTCP_XMT_OFF;
// signal the thread to terminate
SetEvent (pRTCPContext->hRtcpRptRequestEvent);
}
else
{
if (dwTimeOut < RTCP_XMT_MINTIME)
dwTimeOut = RTCP_XMT_MINTIME;
pSSRC->dwUserXmtTimeoutCtrl = dwTimeOut;
pSSRC->dwSSRCStatus |= RTCP_XMT_USER_CTRL;
}
IN_OUT_STR ("RTCP : Exit RTCPSendSessionCtrl()\n");
return dwStatus;
}
// [EOF]