[asterisk-commits] oej: branch oej/pinefrog-rtcp-1.8 r402884 - /team/oej/pinefrog-rtcp-1.8/patches/
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Tue Nov 19 02:24:48 CST 2013
Author: oej
Date: Tue Nov 19 02:24:43 2013
New Revision: 402884
URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=402884
Log:
Adding patch
Added:
team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.8.diff (with props)
Added: team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.8.diff
URL: http://svnview.digium.com/svn/asterisk/team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.8.diff?view=auto&rev=402884
==============================================================================
--- team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.8.diff (added)
+++ team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.8.diff Tue Nov 19 02:24:43 2013
@@ -1,0 +1,2651 @@
+Index: channels/chan_sip.c
+===================================================================
+--- channels/chan_sip.c (.../branches/1.8) (revision 402883)
++++ channels/chan_sip.c (.../team/oej/pinefrog-rtcp-1.8) (revision 402883)
+@@ -275,6 +275,7 @@
+ #include "asterisk/xml.h"
+ #include "sip/include/dialog.h"
+ #include "sip/include/dialplan_functions.h"
++#include "sip/include/rtcp.h"
+
+
+ /*** DOCUMENTATION
+@@ -670,7 +671,6 @@
+ { AST_REDIRECTING_REASON_CALL_FWD_DTE, "unknown"}
+ };
+
+-
+ /*! \name DefaultSettings
+ Default setttings are used as a channel setting and as a default when
+ configuring devices
+@@ -6175,6 +6175,22 @@
+ if (dumphistory)
+ sip_dump_history(p);
+
++ AST_SCHED_DEL(sched, p->rtcpeventid);
++
++ if (p->audioqual) {
++ /* We have a quality report to write to realtime before we leave this world. */
++ qos_write_realtime(p, p->audioqual);
++ free(p->audioqual);
++ p->audioqual = NULL;
++ }
++ if (p->videoqual) {
++ /* We have a quality report to write to realtime before we leave this world. */
++ qos_write_realtime(p, p->videoqual);
++ free(p->videoqual);
++ p->videoqual = NULL;
++ }
++
++
+ if (p->options) {
+ if (p->options->outboundproxy) {
+ ao2_ref(p->options->outboundproxy, -1);
+@@ -6901,6 +6917,7 @@
+ ast_rtp_instance_update_source(p->rtp);
+ res = transmit_response_with_sdp(p, "200 OK", &p->initreq, XMIT_CRITICAL, oldsdp, TRUE);
+ ast_set_flag(&p->flags[1], SIP_PAGE2_DIALOG_ESTABLISHED);
++ start_rtcp_events(p, sched);
+ }
+ sip_pvt_unlock(p);
+ return res;
+@@ -8156,8 +8173,10 @@
+ set_socket_transport(&p->socket, SIP_TRANSPORT_UDP);
+ }
+
++ p->sip_cfg = &sip_cfg;
+ p->socket.fd = -1;
+ p->method = intended_method;
++ p->rtcpeventid = -1;
+ p->initid = -1;
+ p->waitid = -1;
+ p->reinviteid = -1;
+@@ -8219,6 +8238,14 @@
+ build_callid_pvt(p);
+ else
+ ast_string_field_set(p, callid, callid);
++
++ /* Set cnames for the RTCP SDES */
++ if (p->rtp) {
++ ast_rtp_instance_setcname(p->rtp, p->callid, strlen(p->callid));
++ }
++ if (p->vrtp) {
++ ast_rtp_instance_setcname(p->vrtp, p->callid, strlen(p->callid));
++ }
+ /* Assign default music on hold class */
+ ast_string_field_set(p, mohinterpret, default_mohinterpret);
+ ast_string_field_set(p, mohsuggest, default_mohsuggest);
+@@ -9142,6 +9169,7 @@
+ dialog->owner->uniqueid);
+ append_history(dialog, holdstate ? "Hold" : "Unhold", "%s", ast_str_buffer(req->data));
+ if (!holdstate) { /* Put off remote hold */
++ ast_rtp_instance_hold(dialog->rtp, 0); /* Turn off RTP hold */
+ ast_clear_flag(&dialog->flags[1], SIP_PAGE2_CALL_ONHOLD); /* Clear both flags */
+ return;
+ }
+@@ -9910,9 +9938,9 @@
+ ast_queue_control_data(p->owner, AST_CONTROL_HOLD,
+ S_OR(p->mohsuggest, NULL),
+ !ast_strlen_zero(p->mohsuggest) ? strlen(p->mohsuggest) + 1 : 0);
+- if (sendonly)
+- ast_rtp_instance_stop(p->rtp);
+- /* RTCP needs to go ahead, even if we're on hold!!! */
++ if (sendonly == 1 || sendonly == 2) { /* sendonly (from the other side) or inactive */
++ ast_rtp_instance_hold(p->rtp, 1);
++ }
+ /* Activate a re-invite */
+ ast_queue_frame(p->owner, &ast_null_frame);
+ change_hold_state(p, req, TRUE, sendonly);
+@@ -18148,8 +18176,10 @@
+ int x = 0, load_realtime;
+ format_t codec = 0;
+ int realtimepeers;
++ int realtimertpqos = FALSE;
+
+ realtimepeers = ast_check_realtime("sippeers");
++ realtimertpqos = ast_check_realtime("rtpcqr");
+
+ if (argc < 4)
+ return CLI_SHOWUSAGE;
+@@ -18764,10 +18794,12 @@
+ {
+ int realtimepeers;
+ int realtimeregs;
++ int realtimertpqos;
+ char codec_buf[SIPBUFSIZE];
+ const char *msg; /* temporary msg pointer */
+ struct sip_auth_container *credentials;
+
++
+ switch (cmd) {
+ case CLI_INIT:
+ e->command = "sip show settings";
+@@ -18784,6 +18816,7 @@
+
+ realtimepeers = ast_check_realtime("sippeers");
+ realtimeregs = ast_check_realtime("sipregs");
++ realtimertpqos = ast_check_realtime("rtpcqr");
+
+ ast_mutex_lock(&authl_lock);
+ credentials = authl;
+@@ -18853,6 +18886,8 @@
+ }
+ ast_cli(a->fd, " Record SIP history: %s\n", AST_CLI_ONOFF(recordhistory));
+ ast_cli(a->fd, " Call Events: %s\n", AST_CLI_ONOFF(sip_cfg.callevents));
++ ast_cli(a->fd, " RTCP Events: %s\n", AST_CLI_ONOFF(sip_cfg.rtcpevents));
++ ast_cli(a->fd, " RTCP Event timer: %d\n", sip_cfg.rtcptimer);
+ ast_cli(a->fd, " Auth. Failure Events: %s\n", AST_CLI_ONOFF(global_authfailureevents));
+
+ ast_cli(a->fd, " T.38 support: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_T38SUPPORT)));
+@@ -18862,6 +18897,7 @@
+ ast_cli(a->fd, " SIP realtime: Disabled\n" );
+ else
+ ast_cli(a->fd, " SIP realtime: Enabled\n" );
++ ast_cli(a->fd, " QoS realtime reports: %s\n", realtimertpqos ? "Enabled" : "Disabled" );
+ ast_cli(a->fd, " Qualify Freq : %d ms\n", global_qualifyfreq);
+ ast_cli(a->fd, " Q.850 Reason header: %s\n", AST_CLI_YESNO(ast_test_flag(&global_flags[1], SIP_PAGE2_Q850_REASON)));
+ ast_cli(a->fd, " Store SIP_CAUSE: %s\n", AST_CLI_YESNO(global_store_sip_cause));
+@@ -20803,6 +20839,7 @@
+ if (!req->ignore && p->invitestate != INV_CANCELLED && sip_cancel_destroy(p))
+ ast_log(LOG_WARNING, "Unable to cancel SIP destruction. Expect bad things.\n");
+ check_pendings(p);
++ start_rtcp_events(p, sched);
+ break;
+
+ case 180: /* 180 Ringing */
+@@ -21080,6 +21117,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, sched);
+ break;
+
+ case 407: /* Proxy authentication */
+@@ -21730,16 +21768,21 @@
+ }
+ }
+
+-/*! \brief Immediately stop RTP, VRTP and UDPTL as applicable */
++/*! \brief Immediately stop RTP, VRTP, TEXT 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_instance_isactive(p->rtp)) {
++ sip_rtcp_report(p, p->rtp, SDP_AUDIO, TRUE);
+ ast_rtp_instance_stop(p->rtp);
+- if (p->vrtp)
++ }
++ if (p->vrtp && !ast_rtp_instance_isactive(p->vrtp)) {
++ sip_rtcp_report(p, p->vrtp, SDP_VIDEO, TRUE);
+ ast_rtp_instance_stop(p->vrtp);
+- if (p->trtp)
++ }
++ if (p->trtp && !ast_rtp_instance_isactive(p->trtp)) {
+ ast_rtp_instance_stop(p->trtp);
++ }
+ if (p->udptl)
+ ast_udptl_stop(p->udptl);
+ }
+@@ -24660,6 +24703,7 @@
+
+
+ if (p->rtp && (quality = ast_rtp_instance_get_quality(p->rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
++ sip_rtcp_report(p, p->rtp, SDP_AUDIO, TRUE);
+ if (p->do_history) {
+ append_history(p, "RTCPaudio", "Quality:%s", quality);
+
+@@ -24690,6 +24734,7 @@
+ }
+
+ if (p->vrtp && (quality = ast_rtp_instance_get_quality(p->vrtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf)))) {
++ sip_rtcp_report(p, p->rtp, SDP_VIDEO, TRUE);
+ if (p->do_history) {
+ append_history(p, "RTCPvideo", "Quality:%s", quality);
+ }
+@@ -28999,6 +29044,8 @@
+ /* Misc settings for the channel */
+ global_relaxdtmf = FALSE;
+ sip_cfg.callevents = DEFAULT_CALLEVENTS;
++ sip_cfg.rtcpevents = FALSE;
++ sip_cfg.rtcptimer = 0; /* Only report at end of call (default) */
+ global_authfailureevents = FALSE;
+ global_t1 = DEFAULT_TIMER_T1;
+ global_timer_b = 64 * DEFAULT_TIMER_T1;
+@@ -29426,6 +29473,13 @@
+ ast_log(LOG_WARNING, "Invalid qualifyfreq number '%s' at line %d of %s\n", v->value, v->lineno, config);
+ global_qualifyfreq = DEFAULT_QUALIFYFREQ;
+ }
++ } else if (!strcasecmp(v->name, "rtcpevents")) {
++ sip_cfg.rtcpevents = ast_true(v->value);
++ } else if (!strcasecmp(v->name, "rtcpeventtimer")) {
++ if (sscanf(v->value, "%30d", &sip_cfg.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);
++ sip_cfg.rtcptimer = 0;
++ }
+ } else if (!strcasecmp(v->name, "callevents")) {
+ sip_cfg.callevents = ast_true(v->value);
+ } else if (!strcasecmp(v->name, "authfailureevents")) {
+Index: channels/sip/include/sip.h
+===================================================================
+--- channels/sip/include/sip.h (.../branches/1.8) (revision 402883)
++++ channels/sip/include/sip.h (.../team/oej/pinefrog-rtcp-1.8) (revision 402883)
+@@ -696,6 +696,8 @@
+ int compactheaders; /*!< send compact sip headers */
+ int allow_external_domains; /*!< Accept calls to external SIP domains? */
+ int callevents; /*!< Whether we send manager events or not */
++ int rtcpevents; /*!< Whether we send manager RTCP events or not */
++ int rtcptimer; /*!< How often, during a call, to report RTCP stats */
+ int regextenonqualify; /*!< Whether to add/remove regexten when qualifying peers */
+ int legacy_useroption_parsing; /*!< Whether to strip useroptions in URI via semicolons */
+ int matchexternaddrlocally; /*!< Match externaddr/externhost setting against localnet setting */
+@@ -1080,6 +1082,7 @@
+ int waitid; /*!< Wait ID for scheduler after 491 or other delays */
+ int reinviteid; /*!< Reinvite in case of provisional, but no final response */
+ int autokillid; /*!< Auto-kill ID (scheduler) */
++ int rtcpeventid; /*!< Scheduler ID for RTCP Events */
+ int t38id; /*!< T.38 Response ID */
+ struct sip_refer *refer; /*!< REFER: SIP transfer data structure */
+ enum subscriptiontype subscribed; /*!< SUBSCRIBE: Is this dialog a subscription? */
+@@ -1135,6 +1138,12 @@
+ * The large-scale changes would be a good idea for implementing during an SDP rewrite.
+ */
+ struct offered_media offered_media[OFFERED_MEDIA_COUNT];
++ //struct ast_rtp_quality *audioqual; /*!< Audio: The latest quality report, for realtime storage */
++ //struct ast_rtp_quality *videoqual; /*!< Video: The latest quality report, for realtime storage */
++ struct ast_rtp_instance_stats *audioqual; /*!< Audio: The latest quality report, for realtime storage */
++ struct ast_rtp_instance_stats *videoqual; /*!< Video: The latest quality report, for realtime storage */
++ struct sip_settings *sip_cfg; /*! Which sip_cfg is associated with this dialog */
++
+ struct ast_cc_config_params *cc_params;
+ struct sip_epa_entry *epa_entry;
+ int fromdomainport; /*!< Domain port to show in from field */
+Index: channels/sip/include/rtcp.h
+===================================================================
+--- channels/sip/include/rtcp.h (.../branches/1.8) (revision 0)
++++ channels/sip/include/rtcp.h (.../team/oej/pinefrog-rtcp-1.8) (revision 402883)
+@@ -0,0 +1,50 @@
++/*
++ * Asterisk -- An open source telephony toolkit.
++ *
++ * Copyright (C) 2013 Olle E. Johansson, Edvina AB
++ *
++ * See http://www.asterisk.org for more information about
++ * the Asterisk project. Please do not directly contact
++ * any of the maintainers of this project for assistance;
++ * the project provides a web site, mailing lists and IRC
++ * channels for your use.
++ *
++ * This program is free software, distributed under the terms of
++ * the GNU General Public License Version 2. See the LICENSE file
++ * at the top of the source tree.
++ */
++
++/*! \file rtcp.h
++ *
++ * \brief RTCP additional functions
++ *
++ * \author Olle E. Johansson <oej at edvina.net>
++ */
++
++#include "asterisk.h"
++
++#include "asterisk/utils.h"
++#include "asterisk/rtp_engine.h"
++#include "sip.h"
++
++#ifndef _SIP_RTCP_H
++#define _SIP_RTCP_H
++
++/*! \brief Set various data items in the RTP structure, like channel identifier.
++ */
++void sip_rtcp_set_data(struct sip_pvt *dialog, struct ast_rtp_instance *instance, enum media_type type);
++
++int send_rtcp_events(const void *data);
++void start_rtcp_events(struct sip_pvt *dialog, struct sched_context *sched);
++/*
++# For 1.4:
++# static void sip_rtcp_report(struct sip_pvt *p, struct ast_rtp *rtp, enum media_type type, int reporttype);
++*/
++
++void sip_rtcp_report(struct sip_pvt *dialog, struct ast_rtp_instance *instance, enum media_type type, int reporttype);
++//void qos_write_realtime(struct sip_pvt *dialog, struct ast_rtp_quality *qual);
++void qos_write_realtime(struct sip_pvt *dialog, struct ast_rtp_instance_stats *qual);
++
++
++
++#endif /* _SIP_RTCP_H */
+
+Property changes on: channels/sip/include/rtcp.h
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++text/plain
+\ No newline at end of property
+Added: svn:keywords
+## -0,0 +1 ##
++Author Date Id Revision
+\ No newline at end of property
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+\ No newline at end of property
+Index: channels/sip/rtcp.c
+===================================================================
+--- channels/sip/rtcp.c (.../branches/1.8) (revision 0)
++++ channels/sip/rtcp.c (.../team/oej/pinefrog-rtcp-1.8) (revision 402883)
+@@ -0,0 +1,384 @@
++/*
++* Asterisk -- An open source telephony toolkit.
++*
++* Copyright (C) 2013 Olle E. Johansson, Edvina AB
++*
++* See http://www.asterisk.org for more information about
++* the Asterisk project. Please do not directly contact
++* any of the maintainers of this project for assistance;
++* the project provides a web site, mailing lists and IRC
++* channels for your use.
++*
++* This program is free software, distributed under the terms of
++* the GNU General Public License Version 2. See the LICENSE file
++* at the top of the source tree.
++*/
++
++/*! \file rtcp.c
++*
++* \brief RTCP additional functions
++*
++* \author Olle E. Johansson <oej at edvina.net>
++*/
++
++/*** MODULEINFO
++ <support_level>core</support_level>
++***/
++
++#include "asterisk.h"
++
++ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
++
++#include "asterisk/utils.h"
++#include "asterisk/manager.h"
++#include "asterisk/logger.h"
++#include "asterisk/translate.h"
++#include "asterisk/rtp_engine.h"
++#include "include/sip.h"
++#include "include/rtcp.h"
++
++/*! \brief Set various data items in the RTP structure, like channel identifier.
++ */
++void sip_rtcp_set_data(struct sip_pvt *dialog, struct ast_rtp_instance *instance, enum media_type type)
++{
++
++ if (dialog && dialog->owner) {
++ int readtrans = FALSE, writetrans = FALSE;
++ struct ast_channel *bridgepeer = ast_bridged_channel(dialog->owner);
++
++ if (bridgepeer) {
++ /* Store the bridged peer data while we have it */
++ ast_rtp_instance_set_bridged_chan(instance, dialog->owner->name, dialog->owner->uniqueid, S_OR(bridgepeer->name,""), S_OR(bridgepeer->uniqueid,""));
++ ast_debug(1, "---- Setting bridged peer name to %s\n", bridgepeer->name);
++ } else {
++ ast_rtp_instance_set_bridged_chan(instance, dialog->owner->name, dialog->owner->uniqueid, NULL, NULL);
++ }
++ ast_debug(1, "---- Setting channel name to %s\n", dialog->owner->name);
++
++ /* Try to find out if there's active transcoding */
++ /* Currently, the only media stream that has translation is the audio stream. At some point
++ we might have transcoding for other types of media. */
++ if (type == SDP_AUDIO) {
++ struct ast_channel *chan = dialog->owner;
++ const char *rtname = NULL, *wtname = NULL;
++ /* if we have a translator, the bridge delay is increased, which affects the QoS of the call. */
++ readtrans = (chan->readtrans != NULL);
++ writetrans = (chan->writetrans != NULL);
++ if (readtrans) {
++ rtname = chan->readtrans->t->name;
++ }
++ if (writetrans) {
++ wtname = chan->writetrans->t->name;
++ }
++ if (readtrans || writetrans) {
++ ast_rtp_instance_set_translator(instance, rtname, readtrans ? chan->readtrans->t->cost : (const int) 0,
++ wtname, writetrans ? chan->writetrans->t->cost : (const int) 0);
++ }
++
++ if (option_debug > 1) {
++ if (readtrans && dialog->owner->readtrans->t) {
++ ast_debug(1, "--- Audio Read translator: %s Cost %d\n", dialog->owner->readtrans->t->name, dialog->owner->readtrans->t->cost);
++ }
++ if (writetrans && dialog->owner->writetrans->t) {
++ ast_debug(1, "--- Audio Write translator: %s Cost %d\n", dialog->owner->writetrans->t->name, dialog->owner->writetrans->t->cost);
++ }
++ }
++ }
++
++ } else {
++ ast_debug(1, "######## Not setting rtcp media data. Dialog %s Dialog owner %s \n", dialog ? "set" : "unset", dialog->owner ? "set" : "unset");
++ }
++}
++
++/*! \brief send manager report of RTCP
++ reporttype = 0 means report during call (if configured)
++ reporttype = 1 means endof-call (hangup) report
++ reporttype = 10 means report at end of call leg (like transfer)
++*/
++void sip_rtcp_report(struct sip_pvt *dialog, struct ast_rtp_instance *instance, enum media_type media, int reporttype)
++{
++ struct ast_rtp_instance_stats qual;
++ //char *rtpqstring = NULL;
++ //int qosrealtime = ast_check_realtime("rtpcqr");
++ unsigned int duration; /* Duration in secs */
++ memset(&qual, 0, sizeof(qual));
++
++ sip_rtcp_set_data(dialog, instance, media);
++
++ if (ast_rtp_instance_get_stats(instance, &qual, AST_RTP_INSTANCE_STAT_ALL)) {
++ ast_debug(1, "######## Did not get any statistics... bad, bad, RTP instance\n");
++ /* Houston, we got a problem */
++ return;
++ }
++
++ if (dialog->sip_cfg->rtcpevents) {
++ /*
++ 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 = (unsigned int)(ast_tvdiff_ms(ast_tvnow(), qual.start) / 1000);
++ manager_event(EVENT_FLAG_CALL, "RTPQuality",
++ "Channel: %s\r\n" /* AST_CHANNEL for this call */
++ "Uniqueid: %s\r\n" /* AST_CHANNEL for this call */
++ "BridgedChannel: %s\r\n"
++ "BridgedUniqueid: %s\r\n"
++ "RTPreporttype: %s\r\n"
++ "RTPrtcpstatus: %s\r\n"
++ "Duration: %u\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"
++ "TranslateRead: %s\r\n"
++ "TranslateReadCost: %d\r\n"
++ "TranslateWrite: %s\r\n"
++ "TranslateWriteCost: %d\r\n"
++ "\r\n",
++ dialog->owner ? dialog->owner->name : "",
++ dialog->owner ? dialog->owner->uniqueid : "",
++ qual.bridgedchannel[0] ? qual.bridgedchannel : "" ,
++ qual.bridgeduniqueid[0] ? qual.bridgeduniqueid : "",
++ reporttype == 1 ? "Final" : "Update",
++ qual.numberofreports == 0 ? "Inactive" : "Active",
++ duration,
++ dialog->callid,
++ ast_inet_ntoa(qual.them.sin_addr),
++ media == SDP_AUDIO ? "audio" : (media == SDP_VIDEO ? "video" : "fax") ,
++ ast_getformatname(qual.lasttxformat),
++ ast_getformatname(qual.lastrxformat),
++ qual.local_ssrc,
++ qual.remote_ssrc,
++ qual.rtt,
++ qual.maxrtt,
++ qual.minrtt,
++ qual.rxjitter,
++ qual.txjitter,
++ qual.rxploss,
++ /* The local counter of lost packets in inbound stream divided with received packets plus lost packets */
++ (qual.remote_txcount + qual.rxploss) > 0 ? (double) qual.rxploss / (qual.remote_txcount + qual.rxploss) * 100 : 0,
++ qual.txploss,
++ /* The remote counter of lost packets (if we got the reports)
++ divided with our counter of sent packets
++ */
++ (qual.rxcount + qual.txploss) > 0 ? (double) qual.txploss / qual.rxcount * 100 : 0,
++ qual.readtranslator, qual.readcost,
++ qual.writetranslator, qual.writecost
++ );
++ }
++
++ /* 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.
++ Tests have proven that storing to realtime from the call thread is NOT a good thing. Therefore, we just save
++ the quality report structure in the PVT and let the function that kills the pvt store the stuff in the
++ monitor thread instead.
++ */
++ if (reporttype == 1) {
++ ast_log(LOG_DEBUG, "---- Activation qual structure in dialog \n");
++ qual.end = ast_tvnow();
++ qual.mediatype = media;
++ if (media == SDP_AUDIO) { /* Audio */
++ dialog->audioqual = ast_calloc(1, sizeof(struct ast_rtp_instance_stats));
++ (* dialog->audioqual) = qual;
++ } else if (media == SDP_VIDEO) { /* Video */
++ dialog->videoqual = ast_calloc(1,sizeof(struct ast_rtp_instance_stats));
++ (* dialog->videoqual) = qual;
++ }
++ }
++}
++
++/*! \brief Write quality report to realtime storage */
++void qos_write_realtime(struct sip_pvt *dialog, struct ast_rtp_instance_stats *qual)
++{
++ unsigned int duration; /* Duration in secs */
++ char buf_duration[10], buf_lssrc[30], buf_rssrc[30];
++ char buf_rtt[10], buf_rttmin[10], buf_rttmax[10];
++ char localjitter[10], remotejitter[10];
++ char buf_readcost[5], buf_writecost[5];
++ char buf_mediatype[10];
++ char buf_remoteip[25];
++ char buf_inpacketloss[25], buf_outpacketloss[25];
++ char buf_outpackets[25], buf_inpackets[25];
++ int qosrealtime = ast_check_realtime("rtpcqr");
++
++ ast_log(LOG_DEBUG, "************* QOS END REPORTS: The final countdown!!!!! Yeah. \n");
++
++ if (!qual) {
++ ast_log(LOG_ERROR, "No CQR data provided \n");
++ return;
++ }
++
++ /* Since the CDR is already gone, we need to calculate our own duration.
++ The CDR duration is the definitive resource for billing, this is
++ the RTP stream duration which may include early media (ringing and
++ provider messages). Only useful for measurements.
++ */
++ if (!ast_tvzero(qual->end) && !ast_tvzero(qual->start)) {
++ duration = (unsigned int)(ast_tvdiff_ms(qual->end, qual->start) / 1000);
++ } else {
++ ast_log(LOG_DEBUG, "**** WTF? No duration? What type of call is THAT? \n");
++ duration = 0;
++ }
++
++ /* Realtime is based on strings, so let's make strings */
++ sprintf(localjitter, "%f", qual->rxjitter);
++ sprintf(remotejitter, "%f", qual->txjitter);
++ sprintf(buf_lssrc, "%u", qual->local_ssrc);
++ sprintf(buf_rssrc, "%u", qual->remote_ssrc);
++ sprintf(buf_rtt, "%.0f", qual->rtt);
++ sprintf(buf_rttmax, "%.0f", qual->maxrtt);
++ sprintf(buf_rttmin, "%.0f", qual->minrtt);
++ sprintf(buf_duration, "%u", duration);
++ sprintf(buf_readcost, "%d", qual->readcost);
++ sprintf(buf_writecost, "%d", qual->writecost);
++ sprintf(buf_mediatype,"%s", qual->mediatype == SDP_AUDIO ? "audio" : (qual->mediatype == SDP_VIDEO ? "video" : "fax") );
++ sprintf(buf_remoteip,"%s", ast_inet_ntoa(qual->them.sin_addr));
++ sprintf(buf_inpacketloss, "%d", qual->rxploss);
++ sprintf(buf_outpacketloss, "%d", qual->txploss);
++ sprintf(buf_inpackets, "%d", qual->rxcount); /* Silly value. Need to check this */
++ sprintf(buf_outpackets, "%d", qual->txcount);
++ //sprintf(buf_inpackets, "%d", qual->remote_count); /* Do check again */
++ //sprintf(buf_outpackets, "%d", qual->local_count);
++
++ ast_log(LOG_DEBUG, "************* QOS END REPORTS: Probing new logging channel LOG_CQR!!!!! Yeah. \n");
++ ast_log(LOG_CQR, "CQR Channel: %s Uid %s Bch %s Buid %s Pvt %s Media %s Lssrc %s Rssrc %s Rip %s Rtt %s:%s:%s Ljitter %s Rjitter %s Rtcpstatus %s Dur %s Pout %s Plossout %s Pin %s Plossin %s\n",
++ qual->channel[0] ? qual->channel : "",
++ qual->uniqueid[0] ? qual->uniqueid : "",
++ qual->bridgedchannel[0] ? qual->bridgedchannel : "" ,
++ qual->bridgeduniqueid[0] ? qual->bridgeduniqueid : "",
++ dialog->callid,
++ buf_mediatype,
++ buf_lssrc,
++ buf_rssrc,
++ buf_remoteip,
++ buf_rtt, buf_rttmax, buf_rttmin,
++ localjitter,
++ remotejitter,
++ qual->numberofreports == 0 ? "Inactive" : "Active",
++ buf_duration,
++ buf_outpackets,
++ buf_outpacketloss,
++ buf_inpackets,
++ buf_inpacketloss);
++
++ if (!qosrealtime) {
++ return;
++ }
++/* Example database schema for MySQL:
++CREATE TABLE `astcqr` (
++ `channel` varchar(50) NOT NULL,
++ `uniqueid` varchar(35) NOT NULL,
++ `bridgedchannel` varchar(50) NOT NULL,
++ `bridgeduniqueid` varchar(35) NOT NULL,
++ `pvtcallid` varchar(80) NOT NULL,
++ `rtpmedia` varchar(50) NOT NULL,
++ `localssrc` varchar(50) NOT NULL,
++ `remotessrc` varchar(50) NOT NULL,
++ `rtt` varchar(10) NOT NULL,
++ `localjitter` varchar(10) NOT NULL,
++ `remotejitter` varchar(10) NOT NULL,
++ `sendformat` varchar(10) NOT NULL,
++ `receiveformat` varchar(10) NOT NULL,
++ `rtcpstatus` varchar(10) NOT NULL,
++ `duration` varchar(10) NOT NULL,
++ `packetsent` varchar(30) NOT NULL,
++ `packetreceived` varchar(30) NOT NULL,
++ `packetlossin` varchar(30) NOT NULL,
++ `packetlossout` varchar(30) NOT NULL,
++ `rttmax` varchar(12) NOT NULL,
++ `rttmin` varchar(12) NOT NULL,
++ `writetranslator` varchar(15) NOT NULL,
++ `readtranslator` varchar(15) NOT NULL,
++ `writecost` varchar(10) NOT NULL,
++ `readcost` varchar(10) NOT NULL,
++ `remoteip` varchar(25) NOT NULL,
++ KEY `ChannelUnique` (`channel`,`uniqueid`)
++) ENGINE=MyISAM DEFAULT CHARSET=latin1 COMMENT='FOr pinefrog stats'
++*/
++
++ ast_store_realtime("rtpcqr",
++ "channel", qual->channel[0] ? qual->channel : "--no channel--",
++ "uniqueid", qual->uniqueid[0] ? qual->uniqueid : "--no uniqueid --",
++ "bridgedchannel", qual->bridgedchannel[0] ? qual->bridgedchannel : "" ,
++ "bridgeduniqueid", qual->bridgeduniqueid[0] ? qual->bridgeduniqueid : "",
++ "pvtcallid", dialog->callid,
++ "rtpmedia", buf_mediatype,
++ "localssrc", buf_lssrc,
++ "remotessrc", buf_rssrc,
++ "remoteip", buf_remoteip,
++ "rtt", buf_rtt,
++ "rttmax", buf_rttmax,
++ "rttmin", buf_rttmin,
++ "localjitter", localjitter,
++ "remotejitter", remotejitter,
++ "sendformat", ast_getformatname(qual->lasttxformat),
++ "receiveformat", ast_getformatname(qual->lastrxformat),
++ "rtcpstatus", qual->numberofreports == 0 ? "Inactive" : "Active",
++ "duration", buf_duration,
++ "writetranslator", qual->writetranslator[0] ? qual->writetranslator : "",
++ "writecost", buf_writecost,
++ "readtranslator", qual->readtranslator[0] ? qual->readtranslator : "",
++ "readcost", buf_readcost,
++ "packetlossin", buf_inpacketloss,
++ "packetlossout", buf_outpacketloss,
++ "packetsent", buf_outpackets,
++ "packetreceived", buf_inpackets,
++ NULL);
++}
++
++/*! \brief Send RTCP manager events */
++int send_rtcp_events(const void *data)
++{
++ struct sip_pvt *dialog = (struct sip_pvt *) data;
++ ast_log(LOG_DEBUG, "***** SENDING RTCP EVENT \n");
++
++ if (dialog->rtp && !ast_rtp_instance_isactive(dialog->rtp)) {
++ ast_debug(1, " ***** Activating RTCP report \n");
++ sip_rtcp_report(dialog, dialog->rtp, SDP_AUDIO, FALSE);
++ } else {
++ ast_debug(1, " ***** NOT Activating RTCP report \n");
++ }
++ if (dialog->vrtp && !ast_rtp_instance_isactive(dialog->vrtp)) {
++ sip_rtcp_report(dialog, dialog->vrtp, SDP_VIDEO, FALSE);
++ }
++ return (dialog->sip_cfg ? dialog->sip_cfg->rtcptimer : 0);
++}
++
++/*! \brief Activate RTCP events at start of call */
++void start_rtcp_events(struct sip_pvt *dialog, struct sched_context *sched)
++{
++ ast_debug(2, "***** STARTING SENDING RTCP EVENT \n");
++ /* Check if it's already active */
++
++ if (dialog->rtp && !ast_rtp_instance_isactive(dialog->rtp)) {
++ sip_rtcp_set_data(dialog, dialog->rtp, SDP_AUDIO);
++ }
++ if (dialog->vrtp && !ast_rtp_instance_isactive(dialog->vrtp)) {
++ sip_rtcp_set_data(dialog, dialog->vrtp, SDP_VIDEO);
++ }
++
++ if (!dialog->sip_cfg->rtcpevents || !dialog->sip_cfg->rtcptimer) {
++ ast_debug(2, "***** NOT SENDING RTCP EVENTS \n");
++ return;
++ }
++
++ if (dialog->rtcpeventid != -1) {
++ return;
++ }
++
++
++ /*! \brief Schedule events */
++ dialog->rtcpeventid = ast_sched_add(sched, dialog->sip_cfg->rtcptimer * 1000, send_rtcp_events, dialog);
++}
+
+Property changes on: channels/sip/rtcp.c
+___________________________________________________________________
+Added: svn:mime-type
+## -0,0 +1 ##
++text/plain
+\ No newline at end of property
+Added: svn:keywords
+## -0,0 +1 ##
++Author Date Id Revision
+\ No newline at end of property
+Added: svn:eol-style
+## -0,0 +1 ##
++native
+\ No newline at end of property
+Index: channels/sip/dialplan_functions.c
+===================================================================
+--- channels/sip/dialplan_functions.c (.../branches/1.8) (revision 402883)
++++ channels/sip/dialplan_functions.c (.../team/oej/pinefrog-rtcp-1.8) (revision 402883)
+@@ -140,7 +140,9 @@
+
+ snprintf(buf, buflen, "%s", ast_sockaddr_stringify(&sa));
+ } else if (!strcasecmp(args.param, "rtpqos")) {
++/* OEJ - Check this part, different from 1.4 */
+ struct ast_rtp_instance *rtp = NULL;
++ struct ast_rtp_quality *qos;
+
+ if (ast_strlen_zero(args.type)) {
+ args.type = "audio";
+@@ -162,6 +164,11 @@
+ if (!ast_rtp_instance_get_quality(rtp, AST_RTP_INSTANCE_STAT_FIELD_QUALITY, quality_buf, sizeof(quality_buf))) {
+ return -1;
+ }
++#ifdef OEJ
++ if (!ast_rtp_instance_get_qualdata(rtp, ???, &qos)) {
++ this_needs_some_love;
++ }
++#endif
+
+ ast_copy_string(buf, quality_buf, buflen);
+ return res;
+Index: CREDITS
+===================================================================
+--- CREDITS (.../branches/1.8) (revision 402883)
++++ CREDITS (.../team/oej/pinefrog-rtcp-1.8) (revision 402883)
+@@ -23,6 +23,9 @@
+ Omnitor AB, Gunnar Hellström, for funding work with videocaps, T.140 RED,
+ originate with video/text and many more contributions.
+
++Nordicom Norge AS, Kristiansand, Norway, for funding work with RTCP support
++and Call Quality Records.
++
+ === WISHLIST CONTRIBUTERS ===
+ Jeremy McNamara - SpeeX support
+ Nick Seraphin - RDNIS support
+@@ -116,6 +119,7 @@
+ SIP presence support, SIP call state updates (dialog-info),
+ QUEUE_EXISTS function, device state provider architecture,
+ multiparking (together with mvanbaak), meetme and parking device states,
++ RTCP improvements, Call Quality Records,
+ MiniVM - the small voicemail system, many documentation
+ updates/corrections, and many bug fixes.
+ oej(AT)edvina.net, http://edvina.net
+@@ -217,7 +221,8 @@
+ Viagenie, Canada - IPv6 support in socket layers and SIP implementation
+ Developers: Marc Blanchet, Simon Perreault and Jean-Philippe Dionne
+
+-ClearIT AB, Sweden - res_mutestream, queue_exists and various other patches (developed by oej)
++ClearIT AB, Sweden - res_mutestream, queue_exists, RTCP improvements and various
++ other patches (developed by oej)
+
+ Despegar.com, Argentina - AstData API implementation, also sponsored by Google as part of the
+ gsoc/2009 program (developed by Eliel)
+Index: README.pinefrog-rtcp
+===================================================================
+--- README.pinefrog-rtcp (.../branches/1.8) (revision 0)
++++ README.pinefrog-rtcp (.../team/oej/pinefrog-rtcp-1.8) (revision 402883)
+@@ -0,0 +1,204 @@
++Olle E. Johansson
++oej at edvina.net
++
++2013-03-05
++
++
++
++
++
++
++Pinefrog - RTCP cleanup and additions
++-------------------------------------
++
++This branch is aimed at porting the code in pinefrog-1.4, which is now a few years old,
++to Asterisk 1.8 and hopefully (with some help) to Asterisk trunk to be integrated.
++The 1.4 code has been running in production for years in universities, call centers
++and service providers.
++
++The 1.8 port of Pinefrog is supported by Nordicom, Norway (http://www.nordicom.no).
++The 1.4 work was sponsored by several companies, including ClearIT AB, Sweden.
++
++Status of 1.8 port
++------------------
++2013-03-05 Started
++2013-03-12 Updated README to document current status
++
++Todo for 1.8
++------------
++Done. - Add support of outbound and inbound SDES. The SDES includes a stream identifier, CNAME.
++Done. - Add support of outbound SDES end and goodbye
++Done. - Add manager events at end-of call
++Done. - Add realtime storage of RTCP reports
++Done. - Add time manager events (configured in sip.conf)
++Done. - Add more information to RTCP debug
++Done. - Add more data aggregation to ast_rtcp structure (from svn trunk really)
++- Add RTCP for p2p RTP bridges. Needs to be tested and validated.
++
++Background
++==========
++RTCP, as defined in RFC 3550, is a protocol that co-exists with RTP, the protocol used
++for realtime multimedia in VoIP. RTCP gives the endpoints a tool to exchange data about
++the media streams exchanged. As a result, both ends can get informaiton about the
++latency for data sent in both directions, packet loss and jitter for each media stream.
++
++A VoIP call is at least two media streams and they can have different properties in
++regards of quality. A router or switch in the middle could have a lot of outbound traffic,
++causing delays and possible packet loss. This might not affect inbound traffic.
++
++In Asterisk, the RTCP handler is part of the RTP module. The RTP module produces RTCP
++report that can be added to channel variables, cdr logs or sent through AMI.
++
++In 1.4, the data used is mostly based on the latest report, it's not aggregated. This
++is fixed in trunk.
++
++In both implementations (and the 1.6 releases in between) the RTCP support is not
++very complete.
++
++- It doesn't handle RTCP SDES packets
++- It doesn't send RTCP END packets at end of session
++- It doesn't handle receiving END packets
++- It doesn't handle re-invites in a good way.
++- It seems to mix sender and receiver reports, thus mixing data from two streams
++ - when does this happen, if at all?
++
++NOTES
++-----
++RTCP is a mutual protocol. Asterisk sends data to a phone and tells the phone in a
++"Sender report" how much we've sent. The phone responds with a "Receiver report"
++to give data about packets lost between the sender and receiver, variations in
++time (jitter) and timestamps to calculate latency. The phone does the same
++with the RTP stream towards Asterisk - sends a "Sender Report" and receives
++a "Receiver report".
++
++This means that in order to get relevant data, it's a tango for two. The phone
++needs to have a working RTCP implementation. This is sadly not the case for
++all SIP phones. Some phones have timers, so for short calls you will not get
++any reports, because it's set to send RTCP after five minutes or something
++longer than your phone call.
++
++The conclusion is that the CQRs doesn't work for all phones. You will always
++get data from Asterisk's point of view, but you may not get data from the other
++end. Put pressure on your vendors to participate in the RTCP interaction
++so you can get control of your calls.
++
++RTCP and NAT
++------------
++I suspect that RTCP doesn't traverse NAT very well in our implementation. For RTP,
++we start with sending media to probe NAT. I've added emtpy RTCP RR+SDES CNAME packets
++to start probing a NAT (if Asterisk is behind a NAT). I am afraid that very few devices
++do that early on.
++The idea is (like RTP)
++ - Send a few RTCP packets in the start of the session.
++ - The receiver can then apply symmetric RTCP and start sending to the NAT outside port
++ that we're sending from and we'll get their packets.
++
++Logging
++-------
++This module logs Call Quality Records either to Realtime databases (see structure
++below) or to a new log channel named CQR. Here's an example log entry, broken
++to multiple lines for clarity:
++
++[Mar 12 14:10:13] CQR[27938] sip/rtcp.c: CQR Channel: SIP/demo2-0000000b Uid example.com-1363086575.11
++ Bch SIP/demo3-0000000a Buid example.com-1363086575.10 Pvt 12474f1963f3312d0cfc930472a164d7 at 62.80.214.22:5060
++ Media audio Lssrc 653259305 Rssrc 1997455415 Rip 87.96.134.129 Rtt 0:0:0 Ljitter 0.000000 Rjitter 0.026454
++ Rtcpstatus Active Dur 6 Pout 169 Plossout 0 Pin 157 Plossin 0
++
++Todo
++----
++- When CNAME changes, we have a different stream and need to restart the stats.
++ Should we add ability to produce multiple RTCP reports for one "call" and aggregate them?
++ The different parts might have different properties.
++
++Done in 1.4
++-----------
++- Added support of outbound and inbound SDES. The SDES includes a stream identifier, CNAME.
++- Added support of outbound SDES end and goodbye
++- Added manager events at end-of call
++- Added realtime storage of RTCP reports
++- Added time manager events (configured in sip.conf)
++- Added more information to RTCP debug
++- Added more data aggregation to ast_rtcp structure (from svn trunk really)
++- Added RTCP for p2p RTP bridges. Needs to be tested and validated.
++
++Open Issues
++-----------
++The final manager report lacks (in the case of the second channel) the bridged channel.
++We could save that data. This will affect realtime as well, so we need to copy the
++channel name to a stored variable while it exists.
++
++Do we have a counter of consecutive lost packets? How do we measure lost packets on inbound
++stream? Gaps in seq numbers or just the sender reports from the other end compared with received
++no of packets?
++
++
++Ideas and thoughts for the future
++---------------------------------
++- Asterisk propagates jitter and packet loss over a bridge (especially the p2p RTP bridge).
++ If the call is transfered on the OTHER side of the bridge, we have a new call with new
++ properties. Maybe events like this should generate a new SDES and reset RTCP?
++ Part A of the call can have very different properties than part B. If I have a call with
++ someone internally, that then transfers me to a call with someone on the Internet, the
++ call quality (jitter etc) will change dramatically. This will require some sort of CONTROL
++ packet over the bridge, informing about changes on the other side of the bridge (masq).
++- Can we have some sort of ring buffer for the latest RTCP reports for a device (peer)
++ and use that to determine the status of the connection to the peer?
++- Can we use the RTCP APP packet for relaying events in joined bridges, like meetme?
++- What should we use as CNAME? Currently SIP call ID.
++- Separate on the IPs of different media servers. IE we can have one SIP peer with
++ multiple media IPs with different properties
++
++Scenarios to test
++------------------
++- normal bridged call
++- RTP p2p bridged call
++- Nat traversal - Asterisk outside of NAT and inside (as client to external service)
++- Call hold
++- Call with music-on-hold
++- Call transfer
++
++Send feedback from your reports to oej at edvina.net
++
++Database structure
++-------------------
++Example database schema for MySQL:
++
++CREATE TABLE `astcqr` (
++ `channel` varchar(50) NOT NULL,
++ `uniqueid` varchar(35) NOT NULL,
++ `bridgedchannel` varchar(50) NOT NULL,
++ `bridgeduniqueid` varchar(35) NOT NULL,
++ `pvtcallid` varchar(80) NOT NULL,
++ `rtpmedia` varchar(50) NOT NULL,
++ `localssrc` varchar(50) NOT NULL,
++ `remotessrc` varchar(50) NOT NULL,
++ `rtt` varchar(10) NOT NULL,
++ `localjitter` varchar(10) NOT NULL,
[... 1689 lines stripped ...]
More information about the asterisk-commits
mailing list