[asterisk-commits] oej: branch oej/pinefrog-rtcp-1.8 r382457 - /team/oej/pinefrog-rtcp-1.8/patches/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Mar 5 09:18:15 CST 2013


Author: oej
Date: Tue Mar  5 09:18:11 2013
New Revision: 382457

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=382457
Log:
Adding a patch from 1.4 that's the starting point for this work

Added:
    team/oej/pinefrog-rtcp-1.8/patches/
    team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.4.diff   (with props)

Added: team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.4.diff
URL: http://svnview.digium.com/svn/asterisk/team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.4.diff?view=auto&rev=382457
==============================================================================
--- team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.4.diff (added)
+++ team/oej/pinefrog-rtcp-1.8/patches/pinefrog-1.4.diff Tue Mar  5 09:18:11 2013
@@ -1,0 +1,2281 @@
+Index: channels/chan_sip.c
+===================================================================
+--- channels/chan_sip.c	(.../branches/1.4)	(revision 382456)
++++ channels/chan_sip.c	(.../team/oej/pinefrog-1.4)	(revision 382456)
+@@ -478,6 +478,13 @@
+ 	{ SIP_OPT_RESPRIORITY,	NOT_SUPPORTED,	"resource-priority" },
+ };
+ 
++/*! Media types for declaration of RTP streams */
++enum media_type {
++	SDP_AUDIO,	/* AUDIO class */
++	SDP_VIDEO,
++	SDP_IMAGE,
++/* For later versions that 1.4 we need to add SDP_TEXT for T.140 */
++};
+ 
+ /*! \brief SIP Methods we support */
+ #define ALLOWED_METHODS "INVITE, ACK, CANCEL, OPTIONS, BYE, REFER, SUBSCRIBE, NOTIFY, INFO"
+@@ -574,6 +581,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 */
+@@ -1042,6 +1051,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?  */
+@@ -1068,6 +1078,8 @@
+ 	struct sip_invite_param *options;	/*!< Options for INVITE */
+ 	int autoframing;
+ 	int hangupcause;			/*!< Storage of hangupcause copied from our owner before we disconnect from the AST channel (only used at hangup) */
++	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 */
+ 	/*! When receiving an SDP offer, it is important to take note of what media types were offered.
+ 	 * By doing this, even if we don't want to answer a particular media stream with something meaningful, we can
+ 	 * still put an m= line in our answer with the port set to 0.
+@@ -1421,7 +1433,11 @@
+ 				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 sip_rtcp_report(struct sip_pvt *p, struct ast_rtp *rtp, enum media_type type, int reporttype);
+ static void stop_media_flows(struct sip_pvt *p);
++static void qos_write_realtime(struct sip_pvt *dialog, struct ast_rtp_quality *qual);
+ 
+ /*--- Authentication stuff */
+ static int reply_digest(struct sip_pvt *p, struct sip_request *req, char *header, int sipmethod, char *digest, int digest_len);
+@@ -2754,9 +2770,12 @@
+ 	if (option_debug > 2)
+ 		ast_log(LOG_DEBUG, "Destroying SIP peer %s\n", peer->name);
+ 
++
+ 	/* Delete it, it needs to disappear */
+-	if (peer->call)
++	if (peer->call) {
+ 		sip_destroy(peer->call);
++		peer->call = NULL;
++	}
+ 
+ 	if (peer->mwipvt) 	/* We have an active subscription, delete it */
+ 		sip_destroy(peer->mwipvt);
+@@ -3441,11 +3460,25 @@
+ 	if (dumphistory)
+ 		sip_dump_history(p);
+ 
++	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)
+ 		free(p->options);
+ 
+ 	if (p->stateid > -1)
+ 		ast_extension_state_del(p->stateid, NULL);
++	AST_SCHED_DEL(sched, p->rtcpeventid);
+ 
+ 	/* remove any pending extension notify that could be left in
+ 	 * the extension update queue relating to this dialog. */
+@@ -3957,10 +3990,12 @@
+ 			if (!p->pendinginvite) {
+ 				char *audioqos = "";
+ 				char *videoqos = "";
+-				if (p->rtp)
+-					audioqos = ast_rtp_get_quality(p->rtp, NULL);
+-				if (p->vrtp)
+-					videoqos = ast_rtp_get_quality(p->vrtp, NULL);
++				if (p->rtp) {
++					audioqos = ast_rtp_get_quality(p->rtp);
++				}
++				if (p->vrtp) {
++					videoqos = ast_rtp_get_quality(p->vrtp);
++				}
+ 				/* Send a hangup */
+ 				if (oldowner->_state == AST_STATE_UP) {
+ 					transmit_request_with_auth(p, SIP_BYE, 0, XMIT_RELIABLE, 1);
+@@ -4035,6 +4070,7 @@
+ 		ast_rtp_new_source(p->rtp);
+ 		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;
+@@ -4846,6 +4882,7 @@
+ 	ast_mutex_init(&p->lock);
+ 
+ 	p->method = intended_method;
++	p->rtcpeventid = -1;
+ 	p->initid = -1;
+ 	p->waitid = -1;
+ 	p->autokillid = -1;
+@@ -4877,8 +4914,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)) {
+@@ -4936,6 +4974,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->vrtp, 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;
+@@ -5603,11 +5647,6 @@
+ 	return;
+ }
+ 
+-enum media_type {
+-	SDP_AUDIO,
+-	SDP_VIDEO,
+-	SDP_IMAGE,
+-};
+ 
+ static int get_ip_and_port_from_sdp(struct sip_request *req, const enum media_type media, struct sockaddr_in *sin)
+ {
+@@ -12019,6 +12058,10 @@
+ 	int realtimepeers;
+ 	int realtimeusers;
+ 	char codec_buf[SIPBUFSIZE];
++	int realtimertpqos = FALSE;
++#ifdef REALTIME2
++	realtimertpqos = ast_check_realtime("rtpqos");
++#endif
+ 
+ 	realtimepeers = ast_check_realtime("sippeers");
+ 	realtimeusers = ast_check_realtime("sipusers");
+@@ -12050,6 +12093,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));
+@@ -12063,6 +12108,7 @@
+ 		ast_cli(fd, "  SIP realtime:           Disabled\n" );
+ 	else
+ 		ast_cli(fd, "  SIP realtime:           Enabled\n" );
++	ast_cli(fd, "  QOS realtime reports:   %s\n", realtimertpqos ? "Enabled" : "Disabled" );
+ 
+ 	ast_cli(fd, "\nGlobal Signalling Settings:\n");
+ 	ast_cli(fd, "---------------------------\n");
+@@ -13650,6 +13696,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 */
+@@ -14046,14 +14093,286 @@
+ 	}
+ }
+ 
++/*! \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)
++*/
++static void sip_rtcp_report(struct sip_pvt *p, struct ast_rtp *rtp, enum media_type type, int reporttype)
++{
++	struct ast_rtp_quality *qual;
++	char *rtpqstring = NULL;
++	int qosrealtime = ast_check_realtime("rtpqos");
++	unsigned int duration;	/* Duration in secs */
++ 	int readtrans = FALSE, writetrans = FALSE;
++
++	memset(&qual, sizeof(qual), 0);
++  
++	if (p && p->owner) {
++		struct ast_channel *bridgepeer = ast_bridged_channel(p->owner);
++		if (bridgepeer) {
++			/* Store the bridged peer data while we have it */
++			ast_rtcp_set_bridged(rtp, p->owner->name, p->owner->uniqueid, S_OR(bridgepeer->name,""), S_OR(bridgepeer->uniqueid,""));
++			ast_log(LOG_DEBUG, "---- Setting bridged peer name to %s\n", bridgepeer->name);
++		} else {
++			ast_rtcp_set_bridged(rtp, p->owner->name, p->owner->uniqueid, NULL, NULL);
++		}
++
++ 		/* 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) {
++			/* if we have a translator, the bridge delay is increased, which affects the QoS of the call.  */
++ 			readtrans = p->owner->readtrans != NULL;
++ 			writetrans = p->owner->writetrans != NULL;
++			ast_rtcp_settranslator(rtp, readtrans ? p->owner->readtrans->t->name : NULL, readtrans ? p->owner->readtrans->t->cost : 0,
++					writetrans ? p->owner->writetrans->t->name : NULL, writetrans ? p->owner->writetrans->t->cost : 0);
++		
++			if (option_debug > 1) {
++ 				if (readtrans && p->owner->readtrans->t) {
++ 					ast_log(LOG_DEBUG, "--- Audio Read translator: %s Cost %d\n", p->owner->readtrans->t->name, p->owner->readtrans->t->cost);
++ 				}
++ 				if (writetrans && p->owner->writetrans->t) {
++ 					ast_log(LOG_DEBUG, "--- Audio Write translator: %s Cost %d\n", p->owner->writetrans->t->name, p->owner->writetrans->t->cost);
++ 				}
++			}
++		}
++
++	}
++
++	rtpqstring =  ast_rtp_get_quality(rtp);
++	qual = ast_rtp_get_qualdata(rtp);
++	if (!qual) {
++		/* Houston, we got a problem */
++		return;
++	}
++	
++	if (global_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", 
++			p->owner ? p->owner->name : "",
++			p->owner ? p->owner->uniqueid : "",
++			qual->bridgedchan[0] ? qual->bridgedchan : "" ,
++			qual->bridgeduniqueid[0] ? qual->bridgeduniqueid : "",
++			reporttype == 1 ? "Final" : "Update",
++			qual->numberofreports == 0 ? "Inactive" : "Active",
++			duration,
++			p->callid, 
++			ast_inet_ntoa(qual->them.sin_addr), 	
++			type == SDP_AUDIO ? "audio" : (type == SDP_VIDEO ? "video" : "fax") ,
++			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,
++			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 {
++		if (type == SDP_AUDIO) {  /* Audio */
++			p->audioqual = ast_calloc(sizeof(struct ast_rtp_quality), 1);
++			(* p->audioqual) = *qual;
++			p->audioqual->end = ast_tvnow();
++ 			p->audioqual->mediatype = type;
++		} else if (type == SDP_VIDEO) {  /* Video */
++			p->videoqual = ast_calloc(sizeof(struct ast_rtp_quality), 1);
++			(* p->videoqual) = *qual;
++ 			p->videoqual->mediatype = type;
++			p->videoqual->end = ast_tvnow();
++		}
++	}
++}
++
++/*! \brief Write quality report to realtime storage */
++void qos_write_realtime(struct sip_pvt *dialog, struct ast_rtp_quality *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];
++
++	/* 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)) {
++		duration = (unsigned int)(ast_tvdiff_ms(qual->end, qual->start) / 1000);
++	} else {
++		duration = 0;
++	}
++
++	/* Realtime is based on strings, so let's make strings */
++	sprintf(localjitter, "%f", qual->local_jitter);
++	sprintf(remotejitter, "%f", qual->remote_jitter);
++	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->rttmax);
++	sprintf(buf_rttmin, "%.0f", qual->rttmin);
++	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->local_lostpackets);
++	sprintf(buf_outpacketloss, "%d", qual->remote_lostpackets);
++	sprintf(buf_inpackets, "%d", qual->remote_count);	/* Do check again */
++	sprintf(buf_outpackets, "%d", qual->local_count);
++
++	ast_log(LOG_NOTICE,"RTPQOS 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->bridgedchan[0] ? qual->bridgedchan : "" ,
++		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);
++
++#ifdef REALTIME2
++	ast_store_realtime("rtpqos", 
++		"channel", qual->channel[0] ? qual->channel : "--no channel--",
++		"uniqueid", qual->uniqueid[0] ? qual->uniqueid : "--no uniqueid --",
++		"bridgedchan", qual->bridgedchan[0] ? qual->bridgedchan : "" ,
++		"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);
++#endif
++}
++
++/*! \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, SDP_AUDIO, FALSE);
++	}
++	if (dialog->vrtp && ast_rtp_isactive(dialog->vrtp)) {
++		sip_rtcp_report(dialog, dialog->vrtp, SDP_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, SDP_AUDIO, TRUE);
++	}
++	if (p->vrtp && ast_rtp_isactive(p->vrtp)) {
+ 		ast_rtp_stop(p->vrtp);
++		sip_rtcp_report(p, p->vrtp, SDP_VIDEO, TRUE);
++	}
+ 	if (p->udptl)
+ 		ast_udptl_stop(p->udptl);
+ }
+@@ -16523,9 +16842,10 @@
+ 
+ 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);
++	struct ast_rtp_quality *qos;
++
+ 	AST_DECLARE_APP_ARGS(args,
+ 		AST_APP_ARG(param);
+ 		AST_APP_ARG(type);
+@@ -16549,36 +16869,41 @@
+ 		args.field = "all";
+ 
+ 	memset(buf, 0, buflen);
+-	memset(&qos, 0, sizeof(qos));
+ 
+ 	if (p == NULL) {
+ 		return -1;
+ 	}
+ 
+ 	if (strcasecmp(args.type, "AUDIO") == 0) {
+-		all = ast_rtp_get_quality(p->rtp, &qos);
++		all = ast_rtp_get_quality(p->rtp);
++		qos = ast_rtp_get_qualdata(p->rtp);
+ 	} else if (strcasecmp(args.type, "VIDEO") == 0) {
+-		all = ast_rtp_get_quality(p->vrtp, &qos);
++		all = ast_rtp_get_quality(p->vrtp);
++		qos = ast_rtp_get_qualdata(p->vrtp);
++	} else {
++		ast_log(LOG_WARNING, "Unrecognized stream '%s in call to %s'\n", args.typecname, funcname);
++		return -1;
++		
+ 	}
+ 
+ 	if (strcasecmp(args.field, "local_ssrc") == 0)
+-		snprintf(buf, buflen, "%u", qos.local_ssrc);
++		snprintf(buf, buflen, "%u", qos->local_ssrc);
+ 	else if (strcasecmp(args.field, "local_lostpackets") == 0)
+-		snprintf(buf, buflen, "%u", qos.local_lostpackets);
++		snprintf(buf, buflen, "%u", qos->local_lostpackets);
+ 	else if (strcasecmp(args.field, "local_jitter") == 0)
+-		snprintf(buf, buflen, "%.0lf", qos.local_jitter * 1000.0);
++		snprintf(buf, buflen, "%.0lf", qos->local_jitter * 1000.0);
+ 	else if (strcasecmp(args.field, "local_count") == 0)
+-		snprintf(buf, buflen, "%u", qos.local_count);
++		snprintf(buf, buflen, "%u", qos->local_count);
+ 	else if (strcasecmp(args.field, "remote_ssrc") == 0)
+-		snprintf(buf, buflen, "%u", qos.remote_ssrc);
++		snprintf(buf, buflen, "%u", qos->remote_ssrc);
+ 	else if (strcasecmp(args.field, "remote_lostpackets") == 0)
+-		snprintf(buf, buflen, "%u", qos.remote_lostpackets);
++		snprintf(buf, buflen, "%u", qos->remote_lostpackets);
+ 	else if (strcasecmp(args.field, "remote_jitter") == 0)
+-		snprintf(buf, buflen, "%.0lf", qos.remote_jitter * 1000.0);
++		snprintf(buf, buflen, "%.0lf", qos->remote_jitter * 1000.0);
+ 	else if (strcasecmp(args.field, "remote_count") == 0)
+-		snprintf(buf, buflen, "%u", qos.remote_count);
++		snprintf(buf, buflen, "%u", qos->remote_count);
+ 	else if (strcasecmp(args.field, "rtt") == 0)
+-		snprintf(buf, buflen, "%.0lf", qos.rtt * 1000.0);
++		snprintf(buf, buflen, "%.0lf", qos->rtt * 1000.0);
+ 	else if (strcasecmp(args.field, "all") == 0)
+ 		ast_copy_string(buf, all, buflen);
+ 	else {
+@@ -16611,14 +16936,14 @@
+ 	if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY) || p->owner) {
+ 		char *audioqos, *videoqos;
+ 		if (p->rtp) {
+-			audioqos = ast_rtp_get_quality(p->rtp, NULL);
++			audioqos = ast_rtp_get_quality(p->rtp);
+ 			if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY))
+ 				append_history(p, "RTCPaudio", "Quality:%s", audioqos);
+ 			if (p->owner)
+ 				pbx_builtin_setvar_helper(p->owner, "RTPAUDIOQOS", audioqos);
+ 		}
+ 		if (p->vrtp) {
+-			videoqos = ast_rtp_get_quality(p->vrtp, NULL);
++			videoqos = ast_rtp_get_quality(p->vrtp);
+ 			if (!ast_test_flag(&p->flags[0], SIP_NO_HISTORY))
+ 				append_history(p, "RTCPvideo", "Quality:%s", videoqos);
+ 			if (p->owner)
+@@ -19120,6 +19445,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;
+ 
+@@ -19366,6 +19693,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")) {
+Index: README.pinefrog-rtcp
+===================================================================
+--- README.pinefrog-rtcp	(.../branches/1.4)	(revision 0)
++++ README.pinefrog-rtcp	(.../team/oej/pinefrog-1.4)	(revision 382456)
+@@ -0,0 +1,97 @@
++Olle E. Johansson
++oej at edvina.net
++
++
++
++
++
++
++Pinefrog - RTCP cleanup and additions
++-------------------------------------
++
++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?
++
++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.
++
++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.
++- Document realtime storage format. Add missing fields.
++- BUG: RTCP is halted during hold. It should not stop.
++- During HOLD, send RTCP SR reports without report block, only the header and no chunks 
++
++Done
++----
++- 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)
+
+Egenskapsändringar för: README.pinefrog-rtcp
+___________________________________________________________________
+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: include/asterisk/rtp.h
+===================================================================
+--- include/asterisk/rtp.h	(.../branches/1.4)	(revision 382456)
++++ include/asterisk/rtp.h	(.../team/oej/pinefrog-1.4)	(revision 382456)
+@@ -77,16 +77,38 @@
+ 	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 */
++	char channel[AST_MAX_EXTENSION];	/*!< Name of channel */
++	char uniqueid[AST_MAX_EXTENSION];	/*!< uniqueid of channel */
++	char bridgedchan[AST_MAX_EXTENSION];	/*!< Name of bridged channel */
++	char bridgeduniqueid[AST_MAX_EXTENSION];	/*!< uniqueid of bridged channel */
++	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 */
++	struct timeval end;		  /*!< When the call ended */
++	char writetranslator[80];	  /*!< Translator used when writing */
++	char readtranslator[80];		  /*!< Translator providing frames when reading */
++	int writecost;		  /*!< Cost in milliseconds for encoding/decoding 1 second of outbound media */
++	int readcost;		  /*!< Cost in milliseconds for encoding/decoding 1 second of inbound media */
++	int mediatype;			/*! Type of media */
+ };
+ 
+ 
+@@ -180,9 +202,35 @@
+ 
+ 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 Set the transcoding variables for the QoS reports */
++void ast_rtcp_settranslator(struct ast_rtp *rtp, const char *readtranslator, const int readcost, const char *writetranslator, const int writecost);
++
++/*! \brief set the name of the channel and the bridged channel (if any)
++
++At the time when we write the report there might not be a bridge, so we need
++to store this so we can correlate the reports. If a channel changes bridge,
++it can be reset by first setting it to an empty string, then setting to 
++a new name 
++*/
++void ast_rtcp_set_bridged(struct ast_rtp *rtp, const char *channel, const char *uniqueid, const char *bridged_name, const char *bridged_uniqueid);
++
++/*! \brief Store translator information
++
++In order to measure quality of a phone call, information about transcoding is very useful. Translation
++adds processing and latency to the bridged call.
++*/
++void ast_rtcp_set_translation(struct ast_rtp *rtp, const char *writetranslator, const int writecost,
++				const char *readtranslator, const int readcost);
++
++/*! \brief When changing sources, don't generate a new SSRC */
++void ast_rtp_set_constantssrc(struct ast_rtp *rtp);
++
+ /*! \brief Indicate that we need to set the marker bit */
+ void ast_rtp_new_source(struct ast_rtp *rtp);
+ 
++
+ /*! \brief Indicate that we need to set the marker bit and change the ssrc */
+ void ast_rtp_change_source(struct ast_rtp *rtp);
+ 
+@@ -246,9 +294,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);
++char *ast_rtp_get_quality(struct ast_rtp *rtp);
++  
++/*! \brief Return RTCP quality data structure */
++struct ast_rtp_quality *ast_rtp_get_qualdata(struct ast_rtp *rtp);
+ 
+ /*! \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);
+Index: main/channel.c
+===================================================================
+--- main/channel.c	(.../branches/1.4)	(revision 382456)
++++ main/channel.c	(.../team/oej/pinefrog-1.4)	(revision 382456)
+@@ -1601,6 +1601,7 @@
+ {
+ 	if (option_debug)
+ 		ast_log(LOG_DEBUG, "Soft-Hanging up channel '%s'\n", chan->name);
++
+ 	/* Inform channel driver that we need to be hung up, if it cares */
+ 	chan->_softhangup |= cause;
+ 	ast_queue_frame(chan, &ast_null_frame);
+Index: main/rtp.c
+===================================================================
+--- main/rtp.c	(.../branches/1.4)	(revision 382456)
++++ main/rtp.c	(.../team/oej/pinefrog-1.4)	(revision 382456)
+@@ -64,13 +64,32 @@
+ #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
+ 
+ #define DEFAULT_DTMF_TIMEOUT (150 * (8000 / 1000))	/*!< samples */
+@@ -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 sched_context *sched;	/*!< The scheduler context */
++	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;
+@@ -173,7 +195,9 @@
+ 	struct ast_rtcp *rtcp;
+ 	struct ast_codec_pref pref;
+ 	struct ast_rtp *bridged;        /*!< Who we are Packet bridged to */
++	struct ast_rtp_quality *qual;	/*!< Optional QoS data storage for this stream */
+ 	int set_marker_bit:1;           /*!< Whether to set the marker bit or not */
++	int isactive:2;                 /*!< Whether the RTP stream is active or not */
+ };
+ 
+ AST_LIST_HEAD_NOLOCK(frame_list, ast_frame);
+@@ -181,11 +205,15 @@
+ /* 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);

[... 1379 lines stripped ...]



More information about the asterisk-commits mailing list