[asterisk-commits] branch oej/rtcp r9290 - in /team/oej/rtcp: ./ rtp.c

asterisk-commits at lists.digium.com asterisk-commits at lists.digium.com
Thu Feb 9 07:59:13 MST 2006


Author: oej
Date: Thu Feb  9 08:59:11 2006
New Revision: 9290

URL: http://svn.digium.com/view/asterisk?rev=9290&view=rev
Log:
Patch from issue #2863 - implement RTCP 

Modified:
    team/oej/rtcp/   (props changed)
    team/oej/rtcp/rtp.c

Propchange: team/oej/rtcp/
------------------------------------------------------------------------------
    svnmerge-integrated = /trunk:1-9276

Modified: team/oej/rtcp/rtp.c
URL: http://svn.digium.com/view/asterisk/team/oej/rtcp/rtp.c?rev=9290&r1=9289&r2=9290&view=diff
==============================================================================
--- team/oej/rtcp/rtp.c (original)
+++ team/oej/rtcp/rtp.c Thu Feb  9 08:59:11 2006
@@ -58,6 +58,16 @@
 #include "asterisk/utils.h"
 
 #define MAX_TIMESTAMP_SKEW	640
+#define RTP_SEQ_MOD     (1<<16) /*A sequence number can't be more than 16 bits */
+#define RTCP_DEFAULT_INTERVALMS   5000 /* Default milli-seconds between RTCP reports we send */
+#define RTCP_MIN_INTERVALMS       500 /* Min milli-seconds between RTCP reports we send */
+#define RTCP_MAX_INTERVALMS       60000 /* Max milli-seconds between RTCP reports we send */
+#define RTCP_PT_FUR     192
+#define RTCP_PT_SR      200
+#define RTCP_PT_RR      201
+#define RTCP_PT_SDES    202
+#define RTCP_PT_BYE     203
+#define RTCP_PT_APP     204
 
 #define RTP_MTU		1200
 
@@ -68,10 +78,17 @@
 static int rtpstart = 0;		/*!< First port for RTP sessions (set in rtp.conf) */
 static int rtpend = 0;			/*!< Last port for RTP sessions (set in rtp.conf) */
 static int rtpdebug = 0;		/*!< Are we debugging? */
+static int rtcpdebug = 0;		/*!< Are we debugging RTCP? */
+static int rtcpstats = 0;		/*!< Are we debugging RTCP? */
+static int rtcpinterval = RTCP_DEFAULT_INTERVALMS; /*!< Time between rtcp reports in millisecs */
 static struct sockaddr_in rtpdebugaddr;	/*!< Debug packets to/from this host */
+static struct sockaddr_in rtcpdebugaddr;	/*!< Debug RTCP packets to/from this host */
 #ifdef SO_NO_CHECK
 static int nochecksums = 0;
 #endif
+
+/* Forward declarations */
+int ast_rtcp_write(void *data);
 
 /*! \brief The value of each payload format mapping: */
 struct rtpPayloadType {
@@ -93,12 +110,23 @@
 	struct ast_frame f;
 	unsigned char rawdata[8192 + AST_FRIENDLY_OFFSET];
 	unsigned int ssrc;		/*!< Synchronization source, RFC 3550, page 10. */
+	unsigned int themssrc;			/* Their SSRC */
 	unsigned int lastts;
 	unsigned int lastdigitts;
 	unsigned int lastrxts;
 	unsigned int lastividtimestamp;
 	unsigned int lastovidtimestamp;
 	unsigned int lasteventseqn;
+	int lastrxseqno;                /* Last received sequence number */
+	unsigned short seedrxseqno;     /* What sequence number did they start with?*/
+	unsigned int seedrxts;          /* What RTP timestamp did they start with? */
+	unsigned int rxcount;           /* How many packets have we received? */
+	unsigned int rxoctetcount;      /* How many octets have we received? should be rxcount *160*/
+	unsigned int txcount;           /* How many packets have we sent? */
+	unsigned int txoctetcount;      /* How many octets have we sent? (txcount*160)*/
+	unsigned int cycles;            /* Shifted count of sequence number cycles */
+	double rxjitter;                /* Interarrival jitter at the moment */
+	double rxtransit;               /* Relative transit time for previous packet */
 	unsigned int lasteventendseqn;
 	int lasttxformat;
 	int lastrxformat;
@@ -110,6 +138,8 @@
 	struct sockaddr_in them;	/*!< Socket representation of the remote endpoint. */
 	struct timeval rxcore;
 	struct timeval txcore;
+	double drxcore;                 /* The double representation of the first received packet */
+	struct timeval lastrx;          /* timeval when we last received a packet */
 	struct timeval dtmfmute;
 	struct ast_smoother *smoother;
 	int *ioid;
@@ -123,6 +153,7 @@
 	int rtp_lookup_code_cache_isAstFormat; /*!< a cache for the result of rtp_lookup_code(): */
 	int rtp_lookup_code_cache_code;
 	int rtp_lookup_code_cache_result;
+	int rtp_offered_from_local;
 	struct ast_rtcp *rtcp;
 };
 
@@ -140,10 +171,41 @@
 	int s;				/*!< Socket */
 	struct sockaddr_in us;		/*!< Socket representation of the local endpoint. */
 	struct sockaddr_in them;	/*!< Socket representation of the remote endpoint. */
+	unsigned int soc; /* What they told us */
+	unsigned int spc; /* What they told us */
+	unsigned int themrxlsr; /* The middle 32 bits of the NTP timestamp in the last received SR*/
+	struct timeval rxlsr;   /* Time when we got their last SR */
+	struct timeval txlsr;   /* Time when we sent or last SR*/
+	unsigned int expected_prior; /* no. packets in previous interval */
+	unsigned int received_prior; /* no. packets received in previous interval */
+	int schedid; /*Schedid returned from ast_sched_add() to schedule RTCP-transmissions*/
+	unsigned int rr_count; /* number of RRs we've sent, not including report blocks in SR's */
+	unsigned int sr_count; /* number of SRs we've sent */
+	unsigned int lastsrtxcount;     /* Transmit packet count when last SR sent */
+	double accumulated_transit; /* accumulated a-dlsr-lsr */
+	double rtt; /* Last reported rtt */
+	unsigned int reported_jitter; /* The contents of their last jitter entry in the RR */
+	unsigned int reported_lost; /* Reported lost packets in their RR */
+	char quality[AST_MAX_USER_FIELD];
+	double maxrxjitter;
+	double minrxjitter;
+	double maxrtt;
+	double minrtt;
+	int sendfur;
 };
 
 /*! \brief List of current sessions */
 static AST_LIST_HEAD_STATIC(protos, ast_rtp_protocol);
+
+void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw)
+{
+	unsigned int sec, usec, frac;
+	sec = tv.tv_sec + 2208988800u; /* Sec between 1900 and 1970 */
+	usec = tv.tv_usec;
+	frac = (usec << 12) + (usec << 8) - ((usec * 3650) >> 6);
+	*msw = sec;
+	*lsw = frac;
+}
 
 int ast_rtp_fd(struct ast_rtp *rtp)
 {
@@ -155,6 +217,15 @@
 	if (rtp->rtcp)
 		return rtp->rtcp->s;
 	return -1;
+}
+
+unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp)
+{
+	unsigned int interval;
+	/* TODO Do a more reasonable calculation on this one
+	* Look in RFC 3550 Section A.7 for an example*/
+	interval = rtcpinterval;
+	return interval;
 }
 
 void ast_rtp_set_data(struct ast_rtp *rtp, void *data)
@@ -214,6 +285,20 @@
 	}
 	return 1;
 }
+
+static inline int rtcp_debug_test_addr(struct sockaddr_in *addr)
+{
+	if (rtcpdebug == 0)
+		return 0;
+	if (rtcpdebugaddr.sin_addr.s_addr) {
+		if (((ntohs(rtcpdebugaddr.sin_port) != 0)
+			&& (rtcpdebugaddr.sin_port != addr->sin_port))
+			|| (rtcpdebugaddr.sin_addr.s_addr != addr->sin_addr.s_addr))
+		return 0;
+	}
+	return 1;
+}
+
 
 static struct ast_frame *process_cisco_dtmf(struct ast_rtp *rtp, unsigned char *data, int len)
 {
@@ -364,33 +449,43 @@
 struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
 {
 	socklen_t len;
-	int hdrlen = 8;
+	int position, i, packetwords;
 	int res;
 	struct sockaddr_in sin;
-	unsigned int rtcpdata[1024];
+	unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET];
 	char iabuf[INET_ADDRSTRLEN];
+	unsigned int *rtcpheader;
+	int pt;
+	struct timeval now;
+	unsigned int length;
+	int rc;
+	double rtt = 0;
+	double a;
+	double dlsr;
+	double lsr;
+	unsigned int msw;
+	unsigned int lsw;
+	unsigned int comp;
 	
 	if (!rtp || !rtp->rtcp)
 		return &ast_null_frame;
 
 	len = sizeof(sin);
 	
-	res = recvfrom(rtp->rtcp->s, rtcpdata, sizeof(rtcpdata),
+	res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - AST_FRIENDLY_OFFSET,
 					0, (struct sockaddr *)&sin, &len);
+	rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
 	
 	if (res < 0) {
 		if (errno != EAGAIN)
-			ast_log(LOG_WARNING, "RTP Read error: %s\n", strerror(errno));
+			ast_log(LOG_WARNING, "RTCP Read error: %s\n", strerror(errno));
 		if (errno == EBADF)
 			CRASH;
 		return &ast_null_frame;
 	}
 
-	if (res < hdrlen) {
-		ast_log(LOG_WARNING, "RTP Read too short\n");
-		return &ast_null_frame;
-	}
-
+	packetwords = res / 4;
+	
 	if (rtp->nat) {
 		/* Send to whoever sent to us */
 		if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
@@ -402,18 +497,147 @@
 	}
 	if (option_debug)
 		ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res);
+
+	/* Process a compound packet */
+	position = 0;
+	while (position < packetwords) {
+		i = position;
+	  length = ntohl(rtcpheader[i]);
+	  pt = (length & 0xff0000) >> 16;
+	  rc = (length & 0x1f000000) >> 24;
+	  length &= 0xffff;
+    
+		if ((i + length) > packetwords) {
+			ast_log(LOG_WARNING, "RTCP Read too short\n");
+			return &ast_null_frame;
+		}
+		
+	  if(rtcp_debug_test_addr(&sin)){
+	  	ast_verbose("\n\nGot RTCP from %s:%d\n",ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr),ntohs(sin.sin_port));
+	  	ast_verbose("PT: %d(%s)\n",pt,(pt==200)?"Sender Report":(pt==201)?"Receiver Report":(pt==192)?"H.261 FUR":"Unknown");
+	  	ast_verbose("Reception reports: %d\n",rc);
+	  	ast_verbose("SSRC of sender: %u\n",rtcpheader[i+1]);
+	  }
+    
+		i+=2; /* Advance past header and ssrc */
+		
+	  switch(pt){
+	  	case RTCP_PT_SR:
+	  		gettimeofday(&rtp->rtcp->rxlsr,NULL); /* To be able to populate the dlsr */
+	  		rtp->rtcp->spc = ntohl(rtcpheader[i+3]);
+	  		rtp->rtcp->soc = ntohl(rtcpheader[i+4]);
+	  		rtp->rtcp->themrxlsr = ((ntohl(rtcpheader[i]) & 0x0000ffff) << 16) | ((ntohl(rtcpheader[i+1]) & 0xffff) >> 16); /* Going to LSR in RR*/
+    
+	  		if(rtcp_debug_test_addr(&sin)){
+	  			ast_verbose("NTP timestamp: %u.%010u\n",ntohl(rtcpheader[i]), ntohl(rtcpheader[i+1])*4096);
+	  			ast_verbose("RTP timestamp: %u\n",ntohl(rtcpheader[i+2]));
+	  			ast_verbose("SPC: %u\tSOC: %u\n",ntohl(rtcpheader[i+3]),ntohl(rtcpheader[i+4]));
+	  		}
+				i += 5;
+				if (rc < 1)
+					break;
+				/* Intentional fall through */
+	  	case RTCP_PT_RR:
+	  		/* This is the place to calculate RTT */
+				/* Don't handle multiple reception reports (rc > 1) yet */
+	  		gettimeofday(&now, NULL);
+	  		timeval2ntp(now, &msw, &lsw);
+	  		/* Use the one we sent them in our SR instead, rtcp->txlsr could have been rewritten if the dlsr is large */
+	  		if(ntohl(rtcpheader[i+4])){ /* We must have the LSR */
+	  			comp = ((msw & 0xffff) << 16) | ((lsw & 0xffff0000) >> 16);
+	  			a = (double)((comp & 0xffff0000) >> 16) + (double)((double)(comp & 0xffff)/1000000.);
+	  			lsr = (double)((ntohl(rtcpheader[i+4]) & 0xffff0000) >> 16) + (double)((double)(ntohl(rtcpheader[i+4]) & 0xffff)/1000000.);
+	  			dlsr = (double)(ntohl(rtcpheader[i+5])/65536.);
+	  			rtt = a - dlsr - lsr;
+	  			rtp->rtcp->accumulated_transit += rtt;
+	  			rtp->rtcp->rtt = rtt;
+	  			if(rtp->rtcp->maxrtt<rtt)
+	  				rtp->rtcp->maxrtt = rtt;
+	  			if(rtp->rtcp->minrtt>rtt)
+	  				rtp->rtcp->minrtt = rtt;
+	  		}
+	  		rtp->rtcp->reported_jitter = ntohl(rtcpheader[i+3]);
+	  		rtp->rtcp->reported_lost = ntohl(rtcpheader[i+1]) & 0xffffff;
+	  		if(rtcp_debug_test_addr(&sin)){
+	  			ast_verbose("Fraction lost: %d\n", ((ntohl(rtcpheader[i+1]) & 0xff000000) >> 24));
+	  			ast_verbose("Packets lost so far: %d\n", rtp->rtcp->reported_lost);
+	  			ast_verbose("Highest sequence number: %d\n", (ntohl(rtcpheader[i+2]) & 0xffff));
+	  			ast_verbose("Sequence number cycles: %d\n", (ntohl(rtcpheader[i+2]) & 0xffff) >> 16);
+	  			ast_verbose("Interarrival jitter: %u\n", rtp->rtcp->reported_jitter);
+	  			ast_verbose("Last SR(our NTP): %u.%010u\n",ntohl(rtcpheader[i+4])>>16,(ntohl(rtcpheader[i+4])<<16)*4096);
+	  			ast_verbose("DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i+5])/65536.0);
+	  			if(rtt)
+	  				ast_verbose("RTT: %f(sec)\n", rtt);
+	  		}
+	  		break;
+	  	case RTCP_PT_FUR:
+	  		if(rtcp_debug_test_addr(&sin))
+	  			ast_verbose("Received a Fast Update Request\n");
+			  break;
+	  	case RTCP_PT_SDES:
+	  		if(rtcp_debug_test_addr(&sin))
+	  			ast_verbose("Received an SDES from %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+			  break;
+	  	case RTCP_PT_BYE:
+	  		if(rtcp_debug_test_addr(&sin))
+	  			ast_verbose("Received a BYE from %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+			  break;
+	  	default:
+	  		ast_log(LOG_NOTICE, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+	  		break;
+	  }
+		position += (length + 1);
+	}
+			
 	return &ast_null_frame;
 }
 
 static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int timestamp, int mark)
 {
-	struct timeval ts = ast_samp2tv( timestamp, 8000);
-	if (ast_tvzero(rtp->rxcore) || mark) {
-		rtp->rxcore = ast_tvsub(ast_tvnow(), ts);
-		/* Round to 20ms for nice, pretty timestamps */
-		rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 20000;
-	}
-	*tv = ast_tvadd(rtp->rxcore, ts);
+	struct timeval now;
+	double transit;
+	double current_time;
+	double d;
+	double dtv;
+	double prog;
+	
+	if ((!rtp->rxcore.tv_sec && !rtp->rxcore.tv_usec) || mark) {
+		gettimeofday(&rtp->rxcore, NULL);
+		rtp->drxcore = (double)rtp->rxcore.tv_sec + (double)rtp->rxcore.tv_usec/1000000;
+		/* map timestamp to a real time */
+		rtp->seedrxts = timestamp; /* Their RTP timestamp started with this */
+		rtp->rxcore.tv_sec -= timestamp / 8000;
+		rtp->rxcore.tv_usec -= (timestamp % 8000) * 125;
+		/* Round to 0.1ms for nice, pretty timestamps */
+		rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 100;
+		if (rtp->rxcore.tv_usec < 0) {
+			/* Adjust appropriately if necessary */
+			rtp->rxcore.tv_usec += 1000000;
+			rtp->rxcore.tv_sec -= 1;
+		}
+	}
+
+	gettimeofday(&now,NULL);
+	/* rxcore is the mapping between the RTP timestamp and _our_ real time from gettimeofday() */
+	tv->tv_sec = rtp->rxcore.tv_sec + timestamp / 8000;
+	tv->tv_usec = rtp->rxcore.tv_usec + (timestamp % 8000) * 125;
+	if (tv->tv_usec >= 1000000) {
+		tv->tv_usec -= 1000000;
+		tv->tv_sec += 1;
+	}
+	prog = (double)((timestamp-rtp->seedrxts)/8000.);
+	dtv = (double)rtp->drxcore + (double)(prog);
+	current_time = (double)now.tv_sec + (double)now.tv_usec/1000000;
+	transit = current_time - dtv;
+	d = transit - rtp->rxtransit;
+	rtp->rxtransit = transit;
+	if(d<0)
+		d=-d;
+	rtp->rxjitter += (1./16.) * (d - rtp->rxjitter);
+	if(rtp->rxjitter > rtp->rtcp->maxrxjitter)
+		rtp->rtcp->maxrxjitter = rtp->rxjitter;
+	if(rtp->rxjitter < rtp->rtcp->minrxjitter)
+		rtp->rtcp->minrxjitter = rtp->rxjitter;
 }
 
 struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
@@ -424,6 +648,7 @@
 	unsigned int seqno;
 	int version;
 	int payloadtype;
+	int tseqno;
 	int hdrlen = 12;
 	int padding;
 	int mark;
@@ -450,6 +675,7 @@
 			CRASH;
 		return &ast_null_frame;
 	}
+	
 	if (res < hdrlen) {
 		ast_log(LOG_WARNING, "RTP Read too short\n");
 		return &ast_null_frame;
@@ -465,6 +691,10 @@
 		if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
 		    (rtp->them.sin_port != sin.sin_port)) {
 			memcpy(&rtp->them, &sin, sizeof(rtp->them));
+			if(rtp->rtcp){
+			    memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
+			    rtp->rtcp->them.sin_port = htons(ntohs(rtp->them.sin_port)+1);
+			}
 			rtp->rxseqno = 0;
 			ast_set_flag(rtp, FLAG_NAT_ACTIVE);
 			if (option_debug || rtpdebug)
@@ -503,11 +733,35 @@
 		return &ast_null_frame;
 	}
 
+	rtp->rxcount++; /* Only count reasonably valid packets, this'll make the rtcp stats more accurate */
+
+	tseqno = rtp->lastrxseqno +1;
+
+	if(rtp->rxcount==1){
+		/* This is the first RTP packet successfully received from source */
+		rtp->seedrxseqno = seqno;
+	}
+
+	if(rtp->rtcp->schedid<1){
+		/* Schedule transmission of Receiver Report */
+		rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
+	}
+
+	if(tseqno > RTP_SEQ_MOD){ /* if tseqno is greater than RTP_SEQ_MOD it would indicate that the sender cycled */
+		rtp->cycles += RTP_SEQ_MOD;
+		ast_verbose("SEQNO cycled: %u\t%d\n", rtp->cycles, seqno);
+	}
+
+	rtp->lastrxseqno = seqno;
+	
+	if(rtp->themssrc==0)
+		rtp->themssrc = ntohl(rtpheader[2]); /* Record their SSRC to put in future RR */
+	
 	if(rtp_debug_test_addr(&sin))
 		ast_verbose("Got RTP packet from %s:%d (type %d, seq %d, ts %d, len %d)\n"
 			, ast_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr), ntohs(sin.sin_port), payloadtype, seqno, timestamp,res - hdrlen);
 
-   	rtpPT = ast_rtp_lookup_pt(rtp, payloadtype);
+	rtpPT = ast_rtp_lookup_pt(rtp, payloadtype);
 	if (!rtpPT.isAstFormat) {
 		/* This is special in-band data that's not one of our codecs */
 		if (rtpPT.code == AST_RTP_DTMF) {
@@ -859,6 +1113,13 @@
 	}
 }
 
+void ast_rtp_offered_from_local(struct ast_rtp* rtp, int local) {
+	if (rtp)
+		rtp->rtp_offered_from_local = local;
+	else
+		ast_log(LOG_WARNING, "rtp structure is null\n");
+}
+
 struct rtpPayloadType ast_rtp_lookup_pt(struct ast_rtp* rtp, int pt) 
 {
 	struct rtpPayloadType result;
@@ -868,7 +1129,8 @@
 		return result; /* bogus payload type */
 
 	/* Start with the negotiated codecs */
-	result = rtp->current_RTP_PT[pt];
+	if (!rtp->rtp_offered_from_local)
+		result = rtp->current_RTP_PT[pt];
 
 	/* If it doesn't exist, check our static RTP type list, just in case */
 	if (!result.code) 
@@ -988,9 +1250,11 @@
 	memset(rtcp, 0, sizeof(struct ast_rtcp));
 	rtcp->s = rtp_socket();
 	rtcp->us.sin_family = AF_INET;
+	rtcp->them.sin_family = AF_INET;
+
 	if (rtcp->s < 0) {
 		free(rtcp);
-		ast_log(LOG_WARNING, "Unable to allocate socket: %s\n", strerror(errno));
+		ast_log(LOG_WARNING, "Unable to allocate RTCP socket: %s\n", strerror(errno));
 		return NULL;
 	}
 	return rtcp;
@@ -1125,11 +1389,16 @@
 
 void ast_rtp_stop(struct ast_rtp *rtp)
 {
+	if(rtp->rtcp->schedid>0){
+		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+		rtp->rtcp->schedid = -1;
+	}
+
 	memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
 	memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port));
 	if (rtp->rtcp) {
-		memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
-		memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->them.sin_port));
+		memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->rtcp->them.sin_addr));
+		memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->rtcp->them.sin_port));
 	}
 }
 
@@ -1153,8 +1422,50 @@
 	rtp->rxseqno = 0;
 }
 
+char *ast_rtp_get_quality(struct ast_rtp *rtp)
+{
+	/*
+	*ssrc          our ssrc
+	*themssrc      their ssrc
+	*lp            lost packets
+	*rxjitter      our calculated jitter(rx)
+	*rxcount       no. received packets
+	*txjitter      reported jitter of the other end
+	*txcount       transmitted packets
+	*rlp           remote lost packets
+	*/
+	
+	snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f", rtp->ssrc, rtp->themssrc, rtp->rtcp->expected_prior - rtp->rtcp->received_prior, rtp->rxjitter, rtp->rxcount, (double)rtp->rtcp->reported_jitter/65536., rtp->txcount, rtp->rtcp->reported_lost, rtp->rtcp->rtt);
+	
+	return rtp->rtcp->quality;
+}
+
 void ast_rtp_destroy(struct ast_rtp *rtp)
 {
+	if(rtcp_debug_test_addr(&rtp->them) || rtcpstats){
+		/*Print some info on the call here */
+		ast_verbose("  RTP-stats\n");
+		ast_verbose("* Our Receiver:\n");
+		ast_verbose("  SSRC:		 %u\n", rtp->themssrc);
+		ast_verbose("  Received packets: %u\n", rtp->rxcount);
+		ast_verbose("  Lost packets:	 %u\n", rtp->rtcp->expected_prior - rtp->rtcp->received_prior);
+		ast_verbose("  Jitter:		 %.4f\n", rtp->rxjitter);
+		ast_verbose("  Transit:		 %.4f\n", rtp->rxtransit);
+		ast_verbose("  RR-count:	 %u\n", rtp->rtcp->rr_count);
+		ast_verbose("* Our Sender:\n");
+		ast_verbose("  SSRC:		 %u\n", rtp->ssrc);
+		ast_verbose("  Sent packets:	 %u\n", rtp->txcount);
+		ast_verbose("  Lost packets:	 %u\n", rtp->rtcp->reported_lost);
+		ast_verbose("  Jitter:		 %u\n", rtp->rtcp->reported_jitter);
+		ast_verbose("  SR-count:	 %u\n", rtp->rtcp->sr_count);
+		ast_verbose("  RTT:		 %f\n", rtp->rtcp->rtt);
+	}
+
+	if(rtp->rtcp->schedid>0){
+		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+		rtp->rtcp->schedid = -1;
+	}
+
 	if (rtp->smoother)
 		ast_smoother_free(rtp->smoother);
 	if (rtp->ioid)
@@ -1164,6 +1475,7 @@
 	if (rtp->rtcp) {
 		close(rtp->rtcp->s);
 		free(rtp->rtcp);
+		rtp->rtcp=NULL;
 	}
 	free(rtp);
 }
@@ -1269,6 +1581,238 @@
 	return 0;
 }
 
+/* Send an H.261 fast update request, some devices need this rather than SIP XML */
+int ast_rtcp_send_h261fur(void *data)
+{
+	struct ast_rtp *rtp = data;
+	int res;
+
+  rtp->rtcp->sendfur = 1;
+	res = ast_rtcp_write(data);
+	
+	return res;
+}
+
+int ast_rtcp_write_sr(void *data)
+{
+	struct ast_rtp *rtp = data;
+	int res;
+	int len = 0;
+	struct timeval now;
+	unsigned int now_lsw;
+	unsigned int now_msw;
+	unsigned int *rtcpheader;
+	unsigned int lost;
+	unsigned int extended;
+	unsigned int expected;
+	unsigned int expected_interval;
+	unsigned int received_interval;
+	unsigned int lost_interval;
+	int fraction;
+	struct timeval dlsr;
+	char bdata[512];
+	char iabuf[INET_ADDRSTRLEN];
+
+	if(!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0))
+	  return 0;
+	
+	if(!rtp->rtcp->them.sin_addr.s_addr){  /* This'll stop rtcp for this rtp session */
+		ast_verbose("RTCP SR transmission error, rtcp halted %s\n",strerror(errno));
+		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+		rtp->rtcp->schedid = -1;
+		return 0;
+	}
+
+	gettimeofday(&now, NULL);
+	timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/
+	rtcpheader = (unsigned int *)bdata;
+	rtcpheader[1] = htonl(rtp->ssrc);               /* Our SSRC */
+	rtcpheader[2] = htonl(now_msw);                 /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/
+	rtcpheader[3] = htonl(now_lsw);                 /* now, LSW */
+	rtcpheader[4] = htonl(rtp->lastts);             /* FIXME shouldn't be that, it should be now */
+	rtcpheader[5] = htonl(rtp->txcount);            /* No. packets sent */
+	rtcpheader[6] = htonl(rtp->txoctetcount);       /* No. bytes sent */
+	len += 28;
+	
+	extended = rtp->cycles + rtp->lastrxseqno;
+	expected = extended - rtp->seedrxseqno + 1;
+	if(rtp->rxcount > expected) {
+		expected += rtp->rxcount - expected;
+	}
+	lost = expected - rtp->rxcount;
+	expected_interval = expected - rtp->rtcp->expected_prior;
+	rtp->rtcp->expected_prior = expected;
+	received_interval = rtp->rxcount - rtp->rtcp->received_prior;
+	rtp->rtcp->received_prior = rtp->rxcount;
+	lost_interval = expected_interval - received_interval;
+	if(expected_interval == 0 || lost_interval <= 0)
+		fraction = 0;
+	else
+		fraction = (lost_interval << 8) / expected_interval;
+	timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
+	rtcpheader[7] = htonl(rtp->themssrc);
+	rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
+	rtcpheader[9] = htonl((rtp->cycles << 16) | ((rtp->lastrxseqno & 0xffff)));
+	rtcpheader[10] = htonl((unsigned int)rtp->rxjitter);
+	rtcpheader[11] = htonl(rtp->rtcp->themrxlsr);
+	rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
+	len += 24;
+	
+	rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1));
+
+	if (rtp->rtcp->sendfur) {
+		rtcpheader[13] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1);
+		rtcpheader[14] = htonl(rtp->ssrc);               /* Our SSRC */
+		len += 8;
+		rtp->rtcp->sendfur = 0;
+	}
+	
+	/* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ 
+	/* it can change mid call, and SDES can't) */
+  rtcpheader[len/4]     = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
+	rtcpheader[(len/4)+1] = htonl(rtp->ssrc);               /* Our SSRC */
+	rtcpheader[(len/4)+2] = htonl(0x01 << 24);                    /* Empty for the moment */
+	len += 12;
+	
+	res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
+	if(res<0){
+		ast_verbose("RTCP SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno));
+		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+		rtp->rtcp->schedid = -1;
+		return 0;
+	}
+	
+	/* FIXME Don't need to get a new one */
+	gettimeofday(&rtp->rtcp->txlsr, NULL);
+	rtp->rtcp->sr_count++;
+
+  rtp->rtcp->lastsrtxcount = rtp->txcount;	
+	
+	if(rtcp_debug_test_addr(&rtp->rtcp->them)){
+		ast_verbose("Sent RTCP SR to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+		ast_verbose("Our SSRC: %u\n", rtp->ssrc);
+		ast_verbose("Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096);
+		ast_verbose("Sent(RTP): %u\n", rtp->lastts);
+		ast_verbose("Sent packets: %u\n", rtp->txcount);
+		ast_verbose("Sent octets: %u\n", rtp->txoctetcount);
+		ast_verbose("Report block:\n");
+		ast_verbose("Fraction lost: %u\n", fraction);
+		ast_verbose("Cumulative loss: %u\n", lost);
+		ast_verbose("IA jitter: %.4f\n", rtp->rxjitter);
+		ast_verbose("Their last SR: %u\n", rtp->rtcp->themrxlsr);
+		ast_verbose("DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0));
+	}
+	return res;
+}
+
+int ast_rtcp_write_rr(void *data)
+{
+	struct ast_rtp *rtp = data;
+	int res;
+	int len = 32;
+	unsigned int lost;
+	unsigned int extended;
+	unsigned int expected;
+	unsigned int expected_interval;
+	unsigned int received_interval;
+	unsigned int lost_interval;
+	struct timeval now;
+	unsigned int *rtcpheader;
+	char bdata[1024];
+	char iabuf[INET_ADDRSTRLEN];
+	struct timeval dlsr;
+	int fraction;
+
+	if(!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0))
+	  return 0;
+	  
+	if(!rtp->rtcp->them.sin_addr.s_addr){
+		ast_verbose("RTCP RR transmission error to, rtcp halted %s\n",strerror(errno));
+		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+		rtp->rtcp->schedid = -1;
+		return 0;
+	}
+
+	extended = rtp->cycles + rtp->lastrxseqno;
+	expected = extended - rtp->seedrxseqno + 1;
+	lost = expected - rtp->rxcount;
+	expected_interval = expected - rtp->rtcp->expected_prior;
+	rtp->rtcp->expected_prior = expected;
+	received_interval = rtp->rxcount - rtp->rtcp->received_prior;
+	rtp->rtcp->received_prior = rtp->rxcount;
+	lost_interval = expected_interval - received_interval;
+	if(expected_interval == 0 || lost_interval <= 0)
+		fraction = 0;
+	else
+		fraction = (lost_interval << 8) / expected_interval;
+	gettimeofday(&now, NULL);
+	timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
+	rtcpheader = (unsigned int *)bdata;
+	rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1));
+	rtcpheader[1] = htonl(rtp->ssrc);
+	rtcpheader[2] = htonl(rtp->themssrc);
+	rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
+	rtcpheader[4] = htonl((rtp->cycles << 16) | ((rtp->lastrxseqno & 0xffff)));
+	rtcpheader[5] = htonl((unsigned int)rtp->rxjitter);
+	rtcpheader[6] = htonl(rtp->rtcp->themrxlsr);
+	rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
+
+	if (rtp->rtcp->sendfur) {
+		rtcpheader[8] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); /* Header from page 36 in RFC 3550 */
+		rtcpheader[9] = htonl(rtp->ssrc);               /* Our SSRC */
+		len += 8;
+		rtp->rtcp->sendfur = 0;
+	}
+
+	/* Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos */ 
+	/* it can change mid call, and SDES can't) */
+  rtcpheader[len/4]     = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
+	rtcpheader[(len/4)+1] = htonl(rtp->ssrc);               /* Our SSRC */
+	rtcpheader[(len/4)+2] = htonl(0x01 << 24);              /* Empty for the moment */
+	len += 12;
+	
+	res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));
+
+	if(res<0){
+		ast_verbose("RTCP RR transmission error, rtcp halted: %s\n",strerror(errno));
+		/* Remove the scheduler */
+		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
+		rtp->rtcp->schedid = -1;
+		return 0;
+	}
+
+	rtp->rtcp->rr_count++;
+
+	if(rtcp_debug_test_addr(&rtp->rtcp->them)){
+		ast_verbose("\nSending RTCP RR to %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
+		ast_verbose("Our SSRC: %u\n", rtp->ssrc);
+		ast_verbose("Their SSRC: %u\n", rtp->themssrc);
+		ast_verbose("Fraction lost: %d\n", fraction);
+		ast_verbose("Cumulative loss: %u\n", lost);
+		ast_verbose("IA jitter: %.4f\n", rtp->rxjitter);
+		ast_verbose("Their last SR: %u\n", rtp->rtcp->themrxlsr);
+		ast_verbose("DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[7])/65536.0));
+	}
+
+	return res;
+}
+
+/* Write and RTCP packet to the far end */
+/* Decide if we are going to send an SR (with Reception Block) or RR */
+/* RR is sent if we have not sent any rtp packets in the previous interval */
+int ast_rtcp_write(void *data)
+{
+	struct ast_rtp *rtp = data;
+	int res;
+	
+	if (rtp->txcount > rtp->rtcp->lastsrtxcount)
+		res = ast_rtcp_write_sr(data);
+	else
+		res = ast_rtcp_write_rr(data);
+	
+	return res;
+}
+
 int ast_rtp_sendcng(struct ast_rtp *rtp, int level)
 {
 	unsigned int *rtpheader;
@@ -1297,7 +1841,7 @@
 		if (res <0) 
 			ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
 		if(rtp_debug_test_addr(&rtp->them))
-			ast_verbose("Sent Comfort Noise RTP packet to %s:%d (type %d, seq %d, ts %d, len %d)\n"
+			ast_verbose("Sent Comfort Noise RTP packet to %s:%d (type %d, seq %d, ts %u, len %d)\n"
 					, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen);		   
 		   
 	}
@@ -1372,6 +1916,13 @@
 				if (option_debug || rtpdebug)
 					ast_log(LOG_DEBUG, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port));
 				ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN);
+			}
+		}else{
+			rtp->txcount++;
+			rtp->txoctetcount +=(res - hdrlen);
+			
+			if(rtp->rtcp->schedid<1){
+			    rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
 			}
 		}
 				
@@ -1832,6 +2383,37 @@
 	return RESULT_SUCCESS;
 }
 
+static int rtcp_do_debug_ip(int fd, int argc, char *argv[])
+{
+	struct hostent *hp;
+	struct ast_hostent ahp;
+	char iabuf[INET_ADDRSTRLEN];
+	int port = 0;
+	char *p, *arg;
+	if (argc != 5)
+		return RESULT_SHOWUSAGE;
+
+	arg = argv[4];
+	p = strstr(arg, ":");
+	if (p){
+		*p = '\0';
+		p++;
+		port = atoi(p);
+	}
+	hp = ast_gethostbyname(arg, &ahp);
+	if (hp == NULL)
+		return RESULT_SHOWUSAGE;
+	rtcpdebugaddr.sin_family = AF_INET;
+	memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr));
+	rtcpdebugaddr.sin_port = htons(port);
+	if (port == 0)
+		ast_cli(fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtcpdebugaddr.sin_addr));
+	else
+		ast_cli(fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtcpdebugaddr.sin_addr), port);
+	rtcpdebug = 1;
+	return RESULT_SUCCESS;
+}
+
 static int rtp_do_debug(int fd, int argc, char *argv[])
 {
 	if(argc != 2) {
@@ -1845,6 +2427,27 @@
 	return RESULT_SUCCESS;
 }
    
+static int rtcp_do_debug(int fd, int argc, char *argv[]){
+	if(argc != 3){
+		if(argc != 5)
+			return RESULT_SHOWUSAGE;
+		return rtcp_do_debug_ip(fd, argc, argv);
+	}
+	rtcpdebug = 1;
+	memset(&rtcpdebugaddr,0,sizeof(rtcpdebugaddr));
+	ast_cli(fd, "RTCP Debugging Enabled\n");
+	return RESULT_SUCCESS;
+}
+
+static int rtcp_do_stats(int fd, int argc, char *argv[]){
+	if(argc != 3){
+		return RESULT_SHOWUSAGE;
+	}
+	rtcpstats = 1;
+	ast_cli(fd, "RTCP Stats Enabled\n");
+	return RESULT_SUCCESS;
+}
+
 static int rtp_no_debug(int fd, int argc, char *argv[])
 {
 	if(argc !=3)
@@ -1854,6 +2457,25 @@
 	return RESULT_SUCCESS;
 }
 
+static int rtcp_no_debug(int fd, int argc, char *argv[])
+{
+	if(argc !=4)
+		return RESULT_SHOWUSAGE;
+	rtcpdebug = 0;
+	ast_cli(fd,"RTCP Debugging Disabled\n");
+	return RESULT_SUCCESS;
+}
+
+static int rtcp_no_stats(int fd, int argc, char *argv[])
+{
+	if(argc !=4)
+		return RESULT_SHOWUSAGE;
+	rtcpstats = 0;
+	ast_cli(fd,"RTCP Stats Disabled\n");
+	return RESULT_SUCCESS;
+}
+
+
 static char debug_usage[] =
   "Usage: rtp debug [ip host[:port]]\n"
   "       Enable dumping of all RTP packets to and from host.\n";
@@ -1870,6 +2492,37 @@
 
 static struct ast_cli_entry  cli_no_debug =
 {{ "rtp", "no", "debug", NULL } , rtp_no_debug, "Disable RTP debugging", no_debug_usage };
+
+static char rtcp_debug_usage[] =
+  "Usage: rtp rtcp debug [ip host[:port]]\n"
+  "       Enable dumping of all RTCP packets to and from host.\n";
+  
+static char rtcp_no_debug_usage[] =
+  "Usage: rtp rtcp no debug\n"
+  "       Disable all RTCP debugging\n";
+
+static char rtcp_stats_usage[] =
+  "Usage: rtp rtcp stats\n"
+  "       Enable dumping of RTCP stats.\n";
+  
+static char rtcp_no_stats_usage[] =
+  "Usage: rtp rtcp no stats\n"
+  "       Disable all RTCP stats\n";
+
+static struct ast_cli_entry  cli_debug_ip_rtcp =
+{{ "rtp", "rtcp", "debug", "ip", NULL } , rtcp_do_debug, "Enable RTCP debugging on IP", rtcp_debug_usage };
+
+static struct ast_cli_entry  cli_debug_rtcp =
+{{ "rtp", "rtcp", "debug", NULL } , rtcp_do_debug, "Enable RTCP debugging", rtcp_debug_usage };
+
+static struct ast_cli_entry  cli_no_debug_rtcp =
+{{ "rtp", "rtcp", "no", "debug", NULL } , rtcp_no_debug, "Disable RTCP debugging", rtcp_no_debug_usage };
+
+static struct ast_cli_entry  cli_stats_rtcp =
+{{ "rtp", "rtcp", "stats", NULL } , rtcp_do_stats, "Enable RTCP stats", rtcp_stats_usage };
+
+static struct ast_cli_entry  cli_no_stats_rtcp =
+{{ "rtp", "rtcp", "no", "stats", NULL } , rtcp_no_stats, "Disable RTCP stats", rtcp_no_stats_usage };
 
 void ast_rtp_reload(void)
 {
@@ -1895,6 +2548,15 @@
 			if (rtpend > 65535)
 				rtpend = 65535;
 		}
+		if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) {
+			rtcpinterval = atoi(s);
+			if (rtcpinterval == 0)
+				rtcpinterval = 0; /* Just so we're clear... it's zero */
+			if (rtcpinterval < RTCP_MIN_INTERVALMS)
+				rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */
+			if (rtcpinterval > RTCP_MAX_INTERVALMS)
+				rtcpinterval = RTCP_MAX_INTERVALMS;
+		}
 		if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) {
 #ifdef SO_NO_CHECK
 			if (ast_false(s))
@@ -1932,5 +2594,14 @@
 	ast_cli_register(&cli_debug);
 	ast_cli_register(&cli_debug_ip);
 	ast_cli_register(&cli_no_debug);
+
+	ast_cli_register(&cli_debug_rtcp);
+	ast_cli_register(&cli_debug_ip_rtcp);
+	ast_cli_register(&cli_no_debug_rtcp);
+
+	ast_cli_register(&cli_stats_rtcp);
+	ast_cli_register(&cli_no_stats_rtcp);
+	
 	ast_rtp_reload();
 }
+



More information about the asterisk-commits mailing list