|
|
/*----------------------------------------------------------------------------
* 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]
|