[svn-commits] file: trunk r41302 - /trunk/main/rtp.c

svn-commits at lists.digium.com svn-commits at lists.digium.com
Tue Aug 29 18:22:46 MST 2006


Author: file
Date: Tue Aug 29 20:22:46 2006
New Revision: 41302

URL: http://svn.digium.com/view/asterisk?rev=41302&view=rev
Log:
This is the last round of RTP bridge optimizations. Basically it introduces a way that under a straight bridge (ie: no transcoding and no DTMF detection) the core is not touched at all and no frames pass through (not even null frames). This is accomplished by stealing the file descriptors from the channel and using the provided IO context with a custom callback. When a channel is placed on hold this bridge is broken so audio can flow from the core to the other side. When a channel is off hold this bridge is re-established.

Modified:
    trunk/main/rtp.c

Modified: trunk/main/rtp.c
URL: http://svn.digium.com/view/asterisk/trunk/main/rtp.c?rev=41302&r1=41301&r2=41302&view=diff
==============================================================================
--- trunk/main/rtp.c (original)
+++ trunk/main/rtp.c Tue Aug 29 20:22:46 2006
@@ -164,6 +164,7 @@
 static int ast_rtcp_write_sr(void *data);
 static int ast_rtcp_write_rr(void *data);
 static unsigned int ast_rtcp_calc_interval(struct ast_rtp *rtp);
+static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader, int len);
 
 #define FLAG_3389_WARNING		(1 << 0)
 #define FLAG_NAT_ACTIVE			(3 << 1)
@@ -777,6 +778,11 @@
 				ast_log(LOG_DEBUG, "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 we are P2P bridged to another RTP stream, send it directly over */
+	if (rtp->bridged && !bridge_p2p_rtcp_write(rtp, rtcpheader, res))
+		return &ast_null_frame;
+
 	if (option_debug)
 		ast_log(LOG_DEBUG, "Got RTCP report of %d bytes\n", res);
 
@@ -929,8 +935,31 @@
 		rtp->rtcp->minrxjitter = rtp->rxjitter;
 }
 
-/*! \brief Perform a Packet2Packet write */
-static int bridge_p2p_write(struct ast_rtp *rtp, unsigned int *rtpheader, int len, int hdrlen)
+/*! \brief Perform a Packet2Packet RTCP write */
+static int bridge_p2p_rtcp_write(struct ast_rtp *rtp, unsigned int *rtcpheader, int len)
+{
+	struct ast_rtp *bridged = rtp->bridged;
+	int res = 0;
+
+	/* If RTCP is not present on the bridged RTP session, then ignore this */
+	if (!bridged->rtcp)
+		return 0;
+
+	/* Send the data out */
+	res = sendto(bridged->rtcp->s, (void *)rtcpheader, len, 0, (struct sockaddr *)&bridged->rtcp->them, sizeof(bridged->rtcp->them));
+	if (res < 0) {
+		if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE)))
+			ast_log(LOG_DEBUG, "RTCP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->rtcp->them.sin_addr), ntohs(bridged->rtcp->them.sin_port), strerror(errno));
+		else if ((((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug)) && (option_debug || rtpdebug))
+			ast_log(LOG_DEBUG, "RTCP NAT: Can't write RTCP to private address %s:%d, waiting for other end to send first...\n", ast_inet_ntoa(bridged->rtcp->them.sin_addr), ntohs(bridged->rtcp->them.sin_port));
+	} else if (rtp_debug_test_addr(&bridged->rtcp->them))
+		ast_verbose("Sent RTCP P2P packet to %s:%d (len %-6.6u)\n", ast_inet_ntoa(bridged->rtcp->them.sin_addr), ntohs(bridged->rtcp->them.sin_port), len);
+
+	return 0;
+}
+
+/*! \brief Perform a Packet2Packet RTP write */
+static int bridge_p2p_rtp_write(struct ast_rtp *rtp, unsigned int *rtpheader, int len, int hdrlen)
 {
 	struct ast_rtp *bridged = rtp->bridged;
 	int res = 0, payload = 0, bridged_payload = 0, version, padding, mark, ext;
@@ -965,23 +994,19 @@
 	/* Reconstruct part of the packet */
 	rtpheader[0] = htonl((version << 30) | (mark << 23) | (bridged_payload << 16) | (seqno));
 
-	if (bridged->them.sin_port && bridged->them.sin_addr.s_addr) {
-		res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them));
-		if (res < 0) {
-			if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
-				ast_log(LOG_DEBUG, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno));
-			} else if ((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) {
-				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(bridged->them.sin_addr), ntohs(bridged->them.sin_port));
-				ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN);
-			}
-			return -1;
-		} else {
-			if (rtp_debug_test_addr(&bridged->them))
-				ast_verbose("Sent RTP P2P packet to %s:%d (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen);
-			return 0;
-		}
-	}
+	/* Send the packet back out */
+	res = sendto(bridged->s, (void *)rtpheader, len, 0, (struct sockaddr *)&bridged->them, sizeof(bridged->them));
+	if (res < 0) {
+		if (!bridged->nat || (bridged->nat && (ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_ACTIVE))) {
+			ast_log(LOG_DEBUG, "RTP Transmission error of packet to %s:%d: %s\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), strerror(errno));
+		} else if (((ast_test_flag(bridged, FLAG_NAT_ACTIVE) == FLAG_NAT_INACTIVE) || rtpdebug) && !ast_test_flag(bridged, FLAG_NAT_INACTIVE_NOWARN)) {
+			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(bridged->them.sin_addr), ntohs(bridged->them.sin_port));
+			ast_set_flag(bridged, FLAG_NAT_INACTIVE_NOWARN);
+		}
+		return -1;
+	} else if (rtp_debug_test_addr(&bridged->them))
+			ast_verbose("Sent RTP P2P packet to %s:%d (type %-2.2d, len %-6.6u)\n", ast_inet_ntoa(bridged->them.sin_addr), ntohs(bridged->them.sin_port), bridged_payload, len - hdrlen);
 
 	return -1;
 }
@@ -1024,11 +1049,6 @@
 		return &ast_null_frame;
 	}
 
-	/* If we are P2P bridged to another channel, and the write is a success - then return a null frame and not the actual data */
-	if (rtp->bridged && !bridge_p2p_write(rtp, rtpheader, res, hdrlen)) {
-		return &ast_null_frame;
-	}
-
 	/* Get fields */
 	seqno = ntohl(rtpheader[0]);
 
@@ -1063,7 +1083,7 @@
 	}
 
 	/* If we are bridged to another RTP stream, send direct */
-	if (rtp->bridged && !bridge_p2p_write(rtp, rtpheader, res, hdrlen))
+	if (rtp->bridged && !bridge_p2p_rtp_write(rtp, rtpheader, res, hdrlen))
 		return &ast_null_frame;
 
 	if (version != 2)
@@ -1769,12 +1789,10 @@
 			return NULL;
 		}
 	}
-	if (io && sched && callbackmode) {
-		/* Operate this one in a callback mode */
-		rtp->sched = sched;
-		rtp->io = io;
+	rtp->sched = sched;
+	rtp->io = io;
+	if (callbackmode)
 		rtp->ioid = ast_io_add(rtp->io, rtp->s, rtpread, AST_IO_IN, rtp);
-	}
 	ast_rtp_pt_default(rtp);
 	return rtp;
 }
@@ -2355,7 +2373,7 @@
 		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) {
+			} 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));
@@ -2704,11 +2722,105 @@
 	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;
+
+	/* 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);
+	return 0;
+}
+
 /*! \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);
@@ -2722,6 +2834,10 @@
 		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);
@@ -2736,12 +2852,15 @@
 		    (c1->tech_pvt != pvt1) ||
 		    (c0->masq || c0->masqr || c1->masq || c1->masqr)) {
 			ast_log(LOG_DEBUG, "Oooh, something is weird, backing out\n");
-			return AST_BRIDGE_RETRY;
+			res = AST_BRIDGE_RETRY;
+			break;
 		}
 		/* Wait on a channel to feed us a frame */
 		if (!(who = ast_waitfor_n(cs, 2, &timeoutms))) {
-			if (!timeoutms)
-				return AST_BRIDGE_RETRY;
+			if (!timeoutms) {
+				res = AST_BRIDGE_RETRY;
+				break;
+			}
 			if (option_debug)
 				ast_log(LOG_NOTICE, "Ooh, empty read...\n");
 			if (ast_check_hangup(c0) || ast_check_hangup(c1))
@@ -2767,18 +2886,31 @@
 				vp0->bridged = NULL;
 				vp1->bridged = NULL;
 			}
-			return AST_BRIDGE_COMPLETE;
+			res = AST_BRIDGE_COMPLETE;
+			break;
 		} 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);
 			} 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;
+				res = AST_BRIDGE_COMPLETE;
+				break;
 			}
 		} else {
 			/* If this is a DTMF, voice, or video frame write it to the other channel */
@@ -2795,7 +2927,13 @@
 		cs[1] = cs[2];
 	}
 
-	return AST_BRIDGE_FAILED;
+	/* 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



More information about the svn-commits mailing list