This patch adds more detailed statistics for RTP channels, and provides an API call...
authorBrett Bryant <bbryant@digium.com>
Thu, 5 Jun 2008 16:24:19 +0000 (16:24 +0000)
committerBrett Bryant <bbryant@digium.com>
Thu, 5 Jun 2008 16:24:19 +0000 (16:24 +0000)
and normal deviations. Currently this is implemented for chan_sip, but could be added to the func_channel_read callbacks for the CHANNEL function
for any channel that uses RTP.

(closes issue #10590)
Reported by: gasparz
Patches:
      chan_sip_c.diff uploaded by gasparz (license 219)
      rtp_c.diff uploaded by gasparz (license 219)
      rtp_h.diff uploaded by gasparz (license 219)
      audioqos-trunk.diff uploaded by snuffy (license 35)
      rtpqos-trunk-r119891.diff uploaded by sergee (license 138)
Tested by: jsmith, gasparz, snuffy, marsosa, chappell, sergee

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@120635 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channels/chan_sip.c
funcs/func_channel.c
include/asterisk/rtp.h
main/rtp.c

index 61a981a..7379279 100644 (file)
@@ -5063,15 +5063,25 @@ static int sip_hangup(struct ast_channel *ast)
                        }
                } else {        /* Call is in UP state, send BYE */
                        if (!p->pendinginvite) {
+                               struct ast_channel *bridge = ast_bridged_channel(oldowner);
                                char *audioqos = "";
                                char *videoqos = "";
                                char *textqos = "";
+
                                if (p->rtp)
-                                       audioqos = ast_rtp_get_quality(p->rtp, NULL);
+                                       ast_rtp_set_vars(oldowner, p->rtp);
+
+                               if (bridge) {
+                                       struct sip_pvt *q = bridge->tech_pvt;
+
+                                       if (IS_SIP_TECH(bridge->tech) && q->rtp)
+                                               ast_rtp_set_vars(bridge, q->rtp);
+                               }
+
                                if (p->vrtp)
-                                       videoqos = ast_rtp_get_quality(p->vrtp, NULL);
+                                       videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY);
                                if (p->trtp)
-                                       textqos = ast_rtp_get_quality(p->trtp, NULL);
+                                       textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY);
                                /* Send a hangup */
                                transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
 
@@ -18429,10 +18439,13 @@ static int acf_channel_read(struct ast_channel *chan, const char *funcname, char
                        ast_rtp_get_peer(p->vrtp, &sin);
                else if (!strcasecmp(args.type, "text"))
                        ast_rtp_get_peer(p->trtp, &sin);
+               else
+                       return -1;
 
                snprintf(buf, buflen, "%s:%d", ast_inet_ntoa(sin.sin_addr), ntohs(sin.sin_port));
        } else if (!strcasecmp(args.param, "rtpqos")) {
                struct ast_rtp_quality qos;
+               struct ast_rtp *rtp = p->rtp;
                
                memset(&qos, 0, sizeof(qos));
 
@@ -18442,11 +18455,13 @@ static int acf_channel_read(struct ast_channel *chan, const char *funcname, char
                        args.field = "all";
                
                if (strcasecmp(args.type, "AUDIO") == 0) {
-                       all = ast_rtp_get_quality(p->rtp, &qos);
+                       all = ast_rtp_get_quality(rtp = p->rtp, &qos, RTPQOS_SUMMARY);
                } else if (strcasecmp(args.type, "VIDEO") == 0) {
-                       all = ast_rtp_get_quality(p->vrtp, &qos);
+                       all = ast_rtp_get_quality(rtp = p->vrtp, &qos, RTPQOS_SUMMARY);
                } else if (strcasecmp(args.type, "TEXT") == 0) {
-                       all = ast_rtp_get_quality(p->trtp, &qos);
+                       all = ast_rtp_get_quality(rtp = p->trtp, &qos, RTPQOS_SUMMARY);
+               } else {
+                       return -1;
                }
                
                if (strcasecmp(args.field, "local_ssrc") == 0)
@@ -18469,6 +18484,8 @@ static int acf_channel_read(struct ast_channel *chan, const char *funcname, char
                        snprintf(buf, buflen, "%.0f", qos.rtt * 1000.0);
                else if (strcasecmp(args.field, "all") == 0)
                        ast_copy_string(buf, all, buflen);
+               else if (!ast_rtp_get_qos(rtp, args.field, buf, buflen))
+                        ;
                else {
                        ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
                        return -1;
@@ -18501,23 +18518,47 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req)
 
        /* Get RTCP quality before end of call */
        if (p->do_history || p->owner) {
-               char *audioqos, *videoqos, *textqos;
-               if (p->rtp) {
-                       audioqos = ast_rtp_get_quality(p->rtp, NULL);
-                       if (p->do_history)
+               struct ast_channel *bridge = ast_bridged_channel(p->owner);
+               char *videoqos, *textqos;
+
+               if (p->rtp) {   
+                       if (p->do_history) {
+                               char *audioqos,
+                                    *audioqos_jitter,
+                                    *audioqos_loss,
+                                    *audioqos_rtt;
+
+                               audioqos        = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_SUMMARY);
+                               audioqos_jitter = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_JITTER);
+                               audioqos_loss   = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_LOSS);
+                               audioqos_rtt    = ast_rtp_get_quality(p->rtp, NULL, RTPQOS_RTT);
+
                                append_history(p, "RTCPaudio", "Quality:%s", audioqos);
-                       if (p->owner)
-                               pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
+                               append_history(p, "RTCPaudioJitter", "Quality:%s", audioqos_jitter);
+                               append_history(p, "RTCPaudioLoss", "Quality:%s", audioqos_loss);
+                               append_history(p, "RTCPaudioRTT", "Quality:%s", audioqos_rtt);
+                       }
+                       
+                       ast_rtp_set_vars(p->owner, p->rtp);
                }
+
+               if (bridge) {
+                       struct sip_pvt *q = bridge->tech_pvt;
+
+                       if (IS_SIP_TECH(bridge->tech) && q->rtp)
+                               ast_rtp_set_vars(bridge, q->rtp);
+               }
+
                if (p->vrtp) {
-                       videoqos = ast_rtp_get_quality(p->vrtp, NULL);
+                       videoqos = ast_rtp_get_quality(p->vrtp, NULL, RTPQOS_SUMMARY);
                        if (p->do_history)
                                append_history(p, "RTCPvideo", "Quality:%s", videoqos);
                        if (p->owner)
                                pbx_builtin_setvar_helper(p->owner, "RTPVIDEOQOS", videoqos);
                }
+
                if (p->trtp) {
-                       textqos = ast_rtp_get_quality(p->trtp, NULL);
+                       textqos = ast_rtp_get_quality(p->trtp, NULL, RTPQOS_SUMMARY);
                        if (p->do_history)
                                append_history(p, "RTCPtext", "Quality:%s", textqos);
                        if (p->owner)
index 6a03613..506cb28 100644 (file)
@@ -200,12 +200,24 @@ static struct ast_custom_function channel_function = {
                "    local_ssrc            Local SSRC (stream ID)\n"
                "    local_lostpackets     Local lost packets\n"
                "    local_jitter          Local calculated jitter\n"
+               "    local_maxjitter       Local calculated jitter (maximum)\n"
+               "    local_minjitter       Local calculated jitter (minimum)\n"
+               "    local_normdevjitter   Local calculated jitter (normal deviation)\n"
+               "    local_stdevjitter     Local calculated jitter (standard deviation)\n"
                "    local_count           Number of received packets\n"
                "    remote_ssrc           Remote SSRC (stream ID)\n"
                "    remote_lostpackets    Remote lost packets\n"
                "    remote_jitter         Remote reported jitter\n"
+               "    remote_maxjitter      Remote calculated jitter (maximum)\n"
+               "    remote_minjitter      Remote calculated jitter (minimum)\n"
+               "    remote_normdevjitter  Remote calculated jitter (normal deviation)\n"
+               "    remote_stdevjitter    Remote calculated jitter (standard deviation)\n"
                "    remote_count          Number of transmitted packets\n"
                "    rtt                   Round trip time\n"
+               "    maxrtt                Round trip time (maximum)\n"
+               "    minrtt                Round trip time (minimum)\n"
+               "    normdevrtt            Round trip time (normal deviation)\n"
+               "    stdevrtt              Round trip time (standard deviation)\n"
                "    all                   All statistics (in a form suited to logging, but not for parsing)\n"
                "R/O    rtpdest            Get remote RTP destination information\n"
                "       This option takes one additional argument:\n"
index bb8358f..38fe863 100644 (file)
@@ -89,6 +89,13 @@ struct ast_rtp_protocol {
        AST_LIST_ENTRY(ast_rtp_protocol) list;
 };
 
+enum ast_rtp_quality_type {
+       RTPQOS_SUMMARY = 0,
+       RTPQOS_JITTER,
+       RTPQOS_LOSS,
+       RTPQOS_RTT
+};
+
 /*! \brief RTCP quality report storage */
 struct ast_rtp_quality {
        unsigned int local_ssrc;          /*!< Our SSRC */
@@ -259,9 +266,32 @@ int ast_rtp_make_compatible(struct ast_channel *dest, struct ast_channel *src, i
            having to send a re-invite later */
 int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1);
 
-/*! \brief Return RTCP quality string */
-char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual);
+/*! \brief Get QOS stats on a RTP channel */
+int ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, char *buf, unsigned int buflen);
+/*! \brief Set RTPAUDIOQOS(...) variables on a channel when it is being hung up */
+void ast_rtp_set_vars(struct ast_channel *chan, struct ast_rtp *rtp);
 
+/*! \brief Return RTCP quality string 
+ *
+ *  \param rtp An rtp structure to get qos information about.
+ *
+ *  \param qual An (optional) rtp quality structure that will be 
+ *              filled with the quality information described in 
+ *              the ast_rtp_quality structure. This structure is
+ *              not dependent on any qtype, so a call for any
+ *              type of information would yield the same results
+ *              because ast_rtp_quality is not a data type 
+ *              specific to any qos type.
+ *
+ *  \param qtype The quality type you'd like, default should be
+ *               RTPQOS_SUMMARY which returns basic information
+ *               about the call. The return from RTPQOS_SUMMARY
+ *               is basically ast_rtp_quality in a string. The
+ *               other types are RTPQOS_JITTER, RTPQOS_LOSS and
+ *               RTPQOS_RTT which will return more specific 
+ *               statistics.
+ */
+char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual, enum ast_rtp_quality_type qtype);
 /*! \brief Send an H.261 fast update request. Some devices need this rather than the XML message  in SIP */
 int ast_rtcp_send_h261fur(void *data);
 
index e112433..9afd0b9 100644 (file)
@@ -33,8 +33,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include <sys/time.h>
 #include <signal.h>
 #include <fcntl.h>
+#include <math.h> 
 
 #include "asterisk/rtp.h"
+#include "asterisk/pbx.h"
 #include "asterisk/frame.h"
 #include "asterisk/channel.h"
 #include "asterisk/acl.h"
@@ -229,6 +231,7 @@ int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit);
  * 
  */
 struct ast_rtcp {
+       int rtcp_info;
        int s;                          /*!< Socket */
        struct sockaddr_in us;          /*!< Socket representation of the local endpoint. */
        struct sockaddr_in them;        /*!< Socket representation of the remote endpoint. */
@@ -248,10 +251,38 @@ struct ast_rtcp {
        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];
+       char quality_jitter[AST_MAX_USER_FIELD];
+       char quality_loss[AST_MAX_USER_FIELD];
+       char quality_rtt[AST_MAX_USER_FIELD];
+
+       double reported_maxjitter;
+       double reported_minjitter;
+       double reported_normdev_jitter;
+       double reported_stdev_jitter;
+       unsigned int reported_jitter_count;
+
+       double reported_maxlost;
+       double reported_minlost;
+       double reported_normdev_lost;
+       double reported_stdev_lost;
+
+       double rxlost;
+       double maxrxlost;
+       double minrxlost;
+       double normdev_rxlost;
+       double stdev_rxlost;
+       unsigned int rxlost_count;
+
        double maxrxjitter;
        double minrxjitter;
+       double normdev_rxjitter;
+       double stdev_rxjitter;
+       unsigned int rxjitter_count;
        double maxrtt;
        double minrtt;
+       double normdevrtt;
+       double stdevrtt;
+       unsigned int rtt_count;
        int sendfur;
 };
 
@@ -805,6 +836,35 @@ static void rtp_bridge_unlock(struct ast_rtp *rtp)
        return;
 }
 
+/*! \brief Calculate normal deviation */
+static double normdev_compute(double normdev, double sample, unsigned int sample_count)
+{
+       normdev = normdev * sample_count + sample;
+       sample_count++;
+
+       return normdev / sample_count;
+}
+
+static double stddev_compute(double stddev, double sample, double normdev, double normdev_curent, unsigned int sample_count)
+{
+/*
+               for the formula check http://www.cs.umd.edu/~austinjp/constSD.pdf
+               return sqrt( (sample_count*pow(stddev,2) + sample_count*pow((sample-normdev)/(sample_count+1),2) + pow(sample-normdev_curent,2)) / (sample_count+1));
+               we can compute the sigma^2 and that way we would have to do the sqrt only 1 time at the end and would save another pow 2 compute
+               optimized formula
+*/
+#define SQUARE(x) ((x) * (x))
+
+       stddev = sample_count * stddev;
+       sample_count++;
+
+       return stddev + 
+              ( sample_count * SQUARE( (sample - normdev) / sample_count ) ) + 
+              ( SQUARE(sample - normdev_curent) / sample_count );
+
+#undef SQUARE
+}
+
 static struct ast_frame *send_dtmf(struct ast_rtp *rtp, enum ast_frame_type type)
 {
        if (((ast_test_flag(rtp, FLAG_DTMF_COMPENSATE) && type == AST_FRAME_DTMF_END) ||
@@ -1086,6 +1146,12 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
        unsigned int comp;
        struct ast_frame *f = &ast_null_frame;
        
+       double reported_jitter;
+       double reported_normdev_jitter_current;
+       double normdevrtt_current;
+       double reported_lost;
+       double reported_normdev_lost_current;
+
        if (!rtp || !rtp->rtcp)
                return &ast_null_frame;
 
@@ -1179,14 +1245,27 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
                                }
                                rtt = rtt / 1000.;
                                rttsec = rtt / 1000.;
+                               rtp->rtcp->rtt = rttsec;
 
                                if (comp - dlsr >= lsr) {
                                        rtp->rtcp->accumulated_transit += rttsec;
-                                       rtp->rtcp->rtt = rttsec;
+
+                                       if (rtp->rtcp->rtt_count == 0) 
+                                               rtp->rtcp->minrtt = rttsec;
+
                                        if (rtp->rtcp->maxrtt<rttsec)
                                                rtp->rtcp->maxrtt = rttsec;
+
                                        if (rtp->rtcp->minrtt>rttsec)
                                                rtp->rtcp->minrtt = rttsec;
+
+                                       normdevrtt_current = normdev_compute(rtp->rtcp->normdevrtt, rttsec, rtp->rtcp->rtt_count);
+
+                                       rtp->rtcp->stdevrtt = stddev_compute(rtp->rtcp->stdevrtt, rttsec, rtp->rtcp->normdevrtt, normdevrtt_current, rtp->rtcp->rtt_count);
+
+                                       rtp->rtcp->normdevrtt = normdevrtt_current;
+
+                                       rtp->rtcp->rtt_count++;
                                } else if (rtcp_debug_test_addr(&sin)) {
                                        ast_verbose("Internal RTCP NTP clock skew detected: "
                                                           "lsr=%u, now=%u, dlsr=%u (%d:%03dms), "
@@ -1198,7 +1277,45 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
                        }
 
                        rtp->rtcp->reported_jitter = ntohl(rtcpheader[i + 3]);
+                       reported_jitter = (double) rtp->rtcp->reported_jitter;
+
+                       if (rtp->rtcp->reported_jitter_count == 0) 
+                               rtp->rtcp->reported_minjitter = reported_jitter;
+
+                       if (reported_jitter < rtp->rtcp->reported_minjitter) 
+                               rtp->rtcp->reported_minjitter = reported_jitter;
+
+                       if (reported_jitter > rtp->rtcp->reported_maxjitter) 
+                               rtp->rtcp->reported_maxjitter = reported_jitter;
+
+                       reported_normdev_jitter_current = normdev_compute(rtp->rtcp->reported_normdev_jitter, reported_jitter, rtp->rtcp->reported_jitter_count);
+
+                       rtp->rtcp->reported_stdev_jitter = stddev_compute(rtp->rtcp->reported_stdev_jitter, reported_jitter, rtp->rtcp->reported_normdev_jitter, reported_normdev_jitter_current, rtp->rtcp->reported_jitter_count);
+
+                       rtp->rtcp->reported_normdev_jitter = reported_normdev_jitter_current;
+
                        rtp->rtcp->reported_lost = ntohl(rtcpheader[i + 1]) & 0xffffff;
+
+                       reported_lost = (double) rtp->rtcp->reported_lost;
+
+                       /* using same counter as for jitter */
+                       if (rtp->rtcp->reported_jitter_count == 0)
+                               rtp->rtcp->reported_minlost = reported_lost;
+
+                       if (reported_lost < rtp->rtcp->reported_minlost)
+                               rtp->rtcp->reported_minlost = reported_lost;
+
+                       if (reported_lost > rtp->rtcp->reported_maxlost) 
+                               rtp->rtcp->reported_maxlost = reported_lost;
+
+                       reported_normdev_lost_current = normdev_compute(rtp->rtcp->reported_normdev_lost, reported_lost, rtp->rtcp->reported_jitter_count);
+
+                       rtp->rtcp->reported_stdev_lost = stddev_compute(rtp->rtcp->reported_stdev_lost, reported_lost, rtp->rtcp->reported_normdev_lost, reported_normdev_lost_current, rtp->rtcp->reported_jitter_count);
+
+                       rtp->rtcp->reported_normdev_lost = reported_normdev_lost_current;
+
+                       rtp->rtcp->reported_jitter_count++;
+
                        if (rtcp_debug_test_addr(&sin)) {
                                ast_verbose("  Fraction lost: %ld\n", (((long) ntohl(rtcpheader[i + 1]) & 0xff000000) >> 24));
                                ast_verbose("  Packets lost so far: %d\n", rtp->rtcp->reported_lost);
@@ -1210,6 +1327,7 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
                                if (rtt)
                                        ast_verbose("  RTT: %lu(sec)\n", (unsigned long) rtt);
                        }
+
                        if (rtt) {
                                manager_event(EVENT_FLAG_REPORTING, "RTCPReceived", "From %s:%d\r\n"
                                                                    "PT: %d(%s)\r\n"
@@ -1286,7 +1404,7 @@ struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
                }
                position += (length + 1);
        }
-                       
+       rtp->rtcp->rtcp_info = 1;       
        return f;
 }
 
@@ -1299,6 +1417,7 @@ static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int t
        double dtv;
        double prog;
        
+       double normdev_rxjitter_current;
        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;
@@ -1334,8 +1453,16 @@ static void calc_rxstamp(struct timeval *tv, struct ast_rtp *rtp, unsigned int t
        rtp->rxjitter += (1./16.) * (d - rtp->rxjitter);
        if (rtp->rtcp && rtp->rxjitter > rtp->rtcp->maxrxjitter)
                rtp->rtcp->maxrxjitter = rtp->rxjitter;
+       if (rtp->rtcp->rxjitter_count == 1) 
+               rtp->rtcp->minrxjitter = rtp->rxjitter;
        if (rtp->rtcp && rtp->rxjitter < rtp->rtcp->minrxjitter)
                rtp->rtcp->minrxjitter = rtp->rxjitter;
+               
+       normdev_rxjitter_current = normdev_compute(rtp->rtcp->normdev_rxjitter,rtp->rxjitter,rtp->rtcp->rxjitter_count);
+       rtp->rtcp->stdev_rxjitter = stddev_compute(rtp->rtcp->stdev_rxjitter,rtp->rxjitter,rtp->rtcp->normdev_rxjitter,normdev_rxjitter_current,rtp->rtcp->rxjitter_count);
+
+       rtp->rtcp->normdev_rxjitter = normdev_rxjitter_current;
+       rtp->rtcp->rxjitter_count++;
 }
 
 /*! \brief Perform a Packet2Packet RTP write */
@@ -1557,7 +1684,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
                /* Schedule transmission of Receiver Report */
                rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
        }
-       if ( (int)rtp->lastrxseqno - (int)seqno  > 100) /* if so it would indicate that the sender cycled; allow for misordering */
+       if ((int)rtp->lastrxseqno - (int)seqno  > 100) /* if so it would indicate that the sender cycled; allow for misordering */
                rtp->cycles += RTP_SEQ_MOD;
        
        prev_seqno = rtp->lastrxseqno;
@@ -1688,7 +1815,7 @@ struct ast_frame *ast_rtp_read(struct ast_rtp *rtp)
                ast_set_flag(&rtp->f, AST_FRFLAG_HAS_TIMING_INFO);
                rtp->f.ts = timestamp / 8;
                rtp->f.len = rtp->f.samples / ( (ast_format_rate(rtp->f.subclass) == 16000) ? 16 : 8 );
-       } else if(rtp->f.subclass & AST_FORMAT_VIDEO_MASK) {
+       } else if (rtp->f.subclass & AST_FORMAT_VIDEO_MASK) {
                /* Video -- samples is # of samples vs. 90000 */
                if (!rtp->lastividtimestamp)
                        rtp->lastividtimestamp = timestamp;
@@ -2497,7 +2624,89 @@ void ast_rtp_reset(struct ast_rtp *rtp)
        rtp->rxseqno = 0;
 }
 
-char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual)
+static double __ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, int *found)
+{
+       *found = 1;
+
+       if (!strcasecmp(qos, "remote_maxjitter"))
+               return rtp->rtcp->reported_maxjitter * 1000.0;
+       if (!strcasecmp(qos, "remote_minjitter"))
+               return rtp->rtcp->reported_minjitter * 1000.0;
+       if (!strcasecmp(qos, "remote_normdevjitter"))
+               return rtp->rtcp->reported_normdev_jitter * 1000.0;
+       if (!strcasecmp(qos, "remote_stdevjitter"))
+               return sqrt(rtp->rtcp->reported_stdev_jitter) * 1000.0;
+
+       if (!strcasecmp(qos, "local_maxjitter"))
+               return rtp->rtcp->maxrxjitter * 1000.0;
+       if (!strcasecmp(qos, "local_minjitter"))
+               return rtp->rtcp->minrxjitter * 1000.0;
+       if (!strcasecmp(qos, "local_normdevjitter"))
+               return rtp->rtcp->normdev_rxjitter * 1000.0;
+       if (!strcasecmp(qos, "local_stdevjitter"))
+               return sqrt(rtp->rtcp->stdev_rxjitter) * 1000.0;
+
+       if (!strcasecmp(qos, "maxrtt"))
+               return rtp->rtcp->maxrtt * 1000.0;
+       if (!strcasecmp(qos, "minrtt"))
+               return rtp->rtcp->minrtt * 1000.0;
+       if (!strcasecmp(qos, "normdevrtt"))
+               return rtp->rtcp->normdevrtt * 1000.0;
+       if (!strcasecmp(qos, "stdevrtt"))
+               return sqrt(rtp->rtcp->stdevrtt) * 1000.0;
+
+       *found = 0;
+
+       return 0.0;
+}
+
+int ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, char *buf, unsigned int buflen)
+{
+       double value;
+       int found;
+
+       value = __ast_rtp_get_qos(rtp, qos, &found);
+
+       if (!found)
+               return -1;
+
+       snprintf(buf, buflen, "%.0lf", value);
+
+       return 0;
+}
+
+void ast_rtp_set_vars(struct ast_channel *chan, struct ast_rtp *rtp) {
+       char *audioqos;
+       char *audioqos_jitter;
+       char *audioqos_loss;
+       char *audioqos_rtt;
+       struct ast_channel *bridge;
+
+       if (!rtp || !chan)
+               return;
+
+       bridge = ast_bridged_channel(chan);
+
+       audioqos        = ast_rtp_get_quality(rtp, NULL, RTPQOS_SUMMARY);
+       audioqos_jitter = ast_rtp_get_quality(rtp, NULL, RTPQOS_JITTER);
+       audioqos_loss   = ast_rtp_get_quality(rtp, NULL, RTPQOS_LOSS);
+       audioqos_rtt    = ast_rtp_get_quality(rtp, NULL, RTPQOS_RTT);
+
+       pbx_builtin_setvar_helper(chan, "RTPAUDIOQOS", audioqos);
+       pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSJITTER", audioqos_jitter);
+       pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSLOSS", audioqos_loss);
+       pbx_builtin_setvar_helper(chan, "RTPAUDIOQOSRTT", audioqos_rtt);
+
+       if (!bridge)
+               return;
+
+       pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSBRIDGED", audioqos);
+       pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSJITTERBRIDGED", audioqos_jitter);
+       pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSLOSSBRIDGED", audioqos_loss);
+       pbx_builtin_setvar_helper(bridge, "RTPAUDIOQOSRTTBRIDGED", audioqos_rtt);
+}
+
+static char *__ast_rtp_get_quality_jitter(struct ast_rtp *rtp)
 {
        /*
        *ssrc          our ssrc
@@ -2510,21 +2719,129 @@ char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual)
        *rlp           remote lost packets
        *rtt           round trip time
        */
+#define RTCP_JITTER_FORMAT1 \
+       "minrxjitter=%f;" \
+       "maxrxjitter=%f;" \
+       "avgrxjitter=%f;" \
+       "stdevrxjitter=%f;" \
+       "reported_minjitter=%f;" \
+       "reported_maxjitter=%f;" \
+       "reported_avgjitter=%f;" \
+       "reported_stdevjitter=%f;"
+
+#define RTCP_JITTER_FORMAT2 \
+       "rxjitter=%f;"
+
+       if (rtp->rtcp && rtp->rtcp->rtcp_info) {
+               snprintf(rtp->rtcp->quality_jitter, sizeof(rtp->rtcp->quality_jitter), RTCP_JITTER_FORMAT1,
+                       rtp->rtcp->minrxjitter,
+                       rtp->rtcp->maxrxjitter,
+                       rtp->rtcp->normdev_rxjitter,
+                       sqrt(rtp->rtcp->stdev_rxjitter),
+                       rtp->rtcp->reported_minjitter,
+                       rtp->rtcp->reported_maxjitter,
+                       rtp->rtcp->reported_normdev_jitter,
+                       sqrt(rtp->rtcp->reported_stdev_jitter)
+               );
+       } else {
+               snprintf(rtp->rtcp->quality_jitter, sizeof(rtp->rtcp->quality_jitter), RTCP_JITTER_FORMAT2,
+                       rtp->rxjitter
+               );
+       }
 
-       if (qual && rtp) {
-               qual->local_ssrc = rtp->ssrc;
-               qual->local_jitter = rtp->rxjitter;
-               qual->local_count = rtp->rxcount;
-               qual->remote_ssrc = rtp->themssrc;
-               qual->remote_count = rtp->txcount;
-               if (rtp->rtcp) {
-                       qual->local_lostpackets = rtp->rtcp->expected_prior - rtp->rtcp->received_prior;
-                       qual->remote_lostpackets = rtp->rtcp->reported_lost;
-                       qual->remote_jitter = rtp->rtcp->reported_jitter / 65536.0;
-                       qual->rtt = rtp->rtcp->rtt;
-               }
+       return rtp->rtcp->quality_jitter;
+
+#undef RTCP_JITTER_FORMAT1
+#undef RTCP_JITTER_FORMAT2
+}
+
+static char *__ast_rtp_get_quality_loss(struct ast_rtp *rtp)
+{
+       unsigned int lost;
+       unsigned int extended;
+       unsigned int expected;
+       int fraction;
+
+#define RTCP_LOSS_FORMAT1 \
+       "minrxlost=%f;" \
+       "maxrxlost=%f;" \
+       "avgrxlostr=%f;" \
+       "stdevrxlost=%f;" \
+       "reported_minlost=%f;" \
+       "reported_maxlost=%f;" \
+       "reported_avglost=%f;" \
+       "reported_stdevlost=%f;"
+
+#define RTCP_LOSS_FORMAT2 \
+       "lost=%d;" \
+       "expected=%d;"
+       
+       if (rtp->rtcp && rtp->rtcp->rtcp_info && rtp->rtcp->maxrxlost > 0) {
+               snprintf(rtp->rtcp->quality_loss, sizeof(rtp->rtcp->quality_loss), RTCP_LOSS_FORMAT1,
+                       rtp->rtcp->minrxlost,
+                       rtp->rtcp->maxrxlost,
+                       rtp->rtcp->normdev_rxlost,
+                       sqrt(rtp->rtcp->stdev_rxlost),
+                       rtp->rtcp->reported_minlost,
+                       rtp->rtcp->reported_maxlost,
+                       rtp->rtcp->reported_normdev_lost,
+                       sqrt(rtp->rtcp->reported_stdev_lost)
+               );
+       } else {
+               extended = rtp->cycles + rtp->lastrxseqno;
+               expected = extended - rtp->seedrxseqno + 1;
+               if (rtp->rxcount > expected) 
+                       expected += rtp->rxcount - expected;
+               lost = expected - rtp->rxcount;
+
+               if (!expected || lost <= 0)
+                       fraction = 0;
+               else
+                       fraction = (lost << 8) / expected;
+
+               snprintf(rtp->rtcp->quality_loss, sizeof(rtp->rtcp->quality_loss), RTCP_LOSS_FORMAT2,
+                       lost,
+                       expected
+               );
        }
-       if (rtp->rtcp) {
+
+       return rtp->rtcp->quality_loss;
+
+#undef RTCP_LOSS_FORMAT1
+#undef RTCP_LOSS_FORMAT2
+}
+
+static char *__ast_rtp_get_quality_rtt(struct ast_rtp *rtp)
+{
+       if (rtp->rtcp && rtp->rtcp->rtcp_info) {
+               snprintf(rtp->rtcp->quality_rtt, sizeof(rtp->rtcp->quality_rtt), "minrtt=%f;maxrtt=%f;avgrtt=%f;stdevrtt=%f;",
+                       rtp->rtcp->minrtt,
+                       rtp->rtcp->maxrtt,
+                       rtp->rtcp->normdevrtt,
+                       sqrt(rtp->rtcp->stdevrtt)
+               );
+       } else {
+               snprintf(rtp->rtcp->quality_rtt, sizeof(rtp->rtcp->quality_rtt), "Not available");
+       }
+
+       return rtp->rtcp->quality_rtt;
+}
+
+static 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
+       *rtt           round trip time
+       */      
+
+       if (rtp->rtcp && rtp->rtcp->rtcp_info) {
                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,
@@ -2535,10 +2852,50 @@ char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual)
                        (double)rtp->rtcp->reported_jitter / 65536.0,
                        rtp->txcount,
                        rtp->rtcp->reported_lost,
-                       rtp->rtcp->rtt);
-               return rtp->rtcp->quality;
-       } else
-               return "<Unknown> - RTP/RTCP has already been destroyed";
+                       rtp->rtcp->rtt
+               );
+       } else {
+               snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), "ssrc=%u;themssrc=%u;rxjitter=%f;rxcount=%u;txcount=%u;",
+                       rtp->ssrc,
+                       rtp->themssrc,
+                       rtp->rxjitter,
+                       rtp->rxcount,
+                       rtp->txcount
+               );
+       }
+
+       return rtp->rtcp->quality;
+}
+
+char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual, enum ast_rtp_quality_type qtype) 
+{
+       if (qual && rtp) {
+               qual->local_ssrc   = rtp->ssrc;
+               qual->local_jitter = rtp->rxjitter;
+               qual->local_count  = rtp->rxcount;
+               qual->remote_ssrc  = rtp->themssrc;
+               qual->remote_count = rtp->txcount;
+
+               if (rtp->rtcp) {
+                       qual->local_lostpackets  = rtp->rtcp->expected_prior - rtp->rtcp->received_prior;
+                       qual->remote_lostpackets = rtp->rtcp->reported_lost;
+                       qual->remote_jitter      = rtp->rtcp->reported_jitter / 65536.0;
+                       qual->rtt                = rtp->rtcp->rtt;
+               }
+       }
+
+       switch (qtype) {
+       case RTPQOS_SUMMARY:
+               return __ast_rtp_get_quality(rtp);
+       case RTPQOS_JITTER:
+               return __ast_rtp_get_quality_jitter(rtp);
+       case RTPQOS_LOSS:
+               return __ast_rtp_get_quality_loss(rtp);
+       case RTPQOS_RTT:
+               return __ast_rtp_get_quality_rtt(rtp);
+       }
+
+       return NULL;
 }
 
 void ast_rtp_destroy(struct ast_rtp *rtp)
@@ -2944,6 +3301,8 @@ static int ast_rtcp_write_rr(const void *data)
        struct timeval dlsr;
        int fraction;
 
+       double rxlost_current;
+       
        if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0))
                return 0;
          
@@ -2961,6 +3320,22 @@ static int ast_rtcp_write_rr(const void *data)
        received_interval = rtp->rxcount - rtp->rtcp->received_prior;
        rtp->rtcp->received_prior = rtp->rxcount;
        lost_interval = expected_interval - received_interval;
+
+       if (lost_interval <= 0)
+               rtp->rtcp->rxlost = 0;
+       else rtp->rtcp->rxlost = rtp->rtcp->rxlost;
+       if (rtp->rtcp->rxlost_count == 0)
+               rtp->rtcp->minrxlost = rtp->rtcp->rxlost;
+       if (lost_interval < rtp->rtcp->minrxlost) 
+               rtp->rtcp->minrxlost = rtp->rtcp->rxlost;
+       if (lost_interval > rtp->rtcp->maxrxlost) 
+               rtp->rtcp->maxrxlost = rtp->rtcp->rxlost;
+
+       rxlost_current = normdev_compute(rtp->rtcp->normdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->rxlost_count);
+       rtp->rtcp->stdev_rxlost = stddev_compute(rtp->rtcp->stdev_rxlost, rtp->rtcp->rxlost, rtp->rtcp->normdev_rxlost, rxlost_current, rtp->rtcp->rxlost_count);
+       rtp->rtcp->normdev_rxlost = rxlost_current;
+       rtp->rtcp->rxlost_count++;
+
        if (expected_interval == 0 || lost_interval <= 0)
                fraction = 0;
        else
@@ -3223,7 +3598,7 @@ int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
                return 0;
 
        /* If there is no data length, return immediately */
-       if(!_f->datalen && !rtp->red)
+       if (!_f->datalen && !rtp->red)
                return 0;
        
        /* Make sure we have enough space for RTP header */
@@ -3903,8 +4278,8 @@ enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel
         * we can still do packet-to-packet bridging, because passing through the 
         * core will handle DTMF mode translation.
         */
-       if ( (ast_test_flag(p0, FLAG_HAS_DTMF) != ast_test_flag(p1, FLAG_HAS_DTMF)) ||
-                (!c0->tech->send_digit_begin != !c1->tech->send_digit_begin)) {
+       if ((ast_test_flag(p0, FLAG_HAS_DTMF) != ast_test_flag(p1, FLAG_HAS_DTMF)) ||
+               (!c0->tech->send_digit_begin != !c1->tech->send_digit_begin)) {
                if (!ast_test_flag(p0, FLAG_P2P_NEED_DTMF) || !ast_test_flag(p1, FLAG_P2P_NEED_DTMF)) {
                        ast_channel_unlock(c0);
                        ast_channel_unlock(c1);
@@ -4456,7 +4831,7 @@ int rtp_red_init(struct ast_rtp *rtp, int ti, int *red_data_pt, int num_gen)
  */
 void red_buffer_t140(struct ast_rtp *rtp, struct ast_frame *f)
 {
-       if( f->datalen > -1 ) {
+       if (f->datalen > -1) {
                struct rtp_red *red = rtp->red;
                memcpy(&red->buf_data[red->t140.datalen], f->data.ptr, f->datalen); 
                red->t140.datalen += f->datalen;