aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorbbryant <bbryant@f38db490-d61c-443f-a65b-d21fe96a405b>2008-06-05 16:24:19 +0000
committerbbryant <bbryant@f38db490-d61c-443f-a65b-d21fe96a405b>2008-06-05 16:24:19 +0000
commit68dea9b6d66ac8281286c8fd85d3ef4b1ed3a823 (patch)
treed1e5ce5e2319e18edd672e98cdfaae3e096a1e2e
parente387c61e4f71e932c9a81269c34c93b4ff92f0fb (diff)
This patch adds more detailed statistics for RTP channels, and provides an API call to access it, including maximums, minimums, standard deviatinos,
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: http://svn.digium.com/svn/asterisk/trunk@120635 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--channels/chan_sip.c69
-rw-r--r--funcs/func_channel.c12
-rw-r--r--include/asterisk/rtp.h34
-rw-r--r--main/rtp.c427
4 files changed, 500 insertions, 42 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 61a981abe..737927918 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 6a036139f..506cb2867 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 bb8358feb..38fe8639c 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 e11243358..9afd0b940 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;