[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