478 lines
15 KiB
C++
478 lines
15 KiB
C++
/*----------------------------------------------------------------------------
|
|
* File: RTP_STAT.C
|
|
* Product: RTP/RTCP implementation
|
|
* Description: Provides statistical calculations for RTP packets
|
|
*
|
|
*
|
|
* 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 DBG_JITTER_ENABLE 0
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
/ Global Variables
|
|
/--------------------------------------------------------------------------*/
|
|
|
|
|
|
/*---------------------------------------------------------------------------
|
|
/ External Variables
|
|
/--------------------------------------------------------------------------*/
|
|
extern PRTP_CONTEXT pRTPContext;
|
|
extern RRCM_WS RRCMws;
|
|
|
|
#ifdef _DEBUG
|
|
extern char debug_string[];
|
|
#endif
|
|
|
|
|
|
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Function : calculateJitter
|
|
* Description: Determines jitter between current and last received packet.
|
|
*
|
|
* Input : pRTPHeader : -> to the RTP header
|
|
* pSSRC : -> to the session's SSRC list
|
|
*
|
|
* Note: Implementataion adapted from IETF RFC1889
|
|
*
|
|
* Return: RRCM_NoError = OK.
|
|
* Otherwise(!=0) = Error.
|
|
---------------------------------------------------------------------------*/
|
|
DWORD calculateJitter (RTP_HDR_T *pRTPHeader,
|
|
PSSRC_ENTRY pSSRC)
|
|
{
|
|
DWORD dwStatus = RRCM_NoError;
|
|
DWORD streamClk;
|
|
DWORD dwTmp;
|
|
int dwPropagationTime; // packet's transmit time
|
|
int dwIASourceTime; // Packet's timestamp for IA
|
|
int delta; // of 2 consec. packets
|
|
|
|
IN_OUT_STR ("RTP : Enter calculateJitter()\n");
|
|
|
|
// Convert the RTP timestamp to host order
|
|
RRCMws.ntohl (pSSRC->RTPsd, pRTPHeader->ts, (PDWORD)&dwIASourceTime);
|
|
|
|
// lock access
|
|
EnterCriticalSection (&pSSRC->critSect);
|
|
|
|
// Take the difference, after having adjusted the clock to the payload
|
|
// type frequency
|
|
streamClk =
|
|
((PSSRC_ENTRY)pSSRC->pRTCPses->XmtSSRCList.prev)->dwStreamClock;
|
|
if (streamClk)
|
|
{
|
|
dwTmp = streamClk / 1000;
|
|
|
|
// update the time to be in unit of the source clock
|
|
dwPropagationTime = (timeGetTime() * dwTmp) - dwIASourceTime;
|
|
}
|
|
else
|
|
dwPropagationTime = timeGetTime() - dwIASourceTime;
|
|
|
|
// initialize for the first valid packet, otherwise jitter will be off
|
|
if (pSSRC->rcvInfo.dwPropagationTime == 0)
|
|
{
|
|
pSSRC->rcvInfo.dwPropagationTime = dwPropagationTime;
|
|
|
|
LeaveCriticalSection (&pSSRC->critSect);
|
|
|
|
IN_OUT_STR ("RTP : Exit calculateJitter()\n");
|
|
return (dwStatus);
|
|
}
|
|
|
|
#if DBG_JITTER_ENABLE
|
|
wsprintf(debug_string, "RTP : Time: %ld - Src Timestamp: %ld",
|
|
timeGetTime(),
|
|
dwIASourceTime);
|
|
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
|
|
|
|
wsprintf(debug_string, "RTP : Propagation (Src unit): %ld",
|
|
dwPropagationTime);
|
|
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
|
|
|
|
wsprintf(debug_string, "RTP : Previous Propagation (Src unit): %ld",
|
|
pSSRC->rcvInfo.dwPropagationTime);
|
|
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
|
|
#endif
|
|
|
|
// Determine the difference in the transit times and save the latest
|
|
delta = dwPropagationTime - pSSRC->rcvInfo.dwPropagationTime;
|
|
if (delta < 0)
|
|
delta = -delta;
|
|
|
|
// check for a wrap-around, which is always possible, and avoid sending
|
|
// the jitter through the roof - It will take a long time thereafter to
|
|
// go back down to a reasonable level
|
|
// Check against arbitrary large number
|
|
if (delta > 20000)
|
|
{
|
|
pSSRC->rcvInfo.dwPropagationTime = dwPropagationTime;
|
|
|
|
LeaveCriticalSection (&pSSRC->critSect);
|
|
|
|
IN_OUT_STR ("RTP : Exit calculateJitter()\n");
|
|
return (dwStatus);
|
|
}
|
|
|
|
#if DBG_JITTER_ENABLE
|
|
wsprintf(debug_string, "RTP : Delta (Src unit): %ld", delta);
|
|
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
|
|
#endif
|
|
|
|
pSSRC->rcvInfo.dwPropagationTime = dwPropagationTime;
|
|
|
|
#ifdef ENABLE_FLOATING_POINT
|
|
// This is the RFC way to do it
|
|
pSSRC->rcvInfo.interJitter +=
|
|
((1./16.) * ((double)delta - pSSRC->rcvInfo.interJitter));
|
|
#else
|
|
// and this is when we need to remove floating point operation
|
|
pSSRC->rcvInfo.interJitter +=
|
|
(delta - (((long)pSSRC->rcvInfo.interJitter + 8) >> 4));
|
|
#endif
|
|
|
|
LeaveCriticalSection (&pSSRC->critSect);
|
|
|
|
#if DBG_JITTER_ENABLE
|
|
if (streamClk)
|
|
{
|
|
wsprintf(debug_string, "RTP : iJitter: %ld - iJitter (msec): %ld",
|
|
pSSRC->rcvInfo.interJitter,
|
|
(pSSRC->rcvInfo.interJitter / (streamClk / 1000)));
|
|
}
|
|
else
|
|
{
|
|
wsprintf(debug_string, "RTP : iJitter: %ld - Delta: %ld",
|
|
pSSRC->rcvInfo.interJitter,
|
|
delta);
|
|
}
|
|
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
|
|
|
|
wsprintf(debug_string, "RTP : Next RTCP RR iJitter: %ld",
|
|
(pSSRC->rcvInfo.interJitter >> 4));
|
|
RRCM_DBG_MSG (debug_string, 0, NULL, 0, DBG_TRACE);
|
|
#endif
|
|
|
|
IN_OUT_STR ("RTP : Exit calculateJitter()\n");
|
|
|
|
return (dwStatus);
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Function : initRTPStats
|
|
* Description: initializes statistics table for newly recieved SSRC
|
|
*
|
|
* Input : RTPSequence : Sequence number received in the packet.
|
|
* NB: Must be in LittleEndian(IA) format
|
|
* pSSRC : -> to SSRC table entry for this terminal
|
|
*
|
|
* Note: Implementataion adapted from draftspec 08, Appendix A.1
|
|
*
|
|
* Return: None.
|
|
---------------------------------------------------------------------------*/
|
|
void initRTPStats (WORD RTPSequence,
|
|
PSSRC_ENTRY pSSRC)
|
|
{
|
|
IN_OUT_STR ("RTP : Enter initRTPStats()\n");
|
|
|
|
pSSRC->rcvInfo.dwNumPcktRcvd = 0;
|
|
pSSRC->rcvInfo.dwPrvNumPcktRcvd = 0;
|
|
pSSRC->rcvInfo.dwExpectedPrior = 0;
|
|
pSSRC->rcvInfo.dwNumBytesRcvd = 0;
|
|
pSSRC->rcvInfo.dwBadSeqNum = RTP_SEQ_MOD + 1; // Out of range
|
|
pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wSequenceNum =
|
|
RTPSequence;
|
|
pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wCycle = 0;
|
|
|
|
#if 0
|
|
// as per the RFC, but always 1 packet off by doing this ???
|
|
pSSRC->rcvInfo.dwBaseRcvSeqNum = RTPSequence - 1;
|
|
#else
|
|
pSSRC->rcvInfo.dwBaseRcvSeqNum = RTPSequence;
|
|
#endif
|
|
|
|
IN_OUT_STR ("RTP : Exit initRTPStats()\n");
|
|
}
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Function : sequenceCheck
|
|
* Description: Determines whether received packet sequence number is in a
|
|
* valid range to include for statistical tracking purposes.
|
|
*
|
|
* Input : RTPSequence : Sequence number received in the packet.
|
|
* NB: Must be in LittleEndian(IA) format
|
|
* pSSRC : -> to SSRC table entry for this terminal
|
|
*
|
|
* Note: Implementataion adapted from draftspec 08, Appendix A.1
|
|
*
|
|
* Return: TRUE = OK.
|
|
* FALSE = Stale or invalid data.
|
|
---------------------------------------------------------------------------*/
|
|
#if 1
|
|
BOOL sequenceCheck (WORD RTPSequence,
|
|
PSSRC_ENTRY pSSRC)
|
|
{
|
|
WORD delta = RTPSequence -
|
|
pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wSequenceNum;
|
|
|
|
IN_OUT_STR ("RTP : Enter sequenceCheck()\n");
|
|
|
|
// Have we received enough consecutive sequence numbered pckts in order
|
|
// to valide ?
|
|
if (pSSRC->rcvInfo.dwProbation)
|
|
{
|
|
// Is the sequence received the expected one ?
|
|
if (RTPSequence ==
|
|
(pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wSequenceNum + 1))
|
|
{
|
|
// Decrement the number of consecutive packets we need before we
|
|
// consider statistics to be valid
|
|
pSSRC->rcvInfo.dwProbation--;
|
|
pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wSequenceNum =
|
|
RTPSequence;
|
|
|
|
if (pSSRC->rcvInfo.dwProbation == 0)
|
|
{
|
|
initRTPStats(RTPSequence, pSSRC);
|
|
|
|
IN_OUT_STR ("RTP : Exit sequenceCheck()\n");
|
|
|
|
return TRUE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
pSSRC->rcvInfo.dwProbation = MIN_SEQUENTIAL - 1;
|
|
pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wSequenceNum =
|
|
RTPSequence;
|
|
}
|
|
|
|
IN_OUT_STR ("RTP : Exit sequenceCheck()\n");
|
|
|
|
return FALSE;
|
|
}
|
|
else if (delta < MAX_DROPOUT)
|
|
{
|
|
// In order with permissible gap
|
|
if (RTPSequence < pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wSequenceNum)
|
|
// sequence number wrapped - count another 64K cycle
|
|
pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wCycle += 1;
|
|
|
|
pSSRC->rcvInfo.XtendedSeqNum.seq_union.RTPSequence.wSequenceNum = RTPSequence;
|
|
}
|
|
else if (delta <= RTP_SEQ_MOD - MAX_MISORDER)
|
|
{
|
|
// the sequence number made a very large jump
|
|
if (RTPSequence == pSSRC->rcvInfo.dwBadSeqNum)
|
|
// two sequential packet. Assume the other side restarted w/o telling
|
|
// us, so just re-sync, i.e., pretend this was the first packet
|
|
initRTPStats(RTPSequence, pSSRC);
|
|
else
|
|
{
|
|
pSSRC->rcvInfo.dwBadSeqNum = (RTPSequence + 1) & (RTP_SEQ_MOD - 1);
|
|
|
|
IN_OUT_STR ("RTP : Exit sequenceCheck()\n");
|
|
|
|
return FALSE;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
// duplicate or reordered packet
|
|
}
|
|
|
|
IN_OUT_STR ("RTP : Exit sequenceCheck()\n");
|
|
|
|
return (TRUE);
|
|
}
|
|
|
|
#else
|
|
//BOOL sequenceCheck (WORD RTPSequence,
|
|
// PSSRC_ENTRY lpSSRCList)
|
|
//{
|
|
// BOOL bStatus;
|
|
// WORD delta;
|
|
//
|
|
//#ifdef IN_OUT_CHK
|
|
// OutputDebugString ("\nEnter sequenceCheck()");
|
|
//#endif
|
|
//
|
|
// // Have we received a couple of consecutive sequence numbered packets for
|
|
// // validation?
|
|
// if (lpSSRCList->probation) {
|
|
//
|
|
// // Default status is don't include since the source hasn't been validated yet
|
|
// bStatus = FALSE;
|
|
//
|
|
// // Is the sequence received the expected one?
|
|
// if (RTPSequence == (lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum + 1)) {
|
|
// // Decrement the number of consecutive packets we need before we
|
|
// // consider statistics to be valid
|
|
// lpSSRCList->probation--;
|
|
// lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum = RTPSequence;
|
|
// if (lpSSRCList->probation == 0) {
|
|
// initRTPStats(RTPSequence, lpSSRCList);
|
|
// bStatus = TRUE;
|
|
// }
|
|
// }
|
|
// else {
|
|
// lpSSRCList->probation = MIN_SEQUENTIAL - 1;
|
|
// lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum = RTPSequence;
|
|
// }
|
|
// }
|
|
// else {
|
|
// // Default status is include since the source has been validated
|
|
// bStatus = TRUE;
|
|
//
|
|
// // First consider the case where delta is positive (or a duplicate packet)
|
|
// if (RTPSequence >= lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum) {
|
|
//
|
|
// delta = RTPSequence - lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum;
|
|
//
|
|
// if (delta < MAX_DROPOUT) {
|
|
// // packets may be missing, but not too many so as to be deemed restarted
|
|
// lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum = RTPSequence;
|
|
// }
|
|
// else if (delta > (RTP_SEQ_MOD - MAX_MISORDER)) {
|
|
// // There has been a recent wraparound, and this is just a recent old packet
|
|
// // Nothing to do but include for statistical processing
|
|
// }
|
|
// else {
|
|
// // there was a very large jump in sequence numbers
|
|
// if (RTPSequence == lpSSRCList->badSeqNum ) {
|
|
// // Two sequential packets after what was thought was a bad packet or
|
|
// // (assume a very large jump and proceed as if the sender restarted
|
|
// // without telling us) or a new terminal is in the session.
|
|
// initRTPStats(RTPSequence, lpSSRCList);
|
|
// }
|
|
// else {
|
|
// lpSSRCList->badSeqNum = (RTPSequence + 1) & (RTP_SEQ_MOD - 1);
|
|
// bStatus = FALSE;
|
|
// }
|
|
// }
|
|
// }
|
|
// else {
|
|
// // sequence number is less than the last we received. Could be either
|
|
// // a recent late packet, a very late packet, a wraparound or a restartup
|
|
// // of a new session for an SSRC from which we hadn't received a BYE
|
|
//
|
|
// delta = lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum - RTPSequence;
|
|
//
|
|
// if (delta < MAX_MISORDER) {
|
|
// // Packet arrived a little bit late, it's still OK
|
|
// // do nothing here, will be counted in stat routines
|
|
// }
|
|
// else if (delta > (RTP_SEQ_MOD - MAX_DROPOUT)) {
|
|
// // wrap around, adjust cycle number and sequence number
|
|
// lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.cycle++;
|
|
// lpSSRCList->XtendedSeqNum.seq_union.RTPSequence.wSequenceNum = RTPSequence;
|
|
// }
|
|
// else {
|
|
// // there was a very large jump in sequence numbers
|
|
// if (RTPSequence == lpSSRCList->badSeqNum) {
|
|
// // Two sequential packets after what was thought was a bad packet.
|
|
// // Assume a very large jump and proceed as if the sender restarted
|
|
// // without telling us
|
|
// initRTPStats(RTPSequence, lpSSRCList);
|
|
// }
|
|
// else {
|
|
// lpSSRCList->badSeqNum = (RTPSequence + 1) & (RTP_SEQ_MOD - 1);
|
|
// bStatus = FALSE;
|
|
// }
|
|
// }
|
|
// }
|
|
// }
|
|
//
|
|
//#ifdef IN_OUT_CHK
|
|
// OutputDebugString ("\nExit sequenceCheck()");
|
|
//#endif
|
|
//
|
|
// return (bStatus);
|
|
//}
|
|
#endif
|
|
|
|
|
|
/*----------------------------------------------------------------------------
|
|
* Function : updateRTPStats
|
|
* Description: Updates statistics for RTP packets received from net
|
|
*
|
|
* Input : pRTPHeader : -> to packet's RTP header field
|
|
* pSSRC : -> to remote source's statistics table
|
|
* cbTransferred : Number of bytes transferred
|
|
*
|
|
*
|
|
* Return: RRCM_NoError = OK.
|
|
* Otherwise(!=0) = Initialization Error.
|
|
---------------------------------------------------------------------------*/
|
|
DWORD updateRTPStats (RTP_HDR_T *pRTPHeader,
|
|
PSSRC_ENTRY pSSRC,
|
|
DWORD cbTransferred)
|
|
{
|
|
WORD RTPSequenceNum;
|
|
|
|
IN_OUT_STR ("RTP : Enter updateRTPStats()\n");
|
|
|
|
// Update statistics only if the data looks good. Check the sequence number
|
|
// to ensure it is within an appropriate range. First, we must convert the
|
|
// sequence number to IA (little Endian) format
|
|
RRCMws.ntohs (pSSRC->RTPsd, pRTPHeader->seq,
|
|
(unsigned short *)&RTPSequenceNum);
|
|
|
|
if (sequenceCheck (RTPSequenceNum, pSSRC))
|
|
{
|
|
// lock access to data
|
|
EnterCriticalSection (&pSSRC->critSect);
|
|
|
|
// update number of packet received
|
|
pSSRC->rcvInfo.dwNumPcktRcvd++;
|
|
|
|
// Number octets received (exclusive of header) depends on whether
|
|
// a mixer (CSRC != 0) was involved
|
|
if (pRTPHeader->cc == 0)
|
|
{
|
|
pSSRC->rcvInfo.dwNumBytesRcvd +=
|
|
(cbTransferred - (sizeof(RTP_HDR_T) - sizeof(pRTPHeader->csrc[0])));
|
|
}
|
|
else
|
|
{
|
|
pSSRC->rcvInfo.dwNumBytesRcvd +=
|
|
(cbTransferred - sizeof(RTP_HDR_T) +
|
|
((pRTPHeader->cc - 1) * sizeof(pRTPHeader->csrc[0])));
|
|
}
|
|
|
|
// Packet received sequentially in order (difference
|
|
// of 1, or -1 if wraparound) save new current
|
|
// sequence number
|
|
RRCMws.ntohs (pSSRC->RTPsd, pRTPHeader->seq,
|
|
(unsigned short *)&pSSRC->xmtInfo.dwCurXmtSeqNum);
|
|
|
|
// Calculate JITTER
|
|
calculateJitter (pRTPHeader, pSSRC);
|
|
|
|
// unlock access to data
|
|
LeaveCriticalSection (&pSSRC->critSect);
|
|
}
|
|
|
|
IN_OUT_STR ("RTP : Exit updateRTPStats()\n");
|
|
|
|
return (RRCM_NoError);
|
|
}
|
|
|
|
|
|
// [EOF]
|
|
|