Skip to content
Snippets Groups Projects
rtp.c 56 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	struct ast_rtp *rtp;
    	int x;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	int first;
    
    	int startplace;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	rtp = malloc(sizeof(struct ast_rtp));
    	if (!rtp)
    		return NULL;
    	memset(rtp, 0, sizeof(struct ast_rtp));
    	rtp->them.sin_family = AF_INET;
    	rtp->us.sin_family = AF_INET;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	rtp->s = rtp_socket();
    
    Mark Spencer's avatar
    Mark Spencer committed
    	rtp->ssrc = rand();
    	rtp->seqno = rand() & 0xffff;
    	if (rtp->s < 0) {
    		free(rtp);
    
    		ast_log(LOG_ERROR, "Unable to allocate socket: %s\n", strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    		return NULL;
    	}
    
    	if (sched && rtcpenable) {
    		rtp->sched = sched;
    		rtp->rtcp = ast_rtcp_new();
    	}
    
    	
    	/* Select a random port number in the range of possible RTP */
    
    	x = (rand() % (rtpend-rtpstart)) + rtpstart;
    	x = x & ~1;
    
    	/* Save it for future references. */
    
    	startplace = x;
    
    	/* Iterate tring to bind that port and incrementing it otherwise untill a port was found or no ports are available. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    	for (;;) {
    		/* Must be an even port number by RTP spec */
    		rtp->us.sin_port = htons(x);
    
    		/* If there's rtcp, initialize it as well. */
    
    		if (rtp->rtcp)
    			rtp->rtcp->us.sin_port = htons(x + 1);
    
    		/* Try to bind it/them. */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!(first = bind(rtp->s, (struct sockaddr *)&rtp->us, sizeof(rtp->us))) &&
    
    			(!rtp->rtcp || !bind(rtp->rtcp->s, (struct sockaddr *)&rtp->rtcp->us, sizeof(rtp->rtcp->us))))
    
    Mark Spencer's avatar
    Mark Spencer committed
    			break;
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (!first) {
    			/* Primary bind succeeded! Gotta recreate it */
    			close(rtp->s);
    			rtp->s = rtp_socket();
    		}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (errno != EADDRINUSE) {
    
    			/* We got an error that wasn't expected, abort! */
    
    			ast_log(LOG_ERROR, "Unexpected bind error: %s\n", strerror(errno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    			close(rtp->s);
    
    			if (rtp->rtcp) {
    				close(rtp->rtcp->s);
    				free(rtp->rtcp);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    			free(rtp);
    			return NULL;
    		}
    
    		/* The port was used, increment it (by two). */
    
    		x += 2;
    
    		/* Did we go over the limit ? */
    
    		if (x > rtpend)
    
    			/* then, start from the begingig. */
    
    			x = (rtpstart + 1) & ~1;
    
    		/* Check if we reached the place were we started. */
    
    		if (x == startplace) {
    
    			/* If so, there's no ports available. */
    
    			ast_log(LOG_ERROR, "No RTP ports remaining. Can't setup media stream for this call.\n");
    
    			close(rtp->s);
    
    			if (rtp->rtcp) {
    				close(rtp->rtcp->s);
    				free(rtp->rtcp);
    			}
    
    			free(rtp);
    			return NULL;
    		}
    
    	if (io && sched && callbackmode) {
    
    		/* Operate this one in a callback mode */
    		rtp->sched = sched;
    		rtp->io = io;
    		rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
    	}
    
    	ast_rtp_pt_default(rtp);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return rtp;
    }
    
    
    struct ast_rtp *ast_rtp_new(struct sched_context *sched, struct io_context *io, int rtcpenable, int callbackmode)
    {
    	struct in_addr ia;
    
    	memset(&ia, 0, sizeof(ia));
    	return ast_rtp_new_with_bindaddr(sched, io, rtcpenable, callbackmode, ia);
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_rtp_settos(struct ast_rtp *rtp, int tos)
    {
    	int res;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if ((res = setsockopt(rtp->s, IPPROTO_IP, IP_TOS, &tos, sizeof(tos)))) 
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "Unable to set TOS to %d\n", tos);
    	return res;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_rtp_set_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
    {
    	rtp->them.sin_port = them->sin_port;
    	rtp->them.sin_addr = them->sin_addr;
    
    	if (rtp->rtcp) {
    		rtp->rtcp->them.sin_port = htons(ntohs(them->sin_port) + 1);
    		rtp->rtcp->them.sin_addr = them->sin_addr;
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_rtp_get_peer(struct ast_rtp *rtp, struct sockaddr_in *them)
    {
    	them->sin_family = AF_INET;
    	them->sin_port = rtp->them.sin_port;
    	them->sin_addr = rtp->them.sin_addr;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_rtp_get_us(struct ast_rtp *rtp, struct sockaddr_in *us)
    {
    	memcpy(us, &rtp->us, sizeof(rtp->us));
    }
    
    
    void ast_rtp_stop(struct ast_rtp *rtp)
    {
    	memset(&rtp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
    	memset(&rtp->them.sin_port, 0, sizeof(rtp->them.sin_port));
    
    	if (rtp->rtcp) {
    		memset(&rtp->rtcp->them.sin_addr, 0, sizeof(rtp->them.sin_addr));
    		memset(&rtp->rtcp->them.sin_port, 0, sizeof(rtp->them.sin_port));
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_rtp_reset(struct ast_rtp *rtp)
    {
    	memset(&rtp->rxcore, 0, sizeof(rtp->rxcore));
    	memset(&rtp->txcore, 0, sizeof(rtp->txcore));
    	memset(&rtp->dtmfmute, 0, sizeof(rtp->dtmfmute));
    	rtp->lastts = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	rtp->lastrxts = 0;
    	rtp->lastividtimestamp = 0;
    	rtp->lastovidtimestamp = 0;
    	rtp->lasteventseqn = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	rtp->lasttxformat = 0;
    	rtp->lastrxformat = 0;
    	rtp->dtmfcount = 0;
    	rtp->dtmfduration = 0;
    	rtp->seqno = 0;
    	rtp->rxseqno = 0;
    }
    
    
    Mark Spencer's avatar
    Mark Spencer committed
    void ast_rtp_destroy(struct ast_rtp *rtp)
    {
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (rtp->smoother)
    		ast_smoother_free(rtp->smoother);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	if (rtp->ioid)
    		ast_io_remove(rtp->io, rtp->ioid);
    	if (rtp->s > -1)
    		close(rtp->s);
    
    	if (rtp->rtcp) {
    		close(rtp->rtcp->s);
    		free(rtp->rtcp);
    	}
    
    Mark Spencer's avatar
    Mark Spencer committed
    	free(rtp);
    }
    
    
    static unsigned int calc_txstamp(struct ast_rtp *rtp, struct timeval *delivery)
    
    	struct timeval t;
    	long ms;
    	if (ast_tvzero(rtp->txcore)) {
    		rtp->txcore = ast_tvnow();
    
    		/* Round to 20ms for nice, pretty timestamps */
    
    Mark Spencer's avatar
    Mark Spencer committed
    		rtp->txcore.tv_usec -= rtp->txcore.tv_usec % 20000;
    
    	/* Use previous txcore if available */
    	t = (delivery && !ast_tvzero(*delivery)) ? *delivery : ast_tvnow();
    	ms = ast_tvdiff_ms(t, rtp->txcore);
    
    	if (ms < 0)
    		ms = 0;
    
    	/* Use what we just got for next time */
    	rtp->txcore = t;
    	return (unsigned int) ms;
    
    Mark Spencer's avatar
    Mark Spencer committed
    int ast_rtp_senddigit(struct ast_rtp *rtp, char digit)
    {
    	unsigned int *rtpheader;
    	int hdrlen = 12;
    	int res;
    	int x;
    
    	unsigned short duration = 0;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	char data[256];
    
    	char iabuf[INET_ADDRSTRLEN];
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	if ((digit <= '9') && (digit >= '0'))
    		digit -= '0';
    	else if (digit == '*')
    		digit = 10;
    	else if (digit == '#')
    		digit = 11;
    	else if ((digit >= 'A') && (digit <= 'D')) 
    		digit = digit - 'A' + 12;
    	else if ((digit >= 'a') && (digit <= 'd')) 
    		digit = digit - 'a' + 12;
    	else {
    		ast_log(LOG_WARNING, "Don't know how to represent '%c'\n", digit);
    		return -1;
    	}
    
    	payload = ast_rtp_lookup_code(rtp, 0, AST_RTP_DTMF);
    
    Mark Spencer's avatar
    Mark Spencer committed
    
    	/* 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));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	
    	/* 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->lastdigitts);
    
    Mark Spencer's avatar
    Mark Spencer committed
    	rtpheader[2] = htonl(rtp->ssrc); 
    	rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0));
    
    
    	for (x = 0; x < 5; x++) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
    
    			res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
    
    				ast_log(LOG_ERROR, "RTP 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))
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    				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), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    
    		/* Sequence number must be incremented for every packet */
    		rtp->seqno++;
    
    		/* Clear marker bit and set seqno */
    		rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno));
    
    		/* Increment duration for 160 (20ms) */
    		duration += 160;
    		rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (duration));
    	}
    
    	/* Set the End bit */
    	rtpheader[3] |= htonl((1 << 23));
    
    	/* Send last packet and repeat it 2 times */
    	for (x = 0; x < 3; x++) {
    		if (rtp->them.sin_port && rtp->them.sin_addr.s_addr) {
    			res = sendto(rtp->s, (void *) rtpheader, hdrlen + 4, 0, (struct sockaddr *) &rtp->them, sizeof(rtp->them));
    			if (res < 0) {
    				ast_log(LOG_ERROR, "RTP 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 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), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
    			}
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		
    		/* Sequence number must be incremented for every packet, even for retransmitted last two packets */
    		rtp->seqno++;
    		/* Set seqno */
    		rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno));
    
    Mark Spencer's avatar
    Mark Spencer committed
    	}
    
    	/* Increment the digit timestamp by 120ms, to ensure that digits
    
    	   sent sequentially with no intervening non-digit packets do not
    
    	   get sent with the same timestamp, and that sequential digits
    	   have some 'dead air' in between them
    
    	duration += 160;
    	rtp->lastdigitts += duration;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	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;
    
    
    	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 %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;
    }
    
    
    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);
    			}
    		}
    				
    
    		if(rtp_debug_test_addr(&rtp->them))
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    			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)) {
    
    Mark Spencer's avatar
    Mark Spencer committed
    		ast_log(LOG_WARNING, "RTP can only send voice\n");
    		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, *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));
    
    
    	/* if need DTMF, cant native bridge */
    	if (flags & (AST_BRIDGE_DTMF_CHANNEL_0 | AST_BRIDGE_DTMF_CHANNEL_1))
    
    		return AST_BRIDGE_FAILED_NOWARN;
    
    	while(ast_mutex_trylock(&c1->lock)) {
    		ast_mutex_unlock(&c0->lock);
    		usleep(1);
    		ast_mutex_lock(&c0->lock);
    	}
    
    
    	/* 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);
    
    		ast_mutex_unlock(&c0->lock);
    		ast_mutex_unlock(&c1->lock);
    
    		return AST_BRIDGE_FAILED;
    
    	}
    	if (!pr1) {
    		ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
    
    		ast_mutex_unlock(&c0->lock);
    		ast_mutex_unlock(&c1->lock);
    
    		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);
    
    	if (pr0->get_vrtp_info)
    		vp0 = pr0->get_vrtp_info(c0);
    	else
    		vp0 = NULL;
    
    	p1 = pr1->get_rtp_info(c1);
    
    	if (pr1->get_vrtp_info)
    		vp1 = pr1->get_vrtp_info(c1);
    	else
    		vp1 = NULL;
    
    
    	/* Check if bridge is still possible (In SIP canreinvite=no stops this, like NAT) */
    
    	if (!p0 || !p1) {
    		/* Somebody doesn't want to play... */
    
    		ast_mutex_unlock(&c0->lock);
    		ast_mutex_unlock(&c1->lock);
    
    		return AST_BRIDGE_FAILED_NOWARN;
    
    	/* Get codecs from both sides */
    
    	if (pr0->get_codec)
    
    	else
    		codec0 = 0;
    	if (pr1->get_codec)
    
    	else
    		codec1 = 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);
    
    	ast_mutex_unlock(&c0->lock);
    	ast_mutex_unlock(&c1->lock);
    
    	/* 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);
    		if (!f || ((f->frametype == AST_FRAME_DTMF) &&
    				   (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) || 
    			       ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
    			*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(who == c0 ? c1 : c0, f->subclass);
    				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 */
    				if (who == c0) {
    					ast_write(c1, f);
    				} else if (who == c1) {
    					ast_write(c0, 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 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 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 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 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 };
    
    
    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", "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_rtp_reload();
    }