[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