www.pudn.com > rtp11.rar > rtcp.c
#ifdef __cplusplus
extern "C" {
#endif
/*
NOTICE:
This document contains information that is proprietary to RADVISION LTD.
No part of this publication may be reproduced in any form whatsoever without
written prior approval by RADVISION LTD.
RADVISION LTD. reserves the right to revise this publication and make changes
without obligation to notify any person of such revisions or changes.
*/
/****************************************************************************
rtcp.c -- RTCP implementation.
Abstract: The main RTCP module file.
Platforms: All.
****************************************************************************/
#include
#include
#include
#include
#include "buffer.h"
#include "bitfield.h"
#include
#include
#include
#include
#include
#include "rtcp.h"
#define MAXSDES 255
#define MAXRTPSESSIONS 10
#define MAXRTPSESSIONMEMBERS 50
#define MAXRTCPPACKET 1470
#define MAXIPS 20
#define MAX_DROPOUT 3000
#define MAX_MISORDER 100
#define MIN_SEQUENTIAL 2
#define RTP_SEQ_MOD 0x10000
#define ALIGNMENT 0x10
/* RTCP header bit locations - see the standard */
#define HEADER_V 30 /* version */
#define HEADER_P 29 /* padding */
#define HEADER_RC 24 /* reception report count */
#define HEADER_PT 16 /* packet type */
#define HEADER_len 0 /* packet length in 32-bit words */
/* RTCP header bit field lengths - see the standard */
#define HDR_LEN_V 2 /* version */
#define HDR_LEN_P 1 /* padding */
#define HDR_LEN_RC 5 /* reception report count */
#define HDR_LEN_PT 8 /* packet type */
#define HDR_LEN_len 16 /* packet length in 32-bit words */
/* used to overcome byte-allignment issues */
#define SIZEOF_RTCPHEADER (sizeof(UINT32) * 2)
#define SIZEOF_SR (sizeof(UINT32) * 5)
#define SIZEOF_RR (sizeof(UINT32) * 6)
#define SIZEOF_SDES(sdes) (((sdes).length + 6) & 0xfc)
/* initial bit field value for RTCP headers: V=2,P=0,RC=0,PT=0,len=0 */
#define RTCP_HEADER_INIT 0x80000000
typedef enum {
RTCP_SR = 200, /* sender report */
RTCP_RR = 201, /* receiver report */
RTCP_SDES = 202, /* source description items */
RTCP_BYE = 203, /* end of participation */
RTCP_APP = 204 /* application specific */
} rtcpType;
typedef enum {
RTCP_SDES_END = 0,
RTCP_SDES_CNAME = 1,
RTCP_SDES_NAME = 2,
RTCP_SDES_EMAIL = 3,
RTCP_SDES_PHONE = 4,
RTCP_SDES_LOC = 5,
RTCP_SDES_TOOL = 6,
RTCP_SDES_NOTE = 7,
RTCP_SDES_PRIV = 8
} rtcpSDesType;
typedef struct
{
UINT32 msdw;
UINT32 lsdw;
} RV_UINT64;
typedef struct
{
RV_UINT64 tNNTP;
UINT32 tRTP;
UINT32 nPackets;
UINT32 nBytes;
} rtcpSR;
typedef struct
{
UINT32 ssrc;
UINT32 bfLost; /* 8Bit fraction lost and 24 bit cumulative lost */
UINT32 nExtMaxSeq;
UINT32 nJitter;
UINT32 tLSR;
UINT32 tDLSR;
} rtcpRR;
typedef struct {
UINT16 max_seq; /* highest seq. number seen */
UINT32 cycles; /* shifted count of seq. number cycles */
UINT32 base_seq; /* base seq number */
UINT32 bad_seq; /* last 'bad' seq number + 1 */
UINT32 probation; /* sequ. packets till source is valid */
UINT32 received; /* packets received */
UINT32 expected_prior; /* packet expected at last interval */
UINT32 received_prior; /* packet received at last interval */
UINT32 transit; /* relative trans time for prev pkt */
UINT32 jitter; /* estimated jitter */
/* ... */
} rtpSource;
typedef struct
{
UINT8 type;
UINT8 length;
char value[MAXSDES + 1]; /* leave a place for an asciiz */
} rtcpSDES;
typedef struct
{
int invalid;
BOOL active;
rtpSource src;
UINT32 ssrc;
UINT32 tLSRmyTime;
rtcpSR eSR;
rtcpRR eToRR;
rtcpRR eFromRR;
rtcpSDES eCName;
} rtcpInfo;
typedef struct
{
UINT32 bits;
UINT32 ssrc;
} rtcpHeader;
typedef struct
{
BOOL active;
int collision;
UINT32 ssrc;
UINT32 timestamp;
rtcpSR eSR;
rtcpSDES eCName;
} rtcpMyInfo;
typedef struct
{
BOOL isAllocated;
int socket;
rtcpMyInfo myInfo;
UINT32 ip;
UINT16 port;
HTI tElem;
HMEI hMutex; /* Mutex handle*/
rtcpInfo *participantsArray;
int sessionMembers;
int maxSessionMembers;
/*h.e 30.04.01*/
LPRTCPEVENTHANDLER rtcpRecvCallback;
void* haRtcp;
/*==*/
} rtcpSession;
static UINT32 rtcpInitialized = 0;
static UINT32 localIP;
static UINT32 myIPs[MAXIPS];
#ifndef RV_USE_R0
static HSTIMER hst;
#endif
#define reduceNNTP(a) (((a).msdw<<16)+((a).lsdw>>16))
#define W32Len(l) ((l + 3) / 4) /* length in 32-bit words */
/* local functions */
static RV_UINT64 getNNTPTime(void);
static void RVCALLCONV rtcpTimerCallback(void* key);
static void rtcpLiCallback (int socket, liEvents event, int error, void *context);
static UINT32 ipBindTo=LI_ADDR_ANY;
static BOOL isMyIP(UINT32 ip);
static void setSDES(rtcpSDesType type, rtcpSDES* sdes, BYTE *data,
int length);
static void init_seq (rtpSource *s, UINT16 seq);
static int update_seq(rtpSource *s, UINT16 seq, UINT32 ts, UINT32 arrival);
static UINT32 getLost (rtpSource *s);
static UINT32 getJitter (rtpSource *s);
static UINT32 getSequence(rtpSource *s);
/*h.e 30.05.01*/
static UINT32 getSSRCfrom(BYTE *);
/*===*/
static rtcpHeader makeHeader(UINT32 ssrc, UINT8 count, rtcpType type,
UINT16 dataLen);
static rtcpInfo * findSSrc(rtcpSession *,UINT32);
static rtcpInfo *insertNewSSRC(rtcpSession *s, UINT32 ssrc);
int align(int addr, int alignVal)
{
return ((addr / alignVal) * alignVal + alignVal);
}
RVAPI
INT32 RVCALLCONV rtcpGetEnumFirst(
IN HRTCPSESSION hRTCP,
IN INT32 * ssrc);
RVAPI
INT32 RVCALLCONV rtcpGetEnumNext(
IN HRTCPSESSION hRTCP,
IN INT32 prev,
IN INT32 * ssrc);
RVAPI
INT32 RVCALLCONV rtcpProcessCompoundRTCPPacket(
IN HRTCPSESSION hRTCP,
IN OUT BUFFER * buf,
IN RV_UINT64 myTime);
RVAPI
INT32 RVCALLCONV rtcpProcessRTCPPacket(
IN rtcpSession * s,
IN BYTE * data,
IN INT32 dataLen,
IN rtcpType type,
IN INT32 reportCount,
IN RV_UINT64 myTime);
/* == Basic RTCP Functions == */
/*=========================================================================**
** == rtcpInit() == **
** **
** Initializes the RTCP module. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpInit(void)
{
int i,err;
UINT32 **ips;
#ifdef RV_USE_R0
HSTIMER hst;
#else
if (hst) return ERR_RTCP_GENERALERROR;
#endif
if ((err = liInit()) < 0)
return ERR_RTCP_GENERALERROR;
if (rtcpInitialized == 0)
{
if ((hst = mtimerInit(1024,(HAPPTIMER)myIPs)) == NULL)
{
liEnd();
return ERR_RTCP_GENERALERROR;
}
ips = liGetHostAddrs();
if (!ips)
{
liEnd();
mtimerEnd(hst);
hst = NULL;
return ERR_RTCP_GENERALERROR;
}
localIP = liConvertIp((char*)"127.0.0.1");
for (i=0; ips[i]; i++)
myIPs[i] = *(ips[i]);
}
liThreadAttach(NULL);
rtcpInitialized++;
return 0;
}
/*=========================================================================**
** == rtcpInitEx() == **
** **
** Initializes the RTCP module. **
** Parameter: ip - ip address to 'bind' RTCP session **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpInitEx(UINT32 ip)
{
INT32 rc;
if ((rc=rtcpInit()) != ERR_RTCP_GENERALERROR)
ipBindTo=ip;
return rc;
}
/*=========================================================================**
** == rtcpEnd() == **
** **
** Shuts down the RTCP module. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpEnd(void)
{
int err = 0;
liThreadDetach(NULL);
err = liEnd();
#ifdef RV_USE_R0
mtimerEndByHandle((HAPPTIMER)myIPs);
#else
mtimerEnd(hst);
hst = NULL;
#endif
rtcpInitialized--;
return err;
}
/*h.e 30.04.01*/
RVAPI
INT32 RVCALLCONV rtcpSetRTCPRecvEventHandler(
IN HRTCPSESSION hRTCP,
IN LPRTCPEVENTHANDLER rtcpCallback,
IN void * context)
{
rtcpSession *s = (rtcpSession *)hRTCP;
s->rtcpRecvCallback=rtcpCallback;
s->haRtcp=context; /*context is Event to inform Ring3 about RTCP arrival*/
return 0;
}
/*===*/
/*=========================================================================**
** == rtcpGetAllocationSize() **
** **
** Calculates an allocation size for RTCP session **
** **
** PARAMETERS: **
** sessionMembers Maximum number of participants in the session **
** **
** **
** RETURNS: **
** If no error occurs, the function returns an allocation size for **
** RTCP session. Otherwise it returns NULL. **
** **
**=========================================================================*/
RVAPI
int RVCALLCONV rtcpGetAllocationSize(
IN int sessionMembers)
{
return sizeof(rtcpSession) + ALIGNMENT
+ sizeof(rtcpInfo) * sessionMembers + ALIGNMENT
+ mutexGetAllocationSize();
}
/************************************************************************************
* rtcpSetLocalAddress
* description: Set the local address to use for calls to rtcpOpenXXX functions.
* This parameter overrides the value given in rtcpInitEx() for all
* subsequent calls.
* input: ip - Local IP address to use
* output: none.
* return value: Non-negative value on success
* Negative value on failure
***********************************************************************************/
RVAPI
int RVCALLCONV rtcpSetLocalAddress(IN UINT32 ip)
{
ipBindTo = ip;
return 0;
}
/*=========================================================================**
** == rtcpOpenFrom() == **
** **
** Opens a new RTCP session in provided buffer. **
** **
** PARAMETERS: **
** ssrc The synchronization source value for the RTCP session. **
** **
** port The UDP port number to be used for the RTCP session. **
** **
** cname A unique name representing the source of the RTP data. **
** Must not be NULL. **
** maxSessionMembers Maximum number of participants in the session **
** **
** buffer pointer to at least rtcpGetAllocationSize byte of memory **
** **
** bufferSize size of the buffer **
** **
** RETURNS: **
** If no error occurs, the function returns a handle for the new **
** RTCP session. Otherwise it returns NULL. **
** **
** COMMENTS: **
** ----------------------------------------------- **
** buffer | rtcpSession |X| participantsArray |X| "mutex" | **
** ----------------------------------------------- **
** X - alignment area **
**=========================================================================*/
RVAPI
HRTCPSESSION RVCALLCONV rtcpOpenFrom(
IN UINT32 ssrc,
IN UINT16 port,
IN char * cname,
IN int maxSessionMembers,
IN void * buffer,
IN int bufferSize)
{
rtcpSession* s=(rtcpSession*)buffer;
int allocSize=rtcpGetAllocationSize(maxSessionMembers);
int participantsArrayOfs;
int mutexSectionOfs;
if (!cname || strlen(cname) > MAXSDES || allocSize > bufferSize)
{
return NULL;
}
memset(buffer, 0, allocSize);
s->sessionMembers = 0;
s->maxSessionMembers = maxSessionMembers;
s->rtcpRecvCallback = NULL;
s->isAllocated=FALSE;
participantsArrayOfs = align (sizeof(rtcpSession), ALIGNMENT);
s->participantsArray = (rtcpInfo *) ((char *)s + participantsArrayOfs);
s->participantsArray[0].ssrc = s->myInfo.ssrc = ssrc;
s->socket = liOpen(ipBindTo,port,LI_UDP);
if (s->socket==RVERROR)
{
return NULL;
}
s->tElem = (HTI)RVERROR;
/* initialize "mutex" in supplied buffer*/
mutexSectionOfs = participantsArrayOfs + align(sizeof(rtcpInfo)*maxSessionMembers, ALIGNMENT);
s->hMutex = mutexInitFrom( (void *) ((char *)s + mutexSectionOfs) );
#ifndef UNDER_CE
if (!s->hMutex)
{
return NULL;
}
#endif
liCallOn(s->socket,liEvRead,rtcpLiCallback,s);
setSDES(RTCP_SDES_CNAME, &(s->myInfo.eCName), (BYTE*)cname, (int)strlen(cname));
return (HRTCPSESSION)s;
}
/*=========================================================================**
** == rtcpOpen() == **
** **
** Opens a new RTCP session. **
** **
** PARAMETERS: **
** ssrc The synchronization source value for the RTCP session. **
** **
** port The UDP port number to be used for the RTCP session. **
** **
** cname A unique name representing the source of the RTP data. **
** Must not be NULL. **
** **
** RETURNS: **
** If no error occurs, the function returns a handle for the new **
** RTCP session. Otherwise it returns NULL. **
** **
**=========================================================================*/
RVAPI
HRTCPSESSION RVCALLCONV rtcpOpen(
IN UINT32 ssrc,
IN UINT16 port,
IN char * cname)
{
rtcpSession* s;
int allocSize=rtcpGetAllocationSize(MAXRTPSESSIONMEMBERS);
s = (rtcpSession*)calloc(allocSize,1);
if (s==NULL)
return NULL;
if((rtcpSession*)rtcpOpenFrom(ssrc, port, cname, MAXRTPSESSIONMEMBERS, (void*)s, allocSize)==NULL)
{
free(s);
return NULL;
}
s->isAllocated=TRUE;
return (HRTCPSESSION)s;
}
/*=========================================================================**
** == rtcpClose() == **
** **
** Closes an RTCP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpClose(
IN HRTCPSESSION hRTCP)
{
rtcpSession *s = (rtcpSession *)hRTCP;
if (s->tElem!=(HTI)RVERROR)
{
#ifdef RV_USE_R0
mtimerResetByHandle((HAPPTIMER)myIPs, s->tElem);
#else
mtimerReset(hst,s->tElem);
#endif
s->tElem=(HTI)RVERROR;
}
liClose(s->socket);
mutexEnd(s->hMutex);
/* free memory allocated for rtcpSession */
if (s->isAllocated)
free(s);
return 0;
}
/*=========================================================================**
** == rtcpSetRemoteAddress() == **
** **
** Defines the address of the remote peer or of the multicast groop. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** ip The IP address to which the RTCP packets will be sent. **
** **
** port The UDP port to which the RTCP packets should be sent. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
void RVCALLCONV rtcpSetRemoteAddress(
IN HRTCPSESSION hRTCP, /* RTCP Session Opaque Handle */
IN UINT32 ip, /* target ip address */
IN UINT16 port) /* target UDP port */
{
rtcpSession *s = (rtcpSession *)hRTCP;
s->ip = ip;
s->port = port;
if (ip&&port)
{
if (s->tElem==(HTI)RVERROR)
#ifdef RV_USE_R0
s->tElem =mtimerSetByHandle((HAPPTIMER)myIPs, rtcpTimerCallback, s, 5000/* should be calculated*/);
#else
s->tElem =mtimerSet(hst, rtcpTimerCallback, s, 5000/* should be calculated*/);
#endif
}
else
{
rtcpStop(hRTCP);
}
}
/*=========================================================================**
** == rtcpStop() == **
** **
** Stop RTCP transmisions . **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpStop(
IN HRTCPSESSION hRTCP) /* RTCP Session Opaque Handle */
{
rtcpSession *s = (rtcpSession *)hRTCP;
s->ip = 0;
s->port = 0;
/* Clear the list*/
memset(s->participantsArray,0,(sizeof(rtcpInfo))*(s->sessionMembers));
s->myInfo.collision = 0;
s->myInfo.active = 0;
s->myInfo.timestamp = 0;
memset(&(s->myInfo.eSR),0,sizeof(s->myInfo.eSR));
if (s->tElem!=(HTI)RVERROR)
{
#ifdef RV_USE_R0
mtimerResetByHandle((HAPPTIMER)myIPs,s->tElem);
#else
mtimerReset(hst,s->tElem);
#endif
s->tElem=(HTI)RVERROR;
}
return 0;
}
/*=========================================================================**
** == rtcpRTPPacketRecv() == **
** **
** Informs the RTCP session that a packet was received in the **
** corresponding RTP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** localTimestamp The local timestamp for the received packet. **
** **
** timestamp The RTP timestamp from the received packet. **
** **
** sequence The packet sequence number. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpRTPPacketRecv(
IN HRTCPSESSION hRTCP,
IN UINT32 ssrc,
IN UINT32 localTimestamp,
IN UINT32 myTimestamp,
IN UINT16 sequence)
{
rtcpSession *s = (rtcpSession *)hRTCP;
rtcpInfo info, *fInfo;
if (ssrc == s->myInfo.ssrc)
{
s->myInfo.collision = 1;
return ERR_RTCP_SSRCCOLLISION;
}
info.ssrc = ssrc;
/* See if we can find this source or not */
fInfo = findSSrc(s,ssrc);
#ifdef RTP_NOLOCKS
if (!fInfo) /* New source */
{
rtcpHeader head;
/* create an invalid record */
head = makeHeader(ssrc, 0, RTCP_RR, SIZEOF_RTCPHEADER);
/* we'll send it to ourselves to make sure we can handle the change
in the RTCP's database in multi-threaded environments without locking it. */
liUdpSend(s->socket, (UINT8 *)&head, (int)SIZEOF_RTCPHEADER, localIP,
liGetSockPort(s->socket));
}
#else /* RTP_NOLOCKS */
/* The new way of doing things: If we didn't find this SSRC, we lock the
RTCP database and search for the SSRC again. If we don't find it again - we
insert it to the list, and finally we unlock... */
if (!fInfo) /* New source */
{
/* this section is working with threads.*/
/* Lock the rtcp session.*/
mutexLock(s->hMutex);
/* check if the ssrc is exist*/
fInfo = findSSrc(s,ssrc);
if (!fInfo)
{
/* Still no SSRC - we should add it to the list ourselves */
fInfo = insertNewSSRC(s, ssrc);
}
/* unlock the rtcp session.*/
mutexUnlock(s->hMutex);
}
#endif /* RTP_NOLOCKS */
if (fInfo)
{
if (!fInfo->invalid)
{
fInfo->active = TRUE;
update_seq(&(fInfo->src), sequence, localTimestamp, myTimestamp);
}
}
return 0;
}
/*=========================================================================**
** == rtcpRTPPacketSent() == **
** **
** Informs the RTCP session that a packet was sent in the corresponding **
** RTP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** bytes The number of bytes in the sent packet. **
** **
** timestamp The RTP timestamp from the received packet. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpRTPPacketSent(
IN HRTCPSESSION hRTCP,
IN INT32 bytes,
IN UINT32 timestamp)
{
rtcpSession *s = (rtcpSession *)hRTCP;
s->myInfo.active = TRUE;
s->myInfo.eSR.nPackets++;
s->myInfo.eSR.nBytes += bytes;
s->myInfo.eSR.tNNTP = getNNTPTime();
s->myInfo.eSR.tRTP = timestamp;
if (s->myInfo.collision)
return ERR_RTCP_SSRCCOLLISION;
return 0;
}
/*=========================================================================**
** == rtcpGetPort() == **
** **
** Gets the UDP port of an RTCP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** RETURNS: **
** A non-negative value upon success, or a negative integer error **
** code. **
** **
**=========================================================================*/
RVAPI
UINT16 RVCALLCONV rtcpGetPort(
IN HRTCPSESSION hRTCP)
{
rtcpSession *s = (rtcpSession *)hRTCP;
UINT16 sockPort;
sockPort=liGetSockPort(s->socket);
return sockPort;
}
/* == ENDS: Basic RTCP Functions == */
/* == Accessory RTCP Functions == */
/*=========================================================================**
** == rtcpCheckSSRCCollision() == **
** **
** Checks for SSRC collisions in the RTCP session and the corresponding **
** RTP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** RETURNS: **
** TRUE is returned if a collision was detected, otherwise FALSE. **
** **
**=========================================================================*/
RVAPI
BOOL RVCALLCONV rtcpCheckSSRCCollision(
IN HRTCPSESSION hRTCP)
{
rtcpSession *s = (rtcpSession *)hRTCP;
return (s->myInfo.collision != 0);
}
/*=========================================================================**
** == rtcpEnumParticipants() == **
** **
** Provides information about in the RTCP session and the corresponding **
** RTP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** enumerator A pointer to the function that will be called once per **
** SSRC in the session. **
** **
** RETURNS: **
** If the enumeration process was stopped by the enumerator, the **
** function returns FALSE, otherwise TRUE. **
** **
** The prototype of the SSRC enumerator is as follows: **
** **
** BOOL **
** SSRCENUM( **
** IN HRTPSESSION hTRCP, **
** IN UINT32 ssrc **
** ); **
** **
** The parameters passed to the enumerator are as follows: **
** hRTCP The handle of the RTCP session. **
** **
** ssrc A synchronization source that participates in the **
** session. **
** **
** The enumerator should return FALSE if it wants the enumeration process **
** to continue. Returning TRUE will cause rtcpEnumParticipant() to **
** return immediately. **
** **
**=========================================================================*/
RVAPI
BOOL RVCALLCONV rtcpEnumParticipants(
IN HRTCPSESSION hRTCP,
IN LPSSRCENUM enumerator)
{
int elem, ssrc=0;
elem = rtcpGetEnumFirst(hRTCP, &ssrc);
while (elem >= 0)
{
if (enumerator(hRTCP, ssrc))
{
return FALSE;
}
elem = rtcpGetEnumNext(hRTCP, elem, &ssrc);
}
return TRUE;
}
/*=========================================================================**
** == rtcpGetSourceInfo() == **
** **
** Provides information about a particular synchronization source. **
** **
** TBD **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpGetSourceInfo(
IN HRTCPSESSION hRTCP,
IN UINT32 ssrc,
OUT RTCPINFO * info)
{
rtcpSession *s = (rtcpSession *)hRTCP;
rtcpInfo *fInfo, intInfo;
if (ssrc == s->myInfo.ssrc)
{
info->selfNode = TRUE;
info->sr.valid = s->myInfo.active;
info->sr.mNTPtimestamp = s->myInfo.eSR.tNNTP.msdw;
info->sr.lNTPtimestamp = s->myInfo.eSR.tNNTP.lsdw;
info->sr.timestamp = s->myInfo.eSR.tRTP;
info->sr.packets = s->myInfo.eSR.nPackets;
info->sr.octets = s->myInfo.eSR.nBytes;
strncpy(info->cname, s->myInfo.eCName.value, sizeof(info->cname));
return 0;
}
intInfo.ssrc = ssrc;
fInfo = findSSrc(s,ssrc);
if (fInfo)
{
info->selfNode = FALSE;
info->sr.valid = !fInfo->invalid;
info->sr.mNTPtimestamp = fInfo->eSR.tNNTP.msdw;
info->sr.lNTPtimestamp = fInfo->eSR.tNNTP.lsdw;
info->sr.timestamp = fInfo->eSR.tRTP;
info->sr.packets = fInfo->eSR.nPackets;
info->sr.octets = fInfo->eSR.nBytes;
info->rrFrom.valid = TRUE;
info->rrFrom.fractionLost = (fInfo->eFromRR.bfLost >> 24);
info->rrFrom.cumulativeLost = (fInfo->eFromRR.bfLost & 0xffffff);
info->rrFrom.sequenceNumber = fInfo->eFromRR.nExtMaxSeq;
info->rrFrom.jitter = fInfo->eFromRR.nJitter;
info->rrFrom.lSR = fInfo->eFromRR.tLSR;
info->rrFrom.dlSR = fInfo->eFromRR.tDLSR;
info->rrTo.valid = TRUE;
info->rrTo.fractionLost = (fInfo->eToRR.bfLost >> 24);
info->rrTo.cumulativeLost = (fInfo->eToRR.bfLost & 0xffffff);
info->rrTo.sequenceNumber = fInfo->eToRR.nExtMaxSeq;
info->rrTo.jitter = fInfo->eToRR.nJitter;
info->rrTo.lSR = fInfo->eToRR.tLSR;
info->rrTo.dlSR = fInfo->eToRR.tDLSR;
strncpy(info->cname, fInfo->eCName.value, sizeof(info->cname));
}
return (!fInfo) ? ERR_RTCP_ILLEGALSSRC : 0;
}
/*=========================================================================**
** == rtcpSetGroupAddress() == **
** **
** Specifies a multicast IP for an RTCP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** ip The multicast IP address for the RTCP session. **
** SSRC in the session. **
** **
** RETURNS: **
** If the enumeration process was stopped by the enumerator, the **
** function returns FALSE, otherwise TRUE. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpSetGroupAddress(
IN HRTCPSESSION hRTCP,
IN UINT32 ip)
{
rtcpSession *s = (rtcpSession *)hRTCP;
liJoinMulticastGroup(s->socket, ip, LI_ADDR_ANY);
return 0;
}
/*=========================================================================**
** == rtcpGetSSRC() == **
** **
** Returns the synchronization source value for an RTCP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** RETURNS: **
** The synchronization source value for the specified RTCP session. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpGetSSRC(
IN HRTCPSESSION hRTCP)
{
rtcpSession *s = (rtcpSession *)hRTCP;
return s->myInfo.ssrc;
}
/*=========================================================================**
** == rtcpSetSSRC() == **
** **
** Changes the synchronization source value for an RTCP session. **
** **
** PARAMETERS: **
** hRTCP The handle of the RTCP session. **
** **
** ssrc A synchronization srouce value for the RTCP session. **
** **
** RETURNS: **
** If the enumeration process was stopped by the enumerator, the **
** function returns FALSE, otherwise TRUE. **
** **
**=========================================================================*/
RVAPI
INT32 RVCALLCONV rtcpSetSSRC(
IN HRTCPSESSION hRTCP,
IN UINT32 ssrc)
{
rtcpSession *s = (rtcpSession *)hRTCP;
s->myInfo.ssrc = ssrc;
s->myInfo.collision = 0;
return 0;
}
/* == ENDS: Accessory RTCP Functions == */
/* == Internal RTCP Functions == */
RVAPI
INT32 RVCALLCONV rtcpGetEnumFirst(
IN HRTCPSESSION hRTCP,
IN INT32 * ssrc)
{
return rtcpGetEnumNext(hRTCP, -1, ssrc);
}
RVAPI
INT32 RVCALLCONV rtcpGetEnumNext(
IN HRTCPSESSION hRTCP,
IN INT32 prev,
IN INT32 * ssrc)
{
rtcpSession *s = (rtcpSession *)hRTCP;
rtcpInfo* info;
INT32 index, doAgain = 1;
if (prev < 0)
index = 0;
else
index = prev+1;
while ((doAgain == 1)&&(index < s->sessionMembers))
{
info = &s->participantsArray[index];
if (!info->invalid)
{
doAgain = 0;
*ssrc = info->ssrc;
}
else
{
index++;
}
}
if (index < s->sessionMembers)
return index;
else
return -1;
}
RVAPI
INT32 RVCALLCONV rtcpCreateRTCPPacket(
IN HRTCPSESSION hRTCP,
IN OUT BUFFER * buf)
{
rtcpSession *s = (rtcpSession *)hRTCP;
rtcpHeader head;
UINT32 allocated = 0;
BUFFER bufC;
rtcpType type = RTCP_SR;
int index;
if (buffValid(buf, SIZEOF_RTCPHEADER + SIZEOF_SR))
{
RV_UINT64 myTime = s->myInfo.eSR.tNNTP;
UINT8 cc = 0;
rtcpInfo *info;
allocated = SIZEOF_RTCPHEADER;
if (s->myInfo.active)
{
s->myInfo.active = FALSE;
bufC = buffCreate(&(s->myInfo.eSR), SIZEOF_SR);
buffAddToBuffer(buf, &bufC, allocated);
liConvertHeader2l(buf->buffer + allocated, 0, (int)W32Len(bufC.length));
allocated += SIZEOF_SR;
}
else
{
type = RTCP_RR;
}
index = 0;
while( index < s->sessionMembers)
{
info = &s->participantsArray[index];
if (info->active)
{
info->eToRR.bfLost = getLost (&(info->src));
info->eToRR.nJitter = getJitter (&(info->src));
info->eToRR.nExtMaxSeq = getSequence(&(info->src));
info->eToRR.tDLSR =
(info->tLSRmyTime) ?
(reduceNNTP(myTime)-info->tLSRmyTime) :
0;
bufC = buffCreate(&(info->eToRR), SIZEOF_RR);
if (buffAddToBuffer(buf, &bufC, allocated))
{
cc++;
if (cc == 32)
break;
liConvertHeader2l(buf->buffer + allocated, 0,
(int)W32Len(bufC.length));
allocated += SIZEOF_RR;
}
info->active = FALSE;
}
index++;
}
head = makeHeader(s->myInfo.ssrc, cc, type, (UINT16)allocated);
bufC = buffCreate(&head, SIZEOF_RTCPHEADER);
buffAddToBuffer(buf, &bufC, 0);
/* add an CNAME SDES packet to the compound packet */
if (buffValid(buf,
allocated + SIZEOF_RTCPHEADER + SIZEOF_SDES(s->myInfo.eCName)))
{
BUFFER sdes_buf;
/* 'sdes_buf' is inside the compound buffer 'buf' */
sdes_buf = buffCreate(buf->buffer + allocated,
(SIZEOF_RTCPHEADER + SIZEOF_SDES(s->myInfo.eCName)));
head = makeHeader(s->myInfo.ssrc, 1, RTCP_SDES,
(UINT16)sdes_buf.length);
memcpy(sdes_buf.buffer, (char *)&head, SIZEOF_RTCPHEADER);
memcpy(sdes_buf.buffer + SIZEOF_RTCPHEADER, &(s->myInfo.eCName),
SIZEOF_SDES(s->myInfo.eCName));
allocated += sdes_buf.length;
}
if (s->myInfo.collision == 1 &&
buffValid(buf, allocated + SIZEOF_RTCPHEADER))
{
head = makeHeader(s->myInfo.ssrc, 1, RTCP_BYE,
SIZEOF_RTCPHEADER);
bufC = buffCreate(&head, SIZEOF_RTCPHEADER);
buffAddToBuffer(buf, &bufC, allocated);
s->myInfo.collision = 2;
allocated += SIZEOF_RTCPHEADER;
}
}
buf->length = allocated;
return 0;
}
RVAPI
INT32 RVCALLCONV rtcpProcessCompoundRTCPPacket(
IN HRTCPSESSION hRTCP,
IN OUT BUFFER * buf,
IN RV_UINT64 myTime)
{
rtcpSession *s = (rtcpSession *)hRTCP;
rtcpHeader *head;
BYTE *currPtr = buf->buffer, *dataPtr, *compoundEnd;
int hdr_count, hdr_len;
rtcpType hdr_type;
compoundEnd = buf->buffer + buf->length;
while (currPtr < compoundEnd)
{
if ((compoundEnd + 1 - currPtr) < 1)
{
return ERR_RTCP_ILLEGALPACKET;
}
head = (rtcpHeader*)(currPtr);
liConvertHeader2l(currPtr, 0, 1);
hdr_count = bitfieldGet(head->bits, HEADER_RC, HDR_LEN_RC);
hdr_type = (rtcpType)bitfieldGet(head->bits, HEADER_PT, HDR_LEN_PT);
hdr_len = sizeof(UINT32) *
(bitfieldGet(head->bits, HEADER_len, HDR_LEN_len));
if ((compoundEnd - currPtr) < hdr_len)
{
return ERR_RTCP_ILLEGALPACKET;
}
dataPtr = (BYTE *)head + sizeof(UINT32);
rtcpProcessRTCPPacket(s, dataPtr, hdr_len, hdr_type, hdr_count,
myTime);
currPtr += hdr_len + sizeof(UINT32);
}
return 0;
}
RVAPI
INT32 RVCALLCONV rtcpProcessRTCPPacket(
IN rtcpSession * s,
IN BYTE * data,
IN INT32 dataLen,
IN rtcpType type,
IN INT32 reportCount,
IN RV_UINT64 myTime)
{
unsigned scanned = 0;
rtcpInfo info, *fInfo=NULL;
if (dataLen == 0)
return 0;
switch(type)
{
case RTCP_SR:
case RTCP_RR:
{
liConvertHeader2l(data, 0, 1);
info.ssrc = *(UINT32 *)(data);
scanned = sizeof(UINT32);
if (info.ssrc == s->myInfo.ssrc)
{
s->myInfo.collision = 1;
return ERR_RTCP_SSRCCOLLISION;
}
fInfo = findSSrc(s,info.ssrc);
if (!fInfo) /* New source */
{
/* insert the new source */
#ifndef RTP_NOLOCKS
mutexLock(s->hMutex);
#endif
fInfo = insertNewSSRC(s, *(UINT32 *)data);
#ifndef RTP_NOLOCKS
mutexUnlock(s->hMutex);
#endif
}
break;
}
default:
break;
}
/* process the information */
switch(type)
{
case RTCP_SR:
{
liConvertHeader2l(data + scanned, 0, W32Len(sizeof(rtcpSR)));
if (fInfo)
{
fInfo->eSR = *(rtcpSR *)(data + scanned);
fInfo->eToRR.tLSR = reduceNNTP(fInfo->eSR.tNNTP);
fInfo->tLSRmyTime = reduceNNTP(myTime);
}
scanned += SIZEOF_SR;
}
/* fall into RR */
case RTCP_RR:
{
if (fInfo)
{
int i;
rtcpRR* rr = (rtcpRR*)(data + scanned);
liConvertHeader2l(data + scanned, 0,
reportCount * W32Len(sizeof(rtcpRR)));
for (i=0; i < reportCount; i++)
{
if (rr[i].ssrc == s->myInfo.ssrc)
{
fInfo->eFromRR = rr[i];
break;
}
}
}
break;
}
case RTCP_SDES:
{
int i;
rtcpSDES *sdes;
for (i = 0; i < reportCount; i++)
{
liConvertHeader2l(data + scanned, 0, 1);
info.ssrc = *(UINT32 *)(data + scanned);
sdes = (rtcpSDES *)(data + scanned + sizeof(info.ssrc));
fInfo = findSSrc(s,info.ssrc);
if (fInfo)
{
switch(sdes->type)
{
case RTCP_SDES_CNAME:
memcpy(&(fInfo->eCName), sdes,
SIZEOF_SDES(*sdes));
fInfo->eCName.value[sdes->length] = 0;
break;
/* known SDES types that are not handled:
case RTCP_SDES_END:
case RTCP_SDES_NAME:
case RTCP_SDES_EMAIL:
case RTCP_SDES_PHONE:
case RTCP_SDES_LOC:
case RTCP_SDES_TOOL:
case RTCP_SDES_NOTE:
case RTCP_SDES_PRIV:
break;
*/
}
}
scanned += SIZEOF_SDES(*sdes) + sizeof(UINT32);
}
break;
}
case RTCP_BYE:
{
int i;
for (i = 0; i < reportCount; i++)
{
liConvertHeader2l(data + scanned, 0, 1);
info.ssrc = *(UINT32 *)(data + scanned);
scanned += sizeof(info.ssrc);
fInfo = findSSrc(s,info.ssrc);
if (fInfo)
{
/* We don't really delete this SSRC, we just mark it as invalid */
fInfo->invalid = TRUE;
fInfo->ssrc = 0;
}
}
break;
}
case RTCP_APP:
break;
}
return 0;
}
static void RVCALLCONV rtcpTimerCallback(void* key)
{
rtcpSession* s = (rtcpSession*)key;
UINT32 buffer[MAXRTCPPACKET/sizeof(UINT32)+1];
BUFFER buf;
buf = buffCreate(buffer,MAXRTCPPACKET);
/* s->tElem =mtimerSet(hst,rtcpTimerCallback,s,5000 should be calculated); */
rtcpCreateRTCPPacket((HRTCPSESSION)s, &buf);
liUdpSend(s->socket, buf.buffer, (int)buf.length, s->ip, s->port);
}
static void rtcpLiCallback(int socket, liEvents event, int error,
void *context)
{
rtcpSession* s = (rtcpSession*)context;
UINT32 ip;
UINT16 port;
UINT32 buffer[MAXRTCPPACKET/sizeof(UINT32)+1];
BUFFER buf;
if(socket || event || error);
if (s == NULL)
return;
buf = buffCreate(buffer, MAXRTCPPACKET);
buf.length = liUdpRecv(s->socket, buf.buffer, (int)buf.length, &ip, &port);
if (port != liGetSockPort(s->socket) || !isMyIP(ip))
{
INT32 res=rtcpProcessCompoundRTCPPacket((HRTCPSESSION)s, &buf, getNNTPTime());
if ((res==0) && (s->rtcpRecvCallback != NULL))
{
UINT32 ssrc=getSSRCfrom(buf.buffer);
s->rtcpRecvCallback((HRTCPSESSION)s, s->haRtcp, ssrc);
}
}
}
static RV_UINT64 getNNTPTime(void)
{
const UINT32 from1900Till1970 = (UINT32)2208988800ul;
static UINT32 startTime = 0;
static UINT32 startMilliTime = 0;
RV_UINT64 nntpTime;
if (!startTime)
{
startTime = (UINT32)time(NULL) + from1900Till1970;
startMilliTime = timerGetTimeInMilliseconds();
}
nntpTime.msdw = startTime;
nntpTime.lsdw = timerGetTimeInMilliseconds() - startMilliTime;
nntpTime.msdw += nntpTime.lsdw/1000;
nntpTime.lsdw %= 1000;
nntpTime.lsdw *= 4294967;
return nntpTime;
}
static BOOL isMyIP(UINT32 ip)
{
int i;
for (i=0; myIPs[i]; i++)
{
if (ip == myIPs[i])
{
return TRUE;
}
}
return FALSE;
}
static void setSDES(rtcpSDesType type, rtcpSDES* sdes, BYTE *data, int length)
{
sdes->type = (unsigned char)type;
sdes->length = (unsigned char)length;
memcpy(sdes->value, data, length);
memset(sdes->value+length, 0, 4-((length+2)%sizeof(UINT32)));
}
static void init_seq(rtpSource *s, UINT16 seq)
{
s->base_seq = seq;
s->max_seq = seq;
s->bad_seq = RTP_SEQ_MOD + 1;
s->cycles = 0;
s->received = 0;
s->received_prior = 0;
s->expected_prior = 0;
}
static int update_seq(rtpSource *s, UINT16 seq, UINT32 ts, UINT32 arrival)
{
UINT16 udelta = (UINT16)(seq - s->max_seq);
if (s->probation)
{
if (seq == s->max_seq + 1)
{
s->probation--;
s->max_seq = seq;
if (s->probation == 0)
{
init_seq(s, seq);
s->received++;
return 1;
}
}
else
{
s->probation = MIN_SEQUENTIAL - 1;
s->max_seq = seq;
}
return 0;
}
else if (udelta < MAX_DROPOUT)
{
if (seq < s->max_seq) s->cycles += RTP_SEQ_MOD;
s->max_seq = seq;
}
else if (udelta <= RTP_SEQ_MOD - MAX_MISORDER)
{
if (seq == s->bad_seq)
{
init_seq(s, seq);
}
else
{
s->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
return 0;
}
}
else
{
/* duplicate or reordered packet */
}
{
INT32 transit = (INT32)(arrival - ts);
INT32 d = (INT32)(transit - s->transit);
s->transit = transit;
if (d < 0) d = -d;
s->jitter += d - ((s->jitter + 8) >> 4);
}
s->received++;
return 1;
}
/*=========================================================================**
** == makeHeader() == **
** **
** Creates an RTCP packet header. **
** **
** PARAMETERS: **
** ssrc A synchronization source value for the RTCP session. **
** **
** count A count of sender and receiver reports in the packet. **
** **
** type The RTCP packet type. **
** **
** dataLen The length of the data in the packet buffer, in **
** octets, including the size of the header. **
** **
** RETURNS: **
** The function returns a header with the appropriate parameters. **
** **
**=========================================================================*/
static rtcpHeader makeHeader(UINT32 ssrc, UINT8 count, rtcpType type,
UINT16 dataLen)
{
rtcpHeader header;
header.ssrc = ssrc;
header.bits = RTCP_HEADER_INIT;
header.bits = bitfieldSet(header.bits, count, HEADER_RC, HDR_LEN_RC);
header.bits = bitfieldSet(header.bits, type, HEADER_PT, HDR_LEN_PT);
header.bits = bitfieldSet(header.bits, W32Len(dataLen) - 1,
HEADER_len, HDR_LEN_len);
liConvertHeader2h((UINT8 *)&header, 0, W32Len(SIZEOF_RTCPHEADER));
return header;
}
static UINT32 getLost(rtpSource *s)
{
UINT32 extended_max;
UINT32 expected;
INT32 received_interval;
INT32 expected_interval;
INT32 lost;
INT32 lost_interval;
UINT8 fraction;
extended_max = s->cycles + s->max_seq;
expected = extended_max - s->base_seq + 1;
lost = expected - s->received;
expected_interval = expected - s->expected_prior;
s->expected_prior = expected;
received_interval = s->received - s->received_prior;
s->received_prior = s->received;
lost_interval = expected_interval - received_interval;
if (expected_interval == 0 || lost_interval <= 0)
fraction = 0;
else
fraction = (UINT8)((lost_interval << 8) / expected_interval);
return (fraction << 24) + lost;
}
static UINT32 getJitter(rtpSource *s)
{
return s->jitter >> 4;
}
static UINT32 getSequence(rtpSource *s)
{
return s->max_seq + s->cycles;
}
static UINT32 getSSRCfrom(BYTE *head)
{
BYTE *ssrcPtr = (BYTE *)head + sizeof(UINT32);
return *(UINT32 *)(ssrcPtr);
}
static rtcpInfo *findSSrc(rtcpSession *s, UINT32 ssrc)
{
int index = 0;
BOOL doAgain = TRUE;
rtcpInfo *pInfo;
if (s == NULL)
return NULL;
/* Look for the given SSRC */
while ((doAgain) && (index < s->sessionMembers))
{
if (s->participantsArray[index].ssrc == ssrc)
doAgain = FALSE;
else
index ++;
}
if (index < s->sessionMembers )
pInfo = &s->participantsArray[index];
else
pInfo = NULL;
return pInfo;
}
static rtcpInfo *insertNewSSRC(rtcpSession *s, UINT32 ssrc)
{
rtcpInfo* pInfo = NULL;
int index;
if (s->sessionMembers >= s->maxSessionMembers)
{
/* We've got too many - see if we can remove some old ones */
index = 0;
while ((index < s->sessionMembers) &&
(s->participantsArray[index].invalid && s->participantsArray[index].ssrc == 0))
index++;
}
else
{
/* Add it as a new one to the list */
index = s->sessionMembers;
s->sessionMembers++;
}
if (index < s->sessionMembers)
{
/* Got a place for it ! */
pInfo = &s->participantsArray[index];
memset(pInfo, 0, sizeof(rtcpInfo));
pInfo->ssrc = ssrc;
pInfo->eToRR.ssrc = ssrc;
pInfo->active = FALSE;
pInfo->src.probation = MIN_SEQUENTIAL - 1;
}
return pInfo;
}
/* == ENDS: Internal RTCP Functions == */
#ifdef __cplusplus
}
#endif