Skip to content
Snippets Groups Projects
rtp.c 105 KiB
Newer Older
  • Learn to ignore specific revisions
  • Mark Spencer's avatar
    Mark Spencer committed
    	rtpheader[3] = htonl((digit << 24) | (0xa << 16) | (0));
    
    	for (x = 0; x < 6; 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",
    
    					ntohs(rtp->them.sin_port), strerror(errno));
    			if (rtp_debug_test_addr(&rtp->them))
    
    				ast_verbose("Sent RTP DTMF packet to %s:%d (type %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
    
    					    ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastdigitts, res - hdrlen);
    
    Mark Spencer's avatar
    Mark Spencer committed
    		}
    
    		/* Sequence number of last two end packets does not get incremented */
    		if (x < 3)
    			rtp->seqno++;
    
    		/* Clear marker bit and set seqno */
    		rtpheader[0] = htonl((2 << 30) | (payload << 16) | (rtp->seqno));
    
    		/* For the last three packets, set the duration and the end bit */
    		if (x == 2) {
    #if 0
    			/* No, this is wrong...  Do not increment lastdigitts, that's not according
    			   to the RFC, as best we can determine */
    			rtp->lastdigitts++; /* or else the SPA3000 will click instead of beeping... */
    			rtpheader[1] = htonl(rtp->lastdigitts);
    #endif			
    			/* Make duration 800 (100ms) */
    			rtpheader[3] |= htonl((800));
    			/* Set the End bit */
    			rtpheader[3] |= htonl((1 << 23));
    
    	/*! \note 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
    
    	rtp->lastdigitts += 960;
    	/* Increment the sequence number to reflect the last packet
    	   that was sent
    	*/
    	rtp->seqno++;
    
    Mark Spencer's avatar
    Mark Spencer committed
    	return 0;
    }
    
    
    /* \brief Public function: Send an H.261 fast update request, some devices need this rather than SIP XML */
    int ast_rtcp_send_h261fur(void *data)
    {
    	struct ast_rtp *rtp = data;
    	int res;
    
    	rtp->rtcp->sendfur = 1;
    	res = ast_rtcp_write(data);
    	
    	return res;
    }
    
    /*! \brief Send RTCP sender's report */
    static int ast_rtcp_write_sr(void *data)
    {
    	struct ast_rtp *rtp = data;
    	int res;
    	int len = 0;
    	struct timeval now;
    	unsigned int now_lsw;
    	unsigned int now_msw;
    	unsigned int *rtcpheader;
    	unsigned int lost;
    	unsigned int extended;
    	unsigned int expected;
    	unsigned int expected_interval;
    	unsigned int received_interval;
    	int lost_interval;
    	int fraction;
    	struct timeval dlsr;
    	char bdata[512];
    
    
    	if (!rtp || !rtp->rtcp || (&rtp->rtcp->them.sin_addr == 0))
    
    	if (!rtp->rtcp->them.sin_addr.s_addr) {  /* This'll stop rtcp for this rtp session */
    
    		ast_verbose("RTCP SR transmission error, rtcp halted %s\n",strerror(errno));
    
    		if (rtp->rtcp->schedid > 0)
    			ast_sched_del(rtp->sched, rtp->rtcp->schedid);
    
    		rtp->rtcp->schedid = -1;
    		return 0;
    	}
    
    	gettimeofday(&now, NULL);
    	timeval2ntp(now, &now_msw, &now_lsw); /* fill thses ones in from utils.c*/
    	rtcpheader = (unsigned int *)bdata;
    	rtcpheader[1] = htonl(rtp->ssrc);               /* Our SSRC */
    	rtcpheader[2] = htonl(now_msw);                 /* now, MSW. gettimeofday() + SEC_BETWEEN_1900_AND_1970*/
    	rtcpheader[3] = htonl(now_lsw);                 /* now, LSW */
    	rtcpheader[4] = htonl(rtp->lastts);             /* FIXME shouldn't be that, it should be now */
    	rtcpheader[5] = htonl(rtp->txcount);            /* No. packets sent */
    	rtcpheader[6] = htonl(rtp->txoctetcount);       /* No. bytes sent */
    	len += 28;
    	
    	extended = rtp->cycles + rtp->lastrxseqno;
    	expected = extended - rtp->seedrxseqno + 1;
    	if (rtp->rxcount > expected) 
    		expected += rtp->rxcount - expected;
    	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;
    	timersub(&now, &rtp->rtcp->rxlsr, &dlsr);
    	rtcpheader[7] = htonl(rtp->themssrc);
    	rtcpheader[8] = htonl(((fraction & 0xff) << 24) | (lost & 0xffffff));
    	rtcpheader[9] = htonl((rtp->cycles) | ((rtp->lastrxseqno & 0xffff)));
    	rtcpheader[10] = htonl((unsigned int)rtp->rxjitter);
    	rtcpheader[11] = htonl(rtp->rtcp->themrxlsr);
    	rtcpheader[12] = htonl((((dlsr.tv_sec * 1000) + (dlsr.tv_usec / 1000)) * 65536) / 1000);
    	len += 24;
    	
    	rtcpheader[0] = htonl((2 << 30) | (1 << 24) | (RTCP_PT_SR << 16) | ((len/4)-1));
    
    	if (rtp->rtcp->sendfur) {
    		rtcpheader[13] = htonl((2 << 30) | (0 << 24) | (RTCP_PT_FUR << 16) | 1);
    		rtcpheader[14] = htonl(rtp->ssrc);               /* Our SSRC */
    		len += 8;
    		rtp->rtcp->sendfur = 0;
    	}
    	
    	/* 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 SR transmission error to %s:%d, rtcp halted %s\n",ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port), strerror(errno));
    
    		if (rtp->rtcp->schedid > 0)
    			ast_sched_del(rtp->sched, rtp->rtcp->schedid);
    
    		rtp->rtcp->schedid = -1;
    		return 0;
    	}
    	
    	/* FIXME Don't need to get a new one */
    	gettimeofday(&rtp->rtcp->txlsr, NULL);
    	rtp->rtcp->sr_count++;
    
    	rtp->rtcp->lastsrtxcount = rtp->txcount;	
    	
    	if (rtcp_debug_test_addr(&rtp->rtcp->them)) {
    
    		ast_verbose("* Sent RTCP SR to %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
    
    		ast_verbose("  Our SSRC: %u\n", rtp->ssrc);
    		ast_verbose("  Sent(NTP): %u.%010u\n", (unsigned int)now.tv_sec, (unsigned int)now.tv_usec*4096);
    		ast_verbose("  Sent(RTP): %u\n", rtp->lastts);
    		ast_verbose("  Sent packets: %u\n", rtp->txcount);
    		ast_verbose("  Sent octets: %u\n", rtp->txoctetcount);
    		ast_verbose("  Report block:\n");
    		ast_verbose("  Fraction lost: %u\n", fraction);
    		ast_verbose("  Cumulative loss: %u\n", lost);
    		ast_verbose("  IA jitter: %.4f\n", rtp->rxjitter);
    		ast_verbose("  Their last SR: %u\n", rtp->rtcp->themrxlsr);
    		ast_verbose("  DLSR: %4.4f (sec)\n\n", (double)(ntohl(rtcpheader[12])/65536.0));
    	}
    	return res;
    }
    
    /*! \brief Send RTCP recepient's report */
    static int ast_rtcp_write_rr(void *data)
    {
    	struct ast_rtp *rtp = data;
    	int res;
    	int len = 32;
    	unsigned int lost;
    	unsigned int extended;
    	unsigned int expected;
    	unsigned int expected_interval;
    	unsigned int received_interval;
    	int lost_interval;
    	struct timeval now;
    	unsigned int *rtcpheader;
    	char bdata[1024];
    	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));
    
    		if (rtp->rtcp->schedid > 0)
    			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 */
    
    		if (rtp->rtcp->schedid > 0)
    			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(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];
    	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(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(rtp->them.sin_addr), ntohs(rtp->them.sin_port), payload, rtp->seqno, rtp->lastts,res - hdrlen);		   
    
    Mark Spencer's avatar
    Mark Spencer committed
    static int ast_rtp_raw_write(struct ast_rtp *rtp, struct ast_frame *f, int codec)
    
    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(rtp->them.sin_addr), ntohs(rtp->them.sin_port), strerror(errno));
    
    			} else if (((ast_test_flag(rtp, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(rtp, FLAG_NAT_INACTIVE_NOWARN)) {
    
    				/* 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(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
    
    		} else {
    			rtp->txcount++;
    			rtp->txoctetcount +=(res - hdrlen);
    			
    
    			if (rtp->rtcp && 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 %-2.2d, seq %-6.6u, ts %-6.6u, len %-6.6u)\n",
    
    					ast_inet_ntoa(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:
    
    Kevin P. Fleming's avatar
    Kevin P. Fleming committed
    	case AST_FORMAT_G726_AAL2:
    
    		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 loop for true native bridge (reinvite) */
    static enum ast_bridge_result bridge_native_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, struct ast_rtp_protocol *pr0, struct ast_rtp_protocol *pr1, int codec0, int codec1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
    
    	struct ast_frame *fr = NULL;
    	struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
    	int oldcodec0 = codec0, oldcodec1 = codec1;
    	struct sockaddr_in ac1 = {0,}, vac1 = {0,}, ac0 = {0,}, vac0 = {0,};
    	struct sockaddr_in t1 = {0,}, vt1 = {0,}, t0 = {0,}, vt0 = {0,};
    
    	/* Set it up so audio goes directly between the two endpoints */
    
    	/* Test the first channel */
    	if (!(pr0->set_rtp_peer(c0, p1, vp1, codec1, ast_test_flag(p1, FLAG_NAT_ACTIVE)))) {
    
    		ast_rtp_get_peer(p1, &ac1);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_rtp_get_peer(vp1, &vac1);
    
    	} else
    		ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c0->name, c1->name);
    	
    	/* Test the second channel */
    	if (!(pr1->set_rtp_peer(c1, p0, vp0, codec0, ast_test_flag(p0, FLAG_NAT_ACTIVE)))) {
    
    		ast_rtp_get_peer(p0, &ac0);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			ast_rtp_get_peer(vp0, &vac0);
    
    	} else
    		ast_log(LOG_WARNING, "Channel '%s' failed to talk to '%s'\n", c1->name, c0->name);
    
    	/* Now we can unlock and move into our loop */
    
    
    	/* Throw our channels into the structure and enter the loop */
    
    	cs[0] = c0;
    	cs[1] = c1;
    	cs[2] = NULL;
    	for (;;) {
    
    		/* Check if anything 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 (c0->tech_pvt == pvt0)
    				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 (c1->tech_pvt == pvt1)
    				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;
    
    
    		/* Check if they have changed their address */
    
    		ast_rtp_get_peer(p1, &t1);
    
    		if (vp1)
    			ast_rtp_get_peer(vp1, &vt1);
    
    		if (pr1->get_codec)
    			codec1 = pr1->get_codec(c1);
    		ast_rtp_get_peer(p0, &t0);
    
    		if (vp0)
    			ast_rtp_get_peer(vp0, &vt0);
    
    		if (pr0->get_codec)
    			codec0 = pr0->get_codec(c0);
    		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(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(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(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(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 > 1) {
    				ast_log(LOG_DEBUG, "Oooh, '%s' changed end address to %s:%d (format %d)\n",
    
    					c0->name, ast_inet_ntoa(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(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;
    
    
    		/* Wait for frame to come in on the channels */
    		if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
    			if (!timeoutms)
    
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Ooh, empty read...\n");
    
    			if (ast_check_hangup(c0) || ast_check_hangup(c1))
    				break;
    
    		fr = ast_read(who);
    		other = (who == c0) ? c1 : c0;
    		if (!fr || ((fr->frametype == AST_FRAME_DTMF) &&
    			    (((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) ||
    			     ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1))))) {
    			/* Break out of bridge */
    			*fo = fr;
    
    			*rc = who;
    
    				ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup");
    			if (c0->tech_pvt == pvt0)
    				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 (c1->tech_pvt == pvt1)
    				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;
    		} else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
    			if ((fr->subclass == AST_CONTROL_HOLD) ||
    			    (fr->subclass == AST_CONTROL_UNHOLD) ||
    			    (fr->subclass == AST_CONTROL_VIDUPDATE)) {
    				ast_indicate(other, fr->subclass);
    				ast_frfree(fr);
    			} else {
    				*fo = fr;
    				*rc = who;
    				ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
    				return AST_BRIDGE_COMPLETE;
    			}
    		} else {
    			if ((fr->frametype == AST_FRAME_DTMF) ||
    			    (fr->frametype == AST_FRAME_VOICE) ||
    			    (fr->frametype == AST_FRAME_VIDEO)) {
    				ast_write(other, fr);
    			}
    			ast_frfree(fr);
    		}
    		/* Swap priority */
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    	}
    
    	return AST_BRIDGE_FAILED;
    }
    
    
    /*! \brief P2P RTP/RTCP Callback */
    static int p2p_rtp_callback(int *id, int fd, short events, void *cbdata)
    {
    	int res = 0, hdrlen = 12;
    	struct sockaddr_in sin;
    	socklen_t len;
    	unsigned int *header;
    	struct ast_rtp *rtp = cbdata;
    	int is_rtp = 0, is_rtcp = 0;
    
    	if (!rtp)
    		return 1;
    
    	len = sizeof(sin);
    	if ((res = recvfrom(fd, rtp->rawdata + AST_FRIENDLY_OFFSET, sizeof(rtp->rawdata) - AST_FRIENDLY_OFFSET, 0, (struct sockaddr *)&sin, &len)) < 0)
    		return 1;
    
    	header = (unsigned int *)(rtp->rawdata + AST_FRIENDLY_OFFSET);
    
    	/* Determine what this file descriptor is for */
    	if (rtp->s == fd)
    		is_rtp = 1;
    	else if (rtp->rtcp && rtp->rtcp->s == fd)
    		is_rtcp = 1;
    
    	/* If NAT support is turned on, then see if we need to change their address */
    	if (rtp->nat) {
    		/* If this is for RTP, check that - if it's for RTCP, check that */
    		if (is_rtp) {
    			if ((rtp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
    			    (rtp->them.sin_port != sin.sin_port)) {
    				rtp->them = sin;
    				rtp->rxseqno = 0;
    				ast_set_flag(rtp, FLAG_NAT_ACTIVE);
    				if (option_debug || rtpdebug)
    					ast_log(LOG_DEBUG, "P2P RTP NAT: Got audio from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->them.sin_addr), ntohs(rtp->them.sin_port));
    			}
    		} else if (is_rtcp) {
    			if ((rtp->rtcp->them.sin_addr.s_addr != sin.sin_addr.s_addr) ||
    			    (rtp->rtcp->them.sin_port != sin.sin_port)) {
    				rtp->rtcp->them = sin;
    				if (option_debug || rtpdebug)
    					ast_log(LOG_DEBUG, "P2P RTCP NAT: Got RTCP from other end. Now sending to address %s:%d\n", ast_inet_ntoa(rtp->rtcp->them.sin_addr), ntohs(rtp->rtcp->them.sin_port));
    			}
    		}
    	}
    
    	/* If this came from the RTP stream, write out via RTP - if it's RTCP, write out via RTCP */
    	if (is_rtp)
    		bridge_p2p_rtp_write(rtp, header, res, hdrlen);
    	else if (is_rtcp)
    		bridge_p2p_rtcp_write(rtp, header, res);
    
    	return 1;
    }
    
    /*! \brief Helper function to switch a channel and RTP stream into callback mode */
    static int p2p_callback_enable(struct ast_channel *chan, struct ast_rtp *rtp, int *fds, int **iod)
    {
    	/* If we need DTMF or we have no IO structure, then we can't do direct callback */
    	if (ast_test_flag(rtp, FLAG_P2P_NEED_DTMF) || !rtp->io)
    		return 0;
    
    
    	/* If the RTP structure is already in callback mode, remove it temporarily */
    	if (rtp->ioid) {
    		ast_io_remove(rtp->io, rtp->ioid);
    		rtp->ioid = NULL;
    	}
    
    
    	/* Steal the file descriptors from the channel and stash them away */
    	fds[0] = chan->fds[0];
    	fds[1] = chan->fds[1];
    	chan->fds[0] = -1;
    	chan->fds[1] = -1;
    
    	/* Now, fire up callback mode */
    	iod[0] = ast_io_add(rtp->io, fds[0], p2p_rtp_callback, AST_IO_IN, rtp);
    	iod[1] = ast_io_add(rtp->io, fds[1], p2p_rtp_callback, AST_IO_IN, rtp);
    
    	return 1;
    }
    
    /*! \brief Helper function to switch a channel and RTP stream out of callback mode */
    static int p2p_callback_disable(struct ast_channel *chan, struct ast_rtp *rtp, int *fds, int **iod)
    {
    	ast_channel_lock(chan);
    	/* Remove the callback from the IO context */
    	ast_io_remove(rtp->io, iod[0]);
    	ast_io_remove(rtp->io, iod[1]);
    	/* Restore file descriptors */
    	chan->fds[0] = fds[0];
    	chan->fds[1] = fds[1];
    	ast_channel_unlock(chan);
    
    	/* Restore callback mode if previously used */
    	if (ast_test_flag(rtp, FLAG_CALLBACK_MODE))
    	    rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
    
    /*! \brief Bridge loop for partial native bridge (packet2packet) */
    static enum ast_bridge_result bridge_p2p_loop(struct ast_channel *c0, struct ast_channel *c1, struct ast_rtp *p0, struct ast_rtp *p1, struct ast_rtp *vp0, struct ast_rtp *vp1, int timeoutms, int flags, struct ast_frame **fo, struct ast_channel **rc, void *pvt0, void *pvt1)
    {
    	struct ast_frame *fr = NULL;
    	struct ast_channel *who = NULL, *other = NULL, *cs[3] = {NULL, };
    
    	int p0_fds[2] = {-1, -1}, p1_fds[2] = {-1, -1};
    	int *p0_iod[2] = {NULL, }, *p1_iod[2] = {NULL, };
    	int p0_callback = 0, p1_callback = 0;
    	enum ast_bridge_result res = AST_BRIDGE_FAILED;
    
    
    	/* Okay, setup each RTP structure to do P2P forwarding */
    	ast_clear_flag(p0, FLAG_P2P_SENT_MARK);
    	p0->bridged = p1;
    	ast_clear_flag(p1, FLAG_P2P_SENT_MARK);
    	p1->bridged = p0;
    	if (vp0) {
    		ast_clear_flag(vp0, FLAG_P2P_SENT_MARK);
    		vp0->bridged = vp1;
    		ast_clear_flag(vp1, FLAG_P2P_SENT_MARK);
    		vp1->bridged = vp0;
    	}
    
    
    	/* Activate callback modes if possible */
    	p0_callback = p2p_callback_enable(c0, p0, &p0_fds[0], &p0_iod[0]);
    	p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]);
    
    
    	/* Now let go of the channel locks and be on our way */
    	ast_channel_unlock(c0);
    	ast_channel_unlock(c1);
    
    	/* Go into a loop forwarding frames until we don't need to anymore */
    	cs[0] = c0;
    	cs[1] = c1;
    	cs[2] = NULL;
    	for (;;) {
    		/* Check if anything 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");
    
    		}
    		/* Wait on a channel to feed us a frame */
    		if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
    
    			if (option_debug)
    				ast_log(LOG_NOTICE, "Ooh, empty read...\n");
    			if (ast_check_hangup(c0) || ast_check_hangup(c1))
    				break;
    			continue;
    		}
    		/* Read in frame from channel */
    		fr = ast_read(who);
    		other = (who == c0) ? c1 : c0;
    		/* Dependong on the frame we may need to break out of our bridge */
    		if (!fr || ((fr->frametype == AST_FRAME_DTMF) &&
    			    ((who == c0) && (flags & AST_BRIDGE_DTMF_CHANNEL_0)) |
    			    ((who == c1) && (flags & AST_BRIDGE_DTMF_CHANNEL_1)))) {
    			/* Record received frame and who */
    			*fo = fr;
    			*rc = who;
    			if (option_debug)
    				ast_log(LOG_DEBUG, "Oooh, got a %s\n", fr ? "digit" : "hangup");
    			/* Break out of the bridge */
    			p0->bridged = NULL;
    			p1->bridged = NULL;
    			if (vp0) {
    				vp0->bridged = NULL;
    				vp1->bridged = NULL;
    
    		} else if ((fr->frametype == AST_FRAME_CONTROL) && !(flags & AST_BRIDGE_IGNORE_SIGS)) {
    			if ((fr->subclass == AST_CONTROL_HOLD) ||
    			    (fr->subclass == AST_CONTROL_UNHOLD) ||
    			    (fr->subclass == AST_CONTROL_VIDUPDATE)) {
    
    				/* If we are going on hold, then break callback mode */
    				if (fr->subclass == AST_CONTROL_HOLD) {
    					if (p0_callback)
    						p0_callback = p2p_callback_disable(c0, p0, &p0_fds[0], &p0_iod[0]);
    					if (p1_callback)
    						p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &p1_iod[0]);
    				} else if (fr->subclass == AST_CONTROL_UNHOLD) {
    					/* If we are off hold, then go back to callback mode */
    					p0_callback = p2p_callback_enable(c0, p0, &p0_fds[0], &p0_iod[0]);
    					p1_callback = p2p_callback_enable(c1, p1, &p1_fds[0], &p1_iod[0]);
    				}
    
    				ast_indicate(other, fr->subclass);
    				ast_frfree(fr);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			} else {
    
    Mark Spencer's avatar
    Mark Spencer committed
    				*rc = who;
    
    				ast_log(LOG_DEBUG, "Got a FRAME_CONTROL (%d) frame on channel %s\n", fr->subclass, who->name);
    
    Mark Spencer's avatar
    Mark Spencer committed
    			}
    
    			/* If this is a DTMF, voice, or video frame write it to the other channel */
    			if ((fr->frametype == AST_FRAME_DTMF) ||
    			    (fr->frametype == AST_FRAME_VOICE) ||
    			    (fr->frametype == AST_FRAME_VIDEO)) {
    				ast_write(other, fr);
    
    		cs[2] = cs[0];
    		cs[0] = cs[1];
    		cs[1] = cs[2];
    	}
    
    	/* If we are totally avoiding the core, then restore our link to it */
    	if (p0_callback)
    		p0_callback = p2p_callback_disable(c0, p0, &p0_fds[0], &p0_iod[0]);
    	if (p1_callback)
    		p1_callback = p2p_callback_disable(c1, p1, &p1_fds[0], &p1_iod[0]);
    
    	return res;
    
    /*! \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_rtp *p0 = NULL, *p1 = NULL;		/* Audio RTP Channels */
    	struct ast_rtp *vp0 = NULL, *vp1 = NULL;	/* Video RTP channels */
    	struct ast_rtp_protocol *pr0 = NULL, *pr1 = NULL;
    	enum ast_rtp_get_result audio_p0_res = AST_RTP_GET_FAILED, video_p0_res = AST_RTP_GET_FAILED;
    	enum ast_rtp_get_result audio_p1_res = AST_RTP_GET_FAILED, video_p1_res = AST_RTP_GET_FAILED;
    	enum ast_bridge_result res = AST_BRIDGE_FAILED;
    	int codec0 = 0, codec1 = 0;
    	void *pvt0 = NULL, *pvt1 = NULL;
    
    	/* Lock channels */
    	ast_channel_lock(c0);
    	while(ast_channel_trylock(c1)) {
    		ast_channel_unlock(c0);
    		usleep(1);
    		ast_channel_lock(c0);
    	}
    
    	/* Find channel driver interfaces */
    	if (!(pr0 = get_proto(c0))) {
    		ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c0->name);
    		ast_channel_unlock(c0);
    		ast_channel_unlock(c1);
    		return AST_BRIDGE_FAILED;
    	}
    	if (!(pr1 = get_proto(c1))) {
    		ast_log(LOG_WARNING, "Can't find native functions for channel '%s'\n", c1->name);
    		ast_channel_unlock(c0);
    		ast_channel_unlock(c1);
    		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) */
    	audio_p0_res = pr0->get_rtp_info(c0, &p0);
    	video_p0_res = pr0->get_vrtp_info ? pr0->get_vrtp_info(c0, &vp0) : AST_RTP_GET_FAILED;
    	audio_p1_res = pr1->get_rtp_info(c1, &p1);
    	video_p1_res = pr1->get_vrtp_info ? pr1->get_vrtp_info(c1, &vp1) : AST_RTP_GET_FAILED;