Hi

It doesn't say anywhere how you're supposed to submit patches, so I'm
mailing it here.

Ported from Asterisk revision 32230.

Changelog:
*  Adds support for RTCP
*  Provides call quality metrics in the ${RTPAUDIOQOS} and
${RTPVIDEOQOS} variables after the call has hung up

Damjan Jovanovic


-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.


-- 
This message has been scanned for viruses and
dangerous content by MailScanner, and is
believed to be clean.

Index: corelib/rtp.c
===================================================================
--- corelib/rtp.c	(revision 2481)
+++ corelib/rtp.c	(working copy)
@@ -71,13 +71,36 @@
 #define DEFAULT_RTPEND 31000
 #define DEFAULT_DTMFTIMEOUT 3000    /* 3000 samples */
 
+#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
+
 static int dtmftimeout = DEFAULT_DTMFTIMEOUT;
 static int rtpstart = 0;
 static int rtpend = 0;
 static int rtpdebug = 0;        /* Are we debugging? */
 static struct sockaddr_in rtpdebugaddr;    /* Debug packets to/from this host */
 static int nochecksums = 0;
+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 rtcpdebugaddr;       /*!< Debug RTCP packets to/from this host */
 
+/* Forward declarations */
+static int opbx_rtcp_write(void *data);
+static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw);
+static int opbx_rtcp_write_sr(void *data);
+static int opbx_rtcp_write_rr(void *data);
+static unsigned int opbx_rtcp_calc_interval(struct opbx_rtp *rtp);
+
 #define FLAG_3389_WARNING           (1 << 0)
 #define FLAG_NAT_ACTIVE             (3 << 1)
 #define FLAG_NAT_INACTIVE           (0 << 1)
@@ -136,6 +159,16 @@
     return ent;
 }
 
+static 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 opbx_rtp_fd(struct opbx_rtp *rtp)
 {
     return udp_socket_fd(rtp->rtp_sock_info);
@@ -168,6 +201,15 @@
     return old;
 }
 
+unsigned int opbx_rtcp_calc_interval(struct opbx_rtp *rtp)
+{
+	unsigned int interval;
+	/*! \todo XXX Do a more reasonable calculation on this one
+	* Look in RFC 3550 Section A.7 for an example*/
+	interval = rtcpinterval;
+	return interval;
+}
+
 void opbx_rtp_set_data(struct opbx_rtp *rtp, void *data)
 {
     rtp->data = data;
@@ -249,6 +291,19 @@
     return 1;
 }
 
+static inline int rtcp_debug_test_addr(const 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 opbx_frame *process_cisco_dtmf(struct opbx_rtp *rtp, unsigned char *data, int len)
 {
     unsigned int event;
@@ -758,23 +813,38 @@
 {
     static struct opbx_frame null_frame = { OPBX_FRAME_NULL, };
     socklen_t len;
-    int hdrlen = 8;
+    int position, i, packetwords;
     int res;
     int actions;
     struct sockaddr_in sin;
-    unsigned int rtcpdata[1024];
+    unsigned int rtcpdata[8192 + OPBX_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;
+    struct opbx_frame *f = &null_frame;
     
     if (rtp == NULL)
         return &null_frame;
 
     len = sizeof(sin);
 
-    res = udp_socket_recvfrom(rtp->rtcp_sock_info, rtcpdata, sizeof(rtcpdata), 0, (struct sockaddr *) &sin, &len, &actions);
+    res = udp_socket_recvfrom(rtp->rtcp_sock_info, rtcpdata + OPBX_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * OPBX_FRIENDLY_OFFSET, 0, (struct sockaddr *) &sin, &len, &actions);
+    rtcpheader = (unsigned int *)(rtcpdata + OPBX_FRIENDLY_OFFSET);
+
     if (res < 0)
     {
         if (errno != EAGAIN)
-            opbx_log(LOG_DEBUG, "RTP Read error: %s\n", strerror(errno));
+            opbx_log(LOG_DEBUG, "RTCP Read error: %s\n", strerror(errno));
         if (errno == EBADF)
             CRASH;
         return &null_frame;
@@ -785,27 +855,158 @@
             opbx_log(LOG_DEBUG, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", opbx_inet_ntoa(iabuf, sizeof(iabuf), udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr), ntohs(udp_socket_get_them(rtp->rtcp_sock_info)->sin_port));
     }
 
-    if (res < hdrlen)
-    {
-        opbx_log(LOG_DEBUG, "RTP Read too short\n");
-        return &null_frame;
-    }
+    packetwords = res / 4;
+
     if (option_debug)
         opbx_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res);
-    return &null_frame;
+
+    /* 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) {
+            opbx_log(LOG_WARNING, "RTCP Read too short\n");
+            return &null_frame;
+        }
+
+        if(rtcp_debug_test_addr(&sin)){
+            opbx_verbose("\n\nGot RTCP from %s:%d\n",opbx_inet_ntoa(iabuf, sizeof(iabuf), sin.sin_addr),ntohs(sin.sin_port));
+            opbx_verbose("PT: %d(%s)\n",pt,(pt==200)?"Sender Report":(pt==201)?"Receiver Report":(pt==192)?"H.261 FUR":"Unknown");
+            opbx_verbose("Reception reports: %d\n",rc);
+            opbx_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)){
+                    opbx_verbose("NTP timestamp: %lu.%010lu\n",(unsigned long)ntohl(rtcpheader[i]), (unsigned long)ntohl(rtcpheader[i+1])*4096);
+                    opbx_verbose("RTP timestamp: %lu\n",(unsigned long)ntohl(rtcpheader[i+2]));
+                    opbx_verbose("SPC: %lu\tSOC: %lu\n",(unsigned long)ntohl(rtcpheader[i+3]),(unsigned long)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)){
+                    opbx_verbose("Fraction lost: %ld\n", (((long)ntohl(rtcpheader[i+1]) & 0xff000000) >> 24));
+                    opbx_verbose("Packets lost so far: %d\n", rtp->rtcp->reported_lost);
+                    opbx_verbose("Highest sequence number: %ld\n", (long)(ntohl(rtcpheader[i+2]) & 0xffff));
+                    opbx_verbose("Sequence number cycles: %ld\n", (long)(ntohl(rtcpheader[i+2]) & 0xffff) >> 16);
+                    opbx_verbose("Interarrival jitter: %u\n", rtp->rtcp->reported_jitter);
+                    opbx_verbose("Last SR(our NTP): %lu.%010lu\n",(unsigned long)ntohl(rtcpheader[i+4])>>16,((unsigned long)ntohl(rtcpheader[i+4])<<16)*4096);
+                    opbx_verbose("DLSR: %4.4f (sec)\n",ntohl(rtcpheader[i+5])/65536.0);
+                    if(rtt)
+                        opbx_verbose("RTT: %f(sec)\n", rtt);
+                }
+                break;
+            case RTCP_PT_FUR:
+                if(rtcp_debug_test_addr(&sin))
+                    opbx_verbose("Received an RTCP Fast Update Request\n");
+                rtp->f.frametype = OPBX_FRAME_CONTROL;
+                rtp->f.subclass = OPBX_CONTROL_VIDUPDATE;
+                rtp->f.datalen = 0;
+                rtp->f.samples = 0;
+                rtp->f.mallocd = 0;
+                rtp->f.src = "RTP";
+                f = &rtp->f;
+                break;
+            case RTCP_PT_SDES:
+                if(rtcp_debug_test_addr(&sin))
+                    opbx_verbose("Received an SDES from %s:%d\n", opbx_inet_ntoa(iabuf, sizeof(iabuf), udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr), ntohs(udp_socket_get_them(rtp->rtcp_sock_info)->sin_port));
+                break;
+            case RTCP_PT_BYE:
+                if(rtcp_debug_test_addr(&sin))
+                    opbx_verbose("Received a BYE from %s:%d\n", opbx_inet_ntoa(iabuf, sizeof(iabuf), udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr), ntohs(udp_socket_get_them(rtp->rtcp_sock_info)->sin_port));
+                break;
+            default:
+                opbx_log(LOG_NOTICE, "Unknown RTCP packet (pt=%d) received from %s:%d\n", pt, opbx_inet_ntoa(iabuf, sizeof(iabuf), udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr), ntohs(udp_socket_get_them(rtp->rtcp_sock_info)->sin_port));
+                break;
+        }
+        position += (length + 1);
+    }
+
+    return f;
 }
 
 static void calc_rxstamp(struct timeval *tv, struct opbx_rtp *rtp, unsigned int timestamp, int mark)
 {
-    struct timeval ts = opbx_samp2tv(timestamp, 8000);
+    struct timeval now;
+    double transit;
+    double current_time;
+    double d;
+    double dtv;
+    double prog;
 
-    if (opbx_tvzero(rtp->rxcore)  ||  mark)
-    {
-        rtp->rxcore = opbx_tvsub(opbx_tvnow(), ts);
-        /* Round to 20ms for nice, pretty timestamps */
-        rtp->rxcore.tv_usec -= rtp->rxcore.tv_usec % 20000;
+    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;
+        }
     }
-    *tv = opbx_tvadd(rtp->rxcore, ts);
+
+    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 opbx_frame *opbx_rtp_read(struct opbx_rtp *rtp)
@@ -817,6 +1018,7 @@
     uint32_t csrc_count;
     int version;
     int payloadtype;
+    int tseqno;
     int hdrlen = 3*sizeof(uint32_t);
     int mark;
     int actions;
@@ -912,6 +1114,24 @@
     timestamp = ntohl(rtpheader[1]);
     ssrc = ntohl(rtpheader[2]);
 
+    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 = opbx_sched_add(rtp->sched, opbx_rtcp_calc_interval(rtp), opbx_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;
+        opbx_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))
     {
         opbx_verbose("Got RTP packet from %s:%d (type %d, seq %d, ts %d, len %d)\n",
@@ -1349,6 +1569,13 @@
     {
         rtp->sched = sched;
         rtp->rtcp_sock_info = udp_socket_find_group_element(rtp->rtp_sock_info, 1);
+        rtp->rtcp = malloc(sizeof(struct opbx_rtcp));
+        if (!rtp->rtcp)
+        {
+            free(rtp);
+            return NULL;
+        }
+        memset(rtp->rtcp, 0, sizeof(struct opbx_rtcp));
     }
     if (io  &&  sched  &&  callbackmode)
     {
@@ -1421,6 +1648,11 @@
 
 void opbx_rtp_stop(struct opbx_rtp *rtp)
 {
+    if(rtp->rtcp->schedid>0){
+        opbx_sched_del(rtp->sched, rtp->rtcp->schedid);
+        rtp->rtcp->schedid = -1;
+    }
+
     udp_socket_restart(rtp->rtp_sock_info);
     udp_socket_restart(rtp->rtcp_sock_info);
 }
@@ -1445,8 +1677,51 @@
     rtp->rxseqno = 0;
 }
 
+char *opbx_rtp_get_quality(struct opbx_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 opbx_rtp_destroy(struct opbx_rtp *rtp)
 {
+    if(rtcp_debug_test_addr(udp_socket_get_them(rtp->rtp_sock_info)) || rtcpstats){
+        /*Print some info on the call here */
+        opbx_verbose("  RTP-stats\n");
+        opbx_verbose("* Our Receiver:\n");
+        opbx_verbose("  SSRC:             %u\n", rtp->themssrc);
+        opbx_verbose("  Received packets: %u\n", rtp->rxcount);
+        opbx_verbose("  Lost packets:     %u\n", rtp->rtcp->expected_prior - rtp->rtcp->received_prior);
+        opbx_verbose("  Jitter:           %.4f\n", rtp->rxjitter);
+        opbx_verbose("  Transit:          %.4f\n", rtp->rxtransit);
+        opbx_verbose("  RR-count:         %u\n", rtp->rtcp->rr_count);
+        opbx_verbose("* Our Sender:\n");
+        opbx_verbose("  SSRC:             %u\n", rtp->ssrc);
+        opbx_verbose("  Sent packets:     %u\n", rtp->txcount);
+        opbx_verbose("  Lost packets:     %u\n", rtp->rtcp->reported_lost);
+        opbx_verbose("  Jitter:           %u\n", rtp->rtcp->reported_jitter);
+        opbx_verbose("  SR-count:         %u\n", rtp->rtcp->sr_count);
+        opbx_verbose("  RTT:              %f\n", rtp->rtcp->rtt);
+    }
+
+    if(rtp->rtcp->schedid>0){
+        opbx_sched_del(rtp->sched, rtp->rtcp->schedid);
+        rtp->rtcp->schedid = -1;
+    }
+
     if (rtp->smoother)
         opbx_smoother_free(rtp->smoother);
     if (rtp->ioid)
@@ -1456,6 +1731,11 @@
     if (rtp->srtp)
         srtp_dealloc(rtp->srtp);
 #endif
+    if (rtp->rtcp)
+    {
+        free(rtp->rtcp);
+        rtp->rtcp = NULL;
+    }
     free(rtp);
 }
 
@@ -1575,6 +1855,243 @@
     return 0;
 }
 
+/* \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */
+int opbx_rtcp_send_h261fur(void *data)
+{
+    struct opbx_rtp *rtp = data;
+    int res;
+
+    rtp->rtcp->sendfur = 1;
+    res = opbx_rtcp_write(data);
+
+    return res;
+}
+
+/*! \brief Send RTCP sender's report */
+static int opbx_rtcp_write_sr(void *data)
+{
+    struct opbx_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;
+    int lost_interval;
+    int fraction;
+    struct timeval dlsr;
+    char bdata[512];
+    char iabuf[INET_ADDRSTRLEN];
+
+    if(!rtp || !rtp->rtcp || (udp_socket_get_them(rtp->rtcp_sock_info) == 0))
+        return 0;
+
+    if(!udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr.s_addr) {  /* This'll stop rtcp for this rtp session */
+        opbx_verbose("RTCP SR transmission error, rtcp halted %s\n",strerror(errno));
+        opbx_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) | ((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 = udp_socket_sendto(rtp->rtcp_sock_info, (unsigned int *)rtcpheader, len, 0);
+    if (res < 0) {
+        opbx_log(LOG_ERROR, "RTCP SR transmission error to %s:%d, rtcp halted %s\n",opbx_inet_ntoa(iabuf, sizeof(iabuf), udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr), ntohs(udp_socket_get_them(rtp->rtcp_sock_info)->sin_port), strerror(errno));
+        opbx_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(udp_socket_get_them(rtp->rtcp_sock_info))) {
+        opbx_verbose("* Sent RTCP SR to %s:%d\n", opbx_inet_ntoa(iabuf, sizeof(iabuf), udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr), ntohs(udp_socket_get_them(rtp->rtcp_sock_info)->sin_port));
+        opbx_verbose("  Our SSRC: %u\n", rtp->ssrc);
+        opbx_verbose("  Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096);
+        opbx_verbose("  Sent(RTP): %u\n", rtp->lastts);
+        opbx_verbose("  Sent packets: %u\n", rtp->txcount);
+        opbx_verbose("  Sent octets: %u\n", rtp->txoctetcount);
+        opbx_verbose("  Report block:\n");
+        opbx_verbose("  Fraction lost: %u\n", fraction);
+        opbx_verbose("  Cumulative loss: %u\n", lost);
+        opbx_verbose("  IA jitter: %.4f\n", rtp->rxjitter);
+        opbx_verbose("  Their last SR: %u\n", rtp->rtcp->themrxlsr);
+        opbx_verbose("  DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0));
+    }
+    return res;
+}
+
+/*! \brief Send RTCP recepient's report */
+static int opbx_rtcp_write_rr(void *data)
+{
+    struct opbx_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;
+    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 || (udp_socket_get_them(rtp->rtcp_sock_info) == 0))
+        return 0;
+
+    if (!udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr.s_addr){
+        opbx_log(LOG_ERROR, "RTCP RR transmission error to, rtcp halted %s\n",strerror(errno));
+        opbx_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) | ((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;
+    }
+
+    /*! \note 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 = udp_socket_sendto(rtp->rtcp_sock_info,  (unsigned int *)rtcpheader, len, 0);
+
+    if (res < 0) {
+        opbx_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno));
+        /* Remove the scheduler */
+        opbx_sched_del(rtp->sched, rtp->rtcp->schedid);
+        rtp->rtcp->schedid = -1;
+        return 0;
+    }
+
+    rtp->rtcp->rr_count++;
+
+    if (rtcp_debug_test_addr(udp_socket_get_them(rtp->rtcp_sock_info))) {
+        opbx_verbose("\n* Sending RTCP RR to %s:%d\n"
+            "  Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n"
+            "  IA jitter: %.4f\n"
+            "  Their last SR: %u\n"
+            "  DLSR: %4.4f (sec)\n\n",
+            opbx_inet_ntoa(iabuf, sizeof(iabuf),
+                udp_socket_get_them(rtp->rtcp_sock_info)->sin_addr),
+            ntohs(udp_socket_get_them(rtp->rtcp_sock_info)->sin_port),
+            rtp->ssrc, rtp->themssrc, fraction, lost,
+            rtp->rxjitter,
+            rtp->rtcp->themrxlsr,
+            (double)(ntohl(rtcpheader[7])/65536.0));
+    }
+
+    return res;
+}
+
+/*! \brief Write and RTCP packet to the far end
+ * \note 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 */
+static int opbx_rtcp_write(void *data)
+{
+    struct opbx_rtp *rtp = data;
+    int res;
+
+    if (rtp->txcount > rtp->rtcp->lastsrtxcount)
+        res = opbx_rtcp_write_sr(data);
+    else
+        res = opbx_rtcp_write_rr(data);
+
+    return res;
+}
+
 int opbx_rtp_sendcng(struct opbx_rtp *rtp, int level)
 {
     unsigned int *rtpheader;
@@ -1712,6 +2229,14 @@
                 opbx_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN);
             }
         }
+        else
+        {
+            rtp->txcount++;
+            rtp->txoctetcount +=(res - hdrlen);
+
+            if (rtp->rtcp->schedid < 1)
+                rtp->rtcp->schedid = opbx_sched_add(rtp->sched, opbx_rtcp_calc_interval(rtp), opbx_rtcp_write, rtp);
+	}
                 
         if (rtp_debug_test_addr(them))
         {
@@ -2204,6 +2729,37 @@
     return RESULT_SUCCESS;
 }
 
+static int rtcp_do_debug_ip(int fd, int argc, char *argv[])
+{
+    struct hostent *hp;
+    struct opbx_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 = opbx_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)
+        opbx_cli(fd, "RTCP Debugging Enabled for IP: %s\n", opbx_inet_ntoa(iabuf, sizeof(iabuf), rtcpdebugaddr.sin_addr));
+    else
+        opbx_cli(fd, "RTCP Debugging Enabled for IP: %s:%d\n", opbx_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)
@@ -2217,7 +2773,28 @@
     opbx_cli(fd, "RTP Debugging Enabled\n");
     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));
+    opbx_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;
+    opbx_cli(fd, "RTCP Stats Enabled\n");
+    return RESULT_SUCCESS;
+}
+
 static int rtp_no_debug(int fd, int argc, char *argv[])
 {
     if (argc !=3)
@@ -2227,6 +2804,24 @@
     return RESULT_SUCCESS;
 }
 
+static int rtcp_no_debug(int fd, int argc, char *argv[])
+{
+    if(argc !=4)
+        return RESULT_SHOWUSAGE;
+    rtcpdebug = 0;
+    opbx_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;
+    opbx_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";
@@ -2235,6 +2830,22 @@
     "Usage: rtp no debug\n"
     "       Disable all RTP debugging\n";
 
+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 opbx_cli_entry  cli_debug_ip =
 {{ "rtp", "debug", "ip", NULL } , rtp_do_debug, "Enable RTP debugging on IP", debug_usage };
 
@@ -2244,6 +2855,21 @@
 static struct opbx_cli_entry  cli_no_debug =
 {{ "rtp", "no", "debug", NULL } , rtp_no_debug, "Disable RTP debugging", no_debug_usage };
 
+static struct opbx_cli_entry  cli_debug_ip_rtcp =
+{{ "rtp", "rtcp", "debug", "ip", NULL } , rtcp_do_debug, "Enable RTCP debugging on IP", rtcp_debug_usage };
+
+static struct opbx_cli_entry  cli_debug_rtcp =
+{{ "rtp", "rtcp", "debug", NULL } , rtcp_do_debug, "Enable RTCP debugging", rtcp_debug_usage };
+
+static struct opbx_cli_entry  cli_no_debug_rtcp =
+{{ "rtp", "rtcp", "no", "debug", NULL } , rtcp_no_debug, "Disable RTCP debugging", rtcp_no_debug_usage };
+
+static struct opbx_cli_entry  cli_stats_rtcp =
+{{ "rtp", "rtcp", "stats", NULL } , rtcp_do_stats, "Enable RTCP stats", rtcp_stats_usage };
+
+static struct opbx_cli_entry  cli_no_stats_rtcp =
+{{ "rtp", "rtcp", "no", "stats", NULL } , rtcp_no_stats, "Disable RTCP stats", rtcp_no_stats_usage };
+
 void opbx_rtp_reload(void)
 {
     struct opbx_config *cfg;
@@ -2282,6 +2908,16 @@
                 dtmftimeout = DEFAULT_DTMFTIMEOUT;
             }
         }
+        if ((s = opbx_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 = opbx_variable_retrieve(cfg, "general", "rtpchecksums")))
         {
 #ifdef SO_NO_CHECK
@@ -2311,6 +2947,14 @@
     opbx_cli_register(&cli_debug);
     opbx_cli_register(&cli_debug_ip);
     opbx_cli_register(&cli_no_debug);
+
+    opbx_cli_register(&cli_debug_rtcp);
+    opbx_cli_register(&cli_debug_ip_rtcp);
+    opbx_cli_register(&cli_no_debug_rtcp);
+
+    opbx_cli_register(&cli_stats_rtcp);
+    opbx_cli_register(&cli_no_stats_rtcp);
+
     opbx_rtp_reload();
 #ifdef ENABLE_SRTP
     opbx_log(LOG_NOTICE, "srtp_init\n");
Index: channels/chan_sip.c
===================================================================
--- channels/chan_sip.c	(revision 2481)
+++ channels/chan_sip.c	(working copy)
@@ -3295,8 +3295,35 @@
             /* Call is in UP state, send BYE */
             if (!p->pendinginvite)
             {
+                char *audioqos = "";
+                char *videoqos = "";
+                if (p->rtp)
+                    audioqos = opbx_rtp_get_quality(p->rtp);
+                if (p->vrtp)
+                    videoqos = opbx_rtp_get_quality(p->vrtp);
+
                 /* Send a hangup */
                 transmit_request_with_auth(p, SIP_BYE, 0, 1, 1);
+
+                /* Get RTCP quality before end of call */
+                if (recordhistory)
+                {
+                    char buffer[1024];
+                    if (p->rtp)
+                    {
+                        snprintf(buffer, sizeof(buffer), "Quality:%s", audioqos);
+                        append_history(p, "RTCPaudio", buffer);
+                    }
+                    if (p->vrtp)
+                    {
+                        snprintf(buffer, sizeof(buffer), "Quality:%s", videoqos);
+                        append_history(p, "RTCPvideo", buffer);
+                    }
+                }
+                if (p->rtp)
+                    pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
+                if (p->vrtp)
+                    pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
             }
             else
             {
@@ -13567,16 +13594,44 @@
 /*! \brief  handle_request_cancel: Handle incoming CANCEL request */
 static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req, int debug, int ignore)
 {
-        
+    char *audioqos = "";
+    char *videoqos = "";
+ 
     check_via(p, req);
     opbx_set_flag(p, SIP_ALREADYGONE);    
+
     if (p->rtp)
+        audioqos = opbx_rtp_get_quality(p->rtp);
+    if (p->vrtp)
+        videoqos = opbx_rtp_get_quality(p->vrtp);
+
+    /* Get RTCP quality before end of call */
+    if (recordhistory)
     {
+        char buffer[1024];
+        if (p->rtp)
+        {
+            snprintf(buffer, sizeof(buffer), "Quality:%s", audioqos);
+            append_history(p, "RTCPaudio", buffer);
+        }
+        if (p->vrtp)
+        {
+            snprintf(buffer, sizeof(buffer), "Quality:%s", videoqos);
+            append_history(p, "RTCPvideo", buffer);
+        }
+    }
+
+    if (p->rtp)
+    {
+        if (p->owner)
+            pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
         /* Immediately stop RTP */
         opbx_rtp_stop(p->rtp);
     }
     if (p->vrtp)
     {
+        if (p->owner)
+            pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
         /* Immediately stop VRTP */
         opbx_rtp_stop(p->vrtp);
     }
Index: include/openpbx/rtp.h
===================================================================
--- include/openpbx/rtp.h	(revision 2481)
+++ include/openpbx/rtp.h	(working copy)
@@ -76,20 +76,57 @@
 	int code;
 };
 
+struct opbx_rtcp
+{
+        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[OPBX_MAX_USER_FIELD];
+        double maxrxjitter;
+        double minrxjitter;
+        double maxrtt;
+        double minrtt;
+        int sendfur;
+};
+
 struct opbx_rtp
 {
+	struct opbx_rtcp *rtcp;
     udp_socket_info_t *rtp_sock_info;
     udp_socket_info_t *rtcp_sock_info;
 	char resp;
 	struct opbx_frame f;
 	uint8_t rawdata[8192 + OPBX_FRIENDLY_OFFSET];
 	uint32_t ssrc;
+	uint32_t themssrc;	/* their SSRC */
 	uint32_t lastts;
 	uint32_t lastdigitts;
 	uint32_t lastrxts;
 	uint32_t lastividtimestamp;
 	uint32_t lastovidtimestamp;
 	uint32_t 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 */
 	uint32_t lasteventendseqn;
 	int lasttxformat;
 	int lastrxformat;
@@ -102,6 +139,8 @@
 	struct timeval rxcore;
 	struct timeval txcore;
 	struct timeval dtmfmute;
+	double drxcore;                 /*!< The double representation of the first received packet */
+	struct timeval lastrx;          /*!< timeval when we last received a packet */
 	struct opbx_smoother *smoother;
 	int *ioid;
 	uint16_t seqno;
@@ -200,6 +239,12 @@
 
 void opbx_rtp_reload(void);
 
+/*! \brief Return RTCP quality string */
+char *opbx_rtp_get_quality(struct opbx_rtp *rtp);
+
+/*! \brief Send an H.261 fast update request. Some devices need this rather than the XML message  in SIP */
+int opbx_rtcp_send_h261fur(void *data);
+
 int opbx_rtp_set_framems(struct opbx_rtp *rtp, int ms);
 
 #ifdef ENABLE_SRTP
Index: configs/rtp.conf.sample
===================================================================
--- configs/rtp.conf.sample	(revision 2481)
+++ configs/rtp.conf.sample	(working copy)
@@ -13,3 +13,7 @@
 ; Whether to enable or disable UDP checksums on RTP traffic
 ;
 ;rtpchecksums=no
+;
+; Milliseconds between rtcp reports
+; (min 500, max 60000, default 5000)
+;rtcpinterval = 5000
_______________________________________________
Openpbx-dev mailing list
[email protected]
http://lists.openpbx.org/mailman/listinfo/openpbx-dev

Reply via email to