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