summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOlle Johansson <oej@edvina.net>2008-07-05 19:27:42 +0000
committerOlle Johansson <oej@edvina.net>2008-07-05 19:27:42 +0000
commit0a52297cf009170d6a8fa49617c0eb4ccab7a38f (patch)
tree66d5eda276da7f3e74e3e261874de6bef7f35b91
parent509fd1aff7106a9e855f9e76d40f0731bc257e4f (diff)
Add new SIP cli command "sip show channelstats" that displays some QoS data (if we have RTCP reports
and not use the p2p rtp bridge). I could not find a way to detect us using the p2p bridge, which would be nice. git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@128197 65c4cc65-6c06-0410-ace0-fbb531ad65f3
-rw-r--r--channels/chan_sip.c124
-rw-r--r--include/asterisk/rtp.h15
-rw-r--r--main/rtp.c31
3 files changed, 159 insertions, 11 deletions
diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index 280e128e3..7ff5eed29 100644
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -311,6 +311,22 @@ enum invitestates {
INV_CANCELLED = 7, /*!< Transaction cancelled by client or server in non-terminated state */
};
+/*! \brief Readable descriptions of device states.
+ \note Should be aligned to above table as index */
+static const struct invstate2stringtable {
+ const enum invitestates state;
+ const char const *desc;
+} invitestate2string[] = {
+ {INV_NONE, "None" },
+ {INV_CALLING, "Calling (Trying)"},
+ {INV_PROCEEDING, "Proceeding "},
+ {INV_EARLY_MEDIA, "Early media"},
+ {INV_COMPLETED, "Completed (done)"},
+ {INV_CONFIRMED, "Confirmed (up)"},
+ {INV_TERMINATED, "Done"},
+ {INV_CANCELLED, "Cancelled"}
+};
+
enum xmittype {
XMIT_CRITICAL = 2, /*!< Transmit critical SIP message reliably, with re-transmits.
If it fails, it's critical and will cause a teardown of the session */
@@ -453,6 +469,14 @@ struct sip_proxy {
/* Room for a SRV record chain based on the name */
};
+/*! \brief argument for the 'show channels|subscriptions' callback. */
+struct __show_chan_arg {
+ int fd;
+ int subscriptions;
+ int numchans; /* return value */
+};
+
+
/*! \brief States whether a SIP message can create a dialog in Asterisk. */
enum can_create_dialog {
CAN_NOT_CREATE_DIALOG,
@@ -2018,6 +2042,7 @@ static char *complete_sip_user(const char *word, int state, int flags2);
static char *complete_sip_show_user(const char *line, const char *word, int pos, int state);
static char *complete_sipnotify(const char *line, const char *word, int pos, int state);
static char *sip_show_channel(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
+static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *sip_show_history(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a);
static char *sip_do_debug_ip(int fd, char *arg);
static char *sip_do_debug_peer(int fd, char *arg);
@@ -14117,6 +14142,91 @@ static char *sip_unregister(struct ast_cli_entry *e, int cmd, struct ast_cli_arg
return CLI_SUCCESS;
}
+/*! \brief Callback for show_chanstats */
+static int show_chanstats_cb(void *__cur, void *__arg, int flags)
+{
+#define FORMAT2 "%-15.15s %-11.11s %-8.8s %-10.10s %-10.10s (%-2.2s) %-6.6s %-10.10s %-10.10s ( %%) %-6.6s\n"
+#define FORMAT "%-15.15s %-11.11s %-8.8s %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u %-10.10u%-1.1s %-10.10u (%-2.2u%%) %-6.6u\n"
+ struct sip_pvt *cur = __cur;
+ unsigned int rxcount;
+ unsigned int txcount;
+ char durbuf[10];
+ int duration;
+ int durh, durm, durs;
+ struct ast_channel *c = cur->owner;
+ struct __show_chan_arg *arg = __arg;
+ int fd = arg->fd;
+
+
+ if (cur->subscribed != NONE) /* Subscriptions */
+ return 0; /* don't care, we scan all channels */
+
+ if (!cur->rtp) {
+ if (sipdebug)
+ ast_cli(fd, "%-15.15s %-11.11s (inv state: %s) -- %s\n", ast_inet_ntoa(cur->sa.sin_addr), cur->callid, invitestate2string[cur->invitestate].desc, "-- No RTP active");
+ return 0; /* don't care, we scan all channels */
+ }
+ rxcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXCOUNT);
+ txcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXCOUNT);
+
+ /* Find the duration of this channel */
+ if (c && c->cdr && !ast_tvzero(c->cdr->start)) {
+ duration = (int)(ast_tvdiff_ms(ast_tvnow(), c->cdr->start) / 1000);
+ durh = duration / 3600;
+ durm = (duration % 3600) / 60;
+ durs = duration % 60;
+ snprintf(durbuf, sizeof(durbuf), "%02d:%02d:%02d", durh, durm, durs);
+ } else {
+ durbuf[0] = '\0';
+ }
+ /* Print stats for every call with RTP */
+ ast_cli(fd, FORMAT,
+ ast_inet_ntoa(cur->sa.sin_addr),
+ cur->callid,
+ durbuf,
+ rxcount > (unsigned int) 100000 ? (unsigned int) (rxcount)/(unsigned int) 1000 : rxcount,
+ rxcount > (unsigned int) 100000 ? "K":" ",
+ ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS),
+ rxcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS) / rxcount * 100) : 0,
+ ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXJITTER),
+ txcount > (unsigned int) 100000 ? (unsigned int) (txcount)/(unsigned int) 1000 : txcount,
+ txcount > (unsigned int) 100000 ? "K":" ",
+ ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS),
+ txcount > ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS) ? (unsigned int) (ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS)/ txcount * 100) : 0,
+ ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXJITTER)
+ );
+ arg->numchans++;
+
+ return 0; /* don't care, we scan all channels */
+}
+
+/*! \brief SIP show channelstats CLI (main function) */
+static char *sip_show_channelstats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
+{
+ struct __show_chan_arg arg = { .fd = a->fd, .numchans = 0 };
+
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show channelstats";
+ e->usage =
+ "Usage: sip show channelstats\n"
+ " Lists all currently active SIP channel's RTCP statistics.\n"
+ " Note that calls in the much optimized RTP P2P bridge mode will not show any packets here.";
+ return NULL;
+ }
+
+ if (a->argc != 3)
+ return CLI_SHOWUSAGE;
+
+ ast_cli(a->fd, FORMAT2, "Peer", "Call ID", "Duration", "Recv: Pack", "Lost", "%", "Jitter", "Send: Pack", "Lost", "Jitter");
+ /* iterate on the container and invoke the callback on each item */
+ ao2_t_callback(dialogs, OBJ_NODATA, show_chanstats_cb, &arg, "callback to sip show chanstats");
+ ast_cli(a->fd, "%d active SIP channel%s\n", arg.numchans, (arg.numchans != 1) ? "s" : "");
+ return CLI_SUCCESS;
+}
+#undef FORMAT
+#undef FORMAT2
+
/*! \brief List global settings for the SIP channel */
static char *sip_show_settings(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
@@ -14334,13 +14444,6 @@ static const struct cfsubscription_types *find_subscription_type(enum subscripti
* that support iteration through callbacks will be a lot easier.
*/
-/*! \brief argument for the 'show channels|subscriptions' callback. */
-struct __show_chan_arg {
- int fd;
- int subscriptions;
- int numchans; /* return value */
-};
-
#define FORMAT4 "%-15.15s %-10.10s %-15.15s %-15.15s %-13.13s %-15.15s %-10.10s %-6.6d\n"
#define FORMAT3 "%-15.15s %-10.10s %-15.15s %-15.15s %-13.13s %-15.15s %-10.10s %-6.6s\n"
#define FORMAT2 "%-15.15s %-10.10s %-15.15s %-15.15s %-7.7s %-15.15s %-6.6s\n"
@@ -14436,8 +14539,6 @@ static char *sip_show_channels(struct ast_cli_entry *e, int cmd, struct ast_cli_
* given position. As many functions of this kind, each invokation has
* O(state) time complexity so be careful in using it.
*/
-
-
static char *complete_sipch(const char *line, const char *word, int pos, int state)
{
int which=0;
@@ -23001,12 +23102,13 @@ static int reload(void)
static struct ast_cli_entry cli_sip_do_history_deprecated = AST_CLI_DEFINE(sip_do_history_deprecated, "Enable/Disable SIP history");
/*! \brief SIP Cli commands definition */
static struct ast_cli_entry cli_sip[] = {
- AST_CLI_DEFINE(sip_show_channels, "List active SIP channels/subscriptions"),
+ AST_CLI_DEFINE(sip_show_channels, "List active SIP channels or subscriptions"),
+ AST_CLI_DEFINE(sip_show_channelstats, "List statistics for active SIP channels"),
AST_CLI_DEFINE(sip_show_domains, "List our local SIP domains."),
AST_CLI_DEFINE(sip_show_inuse, "List all inuse/limits"),
AST_CLI_DEFINE(sip_show_objects, "List all SIP object allocations"),
AST_CLI_DEFINE(sip_show_peers, "List defined SIP peers"),
- AST_CLI_DEFINE(sip_dbdump, "dump peer info into realtime db sql format"),
+ AST_CLI_DEFINE(sip_dbdump, "Dump peer info into realtime database SQL format"),
AST_CLI_DEFINE(sip_show_registry, "List SIP registration status"),
AST_CLI_DEFINE(sip_unregister, "Unregister (force expiration) a SIP peer from the registery\n"),
AST_CLI_DEFINE(sip_show_settings, "Show SIP global settings"),
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index 38fe8639c..800519572 100644
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -69,6 +69,17 @@ enum ast_rtp_get_result {
AST_RTP_TRY_NATIVE,
};
+/*! \brief Variables used in ast_rtcp_get function */
+enum ast_rtp_qos_vars {
+ AST_RTP_TXCOUNT,
+ AST_RTP_RXCOUNT,
+ AST_RTP_TXJITTER,
+ AST_RTP_RXJITTER,
+ AST_RTP_RXPLOSS,
+ AST_RTP_TXPLOSS,
+ AST_RTP_RTT
+};
+
struct ast_rtp;
/*! T.140 Redundancy structure*/
struct rtp_red;
@@ -268,6 +279,10 @@ int ast_rtp_early_bridge(struct ast_channel *c0, struct ast_channel *c1);
/*! \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 Return RTP and RTCP QoS values */
+unsigned int ast_rtp_get_qosvalue(struct ast_rtp *rtp, enum ast_rtp_qos_vars value);
+
/*! \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);
diff --git a/main/rtp.c b/main/rtp.c
index 146e8c093..4a8bf2b7b 100644
--- a/main/rtp.c
+++ b/main/rtp.c
@@ -2626,6 +2626,37 @@ void ast_rtp_reset(struct ast_rtp *rtp)
rtp->rxseqno = 0;
}
+/*! Get QoS values from RTP and RTCP data (used in "sip show channelstats") */
+unsigned int ast_rtp_get_qosvalue(struct ast_rtp *rtp, enum ast_rtp_qos_vars value)
+{
+ if (rtp == NULL) {
+ if (option_debug > 1)
+ ast_log(LOG_DEBUG, "NO RTP Structure? Kidding me? \n");
+ return 0;
+ }
+ if (option_debug > 1 && rtp->rtcp == NULL) {
+ ast_log(LOG_DEBUG, "NO RTCP structure. Maybe in RTP p2p bridging mode? \n");
+ }
+
+ switch (value) {
+ case AST_RTP_TXCOUNT:
+ return (unsigned int) rtp->txcount;
+ case AST_RTP_RXCOUNT:
+ return (unsigned int) rtp->rxcount;
+ case AST_RTP_TXJITTER:
+ return (unsigned int) (rtp->rxjitter * 100.0);
+ case AST_RTP_RXJITTER:
+ return (unsigned int) rtp->rtcp ? (rtp->rtcp->reported_jitter / (unsigned int) 65536.0) : 0;
+ case AST_RTP_RXPLOSS:
+ return rtp->rtcp ? (rtp->rtcp->expected_prior - rtp->rtcp->received_prior) : 0;
+ case AST_RTP_TXPLOSS:
+ return rtp->rtcp ? rtp->rtcp->reported_lost : 0;
+ case AST_RTP_RTT:
+ return (unsigned int) rtp->rtcp ? rtp->rtcp->rtt * 100 : 0;
+ }
+ return 0; /* To make the compiler happy */
+}
+
static double __ast_rtp_get_qos(struct ast_rtp *rtp, const char *qos, int *found)
{
*found = 1;