diff --git a/channels/chan_sip.c b/channels/chan_sip.c index 07ba786bef..b054465a20 100644 --- a/channels/chan_sip.c +++ b/channels/chan_sip.c @@ -3686,11 +3686,11 @@ static int sip_hangup(struct ast_channel *ast) char *videoqos = ""; char *textqos = ""; if (p->rtp) - audioqos = ast_rtp_get_quality(p->rtp); + audioqos = ast_rtp_get_quality(p->rtp, NULL); if (p->vrtp) - videoqos = ast_rtp_get_quality(p->vrtp); + videoqos = ast_rtp_get_quality(p->vrtp, NULL); if (p->trtp) - textqos = ast_rtp_get_quality(p->trtp); + textqos = ast_rtp_get_quality(p->trtp, NULL); /* Send a hangup */ transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1); @@ -14800,6 +14800,55 @@ static int handle_request_cancel(struct sip_pvt *p, struct sip_request *req) } } +static int acf_audiortpqos_read(struct ast_channel *chan, char *funcname, char *args, char *buf, size_t buflen) +{ + struct ast_rtp_quality qos; + struct sip_pvt *p = chan->tech_pvt; + char *all = ""; + + /* Sanity check */ + if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) { + ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname); + } + + memset(buf, 0, buflen); + memset(&qos, 0, sizeof(qos)); + + if (strcmp(funcname, "AUDIORTPQOS") == 0) { + all = ast_rtp_get_quality(p->rtp, &qos); + } else if (strcmp(funcname, "VIDEORTPQOS") == 0) { + all = ast_rtp_get_quality(p->vrtp, &qos); + } else if (strcmp(funcname, "TEXTRTPQOS") == 0) { + all = ast_rtp_get_quality(p->trtp, &qos); + } + + if (strcasecmp(args, "local_ssrc") == 0) + snprintf(buf, buflen, "%u", qos.local_ssrc); + else if (strcasecmp(args, "local_lostpackets") == 0) + snprintf(buf, buflen, "%u", qos.local_lostpackets); + else if (strcasecmp(args, "local_jitter") == 0) + snprintf(buf, buflen, "%.0lf", qos.local_jitter * 1000.0); + else if (strcasecmp(args, "local_count") == 0) + snprintf(buf, buflen, "%u", qos.local_count); + else if (strcasecmp(args, "remote_ssrc") == 0) + snprintf(buf, buflen, "%u", qos.remote_ssrc); + else if (strcasecmp(args, "remote_lostpackets") == 0) + snprintf(buf, buflen, "%u", qos.remote_lostpackets); + else if (strcasecmp(args, "remote_jitter") == 0) + snprintf(buf, buflen, "%.0lf", qos.remote_jitter * 1000.0); + else if (strcasecmp(args, "remote_count") == 0) + snprintf(buf, buflen, "%u", qos.remote_count); + else if (strcasecmp(args, "rtt") == 0) + snprintf(buf, buflen, "%.0lf", qos.rtt * 1000.0); + else if (strcasecmp(args, "all") == 0) + ast_copy_string(buf, all, buflen); + else { + ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", args, funcname); + return -1; + } + return 0; +} + /*! \brief Handle incoming BYE request */ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) { @@ -14823,14 +14872,14 @@ static int handle_request_bye(struct sip_pvt *p, struct sip_request *req) if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY) || p->owner) { char *audioqos, *videoqos, *textqos; if (p->rtp) { - audioqos = ast_rtp_get_quality(p->rtp); + audioqos = ast_rtp_get_quality(p->rtp, NULL); if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) append_history(p, "RTCPaudio", "Quality:%s", audioqos); if (p->owner) pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos); } if (p->vrtp) { - videoqos = ast_rtp_get_quality(p->vrtp); + videoqos = ast_rtp_get_quality(p->vrtp, NULL); if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY)) append_history(p, "RTCPvideo", "Quality:%s", videoqos); if (p->owner) @@ -18081,6 +18130,63 @@ static struct ast_cli_entry cli_sip[] = { sip_reload_usage }, }; +struct ast_custom_function acf_audiortpqos = { + .name = "AUDIORTPQOS", + .synopsis = "Retrieve statistics about an RTP audio stream", + .desc = +"The following statistics may be retrieved:\n" +" local_ssrc - Local SSRC (stream ID)\n" +" local_lostpackets - Local lost packets\n" +" local_jitter - Local calculated jitter\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_count - Number of transmitted packets\n" +" rtt - Round trip time\n" +" all - All statistics (in a form suited to logging, but not for parsing)", + .syntax = "AUDIORTPQOS()", + .read = acf_audiortpqos_read, +}; + +struct ast_custom_function acf_videortpqos = { + .name = "VIDEORTPQOS", + .synopsis = "Retrieve statistics about an RTP audio stream", + .desc = +"The following statistics may be retrieved:\n" +" local_ssrc - Local SSRC (stream ID)\n" +" local_lostpackets - Local lost packets\n" +" local_jitter - Local calculated jitter\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_count - Number of transmitted packets\n" +" rtt - Round trip time\n" +" all - All statistics (in a form suited to logging, but not for parsing)", + .syntax = "AUDIORTPQOS()", + .read = acf_audiortpqos_read, +}; + +struct ast_custom_function acf_textrtpqos = { + .name = "TEXTRTPQOS", + .synopsis = "Retrieve statistics about an RTP text stream", + .desc = +"The following statistics may be retrieved:\n" +" local_ssrc - Local SSRC (stream ID)\n" +" local_lostpackets - Local lost packets\n" +" local_jitter - Local calculated jitter\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_count - Number of transmitted packets\n" +" rtt - Round trip time\n" +" all - All statistics (in a form suited to logging, but not for parsing)", + .syntax = "TEXTRTPQOS()", + .read = acf_audiortpqos_read, +}; + /*! \brief PBX load module - initialization */ static int load_module(void) { @@ -18130,6 +18236,9 @@ static int load_module(void) ast_custom_function_register(&sippeer_function); ast_custom_function_register(&sipchaninfo_function); ast_custom_function_register(&checksipdomain_function); + ast_custom_function_register(&acf_audiortpqos); + ast_custom_function_register(&acf_videortpqos); + ast_custom_function_register(&acf_textrtpqos); /* Register manager commands */ ast_manager_register2("SIPpeers", EVENT_FLAG_SYSTEM, manager_sip_show_peers, @@ -18159,6 +18268,9 @@ static int unload_module(void) ast_custom_function_unregister(&sippeer_function); ast_custom_function_unregister(&sip_header_function); ast_custom_function_unregister(&checksipdomain_function); + ast_custom_function_unregister(&acf_audiortpqos); + ast_custom_function_unregister(&acf_videortpqos); + ast_custom_function_unregister(&acf_textrtpqos); /* Unregister dial plan applications */ ast_unregister_application(app_dtmfmode); diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h index 5eea063db8..47cc26212e 100644 --- a/include/asterisk/rtp.h +++ b/include/asterisk/rtp.h @@ -84,6 +84,17 @@ struct ast_rtp_protocol { AST_LIST_ENTRY(ast_rtp_protocol) list; }; +struct ast_rtp_quality { + unsigned int local_ssrc; /* Our SSRC */ + unsigned int local_lostpackets; /* Our lost packets */ + double local_jitter; /* Our calculated jitter */ + unsigned int local_count; /* Number of received packets */ + unsigned int remote_ssrc; /* Their SSRC */ + unsigned int remote_lostpackets; /* Their lost packets */ + double remote_jitter; /* Their reported jitter */ + unsigned int remote_count; /* Number of transmitted packets */ + double rtt; /* Round trip time */ +}; /*! RTP callback structure */ typedef int (*ast_rtp_callback)(struct ast_rtp *rtp, struct ast_frame *f, void *data); @@ -219,12 +230,12 @@ 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 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); -char *ast_rtp_get_quality(struct ast_rtp *rtp); /*! \brief Return RTCP quality string */ void ast_rtp_init(void); /*! Initialize RTP subsystem */ int ast_rtp_reload(void); /*! reload rtp configuration */ void ast_rtp_new_init(struct ast_rtp *rtp); diff --git a/main/rtp.c b/main/rtp.c index 4c45c5b3eb..486ff6189e 100644 --- a/main/rtp.c +++ b/main/rtp.c @@ -2128,7 +2128,7 @@ void ast_rtp_reset(struct ast_rtp *rtp) rtp->rxseqno = 0; } -char *ast_rtp_get_quality(struct ast_rtp *rtp) +char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual) { /* *ssrc our ssrc @@ -2139,8 +2139,20 @@ char *ast_rtp_get_quality(struct ast_rtp *rtp) *txjitter reported jitter of the other end *txcount transmitted packets *rlp remote lost packets + *rtt round trip time */ - + + if (qual) { + qual->local_ssrc = rtp->ssrc; + qual->local_lostpackets = rtp->rtcp->expected_prior - rtp->rtcp->received_prior; + qual->local_jitter = rtp->rxjitter; + qual->local_count = rtp->rxcount; + qual->remote_ssrc = rtp->themssrc; + qual->remote_lostpackets = rtp->rtcp->reported_lost; + qual->remote_jitter = rtp->rtcp->reported_jitter / 65536.0; + qual->remote_count = rtp->txcount; + qual->rtt = rtp->rtcp->rtt; + } snprintf(rtp->rtcp->quality, sizeof(rtp->rtcp->quality), "ssrc=%u;themssrc=%u;lp=%u;rxjitter=%f;rxcount=%u;txjitter=%f;txcount=%u;rlp=%u;rtt=%f", rtp->ssrc, rtp->themssrc, rtp->rtcp->expected_prior - rtp->rtcp->received_prior, rtp->rxjitter, rtp->rxcount, (double)rtp->rtcp->reported_jitter/65536., rtp->txcount, rtp->rtcp->reported_lost, rtp->rtcp->rtt); return rtp->rtcp->quality;