Windows2003-3790/enduser/netmeeting/av/rrcm/rtp/rtp_stat.cpp

478 lines
15 KiB
C++
Raw Normal View History

2001-01-01 00:00:00 +01:00
/*----------------------------------------------------------------------------
* 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]