diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 61a981abe5ef36a5246f8f4c39cd94413c9564b7..73792791802c46e5184ceda069499f683c31de57 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -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)
diff --git a/funcs/func_channel.c b/funcs/func_channel.c
index 6a036139f2194bc2dc2e3cda59294dd8b9bcce48..506cb2867237e5fef0f6ba92b67ca6b72ae0db9b 100644
--- a/funcs/func_channel.c
+++ b/funcs/func_channel.c
@@ -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"
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index bb8358feb47138ec36fefb17a908b0b0476527f9..38fe8639ceb5f34eea93808290bc848c2fb87815 100644
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -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);
 
diff --git a/main/rtp.c b/main/rtp.c
index e11243358a7e58a5abe42ff50538a30acb34b1e4..9afd0b940e8c0dd6e464d90ce976f5530ce9e9b2 100644
--- a/main/rtp.c
+++ b/main/rtp.c
@@ -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;