[asterisk-commits] file: branch file/bridging r90427 - in /team/file/bridging: apps/ main/

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Nov 30 20:32:36 CST 2007


Author: file
Date: Fri Nov 30 20:32:36 2007
New Revision: 90427

URL: http://svn.digium.com/view/asterisk?view=rev&rev=90427
Log:
Add the smart bridge operation function. If enabled the bridging core will switch between bridging technologies as channels are added/removed. This all happens in the background automagically.

Modified:
    team/file/bridging/apps/app_confbridge.c
    team/file/bridging/main/bridging.c

Modified: team/file/bridging/apps/app_confbridge.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/apps/app_confbridge.c?view=diff&rev=90427&r1=90426&r2=90427
==============================================================================
--- team/file/bridging/apps/app_confbridge.c (original)
+++ team/file/bridging/apps/app_confbridge.c Fri Nov 30 20:32:36 2007
@@ -88,7 +88,7 @@
 	if (!conference_bridge && dynamic && (conference_bridge = ast_calloc(1, sizeof(*conference_bridge)))) {
 		conference_bridge->dynamic = 1;
 		conference_bridge->name = name;
-		if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_MULTIMIX, 0, NULL))) {
+		if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART, NULL))) {
 			free(conference_bridge);
 			conference_bridge = NULL;
 		} else

Modified: team/file/bridging/main/bridging.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/main/bridging.c?view=diff&rev=90427&r1=90426&r2=90427
==============================================================================
--- team/file/bridging/main/bridging.c (original)
+++ team/file/bridging/main/bridging.c Fri Nov 30 20:32:36 2007
@@ -158,6 +158,10 @@
 				break;
 		}
 
+		/* Make sure we have a channel that is still present */
+		if (!bridge_channel)
+			continue;
+
 		/* Try to read a frame in, if this fails it means they hungup */
 		if (!(frame = (bridge_channel->muted ? ast_read_noaudio(winner) : ast_read(winner))) || (frame->frametype == AST_FRAME_CONTROL && frame->subclass == AST_CONTROL_HANGUP)) {
 			/* Switch the bridged channel state to end and signal it's thread */
@@ -345,6 +349,165 @@
 	return 0;
 }
 
+/*! \brief Function to make a bridged channel compatible with a bridge */
+static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+	int formats[2] = {bridge_channel->chan->readformat, bridge_channel->chan->writeformat};
+
+	/* Are the formats currently in use something ths bridge can handle? */
+	if (!(bridge->technology->formats & bridge_channel->chan->readformat)) {
+		int best_format = ast_best_codec(bridge->technology->formats);
+		
+                /* Read format is a no go... */
+		if (option_debug) {
+			char codec_buf[512];
+			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats);
+			ast_log(LOG_DEBUG, "Bridge technology %s wants to read any of formats %s(%d) but channel has %s(%d)\n", bridge->technology->name, codec_buf, bridge->technology->formats, ast_getformatname(formats[0]), formats[0]);
+		}
+                /* Switch read format to the best one chosen */
+		if (ast_set_read_format(bridge_channel->chan, best_format)) {
+			ast_log(LOG_WARNING, "Failed to set channel %s to read format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+			return -1;
+		}
+		ast_debug(1, "Bridge %p put channel %s into read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+	} else
+		ast_debug(1, "Bridge %p is happy that channel %s already has read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[0]), formats[0]);
+	
+	if (!(bridge->technology->formats & formats[1])) {
+		int best_format = ast_best_codec(bridge->technology->formats);
+		
+                /* Write format is a no go... */
+		if (option_debug) {
+			char codec_buf[512];
+			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats);
+			ast_log(LOG_DEBUG, "Bridge technology %s wants to write any of formats %s(%d) but channel has %s(%d)\n", bridge->technology->name, codec_buf, bridge->technology->formats, ast_getformatname(formats[1]), formats[1]);
+		}
+                /* Switch write format to the best one chosen */
+		if (ast_set_write_format(bridge_channel->chan, best_format)) {
+			ast_log(LOG_WARNING, "Failed to set channel %s to write format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+			return -1;
+		}
+		ast_debug(1, "Bridge %p put channel %s into write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+	} else
+		ast_debug(1, "Bridge %p is happy that channel %s already has write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[1]), formats[1]);
+	
+	return 0;
+}
+
+/*! \brief Perform the smart bridge operation. Basically sees if a new bridge technology should be used instead of the current one. */
+static int smart_bridge_operation(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, int count)
+{
+	int new_capabilities = 0;
+	struct ast_bridge_technology *new_technology = NULL, *old_technology = bridge->technology;
+	pthread_t thread = bridge->thread;
+	struct ast_bridge temp_bridge = {
+		.technology = bridge->technology,
+		.bridge_pvt = bridge->bridge_pvt,
+	};
+	struct ast_bridge_channel *bridge_channel2 = NULL;
+
+	/* Don't bother optimizing if the bridge itself is being shutdown */
+	if (bridge->thread == AST_PTHREADT_STOP) {
+		ast_debug(1, "Bridge %p is shutting down, thus we don't need to perform the smart bridge operation.\n", bridge);
+		return -1;
+	}
+
+	/* Based on current feature determine whether we want to change bridge technologies or not */
+	if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
+		/* If the channel count is 2 or less it can stay like this */
+		if (count <= 2) {
+			ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, count, bridge->technology->name);
+			return 0;
+		}
+		/* Uh oh... we need to move to multiparty mixing for shizzle */
+		new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
+	} else if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
+		/* If the channel count is greater than 2 it can stay like this */
+		if (count > 2) {
+			ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, count, bridge->technology->name);
+			return 0;
+		}
+		/* Yay we can drop down to 1TO1 mixing */
+		new_capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX;
+	}
+
+	/* Now that the above code has determined we need to change to a new technology let's find one */
+	if (!(new_technology = find_best_technology(new_capabilities))) {
+		ast_debug(1, "Smart bridge operation was unable to find new bridge technology with capabilities %d to satisfy bridge %p\n", new_capabilities, bridge);
+		return -1;
+	}
+
+	ast_debug(1, "Performing smart bridge operation on bridge %p, moving from bridge technology %s to %s\n", bridge, old_technology->name, new_technology->name);
+
+	/* We start off by getting the thread servicing the current technology to stop and give control to us */
+	bridge->thread = AST_PTHREADT_STOP;
+	pthread_kill(thread, SIGURG);
+	
+	/* Once we let go of the lock the bridge thread should quit and control should be given to us */
+	ast_mutex_unlock(&bridge->lock);
+
+	/* Of course we have to wait for the thread to exit, that can take some time */
+	ast_debug(1, "Waiting for current bridge thread on %p to exit and control to return to us\n", bridge);
+	pthread_join(thread, NULL);
+	
+	/* Now that it is back to us we have to reacquire the lock so we can muck with things :D */
+	ast_mutex_lock(&bridge->lock);
+	ast_debug(1, "Control of bridge %p now belongs to the smart bridge operation\n", bridge);
+
+	/* Since we have a temporary bridge structure up above we can make the current one clean */
+	bridge->bridge_pvt = NULL;
+	bridge->thread = AST_PTHREADT_NULL;
+
+	/* And setup the new bridge technology now */
+	bridge->technology = new_technology;
+	if (bridge->technology->create) {
+		ast_debug(1, "Giving bridge technology %s the bridge structure %p to setup\n", bridge->technology->name, bridge);
+		if (bridge->technology->create(bridge))
+			ast_debug(1, "Bridge technology %s failed to setup bridge structure %p\n", bridge->technology->name, bridge);
+	}
+
+	/* Our next step is to depart all the channels from one bridge technology and join them with the other */
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel2, list) {
+		/* Skip over channel that initiated the smart bridge operation if present */
+		if (bridge_channel && bridge_channel2 == bridge_channel)
+			continue;
+		/* First we part them from the old technology */
+		if (old_technology->leave) {
+			ast_debug(1, "Giving bridge technology %s notification that %p is leaving bridge %p (really %p)\n", old_technology->name, bridge_channel2, &temp_bridge, bridge);
+			if (old_technology->leave(&temp_bridge, bridge_channel2))
+				ast_debug(1, "Bridge technology %s failed to allow %p (really %p) to leave bridge %p\n", old_technology->name, bridge_channel2, &temp_bridge, bridge);
+		}
+		/* Second we have to make the channel compatible with the bridge */
+		bridge_make_compatible(bridge, bridge_channel2);
+		/* Third we join them to the new one */
+		if (new_technology->join) {
+			ast_debug(1, "Giving bridge technology %s notification that %p is joining bridge %p\n", new_technology->name, bridge_channel2, bridge);
+			if (new_technology->join(bridge, bridge_channel2))
+				ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n", new_technology->name, bridge_channel2, bridge);
+		}
+	}
+
+	/* Now that all the channels are gone the old bridge technology destruction can be finalized */
+	if (old_technology->destroy) {
+		ast_debug(1, "Giving bridge technology %s the bridge structure %p (really %p) to destroy\n", old_technology->name, &temp_bridge, bridge);
+		if (old_technology->destroy(&temp_bridge))
+			ast_debug(1, "Bridge technology %s failed to destroy bridge structure %p (really %p)... some memory may have leaked\n", old_technology->name, &temp_bridge, bridge);
+	}
+
+	/* If the new technology needs a thread and we were called when a channel hung up start one up */
+	if (!bridge_channel && (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
+		if ((bridge->thread == AST_PTHREADT_NULL) && (ast_pthread_create(&bridge->thread, NULL, bridge_thread, bridge))) {
+			ast_debug(1, "Failed to create bridge thread for %p\n", bridge);
+			return -1;
+		} else {
+			ast_debug(1, "Poked thread servicing bridge %p\n", bridge);
+			pthread_kill(bridge->thread, SIGURG);
+		}
+	}
+
+	return 0;
+}
+
 /*! \brief Run in a multithreaded model. Each joined channel does writing/reading in their own thread. */
 static enum ast_bridge_channel_state bridge_channel_join_multithreaded(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
@@ -360,9 +523,6 @@
 		}
 	}
 
-	/* Before we go into the loop drop our lock on the bridge, we don't care about it anymore really */
-	ast_mutex_unlock(&bridge->lock);
-
 	/* Go into a loop waiting for frames from the channel, or the associated file descriptors to trip */
 	for (;;) {
 		struct ast_channel *chan = NULL;
@@ -375,8 +535,12 @@
 			break;
 
 		/* Wait for data on any of the file descriptors */
+		ast_mutex_unlock(&bridge->lock);
 		if (!bridge_channel->suspended)
 			chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &ms);
+		else
+			usleep(1);
+		ast_mutex_lock(&bridge->lock);
 
 		if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT)
 			break;
@@ -402,9 +566,6 @@
 		}
 	}
 
-	/* Reclaim our bridge lock */
-	ast_mutex_lock(&bridge->lock);
-
 	return bridge_channel->state;
 }
 
@@ -443,43 +604,6 @@
 {
 	int formats[2] = {bridge_channel->chan->readformat, bridge_channel->chan->writeformat};
 
-	/* Are the formats currently in use something this bridge can handle? */
-	if (!(bridge->technology->formats & formats[0])) {
-		int best_format = ast_best_codec(bridge->technology->formats);
-
-		/* Read format is a no go... */
-		if (option_debug) {
-			char codec_buf[512];
-			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats);
-			ast_log(LOG_DEBUG, "Bridge technology %s wants to read any of formats %s(%d) but channel has %s(%d)\n", bridge->technology->name, codec_buf, bridge->technology->formats, ast_getformatname(formats[0]), formats[0]);
-		}
-		/* Switch read format to the best one chosen */
-		if (ast_set_read_format(bridge_channel->chan, best_format)) {
-			ast_log(LOG_WARNING, "Failed to set channel %s to read format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(best_format), best_format);
-			return -1;
-		}
-		ast_debug(1, "Bridge %p put channel %s into read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
-	} else
-		ast_debug(1, "Bridge %p is happy that channel %s already has read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[0]), formats[0]);
-
-	if (!(bridge->technology->formats & formats[1])) {
-		int best_format = ast_best_codec(bridge->technology->formats);
-
-		/* Write format is a no go... */
-		if (option_debug) {
-			char codec_buf[512];
-			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats);
-			ast_log(LOG_DEBUG, "Bridge technology %s wants to write any of formats %s(%d) but channel has %s(%d)\n", bridge->technology->name, codec_buf, bridge->technology->formats, ast_getformatname(formats[1]), formats[1]);
-		}
-		/* Switch write format to the best one chosen */
-		if (ast_set_write_format(bridge_channel->chan, best_format)) {
-			ast_log(LOG_WARNING, "Failed to set channel %s to write format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(best_format), best_format);
-			return -1;
-		}
-		ast_debug(1, "Bridge %p put channel %s into write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
-	} else
-		ast_debug(1, "Bridge %p is happy that channel %s already has write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[1]), formats[1]);
-
 	/* Before we actually become part of this bridge make sure we are in the signalling wait state */
 	bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
 
@@ -488,6 +612,10 @@
 
 	/* Notify the bridge thread that a new bridged channel is part of the bridge, this will cause it to rebuild the bridge array */
 	bridge->rebuild = 1;
+
+	/* If smart bridging is enabled perform the operation to see if the underlying bridge technology needs to change */
+	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART))
+		smart_bridge_operation(bridge, bridge_channel, bridge->num);
 
 	/* Of course if this is the first channel we actually have to create the bridge thread if the technology wants it */
 	if ((bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
@@ -495,10 +623,14 @@
 			ast_debug(1, "Failed to create bridge thread for %p\n", bridge);
 			return -1;
 		} else {
+			ast_debug(1, "Poked thread servicing bridge %p\n", bridge);
 			/* Poke the bridge out of it's poll if there */
 			pthread_kill(bridge->thread, SIGURG);
 		}
 	}
+
+	/* Make sure that the channel's formats are compatible before joining */
+	bridge_make_compatible(bridge, bridge_channel);
 
 	/* If the bridge technology wants notification that this channel is joining the bridge, give it it */
 	if (bridge->technology->join) {
@@ -525,6 +657,10 @@
 	/* Remove ourselves from the bridge */
 	AST_LIST_REMOVE(&bridge->channels, bridge_channel, list);
 
+	/* And for my last trick... perform the smart bridge operation yet again */
+	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART))
+		smart_bridge_operation(bridge, NULL, bridge->num-1);
+
 	/* Restore original formats if need be */
 	if (bridge_channel->chan->readformat != formats[0]) {
 		ast_debug(1, "Bridge is returning %p to read format %s(%d)\n", bridge_channel, ast_getformatname(formats[0]), formats[0]);




More information about the asterisk-commits mailing list