[asterisk-commits] rmudgett: branch group/bridge_construction r386589 - in /team/group/bridge_co...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu Apr 25 17:03:46 CDT 2013


Author: rmudgett
Date: Thu Apr 25 17:03:36 2013
New Revision: 386589

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386589
Log:
Make local channel optimization also use the swap method.

Added the additional way to optimize out local channels in more
circumstances.  Swap the Local;2 channel in BridgeB with the only peer
channel (A) in BridgeA.  This optimization strategy is needed for the
parking and conference bridges to keep bridge channel information
associated with the same call.

A -- BridgeA -- Local;1-Local;2 -- BridgeB -- B

* Extracted the ast_bridge_channel_peer() routine from the simple_bridge
so local channel swap optimization can utilize it as well.

* Implemented ast_bridge_move() as a side benefit to the swap
optimization.

* Refactored ast_bridge_local_optimized_out() to simplify its structure
because the swap optimization needs to be checked before the merge
optimization.

(issue ASTERISK-21058)
Review: https://reviewboard.asterisk.org/r/2474/

Modified:
    team/group/bridge_construction/apps/app_bridgewait.c
    team/group/bridge_construction/apps/confbridge/conf_config_parser.c
    team/group/bridge_construction/bridges/bridge_simple.c
    team/group/bridge_construction/include/asterisk/bridging.h
    team/group/bridge_construction/include/asterisk/bridging_features.h
    team/group/bridge_construction/main/bridging.c

Modified: team/group/bridge_construction/apps/app_bridgewait.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/app_bridgewait.c?view=diff&rev=386589&r1=386588&r2=386589
==============================================================================
--- team/group/bridge_construction/apps/app_bridgewait.c (original)
+++ team/group/bridge_construction/apps/app_bridgewait.c Thu Apr 25 17:03:36 2013
@@ -196,7 +196,8 @@
 	ast_mutex_lock(&bridgewait_lock);
 	if (!holding_bridge) {
 		holding_bridge = ast_bridge_base_new(AST_BRIDGE_CAPABILITY_HOLDING,
-			AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM);
+			AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
+				| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM);
 	}
 	ast_mutex_unlock(&bridgewait_lock);
 	if (!holding_bridge) {

Modified: team/group/bridge_construction/apps/confbridge/conf_config_parser.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/confbridge/conf_config_parser.c?view=diff&rev=386589&r1=386588&r2=386589
==============================================================================
--- team/group/bridge_construction/apps/confbridge/conf_config_parser.c (original)
+++ team/group/bridge_construction/apps/confbridge/conf_config_parser.c Thu Apr 25 17:03:36 2013
@@ -1915,6 +1915,7 @@
 	/* This option should only be used with the CONFBRIDGE dialplan function */
 	aco_option_register_custom(&cfg_info, "template", ACO_EXACT, user_types, NULL, user_template_handler, 0);
 
+/* BUGBUG need a user supplied bridge merge_priority to merge ConfBridges (default = 1, range 1-INT_MAX) */
 	/* Bridge options */
 	aco_option_register(&cfg_info, "type", ACO_EXACT, bridge_types, NULL, OPT_NOOP_T, 0, 0);
 	aco_option_register(&cfg_info, "jitterbuffer", ACO_EXACT, bridge_types, "no", OPT_BOOLFLAG_T, 1, FLDSET(struct bridge_profile, flags), USER_OPT_JITTERBUFFER);

Modified: team/group/bridge_construction/bridges/bridge_simple.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/bridges/bridge_simple.c?view=diff&rev=386589&r1=386588&r2=386589
==============================================================================
--- team/group/bridge_construction/bridges/bridge_simple.c (original)
+++ team/group/bridge_construction/bridges/bridge_simple.c Thu Apr 25 17:03:36 2013
@@ -70,15 +70,10 @@
 {
 	struct ast_bridge_channel *other;
 
-	/* If this is the only channel in this bridge then immediately exit */
-	if (AST_LIST_FIRST(&bridge->channels) == AST_LIST_LAST(&bridge->channels)) {
+	/* Find the channel we actually want to write to */
+	other = ast_bridge_channel_peer(bridge_channel);
+	if (!other) {
 		return -1;
-	}
-
-	/* Find the channel we actually want to write to */
-	other = AST_LIST_FIRST(&bridge->channels);
-	if (other == bridge_channel) {
-		other = AST_LIST_LAST(&bridge->channels);
 	}
 
 	/* The bridging core takes care of freeing the passed in frame. */

Modified: team/group/bridge_construction/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging.h?view=diff&rev=386589&r1=386588&r2=386589
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Thu Apr 25 17:03:36 2013
@@ -349,6 +349,17 @@
 typedef void (*ast_bridge_notify_masquerade_fn)(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel);
 
 /*!
+ * \brief Get the merge priority of this bridge.
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \note On entry, self is already locked.
+ *
+ * \return Merge priority
+ */
+typedef int (*ast_bridge_merge_priority_fn)(struct ast_bridge *self);
+
+/*!
  * \brief Bridge virtual methods table definition.
  *
  * \note Any changes to this struct must be reflected in
@@ -367,6 +378,8 @@
 	ast_bridge_pull_channel_fn pull;
 	/*! Notify the bridge of a masquerade with the channel. */
 	ast_bridge_notify_masquerade_fn notify_masquerade;
+	/*! Get the bridge merge priority. */
+	ast_bridge_merge_priority_fn get_merge_priority;
 };
 
 /*!
@@ -766,6 +779,21 @@
 int ast_bridge_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge);
 
 /*!
+ * \brief Move a channel from one bridge to another.
+ * \since 12.0.0
+ *
+ * \param dst_bridge Destination bridge of bridge channel move.
+ * \param src_bridge Source bridge of bridge channel move.
+ * \param chan Channel to move.
+ * \param swap Channel to replace in dst_bridge.
+ * \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_channel *chan, struct ast_channel *swap, int attempt_recovery);
+
+/*!
  * \brief Adjust the bridge merge inhibit request count.
  * \since 12.0.0
  *
@@ -1163,6 +1191,21 @@
 void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel);
 
 /*!
+ * \brief Get the peer bridge channel of a two party bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to get the peer of.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \note This is an internal bridge function.
+ *
+ * \retval peer on success.
+ * \retval NULL no peer channel.
+ */
+struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel);
+
+/*!
  * \brief Adjust the internal mixing sample rate of a bridge
  * used during multimix mode.
  *

Modified: team/group/bridge_construction/include/asterisk/bridging_features.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging_features.h?view=diff&rev=386589&r1=386588&r2=386589
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_features.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_features.h Thu Apr 25 17:03:36 2013
@@ -40,8 +40,12 @@
 	AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM = (1 << 3),
 	/*! Bridge channels cannot be merged to this bridge. */
 	AST_BRIDGE_FLAG_MERGE_INHIBIT_TO = (1 << 4),
+	/*! Bridge channels cannot be local channel swap optimized from this bridge. */
+	AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM = (1 << 5),
+	/*! Bridge channels cannot be local channel swap optimized to this bridge. */
+	AST_BRIDGE_FLAG_SWAP_INHIBIT_TO = (1 << 6),
 	/*! Bridge channels can be moved to another bridge only by masquerade (ConfBridge) */
-	AST_BRIDGE_FLAG_MASQUERADE_ONLY = (1 << 5),
+	AST_BRIDGE_FLAG_MASQUERADE_ONLY = (1 << 7),
 };
 
 /*! \brief Flags used for per bridge channel features */

Modified: team/group/bridge_construction/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/bridging.c?view=diff&rev=386589&r1=386588&r2=386589
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Thu Apr 25 17:03:36 2013
@@ -1214,7 +1214,8 @@
 		|| !v_table->dissolving
 		|| !v_table->push
 		|| !v_table->pull
-		|| !v_table->notify_masquerade) {
+		|| !v_table->notify_masquerade
+		|| !v_table->get_merge_priority) {
 		ast_log(LOG_ERROR, "Virtual method table for bridge class %s not complete.\n",
 			v_table && v_table->name ? v_table->name : "<unknown>");
 		ast_assert(0);
@@ -1360,6 +1361,22 @@
 	self->reconfigured = 1;
 }
 
+/*!
+ * \internal
+ * \brief Get the merge priority of this bridge.
+ * \since 12.0.0
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \note On entry, self is already locked.
+ *
+ * \return Merge priority
+ */
+static int bridge_base_get_merge_priority(struct ast_bridge *self)
+{
+	return 0;
+}
+
 struct ast_bridge_methods ast_bridge_base_v_table = {
 	.name = "base",
 	.destroy = bridge_base_destroy,
@@ -1367,6 +1384,7 @@
 	.push = bridge_base_push,
 	.pull = bridge_base_pull,
 	.notify_masquerade = bridge_base_notify_masquerade,
+	.get_merge_priority = bridge_base_get_merge_priority,
 };
 
 struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags)
@@ -3131,6 +3149,114 @@
 
 /*!
  * \internal
+ * \brief Move a bridge channel from one bridge to another.
+ * \since 12.0.0
+ *
+ * \param dst_bridge Destination bridge of bridge channel move.
+ * \param bridge_channel Channel moving from one bridge to another.
+ * \param attempt_recovery TRUE if failure attempts to push channel back into original bridge.
+ *
+ * \note The dst_bridge and bridge_channel->bridge are assumed already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+static int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery)
+{
+	struct ast_bridge *orig_bridge;
+	int was_in_bridge;
+	int res = 0;
+
+/* BUGBUG need bridge move stasis event and a success/fail event. */
+	if (bridge_channel->swap) {
+		ast_debug(1, "Moving %p(%s) into bridge %s swapping with %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan), dst_bridge->uniqueid,
+			ast_channel_name(bridge_channel->swap));
+	} else {
+		ast_debug(1, "Moving %p(%s) into bridge %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan), dst_bridge->uniqueid);
+	}
+
+	orig_bridge = bridge_channel->bridge;
+	was_in_bridge = bridge_channel->in_bridge;
+
+	bridge_channel_pull(bridge_channel);
+
+	/* Point to new bridge.*/
+	ao2_ref(dst_bridge, +1);
+	ast_bridge_channel_lock(bridge_channel);
+	bridge_channel->bridge = dst_bridge;
+	ast_bridge_channel_unlock(bridge_channel);
+
+	if (bridge_channel_push(bridge_channel)) {
+		/* Try to put the channel back into the original bridge. */
+		if (attempt_recovery && was_in_bridge) {
+			/* Point back to original bridge. */
+			ao2_ref(orig_bridge, +1);
+			ast_bridge_channel_lock(bridge_channel);
+			bridge_channel->bridge = orig_bridge;
+			ast_bridge_channel_unlock(bridge_channel);
+			ao2_ref(dst_bridge, -1);
+
+			if (bridge_channel_push(bridge_channel)) {
+				ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+			}
+		} else {
+			ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+		}
+		res = -1;
+	}
+
+	bridge_reconfigured(dst_bridge);
+	bridge_reconfigured(orig_bridge);
+	ao2_ref(orig_bridge, -1);
+	return res;
+}
+
+int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_channel *chan, struct ast_channel *swap, int attempt_recovery)
+{
+	struct ast_bridge_channel *bridge_channel;
+	int res = -1;
+
+	ast_bridge_lock_both(dst_bridge, src_bridge);
+
+	bridge_channel = find_bridge_channel(src_bridge, chan);
+	if (!bridge_channel) {
+		ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, channel not in bridge.\n",
+			ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid);
+	} else if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, channel leaving bridge.\n",
+			ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid);
+	} else if (dst_bridge->dissolved || src_bridge->dissolved) {
+		ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, one or both bridges are dissolved.\n",
+			ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid);
+	} else {
+		bridge_channel->swap = swap;
+		res = bridge_move_do(dst_bridge, bridge_channel, attempt_recovery);
+	}
+
+	ast_bridge_unlock(src_bridge);
+	ast_bridge_unlock(dst_bridge);
+	return res;
+}
+
+struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	struct ast_bridge_channel *other = NULL;
+
+	if (bridge_channel->in_bridge && bridge->num_channels == 2) {
+		other = AST_LIST_FIRST(&bridge->channels);
+		if (other == bridge_channel) {
+			other = AST_LIST_LAST(&bridge->channels);
+		}
+	}
+
+	return other;
+}
+
+/*!
+ * \internal
  * \brief Lock the local channel stack for chan and prequalify it.
  * \since 12.0.0
  *
@@ -3163,6 +3289,7 @@
 	if (bridge->inhibit_merge
 		|| bridge->dissolved
 		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
+		|| !bridge_channel->in_bridge
 		|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
@@ -3209,6 +3336,7 @@
 	if (bridge->inhibit_merge
 		|| bridge->dissolved
 		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
+		|| !bridge_channel->in_bridge
 		|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
@@ -3218,76 +3346,207 @@
 	return bridge;
 }
 
-int ast_bridge_local_optimized_out(struct ast_channel *chan, struct ast_channel *peer)
-{
-	struct ast_bridge *chan_bridge;
-	struct ast_bridge *peer_bridge;
-	struct ast_bridge_channel *chan_bridge_channel;
-	struct ast_bridge_channel *peer_bridge_channel;
+/*!
+ * \internal
+ * \brief Check and attempt to swap optimize out the local channels.
+ * \since 12.0.0
+ *
+ * \param chan_bridge
+ * \param chan_bridge_channel
+ * \param peer_bridge
+ * \param peer_bridge_channel
+ *
+ * \retval 1 if local channels failed to optimize out.
+ * \retval 0 if local channels were not optimized out.
+ * \retval -1 if local channels were optimized out.
+ */
+static int check_swap_optimize_out(struct ast_bridge *chan_bridge,
+	struct ast_bridge_channel *chan_bridge_channel, struct ast_bridge *peer_bridge,
+	struct ast_bridge_channel *peer_bridge_channel)
+{
+	struct ast_bridge *dst_bridge = NULL;
+	struct ast_bridge *src_bridge = NULL;
+	struct ast_bridge_channel *dst_bridge_channel = NULL;
+	struct ast_bridge_channel *src_bridge_channel = NULL;
+	int peer_priority;
+	int chan_priority;
 	int res = 0;
 
-	chan_bridge = optimize_lock_chan_stack(chan);
-	if (!chan_bridge) {
-		return res;
-	}
-	chan_bridge_channel = ast_channel_internal_bridge_channel(chan);
-
-	peer_bridge = optimize_lock_peer_stack(peer);
-	if (peer_bridge) {
-		struct ast_bridge *dst_bridge = NULL;
-		struct ast_bridge *src_bridge = NULL;
-
-		peer_bridge_channel = ast_channel_internal_bridge_channel(peer);
-
-		if (!ast_test_flag(&chan_bridge->feature_flags,
-				AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)
-			&& !ast_test_flag(&peer_bridge->feature_flags,
-				AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)) {
-/* BUGBUG need to have a merge priority bridge class method to help determine which way to optimize merge. */
-			/* Can merge either way. */
+	if (!ast_test_flag(&chan_bridge->feature_flags,
+			AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)
+		&& !ast_test_flag(&peer_bridge->feature_flags,
+			AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)) {
+		/*
+		 * Can swap either way.  Swap to the higher priority merge
+		 * bridge.
+		 */
+		chan_priority = chan_bridge->v_table->get_merge_priority(chan_bridge);
+		peer_priority = peer_bridge->v_table->get_merge_priority(peer_bridge);
+		if (chan_bridge->num_channels == 2
+			&& chan_priority <= peer_priority) {
+			dst_bridge = peer_bridge;
+			src_bridge = chan_bridge;
+			dst_bridge_channel = peer_bridge_channel;
+			src_bridge_channel = chan_bridge_channel;
+		} else if (peer_bridge->num_channels == 2
+			&& peer_priority <= chan_priority) {
+			dst_bridge = chan_bridge;
+			src_bridge = peer_bridge;
+			dst_bridge_channel = chan_bridge_channel;
+			src_bridge_channel = peer_bridge_channel;
+		}
+	} else if (chan_bridge->num_channels == 2
+		&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)
+		&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
+		/* Can swap optimize only one way. */
+		dst_bridge = peer_bridge;
+		src_bridge = chan_bridge;
+		dst_bridge_channel = peer_bridge_channel;
+		src_bridge_channel = chan_bridge_channel;
+	} else if (peer_bridge->num_channels == 2
+		&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)
+		&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_SWAP_INHIBIT_TO)) {
+		/* Can swap optimize only one way. */
+		dst_bridge = chan_bridge;
+		src_bridge = peer_bridge;
+		dst_bridge_channel = chan_bridge_channel;
+		src_bridge_channel = peer_bridge_channel;
+	}
+	if (dst_bridge) {
+		struct ast_bridge_channel *other;
+
+		res = 1;
+		other = ast_bridge_channel_peer(src_bridge_channel);
+		if (other) {
+			ast_debug(1, "Move-swap optimizing %s <-- %s.\n",
+				ast_channel_name(dst_bridge_channel->chan),
+				ast_channel_name(other->chan));
+
+			other->swap = dst_bridge_channel->chan;
+			if (!bridge_move_do(dst_bridge, other, 1)) {
+				ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+				res = -1;
+			}
+		}
+	}
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief Check and attempt to merge optimize out the local channels.
+ * \since 12.0.0
+ *
+ * \param chan_bridge
+ * \param chan_bridge_channel
+ * \param peer_bridge
+ * \param peer_bridge_channel
+ *
+ * \retval 0 if local channels were not optimized out.
+ * \retval -1 if local channels were optimized out.
+ */
+static int check_merge_optimize_out(struct ast_bridge *chan_bridge,
+	struct ast_bridge_channel *chan_bridge_channel, struct ast_bridge *peer_bridge,
+	struct ast_bridge_channel *peer_bridge_channel)
+{
+	struct ast_bridge *dst_bridge = NULL;
+	struct ast_bridge *src_bridge = NULL;
+	int peer_priority;
+	int chan_priority;
+	int res = 0;
+
+	if (!ast_test_flag(&chan_bridge->feature_flags,
+			AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)
+		&& !ast_test_flag(&peer_bridge->feature_flags,
+			AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)) {
+		/*
+		 * Can merge either way.  Merge to the higher priority merge
+		 * bridge.  Otherwise merge to the larger bridge.
+		 */
+		chan_priority = chan_bridge->v_table->get_merge_priority(chan_bridge);
+		peer_priority = peer_bridge->v_table->get_merge_priority(peer_bridge);
+		if (peer_priority < chan_priority) {
+			dst_bridge = chan_bridge;
+			src_bridge = peer_bridge;
+		} else if (chan_priority < peer_priority) {
+			dst_bridge = peer_bridge;
+			src_bridge = chan_bridge;
+		} else {
+			/* Merge to the larger bridge. */
 			if (peer_bridge->num_channels <= chan_bridge->num_channels) {
-				/* Merge to the larger bridge. */
 				dst_bridge = chan_bridge;
 				src_bridge = peer_bridge;
 			} else {
 				dst_bridge = peer_bridge;
 				src_bridge = chan_bridge;
 			}
-		} else if (!ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO)
-			&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)) {
-			/* Can merge only one way. */
-			dst_bridge = chan_bridge;
-			src_bridge = peer_bridge;
-		} else if (!ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO)
-			&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)) {
-			/* Can merge only one way. */
-			dst_bridge = peer_bridge;
-			src_bridge = chan_bridge;
-		}
-
-		if (dst_bridge) {
-			if (src_bridge->num_channels < 2 || dst_bridge->num_channels < 2) {
-				ast_debug(4, "Can't optimize %s -- %s out, not enough channels in a bridge.\n",
-					ast_channel_name(chan), ast_channel_name(peer));
-			} else if ((2 + 2) < dst_bridge->num_channels + src_bridge->num_channels
-				&& !(dst_bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)
-				&& !ast_test_flag(&dst_bridge->feature_flags, AST_BRIDGE_FLAG_SMART)) {
-				ast_debug(4, "Can't optimize %s -- %s out, multimix is needed and it cannot be acquired.\n",
-					ast_channel_name(chan), ast_channel_name(peer));
-			} else {
-				struct ast_bridge_channel *kick_me[] = {
-					chan_bridge_channel,
-					peer_bridge_channel,
-					};
-
-				ast_debug(1, "Optimizing %s -- %s out.\n",
-					ast_channel_name(chan), ast_channel_name(peer));
-
-				bridge_merge_do(dst_bridge, src_bridge, kick_me, ARRAY_LEN(kick_me));
-				res = -1;
-			}
+		}
+	} else if (!ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO)
+		&& !ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)) {
+		/* Can merge only one way. */
+		dst_bridge = chan_bridge;
+		src_bridge = peer_bridge;
+	} else if (!ast_test_flag(&peer_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_TO)
+		&& !ast_test_flag(&chan_bridge->feature_flags, AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM)) {
+		/* Can merge only one way. */
+		dst_bridge = peer_bridge;
+		src_bridge = chan_bridge;
+	}
+
+	if (dst_bridge) {
+		if (src_bridge->num_channels < 2 || dst_bridge->num_channels < 2) {
+			ast_debug(4, "Can't optimize %s -- %s out, not enough channels in a bridge.\n",
+				ast_channel_name(chan_bridge_channel->chan),
+				ast_channel_name(peer_bridge_channel->chan));
+		} else if ((2 + 2) < dst_bridge->num_channels + src_bridge->num_channels
+			&& !(dst_bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)
+			&& !ast_test_flag(&dst_bridge->feature_flags, AST_BRIDGE_FLAG_SMART)) {
+			ast_debug(4, "Can't optimize %s -- %s out, multimix is needed and it cannot be acquired.\n",
+				ast_channel_name(chan_bridge_channel->chan),
+				ast_channel_name(peer_bridge_channel->chan));
 		} else {
-/* BUGBUG this is where we would decide if we can swap a channel instead of merging. */
+			struct ast_bridge_channel *kick_me[] = {
+				chan_bridge_channel,
+				peer_bridge_channel,
+				};
+
+			ast_debug(1, "Merge optimizing %s -- %s out.\n",
+				ast_channel_name(chan_bridge_channel->chan),
+				ast_channel_name(peer_bridge_channel->chan));
+
+			bridge_merge_do(dst_bridge, src_bridge, kick_me, ARRAY_LEN(kick_me));
+			res = -1;
+		}
+	}
+
+	return res;
+}
+
+int ast_bridge_local_optimized_out(struct ast_channel *chan, struct ast_channel *peer)
+{
+	struct ast_bridge *chan_bridge;
+	struct ast_bridge *peer_bridge;
+	struct ast_bridge_channel *chan_bridge_channel;
+	struct ast_bridge_channel *peer_bridge_channel;
+	int res = 0;
+
+	chan_bridge = optimize_lock_chan_stack(chan);
+	if (!chan_bridge) {
+		return res;
+	}
+	chan_bridge_channel = ast_channel_internal_bridge_channel(chan);
+
+	peer_bridge = optimize_lock_peer_stack(peer);
+	if (peer_bridge) {
+		peer_bridge_channel = ast_channel_internal_bridge_channel(peer);
+
+		res = check_swap_optimize_out(chan_bridge, chan_bridge_channel,
+			peer_bridge, peer_bridge_channel);
+		if (!res) {
+			res = check_merge_optimize_out(chan_bridge, chan_bridge_channel,
+				peer_bridge, peer_bridge_channel);
+		} else if (0 < res) {
+			res = 0;
 		}
 
 		/* Release peer locks. */




More information about the asterisk-commits mailing list