From d3418aa14a83615a488508df8dcf60e86c0504c9 Mon Sep 17 00:00:00 2001
From: Mark Spencer <markster@digium.com>
Date: Fri, 7 Jan 2005 07:11:40 +0000
Subject: [PATCH] Support CNG transmission when on hold (bug #2904)

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@4704 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 channels/chan_sip.c    | 32 ++++++++++++++++++++++++++++++--
 include/asterisk/rtp.h |  2 ++
 rtp.c                  | 40 ++++++++++++++++++++++++++++++++++++++++
 3 files changed, 72 insertions(+), 2 deletions(-)

diff --git a/channels/chan_sip.c b/channels/chan_sip.c
index fa0f63214f..e2a3c77783 100755
--- a/channels/chan_sip.c
+++ b/channels/chan_sip.c
@@ -151,6 +151,8 @@ static int global_rtptimeout = 0;
 
 static int global_rtpholdtimeout = 0;
 
+static int global_rtpkeepalive = 0;
+
 static int global_reg_timeout = DEFAULT_REGISTRATION_TIMEOUT;
 
 /* Object counters */
@@ -379,8 +381,10 @@ static struct sip_pvt {
 	int initid;				/* Auto-congest ID if appropriate */
 	int autokillid;				/* Auto-kill ID */
 	time_t lastrtprx;			/* Last RTP received */
+	time_t lastrtptx;			/* Last RTP sent */
 	int rtptimeout;				/* RTP timeout time */
 	int rtpholdtimeout;			/* RTP timeout when on hold */
+	int rtpkeepalive;			/* Send RTP packets for keepalive */
 
 	int subscribed;				/* Is this call a subscription?  */
     	int stateid;
@@ -468,6 +472,7 @@ struct sip_peer {
 	int capability;			/* Codec capability */
 	int rtptimeout;
 	int rtpholdtimeout;
+	int rtpkeepalive;			/* Send RTP packets for keepalive */
 	unsigned int callgroup;		/* Call group */
 	unsigned int pickupgroup;	/* Pickup group */
 	struct sockaddr_in addr;	/* IP address of peer */
@@ -1821,6 +1826,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
 					transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
 					ast_set_flag(p, SIP_PROGRESS_SENT);	
 				}
+				time(&p->lastrtptx);
 				res =  ast_rtp_write(p->rtp, frame);
 			}
 			ast_mutex_unlock(&p->lock);
@@ -1833,6 +1839,7 @@ static int sip_write(struct ast_channel *ast, struct ast_frame *frame)
 					transmit_response_with_sdp(p, "183 Session Progress", &p->initreq, 0);
 					ast_set_flag(p, SIP_PROGRESS_SENT);	
 				}
+				time(&p->lastrtptx);
 				res =  ast_rtp_write(p->vrtp, frame);
 			}
 			ast_mutex_unlock(&p->lock);
@@ -2346,6 +2353,7 @@ static struct sip_pvt *sip_alloc(char *callid, struct sockaddr_in *sin, int useg
 	strncpy(p->musicclass, global_musicclass, sizeof(p->musicclass) - 1);
 	p->rtptimeout = global_rtptimeout;
 	p->rtpholdtimeout = global_rtpholdtimeout;
+	p->rtpkeepalive = global_rtpkeepalive;
 	p->capability = global_capability;
 	if (ast_test_flag(p, SIP_DTMF) == SIP_DTMF_RFC2833)
 		p->noncodeccapability |= AST_RTP_DTMF;
@@ -2637,6 +2645,7 @@ static int process_sdp(struct sip_pvt *p, struct sip_request *req)
 
 	/* Update our last rtprx when we receive an SDP, too */
 	time(&p->lastrtprx);
+	time(&p->lastrtptx);
 
 	/* Get codec and RTP info from SDP */
 	if (strcasecmp(get_header(req, "Content-Type"), "application/sdp")) {
@@ -3550,6 +3559,7 @@ static int add_sdp(struct sip_request *resp, struct sip_pvt *p)
 	}
 	/* Update lastrtprx when we send our SDP */
 	time(&p->lastrtprx);
+	time(&p->lastrtptx);
 	return 0;
 }
 
@@ -8192,8 +8202,13 @@ restartsearch:
 		sip = iflist;
 		while(sip) {
 			ast_mutex_lock(&sip->lock);
-			if (sip->rtp && sip->owner && (sip->owner->_state == AST_STATE_UP) && sip->lastrtprx && (sip->rtptimeout || sip->rtpholdtimeout) && !sip->redirip.sin_addr.s_addr) {
-				if (t > sip->lastrtprx + sip->rtptimeout) {
+			if (sip->rtp && sip->owner && (sip->owner->_state == AST_STATE_UP) && !sip->redirip.sin_addr.s_addr) {
+				if (sip->lastrtptx && sip->rtpkeepalive && t > sip->lastrtptx + sip->rtpkeepalive) {
+					/* Need to send an empty RTP packet */
+					time(&sip->lastrtptx);
+					ast_rtp_sendcng(sip->rtp, 0);
+				}
+				if (sip->lastrtprx && (sip->rtptimeout || sip->rtpholdtimeout) && t > sip->lastrtprx + sip->rtptimeout) {
 					/* Might be a timeout now -- see if we're on hold */
 					struct sockaddr_in sin;
 					ast_rtp_get_peer(sip->rtp, &sin);
@@ -8718,6 +8733,7 @@ static struct sip_peer *temp_peer(char *name)
 	peer->capability = global_capability;
 	peer->rtptimeout = global_rtptimeout;
 	peer->rtpholdtimeout = global_rtpholdtimeout;
+	peer->rtpkeepalive = global_rtpkeepalive;
 	ast_set_flag(peer, SIP_SELFDESTRUCT);
 	ast_set_flag(peer, SIP_DYNAMIC);
 	peer->prefs = prefs;
@@ -8883,6 +8899,11 @@ static struct sip_peer *build_peer(const char *name, struct ast_variable *v, int
 					ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
 					peer->rtpholdtimeout = global_rtpholdtimeout;
 				}
+			} else if (!strcasecmp(v->name, "rtpkeepalive")) {
+				if ((sscanf(v->value, "%d", &peer->rtpkeepalive) != 1) || (peer->rtpkeepalive < 0)) {
+					ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d.  Using default.\n", v->value, v->lineno);
+					peer->rtpkeepalive = global_rtpkeepalive;
+				}
 			} else if (!strcasecmp(v->name, "qualify")) {
 				if (!strcasecmp(v->value, "no")) {
 					peer->maxms = 0;
@@ -8968,6 +8989,7 @@ static int reload_config(void)
 	ourport = DEFAULT_SIP_PORT;
 	global_rtptimeout = 0;
 	global_rtpholdtimeout = 0;
+	global_rtpkeepalive = 0;
 	pedanticsipchecking = 0;
 	ast_clear_flag(&global_flags, AST_FLAGS_ALL);
 	ast_set_flag(&global_flags, SIP_DTMF_RFC2833);
@@ -9018,6 +9040,11 @@ static int reload_config(void)
 				ast_log(LOG_WARNING, "'%s' is not a valid RTP hold time at line %d.  Using default.\n", v->value, v->lineno);
 				global_rtpholdtimeout = 0;
 			}
+		} else if (!strcasecmp(v->name, "rtpkeepalive")) {
+			if ((sscanf(v->value, "%d", &global_rtpkeepalive) != 1) || (global_rtpkeepalive < 0)) {
+				ast_log(LOG_WARNING, "'%s' is not a valid RTP keepalive time at line %d.  Using default.\n", v->value, v->lineno);
+				global_rtpkeepalive = 0;
+			}
 		} else if (!strcasecmp(v->name, "videosupport")) {
 			videosupport = ast_true(v->value);
 		} else if (!strcasecmp(v->name, "compactheaders")) {
@@ -9276,6 +9303,7 @@ static int sip_set_rtp_peer(struct ast_channel *chan, struct ast_rtp *rtp, struc
 		}
 		/* Reset lastrtprx timer */
 		time(&p->lastrtprx);
+		time(&p->lastrtptx);
 		ast_mutex_unlock(&p->lock);
 		return 0;
 	}
diff --git a/include/asterisk/rtp.h b/include/asterisk/rtp.h
index 6b2df7c02f..0fc653e59b 100755
--- a/include/asterisk/rtp.h
+++ b/include/asterisk/rtp.h
@@ -78,6 +78,8 @@ int ast_rtcp_fd(struct ast_rtp *rtp);
 
 int ast_rtp_senddigit(struct ast_rtp *rtp, char digit);
 
+int ast_rtp_sendcng(struct ast_rtp *rtp, int level);
+
 int ast_rtp_settos(struct ast_rtp *rtp, int tos);
 
 // Setting RTP payload types from lines in a SDP description:
diff --git a/rtp.c b/rtp.c
index acf67d081e..c213dcd592 100755
--- a/rtp.c
+++ b/rtp.c
@@ -1129,6 +1129,46 @@ int ast_rtp_senddigit(struct ast_rtp *rtp, char digit)
 	return 0;
 }
 
+int ast_rtp_sendcng(struct ast_rtp *rtp, int level)
+{
+	unsigned int *rtpheader;
+	int hdrlen = 12;
+	int res;
+	int payload;
+	char data[256];
+	char iabuf[INET_ADDRSTRLEN];
+	level = 127 - (level & 0x7f);
+	payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_CN);
+
+	/* If we have no peer, return immediately */	
+	if (!rtp->them.sin_addr.s_addr)
+		return 0;
+
+	gettimeofday(&rtp->dtmfmute, NULL);
+	rtp->dtmfmute.tv_usec += (500 * 1000);
+	if (rtp->dtmfmute.tv_usec > 1000000) {
+		rtp->dtmfmute.tv_usec -= 1000000;
+		rtp->dtmfmute.tv_sec += 1;
+	}
+	
+	/* Get a pointer to the header */
+	rtpheader = (unsigned int *)data;
+	rtpheader[0] = htonl((2 << 30) | (1 << 23) | (payload << 16) | (rtp->seqno++));
+	rtpheader[1] = htonl(rtp->lastts);
+	rtpheader[2] = htonl(rtp->ssrc); 
+	data[12] = level;
+	if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
+		res = sendto(rtp->s, (void *)rtpheader, hdrlen + 1, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them));
+		if (res <0) 
+			ast_log(LOG_ERROR, "RTP Comfort Noise Transmission error to %s:%d: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
+		if(rtp_debug_test_addr(&rtp->them))
+			ast_verbose("Sent Comfort Noise RTP packet to %s:%d (type %d, seq %d, ts %d, len %d)\n"
+					, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen);		   
+		   
+	}
+	return 0;
+}
+
 #ifdef SOLARIS
 static void put_uint32(unsigned char *buf, int i)
 {
-- 
GitLab