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

1209 lines
32 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: RTCPRECV.C
* Product: RTP/RTCP implementation
* Description: Provides the RTCP receive 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"
#define MIN(a, b) ((a < b) ? a : b)
/*---------------------------------------------------------------------------
/ 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 : RTCPrcvInit
* Description: RTCP receive initialisation.
*
* Input : pRTCP : Pointer to the RTCP session information
*
* Return: TRUE/FALSE
---------------------------------------------------------------------------*/
DWORD RTCPrcvInit (PSSRC_ENTRY pSSRC)
{
PRTCP_BFR_LIST pRcvStruct;
PRTCP_SESSION pRTCP;
int dwStatus;
int dwError;
int errorCnt = 0;
int bfrErrorCnt = 0;
DWORD idx;
int wsockSuccess = FALSE;
// save a pointer to the corresponding RTCP session
pRTCP = pSSRC->pRTCPses;
// Post receive buffers for WS-2. As these buffers are posted per receive
// thread, few of them should be plenty enough for RTCP.
for (idx = 0; idx < pRTPContext->registry.NumRTCPPostedBfr; idx++)
{
// get a free RTCP buffer for a receive operation
pRcvStruct =
(PRTCP_BFR_LIST)removePcktFromTail(&pRTCP->RTCPrcvBfrList,
&pRTCP->critSect);
// check buffer
if (pRcvStruct == NULL)
{
RRCM_DBG_MSG ("RTCP: ERROR - Rcv Bfr Allocation Error", 0,
__FILE__, __LINE__, DBG_ERROR);
// make sure we have at least one buffer
ASSERT (pRcvStruct);
break;
}
// SSRC entry address of our own session
pRcvStruct->pSSRC = pSSRC;
// received address length reset by the receive routine
pRcvStruct->addrLen = sizeof(SOCKADDR);
// use hEvent to recover the buffer's address
pRcvStruct->overlapped.hEvent = (WSAEVENT)pRcvStruct;
// post the receive buffer for this thread
dwStatus = RRCMws.recvFrom (pSSRC->RTCPsd,
&pRcvStruct->bfr,
pRcvStruct->dwBufferCount,
&pRcvStruct->dwNumBytesXfr,
&pRcvStruct->dwFlags,
(PSOCKADDR)pRcvStruct->addr,
&pRcvStruct->addrLen,
(LPWSAOVERLAPPED)&pRcvStruct->overlapped,
RTCPrcvCallback);
// Check Winsock status
if (dwStatus != 0)
{
// error, the receive request won't proceed
dwError = GetLastError();
if ((dwError != WSA_IO_PENDING) && (dwError != WSAEMSGSIZE))
{
RRCM_DBG_MSG ("RTCP: ERROR WSARecvFrom()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
// notify application if interested
RRCMnotification (RRCM_RTCP_WS_RCV_ERROR, pSSRC,
pSSRC->SSRC, dwError);
// Return the buffer to the free queue
addToHeadOfList (&pRTCP->RTCPrcvBfrList,
(PLINK_LIST)pRcvStruct,
&pRTCP->critSect);
}
else
{
wsockSuccess = TRUE;
// increment number of I/O pending
InterlockedIncrement ((long *)&pRTCP->dwNumRcvIoPending);
}
}
else
{
wsockSuccess = TRUE;
// increment number of I/O pending
InterlockedIncrement ((long *)&pRTCP->dwNumRcvIoPending);
}
}
// make sure we posted at least some buffers
if (wsockSuccess == FALSE)
{
// release all resources and kill the receive thread
#ifdef _DEBUG
wsprintf(debug_string,
"RTCP: ERROR - Exit RCV init %s: Line:%d", __FILE__, __LINE__);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
return(FALSE);
}
return (TRUE);
}
/*----------------------------------------------------------------------------
* Function : RTCPrcvCallback
* Description: Receive callback routine from Winsock2.
*
* Input : dwError: I/O completion status
* cbTransferred: Number of bytes received
* lpOverlapped: -> to overlapped structure
* dwFlags: Flags
*
*
* Return: None
---------------------------------------------------------------------------*/
void CALLBACK RTCPrcvCallback (DWORD dwError,
DWORD cbTransferred,
LPWSAOVERLAPPED lpOverlapped,
DWORD dwFlags)
{
PRTCP_BFR_LIST pRcvStruct;
RTCP_T *pRTCPpckt;
PRTCP_SESSION pRTCPses;
PSSRC_ENTRY pSSRC;
PAPP_RTCP_BFR pAppBfr;
DWORD dwStatus = 0;
DWORD i;
DWORD pcktLen;
DWORD dwSSRC;
USHORT wHost;
SOCKET RTCPsd;
unsigned char *pEndPckt;
unsigned char *pEndBlock;
int tmpSize;
#if IO_CHECK
DWORD initTime = timeGetTime();
#endif
IN_OUT_STR ("RTCP: Enter RTCPrcvCallback\n");
// hEvent in the WSAOVERLAPPED struct points to our buffer's information
pRcvStruct = (PRTCP_BFR_LIST)lpOverlapped->hEvent;
// SSRC entry pointer
pSSRC = pRcvStruct->pSSRC;
// check Winsock callback error status
if (dwError)
{
// 65534 is probably a temporary bug in WS2
if ((dwError == 65534) || (dwError == WSA_OPERATION_ABORTED))
{
RRCM_DBG_MSG ("RTCP: I/O Aborted", dwError,
__FILE__, __LINE__, DBG_NOTIFY);
}
else
{
RRCM_DBG_MSG ("RTCP: ERROR - Rcv Callback", dwError,
__FILE__, __LINE__, DBG_ERROR);
}
// invalid RTCP packet header, re-queue the buffer
RTCPpostRecvBfr (pSSRC, pRcvStruct);
IN_OUT_STR ("RTCP: Exit RTCPrcvCallback\n");
return;
}
// read the RTCP packet
pRTCPpckt = (RTCP_T *)pRcvStruct->bfr.buf;
#if (defined(_DEBUG) || defined(PCS_COMPLIANCE))
//INTEROP
if (RTPLogger)
{
InteropOutput (RTPLogger,
(BYTE FAR*)(pRcvStruct->bfr.buf),
cbTransferred,
RTPLOG_RECEIVED_PDU | RTCP_PDU);
}
#endif
// get the RTCP session ptr
pRTCPses = pSSRC->pRTCPses;
// Check RTCP header validity of first packet in report.
// Filter out junk. First thing in RTCP packet must be
// either SR, RR or BYE
if ((pRTCPpckt->common.type != RTP_TYPE) ||
((pRTCPpckt->common.pt != RTCP_SR) &&
(pRTCPpckt->common.pt != RTCP_RR) &&
(pRTCPpckt->common.pt != RTCP_BYE)))
{
#ifdef MONITOR_STATS
pRTCPses->dwNumRTCPhdrErr++;
#endif
// invalid RTCP packet header, re-queue the buffer
RTCPpostRecvBfr (pSSRC, pRcvStruct);
#if 0 // we could have shutdown so this code can fault
if (pRTCPpckt->common.pt == FLUSH_RTP_PAYLOAD_TYPE)
{
RRCM_DBG_MSG ("RTCP: Flushing RCV I/O", 0, NULL, 0, DBG_NOTIFY);
}
else
{
wsprintf(debug_string,
"RTCP: ERROR - Pckt Header Error. Type:%d / Payload:%d",
pRTCPpckt->common.type, pRTCPpckt->common.pt);
RRCM_DBG_MSG (debug_string, 0, __FILE__, __LINE__, DBG_TRACE);
}
#endif
IN_OUT_STR ("RTCP: Exit RTCPrcvCallback\n");
return;
}
// get the socket descriptor
RTCPsd = pSSRC->RTCPsd;
// get the sender's SSRC
RRCMws.ntohl (RTCPsd, pRTCPpckt->r.sr.ssrc, &dwSSRC);
// skip our own loopback if we receive it
if (ownLoopback (RTCPsd, dwSSRC, pRTCPses))
{
RTCPpostRecvBfr (pSSRC, pRcvStruct);
return;
}
// at this point we think the RTCP packet's valid. Get the sender's
// address, if not already known
if (!(pRTCPses->dwSessionStatus & RTCP_DEST_LEARNED))
{
pRTCPses->dwSessionStatus |= RTCP_DEST_LEARNED;
pRTCPses->toLen = pRcvStruct->addrLen;
memcpy (&pRTCPses->toBfr, &pRcvStruct->addr, pRcvStruct->addrLen);
#ifdef ENABLE_ISDM2
// register our Xmt SSRC - Rcvd one will be found later
if (Isdm2.hISDMdll)
registerSessionToISDM (pSSRC, pRTCPses, &Isdm2);
#endif
}
// Update our RTCP average packet size estimator
EnterCriticalSection (&pRTCPses->critSect);
tmpSize = (cbTransferred + NTWRK_HDR_SIZE) - pRTCPses->avgRTCPpktSizeRcvd;
#ifdef ENABLE_FLOATING_POINT
// As per RFC
tmpSize = (int)(tmpSize * RTCP_SIZE_GAIN);
#else
// Need to remove floating point operation
tmpSize = tmpSize / 16;
#endif
pRTCPses->avgRTCPpktSizeRcvd += tmpSize;
LeaveCriticalSection (&pRTCPses->critSect);
// check if the raw RTCP packet needs to be copied into an application
// buffer - Mainly used by ActiveMovieRTP to propagate the reports up
// the filter graph to the Receive Payload Handler
pAppBfr = (PAPP_RTCP_BFR)removePcktFromHead (&(pRTCPses->appRtcpBfrList),
&pRTCPses->critSect);
if (pAppBfr && !(pAppBfr->dwBfrStatus & RTCP_SR_ONLY))
{
// copy the full RTCP packet
memcpy (pAppBfr->bfr,
pRTCPpckt,
MIN(pAppBfr->dwBfrLen, cbTransferred));
// number of bytes received
pAppBfr->dwBytesRcvd = MIN(pAppBfr->dwBfrLen, cbTransferred);
// set the event associated with this buffer
SetEvent (pAppBfr->hBfrEvent);
}
// end of the received packet
pEndPckt = (unsigned char *)pRTCPpckt + cbTransferred;
while ((unsigned char *)pRTCPpckt < pEndPckt)
{
// get the length
dwStatus = RRCMws.ntohs (RTCPsd, pRTCPpckt->common.length, &wHost);
if (dwStatus)
{
RRCM_DBG_MSG ("RTCP: ERROR - WSANtohs()", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
// get this report block length
pcktLen = (wHost + 1) << 2;
pEndBlock = (unsigned char *)pRTCPpckt + pcktLen;
// sanity check
if (pEndBlock > pEndPckt)
{
RRCM_DBG_MSG ("RTCP: ERROR - Rcv packet length error", 0,
__FILE__, __LINE__, DBG_ERROR);
#ifdef MONITOR_STATS
pRTCPses->dwNumRTCPlenErr++;
#endif
break;
}
// make sure the version is correct for all packets
if (pRTCPpckt->common.type != RTP_TYPE)
{
#ifdef MONITOR_STATS
pRTCPses->dwNumRTCPhdrErr++;
#endif
// invalid RTCP packet header, packet will be re-queued
break;
}
switch (pRTCPpckt->common.pt)
{
case RTCP_SR:
// check if only the SR needs to be propagated up to the app
if (pAppBfr && (pAppBfr->dwBfrStatus & RTCP_SR_ONLY))
{
// copy the RTCP SR
memcpy (pAppBfr->bfr,
pRTCPpckt,
MIN(pAppBfr->dwBfrLen, 24));
// number of bytes received
pAppBfr->dwBytesRcvd = MIN(pAppBfr->dwBfrLen, 24);
// set the event associated with this buffer
SetEvent (pAppBfr->hBfrEvent);
}
// get the sender's SSRC
RRCMws.ntohl (RTCPsd, pRTCPpckt->r.sr.ssrc, &dwSSRC);
// parse the sender report
parseRTCPsr (RTCPsd, pRTCPpckt, pRTCPses, pRcvStruct);
// parse additional receiver reports if any
for (i = 0; i < pRTCPpckt->common.count; i++)
{
parseRTCPrr (RTCPsd, &pRTCPpckt->r.sr.rr[i],
pRTCPses, pRcvStruct,
dwSSRC);
}
// notify application if interested
RRCMnotification (RRCM_RECV_RTCP_SNDR_REPORT_EVENT, pSSRC,
dwSSRC, 0);
break;
case RTCP_RR:
// get the sender's SSRC
RRCMws.ntohl (RTCPsd, pRTCPpckt->r.rr.ssrc, &dwSSRC);
// parse receiver reports
for (i = 0; i < pRTCPpckt->common.count; i++)
{
parseRTCPrr (RTCPsd, &pRTCPpckt->r.rr.rr[i],
pRTCPses, pRcvStruct,
dwSSRC);
}
// notify application if interested
RRCMnotification (RRCM_RECV_RTCP_RECV_REPORT_EVENT, pSSRC,
dwSSRC, 0);
break;
case RTCP_SDES:
{
PCHAR buf;
buf = (PCHAR)&pRTCPpckt->r.sdes;
for (i = 0; i < pRTCPpckt->common.count; i++)
{
buf = parseRTCPsdes (RTCPsd, buf, pRTCPses, pRcvStruct);
if (buf == NULL)
break;
}
}
break;
case RTCP_BYE:
for (i = 0; i < pRTCPpckt->common.count; i++)
parseRTCPbye (RTCPsd, pRTCPpckt->r.bye.src[i],
pRTCPses, pRcvStruct);
break;
default:
break;
}
// go to next report block
pRTCPpckt = (RTCP_T *)(pEndBlock);
}
// post back the buffer to WS-2
RTCPpostRecvBfr (pSSRC, pRcvStruct);
#if IO_CHECK
wsprintf(debug_string,
"RTCP: Leaving Rcv Callback after: %ld msec\n",
timeGetTime() - initTime);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
IN_OUT_STR ("RTCP: Exit RTCPrcvCallback\n");
}
/*----------------------------------------------------------------------------
* Function : parseRTCPsr
* Description: Parse an RTCP sender reports and update the corresponding
* statistics.
*
* Input : sd: RTCP Socket descriptor
* pRTCPpckt: -> to the RTCP packet
* pRTCPses: -> to the RTCP session information
* pRcvStruct: -> to the receive structure information
*
* Return: OK: RRCM_NoError
* !0: Error code (see RRCM.H)
---------------------------------------------------------------------------*/
DWORD parseRTCPsr (SOCKET sd,
RTCP_T *pRTCPpckt,
PRTCP_SESSION pRTCPses,
PRTCP_BFR_LIST pRcvStruct)
{
PSSRC_ENTRY pSSRC;
DWORD dwSSRC;
IN_OUT_STR ("RTCP: Enter parseRTCPsr\n");
// get the sender's SSRC
RRCMws.ntohl (sd, pRTCPpckt->r.sr.ssrc, &dwSSRC);
#ifdef _DEBUG
wsprintf(debug_string, "RTCP: Receive SR from SSRC:x%lX", dwSSRC);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
// look for the SSRC entry in the list for this RTCP session
pSSRC = searchforSSRCatTail((PSSRC_ENTRY)pRTCPses->RcvSSRCList.prev,
dwSSRC);
if (pSSRC == NULL)
{
// new SSRC, create an entry in this RTCP session
pSSRC = createSSRCEntry(dwSSRC,
pRTCPses,
(PSOCKADDR)pRcvStruct->addr,
(DWORD)pRcvStruct->addrLen,
FALSE);
if (pSSRC == NULL)
{
// cannot create a new entry, out of resources
RRCM_DBG_MSG ("RTCP: ERROR - Resource allocation", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP: Exit parseRTCPsr\n");
return (RRCMError_RTCPResources);
}
// notify application if it desired so
RRCMnotification (RRCM_NEW_SOURCE_EVENT, pSSRC, dwSSRC,
UNKNOWN_PAYLOAD_TYPE);
}
// get the RTP timestamp
RRCMws.ntohl (sd, pRTCPpckt->r.sr.rtp_ts, &pSSRC->xmtInfo.dwRTPts);
// number of packets send
RRCMws.ntohl (sd, pRTCPpckt->r.sr.psent, &pSSRC->xmtInfo.dwNumPcktSent);
// number of bytes sent
RRCMws.ntohl (sd, pRTCPpckt->r.sr.osent, &pSSRC->xmtInfo.dwNumBytesSent);
// get the NTP most significant word
RRCMws.ntohl (sd, pRTCPpckt->r.sr.ntp_sec, &pSSRC->xmtInfo.dwNTPmsw);
// get the NTP least significant word
RRCMws.ntohl (sd, pRTCPpckt->r.sr.ntp_frac, &pSSRC->xmtInfo.dwNTPlsw);
// last SR timestamp (middle 32 bits of the NTP timestamp)
pSSRC->xmtInfo.dwLastSR = ((pSSRC->xmtInfo.dwNTPmsw & 0x0000FFFF) << 16);
pSSRC->xmtInfo.dwLastSR |= ((pSSRC->xmtInfo.dwNTPlsw & 0xFFFF0000) >> 16);
// last time this SSRC's heard
pSSRC->dwLastReportRcvdTime = pSSRC->xmtInfo.dwLastSRLocalTime =
timeGetTime();
// get the source address information
if (!(pSSRC->dwSSRCStatus & NETWK_ADDR_UPDATED))
{
saveNetworkAddress(pSSRC,
(PSOCKADDR)pRcvStruct->addr,
pRcvStruct->addrLen);
}
// increment the number of report received from this SSRC
InterlockedIncrement ((long *)&pSSRC->dwNumRptRcvd);
#ifdef ENABLE_ISDM2
// update ISDM
if (Isdm2.hISDMdll && pRTCPses->hSessKey)
{
if (pSSRC->hISDM)
updateISDMstat (pSSRC, &Isdm2, RECVR, FALSE);
else
registerSessionToISDM (pSSRC, pRTCPses, &Isdm2);
}
#endif
IN_OUT_STR ("RTCP: Exit parseRTCPsr\n");
return (RRCM_NoError);
}
/*----------------------------------------------------------------------------
* Function : parseRTCPrr
* Description: Parse an RTCP receiver reports and update the corresponding
* statistics.
*
* Input : sd: RTCP socket descriptor
* pRR: -> to receiver report buffer
* pRTCPses: -> to the RTCP session information
* pRcvStruct: -> to the receive structure information
* senderSSRC: Sender's SSRC
*
* Return: OK: RRCM_NoError
* !0: Error code (see RRCM.H)
---------------------------------------------------------------------------*/
DWORD parseRTCPrr (SOCKET sd,
RTCP_RR_T *pRR,
PRTCP_SESSION pRTCPses,
PRTCP_BFR_LIST pRcvStruct,
DWORD senderSSRC)
{
PSSRC_ENTRY pSSRC;
DWORD dwSSRC;
DWORD dwGetFeedback = FALSE;
IN_OUT_STR ("RTCP: Enter parseRTCPrr\n");
#ifdef _DEBUG
wsprintf(debug_string,
"RTCP: Receive RR from sender SSRC:x%lX", senderSSRC);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
// get the receiver report SSRC
RRCMws.ntohl (sd, pRR->ssrc, &dwSSRC);
#ifdef _DEBUG
wsprintf(debug_string, "RTCP: RR for SSRC:x%lX", dwSSRC);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
//
// NOTE:
// For now we just keep track of feedback information about ourselve. Later
// the link list can be used to keep track about everybody feedback
// information.
//
// Check to see if we're interested in this report, ie, does this SSRC report
// information about one of our active sender.
dwGetFeedback =
searchforSSRCatTail((PSSRC_ENTRY)pRTCPses->XmtSSRCList.prev,
dwSSRC) != NULL;
// look for the sender SSRC entry in the list for this RTCP session
pSSRC =
searchforSSRCatTail((PSSRC_ENTRY)pRTCPses->RcvSSRCList.prev,
senderSSRC);
if (pSSRC == NULL)
{
// new SSRC, create an entry in this RTCP session
pSSRC = createSSRCEntry(senderSSRC,
pRTCPses,
(PSOCKADDR)pRcvStruct->addr,
(DWORD)pRcvStruct->addrLen,
FALSE);
if (pSSRC == NULL)
{
// cannot create a new entry, out of resources
RRCM_DBG_MSG ("RTCP: ERROR - Resource allocation", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP: Exit parseRTCPrr\n");
return (RRCMError_RTCPResources);
}
// notify application if it desired so
RRCMnotification (RRCM_NEW_SOURCE_EVENT, pSSRC, senderSSRC,
UNKNOWN_PAYLOAD_TYPE);
}
// update RR feedback information
if (dwGetFeedback)
updateRRfeedback (sd, senderSSRC, dwSSRC, pRR, pSSRC);
// last time this SSRC's heard
pSSRC->dwLastReportRcvdTime = timeGetTime();
// get the source address information
if (!(pSSRC->dwSSRCStatus & NETWK_ADDR_UPDATED))
{
saveNetworkAddress(pSSRC,
(PSOCKADDR)pRcvStruct->addr,
pRcvStruct->addrLen);
}
// increment the number of report received from this SSRC
InterlockedIncrement ((long *)&pSSRC->dwNumRptRcvd);
#ifdef ENABLE_ISDM2
// update ISDM
if (Isdm2.hISDMdll && pRTCPses->hSessKey)
{
if (pSSRC->hISDM)
updateISDMstat (pSSRC, &Isdm2, RECVR, TRUE);
else
registerSessionToISDM (pSSRC, pRTCPses, &Isdm2);
}
#endif
IN_OUT_STR ("RTCP: Exit parseRTCPrr\n");
return (RRCM_NoError);
}
/*----------------------------------------------------------------------------
* Function : parseRTCPsdes
* Description: Parse an RTCP SDES packet
*
* Input : sd: RTCP socket descriptor
* bfr: -> to SDES buffer
* pRTCPses: -> to the RTCP session information
* pRcvStruct: -> to the receive structure information
*
* Return: OK: RRCM_NoError
* !0: Error code (see RRCM.H)
---------------------------------------------------------------------------*/
PCHAR parseRTCPsdes (SOCKET sd,
PCHAR bfr,
PRTCP_SESSION pRTCPses,
PRTCP_BFR_LIST pRcvStruct)
{
DWORD dwHost;
DWORD ssrc = *(DWORD *)bfr;
RTCP_SDES_ITEM_T *pSdes;
PSSRC_ENTRY pSSRC;
IN_OUT_STR ("RTCP: Enter parseRTCPsdes\n");
// get the SSRC
RRCMws.ntohl (sd, ssrc, &dwHost);
#ifdef _DEBUG
wsprintf(debug_string, "RTCP: Receive SDES from SSRC: x%lX", dwHost);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
// look for the SSRC entry in the list for this RTCP session
pSSRC = searchforSSRCatTail ((PSSRC_ENTRY)pRTCPses->RcvSSRCList.prev,
dwHost);
if (pSSRC == NULL)
{
#ifdef _DEBUG
wsprintf(debug_string,
"RTCP: SDES and SSRC (x%lX) not found for session (Addr x%lX)",
dwHost, pRTCPses);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
// new SSRC, create an entry in this RTCP session
pSSRC = createSSRCEntry(dwHost,
pRTCPses,
(PSOCKADDR)pRcvStruct->addr,
(DWORD)pRcvStruct->addrLen,
FALSE);
if (pSSRC == NULL)
{
// cannot create a new entry, out of resources
RRCM_DBG_MSG ("RTCP: ERROR - Resource allocation", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP: Exit parseRTCPsdes\n");
return (NULL);
}
// notify application if it desired so
RRCMnotification (RRCM_NEW_SOURCE_EVENT, pSSRC, dwHost,
UNKNOWN_PAYLOAD_TYPE);
}
// read the SDES chunk
pSdes = (RTCP_SDES_ITEM_T *)(bfr + sizeof(DWORD));
// go through until a 'type = 0' is found
for (; pSdes->dwSdesType;
pSdes = (RTCP_SDES_ITEM_T *)((char *)pSdes + pSdes->dwSdesLength + 2))
{
switch (pSdes->dwSdesType)
{
case RTCP_SDES_CNAME:
if (pSSRC->cnameInfo.dwSdesLength == 0)
{
pSSRC->cnameInfo.dwSdesLength = pSdes->dwSdesLength;
// get the Cname
memcpy (pSSRC->cnameInfo.sdesBfr, pSdes->sdesData,
min (pSdes->dwSdesLength, MAX_SDES_LEN-1));
}
else
{
// check to see for a loop/collision of the SSRC
if (memcmp (pSdes->sdesData, pSSRC->cnameInfo.sdesBfr,
min (pSdes->dwSdesLength, MAX_SDES_LEN-1)) != 0)
{
// loop/collision of a third-party detected
pSSRC->dwSSRCStatus |= THIRD_PARTY_COLLISION;
// notify application if interested
RRCMnotification (RRCM_REMOTE_COLLISION_EVENT, pSSRC,
pSSRC->SSRC, 0);
// RTP & RTCP packet from this SSRC will be rejected
// until the senders resolve the collision
IN_OUT_STR ("RTCP: Exit parseRTCPsdes\n");
return NULL;
}
}
break;
case RTCP_SDES_NAME:
// the name can change, not like the Cname, so update it
// every time.
pSSRC->nameInfo.dwSdesLength = pSdes->dwSdesLength;
// get the name
memcpy (pSSRC->nameInfo.sdesBfr, pSdes->sdesData,
min (pSdes->dwSdesLength, MAX_SDES_LEN-1));
break;
default:
break;
}
}
// last time this SSRC's heard
pSSRC->dwLastReportRcvdTime = timeGetTime();
// get the source address information
if (!(pSSRC->dwSSRCStatus & NETWK_ADDR_UPDATED))
{
saveNetworkAddress(pSSRC,
(PSOCKADDR)pRcvStruct->addr,
pRcvStruct->addrLen);
}
// adjust pointer
bfr = (char *)pSdes;
IN_OUT_STR ("RTCP: Exit parseRTCPsdes\n");
// go the next 32 bits boundary
return bfr + ((4 - ((LONG_PTR)bfr & 0x3)) & 0x3);
}
/*----------------------------------------------------------------------------
* Function : parseRTCPbye
* Description: Parse an RTCP BYE packet
*
* Input : sd: RTCP socket descriptor
* ssrc: SSRC
* pRTCPses: -> to the RTCP session information
* pRcvStruct: -> to the receive structure
*
* Return: OK: RRCM_NoError
* !0: Error code (see RRCM.H)
---------------------------------------------------------------------------*/
DWORD parseRTCPbye (SOCKET sd,
DWORD ssrc,
PRTCP_SESSION pRTCPses,
PRTCP_BFR_LIST pRcvStruct)
{
DWORD dwStatus;
DWORD dwHost;
PSSRC_ENTRY pSSRC;
IN_OUT_STR ("RTCP: Enter parseRTCPbye\n");
RRCMws.ntohl (sd, ssrc, &dwHost);
#ifdef _DEBUG
wsprintf(debug_string, "RTCP: BYE from SSRC: x%lX", dwHost);
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
#endif
// find the SSRC entry
pSSRC = searchforSSRCatTail((PSSRC_ENTRY)pRTCPses->RcvSSRCList.prev,
dwHost);
if (pSSRC == NULL)
{
#ifdef _DEBUG
wsprintf(debug_string,
"RTCP: SSRC: x%lX not found in session: x%lX",
dwHost, pRTCPses);
RRCM_DBG_MSG (debug_string, 0, __FILE__, __LINE__, DBG_TRACE);
IN_OUT_STR ("RTCP: Exit parseRTCPbye\n");
#endif
return (RRCM_NoError);
}
// make sure the BYE is coming from the expected source and not intruder
if ((pRcvStruct->addrLen != pSSRC->fromLen) ||
#if 0
// There is a bug NT's Winsock2 implememtation. The unused bytes of
// SOCKADDR are not reset to 0 as they should be. Work fine on W95
// Temporarily just check the first 8 bytes, i.e. address family, port
// and IP address.
(memcmp (&pRcvStruct->addr, &pSSRC->from, pSSRC->fromLen)))
#else
(memcmp (&pRcvStruct->addr, &pSSRC->from, 8)))
#endif
return (RRCM_NoError);
// notify application if interested
RRCMnotification (RRCM_BYE_EVENT, pSSRC, dwHost, 0);
// delete this SSRC from the list
dwStatus = deleteSSRCEntry (dwHost, pRTCPses);
#ifdef _DEBUG
if (dwStatus == FALSE)
{
wsprintf(debug_string,
"RTCP: SSRC: x%lX not found in session: x%lX",
dwHost, pRTCPses);
RRCM_DBG_MSG (debug_string, 0, __FILE__, __LINE__, DBG_TRACE);
}
#endif
IN_OUT_STR ("RTCP: Exit parseRTCPbye\n");
return (RRCM_NoError);
}
/*----------------------------------------------------------------------------
* Function : ownLoopback
* Description: Determine if we receive our own loopback. We don't want to
* create an entry for ourselve, as we're already in the list.
*
* Input : sd: RTCP socket descriptor
* ssrc: SSRC
* pRTCPses: -> to the RTCP session's information
*
* Return: TRUE: Our loopback
* FALSE: No loopback
---------------------------------------------------------------------------*/
DWORD ownLoopback (SOCKET sd,
DWORD ssrc,
PRTCP_SESSION pRTCPses)
{
PSSRC_ENTRY pSSRC;
IN_OUT_STR ("RTCP: Enter ownLoopback\n");
// don't create an entry if received our own xmit back
pSSRC = searchforSSRCatTail((PSSRC_ENTRY)pRTCPses->XmtSSRCList.prev,
ssrc);
IN_OUT_STR ("RTCP: Exit ownLoopback\n");
if (pSSRC)
return TRUE;
else
return FALSE;
}
/*----------------------------------------------------------------------------
* Function : updateRRfeedback
* Description: Update the Receiver Report feedback for an active source
*
* Input : sd: RTCP socket descriptor
* dwSndSSRC: Sender's SSRC
* pRR: -> to receiver report entry
* pSSRC: -> to the SSRC entry
*
* Return: TRUE
---------------------------------------------------------------------------*/
DWORD updateRRfeedback (SOCKET sd,
DWORD dwSndSSRC,
DWORD dwSSRCfedback,
RTCP_RR_T *pRR,
PSSRC_ENTRY pSSRC)
{
DWORD dwHost;
IN_OUT_STR ("RTCP: Enter updateRRfeedback\n");
// Note when we last heard from the receiver
pSSRC->rrFeedback.dwLastRcvRpt = timeGetTime();
// SSRC who's feedback is for (ourselve for now)
pSSRC->rrFeedback.SSRC = dwSSRCfedback;
// get delay since last SR
RRCMws.ntohl (sd, pRR->dlsr, &pSSRC->rrFeedback.dwDelaySinceLastSR);
// get last SR
RRCMws.ntohl (sd, pRR->lsr, &pSSRC->rrFeedback.dwLastSR);
// get the jitter
RRCMws.ntohl (sd, pRR->jitter, &pSSRC->rrFeedback.dwInterJitter);
// highest sequence number received
RRCMws.ntohl (sd, pRR->expected,
&pSSRC->rrFeedback.XtendedSeqNum.seq_union.dwXtndedHighSeqNumRcvd);
// fraction lost
pSSRC->rrFeedback.fractionLost = (pRR->received & 0xFF);
// cumulative number of packet lost
RRCMws.ntohl (sd, pRR->received, &dwHost);
dwHost &= 0x00FFFFFF;
pSSRC->rrFeedback.cumNumPcktLost = dwHost;
IN_OUT_STR ("RTCP: Exit updateRRfeedback\n");
return TRUE;
}
/*----------------------------------------------------------------------------
* Function : RTCPpostRecvBfr
* Description: RTCP post a receive buffer to Winsock-2
*
* Input : sd: RTCP socket descriptor
* pSSRC: -> to the SSRC entry
*
* Return: TRUE
---------------------------------------------------------------------------*/
void RTCPpostRecvBfr (PSSRC_ENTRY pSSRC,
PRTCP_BFR_LIST pRcvStruct)
{
DWORD dwStatus;
DWORD dwError;
IN_OUT_STR ("RTCP: Enter RTCPpostRecvBfr\n");
// decrement number of I/O pending
InterlockedDecrement ((long *)&pSSRC->pRTCPses->dwNumRcvIoPending);
// don't repost any buffer if within the shutdown procedure
if ((pSSRC->pRTCPses->dwSessionStatus & SHUTDOWN_IN_PROGRESS) &&
(pSSRC->pRTCPses->dwNumRcvIoPending == 0))
{
// shutdown done - set event
if (SetEvent (pSSRC->pRTCPses->hShutdownDone) == FALSE)
{
RRCM_DBG_MSG ("RTCP: SetEvent() Error\n", GetLastError(),
__FILE__, __LINE__, DBG_ERROR);
}
IN_OUT_STR ("RTCP: Exit RTCPpostRecvBfr\n");
return;
}
else if (pSSRC->pRTCPses->dwSessionStatus & SHUTDOWN_IN_PROGRESS)
{
IN_OUT_STR ("RTCP: Exit RTCPpostRecvBfr\n");
return;
}
// clear number of bytes transferred
pRcvStruct->dwNumBytesXfr = 0;
dwStatus = RRCMws.recvFrom (pSSRC->RTCPsd,
&pRcvStruct->bfr,
pRcvStruct->dwBufferCount,
&pRcvStruct->dwNumBytesXfr,
&pRcvStruct->dwFlags,
(PSOCKADDR)pRcvStruct->addr,
&pRcvStruct->addrLen,
(LPWSAOVERLAPPED)&pRcvStruct->overlapped,
RTCPrcvCallback);
// Check Winsock status
if (dwStatus != 0)
{
// error, the receive request won't proceed
dwError = GetLastError();
if ((dwError != WSA_IO_PENDING) && (dwError != WSAEMSGSIZE))
{
RRCM_DBG_MSG ("RTCP: ERROR - WSARecvFrom()", dwError,
__FILE__, __LINE__, DBG_ERROR);
// notify application if interested
RRCMnotification (RRCM_RTCP_WS_RCV_ERROR, pSSRC,
pSSRC->SSRC, dwError);
// Return the buffer to the free queue
addToHeadOfList (&pSSRC->pRTCPses->RTCPrcvBfrList,
(PLINK_LIST)pRcvStruct,
&pSSRC->pRTCPses->critSect);
}
else
{
// increment number of I/O pending
InterlockedIncrement ((long *)&pSSRC->pRTCPses->dwNumRcvIoPending);
}
}
else
{
// synchronous completion - callback has been scheduled
// increment number of I/O pending
InterlockedIncrement ((long *)&pSSRC->pRTCPses->dwNumRcvIoPending);
}
IN_OUT_STR ("RTCP: Exit RTCPpostRecvBfr\n");
}
/*----------------------------------------------------------------------------
* Function : addApplicationRtcpBfr
* Description: Add an application provided buffer for RTCP to copy the
* raw received reports to be used by the application if it
* desired so.
*
* Input : RTPsession: Handle to the RTP session
* pAppBfr: -> an application buffer data structure
*
* Return: TRUE
---------------------------------------------------------------------------*/
HRESULT WINAPI addApplicationRtcpBfr (DWORD_PTR RTPsession,
PAPP_RTCP_BFR pAppBfr)
{
IN_OUT_STR ("RTCP : Enter addApplicationRtcpBfr()\n");
PRTP_SESSION pSession = (PRTP_SESSION)RTPsession;
PRTCP_SESSION pRTCPSess;
if (pSession == NULL)
{
RRCM_DBG_MSG ("RTCP : ERROR - Invalid RTP session", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP : Exit addApplicationRtcpBfr()\n");
return (MAKE_RRCM_ERROR(RRCMError_RTPSessResources));
}
pRTCPSess = (PRTCP_SESSION)pSession->pRTCPSession;
if (pRTCPSess == NULL)
{
RRCM_DBG_MSG ("RTCP : ERROR - Invalid RTCP session", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP : Exit addApplicationRtcpBfr()\n");
return (MAKE_RRCM_ERROR(RRCMError_RTCPInvalidSession));
}
// Let's add this buffer to our list
addToTailOfList(&(pRTCPSess->appRtcpBfrList),
(PLINK_LIST)pAppBfr,
&pRTCPSess->critSect);
IN_OUT_STR ("RTCP : Exit addApplicationRtcpBfr()\n");
return NOERROR;
}
/*----------------------------------------------------------------------------
* Function : removeApplicationRtcpBfr
* Description: Remove an application provided buffer to this RTCP session.
*
* Input : RTPsession: RTP session handle
*
* Return: Application buffer address / NULL
---------------------------------------------------------------------------*/
PAPP_RTCP_BFR WINAPI removeApplicationRtcpBfr (DWORD_PTR RTPsession)
{
PRTP_SESSION pSession = (PRTP_SESSION)RTPsession;
PRTCP_SESSION pRTCPSess;
PAPP_RTCP_BFR pAppBfr;
IN_OUT_STR ("RTCP : Enter removeApplicationRtcpBfr()\n");
if (pSession == NULL)
{
RRCM_DBG_MSG ("RTCP : ERROR - Invalid RTP session", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP : Exit removeApplicationRtcpBfr()\n");
return NULL;
}
pRTCPSess = (PRTCP_SESSION)pSession->pRTCPSession;
if (pRTCPSess == NULL)
{
RRCM_DBG_MSG ("RTCP : ERROR - Invalid RTCP session", 0,
__FILE__, __LINE__, DBG_ERROR);
IN_OUT_STR ("RTCP : Exit removeApplicationRtcpBfr()\n");
return NULL;
}
pAppBfr = (PAPP_RTCP_BFR)removePcktFromHead (&(pRTCPSess->appRtcpBfrList),
&pRTCPSess->critSect);
IN_OUT_STR ("RTCP : Exit removeApplicationRtcpBfr()\n");
return pAppBfr;
}
// [EOF]