[asterisk-commits] mmichelson: branch mmichelson/more_transfer r389252 - in /team/mmichelson/mor...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon May 20 13:16:40 CDT 2013


Author: mmichelson
Date: Mon May 20 13:16:36 2013
New Revision: 389252

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389252
Log:
Add method to determine allowability of unreal channel optimization between bridges.

The idea here is that, given two bridges, the question of whether unreal channel
optimization is allowed between the bridges would be allowed.

The attended transfer code uses this to determine how to act when there are two
bridges involved. If optimization is allowed, then the type of optimization that
would be used should be used directly. If optimization is not allowed, then an
unreal channel will be used to link the two bridges together to complete the
transfer.

The method to determine optimization allowability is public since it may be
useful for other purposes, such as determining whether to send stasis messages
about local channel optimizations.


Modified:
    team/mmichelson/more_transfer/include/asterisk/bridging.h
    team/mmichelson/more_transfer/main/bridging.c

Modified: team/mmichelson/more_transfer/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/include/asterisk/bridging.h?view=diff&rev=389252&r1=389251&r2=389252
==============================================================================
--- team/mmichelson/more_transfer/include/asterisk/bridging.h (original)
+++ team/mmichelson/more_transfer/include/asterisk/bridging.h Mon May 20 13:16:36 2013
@@ -867,6 +867,45 @@
 int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel *peer);
 
 /*!
+ * \brief Tells, if optimization is allowed, how the optimization would be performed
+ */
+enum ast_bridge_optimization {
+	/*! Optimization would swap peer into the chan_bridge */
+	AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE,
+	/*! Optimization would swap chan into the peer_bridge */
+	AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE,
+	/*! Optimization would merge peer_bridge into chan_bridge */
+	AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE,
+	/*! Optimization would merge chan_bridge into peer_bridge */
+	AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE,
+	/*! Optimization is not permitted on one or both bridges */
+	AST_BRIDGE_OPTIMIZE_PROHIBITED,
+};
+
+/*!
+ * \brief Determine if bridges allow for optimization to occur betweem them
+ * \since 12.0.0
+ *
+ * \param chan_bridge First bridge being tested
+ * \param peer_bridge Second bridge being tested
+ *
+ * This determines if two bridges allow for unreal channel optimization
+ * to occur between them. The function does not require for unreal channels
+ * to already be in the bridges when called.
+ *
+ * \note It is assumed that both bridges are locked prior to calling this function
+ *
+ * \note A return other than AST_BRIDGE_OPTIMIZE_PROHIBITED does not guarantee
+ * that an optimization attempt will succeed. However, a return of
+ * AST_BRIDGE_OPTIMIZE_PROHIBITED guarantees that an optimization attempt will
+ * never succeed.
+ *
+ * \returns Optimization allowability for the bridges
+ */
+enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge);
+
+/*!
  * \brief Try locking the bridge_channel.
  *
  * \param bridge_channel What to try locking

Modified: team/mmichelson/more_transfer/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/mmichelson/more_transfer/main/bridging.c?view=diff&rev=389252&r1=389251&r2=389252
==============================================================================
--- team/mmichelson/more_transfer/main/bridging.c (original)
+++ team/mmichelson/more_transfer/main/bridging.c Mon May 20 13:16:36 2013
@@ -3606,6 +3606,19 @@
 	return other;
 }
 
+static int bridge_allows_optimization(struct ast_bridge *bridge)
+{
+	return !(bridge->inhibit_merge
+		|| bridge->dissolved
+		|| ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_MASQUERADE_ONLY));
+}
+
+static int bridge_channel_allows_optimization(struct ast_bridge_channel *bridge_channel)
+{
+	return bridge_channel->in_bridge
+		&& AST_LIST_EMPTY(&bridge_channel->wr_queue);
+}
+
 /*!
  * \internal
  * \brief Lock the unreal channel stack for chan and prequalify it.
@@ -3637,11 +3650,8 @@
 		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)
-		|| !bridge_channel->in_bridge
-		|| !AST_LIST_EMPTY(&bridge_channel->wr_queue)) {
+	if (!bridge_channel_allows_optimization(bridge_channel) ||
+			!bridge_allows_optimization(bridge)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
 		return NULL;
@@ -3684,11 +3694,8 @@
 		ast_channel_unlock(peer);
 		return NULL;
 	}
-	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)) {
+	if (!bridge_allows_optimization(bridge) ||
+			!bridge_channel_allows_optimization(bridge_channel)) {
 		ast_bridge_unlock(bridge);
 		ast_bridge_channel_unlock(bridge_channel);
 		ast_channel_unlock(peer);
@@ -3699,28 +3706,30 @@
 
 /*!
  * \internal
- * \brief Check and attempt to swap optimize out the unreal channels.
- * \since 12.0.0
- *
- * \param chan_bridge
- * \param chan_bridge_channel
- * \param peer_bridge
- * \param peer_bridge_channel
- *
- * \retval 1 if unreal channels failed to optimize out.
- * \retval 0 if unreal channels were not optimized out.
- * \retval -1 if unreal 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_channel *dst_bridge_channel = NULL;
-	struct ast_bridge_channel *src_bridge_channel = NULL;
+ * \brief Indicates allowability of a swap optimization
+ */
+enum bridge_allow_swap {
+	/*! Bridges cannot allow for a swap optimization to occure */
+	SWAP_PROHIBITED,
+	/*! Bridge swap optimization can occur into the chan_bridge */
+	SWAP_TO_CHAN_BRIDGE,
+	/*! Bridge swap optimization can occur into the peer_bridge */
+	SWAP_TO_PEER_BRIDGE,
+};
+
+/*!
+ * \internal
+ * \brief Determine if two bridges allow for swap optimization to occur
+ *
+ * \param chan_bridge First bridge being tested
+ * \param peer_bridge Second bridge being tested
+ * \return Allowability of swap optimization
+ */
+static enum bridge_allow_swap bridges_allow_swap_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge)
+{
+	int chan_priority;
 	int peer_priority;
-	int chan_priority;
-	int res = 0;
 
 	if (!ast_test_flag(&chan_bridge->feature_flags,
 			AST_BRIDGE_FLAG_SWAP_INHIBIT_TO | AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM)
@@ -3734,53 +3743,29 @@
 		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;
-			dst_bridge_channel = peer_bridge_channel;
-			src_bridge_channel = chan_bridge_channel;
+			return SWAP_TO_PEER_BRIDGE;
 		} else if (peer_bridge->num_channels == 2
 			&& peer_priority <= chan_priority) {
-			dst_bridge = chan_bridge;
-			dst_bridge_channel = chan_bridge_channel;
-			src_bridge_channel = peer_bridge_channel;
+			return SWAP_TO_CHAN_BRIDGE;
 		}
 	} 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;
-		dst_bridge_channel = peer_bridge_channel;
-		src_bridge_channel = chan_bridge_channel;
+		return SWAP_TO_PEER_BRIDGE;
 	} 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;
-		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 && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-			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 unreal channels.
+		return SWAP_TO_CHAN_BRIDGE;
+	}
+
+	return SWAP_PROHIBITED;
+}
+
+/*!
+ * \internal
+ * \brief Check and attempt to swap optimize out the unreal channels.
  * \since 12.0.0
  *
  * \param chan_bridge
@@ -3788,6 +3773,107 @@
  * \param peer_bridge
  * \param peer_bridge_channel
  *
+ * \retval 1 if unreal channels failed to optimize out.
+ * \retval 0 if unreal channels were not optimized out.
+ * \retval -1 if unreal 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;
+	struct ast_bridge_channel *dst_bridge_channel;
+	struct ast_bridge_channel *src_bridge_channel;
+	struct ast_bridge_channel *other;
+	int res = 1;
+
+	switch (bridges_allow_swap_optimization(chan_bridge, peer_bridge)) {
+	case SWAP_TO_CHAN_BRIDGE:
+		dst_bridge = chan_bridge;
+		dst_bridge_channel = chan_bridge_channel;
+		src_bridge_channel = peer_bridge_channel;
+		break;
+	case SWAP_TO_PEER_BRIDGE:
+		dst_bridge = peer_bridge;
+		dst_bridge_channel = peer_bridge_channel;
+		src_bridge_channel = chan_bridge_channel;
+		break;
+	case SWAP_PROHIBITED:
+	default:
+		return 0;
+	}
+
+	other = ast_bridge_channel_peer(src_bridge_channel);
+	if (other && other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		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 Indicates allowability of a merge optimization
+ */
+enum bridge_allow_merge {
+	/*! Bridge properties prohibit merge optimization */
+	MERGE_PROHIBITED,
+	/*! Merge optimization cannot occur because the source bridge has too few channels */
+	MERGE_NOT_ENOUGH_CHANNELS,
+	/*! Merge optimization cannot occur because multimix capability could not be requested */
+	MERGE_NO_MULTIMIX,
+	/*! Merge optimization allowed between bridges */
+	MERGE_ALLOWED,
+};
+
+/*!
+ * \internal
+ * \brief Determines allowability of a merge optimization
+ *
+ * \note The merge output parameter is undefined if MERGE_PROHIBITED is returned. For success
+ * and other failure returns, a merge direction was determined, and the parameter is safe to
+ * access.
+ *
+ * \param chan_bridge First bridge being tested
+ * \param peer_bridge Second bridge being tested
+ * \param[out] merge Indicates the recommended direction for the bridge merge
+ */
+static enum bridge_allow_merge bridges_allow_merge_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge, struct merge_direction *merge)
+{
+	*merge = bridge_merge_determine_direction(chan_bridge, peer_bridge);
+	if (!merge->dest) {
+		MERGE_PROHIBITED;
+	}
+	if (merge->src->num_channels < 2) {
+		return MERGE_NOT_ENOUGH_CHANNELS;
+	} else if ((2 + 2) < merge->dest->num_channels + merge->src->num_channels
+		&& !(merge->dest->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)
+		&& (!ast_test_flag(&merge->dest->feature_flags, AST_BRIDGE_FLAG_SMART)
+			|| !(merge->dest->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX))) {
+		return MERGE_NO_MULTIMIX;
+	} 
+
+	return MERGE_ALLOWED;
+}
+
+/*!
+ * \internal
+ * \brief Check and attempt to merge optimize out the unreal channels.
+ * \since 12.0.0
+ *
+ * \param chan_bridge
+ * \param chan_bridge_channel
+ * \param peer_bridge
+ * \param peer_bridge_channel
+ *
  * \retval 0 if unreal channels were not optimized out.
  * \retval -1 if unreal channels were optimized out.
  */
@@ -3796,39 +3882,36 @@
 	struct ast_bridge_channel *peer_bridge_channel)
 {
 	struct merge_direction merge;
-	int res = 0;
-
-	merge = bridge_merge_determine_direction(chan_bridge, peer_bridge);
-	if (!merge.dest) {
-		return res;
-	}
-	if (merge.src->num_channels < 2) {
+	struct ast_bridge_channel *kick_me[] = {
+		chan_bridge_channel,
+		peer_bridge_channel,
+	};
+
+	switch (bridges_allow_merge_optimization(chan_bridge, peer_bridge, &merge)) {
+	case MERGE_ALLOWED:
+		break;
+	case MERGE_PROHIBITED:
+		return 0;
+	case MERGE_NOT_ENOUGH_CHANNELS:
 		ast_debug(4, "Can't optimize %s -- %s out, not enough channels in bridge %s.\n",
 			ast_channel_name(chan_bridge_channel->chan),
 			ast_channel_name(peer_bridge_channel->chan),
 			merge.src->uniqueid);
-	} else if ((2 + 2) < merge.dest->num_channels + merge.src->num_channels
-		&& !(merge.dest->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)
-		&& (!ast_test_flag(&merge.dest->feature_flags, AST_BRIDGE_FLAG_SMART)
-			|| !(merge.dest->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX))) {
+		return 0;
+	case MERGE_NO_MULTIMIX:
 		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 {
-		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(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me));
-		res = -1;
-	}
-
-	return res;
+		return 0;
+	}
+
+	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(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me));
+
+	return -1;
 }
 
 int ast_bridge_unreal_optimized_out(struct ast_channel *chan, struct ast_channel *peer)
@@ -3869,6 +3952,36 @@
 	ast_bridge_channel_unlock(chan_bridge_channel);
 
 	return res;
+}
+
+enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge,
+		struct ast_bridge *peer_bridge)
+{
+	struct merge_direction merge;
+
+	if (!bridge_allows_optimization(chan_bridge) || !bridge_allows_optimization(peer_bridge)) {
+		return AST_BRIDGE_OPTIMIZE_PROHIBITED;
+	}
+
+	switch (bridges_allow_swap_optimization(chan_bridge, peer_bridge)) {
+	case SWAP_TO_CHAN_BRIDGE:
+		return AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE;
+	case SWAP_TO_PEER_BRIDGE:
+		return AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE;
+	case SWAP_PROHIBITED:
+	default:
+		break;
+	}
+
+	if (bridges_allow_merge_optimization(chan_bridge, peer_bridge, &merge) != MERGE_ALLOWED) {
+		return AST_BRIDGE_OPTIMIZE_PROHIBITED;
+	}
+
+	if (merge.dest == chan_bridge) {
+		return AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE;
+	} else {
+		return AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE;
+	}
 }
 
 /*!
@@ -5200,6 +5313,86 @@
 	return bridge;
 }
 
+/*!
+ * \internal
+ * \brief Performs an attended transfer by moving a channel from one bridge to another
+ *
+ * The channel that is bridged to the source_channel is moved into the dest_bridge from
+ * the source_bridge. The swap_channel is swapped out of the dest_bridge and placed in
+ * the source_bridge.
+ *
+ * \note dest_bridge and source_bridge MUST be locked before calling this function.
+ *
+ * \param dest_bridge The final bridge for the attended transfer
+ * \param source_bridge The bridge from which a channel is moved
+ * \param source_channel Channel who is bridged to the channel that will move
+ * \param swap_channel Channel to be swapped out of the dest_bridge
+ * \return The success or failure of the swap attempt
+ */
+static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge *dest_bridge,
+		struct ast_bridge *source_bridge, struct ast_bridge_channel *source_bridge_channel,
+		struct ast_channel *swap_channel)
+{
+	struct ast_bridge_channel *bridged_to_source;
+
+	bridged_to_source = ast_bridge_channel_peer(source_bridge_channel);
+	if (bridged_to_source) {
+		bridged_to_source->swap = swap_channel;
+		return bridge_move_do(dest_bridge, bridged_to_source, 1) ?
+			AST_BRIDGE_TRANSFER_FAIL : AST_BRIDGE_TRANSFER_SUCCESS;
+	} else {
+		return AST_BRIDGE_TRANSFER_INVALID;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Function that performs an attended transfer when both transferer channels are bridged
+ *
+ * The method by which the transfer is performed is dependent on whether the bridges allow for
+ * optimization to occur between them. If no optimization is permitted, then an unreal channel
+ * is placed as a link between the two bridges. If optimization is permitted, then that means
+ * we are free to perform move or merge operations in order to perform the transfer.
+ *
+ * \note to_transferee_bridge and to_target_bridge MUST be locked before calling this function
+ *
+ * \param to_transferee The channel that is bridged to the transferee
+ * \param to_transferee_bridge_channel to_transferee's bridge_channel
+ * \param to_transfer_target The channel that is bridged to the transfer target
+ * \param to_target_bridge_channel to_transfer_target's bridge_channel
+ * \param to_transferee_bridge The bridge between to_transferee and the transferee
+ * \param to_target_bridge The bridge between to_transfer_target and the transfer_target
+ * \return The success or failure of the attended transfer
+ */
+static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel *to_transferee,
+		struct ast_bridge_channel *to_transferee_bridge_channel,
+		struct ast_channel *to_transfer_target,
+		struct ast_bridge_channel *to_target_bridge_channel,
+		struct ast_bridge *to_transferee_bridge, struct ast_bridge *to_target_bridge)
+{
+	struct ast_bridge_channel *kick_me[2] = {
+			to_transferee_bridge_channel,
+			to_target_bridge_channel,
+	};
+
+	switch (ast_bridges_allow_optimization(to_transferee_bridge, to_target_bridge)) {
+	case AST_BRIDGE_OPTIMIZE_SWAP_TO_CHAN_BRIDGE:
+		return bridge_swap_attended_transfer(to_transferee_bridge, to_target_bridge, to_target_bridge_channel, to_transferee);
+	case AST_BRIDGE_OPTIMIZE_SWAP_TO_PEER_BRIDGE:
+		return bridge_swap_attended_transfer(to_target_bridge, to_transferee_bridge, to_transferee_bridge_channel, to_transfer_target);
+	case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE:
+		bridge_merge_do(to_transferee_bridge, to_target_bridge, kick_me, 2);
+		return AST_BRIDGE_TRANSFER_SUCCESS;
+	case AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE:
+		bridge_merge_do(to_target_bridge, to_transferee_bridge, kick_me, 2);
+		return AST_BRIDGE_TRANSFER_SUCCESS;
+	case AST_BRIDGE_OPTIMIZE_PROHIBITED:
+	default:
+		return attended_transfer_bridge(to_transferee, to_transfer_target,
+			to_transferee_bridge, to_target_bridge);
+	}
+}
+
 enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_transferee,
 		struct ast_channel *to_transfer_target)
 {
@@ -5223,8 +5416,26 @@
 
 	/* Let's get the easy one out of the way first */
 	if (to_transferee_bridge && to_target_bridge) {
-		return attended_transfer_bridge(to_transferee, to_transfer_target,
+		RAII_VAR(struct ast_bridge_channel *, to_transferee_bridge_channel, NULL, ao2_cleanup);
+		RAII_VAR(struct ast_bridge_channel *, to_target_bridge_channel, NULL, ao2_cleanup);
+		enum ast_transfer_result res;
+
+		ast_channel_lock(to_transferee);
+		to_transferee_bridge_channel = ast_channel_get_bridge_channel(to_transferee);
+		ast_channel_unlock(to_transferee);
+
+		ast_channel_lock(to_transfer_target);
+		to_target_bridge_channel = ast_channel_get_bridge_channel(to_transfer_target);
+		ast_channel_unlock(to_transfer_target);
+
+		ast_bridge_lock_both(to_transferee_bridge, to_target_bridge);
+		res = two_bridge_attended_transfer(to_transferee, to_transferee_bridge_channel,
+				to_transfer_target, to_target_bridge_channel,
 				to_transferee_bridge, to_target_bridge);
+		ast_bridge_unlock(to_transferee_bridge);
+		ast_bridge_unlock(to_target_bridge);
+
+		return res;
 	}
 
 	the_bridge = to_transferee_bridge ?: to_target_bridge;




More information about the asterisk-commits mailing list