[asterisk-commits] rmudgett: branch rmudgett/bridge_phase r386152 - in /team/rmudgett/bridge_pha...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Apr 19 14:32:56 CDT 2013


Author: rmudgett
Date: Fri Apr 19 14:32:54 2013
New Revision: 386152

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386152
Log:
Implement local channel optimization using bridge merges.

A future additional way to optimize out local channels is to swap the
Local;2 channel in BridgeB with the only peer channel (A) in BridgeA.
This optimization strategy would be needed if BridgeB prohibits bridge
merges.

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

Modified:
    team/rmudgett/bridge_phase/bridges/bridge_builtin_features.c
    team/rmudgett/bridge_phase/channels/chan_local.c
    team/rmudgett/bridge_phase/include/asterisk/bridging.h
    team/rmudgett/bridge_phase/main/bridging.c

Modified: team/rmudgett/bridge_phase/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/bridges/bridge_builtin_features.c?view=diff&rev=386152&r1=386151&r2=386152
==============================================================================
--- team/rmudgett/bridge_phase/bridges/bridge_builtin_features.c (original)
+++ team/rmudgett/bridge_phase/bridges/bridge_builtin_features.c Fri Apr 19 14:32:54 2013
@@ -193,7 +193,7 @@
 	}
 
 	/* Impart the new channel onto the bridge, and have it take our place. */
-	if (ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL, 1)) {
+	if (ast_bridge_impart(bridge_channel->bridge, chan, bridge_channel->chan, NULL, 1)) {
 		ast_hangup(chan);
 		return 0;
 	}
@@ -255,6 +255,8 @@
 	const char *context;
 	enum atxfer_code transfer_code = ATXFER_INCOMPLETE;
 
+	bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1);
+
 /* BUGBUG the peer needs to be put on hold for the transfer. */
 	ast_channel_lock(bridge_channel->chan);
 	context = ast_strdupa(get_transfer_context(bridge_channel->chan,
@@ -263,12 +265,16 @@
 
 	/* Grab the extension to transfer to */
 	if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
 		return 0;
 	}
 
 	/* Get a channel that is the destination we wish to call */
 	peer = dial_transfer(bridge_channel->chan, exten, context);
 	if (!peer) {
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
 /* BUGBUG beeperr needs to be configurable from features.conf */
 		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
 		return 0;
@@ -294,6 +300,8 @@
 			attended_transfer_threeway, &transfer_code, NULL, 0)) {
 		ast_bridge_features_cleanup(&caller_features);
 		ast_hangup(peer);
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
 /* BUGBUG beeperr needs to be configurable from features.conf */
 		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
 		return 0;
@@ -306,10 +314,13 @@
 	if (!attended_bridge) {
 		ast_bridge_features_cleanup(&caller_features);
 		ast_hangup(peer);
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
 /* BUGBUG beeperr needs to be configurable from features.conf */
 		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
 		return 0;
 	}
+	ast_bridge_merge_inhibit(attended_bridge, +1);
 
 	/* This is how this is going down, we are imparting the channel we called above into this bridge first */
 /* BUGBUG we should impart the peer as an independent and move it to the original bridge. */
@@ -317,6 +328,8 @@
 		ast_bridge_destroy(attended_bridge);
 		ast_bridge_features_cleanup(&caller_features);
 		ast_hangup(peer);
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
 /* BUGBUG beeperr needs to be configurable from features.conf */
 		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
 		return 0;
@@ -361,7 +374,7 @@
 	case ATXFER_COMPLETE:
 		/* The peer takes our place in the bridge. */
 		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-		xfer_failed = ast_bridge_impart(bridge, peer, bridge_channel->chan, NULL, 1);
+		xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, bridge_channel->chan, NULL, 1);
 		break;
 	case ATXFER_THREEWAY:
 		/*
@@ -370,12 +383,14 @@
 		 * Just impart the peer onto the bridge and have us return to it
 		 * as normal.
 		 */
-		xfer_failed = ast_bridge_impart(bridge, peer, NULL, NULL, 1);
+		xfer_failed = ast_bridge_impart(bridge_channel->bridge, peer, NULL, NULL, 1);
 		break;
 	case ATXFER_ABORT:
 		/* Transferer decided not to transfer the call after all. */
 		break;
 	}
+	ast_bridge_merge_inhibit(bridge, -1);
+	ao2_ref(bridge, -1);
 	if (xfer_failed) {
 		ast_hangup(peer);
 		if (!ast_check_hangup_locked(bridge_channel->chan)) {

Modified: team/rmudgett/bridge_phase/channels/chan_local.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/channels/chan_local.c?view=diff&rev=386152&r1=386151&r2=386152
==============================================================================
--- team/rmudgett/bridge_phase/channels/chan_local.c (original)
+++ team/rmudgett/bridge_phase/channels/chan_local.c Fri Apr 19 14:32:54 2013
@@ -54,6 +54,7 @@
 #include "asterisk/stringfields.h"
 #include "asterisk/devicestate.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/bridging.h"
 
 /*** DOCUMENTATION
 	<manager name="LocalOptimizeAway" language="en_US">
@@ -151,7 +152,6 @@
 	struct ast_channel *chan;       /*!< Outbound channel - PBX is run here */
 };
 
-#define LOCAL_ALREADY_MASQED  (1 << 0) /*!< Already masqueraded */
 #define LOCAL_LAUNCHED_PBX    (1 << 1) /*!< PBX was launched */
 #define LOCAL_NO_OPTIMIZATION (1 << 2) /*!< Do not optimize using masquerading */
 #define LOCAL_MOH_PASSTHRU    (1 << 3) /*!< Pass through music on hold start/stop frames */
@@ -449,146 +449,32 @@
 
 /*!
  * \internal
- * \note This function assumes that we're only called from the "outbound" local channel side
+ * \brief Check and optimize out the local channels between bridges.
+ * \since 12.0.0
  *
- * \note it is assummed p is locked and reffed before entering this function
+ * \param ast Channel writing a frame into the local channels.
+ * \param p Local channel private.
+ *
+ * \note It is assumed that ast is locked.
+ * \note It is assumed that p is locked.
+ *
+ * \retval 0 if local channels were not optimized out.
+ * \retval non-zero if local channels were optimized out.
  */
-static void check_bridge(struct ast_channel *ast, struct local_pvt *p)
-{
-	struct ast_channel *owner;
-	struct ast_channel *chan;
-	struct ast_channel *bridged_chan;
-	struct ast_frame *f;
-
+static int optimized_out(struct ast_channel *ast, struct local_pvt *p)
+{
 	/* Do a few conditional checks early on just to see if this optimization is possible */
-	if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
-		|| !p->chan || !p->owner) {
-		return;
-	}
-
-	/* Safely get the channel bridged to p->chan */
-	chan = ast_channel_ref(p->chan);
-
-	ao2_unlock(p); /* don't call bridged channel with the pvt locked */
-	bridged_chan = ast_bridged_channel(chan);
-	ao2_lock(p);
-
-	chan = ast_channel_unref(chan);
-
-	/* since we had to unlock p to get the bridged chan, validate our
-	 * data once again and verify the bridged channel is what we expect
-	 * it to be in order to perform this optimization */
-	if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION | LOCAL_ALREADY_MASQED)
-		|| !p->chan || !p->owner
-		|| (ast_channel_internal_bridged_channel(p->chan) != bridged_chan)) {
-		return;
-	}
-
-	/* only do the masquerade if we are being called on the outbound channel,
-	   if it has been bridged to another channel and if there are no pending
-	   frames on the owner channel (because they would be transferred to the
-	   outbound channel during the masquerade)
-	*/
-	if (!ast_channel_internal_bridged_channel(p->chan) /* Not ast_bridged_channel!  Only go one step! */
-		|| !AST_LIST_EMPTY(ast_channel_readq(p->owner))
-		|| ast != p->chan /* Sanity check (should always be false) */) {
-		return;
-	}
-
-	/* Masquerade bridged channel into owner */
-	/* Lock everything we need, one by one, and give up if
-	   we can't get everything.  Remember, we'll get another
-	   chance in just a little bit */
-	if (ast_channel_trylock(ast_channel_internal_bridged_channel(p->chan))) {
-		return;
-	}
-	if (ast_check_hangup(ast_channel_internal_bridged_channel(p->chan))
-		|| ast_channel_trylock(p->owner)) {
-		ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
-		return;
-	}
-
-	/*
-	 * At this point we have 4 locks:
-	 * p, p->chan (same as ast), p->chan->_bridge, p->owner
-	 *
-	 * Flush a voice or video frame on the outbound channel to make
-	 * the queue empty faster so we can get optimized out.
-	 */
-	f = AST_LIST_FIRST(ast_channel_readq(p->chan));
-	if (f && (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
-		AST_LIST_REMOVE_HEAD(ast_channel_readq(p->chan), frame_list);
-		ast_frfree(f);
-		f = AST_LIST_FIRST(ast_channel_readq(p->chan));
-	}
-
-	if (f
-		|| ast_check_hangup(p->owner)
-		|| ast_channel_masquerade(p->owner, ast_channel_internal_bridged_channel(p->chan))) {
-		ast_channel_unlock(p->owner);
-		ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
-		return;
-	}
-
-	/* Masquerade got setup. */
-	ast_debug(4, "Masquerading %s <- %s\n",
-		ast_channel_name(p->owner),
-		ast_channel_name(ast_channel_internal_bridged_channel(p->chan)));
-	if (ast_channel_monitor(p->owner)
-		&& !ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan))) {
-		struct ast_channel_monitor *tmp;
-
-		/* If a local channel is being monitored, we don't want a masquerade
-		 * to cause the monitor to go away. Since the masquerade swaps the monitors,
-		 * pre-swapping the monitors before the masquerade will ensure that the monitor
-		 * ends up where it is expected.
-		 */
-		tmp = ast_channel_monitor(p->owner);
-		ast_channel_monitor_set(p->owner, ast_channel_monitor(ast_channel_internal_bridged_channel(p->chan)));
-		ast_channel_monitor_set(ast_channel_internal_bridged_channel(p->chan), tmp);
-	}
-	if (ast_channel_audiohooks(p->chan)) {
-		struct ast_audiohook_list *audiohooks_swapper;
-
-		audiohooks_swapper = ast_channel_audiohooks(p->chan);
-		ast_channel_audiohooks_set(p->chan, ast_channel_audiohooks(p->owner));
-		ast_channel_audiohooks_set(p->owner, audiohooks_swapper);
-	}
-
-	/* If any Caller ID was set, preserve it after masquerade like above. We must check
-	 * to see if Caller ID was set because otherwise we'll mistakingly copy info not
-	 * set from the dialplan and will overwrite the real channel Caller ID. The reason
-	 * for this whole preswapping action is because the Caller ID is set on the channel
-	 * thread (which is the to be masqueraded away local channel) before both local
-	 * channels are optimized away.
-	 */
-	if (ast_channel_caller(p->owner)->id.name.valid || ast_channel_caller(p->owner)->id.number.valid
-		|| ast_channel_caller(p->owner)->id.subaddress.valid || ast_channel_caller(p->owner)->ani.name.valid
-		|| ast_channel_caller(p->owner)->ani.number.valid || ast_channel_caller(p->owner)->ani.subaddress.valid) {
-		SWAP(*ast_channel_caller(p->owner), *ast_channel_caller(ast_channel_internal_bridged_channel(p->chan)));
-	}
-	if (ast_channel_redirecting(p->owner)->from.name.valid || ast_channel_redirecting(p->owner)->from.number.valid
-		|| ast_channel_redirecting(p->owner)->from.subaddress.valid || ast_channel_redirecting(p->owner)->to.name.valid
-		|| ast_channel_redirecting(p->owner)->to.number.valid || ast_channel_redirecting(p->owner)->to.subaddress.valid) {
-		SWAP(*ast_channel_redirecting(p->owner), *ast_channel_redirecting(ast_channel_internal_bridged_channel(p->chan)));
-	}
-	if (ast_channel_dialed(p->owner)->number.str || ast_channel_dialed(p->owner)->subaddress.valid) {
-		SWAP(*ast_channel_dialed(p->owner), *ast_channel_dialed(ast_channel_internal_bridged_channel(p->chan)));
-	}
-	ast_app_group_update(p->chan, p->owner);
-	ast_set_flag(p, LOCAL_ALREADY_MASQED);
-
-	ast_channel_unlock(p->owner);
-	ast_channel_unlock(ast_channel_internal_bridged_channel(p->chan));
-
-	/* Do the masquerade now. */
-	owner = ast_channel_ref(p->owner);
-	ao2_unlock(p);
-	ast_channel_unlock(ast);
-	ast_do_masquerade(owner);
-	ast_channel_unref(owner);
-	ast_channel_lock(ast);
-	ao2_lock(p);
+	if (ast_test_flag(p, LOCAL_NO_OPTIMIZATION) || !p->chan || !p->owner) {
+		return 0;
+	}
+	if (ast == p->owner) {
+		return ast_bridge_local_optimized_out(p->owner, p->chan);
+	}
+	if (ast == p->chan) {
+		return ast_bridge_local_optimized_out(p->chan, p->owner);
+	}
+	/* What is ast? */
+	return 0;
 }
 
 static struct ast_frame  *local_read(struct ast_channel *ast)
@@ -607,21 +493,19 @@
 	}
 
 	/* Just queue for delivery to the other side */
-	ao2_ref(p, 1); /* ref for local_queue_frame */
+	ao2_ref(p, 1);
 	ao2_lock(p);
-	isoutbound = IS_OUTBOUND(ast, p);
-
-	if (isoutbound
-		&& (f->frametype == AST_FRAME_VOICE || f->frametype == AST_FRAME_VIDEO)) {
-		check_bridge(ast, p);
-	}
-
-	if (!ast_test_flag(p, LOCAL_ALREADY_MASQED)) {
+	switch (f->frametype) {
+	case AST_FRAME_VOICE:
+	case AST_FRAME_VIDEO:
+		if (optimized_out(ast, p)) {
+			break;
+		}
+		/* fall through */
+	default:
+		isoutbound = IS_OUTBOUND(ast, p);
 		res = local_queue_frame(p, isoutbound, f, ast, 1);
-	} else {
-		ast_debug(1, "Not posting to '%s' queue since already masqueraded out\n",
-			ast_channel_name(ast));
-		res = 0;
+		break;
 	}
 	ao2_unlock(p);
 	ao2_ref(p, -1);

Modified: team/rmudgett/bridge_phase/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/include/asterisk/bridging.h?view=diff&rev=386152&r1=386151&r2=386152
==============================================================================
--- team/rmudgett/bridge_phase/include/asterisk/bridging.h (original)
+++ team/rmudgett/bridge_phase/include/asterisk/bridging.h Fri Apr 19 14:32:54 2013
@@ -96,6 +96,15 @@
 	AST_BRIDGE_CHANNEL_STATE_END,
 	/*! Bridged channel was forced out and should be hung up */
 	AST_BRIDGE_CHANNEL_STATE_HANGUP,
+};
+
+enum ast_bridge_channel_thread_state {
+	/*! Bridge channel thread is idle/waiting. */
+	AST_BRIDGE_CHANNEL_THREAD_IDLE,
+	/*! Bridge channel thread is writing a normal/simple frame. */
+	AST_BRIDGE_CHANNEL_THREAD_SIMPLE,
+	/*! Bridge channel thread is processing a frame. */
+	AST_BRIDGE_CHANNEL_THREAD_FRAME,
 };
 
 struct ast_bridge_technology;
@@ -186,6 +195,15 @@
 	int alert_pipe[2];
 	/*! TRUE if the bridge channel thread is waiting on channels (needs to be atomically settable) */
 	int waiting;
+	/*!
+	 * \brief The bridge channel thread activity.
+	 *
+	 * \details Used by local channel optimization to determine if
+	 * the thread is in an acceptable state to optimize.
+	 *
+	 * \note Needs to be atomically settable.
+	 */
+	enum ast_bridge_channel_thread_state activity;
 };
 
 enum ast_bridge_action_type {
@@ -762,6 +780,20 @@
 void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request);
 
 /*!
+ * \brief Adjust the bridge_channel's bridge merge inhibit request count.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to operate on.
+ * \param request Inhibit request increment.
+ *     (Positive to add requests.  Negative to remove requests.)
+ *
+ * \note This API call is meant for internal bridging operations.
+ *
+ * \retval bridge adjusted merge inhibit with reference count.
+ */
+struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request);
+
+/*!
  * \brief Suspend a channel temporarily from a bridge
  *
  * \param bridge Bridge to suspend the channel from
@@ -807,6 +839,20 @@
  *       Doing so may result in bad things happening.
  */
 int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*!
+ * \brief Check and optimize out the local channels between bridges.
+ * \since 12.0.0
+ *
+ * \param chan Local channel writing a frame into the channel driver.
+ * \param peer Other local channel in the pair.
+ *
+ * \note It is assumed that chan is locked.
+ *
+ * \retval 0 if local channels were not optimized out.
+ * \retval non-zero if local channels were optimized out.
+ */
+int ast_bridge_local_optimized_out(struct ast_channel *chan, struct ast_channel *peer);
 
 /*!
  * \brief Try locking the bridge_channel.

Modified: team/rmudgett/bridge_phase/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/rmudgett/bridge_phase/main/bridging.c?view=diff&rev=386152&r1=386151&r2=386152
==============================================================================
--- team/rmudgett/bridge_phase/main/bridging.c (original)
+++ team/rmudgett/bridge_phase/main/bridging.c Fri Apr 19 14:32:54 2013
@@ -2163,6 +2163,7 @@
 		break;
 	default:
 		/* Write the frame to the channel. */
+		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE;
 		ast_write(bridge_channel->chan, fr);
 		break;
 	}
@@ -2236,6 +2237,9 @@
 		chan = ast_waitfor_nandfds(&bridge_channel->chan, 1,
 			&bridge_channel->alert_pipe[0], 1, NULL, &outfd, &ms);
 		bridge_channel->waiting = 0;
+		ast_bridge_channel_lock(bridge_channel);
+		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME;
+		ast_bridge_channel_unlock(bridge_channel);
 		if (!bridge_channel->suspended
 			&& bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
 			if (chan) {
@@ -2245,6 +2249,7 @@
 				bridge_channel_handle_write(bridge_channel);
 			}
 		}
+		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE;
 		return;
 	}
 	ast_bridge_channel_unlock(bridge_channel);
@@ -3071,6 +3076,8 @@
  *
  * \param dst_bridge Destination bridge of merge.
  * \param src_bridge Source bridge of merge.
+ * \param kick_me Array of channels to kick from the bridges.
+ * \param num_kick Number of channels in the kick_me array.
  *
  * \return Nothing
  *
@@ -3084,7 +3091,7 @@
  * this operation is completed.  The caller should explicitly
  * call ast_bridge_destroy().
  */
-static void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge)
+static void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, int num_kick)
 {
 	struct ast_bridge_channel *bridge_channel;
 
@@ -3092,6 +3099,15 @@
 		src_bridge->uniqueid, dst_bridge->uniqueid);
 
 	ast_bridge_publish_merge(dst_bridge, src_bridge);
+
+	if (kick_me) {
+		unsigned int idx;
+
+		for (idx = 0; idx < num_kick; ++idx) {
+			ast_bridge_change_state(kick_me[idx], AST_BRIDGE_CHANNEL_STATE_HANGUP);
+			bridge_channel_pull(kick_me[idx]);
+		}
+	}
 
 	/* Move channels from src_bridge over to dst_bridge */
 	while ((bridge_channel = AST_LIST_FIRST(&src_bridge->channels))) {
@@ -3144,7 +3160,7 @@
 	} else {
 		++dst_bridge->inhibit_merge;
 		++src_bridge->inhibit_merge;
-		bridge_merge_do(dst_bridge, src_bridge);
+		bridge_merge_do(dst_bridge, src_bridge, NULL, 0);
 		--src_bridge->inhibit_merge;
 		--dst_bridge->inhibit_merge;
 		res = 0;
@@ -3155,17 +3171,219 @@
 	return res;
 }
 
+/*!
+ * \internal
+ * \brief Lock the local channel stack for chan and prequalify it.
+ * \since 12.0.0
+ *
+ * \param chan Local channel writing a frame into the channel driver.
+ *
+ * \note It is assumed that chan is locked.
+ *
+ * \retval bridge on success with bridge and bridge_channel locked.
+ * \retval NULL if cannot do optimization now.
+ */
+static struct ast_bridge *lock_local_chan(struct ast_channel *chan)
+{
+	struct ast_bridge *bridge;
+	struct ast_bridge_channel *bridge_channel;
+
+	if (!AST_LIST_EMPTY(ast_channel_readq(chan))) {
+		return NULL;
+	}
+	bridge_channel = ast_channel_internal_bridge_channel(chan);
+	if (!bridge_channel || ast_bridge_channel_trylock(bridge_channel)) {
+		return NULL;
+	}
+	bridge = bridge_channel->bridge;
+	if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_SIMPLE
+		|| bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT
+		|| ast_bridge_trylock(bridge)) {
+		ast_bridge_channel_unlock(bridge_channel);
+		return NULL;
+	}
+	if (bridge->inhibit_merge
+		|| bridge->dissolved
+		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
+		|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
+		ast_bridge_unlock(bridge);
+		ast_bridge_channel_unlock(bridge_channel);
+		return NULL;
+	}
+	return bridge;
+}
+
+/*!
+ * \internal
+ * \brief Lock the local channel stack for peer and prequalify it.
+ * \since 12.0.0
+ *
+ * \param peer Other local channel in the pair.
+ *
+ * \retval bridge on success with bridge, bridge_channel, and peer locked.
+ * \retval NULL if cannot do optimization now.
+ */
+static struct ast_bridge *lock_local_peer(struct ast_channel *peer)
+{
+	struct ast_bridge *bridge;
+	struct ast_bridge_channel *bridge_channel;
+
+	if (ast_channel_trylock(peer)) {
+		return NULL;
+	}
+	if (!AST_LIST_EMPTY(ast_channel_readq(peer))) {
+		ast_channel_unlock(peer);
+		return NULL;
+	}
+	bridge_channel = ast_channel_internal_bridge_channel(peer);
+	if (!bridge_channel || ast_bridge_channel_trylock(bridge_channel)) {
+		ast_channel_unlock(peer);
+		return NULL;
+	}
+	bridge = bridge_channel->bridge;
+	if (bridge_channel->activity != AST_BRIDGE_CHANNEL_THREAD_IDLE
+		|| bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT
+		|| ast_bridge_trylock(bridge)) {
+		ast_bridge_channel_unlock(bridge_channel);
+		ast_channel_unlock(peer);
+		return NULL;
+	}
+	if (bridge->inhibit_merge
+		|| bridge->dissolved
+		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY)
+		|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
+		ast_bridge_unlock(bridge);
+		ast_bridge_channel_unlock(bridge_channel);
+		ast_channel_unlock(peer);
+		return NULL;
+	}
+	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;
+	int res = 0;
+
+	chan_bridge = lock_local_chan(chan);
+	if (!chan_bridge) {
+		return res;
+	}
+	chan_bridge_channel = ast_channel_internal_bridge_channel(chan);
+
+	peer_bridge = lock_local_peer(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 (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 {
+/* BUGBUG this is where we would decide if we can swap a channel instead of merging. */
+		}
+
+		/* Release peer locks. */
+		ast_bridge_unlock(peer_bridge);
+		ast_bridge_channel_unlock(peer_bridge_channel);
+		ast_channel_unlock(peer);
+	}
+
+	/* Release chan locks. */
+	ast_bridge_unlock(chan_bridge);
+	ast_bridge_channel_unlock(chan_bridge_channel);
+
+	return res;
+}
+
+/*!
+ * \internal
+ * \brief Adjust the bridge merge inhibit request count.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ * \param request Inhibit request increment.
+ *     (Positive to add requests.  Negative to remove requests.)
+ *
+ * \note This function assumes bridge is locked.
+ *
+ * \return Nothing
+ */
+static void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request)
+{
+	int new_request;
+
+	new_request = bridge->inhibit_merge + request;
+	ast_assert(0 <= new_request);
+	bridge->inhibit_merge = new_request;
+}
+
 void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request)
 {
-	int new_request;
-
 	ast_bridge_lock(bridge);
-	new_request = bridge->inhibit_merge + request;
-	if (new_request < 0) {
-		new_request = 0;
-	}
-	bridge->inhibit_merge = new_request;
+	bridge_merge_inhibit_nolock(bridge, request);
 	ast_bridge_unlock(bridge);
+}
+
+struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request)
+{
+	struct ast_bridge *bridge;
+
+	ast_bridge_channel_lock_bridge(bridge_channel);
+	bridge = bridge_channel->bridge;
+	ao2_ref(bridge, +1);
+	bridge_merge_inhibit_nolock(bridge, request);
+	ast_bridge_unlock(bridge);
+	return bridge;
 }
 
 int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)




More information about the asterisk-commits mailing list