[asterisk-commits] oej: branch oej/pinefrog-deluxe-rtcp-test r241852 - in /team/oej/pinefrog-del...
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Thu Jan 21 04:00:12 CST 2010
Author: oej
Date: Thu Jan 21 04:00:10 2010
New Revision: 241852
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=241852
Log:
Merging changes from multiple branches
Modified:
team/oej/pinefrog-deluxe-rtcp-test/channels/chan_sip.c
team/oej/pinefrog-deluxe-rtcp-test/configs/sip.conf.sample
team/oej/pinefrog-deluxe-rtcp-test/include/asterisk/rtp.h
team/oej/pinefrog-deluxe-rtcp-test/main/rtp.c
Modified: team/oej/pinefrog-deluxe-rtcp-test/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/pinefrog-deluxe-rtcp-test/channels/chan_sip.c?view=diff&rev=241852&r1=241851&r2=241852
==============================================================================
--- team/oej/pinefrog-deluxe-rtcp-test/channels/chan_sip.c (original)
+++ team/oej/pinefrog-deluxe-rtcp-test/channels/chan_sip.c Thu Jan 21 04:00:10 2010
@@ -267,6 +267,22 @@
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"}
+};
+
/* Do _NOT_ make any changes to this enum, or the array following it;
if you think you are doing the right thing, you are probably
not doing the right thing. If you think there are changes
@@ -573,6 +589,8 @@
static char global_useragent[AST_MAX_EXTENSION]; /*!< Useragent for the SIP channel */
static int allow_external_domains; /*!< Accept calls to external SIP domains? */
static int global_callevents; /*!< Whether we send manager events or not */
+static int global_rtcpevents; /*!< Whether we send manager RTCP events or not */
+static int global_rtcptimer; /*!< How often, during a call, to report RTCP stats */
static int global_t1min; /*!< T1 roundtrip time minimum */
static int global_autoframing; /*!< Turn autoframing on or off. */
static enum transfermodes global_allowtransfer; /*!< SIP Refer restriction scheme */
@@ -1023,6 +1041,7 @@
int initid; /*!< Auto-congest ID if appropriate (scheduler) */
int waitid; /*!< Wait ID for scheduler after 491 or other delays */
int autokillid; /*!< Auto-kill ID (scheduler) */
+ int rtcpeventid; /*!< Scheduler ID for RTCP Events */
enum transfermodes allowtransfer; /*!< REFER: restriction scheme */
struct sip_refer *refer; /*!< REFER: SIP transfer data structure */
enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */
@@ -1369,6 +1388,8 @@
char **m_buf, size_t *m_size, char **a_buf, size_t *a_size,
int debug);
static enum sip_result add_sdp(struct sip_request *resp, struct sip_pvt *p, int add_audio, int add_t38);
+static int send_rtcp_events(const void *data);
+static void start_rtcp_events(struct sip_pvt *dialog);
static void stop_media_flows(struct sip_pvt *p);
/*--- Authentication stuff */
@@ -1437,6 +1458,7 @@
static const struct cfsubscription_types *find_subscription_type(enum subscriptiontype subtype);
static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions);
static int sip_show_channels(int fd, int argc, char *argv[]);
+static int sip_show_channelstats(int fd, int argc, char *argv[]);
static int sip_show_subscriptions(int fd, int argc, char *argv[]);
static int __sip_show_channels(int fd, int argc, char *argv[], int subscriptions);
static char *complete_sipch(const char *line, const char *word, int pos, int state);
@@ -1466,6 +1488,7 @@
static int sip_do_reload(enum channelreloadreason reason);
static int sip_reload(int fd, int argc, char *argv[]);
static int acf_channel_read(struct ast_channel *chan, char *funcname, char *preparse, char *buf, size_t buflen);
+static int get_rtp_quality(struct sip_pvt *dialog, const char *chantype, const char *qostype, char *buf, size_t buf_len);
/*--- Debugging
Functions for enabling debug per IP or fully, or enabling history logging for
@@ -3296,6 +3319,7 @@
if (p->stateid > -1)
ast_extension_state_del(p->stateid, NULL);
+ AST_SCHED_DEL(sched, p->rtcpeventid);
AST_SCHED_DEL(sched, p->initid);
AST_SCHED_DEL(sched, p->waitid);
AST_SCHED_DEL(sched, p->autokillid);
@@ -3732,6 +3756,7 @@
ast_log(LOG_DEBUG, "Hanging up channel in state %s (not UP)\n", ast_state2str(ast->_state));
}
+ ast_log(LOG_DEBUG, "----%%%%%%----- Stopping media flows in sip_hangup\n");
stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
append_history(p, needcancel ? "Cancel" : "Hangup", "Cause %s", p->owner ? ast_cause2str(p->hangupcause) : "Unknown");
@@ -3873,6 +3898,7 @@
res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL);
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
+ start_rtcp_events(p);
}
ast_mutex_unlock(&p->lock);
return res;
@@ -4675,6 +4701,7 @@
ast_mutex_init(&p->lock);
p->method = intended_method;
+ p->rtcpeventid = -1;
p->initid = -1;
p->waitid = -1;
p->autokillid = -1;
@@ -4707,8 +4734,9 @@
if (sip_methods[intended_method].need_rtp) {
p->rtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
/* If the global videosupport flag is on, we always create a RTP interface for video */
- if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT))
+ if (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT)) {
p->vrtp = ast_rtp_new_with_bindaddr(sched, io, 1, 0, bindaddr.sin_addr);
+ }
if (ast_test_flag(&p->flags[1], SIP_PAGE2_T38SUPPORT))
p->udptl = ast_udptl_new_with_bindaddr(sched, io, 0, bindaddr.sin_addr);
if (!p->rtp || (ast_test_flag(&p->flags[1], SIP_PAGE2_VIDEOSUPPORT) && !p->vrtp)) {
@@ -4766,6 +4794,12 @@
else
ast_string_field_set(p, callid, callid);
/* Assign default music on hold class */
+ if (p->rtp) {
+ ast_rtcp_setcname(p->rtp, p->callid, strlen(p->callid));
+ }
+ if (p->vrtp) {
+ ast_rtcp_setcname(p->rtp, p->callid, strlen(p->callid));
+ }
ast_string_field_set(p, mohinterpret, default_mohinterpret);
ast_string_field_set(p, mohsuggest, default_mohsuggest);
p->capability = global_capability;
@@ -11081,6 +11115,117 @@
}
#undef FORMAT
+/*! \brief Add manager headers for QoS to existing manager reply */
+static void manager_add_qos(struct mansession *s, char *mediatype, struct sip_pvt *dialog)
+{
+ char buf[SIPBUFSIZE];
+
+ get_rtp_quality(dialog, mediatype, "local_ssrc", buf, SIPBUFSIZE );
+ astman_append(s, "LocalSSRC(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "remote_ssrc", buf, SIPBUFSIZE );
+ astman_append(s, "RemoteSSRC(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "local_jitter", buf, SIPBUFSIZE );
+ astman_append(s, "LocalJitter(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "local_count", buf, SIPBUFSIZE );
+ astman_append(s, "LocalPacketCount(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "remote_count", buf, SIPBUFSIZE );
+ astman_append(s, "RemotePacketCount(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "local_lostpackets", buf, SIPBUFSIZE );
+ astman_append(s, "LocalLostPackets(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "remote_lostpackets", buf, SIPBUFSIZE );
+ astman_append(s, "RemoteLostPackets(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "remote_jitter", buf, SIPBUFSIZE );
+ astman_append(s, "RemoteJitter(%s): %s\r\n", mediatype, buf);
+ get_rtp_quality(dialog, mediatype, "rtt", buf, SIPBUFSIZE );
+ astman_append(s, "MediaRtt(%s): %s\r\n", mediatype, buf);
+}
+
+static char mandescr_sip_channel[] =
+"Description: Show one SIP channel with details on current status.\n"
+"Variables: \n"
+" Channel: <name> The channel name (AST) you want to check.\n"
+" ActionID: <id> Optional action ID for this AMI transaction.\n"
+" Datatype: <name> Optional parameter name required. If not specified, all data will be sent\n"
+"\n"
+" Datatype Description\n"
+" qos Current QoS value for this channel (sender and receiver)\n"
+"\n";
+
+/*! \brief Show SIP channel data in the manager API */
+static int manager_sip_channel(struct mansession *s, const struct message *m)
+{
+ const char *channel;
+ const char *datatype;
+ const char *actionid;
+ int all = FALSE;
+ struct ast_channel *chan = NULL;
+ struct sip_pvt *dialog;
+ int sentsuccess = FALSE;
+
+ actionid = astman_get_header(m,"ActionID");
+ channel = astman_get_header(m,"Channel");
+
+ /* Do we have a channel name? */
+ if (ast_strlen_zero(channel)) {
+ astman_send_error(s, m, "Channel: <channelname> missing.");
+ return 0;
+ }
+ /* Can we find a channel with that name? */
+ chan = ast_get_channel_by_name_locked(channel);
+ if (!chan) {
+ astman_send_error(s, m, "No such channel");
+ return 0;
+ }
+ /* Sanity check - is this pvt a SIP channel? */
+ if (chan->tech != &sip_tech && chan->tech != &sip_tech_info) {
+ astman_send_error(s, m, "Cannot get SIPchannel-info on a non-SIP channel\n");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ /* Try to grab the channel pvt handle */
+ dialog = chan->tech_pvt;
+ if (!dialog) {
+ astman_send_error(s, m, "No active SIP channel\n");
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ /* Ok, we have a channel with a SIP dialog attached,
+ time to find out what they want
+ ...and when my plane is about to leave from ARN */
+ datatype = astman_get_header(m,"Datatype");
+ if (ast_strlen_zero(datatype) || !strcasecmp(datatype, "all")) {
+ all = TRUE;
+ }
+
+
+ if (all || !strcasecmp(datatype, "qos")) {
+ /* When merged with pinefrog, we need to check if RTCP is active at all and
+ give a status message for rtcp. Otherwise, the reports are a bit silly */
+ if (!sentsuccess) { /* Yes, it's kind of silly, but this section will be copied to new datatypes... */
+ astman_append(s, "Response: Success\r\n");
+ if (!ast_strlen_zero(actionid)) {
+ astman_append(s, "ActionID: %s\r\n",actionid);
+ }
+ sentsuccess = TRUE;
+ }
+ if (dialog->rtp) {
+ manager_add_qos(s, "audio", dialog);
+ }
+ if (dialog->vrtp) {
+ manager_add_qos(s, "video", dialog);
+ }
+ } else if (!all) {
+ char errbuf[SIPBUFSIZE];
+ snprintf(errbuf, SIPBUFSIZE, "Unknown datatype: %s\n", datatype);
+ astman_send_error(s, m, errbuf);
+ ast_channel_unlock(chan);
+ return 0;
+ }
+ astman_append(s, "\r\n\r\n" );
+ ast_channel_unlock(chan);
+ return(0);
+}
+
static char mandescr_show_peer[] =
"Description: Show one SIP peer with details on current status.\n"
"Variables: \n"
@@ -11451,6 +11596,8 @@
ast_cli(fd, " From: Domain: %s\n", default_fromdomain);
ast_cli(fd, " Record SIP history: %s\n", recordhistory ? "On" : "Off");
ast_cli(fd, " Call Events: %s\n", global_callevents ? "On" : "Off");
+ ast_cli(fd, " RTCP Events: %s\n", global_rtcpevents ? "On" : "Off");
+ ast_cli(fd, " RTCP Event timer: %d\n", global_rtcptimer);
ast_cli(fd, " IP ToS SIP: %s\n", ast_tos2str(global_tos_sip));
ast_cli(fd, " IP ToS RTP audio: %s\n", ast_tos2str(global_tos_audio));
ast_cli(fd, " IP ToS RTP video: %s\n", ast_tos2str(global_tos_video));
@@ -11619,6 +11766,84 @@
#undef FORMAT
#undef FORMAT2
#undef FORMAT3
+}
+
+/*! \brief SIP show channelstats CLI (main function) */
+ /*Print some info on the call here */
+static int sip_show_channelstats(int fd, int argc, char *argv[])
+{
+//#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"
+#define FORMAT2 "%-15.15s %-11.11s %-8.8s %-10.10s %-10.10s ( %%) %-6.6s %-10.10s %-10.10s ( %%) %-6.6s\n"
+#define FORMAT "%-15.15s %-11.11s %-8.8s %-10.10u%-1.1s %-10.10u (%5.2f%%) %-6.6u %-10.10u%-1.1s %-10.10u (%5.2f%%) %-6.6u\n"
+ struct sip_pvt *cur;
+ int numchans = 0;
+ char durbuf[10];
+ int duration;
+ int durh, durm, durs;
+
+ if (argc != 3)
+ return RESULT_SHOWUSAGE;
+ ast_mutex_lock(&iflock);
+ cur = iflist;
+ ast_cli(fd, FORMAT2, "Peer", "Call ID", "Duration", "Recv: Pack", "Lost", "Jitter", "Send: Pack", "Lost", "Jitter");
+ for (; cur; cur = cur->next) {
+ unsigned int rxcount, rxploss;
+ unsigned int txcount, txploss;
+ struct ast_channel *c = cur->owner;
+
+ if (cur->subscribed != NONE) /* Subscriptions */
+ continue;
+
+ 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");
+ continue;
+ }
+ rxcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXCOUNT);
+ txcount = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXCOUNT);
+ rxploss = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_RXPLOSS);
+ txploss = ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXPLOSS);
+
+ /* 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":" ", rxploss,
+ (rxcount + rxploss) > 0 ? (double) rxploss / (rxcount + rxploss) * 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":" ",
+ txploss,
+ txcount > 0 ? (double) txploss / txcount * 100 : 0,
+ ast_rtp_get_qosvalue(cur->rtp, AST_RTP_TXJITTER)
+ );
+ numchans++;
+ /* If we have a lot of channels, give other processes a chance to get the iflock. Calls are more important than the CLI. */
+ if (numchans % 100 == 0) {
+ ast_mutex_unlock(&iflock);
+ /* Sleep for a short amount of time */
+ usleep(5);
+ ast_mutex_lock(&iflock);
+ }
+ }
+ ast_mutex_unlock(&iflock);
+ ast_cli(fd, "%d active SIP channel%s\n", numchans, (numchans != 1) ? "s" : "");
+ return RESULT_SUCCESS;
+#undef FORMAT
+#undef FORMAT2
}
/*! \brief Support routine for 'sip show channel' CLI */
@@ -12418,6 +12643,11 @@
"Usage: sip show channels\n"
" Lists all currently active SIP channels.\n";
+static char show_channelstats_usage[] =
+"Usage: sip show channelstats\n"
+" Lists all currently active SIP channel's RTCP statistics.\n"
+" Note that calls in the much optimized P2P bridge mode will not show any packets here.";
+
static char show_channel_usage[] =
"Usage: sip show channel <channel>\n"
" Provides detailed status on a given SIP channel.\n";
@@ -13024,6 +13254,7 @@
ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
xmitres = transmit_request(p, SIP_ACK, seqno, XMIT_UNRELIABLE, TRUE);
check_pendings(p);
+ start_rtcp_events(p);
break;
case 407: /* Proxy authentication */
case 401: /* Www auth */
@@ -13413,14 +13644,135 @@
}
}
+/*! \brief send manager report of RTCP
+ endreport means endof-call report
+*/
+static void sip_rtcp_report(struct sip_pvt *p, struct ast_rtp *rtp, const char *mediatype, int endreport)
+{
+ struct ast_rtp_quality qual;
+ char *rtpqstring = NULL;
+ char localjitter[10], remotejitter[10];
+ int qosrealtime = ast_check_realtime("rtpqos");
+ long int duration; /* Duration in secs */
+ struct ast_channel *bridgepeer = NULL;
+
+ if (p && p->owner) {
+ bridgepeer = ast_bridged_channel(p->owner);
+
+ }
+
+ if (global_rtcpevents) {
+ rtpqstring = ast_rtp_get_quality(rtp, &qual);
+ /*
+ If numberofreports == 0 we have no incoming RTCP active, thus we can't
+ get any reliable data to handle packet loss or any RTT timing.
+ */
+ duration = (long int)(ast_tvdiff_ms(ast_tvnow(), qual.start) / 1000);
+ manager_event(EVENT_FLAG_CALL, "RTPQuality",
+ "Channel: %s\r\n" /* AST_CHANNEL for this call */
+ "BridgedChannel: %s\r\n"
+ "RTPreporttype: %s\r\n"
+ "RTPrtcpstatus: %s\r\n"
+ "Duration: %ld\r\n" /* used in cdr_manager */
+ "PvtCallid: %s\r\n" /* ??? Generic PVT identifier */
+ "RTPipaddress: %s\r\n"
+ "RTPmedia: %s\r\n" /* Audio, video, text */
+ "RTPsendformat: %s\r\n"
+ "RTPrecvformat: %s\r\n"
+ "RTPlocalssrc: %u\r\n"
+ "RTPremotessrc: %u\r\n"
+ "RTPrtt: %f\r\n"
+ "RTPrttMax: %f\r\n"
+ "RTPrttMin: %f\r\n"
+ "RTPLocalJitter: %f\r\n"
+ "RTPRemoteJitter: %f\r\n"
+ "RTPInPacketLoss: %d\r\n"
+ "RTPInLocalPlPercent: %5.2f\r\n"
+ "RTPOutPacketLoss: %d\r\n"
+ "RTPOutPlPercent: %5.2f\r\n"
+ "\r\n",
+ p->owner ? p->owner->name : "",
+ bridgepeer ? bridgepeer->name : "",
+ endreport ? "Final" : "Update",
+ qual.numberofreports == 0 ? "Inactive" : "Active",
+ duration,
+ p->callid,
+ ast_inet_ntoa(qual.them.sin_addr),
+ mediatype,
+ ast_getformatname(qual.lasttxformat),
+ ast_getformatname(qual.lastrxformat),
+ qual.local_ssrc,
+ qual.remote_ssrc,
+ qual.rtt,
+ qual.rttmax,
+ qual.rttmin,
+ qual.local_jitter,
+ qual.remote_jitter,
+ qual.local_lostpackets,
+ /* The local counter of lost packets in inbound stream divided with received packets plus lost packets */
+ (qual.remote_count + qual.local_lostpackets) > 0 ? (double) qual.local_lostpackets / (qual.remote_count + qual.local_lostpackets) * 100 : 0,
+ qual.remote_lostpackets,
+ /* The remote counter of lost packets (if we got the reports)
+ divided with our counter of sent packets
+ */
+ (qual.local_count + qual.remote_lostpackets) > 0 ? (double) qual.remote_lostpackets / qual.local_count * 100 : 0
+ );
+ }
+ /* CDR records are not reliable when it comes to near-death-of-channel events, so we need to store the RTCP
+ report in realtime when we have it */
+ if (endreport && qosrealtime) {
+ if (rtpqstring == NULL) {
+ rtpqstring = ast_rtp_get_quality(rtp, &qual);
+ }
+ sprintf(localjitter, "%f", qual.local_jitter);
+ sprintf(remotejitter, "%f", qual.remote_jitter);
+ ast_update_realtime("rtpqos", "Channel", p->owner ? p->owner->name : "", "pvtcallid", p->callid, "rtpmedia", mediatype, "localssrc", qual.local_ssrc, "remotessrc", qual.remote_ssrc, "rtt", qual.rtt, "localjitter", localjitter, "remotejitter", remotejitter, NULL);
+ }
+}
+
+/*! \brief Send RTCP manager events */
+static int send_rtcp_events(const void *data)
+{
+ struct sip_pvt *dialog = (struct sip_pvt *) data;
+
+ if (dialog->rtp && ast_rtp_isactive(dialog->rtp)) {
+ sip_rtcp_report(dialog, dialog->rtp, "audio", FALSE);
+ }
+ if (dialog->vrtp && ast_rtp_isactive(dialog->vrtp)) {
+ sip_rtcp_report(dialog, dialog->rtp, "video", FALSE);
+ }
+ return global_rtcptimer;
+}
+
+/*! \brief Activate RTCP events at start of call */
+static void start_rtcp_events(struct sip_pvt *dialog)
+{
+ if (!global_rtcpevents || !global_rtcptimer) {
+ return;
+ }
+ /* Check if it's already active */
+ if (dialog->rtcpeventid != -1) {
+ return;
+ }
+
+ /*! \brief Schedule events */
+ dialog->rtcpeventid = ast_sched_add(sched, global_rtcptimer * 1000, send_rtcp_events, dialog);
+}
+
+
/*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */
static void stop_media_flows(struct sip_pvt *p)
{
+
/* Immediately stop RTP, VRTP and UDPTL as applicable */
- if (p->rtp)
+ if (p->rtp && ast_rtp_isactive(p->rtp)) {
ast_rtp_stop(p->rtp);
- if (p->vrtp)
+ sip_rtcp_report(p, p->rtp, "audio", TRUE);
+ }
+ if (p->vrtp) {
ast_rtp_stop(p->vrtp);
+ sip_rtcp_report(p, p->rtp, "video", TRUE);
+ }
if (p->udptl)
ast_udptl_stop(p->udptl);
}
@@ -15858,9 +16210,8 @@
static int acf_channel_read(struct ast_channel *chan, char *funcname, char *preparse, char *buf, size_t buflen)
{
- struct ast_rtp_quality qos;
struct sip_pvt *p = chan->tech_pvt;
- char *all = "", *parse = ast_strdupa(preparse);
+ char *parse = ast_strdupa(preparse);
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(param);
AST_APP_ARG(type);
@@ -15873,6 +16224,10 @@
ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname);
return 0;
}
+ if (p == NULL) {
+ ast_log(LOG_ERROR, "Cannot call %s on a non-SIP channel\n", funcname);
+ return -1;
+ }
if (strcasecmp(args.param, "rtpqos"))
return 0;
@@ -15884,40 +16239,50 @@
args.field = "all";
memset(buf, 0, buflen);
+ if (get_rtp_quality(p, args.type, args.field, buf, buflen)) {
+ ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
+ return -1;
+ }
+ return 0;
+}
+
+/*! \brief Get RTP quality string */
+static int get_rtp_quality(struct sip_pvt *dialog, const char *chantype, const char *qostype, char *buf, size_t buflen)
+{
+ struct ast_rtp_quality qos;
+ char *all = "";
memset(&qos, 0, sizeof(qos));
- if (p == NULL) {
- return -1;
- }
-
- if (strcasecmp(args.type, "AUDIO") == 0) {
- all = ast_rtp_get_quality(p->rtp, &qos);
- } else if (strcasecmp(args.type, "VIDEO") == 0) {
- all = ast_rtp_get_quality(p->vrtp, &qos);
- }
-
- if (strcasecmp(args.field, "local_ssrc") == 0)
+ if (strcasecmp(chantype, "AUDIO") == 0) {
+ all = ast_rtp_get_quality(dialog->rtp, &qos);
+ } else if (strcasecmp(chantype, "VIDEO") == 0) {
+ all = ast_rtp_get_quality(dialog->vrtp, &qos);
+ } else {
+ return -1; /* Unknown media */
+ }
+
+ if (strcasecmp(qostype, "local_ssrc") == 0) {
snprintf(buf, buflen, "%u", qos.local_ssrc);
- else if (strcasecmp(args.field, "local_lostpackets") == 0)
+ } else if (strcasecmp(qostype, "local_lostpackets") == 0) {
snprintf(buf, buflen, "%u", qos.local_lostpackets);
- else if (strcasecmp(args.field, "local_jitter") == 0)
+ } else if (strcasecmp(qostype, "local_jitter") == 0) {
snprintf(buf, buflen, "%.0lf", qos.local_jitter * 1000.0);
- else if (strcasecmp(args.field, "local_count") == 0)
+ } else if (strcasecmp(qostype, "local_count") == 0) {
snprintf(buf, buflen, "%u", qos.local_count);
- else if (strcasecmp(args.field, "remote_ssrc") == 0)
+ } else if (strcasecmp(qostype, "remote_ssrc") == 0) {
snprintf(buf, buflen, "%u", qos.remote_ssrc);
- else if (strcasecmp(args.field, "remote_lostpackets") == 0)
+ } else if (strcasecmp(qostype, "remote_lostpackets") == 0) {
snprintf(buf, buflen, "%u", qos.remote_lostpackets);
- else if (strcasecmp(args.field, "remote_jitter") == 0)
+ } else if (strcasecmp(qostype, "remote_jitter") == 0) {
snprintf(buf, buflen, "%.0lf", qos.remote_jitter * 1000.0);
- else if (strcasecmp(args.field, "remote_count") == 0)
+ } else if (strcasecmp(qostype, "remote_count") == 0) {
snprintf(buf, buflen, "%u", qos.remote_count);
- else if (strcasecmp(args.field, "rtt") == 0)
+ } else if (strcasecmp(qostype, "rtt") == 0) {
snprintf(buf, buflen, "%.0lf", qos.rtt * 1000.0);
- else if (strcasecmp(args.field, "all") == 0)
+ } else if (strcasecmp(qostype, "all") == 0) {
ast_copy_string(buf, all, buflen);
- else {
- ast_log(LOG_WARNING, "Unrecognized argument '%s' to %s\n", preparse, funcname);
+ } else {
+ /* Unrecognized argument, error! Let the caller print error message */
return -1;
}
return 0;
@@ -15961,6 +16326,7 @@
}
}
+ ast_log(LOG_DEBUG, "----%%%%%%----- Stopping media flows in handle_request_bye\n");
stop_media_flows(p); /* Immediately stop RTP, VRTP and UDPTL as applicable */
if (!ast_strlen_zero(get_header(req, "Also"))) {
@@ -18361,6 +18727,8 @@
/* Misc settings for the channel */
global_relaxdtmf = FALSE;
global_callevents = FALSE;
+ global_rtcpevents = FALSE;
+ global_rtcptimer = 0; /* Only report at end of call (if enabled) */
global_t1min = DEFAULT_T1MIN;
global_shrinkcallerid = 1;
@@ -18607,6 +18975,13 @@
ast_log(LOG_WARNING, "Qualification default should be 'yes', 'no', or a number of milliseconds at line %d of sip.conf\n", v->lineno);
default_qualify = 0;
}
+ } else if (!strcasecmp(v->name, "rtcpevents")) {
+ global_rtcpevents = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "rtcpeventtimer")) {
+ if (sscanf(v->value, "%30d", &global_rtcptimer) != 1) {
+ ast_log(LOG_WARNING, "RTCP event timer needs to be value (seconds between reports) at line %d of sip.conf\n", v->lineno);
+ global_rtcptimer = 0;
+ }
} else if (!strcasecmp(v->name, "callevents")) {
global_callevents = ast_true(v->value);
} else if (!strcasecmp(v->name, "maxcallbitrate")) {
@@ -19376,6 +19751,10 @@
sip_show_channels, "List active SIP channels",
show_channels_usage },
+ { { "sip", "show", "channelstats", NULL },
+ sip_show_channelstats, "List active SIP channel statistics (based on RTCP)",
+ show_channelstats_usage },
+
{ { "sip", "show", "domains", NULL },
sip_show_domains, "List our local SIP domains.",
show_domains_usage },
@@ -19524,6 +19903,8 @@
"List SIP peers (text format)", mandescr_show_peers);
ast_manager_register2("SIPshowpeer", EVENT_FLAG_SYSTEM, manager_sip_show_peer,
"Show SIP peer (text format)", mandescr_show_peer);
+ ast_manager_register2("SIPchannel", EVENT_FLAG_CALL, manager_sip_channel,
+ "Show information about SIP channel", mandescr_sip_channel);
sip_poke_all_peers();
sip_send_all_registers();
@@ -19562,6 +19943,7 @@
ast_udptl_proto_unregister(&sip_udptl);
/* Unregister AMI actions */
+ ast_manager_unregister("SIPchannel");
ast_manager_unregister("SIPpeers");
ast_manager_unregister("SIPshowpeer");
Modified: team/oej/pinefrog-deluxe-rtcp-test/configs/sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/oej/pinefrog-deluxe-rtcp-test/configs/sip.conf.sample?view=diff&rev=241852&r1=241851&r2=241852
==============================================================================
--- team/oej/pinefrog-deluxe-rtcp-test/configs/sip.conf.sample (original)
+++ team/oej/pinefrog-deluxe-rtcp-test/configs/sip.conf.sample Thu Jan 21 04:00:10 2010
@@ -149,6 +149,9 @@
; for peers and users as well
;callevents=no ; generate manager events when sip ua
; performs events (e.g. hold)
+;rtcpevents=no ; Enable this to get QoS reports for each call in AMI
+;rtcpeventtimer=0 ; How often during a call to send reports. 0 = only at end of call
+ ; Other value means how many seconds between each report.
;alwaysauthreject = yes ; When an incoming INVITE or REGISTER is to be rejected,
; for any reason, always reject with an identical response
; equivalent to valid username and invalid password/hash
Modified: team/oej/pinefrog-deluxe-rtcp-test/include/asterisk/rtp.h
URL: http://svnview.digium.com/svn/asterisk/team/oej/pinefrog-deluxe-rtcp-test/include/asterisk/rtp.h?view=diff&rev=241852&r1=241851&r2=241852
==============================================================================
--- team/oej/pinefrog-deluxe-rtcp-test/include/asterisk/rtp.h (original)
+++ team/oej/pinefrog-deluxe-rtcp-test/include/asterisk/rtp.h Thu Jan 21 04:00:10 2010
@@ -61,6 +61,17 @@
AST_RTP_TRY_PARTIAL,
/*! RTP structure exists and native bridge can occur */
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;
@@ -77,16 +88,28 @@
AST_LIST_ENTRY(ast_rtp_protocol) list;
};
+/*! \brief Data structure only used for RTCP reports */
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 */
+ unsigned int numberofreports; /*!< Number of reports received from remote end */
+ unsigned int local_ssrc; /*!< Our SSRC */
+ unsigned int local_lostpackets; /*!< Our lost packets */
+ double local_jitter; /*!< Our calculated jitter */
+ double local_jitter_max; /*!< Our calculated jitter */
+ double local_jitter_min; /*!< 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 */
+ double remote_jitter_max; /*!< Their reported jitter */
+ double remote_jitter_min; /*!< Their reported jitter */
+ unsigned int remote_count; /*!< Number of transmitted packets */
+ double rtt; /*!< Round trip time */
+ double rttmax; /*!< Max observed round trip time */
+ double rttmin; /*!< Max observed round trip time */
+ int lasttxformat; /*!< Last used codec on transmitted stream */
+ int lastrxformat; /*!< Last used codec on received stream */
+ struct sockaddr_in them; /*!< The Ip address used for media by remote end */
+ struct timeval start; /*!< When the call started */
};
@@ -179,6 +202,10 @@
int ast_rtp_settos(struct ast_rtp *rtp, int tos);
+void ast_rtcp_setcname(struct ast_rtp *rtp, const char *cname, size_t length);
+
+
+
/*! \brief When changing sources, don't generate a new SSRC */
void ast_rtp_set_constantssrc(struct ast_rtp *rtp);
@@ -244,9 +271,13 @@
int ast_rtp_early_bridge(struct ast_channel *dest, struct ast_channel *src);
void ast_rtp_stop(struct ast_rtp *rtp);
+int ast_rtp_isactive(struct ast_rtp *rtp);
/*! \brief Return RTCP quality string */
char *ast_rtp_get_quality(struct ast_rtp *rtp, struct ast_rtp_quality *qual);
+
+/*! \brief Return RTP and RTCP QoS values */
+unsigned int ast_rtp_get_qosvalue(struct ast_rtp *rtp, enum ast_rtp_qos_vars value);
/*! \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);
Modified: team/oej/pinefrog-deluxe-rtcp-test/main/rtp.c
URL: http://svnview.digium.com/svn/asterisk/team/oej/pinefrog-deluxe-rtcp-test/main/rtp.c?view=diff&rev=241852&r1=241851&r2=241852
==============================================================================
--- team/oej/pinefrog-deluxe-rtcp-test/main/rtp.c (original)
+++ team/oej/pinefrog-deluxe-rtcp-test/main/rtp.c Thu Jan 21 04:00:10 2010
@@ -64,12 +64,31 @@
#define RTCP_MIN_INTERVALMS 500 /*!< Min milli-seconds between RTCP reports we send */
#define RTCP_MAX_INTERVALMS 60000 /*!< Max milli-seconds between RTCP reports we send */
-#define RTCP_PT_FUR 192
-#define RTCP_PT_SR 200
-#define RTCP_PT_RR 201
-#define RTCP_PT_SDES 202
-#define RTCP_PT_BYE 203
-#define RTCP_PT_APP 204
+#define RTCP_PT_FUR 192 /*!< FIR - Full Intra-frame request (h.261) */
+#define RTCP_PT_NACK 193 /*!< NACK - Negative acknowledgement (h.261) */
+#define RTCP_PT_IJ 195 /*!< IJ - RFC 5450 Extended Inter-arrival jitter report */
+#define RTCP_PT_SR 200 /*!< SR - RFC 3550 Sender report */
+#define RTCP_PT_RR 201 /*!< RR - RFC 3550 Receiver report */
+#define RTCP_PT_SDES 202 /*!< SDES - Source Description */
+#define RTCP_PT_BYE 203 /*!< BYE - Goodbye */
+#define RTCP_PT_APP 204 /*!< APP - Application defined */
+#define RTCP_PT_RTPFB 205 /*!< RTPFB - Generic RTP feedback RFC 4585 */
+#define RTCP_PT_PSFB 206 /*!< PSFB - Payload specific data RFC 4585 */
+#define RTCP_PT_XR 207 /*!< XR - Extended report - RFC3611 */
+
+/*! \brief RFC 3550 RTCP SDES Item types */
+enum rtcp_sdes {
+ SDES_END = 0, /*!< End of SDES list */
+ SDES_CNAME = 1, /*!< Canonical name */
+ SDES_NAME = 2, /*!< User name */
+ SDES_EMAIL = 3, /*!< User's e-mail address */
+ SDES_PHONE = 4, /*!< User's phone number */
+ SDES_LOC = 5, /*!< Geographic user location */
+ SDES_TOOL = 6, /*!< Name of application or tool */
+ SDES_NOTE = 7, /*!< Notice about the source */
+ SDES_PRIV = 8, /*!< SDES Private extensions */
+ SDES_H323_CADDR = 9, /*!< H.323 Callable address */
+};
#define RTP_MTU 1200
@@ -155,14 +174,17 @@
struct timeval rxcore;
struct timeval txcore;
double drxcore; /*!< The double representation of the first received packet */
+ struct timeval start; /*!< When the stream started (we can't depend on CDRs) */
struct timeval lastrx; /*!< timeval when we last received a packet */
struct timeval dtmfmute;
struct ast_smoother *smoother;
int *ioid;
+ int *ioidrtcp;
unsigned short seqno; /*!< Sequence number, RFC 3550, page 13. */
unsigned short rxseqno;
struct sched_context *sched;
- struct io_context *io;
+ struct io_context *io; /*!< for RTP callback */
+ struct io_context *iortcp; /*!< for RTCP callback */
void *data;
ast_rtp_callback callback;
ast_mutex_t bridge_lock;
@@ -175,16 +197,21 @@
struct ast_rtp *bridged; /*!< Who we are Packet bridged to */
int set_marker_bit:1; /*!< Whether to set the marker bit or not */
unsigned int constantssrc:1;
+ int isactive:1; /*!< Whether to RTP stream is active or not */
};
/* Forward declarations */
static int ast_rtcp_write(const void *data);
static void timeval2ntp(struct timeval tv, unsigned int *msw, unsigned int *lsw);
-static int ast_rtcp_write_sr(const void *data);
-static int ast_rtcp_write_rr(const void *data);
+static int ast_rtcp_write_sr(const void *data, int goodbye);
+static int ast_rtcp_write_rr(const void *data, int goodbye);
static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp);
static int ast_rtp_senddigit_continuation(struct ast_rtp *rtp);
int ast_rtp_senddigit_end(struct ast_rtp *rtp, char digit);
+static struct ast_frame *ast_rtcp_read_fd(int fd, struct ast_rtp *rtp);
+static int ast_rtcp_write_empty(struct ast_rtp *rtp, int fd);
+static int p2p_rtcp_callback(int *id, int fd, short events, void *cbdata);
+static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery);
#define FLAG_3389_WARNING (1 << 0)
#define FLAG_NAT_ACTIVE (3 << 1)
@@ -209,6 +236,10 @@
*/
struct ast_rtcp {
int s; /*!< Socket */
+ char ourcname[255]; /*!< Our SDES RTP session name (CNAME) */
+ size_t ourcnamelength; /*!< Length of CNAME (utf8) */
+ char theircname[255]; /*!< Their SDES RTP session name (CNAME) */
+ size_t theircnamelength; /*!< Length of CNAME (utf8) */
struct sockaddr_in us; /*!< Socket representation of the local endpoint. */
struct sockaddr_in them; /*!< Socket representation of the remote endpoint. */
struct sockaddr_in altthem; /*!< Alternate source for RTCP */
@@ -226,12 +257,23 @@
double accumulated_transit; /*!< accumulated a-dlsr-lsr */
double rtt; /*!< Last reported rtt */
unsigned int reported_jitter; /*!< The contents of their last jitter entry in the RR */
+ double reported_maxjitter; /*!< The contents of their max jitter entry received by us */
+ double reported_minjitter; /*!< The contents of their min jitter entry received by us */
+ unsigned int reported_jitter_count; /*! Number of reports received */
unsigned int reported_lost; /*!< Reported lost packets in their RR */
+ double reported_maxlost;
+ double reported_minlost;
+ double rxlost;
+ double maxrxlost;
+ double minrxlost;
+ unsigned int rxlost_count; /*! Number of reports received */
char quality[AST_MAX_USER_FIELD];
double maxrxjitter;
double minrxjitter;
+ unsigned int rxjitter_count; /*! Number of reports received */
double maxrtt;
double minrtt;
+ unsigned int rtt_count; /*! Number of reports received */
int sendfur;
};
@@ -530,6 +572,22 @@
return (subclass == AST_FORMAT_G722) ? 8000 : ast_format_rate(subclass);
}
+/*! \brief Schedule RTCP transmissions for RTP channel */
+static void ast_rtcp_schedule(struct ast_rtp *rtp)
+{
+ /* Do not schedule RR if RTCP isn't run */
+ if (rtp->rtcp && rtp->rtcp->them.sin_addr.s_addr && rtp->rtcp->schedid < 1) {
+ /* Schedule transmission of Receiver Report */
+ ast_rtcp_write_empty(rtp, rtp->rtcp->s);
+ rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
+ ast_log(LOG_DEBUG, "-------- SCHEDULING RTCP reports!!!\n");
+ } else {
+ // ast_log(LOG_DEBUG, "----- NOT SCHEDULING RTCP - RTCP %s RTCP address %s schedid %d\n",
+ //rtp->rtcp ? "yes" : "no",
+ //rtp->rtcp->them.sin_addr.s_addr ? "yes" : "no", rtp->rtcp->schedid);
+ }
+}
+
unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp)
{
unsigned int interval;
@@ -866,12 +924,27 @@
return 1;
}
+static int p2p_rtcp_callback(int *id, int fd, short events, void *cbdata)
+{
+ struct ast_rtp *rtp = cbdata;
+ ast_rtcp_read_fd(fd, rtp);
+ /* For now, skip any frames that is output. Which is bad for FUR's, but well. DEBUG */
+ return 1;
+}
+
struct ast_frame *ast_rtcp_read(struct ast_rtp *rtp)
{
+ return ast_rtcp_read_fd(rtp->rtcp->s, rtp);
+}
+
+static struct ast_frame *ast_rtcp_read_fd(int fd, struct ast_rtp *rtp)
+{
socklen_t len;
- int position, i, packetwords;
+ int position, i, j, packetwords;
int res;
struct sockaddr_in sin;
+ char *sdes;
+ unsigned int sdeslength, sdestype;
unsigned int rtcpdata[8192 + AST_FRIENDLY_OFFSET];
unsigned int *rtcpheader;
int pt;
@@ -885,6 +958,7 @@
unsigned int msw;
unsigned int lsw;
unsigned int comp;
+ double reported_jitter, reported_lost;
struct ast_frame *f = &ast_null_frame;
if (!rtp || !rtp->rtcp)
@@ -892,8 +966,9 @@
len = sizeof(sin);
- res = recvfrom(rtp->rtcp->s, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET,
- 0, (struct sockaddr *)&sin, &len);
+ res = recvfrom(fd, rtcpdata + AST_FRIENDLY_OFFSET, sizeof(rtcpdata) - sizeof(unsigned int) * AST_FRIENDLY_OFFSET,
+ 0, (struct sockaddr *)&sin, &len);
+
rtcpheader = (unsigned int *)(rtcpdata + AST_FRIENDLY_OFFSET);
if (res < 0) {
@@ -905,7 +980,7 @@
return &ast_null_frame;
}
- packetwords = res / 4;
+ packetwords = res / 4; /* Each RTCP segment is 32 bits */
if (rtp->nat) {
/* Send to whoever sent to us */
@@ -914,58 +989,84 @@
((rtp->rtcp->altthem.sin_addr.s_addr != sin.sin_addr.s_addr) ||
(rtp->rtcp->altthem.sin_port != sin.sin_port))) {
memcpy(&rtp->rtcp->them, &sin, sizeof(rtp->rtcp->them));
- if (option_debug || rtpdebug)
+ if (option_debug || rtpdebug) {
ast_log(LOG_DEBUG, "RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
- }
- }
-
- if (option_debug)
- ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res);
-
- /* Process a compound packet */
+ }
+ }
+ }
+
+ if (option_debug) {
+ ast_log(LOG_DEBUG, "Got RTCP report of %d bytes - %d messages\n", res, packetwords);
+ }
+
+ /* Process a compound packet
+ - A compound packet should start with a sender or receiver report. BYE can start as well
+ (seen in implementations)
+ - Packet length should be a multiple of four bytes
+ */
[... 1028 lines stripped ...]
More information about the asterisk-commits
mailing list