windows-nt/Source/XPSP1/NT/enduser/netmeeting/av/rrcm/rtcp/rtcpsend.cpp

1107 lines
40 KiB
C++
Raw Normal View History

2020-09-26 03:20:57 -05:00
/*----------------------------------------------------------------------------
* 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]