Skip to content
Snippets Groups Projects
rtp.c 89.3 KiB
Newer Older
	int lost_interval;
	struct timeval now;
	unsigned int *rtcpheader;
	char bdata[1024];
	char iabuf[INET_ADDRSTRLEN];
	struct timeval dlsr;
	int fraction;

	if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0))
		return 0;
	  
	if (!rtp->rtcp->them.sin_addr.s_addr) {
		ast_log(LOG_ERROR, "RTCP RR transmission error to, rtcp halted %s\n",strerror(errno));
		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
		rtp->rtcp->schedid = -1;
		return 0;
	}

	extended = rtp->cycles + rtp->lastrxseqno;
	expected = extended - rtp->seedrxseqno + 1;
	lost = expected - rtp->rxcount;
	expected_interval = expected - rtp->rtcp->expected_prior;
	rtp->rtcp->expected_prior = expected;
	received_interval = rtp->rxcount - rtp->rtcp->received_prior;
	rtp->rtcp->received_prior = rtp->rxcount;
	lost_interval = expected_interval - received_interval;
	if (expected_interval == 0 || lost_interval <= 0)
		fraction = 0;
	else
		fraction = (lost_interval << 8) / expected_interval;
	gettimeofday(&now, NULL);
	timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
	rtcpheader = (unsigned int *)bdata;
	rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_RR << 16) | ((len/4)-1));
	rtcpheader[1] = htonl(rtp->ssrc);
	rtcpheader[2] = htonl(rtp->themssrc);
	rtcpheader[3] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
	rtcpheader[4] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
	rtcpheader[5] = htonl((unsigned int)rtp->rxjitter);
	rtcpheader[6] = htonl(rtp->rtcp->themrxlsr);
	rtcpheader[7] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);

	if (rtp->rtcp->sendfur) {
		rtcpheader[8] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1); /* Header from page 36 in RFC 3550 */
		rtcpheader[9] = htonl(rtp->ssrc);               /* Our SSRC */
		len += 8;
		rtp->rtcp->sendfur = 0;
	}

	/*! \note Insert SDES here. Probably should make SDES text equal to mimetypes[code].type (not subtype 'cos 
	it can change mid call, and SDES can't) */
	rtcpheader[len/4]     = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SDES << 16) | 2);
	rtcpheader[(len/4)+1] = htonl(rtp->ssrc);               /* Our SSRC */
	rtcpheader[(len/4)+2] = htonl(0x01 << 24);              /* Empty for the moment */
	len += 12;
	
	res = sendto(rtp->rtcp->s, (unsigned int *)rtcpheader, len, 0, (struct sockaddr *)&rtp->rtcp->them, sizeof(rtp->rtcp->them));

	if (res < 0) {
		ast_log(LOG_ERROR, "RTCP RR transmission error, rtcp halted: %s\n",strerror(errno));
		/* Remove the scheduler */
		ast_sched_del(rtp->sched, rtp->rtcp->schedid);
		rtp->rtcp->schedid = -1;
		return 0;
	}

	rtp->rtcp->rr_count++;

	if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
		ast_verbose("\n* Sending RTCP RR to %s:%d\n"
			"  Our SSRC: %u\nTheir SSRC: %u\niFraction lost: %d\nCumulative loss: %u\n" 
			"  IA jitter: %.4f\n" 
			"  Their last SR: %u\n" 
			"  DLSR: %4.4f (sec)\n\n",
			ast_inet_ntoa(iabuf, sizeof(iabuf),
			rtp->rtcp->them.sin_addr),
			ntohs(rtp->rtcp->them.sin_port),
			rtp->ssrc, rtp->themssrc, fraction, lost,
			rtp->rxjitter,
			rtp->rtcp->themrxlsr,
			(double)(ntohl(rtcpheader[7])/65536.0));
	}

	return res;
}

/*! \brief Write and RTCP packet to the far end
 * \note Decide if we are going to send an SR (with Reception Block) or RR 
 * RR is sent if we have not sent any rtp packets in the previous interval */
static int ast_rtcp_write(void *data)
{
	struct ast_rtp *rtp = data;
	int res;
	
	if (rtp->txcount > rtp->rtcp->lastsrtxcount)
		res = ast_rtcp_write_sr(data);
	else
		res = ast_rtcp_write_rr(data);
	
	return res;
}

/*! \brief generate comfort noice (CNG) */
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;

	rtp->dtmfmute = ast_tvadd(ast_tvnow(), ast_tv(0, 500000));

	/* 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 %u, 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;
}

Mark Spencer's avatar
Mark Spencer committed
static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec)
	char iabuf[INET_ADDRSTRLEN];
Mark Spencer's avatar
Mark Spencer committed
	int hdrlen = 12;
	int res;
	unsigned int ms;
Mark Spencer's avatar
Mark Spencer committed
	int pred;
	ms = calc_txstamp(rtp, &f->delivery);
Mark Spencer's avatar
Mark Spencer committed
	/* Default prediction */
	if (f->subclass < AST_FORMAT_MAX_AUDIO) {
		pred = rtp->lastts + f->samples;
		/* Re-calculate last TS */
		rtp->lastts = rtp->lastts + ms * 8;
			/* If this isn't an absolute delivery time, Check if it is close to our prediction, 
			   and if so, go with our prediction */
			if (abs(rtp->lastts - pred) < MAX_TIMESTAMP_SKEW)
				rtp->lastts = pred;
				if (option_debug > 2)
					ast_log(LOG_DEBUG, "Difference is %d, ms is %d\n", abs(rtp->lastts - pred), ms);
		mark = f->subclass & 0x1;
		pred = rtp->lastovidtimestamp + f->samples;
		/* Re-calculate last TS */
		rtp->lastts = rtp->lastts + ms * 90;
		/* If it's close to our prediction, go for it */
			if (abs(rtp->lastts - pred) < 7200) {
				rtp->lastts = pred;
				rtp->lastovidtimestamp += f->samples;
			} else {
				if (option_debug > 2)
					ast_log(LOG_DEBUG, "Difference is %d, ms is %d (%d), pred/ts/samples %d/%d/%d\n", abs(rtp->lastts - pred), ms, ms * 90, rtp->lastts, pred, f->samples);
	/* If the timestamp for non-digit packets has moved beyond the timestamp
	   for digits, update the digit timestamp.
	*/
	if (rtp->lastts > rtp->lastdigitts)
		rtp->lastdigitts = rtp->lastts;

	/* Get a pointer to the header */
	rtpheader = (unsigned char *)(f->data - hdrlen);

	put_unaligned_uint32(rtpheader, htonl((2 << 30) | (codec << 16) | (rtp->seqno) | (mark << 23)));
	put_unaligned_uint32(rtpheader + 4, htonl(rtp->lastts));
	put_unaligned_uint32(rtpheader + 8, htonl(rtp->ssrc)); 
	if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
		res = sendto(rtp->s, (void *)rtpheader, f->datalen + hdrlen, 0, (struct sockaddr *)&rtp->them, sizeof(rtp->them));
		if (res <0) {
			if (!rtp->nat || (rtp->nat && (ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
				ast_log(LOG_DEBUG, "RTP Transmission error of packet %d to %s:%d: %s\n", rtp->seqno, ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
			} else if ((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) {
				/* Only give this error message once if we are not RTP debugging */
				if (option_debug || rtpdebug)
					ast_log(LOG_DEBUG, "RTP NAT: Can't write RTP to private address %s:%d, waiting for other end to send audio...\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port));
				ast_set_flag(rtp, FLAG_NAT_INACTIVE_NOWARN);
			}
		} else {
			rtp->txcount++;
			rtp->txoctetcount +=(res - hdrlen);
			
			if (rtp->rtcp->schedid < 1) 
			    rtp->rtcp->schedid = ast_sched_add(rtp->sched, ast_rtcp_calc_interval(rtp), ast_rtcp_write, rtp);
		if (rtp_debug_test_addr(&rtp->them))
			ast_verbose("Sent RTP packet to %s:%d (type %d, seq %u, ts %u, len %u)\n",
					ast_inet_ntoa(iabuf, sizeof(iabuf), rtp->them.sin_addr), ntohs(rtp->them.sin_port), codec, rtp->seqno, rtp->lastts,res - hdrlen);
Mark Spencer's avatar
Mark Spencer committed
	return 0;
}
Mark Spencer's avatar
Mark Spencer committed

int ast_rtp_write(struct ast_rtp *rtp, struct ast_frame *_f)
{
	struct ast_frame *f;
	int codec;
	int hdrlen = 12;
	int subclass;
Mark Spencer's avatar
Mark Spencer committed

	/* If we have no peer, return immediately */	
	if (!rtp->them.sin_addr.s_addr)
		return 0;

	/* If there is no data length, return immediately */
	if (!_f->datalen) 
		return 0;
Mark Spencer's avatar
Mark Spencer committed
	/* Make sure we have enough space for RTP header */
	if ((_f->frametype != AST_FRAME_VOICE) && (_f->frametype != AST_FRAME_VIDEO)) {
		ast_log(LOG_WARNING, "RTP can only send voice and video\n");
Mark Spencer's avatar
Mark Spencer committed
		return -1;
	}

	subclass = _f->subclass;
	if (_f->frametype == AST_FRAME_VIDEO)
		subclass &= ~0x1;
	codec = ast_rtp_lookup_code(rtp, 1, subclass);
Mark Spencer's avatar
Mark Spencer committed
	if (codec < 0) {
		ast_log(LOG_WARNING, "Don't know how to send format %s packets with RTP\n", ast_getformatname(_f->subclass));
Mark Spencer's avatar
Mark Spencer committed
		return -1;
	}

	if (rtp->lasttxformat != subclass) {
Mark Spencer's avatar
Mark Spencer committed
		/* New format, reset the smoother */
		if (option_debug)
			ast_log(LOG_DEBUG, "Ooh, format changed from %s to %s\n", ast_getformatname(rtp->lasttxformat), ast_getformatname(subclass));
		rtp->lasttxformat = subclass;
Mark Spencer's avatar
Mark Spencer committed
		if (rtp->smoother)
			ast_smoother_free(rtp->smoother);
		rtp->smoother = NULL;
	}

	switch(subclass) {
	case AST_FORMAT_SLINEAR:
		if (!rtp->smoother) {
			rtp->smoother = ast_smoother_new(320);
		}
		if (!rtp->smoother) {
			ast_log(LOG_WARNING, "Unable to create smoother :(\n");
			return -1;
		}
		ast_smoother_feed_be(rtp->smoother, _f);
		
		while((f = ast_smoother_read(rtp->smoother)))
			ast_rtp_raw_write(rtp, f, codec);
		break;
Mark Spencer's avatar
Mark Spencer committed
	case AST_FORMAT_ULAW:
	case AST_FORMAT_ALAW:
		if (!rtp->smoother) {
			rtp->smoother = ast_smoother_new(160);
		}
		if (!rtp->smoother) {
			ast_log(LOG_WARNING, "Unable to create smoother :(\n");
			return -1;
		}
		ast_smoother_feed(rtp->smoother, _f);
		
		while((f = ast_smoother_read(rtp->smoother)))
			ast_rtp_raw_write(rtp, f, codec);
		break;
Mark Spencer's avatar
Mark Spencer committed
	case AST_FORMAT_ADPCM:
	case AST_FORMAT_G726:
		if (!rtp->smoother) {
			rtp->smoother = ast_smoother_new(80);
		}
		if (!rtp->smoother) {
			ast_log(LOG_WARNING, "Unable to create smoother :(\n");
			return -1;
		}
		ast_smoother_feed(rtp->smoother, _f);
		
Mark Spencer's avatar
Mark Spencer committed
		while((f = ast_smoother_read(rtp->smoother)))
			ast_rtp_raw_write(rtp, f, codec);
		break;
	case AST_FORMAT_G729A:
		if (!rtp->smoother) {
			rtp->smoother = ast_smoother_new(20);
			if (rtp->smoother)
				ast_smoother_set_flags(rtp->smoother, AST_SMOOTHER_FLAG_G729);
Mark Spencer's avatar
Mark Spencer committed
		}
		if (!rtp->smoother) {
			ast_log(LOG_WARNING, "Unable to create g729 smoother :(\n");
			return -1;
		}
		ast_smoother_feed(rtp->smoother, _f);
		
		while((f = ast_smoother_read(rtp->smoother)))
			ast_rtp_raw_write(rtp, f, codec);
		break;
Mark Spencer's avatar
Mark Spencer committed
	case AST_FORMAT_GSM:
		if (!rtp->smoother) {
			rtp->smoother = ast_smoother_new(33);
		}
		if (!rtp->smoother) {
			ast_log(LOG_WARNING, "Unable to create GSM smoother :(\n");
			return -1;
		}
		ast_smoother_feed(rtp->smoother, _f);
		while((f = ast_smoother_read(rtp->smoother)))
			ast_rtp_raw_write(rtp, f, codec);
		break;
Mark Spencer's avatar
Mark Spencer committed
	case AST_FORMAT_ILBC:
		if (!rtp->smoother) {
Mark Spencer's avatar
Mark Spencer committed
			rtp->smoother = ast_smoother_new(50);
Mark Spencer's avatar
Mark Spencer committed
		}
		if (!rtp->smoother) {
			ast_log(LOG_WARNING, "Unable to create ILBC smoother :(\n");
			return -1;
		}
		ast_smoother_feed(rtp->smoother, _f);
		while((f = ast_smoother_read(rtp->smoother)))
			ast_rtp_raw_write(rtp, f, codec);
		break;
Mark Spencer's avatar
Mark Spencer committed
	default:	
		ast_log(LOG_WARNING, "Not sure about sending format %s packets\n", ast_getformatname(subclass));
		/* fall through to... */
	case AST_FORMAT_H261:
	case AST_FORMAT_H263:
	case AST_FORMAT_H263_PLUS:
	case AST_FORMAT_H264:
	case AST_FORMAT_G723_1:
Mark Spencer's avatar
Mark Spencer committed
	case AST_FORMAT_LPC10:
	case AST_FORMAT_SPEEX:
	        /* Don't buffer outgoing frames; send them one-per-packet: */
Mark Spencer's avatar
Mark Spencer committed
		if (_f->offset < hdrlen) {
			f = ast_frdup(_f);
		} else {
			f = _f;
		}
		ast_rtp_raw_write(rtp, f, codec);
	}
		
	return 0;
}
/*! \brief Unregister interface to channel driver */
void ast_rtp_proto_unregister(struct ast_rtp_protocol *proto)
{
	AST_LIST_REMOVE(&protos, proto, list);
/*! \brief Register interface to channel driver */
int ast_rtp_proto_register(struct ast_rtp_protocol *proto)
{
	struct ast_rtp_protocol *cur;

	AST_LIST_LOCK(&protos);
	AST_LIST_TRAVERSE(&protos, cur, list) {	
		if (!strcmp(cur->type, proto->type)) {
			ast_log(LOG_WARNING, "Tried to register same protocol '%s' twice\n", cur->type);
	AST_LIST_INSERT_HEAD(&protos, proto, list);
	AST_LIST_UNLOCK(&protos);
	
/*! \brief Bridge calls. If possible and allowed, initiate
	re-invite so the peers exchange media directly outside 
	of Asterisk. */
enum ast_bridge_result ast_rtp_bridge(struct ast_channel *c0, struct ast_channel *c1, int flags, struct ast_frame **fo, struct ast_channel **rc, int timeoutms)
{
	struct ast_frame *f;
	struct ast_channel *who, *other, *cs[3];
	struct ast_rtp *p0, *p1;		/* Audio RTP Channels */
	struct ast_rtp *vp0, *vp1;		/* Video RTP channels */
	struct ast_rtp_protocol *pr0, *pr1;
	struct sockaddr_in ac0, ac1;
	struct sockaddr_in vac0, vac1;
	struct sockaddr_in t0, t1;
	char iabuf[INET_ADDRSTRLEN];
	void *pvt0, *pvt1;
	int codec0,codec1, oldcodec0, oldcodec1;
	
	memset(&vt0, 0, sizeof(vt0));
	memset(&vt1, 0, sizeof(vt1));
	memset(&vac0, 0, sizeof(vac0));
	memset(&vac1, 0, sizeof(vac1));

	/* Lock channels */
	ast_channel_lock(c0);
	while(ast_channel_trylock(c1)) {
		ast_channel_unlock(c0);
		usleep(1);

	/* Find channel driver interfaces */
	pr0 = get_proto(c0);
	pr1 = get_proto(c1);
	if (!pr0) {
		ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
		return AST_BRIDGE_FAILED;
	}
	if (!pr1) {
		ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
		return AST_BRIDGE_FAILED;

	/* Get channel specific interface structures */
	pvt0 = c0->tech_pvt;
	pvt1 = c1->tech_pvt;

	/* Get audio and video interface (if native bridge is possible) */
	p0 = pr0->get_rtp_info(c0);
	vp0 = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0) : NULL;
	p1 = pr1->get_rtp_info(c1);
	vp1 = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1) : NULL;

	/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
	if (!p0 || !p1) {
		/* Somebody doesn't want to play... */
		return AST_BRIDGE_FAILED_NOWARN;

	if (ast_test_flag(p0, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) {
		/* can't bridge, we are carrying DTMF for this channel and the bridge
		   needs it
		*/
		ast_channel_unlock(c0);
		ast_channel_unlock(c1);
		return AST_BRIDGE_FAILED_NOWARN;
	}

	if (ast_test_flag(p1, FLAG_HAS_DTMF) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)) {
		/* can't bridge, we are carrying DTMF for this channel and the bridge
		   needs it
		*/
		ast_channel_unlock(c0);
		ast_channel_unlock(c1);
		return AST_BRIDGE_FAILED_NOWARN;
	}

	/* Get codecs from both sides */
	codec0 = pr0->get_codec ? pr0->get_codec(c0) : 0;
	codec1 = pr1->get_codec ? pr1->get_codec(c1) : 0;
	if (pr0->get_codec && pr1->get_codec) {
		/* Hey, we can't do reinvite if both parties speak different codecs */
		if (!(codec0 & codec1)) {
			if (option_debug)
				ast_log(LOG_DEBUG, "Channel codec0 = %d is not codec1 = %d, cannot native bridge in RTP.\n", codec0, codec1);
			return AST_BRIDGE_FAILED_NOWARN;
	if (option_verbose > 2) 
		ast_verbose(VERBOSE_PREFIX_3 "Native bridging %s and %s\n", c0->name, c1->name);

	/* Ok, we should be able to redirect the media. Start with one channel */
	if (pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE))) 
		ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
	else {
		/* Store RTP peer */
		ast_rtp_get_peer(p1, &ac1);
Mark Spencer's avatar
Mark Spencer committed
			ast_rtp_get_peer(vp1, &vac1);
	/* Then test the other channel */
	if (pr1->set_rtp_peer(c1, p0, vp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))
		ast_log(LOG_WARNING, "Channel '%s' failed to talk back to '%s'\n", c1->name, c0->name);
	else {
		/* Store RTP peer */
		ast_rtp_get_peer(p0, &ac0);
Mark Spencer's avatar
Mark Spencer committed
			ast_rtp_get_peer(vp0, &vac0);
	/* External RTP Bridge up, now loop and see if something happes that force us to take the
		media back to Asterisk */
	cs[0] = c0;
	cs[1] = c1;
	cs[2] = NULL;
	oldcodec0 = codec0;
	oldcodec1 = codec1;
	for (;;) {
		/* Check if something changed... */
		if ((c0->tech_pvt != pvt0)  ||
			(c1->tech_pvt != pvt1) ||
			(c0->masq || c0->masqr || c1->masq || c1->masqr)) {
				ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
					if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0)) 
						ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
					if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0)) 
						ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
				return AST_BRIDGE_RETRY;
		/* Now check if they have changed address */
		ast_rtp_get_peer(p1, &t1);
Mark Spencer's avatar
Mark Spencer committed
		ast_rtp_get_peer(p0, &t0);
		if (pr0->get_codec)
			codec0 = pr0->get_codec(c0);
		if (pr1->get_codec)
			codec1 = pr1->get_codec(c1);
		if (vp1)
			ast_rtp_get_peer(vp1, &vt1);
		if (vp0)
			ast_rtp_get_peer(vp0, &vt0);
		if (inaddrcmp(&t1, &ac1) || (vp1 && inaddrcmp(&vt1, &vac1)) || (codec1 != oldcodec1)) {
			if (option_debug > 1) {
				ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n", 
					c1->name, ast_inet_ntoa(iabuf, sizeof(iabuf), t1.sin_addr), ntohs(t1.sin_port), codec1);
				ast_log(LOG_DEBUG, "Oooh, '%s' changed end vaddress to %s:%d (format %d)\n", 
					c1->name, ast_inet_ntoa(iabuf, sizeof(iabuf), vt1.sin_addr), ntohs(vt1.sin_port), codec1);
				ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n", 
					c1->name, ast_inet_ntoa(iabuf, sizeof(iabuf), ac1.sin_addr), ntohs(ac1.sin_port), oldcodec1);
				ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n", 
					c1->name, ast_inet_ntoa(iabuf, sizeof(iabuf), vac1.sin_addr), ntohs(vac1.sin_port), oldcodec1);
			}
			if (pr0->set_rtp_peer(c0, t1.sin_addr.s_addr ? p1 : NULL, vt1.sin_addr.s_addr ? vp1 : NULL, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE))) 
				ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c0->name, c1->name);
			memcpy(&ac1, &t1, sizeof(ac1));
			memcpy(&vac1, &vt1, sizeof(vac1));
			oldcodec1 = codec1;
		if (inaddrcmp(&t0, &ac0) || (vp0 && inaddrcmp(&vt0, &vac0))) {
			if (option_debug) {
				ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n", 
					c0->name, ast_inet_ntoa(iabuf, sizeof(iabuf), t0.sin_addr), ntohs(t0.sin_port), codec0);
				ast_log(LOG_DEBUG, "Oooh, '%s' was %s:%d/(format %d)\n", 
					c0->name, ast_inet_ntoa(iabuf, sizeof(iabuf), ac0.sin_addr), ntohs(ac0.sin_port), oldcodec0);
			}
			if (pr1->set_rtp_peer(c1, t0.sin_addr.s_addr ? p0 : NULL, vt0.sin_addr.s_addr ? vp0 : NULL, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))
				ast_log(LOG_WARNING, "Channel '%s' failed to update to '%s'\n", c1->name, c0->name);
			memcpy(&ac0, &t0, sizeof(ac0));
			memcpy(&vac0, &vt0, sizeof(vac0));
			oldcodec0 = codec0;
		who = ast_waitfor_n(cs, 2, &timeoutms);
		if (!who) {
			if (option_debug)
				ast_log(LOG_DEBUG, "Ooh, empty read...\n");
			/* check for hangup / whentohangup */
			if (ast_check_hangup(c0) || ast_check_hangup(c1))
				break;
			continue;
		}
		f = ast_read(who);
		other = (who == c0) ? c1 : c0; /* the other channel */
		if (!f || ((f->frametype == AST_FRAME_DTMF) &&
				   (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || 
			       ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
			/* breaking out of the bridge. */
			*fo = f;
			*rc = who;
			if (option_debug)
				ast_log(LOG_DEBUG, "Oooh, got a %s\n", f ? "digit" : "hangup");
				if (pr0->set_rtp_peer(c0, NULL, NULL, 0, 0)) 
					ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c0->name);
				if (pr1->set_rtp_peer(c1, NULL, NULL, 0, 0)) 
					ast_log(LOG_WARNING, "Channel '%s' failed to break RTP bridge\n", c1->name);
			return AST_BRIDGE_COMPLETE;
Mark Spencer's avatar
Mark Spencer committed
		} else if ((f->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
			if ((f->subclass == AST_CONTROL_HOLD) || (f->subclass == AST_CONTROL_UNHOLD) ||
			    (f->subclass == AST_CONTROL_VIDUPDATE)) {
				ast_indicate(other, f->subclass);
Mark Spencer's avatar
Mark Spencer committed
				ast_frfree(f);
			} else {
				*fo = f;
				*rc = who;
				ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", f->subclass, who->name);
				return AST_BRIDGE_COMPLETE;
			}
Mark Spencer's avatar
Mark Spencer committed
			if ((f->frametype == AST_FRAME_DTMF) || 
				(f->frametype == AST_FRAME_VOICE) || 
				(f->frametype == AST_FRAME_VIDEO)) {
				/* Forward voice or DTMF frames if they happen upon us */
				ast_write(other, f);
			ast_frfree(f);
		/* Swap priority not that it's a big deal at this point */
		cs[2] = cs[0];
		cs[0] = cs[1];
		cs[1] = cs[2];
		
	}
	return AST_BRIDGE_FAILED;
static int rtp_do_debug_ip(int fd, int argc, char *argv[])
{
	struct hostent *hp;
	struct ast_hostent ahp;
	char iabuf[INET_ADDRSTRLEN];
	int port = 0;
	char *p, *arg;
	if (argc != 4)
		return RESULT_SHOWUSAGE;
	arg = argv[3];
	p = strstr(arg, ":");
		*p = '\0';
		p++;
		port = atoi(p);
	}
	hp = ast_gethostbyname(arg, &ahp);
	if (hp == NULL)
		return RESULT_SHOWUSAGE;
	rtpdebugaddr.sin_family = AF_INET;
	memcpy(&rtpdebugaddr.sin_addr, hp->h_addr, sizeof(rtpdebugaddr.sin_addr));
	rtpdebugaddr.sin_port = htons(port);
	if (port == 0)
		ast_cli(fd, "RTP Debugging Enabled for IP: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtpdebugaddr.sin_addr));
	else
		ast_cli(fd, "RTP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtpdebugaddr.sin_addr), port);
	rtpdebug = 1;
	return RESULT_SUCCESS;
}

static int rtcp_do_debug_ip(int fd, int argc, char *argv[])
{
	struct hostent *hp;
	struct ast_hostent ahp;
	char iabuf[INET_ADDRSTRLEN];
	int port = 0;
	char *p, *arg;
	if (argc != 5)
		return RESULT_SHOWUSAGE;

	arg = argv[4];
	p = strstr(arg, ":");
	if (p) {
		*p = '\0';
		p++;
		port = atoi(p);
	}
	hp = ast_gethostbyname(arg, &ahp);
	if (hp == NULL)
		return RESULT_SHOWUSAGE;
	rtcpdebugaddr.sin_family = AF_INET;
	memcpy(&rtcpdebugaddr.sin_addr, hp->h_addr, sizeof(rtcpdebugaddr.sin_addr));
	rtcpdebugaddr.sin_port = htons(port);
	if (port == 0)
		ast_cli(fd, "RTCP Debugging Enabled for IP: %s\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtcpdebugaddr.sin_addr));
	else
		ast_cli(fd, "RTCP Debugging Enabled for IP: %s:%d\n", ast_inet_ntoa(iabuf, sizeof(iabuf), rtcpdebugaddr.sin_addr), port);
	rtcpdebug = 1;
	return RESULT_SUCCESS;
}

static int rtp_do_debug(int fd, int argc, char *argv[])
{
	if (argc != 2) {
		if (argc != 4)
			return RESULT_SHOWUSAGE;
		return rtp_do_debug_ip(fd, argc, argv);
	}
	rtpdebug = 1;
	memset(&rtpdebugaddr,0,sizeof(rtpdebugaddr));
	ast_cli(fd, "RTP Debugging Enabled\n");
	return RESULT_SUCCESS;
}
   
static int rtcp_do_debug(int fd, int argc, char *argv[]) {
	if (argc != 3) {
		if (argc != 5)
			return RESULT_SHOWUSAGE;
		return rtcp_do_debug_ip(fd, argc, argv);
	}
	rtcpdebug = 1;
	memset(&rtcpdebugaddr,0,sizeof(rtcpdebugaddr));
	ast_cli(fd, "RTCP Debugging Enabled\n");
	return RESULT_SUCCESS;
}

static int rtcp_do_stats(int fd, int argc, char *argv[]) {
	if (argc != 3) {
		return RESULT_SHOWUSAGE;
	}
	rtcpstats = 1;
	ast_cli(fd, "RTCP Stats Enabled\n");
	return RESULT_SUCCESS;
}

static int rtp_no_debug(int fd, int argc, char *argv[])
{
	if (argc != 3)
		return RESULT_SHOWUSAGE;
	rtpdebug = 0;
	ast_cli(fd,"RTP Debugging Disabled\n");
	return RESULT_SUCCESS;
}

static int rtcp_no_debug(int fd, int argc, char *argv[])
{
	if (argc != 4)
		return RESULT_SHOWUSAGE;
	rtcpdebug = 0;
	ast_cli(fd,"RTCP Debugging Disabled\n");
	return RESULT_SUCCESS;
}

static int rtcp_no_stats(int fd, int argc, char *argv[])
{
	if (argc != 4)
		return RESULT_SHOWUSAGE;
	rtcpstats = 0;
	ast_cli(fd,"RTCP Stats Disabled\n");
	return RESULT_SUCCESS;
}


static int stun_do_debug(int fd, int argc, char *argv[])
{
	if (argc != 2) {
		return RESULT_SHOWUSAGE;
	}
	stundebug = 1;
	ast_cli(fd, "STUN Debugging Enabled\n");
	return RESULT_SUCCESS;
}
   
static int stun_no_debug(int fd, int argc, char *argv[])
{
	if (argc != 3)
		return RESULT_SHOWUSAGE;
	stundebug = 0;
	ast_cli(fd,"STUN Debugging Disabled\n");
	return RESULT_SUCCESS;
}


static char debug_usage[] =
  "Usage: rtp debug [ip host[:port]]\n"
  "       Enable dumping of all RTP packets to and from host.\n";
static char no_debug_usage[] =
  "Usage: rtp no debug\n"
  "       Disable all RTP debugging\n";

static char stun_debug_usage[] =
  "Usage: stun debug\n"
  "       Enable STUN (Simple Traversal of UDP through NATs) debugging\n";

static char stun_no_debug_usage[] =
  "Usage: stun no debug\n"
  "       Disable STUN debugging\n";


static struct ast_cli_entry  cli_debug_ip =
{{ "rtp", "debug", "ip", NULL } , rtp_do_debug, "Enable RTP debugging on IP", debug_usage };

static struct ast_cli_entry  cli_debug =
{{ "rtp", "debug", NULL } , rtp_do_debug, "Enable RTP debugging", debug_usage };

static struct ast_cli_entry  cli_no_debug =
{{ "rtp", "no", "debug", NULL } , rtp_no_debug, "Disable RTP debugging", no_debug_usage };

static char rtcp_debug_usage[] =
  "Usage: rtp rtcp debug [ip host[:port]]\n"
  "       Enable dumping of all RTCP packets to and from host.\n";
  
static char rtcp_no_debug_usage[] =
  "Usage: rtp rtcp no debug\n"
  "       Disable all RTCP debugging\n";

static char rtcp_stats_usage[] =
  "Usage: rtp rtcp stats\n"
  "       Enable dumping of RTCP stats.\n";
  
static char rtcp_no_stats_usage[] =
  "Usage: rtp rtcp no stats\n"
  "       Disable all RTCP stats\n";

static struct ast_cli_entry  cli_debug_ip_rtcp =
{{ "rtp", "rtcp", "debug", "ip", NULL } , rtcp_do_debug, "Enable RTCP debugging on IP", rtcp_debug_usage };

static struct ast_cli_entry  cli_debug_rtcp =
{{ "rtp", "rtcp", "debug", NULL } , rtcp_do_debug, "Enable RTCP debugging", rtcp_debug_usage };

static struct ast_cli_entry  cli_no_debug_rtcp =
{{ "rtp", "rtcp", "no", "debug", NULL } , rtcp_no_debug, "Disable RTCP debugging", rtcp_no_debug_usage };

static struct ast_cli_entry  cli_stats_rtcp =
{{ "rtp", "rtcp", "stats", NULL } , rtcp_do_stats, "Enable RTCP stats", rtcp_stats_usage };

static struct ast_cli_entry  cli_no_stats_rtcp =
{{ "rtp", "rtcp", "no", "stats", NULL } , rtcp_no_stats, "Disable RTCP stats", rtcp_no_stats_usage };

static struct ast_cli_entry  cli_stun_debug =
{{ "stun", "debug", NULL } , stun_do_debug, "Enable STUN debugging", stun_debug_usage };

static struct ast_cli_entry  cli_stun_no_debug =
{{ "stun", "no", "debug", NULL } , stun_no_debug, "Disable STUN debugging", stun_no_debug_usage };

int ast_rtp_reload(void)
{
	struct ast_config *cfg;
	char *s;
	rtpstart = 5000;
	rtpend = 31000;
	dtmftimeout = DEFAULT_DTMF_TIMEOUT;
	cfg = ast_config_load("rtp.conf");
	if (cfg) {
		if ((s = ast_variable_retrieve(cfg, "general", "rtpstart"))) {
			rtpstart = atoi(s);
			if (rtpstart < 1024)
				rtpstart = 1024;
			if (rtpstart > 65535)
				rtpstart = 65535;
		}
		if ((s = ast_variable_retrieve(cfg, "general", "rtpend"))) {
			rtpend = atoi(s);
			if (rtpend < 1024)
				rtpend = 1024;
			if (rtpend > 65535)
				rtpend = 65535;
		}
		if ((s = ast_variable_retrieve(cfg, "general", "rtcpinterval"))) {
			rtcpinterval = atoi(s);
			if (rtcpinterval == 0)
				rtcpinterval = 0; /* Just so we're clear... it's zero */
			if (rtcpinterval < RTCP_MIN_INTERVALMS)
				rtcpinterval = RTCP_MIN_INTERVALMS; /* This catches negative numbers too */
			if (rtcpinterval > RTCP_MAX_INTERVALMS)
				rtcpinterval = RTCP_MAX_INTERVALMS;
		}
		if ((s = ast_variable_retrieve(cfg, "general", "rtpchecksums"))) {
			if (ast_false(s))
				nochecksums = 1;
				ast_log(LOG_WARNING, "Disabling RTP checksums is not supported on this operating system!\n");
#endif
		if ((s = ast_variable_retrieve(cfg, "general", "dtmftimeout"))) {
			dtmftimeout = atoi(s);
			if ((dtmftimeout < 0) || (dtmftimeout > 20000)) {
				ast_log(LOG_WARNING, "DTMF timeout of '%d' outside range, using default of '%d' instead\n",
					dtmftimeout, DEFAULT_DTMF_TIMEOUT);
				dtmftimeout = DEFAULT_DTMF_TIMEOUT;
			};
		}
		ast_config_destroy(cfg);
	}
	if (rtpstart >= rtpend) {
		ast_log(LOG_WARNING, "Unreasonable values for RTP start/end port in rtp.conf\n");
		rtpstart = 5000;
		rtpend = 31000;
	}
	if (option_verbose > 1)
		ast_verbose(VERBOSE_PREFIX_2 "RTP Allocating from port range %d -> %d\n", rtpstart, rtpend);
/*! \brief Initialize the RTP system in Asterisk */
void ast_rtp_init(void)
{
	ast_cli_register(&cli_debug);
	ast_cli_register(&cli_debug_ip);
	ast_cli_register(&cli_no_debug);

	ast_cli_register(&cli_debug_rtcp);
	ast_cli_register(&cli_debug_ip_rtcp);
	ast_cli_register(&cli_no_debug_rtcp);

	ast_cli_register(&cli_stats_rtcp);
	ast_cli_register(&cli_no_stats_rtcp);
	
	ast_cli_register(&cli_stun_debug);
	ast_cli_register(&cli_stun_no_debug);
	ast_rtp_reload();
}