/*---------------------------------------------------------------------------- * 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" /*--------------------------------------------------------------------------- / Global Variables /--------------------------------------------------------------------------*/ #define DBG_CUM_FRACT_LOSS 0 #define FRACTION_ENTRIES 10 #define FRACTION_SHIFT_MAX 32 long microSecondFrac [FRACTION_ENTRIES] = {500000, 250000, 125000, 62500, 31250, 15625, 7812, // some precision lost 3906, // some precision lost 1953, // some precision lost 976}; // ~ 1 milli second /*--------------------------------------------------------------------------- / External Variables /--------------------------------------------------------------------------*/ extern PRTCP_CONTEXT pRTCPContext; 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 : xmtRTCPreport * Description: RTCP report generator * * Input : pSSRC : -> to the SSRC entry * * Return: None. ---------------------------------------------------------------------------*/ BOOL FormatRTCPReport (PRTCP_SESSION pRTCP, PSSRC_ENTRY pSSRC, DWORD curTime) { PLINK_LIST pTmp; RTCP_COMMON_T *pRTCPhdr; RTCP_RR_T *pRTCPrr; RECEIVER_RPT *pRTCPrecvr; SENDER_RPT *pRTCPsr; DWORD numRcvRpt; DWORD numSrc; SOCKET sd; DWORD dwTotalRtpBW = 0; PDWORD pLastWord; SDES_DATA sdesBfr[MAX_NUM_SDES]; PCHAR pData; unsigned short pcktLen; int weSent = FALSE; #ifdef _DEBUG DWORD timeDiff; #endif #ifdef ENABLE_ISDM2 // update ISDM if (Isdm2.hISDMdll && pSSRC->hISDM) updateISDMstat (pSSRC, &Isdm2, XMITR, TRUE); #endif ASSERT (!pSSRC->dwNumXmtIoPending); // should only have one pending send if (pSSRC->dwNumXmtIoPending) return FALSE; memset (sdesBfr, 0x00, sizeof(SDES_DATA) * MAX_NUM_SDES); // lock out access to the SSRC entry EnterCriticalSection (&pSSRC->critSect); // socket descriptor sd = pSSRC->RTCPsd; // RTCP common header pRTCPhdr = (RTCP_COMMON_T *)pRTCP->XmtBfr.buf; // RTP protocol version pRTCPhdr->type = RTP_TYPE; // reset the flag weSent = 0; // SR or RR ? Check our Xmt list entry to know if we've sent data if (pSSRC->xmtInfo.dwCurXmtSeqNum != pSSRC->xmtInfo.dwPrvXmtSeqNum) { // set flag for Bw calculation weSent = TRUE; // update packet count pSSRC->xmtInfo.dwPrvXmtSeqNum = pSSRC->xmtInfo.dwCurXmtSeqNum; // build SR RTCPbuildSenderRpt (pSSRC, pRTCPhdr, &pRTCPsr, sd); // set the receiver report pointer pData = (PCHAR)(pRTCPsr + 1); // adjust for the additional structure defined in the SENDER_RPT pData -= sizeof (RTCP_RR_T); pRTCPrr = (RTCP_RR_T *)pData; #ifdef DYNAMIC_RTCP_BW // calculate the RTP bandwidth used by this transmitter dwTotalRtpBW = updateRtpXmtBW (pSSRC); #endif } else { // payload type, RR pRTCPhdr->pt = RTCP_RR; // set the receiver report pointer pRTCPrecvr = (RECEIVER_RPT *)(pRTCPhdr + 1); // set our SSRC as the originator of this report RRCMws.htonl (sd, pSSRC->SSRC, &pRTCPrecvr->ssrc); pRTCPrr = pRTCPrecvr->rr; } // build receiver report list numRcvRpt = 0; numSrc = 0; // go through the received SSRCs list pTmp = pRTCP->RcvSSRCList.prev; while (pTmp) { // increment the number of sources for later time-out calculation numSrc++; // check to see if this entry is an active sender if (((PSSRC_ENTRY)pTmp)->rcvInfo.dwNumPcktRcvd == ((PSSRC_ENTRY)pTmp)->rcvInfo.dwPrvNumPcktRcvd) { // not an active source, don't include it in the RR pTmp = pTmp->next; // next entry in SSRC list continue; } // build RR RTCPbuildReceiverRpt ((PSSRC_ENTRY)pTmp, pRTCPrr, sd); #ifdef DYNAMIC_RTCP_BW // calculate the RTP bandwidth used by this remote stream dwTotalRtpBW += updateRtpRcvBW ((PSSRC_ENTRY)pTmp); #endif // next entry in receiver report pRTCPrr++; // next entry in SSRC list pTmp = pTmp->next; if (++numRcvRpt >= MAX_RR_ENTRIES) // !!! TODO !!! // When over 31 sources, generate a second packet or go round robin break; } // check to see if any Receiver Report. If not, still send an empty RR, // that will be followed by an SDES CNAME, for case like initialization // time, or when no stream received yet if ((numRcvRpt == 0) && (weSent == TRUE)) { // adjust to the right place pRTCPrr = (RTCP_RR_T *)pData; } // report count pRTCPhdr->count = (WORD)numRcvRpt; // packet length for the previous SR/RR pcktLen = (unsigned short)((char *)pRTCPrr - pRTCP->XmtBfr.buf); RRCMws.htons (sd, (WORD)((pcktLen >> 2) - 1), &pRTCPhdr->length); // check which SDES needs to be send RTCPcheckSDEStoXmit (pSSRC, sdesBfr); // build the SDES information pLastWord = RTCPbuildSDES ((RTCP_COMMON_T *)pRTCPrr, pSSRC, sd, pRTCP->XmtBfr.buf, sdesBfr); // calculate total RTCP packet length to xmit pRTCP->XmtBfr.len = (u_long)((char *)pLastWord - pRTCP->XmtBfr.buf); if ( ! (pSSRC->dwSSRCStatus & RTCP_XMT_USER_CTRL)) { #ifdef DYNAMIC_RTCP_BW // get 5% of the total RTP bandwidth dwTotalRtpBW = (dwTotalRtpBW * 5) / 100; // calculate the next interval based upon RTCP parameters if (dwTotalRtpBW < pSSRC->xmtInfo.dwRtcpStreamMinBW) { dwTotalRtpBW = pSSRC->xmtInfo.dwRtcpStreamMinBW; } #ifdef _DEBUG wsprintf(debug_string, "RTCP: RTCP BW (Bytes/sec) = %ld", dwTotalRtpBW); RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE); #endif #else dwTotalRtpBW = pSSRC->xmtInfo.dwRtcpStreamMinBW; #endif pSSRC->dwNextReportSendTime = curTime + RTCPxmitInterval (numSrc + 1, numRcvRpt, dwTotalRtpBW, weSent, (int)(pRTCP->XmtBfr.len + NTWRK_HDR_SIZE), &pRTCP->avgRTCPpktSizeRcvd, 0); } else { // user's control of the RTCP timeout interval if (pSSRC->dwUserXmtTimeoutCtrl != RTCP_XMT_OFF) { pSSRC->dwNextReportSendTime = timeGetTime() + pSSRC->dwUserXmtTimeoutCtrl; } else { pSSRC->dwNextReportSendTime = RTCP_XMT_OFF; } } #ifdef _DEBUG timeDiff = curTime - pSSRC->dwPrvTime; pSSRC->dwPrvTime = curTime; wsprintf(debug_string, "RTCP: Sent report #%ld for SSRC x%lX after %5ld msec - (%s) w/ %d RR", pSSRC->dwNumRptSent, pSSRC->SSRC, timeDiff, (weSent==TRUE) ? "SR": "RR", numRcvRpt); RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE); #endif // unlock pointer access LeaveCriticalSection (&pSSRC->critSect); return TRUE; } /*---------------------------------------------------------------------------- * Function : getSSRCpcktLoss * Description: Calculate the packet loss fraction and cumulative number. * * Input : pSSRC: -> to SSRC entry * update: Flag. Update the number received, or just calculate the * number of packet lost w/o updating the counters. * * * Return: Fraction Lost: Number of packet lost (8:24) ---------------------------------------------------------------------------*/ DWORD getSSRCpcktLoss (PSSRC_ENTRY pSSRC, DWORD update) { DWORD expected; DWORD expectedInterval; DWORD rcvdInterval; int lostInterval; DWORD fraction; DWORD cumLost; DWORD dwTmp; IN_OUT_STR ("RTCP: Enter getSSRCpcktLoss()\n"); // if nothing has been received, there is no loss if (pSSRC->rcvInfo.dwNumPcktRcvd == 0) { IN_OUT_STR ("RTCP: Exit getSSRCpcktLoss()\n"); return 0; } // as per the RFC, but always one packet off when doing it ??? expected = pSSRC->rcvInfo.XtendedSeqNum.seq_union.dwXtndedHighSeqNumRcvd - pSSRC->rcvInfo.dwBaseRcvSeqNum + 1; cumLost = expected - pSSRC->rcvInfo.dwNumPcktRcvd; // 24 bits value cumLost &= 0x00FFFFFF; #if DBG_CUM_FRACT_LOSS wsprintf(debug_string, "RTCP : High Seq. #: %ld - Base: %ld", pSSRC->rcvInfo.XtendedSeqNum.seq_union.dwXtndedHighSeqNumRcvd, pSSRC->rcvInfo.dwBaseRcvSeqNum + 1); RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE); wsprintf(debug_string, "RTCP : Expected: %ld - CumLost: %ld", expected, cumLost); RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE); #endif // Network byte order the lost number (will be or'ed with the fraction) RRCMws.htonl (pSSRC->RTPsd, cumLost, &dwTmp); cumLost = dwTmp; // fraction lost (per RFC) expectedInterval = expected - pSSRC->rcvInfo.dwExpectedPrior; rcvdInterval = pSSRC->rcvInfo.dwNumPcktRcvd - pSSRC->rcvInfo.dwPrvNumPcktRcvd; #if DBG_CUM_FRACT_LOSS wsprintf(debug_string, "RTCP : Exp. interval: %ld - Rcv interval: %ld", expectedInterval, rcvdInterval); RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE); #endif // check if need to update the data, or just calculate the loss if (update) { pSSRC->rcvInfo.dwExpectedPrior = expected; pSSRC->rcvInfo.dwPrvNumPcktRcvd = pSSRC->rcvInfo.dwNumPcktRcvd; } lostInterval = expectedInterval - rcvdInterval; if (expectedInterval == 0 || lostInterval <= 0) fraction = 0; else { fraction = (lostInterval << 8) / expectedInterval; // 8 bits value if (fraction > 0x000000FF) // 100 % loss fraction = 0x000000FF; fraction &= 0x000000FF; } #if DBG_CUM_FRACT_LOSS wsprintf(debug_string, "RTCP : Lost interval: %ld - Fraction: %ld", lostInterval, fraction); RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE); #endif // get the 32 bits fraction/number cumLost |= fraction; IN_OUT_STR ("RTCP: Exit getSSRCpcktLoss()\n"); return cumLost; } /*---------------------------------------------------------------------------- * Function : RTCPcheckSDEStoXmit * Description: Check which SDES needs to be transmitted with this report. * SDES frequency varies for each type and is defined by the * application. * * Input : pSSRC: -> to the SSRC entry * pSdes: -> to SDES buffer to initialize * * Return: None ---------------------------------------------------------------------------*/ void RTCPcheckSDEStoXmit (PSSRC_ENTRY pSSRC, PSDES_DATA pSdes) { PSDES_DATA pTmpSdes = pSdes; IN_OUT_STR ("RTCP: Enter RTCPcheckSDEStoXmit()\n"); if (pSSRC->cnameInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->cnameInfo.dwSdesFrequency) == 0) { // CNAME pTmpSdes->dwSdesType = RTCP_SDES_CNAME; pTmpSdes->dwSdesLength = pSSRC->cnameInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->cnameInfo.sdesBfr, pSSRC->cnameInfo.dwSdesLength); } } pTmpSdes++; if (pSSRC->nameInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->nameInfo.dwSdesFrequency) == 0) { // NAME pTmpSdes->dwSdesType = RTCP_SDES_NAME; pTmpSdes->dwSdesLength = pSSRC->nameInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->nameInfo.sdesBfr, pSSRC->nameInfo.dwSdesLength); } } pTmpSdes++; if (pSSRC->emailInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->emailInfo.dwSdesFrequency) == 0) { // EMAIL pTmpSdes->dwSdesType = RTCP_SDES_EMAIL; pTmpSdes->dwSdesLength = pSSRC->emailInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->emailInfo.sdesBfr, pSSRC->emailInfo.dwSdesLength); } } pTmpSdes++; if (pSSRC->phoneInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->phoneInfo.dwSdesFrequency) == 0) { // PHONE pTmpSdes->dwSdesType = RTCP_SDES_PHONE; pTmpSdes->dwSdesLength = pSSRC->phoneInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->phoneInfo.sdesBfr, pSSRC->phoneInfo.dwSdesLength); } } pTmpSdes++; if (pSSRC->locInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->locInfo.dwSdesFrequency) == 0) { // LOCATION pTmpSdes->dwSdesType = RTCP_SDES_LOC; pTmpSdes->dwSdesLength = pSSRC->locInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->locInfo.sdesBfr, pSSRC->locInfo.dwSdesLength); } } pTmpSdes++; if (pSSRC->toolInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->toolInfo.dwSdesFrequency) == 0) { // TOOL pTmpSdes->dwSdesType = RTCP_SDES_TOOL; pTmpSdes->dwSdesLength = pSSRC->toolInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->toolInfo.sdesBfr, pSSRC->toolInfo.dwSdesLength); } } pTmpSdes++; if (pSSRC->txtInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->txtInfo.dwSdesFrequency) == 0) { // TEXT pTmpSdes->dwSdesType = RTCP_SDES_TXT; pTmpSdes->dwSdesLength = pSSRC->txtInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->txtInfo.sdesBfr, pSSRC->txtInfo.dwSdesLength); } } pTmpSdes++; if (pSSRC->privInfo.dwSdesFrequency) { if ((pSSRC->dwNumRptSent % pSSRC->privInfo.dwSdesFrequency) == 0) { // PRIVATE pTmpSdes->dwSdesType = RTCP_SDES_PRIV; pTmpSdes->dwSdesLength = pSSRC->privInfo.dwSdesLength; memcpy (pTmpSdes->sdesBfr, pSSRC->privInfo.sdesBfr, pSSRC->privInfo.dwSdesLength); } } pTmpSdes++; pTmpSdes->dwSdesLength = 0; IN_OUT_STR ("RTCP: Exit RTCPcheckSDEStoXmit()\n"); } /*---------------------------------------------------------------------------- * Function : RTCPbuildSDES * Description: Build the SDES report * * Input : pRTCPhdr: -> to the RTCP packet header * pSSRC: -> to the SSRC entry * sd: Socket descriptor * startAddr: -> to the packet start address * pSdes: -> to the SDES information to build * * Return: pLastWord: Address of the packet last Dword ---------------------------------------------------------------------------*/ PDWORD RTCPbuildSDES (RTCP_COMMON_T *pRTCPhdr, PSSRC_ENTRY pSSRC, SOCKET sd, PCHAR startAddr, PSDES_DATA pSdes) { RTCP_SDES_T *pRTCPsdes; RTCP_SDES_ITEM_T *pRTCPitem; int pad = 0; PCHAR ptr; unsigned short pcktLen; IN_OUT_STR ("RTCP: Enter RTCPbuildSDES()\n"); // setup header pRTCPhdr->type = RTP_TYPE; pRTCPhdr->pt = RTCP_SDES; pRTCPhdr->p = 0; pRTCPhdr->count = 1; // SDES specific header pRTCPsdes = (RTCP_SDES_T *)(pRTCPhdr + 1); // Get the SSRC RRCMws.htonl (sd, pSSRC->SSRC, &pRTCPsdes->src); // SDES item pRTCPitem = (RTCP_SDES_ITEM_T *)pRTCPsdes->item; while (pSdes->dwSdesLength) { // set SDES item characteristics pRTCPitem->dwSdesType = (char)pSdes->dwSdesType; // make sure we don't go too far if (pSdes->dwSdesLength > MAX_SDES_LEN) pSdes->dwSdesLength = MAX_SDES_LEN; pRTCPitem->dwSdesLength = (unsigned char)(pSdes->dwSdesLength); memcpy (pRTCPitem->sdesData, pSdes->sdesBfr, pSdes->dwSdesLength); // packet length pcktLen = (unsigned short)((char *)(pRTCPitem->sdesData + pRTCPitem->dwSdesLength) - (char *)pRTCPsdes); pRTCPitem = (RTCP_SDES_ITEM_T *)((unsigned char *)pRTCPsdes + pcktLen); pSdes->dwSdesLength = 0; // setting this to zero will clear this entry // next SDES pSdes++; } // total SDES packet length pcktLen = (unsigned short)((char *)pRTCPitem - (char *)pRTCPhdr); // Zero the last word of the SDES item chunk, and padd to the // 32 bits boundary. If we landed exactly on the boundary then // have a whole null word to terminate the sdes, as is needed. pad = 4 - (pcktLen & 3); pcktLen += (unsigned short)pad; ptr = (PCHAR)pRTCPitem; while (pad--) *ptr++ = 0x00; // update packet length in header field RRCMws.htons (sd, (WORD)((pcktLen >> 2) - 1), &pRTCPhdr->length); IN_OUT_STR ("RTCP: Exit RTCPbuildSDES()\n"); return ((PDWORD)ptr); } /*---------------------------------------------------------------------------- * Function : RTCPbuildSenderRpt * Description: Build the RTCP Sender Report * * Input : pSSRC: -> to the SSRC entry * pRTCPhdr: -> to the RTCP packet header * pRTCPsr: -> to the Sender Report header * sd: Socket descriptor * * Return: None ---------------------------------------------------------------------------*/ void RTCPbuildSenderRpt (PSSRC_ENTRY pSSRC, RTCP_COMMON_T *pRTCPhdr, SENDER_RPT **pRTCPsr, SOCKET sd) { DWORD dwTmp; DWORD NTPtime; DWORD RTPtime; DWORD RTPtimeStamp = 0; IN_OUT_STR ("RTCP: Enter RTCPbuildSenderRpt()\n"); // payload type, SR pRTCPhdr->pt = RTCP_SR; // padding pRTCPhdr->p = 0; // sender report header *pRTCPsr = (SENDER_RPT *)(pRTCPhdr + 1); // fill in sender report packet RRCMws.htonl (sd, pSSRC->SSRC, &((*pRTCPsr)->ssrc)); RRCMws.htonl (sd, pSSRC->xmtInfo.dwNumPcktSent, &((*pRTCPsr)->psent)); RRCMws.htonl (sd, pSSRC->xmtInfo.dwNumBytesSent, &((*pRTCPsr)->osent)); // NTP timestamp NTPtime = RTPtime = timeGetTime (); // get the number of seconds (integer calculation) dwTmp = NTPtime/1000; // NTP Msw RRCMws.htonl (sd, (NTPtime/1000), &((*pRTCPsr)->ntp_sec)); // convert back dwTmp from second to millisecond dwTmp *= 1000; // get the remaining number of millisecond for the LSW NTPtime -= dwTmp; // NTP Lsw RRCMws.htonl (sd, usec2ntpFrac ((long)NTPtime*1000), &((*pRTCPsr)->ntp_frac)); // calculate the RTP timestamp offset which correspond to this NTP // time and convert it to stream samples if (pSSRC->dwStreamClock) { RTPtimeStamp = pSSRC->xmtInfo.dwLastSendRTPTimeStamp + ((RTPtime - pSSRC->xmtInfo.dwLastSendRTPSystemTime) * (pSSRC->dwStreamClock / 1000)); } RRCMws.htonl (sd, RTPtimeStamp, &((*pRTCPsr)->rtp_ts)); IN_OUT_STR ("RTCP: Exit RTCPbuildSenderRpt()\n"); } /*---------------------------------------------------------------------------- * Function : usec2ntp * Description: Convert microsecond to fraction of second for NTP * As per VIC. * Convert micro-second to fraction of second * 2^32. This * routine uses the factorization: * 2^32/10^6 = 4096 + 256 - 1825/32 * which results in a max conversion error of 3*10^-7 and an * average error of half that * * Input : usec: Micro second * * Return: Fraction of second in NTP format ---------------------------------------------------------------------------*/ DWORD usec2ntp (DWORD usec) { DWORD tmp = (usec * 1825) >>5; return ((usec << 12) + (usec << 8) - tmp); } /*---------------------------------------------------------------------------- * Function : usec2ntpFrac * Description: Convert microsecond to fraction of second for NTP. * Just uses an array of microsecond and set the corresponding * bit. * * Input : usec: Micro second * * Return: Fraction of second ---------------------------------------------------------------------------*/ DWORD usec2ntpFrac (long usec) { DWORD idx; DWORD fraction = 0; DWORD tmpFraction = 0; long tmpVal; DWORD shift; for (idx=0, shift=FRACTION_SHIFT_MAX-1; idx < FRACTION_ENTRIES; idx++, shift--) { tmpVal = usec; if ((tmpVal - microSecondFrac[idx]) > 0) { usec -= microSecondFrac[idx]; tmpFraction = (1 << shift); fraction |= tmpFraction; } else if ((tmpVal - microSecondFrac[idx]) == 0) { tmpFraction = (1 << shift); fraction |= tmpFraction; break; } } return fraction; } /*---------------------------------------------------------------------------- * Function : RTCPbuildReceiverRpt * Description: Build the RTCP Receiver Report * * Input : pSSRC: -> to the SSRC entry * pRTCPsr: -> to the Receiver Report header * sd: Socket descriptor * * Return: None ---------------------------------------------------------------------------*/ void RTCPbuildReceiverRpt (PSSRC_ENTRY pSSRC, RTCP_RR_T *pRTCPrr, SOCKET sd) { DWORD dwDlsr; IN_OUT_STR ("RTCP: Enter RTCPbuildReceiverRpt()\n"); // get the SSRC RRCMws.htonl (sd, pSSRC->SSRC, &pRTCPrr->ssrc); // get fraction and cumulative number of packet lost (per RFC) pRTCPrr->received = getSSRCpcktLoss (pSSRC, TRUE); // extended highest sequence number received RRCMws.htonl (sd, pSSRC->rcvInfo.XtendedSeqNum.seq_union.dwXtndedHighSeqNumRcvd, &pRTCPrr->expected); // interrarival jitter #ifdef ENABLE_FLOATING_POINT RRCMws.htonl (sd, pSSRC->rcvInfo.interJitter, &pRTCPrr->jitter); #else // Check RFC for details of the round off RRCMws.htonl (sd, (pSSRC->rcvInfo.interJitter >> 4), &pRTCPrr->jitter); #endif // last SR RRCMws.htonl (sd, pSSRC->xmtInfo.dwLastSR, &pRTCPrr->lsr); // delay since last SR (only if an SR has been received from // this source, otherwise 0) if (pRTCPrr->lsr) { // get the DLSR dwDlsr = getDLSR (pSSRC); RRCMws.htonl (sd, dwDlsr, &pRTCPrr->dlsr); } else pRTCPrr->dlsr = 0; IN_OUT_STR ("RTCP: Exit RTCPbuildReceiverRpt()\n"); } /*---------------------------------------------------------------------------- * Function : getDLSR * Description: Get a DLSR packet * * Input : pSSRC: -> to the SSRC entry * * Return: DLSR in seconds:fraction format ---------------------------------------------------------------------------*/ DWORD getDLSR (PSSRC_ENTRY pSSRC) { DWORD dwDlsr; DWORD dwTime; DWORD dwTmp; // DLSR in millisecond dwTime = timeGetTime() - pSSRC->xmtInfo.dwLastSRLocalTime; // get the number of seconds (integer calculation) dwTmp = dwTime/1000; // set the DLSR upper 16 bits (seconds) dwDlsr = dwTmp << 16; // convert back dwTmp from second to millisecond dwTmp *= 1000; // get the remaining number of millisecond for the LSW dwTime -= dwTmp; // convert microseconds to fraction of seconds dwTmp = usec2ntpFrac ((long)dwTime*1000); // get only the upper 16 bits dwTmp >>= 16; dwTmp &= 0x0000FFFF; // set the DLSR lower 16 bits (fraction of seconds) dwDlsr |= dwTmp; return dwDlsr; } /*---------------------------------------------------------------------------- * Function : RTCPsendBYE * Description: Send an RTCP BYE packet * * Input : pRTCP: -> to the RTCP session * pSSRC: -> to the SSRC entry * byeReason: -> to the Bye reason, eg, "camera malfunction"... * * Return: None ---------------------------------------------------------------------------*/ void RTCPsendBYE (PSSRC_ENTRY pSSRC, PCHAR pByeReason) { #define MAX_RTCP_BYE_SIZE 500 // ample PRTCP_SESSION pRTCP; WSABUF wsaBuf; char buf[MAX_RTCP_BYE_SIZE]; RTCP_COMMON_T *pRTCPhdr; RTCP_RR_T *pRTCPrr; RECEIVER_RPT *pRTCPrecvr; BYE_PCKT *pBye; DWORD *pLastWord; DWORD dwStatus; DWORD dwNumBytesXfr; DWORD offset; DWORD byeLen; unsigned short pcktLen; PCHAR pBfr; IN_OUT_STR ("RTCP: Enter RTCPsendBYE()\n"); // get the RTCP session pRTCP = pSSRC->pRTCPses; // check to see if under H.323 conference control. Don't send BYE in // this case if (pRTCP->dwSessionStatus & H323_CONFERENCE) return; // make sure the destination address is known if (!(pRTCP->dwSessionStatus & RTCP_DEST_LEARNED)) { IN_OUT_STR ("RTCP: Exit RTCPsendBYE()\n"); return; } // RTCP common header pRTCPhdr = (RTCP_COMMON_T *)buf; // RTP protocol version pRTCPhdr->type = RTP_TYPE; // empty RR pRTCPhdr->pt = RTCP_RR; // padding pRTCPhdr->p = 0; // report count pRTCPhdr->count = 0; // set the receiver report pointer pRTCPrecvr = (RECEIVER_RPT *)(pRTCPhdr + 1); // get our SSRC RRCMws.htonl (pSSRC->RTCPsd, pSSRC->SSRC, &pRTCPrecvr->ssrc); // build receiver report list pRTCPrr = pRTCPrecvr->rr; // just adjust the size, sending 0 or garbagge doesn't matter, the // report count will tell the receiver what's valid pRTCPrr++; // packet length for the previous RR pcktLen = (unsigned short)((char *)pRTCPrr - buf); RRCMws.htons (pSSRC->RTCPsd, (WORD)((pcktLen >> 2) - 1), &pRTCPhdr->length); // BYE packet pRTCPhdr = (RTCP_COMMON_T *)pRTCPrr; pRTCPhdr->type = RTP_TYPE; pRTCPhdr->pt = RTCP_BYE; pRTCPhdr->count = 1; pBye = (BYE_PCKT *)pRTCPhdr + 1; RRCMws.htonl (pSSRC->RTCPsd, pSSRC->SSRC, pBye->src); pBye++; // send the reason pBfr = (PCHAR)pBye; if (pByeReason) byeLen = min (strlen(pByeReason), MAX_SDES_LEN); else byeLen = strlen ("Session Terminated"); // Pre-zero the last word of the SDES item chunk, and padd to the // 32 bits boundary. Need to do this before the memcpy, If we // landed exactly on the boundary then this will give us a whole // null word to terminate the sdes, as is needed. offset = (DWORD)((pBfr - buf) + byeLen); pLastWord = (unsigned long *)(buf + (offset & ~3)); *pLastWord++ = 0; if (pByeReason) memcpy (pBfr+1, pByeReason, byeLen); else strcpy (pBfr+1, "Session Terminated"); *pBfr = (unsigned char)byeLen; pcktLen = (unsigned short)((char *)pLastWord - (char *)pRTCPhdr); RRCMws.htons (pSSRC->RTCPsd, (WORD)((pcktLen >> 2) - 1), &pRTCPhdr->length); // calculate total RTCP packet length to xmit wsaBuf.len = (u_long)((char *)pLastWord - buf); #if (defined(_DEBUG) || defined(PCS_COMPLIANCE)) if (RTPLogger) { //INTEROP InteropOutput (RTPLogger, (BYTE FAR*)(buf), (int)wsaBuf.len, RTPLOG_SENT_PDU | RTCP_PDU); } #endif // send the packet dwStatus = RRCMws.sendTo (pSSRC->RTCPsd, &wsaBuf, 1, &dwNumBytesXfr, 0, (PSOCKADDR)pRTCP->toBfr, pRTCP->toLen, NULL, NULL); IN_OUT_STR ("RTCP: Exit RTCPsendBYE()\n"); } /*---------------------------------------------------------------------------- * Function : updateRtpXmtBW * Description: Calculate a sending stream bandwidth during the last report * interval. * * Input : pSSRC: -> to the SSRC entry * * Return: Bandwith used by transmitter in bytes/sec ---------------------------------------------------------------------------*/ #ifdef DYNAMIC_RTCP_BW DWORD updateRtpXmtBW (PSSRC_ENTRY pSSRC) { DWORD dwBW = 0; DWORD dwTimeInterval; DWORD dwByteInterval; DWORD dwTimeNow = timeGetTime(); IN_OUT_STR ("RTCP: Enter updateRtpXmtBW()\n"); if (pSSRC->xmtInfo.dwLastTimeBwCalculated == 0) { pSSRC->xmtInfo.dwLastTimeBwCalculated = dwTimeNow; pSSRC->xmtInfo.dwLastTimeNumBytesSent = pSSRC->xmtInfo.dwNumBytesSent; pSSRC->xmtInfo.dwLastTimeNumPcktSent = pSSRC->xmtInfo.dwNumPcktSent; } else { dwTimeInterval = dwTimeNow - pSSRC->xmtInfo.dwLastTimeBwCalculated; pSSRC->xmtInfo.dwLastTimeBwCalculated = dwTimeNow; // get the interval in second (we loose the fractional part) dwTimeInterval = dwTimeInterval / 1000; dwByteInterval = pSSRC->xmtInfo.dwNumBytesSent - pSSRC->xmtInfo.dwLastTimeNumBytesSent; dwBW = dwByteInterval / dwTimeInterval; pSSRC->xmtInfo.dwLastTimeNumBytesSent = pSSRC->xmtInfo.dwNumBytesSent; pSSRC->xmtInfo.dwLastTimeNumPcktSent = pSSRC->xmtInfo.dwNumPcktSent; } IN_OUT_STR ("RTCP: Exit updateRtpXmtBW()\n"); return dwBW; } #endif // #ifdef DYNAMIC_RTCP_BW /*---------------------------------------------------------------------------- * Function : updateRtpRcvBW * Description: Calculate a remote stream RTP bandwidth during the last * report interval. * * Input : pSSRC: -> to the SSRC entry * * Return: Bandwith used by the remote stream in bytes/sec ---------------------------------------------------------------------------*/ #ifdef DYNAMIC_RTCP_BW DWORD updateRtpRcvBW (PSSRC_ENTRY pSSRC) { DWORD dwBW = 0; DWORD dwTimeInterval; DWORD dwByteInterval; DWORD dwTimeNow = timeGetTime(); IN_OUT_STR ("RTCP: Enter updateRtpRcvBW()\n"); if (pSSRC->rcvInfo.dwLastTimeBwCalculated == 0) { pSSRC->rcvInfo.dwLastTimeBwCalculated = dwTimeNow; pSSRC->rcvInfo.dwLastTimeNumBytesRcvd = pSSRC->rcvInfo.dwNumPcktRcvd; pSSRC->rcvInfo.dwLastTimeNumPcktRcvd = pSSRC->rcvInfo.dwNumBytesRcvd; } else { dwTimeInterval = dwTimeNow - pSSRC->rcvInfo.dwLastTimeBwCalculated; pSSRC->rcvInfo.dwLastTimeBwCalculated = dwTimeNow; // get the interval in second (we loose the fractional part) dwTimeInterval = dwTimeInterval / 1000; dwByteInterval = pSSRC->rcvInfo.dwNumBytesRcvd - pSSRC->rcvInfo.dwLastTimeNumBytesRcvd; dwBW = dwByteInterval / dwTimeInterval; pSSRC->rcvInfo.dwLastTimeNumBytesRcvd = pSSRC->rcvInfo.dwNumPcktRcvd; pSSRC->rcvInfo.dwLastTimeNumPcktRcvd = pSSRC->rcvInfo.dwNumBytesRcvd; } IN_OUT_STR ("RTCP: Exit updateRtpXmtBW()\n"); return dwBW; } #endif // #ifdef DYNAMIC_RTCP_BW // [EOF]