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;