[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