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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Jan 25 16:08:34 CST 2013


Author: rmudgett
Date: Fri Jan 25 16:08:15 2013
New Revision: 380137

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=380137
Log:
Initial conversion of the system to use the Bridging API.

Expected things to not work:
* Local channel optimization is disabled.
* Channel driver transfers may not work.
* DTMF Monitor, MixMonitor, Parking features do not work.
* DTMF blind and attended transfer work but only on by the called party
and they currently work differently.
* Dynamic features do not work.

ast_bridge_impart()/ast_bridge_depart() usage changed.

Modified:
    team/group/bridge_construction/bridges/bridge_builtin_features.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
    team/group/bridge_construction/main/features.c

Modified: team/group/bridge_construction/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/bridges/bridge_builtin_features.c?view=diff&rev=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/bridges/bridge_builtin_features.c (original)
+++ team/group/bridge_construction/bridges/bridge_builtin_features.c Fri Jan 25 16:08:15 2013
@@ -47,8 +47,14 @@
 #include "asterisk/file.h"
 #include "asterisk/app.h"
 #include "asterisk/astobj2.h"
-
-/*! \brief Helper function that presents dialtone and grabs extension */
+#include "asterisk/pbx.h"
+
+/*!
+ * \brief Helper function that presents dialtone and grabs extension
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
 static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
 {
 	int res;
@@ -56,15 +62,34 @@
 	/* Play the simple "transfer" prompt out and wait */
 	res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
 	ast_stopstream(chan);
-
-	/* If the person hit a DTMF digit while the above played back stick it into the buffer */
+	if (res < 0) {
+		/* Hangup or error */
+		return -1;
+	}
 	if (res) {
-		exten[0] = (char)res;
+		/* Store the DTMF digit that interrupted playback of the file. */
+		exten[0] = res;
 	}
 
 	/* Drop to dialtone so they can enter the extension they want to transfer to */
-	res = ast_app_dtget(chan, context, exten, exten_len, 100, 1000);
-
+	res = ast_app_dtget(chan, context, exten, exten_len, exten_len - 1, 3000);
+	if (res < 0) {
+		/* Hangup or error */
+		res = -1;
+	} else if (!res) {
+		/* 0 for invalid extension dialed. */
+		if (ast_strlen_zero(exten)) {
+			ast_debug(1, "%s dialed no digits.\n", ast_channel_name(chan));
+		} else {
+			ast_debug(1, "%s dialed '%s@%s' does not exist.\n",
+				ast_channel_name(chan), exten, context);
+		}
+		ast_stream_and_wait(chan, "pbx-invalid", AST_DIGIT_NONE);
+		res = -1;
+	} else {
+		/* Dialed extension is valid. */
+		res = 0;
+	}
 	return res;
 }
 
@@ -79,7 +104,9 @@
 	snprintf(destination, sizeof(destination), "%s@%s", exten, context);
 
 	/* Now we request that chan_local prepare to call the destination */
-	if (!(chan = ast_request("Local", ast_channel_nativeformats(caller), caller, destination, &cause))) {
+	chan = ast_request("Local", ast_channel_nativeformats(caller), caller, destination,
+		&cause);
+	if (!chan) {
 		return NULL;
 	}
 
@@ -100,28 +127,65 @@
 	return chan;
 }
 
+/*!
+ * \internal
+ * \brief Determine the transfer context to use.
+ * \since 12.0.0
+ *
+ * \param transferer Channel initiating the transfer.
+ * \param context User supplied context if available.  May be NULL.
+ *
+ * \return The context to use for the transfer.
+ */
+static const char *get_transfer_context(struct ast_channel *transferer, const char *context)
+{
+	if (!ast_strlen_zero(context)) {
+		return context;
+	}
+	context = pbx_builtin_getvar_helper(transferer, "TRANSFER_CONTEXT");
+	if (!ast_strlen_zero(context)) {
+		return context;
+	}
+	context = ast_channel_macrocontext(transferer);
+	if (!ast_strlen_zero(context)) {
+		return context;
+	}
+	context = ast_channel_context(transferer);
+	if (!ast_strlen_zero(context)) {
+		return context;
+	}
+	return "default";
+}
+
 /*! \brief Internal built in feature for blind transfers */
 static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
 	char exten[AST_MAX_EXTENSION] = "";
 	struct ast_channel *chan = NULL;
 	struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
-	const char *context = (blind_transfer && !ast_strlen_zero(blind_transfer->context) ? blind_transfer->context : ast_channel_context(bridge_channel->chan));
+	const char *context;
+
+	ast_channel_lock(bridge_channel->chan);
+	context = ast_strdupa(get_transfer_context(bridge_channel->chan,
+		blind_transfer ? blind_transfer->context : NULL));
+	ast_channel_unlock(bridge_channel->chan);
 
 	/* Grab the extension to transfer to */
-	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
-		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
+	if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
 		return 0;
 	}
 
 	/* Get a channel that is the destination we wish to call */
-	if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
-		return 0;
-	}
-
-	/* This is sort of the fun part. We impart the above channel onto the bridge, and have it take our place. */
-	ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL, 1);
+	chan = dial_transfer(bridge_channel->chan, exten, context);
+	if (!chan) {
+		return 0;
+	}
+
+	/* Impart the new channel onto the bridge, and have it take our place. */
+	if (ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL, 1)) {
+		ast_hangup(chan);
+		return 0;
+	}
 
 	return 0;
 }
@@ -138,100 +202,111 @@
 	return 0;
 }
 
-/*! \brief Attended transfer abort feature */
-static int attended_abort_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	struct ast_bridge_channel *called_bridge_channel = NULL;
-
-	/* It is possible (albeit unlikely) that the bridge channels list may change, so we have to ensure we do all of our magic while locked */
-	ao2_lock(bridge);
-
-	if (AST_LIST_FIRST(&bridge->channels) != bridge_channel) {
-		called_bridge_channel = AST_LIST_FIRST(&bridge->channels);
-	} else {
-		called_bridge_channel = AST_LIST_LAST(&bridge->channels);
-	}
-
-	/* Now we basically eject the other channel from the bridge. This will cause their thread to hang them up, and our own code to consider the transfer failed. */
-	if (called_bridge_channel) {
-		ast_bridge_change_state(called_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-	}
-
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
-
-	ao2_unlock(bridge);
-
-	return 0;
-}
-
 /*! \brief Internal built in feature for attended transfers */
 static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 {
 	char exten[AST_MAX_EXTENSION] = "";
-	struct ast_channel *chan = NULL;
-	struct ast_bridge *attended_bridge = NULL;
-	struct ast_bridge_features caller_features, called_features;
+	struct ast_channel *peer;
+	struct ast_bridge *attended_bridge;
+	struct ast_bridge_features caller_features;
 	enum ast_bridge_channel_state attended_bridge_result;
+	int xfer_failed;
 	struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
-	const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : ast_channel_context(bridge_channel->chan));
+	const char *context;
+
+	ast_channel_lock(bridge_channel->chan);
+	context = ast_strdupa(get_transfer_context(bridge_channel->chan,
+		attended_transfer ? attended_transfer->context : NULL));
+	ast_channel_unlock(bridge_channel->chan);
 
 	/* Grab the extension to transfer to */
-	if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
-		ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
+	if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
 		return 0;
 	}
 
 	/* Get a channel that is the destination we wish to call */
-	if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+	peer = dial_transfer(bridge_channel->chan, exten, context);
+	if (!peer) {
+		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
 		return 0;
 	}
 
 	/* Create a bridge to use to talk to the person we are calling */
-	if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
-		ast_hangup(chan);
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
-		return 0;
-	}
-
-	/* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */
-	ast_bridge_features_init(&called_features);
-	ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE);
+	attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE,
+		AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+	if (!attended_bridge) {
+		ast_hangup(peer);
+		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+		return 0;
+	}
 
 	/* This is how this is going down, we are imparting the channel we called above into this bridge first */
-	ast_bridge_impart(attended_bridge, chan, NULL, &called_features, 1);
+	if (ast_bridge_impart(attended_bridge, peer, NULL, NULL, 0)) {
+		ast_bridge_destroy(attended_bridge);
+		ast_hangup(peer);
+		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+		return 0;
+	}
 
 	/* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
 	ast_bridge_features_init(&caller_features);
 	ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP,
-				   (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL);
-	ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"),
-				 attended_threeway_transfer, NULL, NULL);
-	ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"),
-				 attended_abort_transfer, NULL, NULL);
+		attended_transfer && !ast_strlen_zero(attended_transfer->complete)
+			? attended_transfer->complete : "*1",
+		NULL);
+	ast_bridge_features_hook(&caller_features,
+		attended_transfer && !ast_strlen_zero(attended_transfer->threeway)
+			? attended_transfer->threeway : "*2",
+		attended_threeway_transfer, NULL, NULL);
 
 	/* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */
 	attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL);
 
-	/* Since the above returned the caller features structure is of no more use */
+	/* Wait for peer thread to exit bridge and die. */
+	if (!ast_autoservice_start(bridge_channel->chan)) {
+		ast_bridge_depart(attended_bridge, peer);
+		ast_autoservice_stop(bridge_channel->chan);
+	} else {
+		ast_bridge_depart(attended_bridge, peer);
+	}
+
+	/* Now that all channels are out of it we can destroy the bridge and the feature structures */
 	ast_bridge_features_cleanup(&caller_features);
-
-	/* Drop the channel we are transferring to out of the above bridge since it has ended */
-	if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) {
-		/* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */
-		if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) {
-			/* We want to impart them upon the bridge and just have us return to it as normal */
-			ast_bridge_impart(bridge, chan, NULL, NULL, 1);
-		} else {
-			ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL, 1);
+	ast_bridge_destroy(attended_bridge);
+
+	xfer_failed = -1;
+	switch (attended_bridge_result) {
+	case AST_BRIDGE_CHANNEL_STATE_END:
+		if (!ast_check_hangup_locked(bridge_channel->chan)) {
+			/* Transferer aborted the transfer. */
+			break;
 		}
-	} else {
-		ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
-	}
-
-	/* Now that all channels are out of it we can destroy the bridge and the called features structure */
-	ast_bridge_features_cleanup(&called_features);
-	ast_bridge_destroy(attended_bridge);
+
+		/* 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);
+		break;
+	case AST_BRIDGE_CHANNEL_STATE_HANGUP:
+		/* Peer hungup */
+		break;
+	case AST_BRIDGE_CHANNEL_STATE_DEPART:
+		/*
+		 * Transferer wants to convert to a threeway call.
+		 *
+		 * 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);
+		break;
+	default:
+		break;
+	}
+	if (xfer_failed) {
+		ast_hangup(peer);
+		if (!ast_check_hangup_locked(bridge_channel->chan)) {
+			ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
+		}
+	}
 
 	return 0;
 }

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=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Fri Jan 25 16:08:15 2013
@@ -156,8 +156,8 @@
 	int fds[4];
 	/*! Bit to indicate whether the channel is suspended from the bridge or not */
 	unsigned int suspended:1;
-	/*! Bit to indicate if a imparted channel is allowed to get hungup after leaving the bridge by the bridging core. */
-	unsigned int allow_impart_hangup:1;
+	/*! TRUE if the imparted channel must wait for an explicit depart from the bridge to reclaim the channel. */
+	unsigned int depart_wait:1;
 	/*! Features structure for features that are specific to this channel */
 	struct ast_bridge_features *features;
 	/*! Technology optimization parameters used by bridging technologies capable of
@@ -212,6 +212,8 @@
  * \brief Structure that contains information about a bridge
  */
 struct ast_bridge {
+	/*! Condition, used if we want to wake up the bridge thread. */
+	ast_cond_t cond;
 	/*! Number of channels participating in the bridge */
 	int num;
 	/*! The video mode this bridge is using */
@@ -249,7 +251,9 @@
 	struct ast_callid *callid;
 	/*! Linked list of channels participating in the bridge */
 	AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) channels;
-	};
+	/*! Linked list of channels removed from the bridge and waiting to be departed. */
+	AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) depart_wait;
+};
 
 /*!
  * \brief Create a new bridge
@@ -264,7 +268,7 @@
  *
  * \code
  * struct ast_bridge *bridge;
- * bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE);
+ * bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
  * \endcode
  *
  * This creates a simple two party bridge that will be destroyed once one of
@@ -374,9 +378,9 @@
  *
  * \param bridge Bridge to impart on
  * \param chan Channel to impart
- * \param swap Channel to swap out if swapping
- * \param features Bridge features structure
- * \param allow_hangup  Indicates if the bridge thread should manage hanging up of the channel or not.
+ * \param swap Channel to swap out if swapping.  NULL if not swapping.
+ * \param features Bridge features structure. Must be NULL or obtained by ast_bridge_features_new().
+ * \param independent TRUE if caller does not want to reclaim the channel using ast_bridge_depart().
  *
  * \retval 0 on success
  * \retval -1 on failure
@@ -387,18 +391,34 @@
  * ast_bridge_impart(bridge, chan, NULL, NULL, 0);
  * \endcode
  *
- * This adds a channel pointed to by the chan pointer to the bridge pointed to by
- * the bridge pointer. This function will return immediately and will not wait
- * until the channel is no longer part of the bridge.
- *
- * If this channel will be replacing another channel the other channel can be specified
- * in the swap parameter. The other channel will be thrown out of the bridge in an
- * atomic fashion.
- *
- * If channel specific features are enabled a pointer to the features structure
- * can be specified in the features parameter.
- */
-int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int allow_hangup);
+ * \details
+ * This adds a channel pointed to by the chan pointer to the
+ * bridge pointed to by the bridge pointer.  This function will
+ * return immediately and will not wait until the channel is no
+ * longer part of the bridge.
+ *
+ * If this channel will be replacing another channel the other
+ * channel can be specified in the swap parameter.  The other
+ * channel will be thrown out of the bridge in an atomic
+ * fashion.
+ *
+ * If channel specific features are enabled, a pointer to the
+ * features structure can be specified in the features
+ * parameter.
+ *
+ * \note If you impart a channel as not independent you MUST
+ * ast_bridge_depart() the channel.  The bridge channel thread
+ * is created join-able.  The implication is that the channel is
+ * special and is not intended to be moved to another bridge.
+ *
+ * \note If you impart a channel as independent you must not
+ * ast_bridge_depart() the channel.  The bridge channel thread
+ * is created non-join-able.  The channel must be treated as if
+ * it were placed into the bridge by ast_bridge_join().
+ * Channels placed into a bridge by ast_bridge_join() are
+ * removed by a third party using ast_bridge_remove().
+ */
+int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int independent);
 
 /*!
  * \brief Depart a channel from a bridge
@@ -420,7 +440,7 @@
  * This does not hang up the channel.
  *
  * \note This API call can only be used on channels that were added to the bridge
- *       using the ast_bridge_impart API call.
+ *       using the ast_bridge_impart API call with the independent flag FALSE.
  */
 int ast_bridge_depart(struct ast_bridge *bridge, struct ast_channel *chan);
 

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=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_features.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_features.h Fri Jan 25 16:08:15 2013
@@ -30,10 +30,12 @@
 
 /*! \brief Flags used for bridge features */
 enum ast_bridge_feature_flags {
-	/*! Upon hangup the bridge should be discontinued */
-	AST_BRIDGE_FLAG_DISSOLVE = (1 << 0),
+	/*! Upon channel hangup the bridge should be ended. */
+	AST_BRIDGE_FLAG_DISSOLVE_HANGUP = (1 << 0),
 	/*! Move between bridging technologies as needed. */
 	AST_BRIDGE_FLAG_SMART = (1 << 1),
+	/*! The bridge ends when the last channel leaves. (There is no external bridge manager.) */
+	AST_BRIDGE_FLAG_DISSOLVE_EMPTY = (1 << 2),
 };
 
 /*! \brief Built in DTMF features */
@@ -52,6 +54,25 @@
 	 * AST_BRIDGE_CHANNEL_STATE_END.
 	 */
 	AST_BRIDGE_BUILTIN_HANGUP,
+	/*!
+	 * DTMF based Park
+	 *
+	 * \details The bridge is parked and the channel hears the
+	 * parking slot to which it was parked.
+	 */
+	AST_BRIDGE_BUILTIN_PARKCALL,
+	/*!
+	 * DTMF one-touch-record toggle using Monitor app.
+	 *
+	 * \note Only valid on two party bridges.
+	 */
+	AST_BRIDGE_BUILTIN_AUTOMON,
+	/*!
+	 * DTMF one-touch-record toggle using MixMonitor app.
+	 *
+	 * \note Only valid on two party bridges.
+	 */
+	AST_BRIDGE_BUILTIN_AUTOMIXMON,
 
 	/*! End terminator for list of built in features. Must remain last. */
 	AST_BRIDGE_BUILTIN_END
@@ -153,14 +174,12 @@
  * \brief Structure that contains configuration information for the attended transfer built in feature
  */
 struct ast_bridge_features_attended_transfer {
-	/*! DTMF string used to abort the transfer */
-	char abort[MAXIMUM_DTMF_FEATURE_STRING];
+	/*! Context to use for transfers */
+	char context[AST_MAX_CONTEXT];
 	/*! DTMF string used to turn the transfer into a three way conference */
 	char threeway[MAXIMUM_DTMF_FEATURE_STRING];
 	/*! DTMF string used to complete the transfer */
 	char complete[MAXIMUM_DTMF_FEATURE_STRING];
-	/*! Context to use for transfers */
-	char context[AST_MAX_CONTEXT];
 };
 
 /*!
@@ -288,11 +307,11 @@
  * \code
  * struct ast_bridge_features features;
  * ast_bridge_features_init(&features);
- * ast_bridge_features_set_flag(&features, AST_BRIDGE_FLAG_DISSOLVE);
- * \endcode
- *
- * This sets the AST_BRIDGE_FLAG_DISSOLVE feature to be enabled on the features structure
- * 'features'.
+ * ast_bridge_features_set_flag(&features, AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+ * \endcode
+ *
+ * This sets the AST_BRIDGE_FLAG_DISSOLVE_HANGUP feature to be
+ * enabled on the features structure 'features'.
  */
 void ast_bridge_features_set_flag(struct ast_bridge_features *features, enum ast_bridge_feature_flags flag);
 
@@ -341,6 +360,41 @@
 void ast_bridge_features_cleanup(struct ast_bridge_features *features);
 
 /*!
+ * \brief Allocate a new bridge features struct.
+ * \since 12.0.0
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features *features;
+ * features = ast_bridge_features_new();
+ * ast_bridge_features_destroy(features);
+ * \endcode
+ *
+ * \retval features New allocated features struct.
+ * \retval NULL on error.
+ */
+struct ast_bridge_features *ast_bridge_features_new(void);
+
+/*!
+ * \brief Destroy an allocated bridge features struct.
+ * \since 12.0.0
+ *
+ * \param features Bridge features structure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features *features;
+ * features = ast_bridge_features_new();
+ * ast_bridge_features_destroy(features);
+ * \endcode
+ *
+ * \return Nothing
+ */
+void ast_bridge_features_destroy(struct ast_bridge_features *features);
+
+/*!
  * \brief Play a DTMF stream into a bridge, optionally not to a given channel
  *
  * \param bridge Bridge to play stream into

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=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Fri Jan 25 16:08:15 2013
@@ -154,8 +154,9 @@
 static void bridge_poke(struct ast_bridge *bridge)
 {
 	/* Poke the thread just in case */
-	if (bridge->thread != AST_PTHREADT_NULL && bridge->thread != AST_PTHREADT_STOP) {
+	if (bridge->thread != AST_PTHREADT_NULL) {
 		pthread_kill(bridge->thread, SIGURG);
+		ast_cond_signal(&bridge->cond);
 	}
 }
 
@@ -170,13 +171,16 @@
  */
 static void bridge_stop(struct ast_bridge *bridge)
 {
-	pthread_t thread = bridge->thread;
+	pthread_t thread;
 
 	bridge->stop = 1;
 	bridge_poke(bridge);
+	thread = bridge->thread;
+	bridge->thread = AST_PTHREADT_NULL;
 	ao2_unlock(bridge);
 	pthread_join(thread, NULL);
 	ao2_lock(bridge);
+	bridge->stop = 0;
 }
 
 /*!
@@ -211,6 +215,8 @@
 		bridge->array = new_array;
 		bridge->array_size += BRIDGE_ARRAY_GROW;
 	}
+
+	bridge_poke(bridge);
 }
 
 /*! \brief Helper function to remove a channel from the bridge array
@@ -285,10 +291,10 @@
 /*! \brief Internal function to see whether a bridge should dissolve, and if so do it */
 static void bridge_check_dissolve(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	if (!ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE)
+	if (!ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
 		&& (!bridge_channel->features
 			|| !bridge_channel->features->usable
-			|| !ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_FLAG_DISSOLVE))) {
+			|| !ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP))) {
 		return;
 	}
 
@@ -324,6 +330,14 @@
 static int bridge_drop_control_frame(int subclass)
 {
 	switch (subclass) {
+	case AST_CONTROL_READ_ACTION:
+	case AST_CONTROL_CC:
+	case AST_CONTROL_MCID:
+	case AST_CONTROL_AOC:
+	case AST_CONTROL_CONNECTED_LINE:
+	case AST_CONTROL_REDIRECTING:
+		return 1;
+
 	case AST_CONTROL_ANSWER:
 	case -1:
 		return 1;
@@ -404,7 +418,10 @@
 /*! \brief Generic thread loop, TODO: Rethink this/improve it */
 static int generic_thread_loop(struct ast_bridge *bridge)
 {
-	while (!bridge->stop && !bridge->refresh && bridge->array_num) {
+	if (bridge->stop || bridge->refresh || !bridge->array_num) {
+		return 0;
+	}
+	for (;;) {
 		struct ast_channel *winner;
 		int to = -1;
 
@@ -422,11 +439,13 @@
 		bridge->waiting = 0;
 		ao2_lock(bridge);
 
+		if (bridge->stop || bridge->refresh || !bridge->array_num) {
+			return 0;
+		}
+
 		/* Process whatever they did */
 		ast_bridge_handle_trip(bridge, NULL, winner, -1);
 	}
-
-	return 0;
 }
 
 /*! \brief Bridge thread function */
@@ -444,7 +463,13 @@
 	ast_debug(1, "Started bridge thread for %p\n", bridge);
 
 	/* Loop around until we are told to stop */
-	while (!bridge->stop && bridge->array_num && !res) {
+	while (!bridge->stop) {
+		if (!bridge->array_num) {
+			/* Wait for a channel to be added to the bridge. */
+			ast_cond_wait(&bridge->cond, ao2_object_get_lockaddr(bridge));
+			continue;
+		}
+
 		/* In case the refresh bit was set simply set it back to off */
 		bridge->refresh = 0;
 
@@ -459,14 +484,20 @@
 		res = bridge->technology->thread
 			? bridge->technology->thread(bridge)
 			: generic_thread_loop(bridge);
+		if (res) {
+			/*
+			 * A bridge error occurred.  Sleep and try again later so we
+			 * won't flood the logs.
+			 */
+			ao2_unlock(bridge);
+			sleep(1);
+			ao2_lock(bridge);
+		}
 	}
 
 	ast_debug(1, "Ending bridge thread for %p\n", bridge);
 
-	/* Indicate the bridge thread is no longer active */
-	bridge->thread = AST_PTHREADT_NULL;
 	ao2_unlock(bridge);
-
 	ao2_ref(bridge, -1);
 
 	return NULL;
@@ -513,6 +544,7 @@
 
 	/* There should not be any channels left in the bridge. */
 	ast_assert(AST_LIST_EMPTY(&bridge->channels));
+	ast_assert(AST_LIST_EMPTY(&bridge->depart_wait));
 
 	/* Pass off the bridge to the technology to destroy if needed */
 	if (bridge->technology->destroy) {
@@ -533,6 +565,7 @@
 
 	/* Drop the array of channels */
 	ast_free(bridge->array);
+	ast_cond_destroy(&bridge->cond);
 }
 
 struct ast_bridge *ast_bridge_new(uint32_t capabilities, int flags)
@@ -569,6 +602,7 @@
 		return NULL;
 	}
 
+	ast_cond_init(&bridge->cond, NULL);
 	bridge->technology = bridge_technology;
 	bridge->thread = AST_PTHREADT_NULL;
 
@@ -695,6 +729,7 @@
 		.bridge_pvt = bridge->bridge_pvt,
 	};
 	struct ast_bridge_channel *bridge_channel2;
+	pthread_t thread;
 
 	/* Based on current feature determine whether we want to change bridge technologies or not */
 	if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
@@ -723,10 +758,16 @@
 		return -1;
 	}
 
+	/*
+	 * We are now committed to changing the bridge technology.  We
+	 * must not release the bridge lock until we have installed the
+	 * new bridge technology.
+	 */
 	ast_debug(1, "Performing smart bridge operation on bridge %p, moving from bridge technology %s to %s\n",
 		bridge, old_technology->name, new_technology->name);
 
 	/* If a thread is currently executing for the current technology tell it to stop */
+	thread = AST_PTHREADT_NULL;
 	if (bridge->thread != AST_PTHREADT_NULL) {
 		/*
 		 * If the new bridge technology also needs a thread simply tell
@@ -740,7 +781,10 @@
 			bridge_poke(bridge);
 		} else {
 			ast_debug(1, "Telling current bridge thread for bridge %p to stop\n", bridge);
-			bridge_stop(bridge);
+			bridge->stop = 1;
+			bridge_poke(bridge);
+			thread = bridge->thread;
+			bridge->thread = AST_PTHREADT_NULL;
 		}
 	}
 
@@ -795,6 +839,14 @@
 
 		/* Fourth we tell them to wake up so they become aware that the above has happened */
 		bridge_channel_poke(bridge_channel2);
+	}
+
+	if (thread != AST_PTHREADT_NULL) {
+		/* Wait for the old bridge thread to die. */
+		ao2_unlock(bridge);
+		pthread_join(thread, NULL);
+		ao2_lock(bridge);
+		bridge->stop = 0;
 	}
 
 	/* Now that all the channels have been moved over we need to get rid of all the information the old technology may have left around */
@@ -964,7 +1016,11 @@
 		 * here if the hook did not already change the state.
 		 */
 		if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
-			ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+			ao2_lock(bridge_channel);
+			if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_FEATURE) {
+				ast_bridge_change_state_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+			}
+			ao2_unlock(bridge_channel);
 		}
 	} else {
 		ast_bridge_dtmf_stream(bridge, dtmf, bridge_channel->chan);
@@ -1052,10 +1108,17 @@
 	while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
 		/* Update bridge pointer on channel */
 		ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
+
+		/* Wait for an old bridge thread to die. */
+		while (bridge_channel->bridge->stop) {
+			ao2_unlock(bridge_channel->bridge);
+			sched_yield();
+			ao2_lock(bridge_channel->bridge);
+		}
+
 		/* If the technology requires a thread and one is not running, start it up */
 		if (bridge_channel->bridge->thread == AST_PTHREADT_NULL
 			&& (bridge_channel->bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
-			bridge_channel->bridge->stop = 0;
 			ast_debug(1, "Starting a bridge thread for bridge %p\n", bridge_channel->bridge);
 			ao2_ref(bridge_channel->bridge, +1);
 			if (ast_pthread_create(&bridge_channel->bridge->thread, NULL, bridge_thread, bridge_channel->bridge)) {
@@ -1134,6 +1197,12 @@
 	bridge_channel->bridge->num--;
 	AST_LIST_REMOVE(&bridge_channel->bridge->channels, bridge_channel, entry);
 
+	if (bridge_channel->depart_wait
+		&& bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_DEPART) {
+		/* Put the channel into the ast_bridge_depart wait list. */
+		AST_LIST_INSERT_TAIL(&bridge_channel->bridge->depart_wait, bridge_channel, entry);
+	}
+
 	bridge_array_remove(bridge_channel->bridge, bridge_channel->chan);
 
 	/* Perform the smart bridge operation if needed since a channel has left */
@@ -1225,37 +1294,69 @@
 	return state;
 }
 
-/*! \brief Thread responsible for imparted bridged channels */
-static void *bridge_channel_thread(void *data)
+/*! \brief Thread responsible for imparted bridged channels to be departed */
+static void *bridge_channel_depart_thread(void *data)
 {
 	struct ast_bridge_channel *bridge_channel = data;
-	enum ast_bridge_channel_state state;
 
 	if (bridge_channel->callid) {
 		ast_callid_threadassoc_add(bridge_channel->callid);
 	}
 
+	bridge_channel_join(bridge_channel);
+
+	/* cleanup */
+	bridge_channel->swap = NULL;
+	ast_bridge_features_destroy(bridge_channel->features);
+	bridge_channel->features = NULL;
+
+	return NULL;
+}
+
+/*! \brief Thread responsible for independent imparted bridged channels */
+static void *bridge_channel_ind_thread(void *data)
+{
+	struct ast_bridge_channel *bridge_channel = data;
+	struct ast_channel *chan;
+	enum ast_bridge_channel_state state;
+
+	if (bridge_channel->callid) {
+		ast_callid_threadassoc_add(bridge_channel->callid);
+	}
+
 	state = bridge_channel_join(bridge_channel);
 
-	/* If no other thread is going to take the channel then hang it up, or else we would have to service it until something else came along */
-	if (bridge_channel->allow_impart_hangup && (state == AST_BRIDGE_CHANNEL_STATE_END || state == AST_BRIDGE_CHANNEL_STATE_HANGUP)) {
-		ast_hangup(bridge_channel->chan);
-	}
+	chan = bridge_channel->chan;
 
 	/* cleanup */
-	ao2_lock(bridge_channel);
 	bridge_channel->chan = NULL;
 	bridge_channel->swap = NULL;
+	ast_bridge_features_destroy(bridge_channel->features);
 	bridge_channel->features = NULL;
-	ao2_unlock(bridge_channel);
 
 	ao2_ref(bridge_channel, -1);
 
+	switch (state) {
+	case AST_BRIDGE_CHANNEL_STATE_DEPART:
+		ast_log(LOG_ERROR, "Independently imparted channel was departed: %s\n",
+			ast_channel_name(chan));
+		ast_assert(0);
+		/* fallthrough */
+	case AST_BRIDGE_CHANNEL_STATE_HANGUP:
+	case AST_BRIDGE_CHANNEL_STATE_END:
+		ast_hangup(chan);
+		break;
+	default:
+		ast_hangup(chan);
+		break;
+	}
+
 	return NULL;
 }
 
-int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int allow_hangup)
-{
+int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features, int independent)
+{
+	int res;
 	struct ast_bridge_channel *bridge_channel;
 
 	/* Try to allocate a structure for the bridge channel */
@@ -1268,11 +1369,23 @@
 	bridge_channel->chan = chan;
 	bridge_channel->swap = swap;
 	bridge_channel->features = features;
-	bridge_channel->allow_impart_hangup = allow_hangup;
+	bridge_channel->depart_wait = independent ? 0 : 1;
 	bridge_channel->callid = ast_read_threadstorage_callid();
 
 	/* Actually create the thread that will handle the channel */
-	if (ast_pthread_create(&bridge_channel->thread, NULL, bridge_channel_thread, bridge_channel)) {
+	if (independent) {
+		res = ast_pthread_create_detached(&bridge_channel->thread, NULL,
+			bridge_channel_ind_thread, bridge_channel);
+	} else {
+		res = ast_pthread_create(&bridge_channel->thread, NULL,
+			bridge_channel_depart_thread, bridge_channel);
+	}
+	if (res) {
+		/* cleanup */
+		bridge_channel->chan = NULL;
+		bridge_channel->swap = NULL;
+		bridge_channel->features = NULL;
+
 		ao2_ref(bridge_channel, -1);
 		return -1;
 	}
@@ -1287,19 +1400,44 @@
 
 	ao2_lock(bridge);
 
-	/* Try to find the channel that we want to depart */
-	if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
-		ao2_unlock(bridge);
-		return -1;
-	}
-
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
+	do {
+		/* Try to find the channel that we want to depart */
+		bridge_channel = find_bridge_channel(bridge, chan);
+		if (bridge_channel) {
+			/* Channel is still in the bridge. */
+			if (!bridge_channel->depart_wait) {
+				ast_log(LOG_ERROR, "Bridged channel cannot be departed: %s\n",
+					ast_channel_name(bridge_channel->chan));
+				ao2_unlock(bridge);
+				return -1;
+			}
+			ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
+			break;
+		}
+
+		/* Was the channel already removed from the bridge? */
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&bridge->depart_wait, bridge_channel, entry) {
+			if (bridge_channel->chan == chan) {
+				AST_LIST_REMOVE_CURRENT(entry);
+				break;
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
+		if (!bridge_channel) {
+			/* Channel does not exist. */
+			ao2_unlock(bridge);
+			return -1;
+		}
+	} while (0);
 	thread = bridge_channel->thread;
 
 	ao2_unlock(bridge);
 
+	/* Wait for the depart thread to die */
 	pthread_join(thread, NULL);
 
+	/* We can get rid of the bridge_channel after the depart thread has died. */
+	ao2_ref(bridge_channel, -1);
 	return 0;
 }
 
@@ -1325,6 +1463,7 @@
 int ast_bridge_merge(struct ast_bridge *bridge0, struct ast_bridge *bridge1)
 {
 	struct ast_bridge_channel *bridge_channel;
+	pthread_t thread;
 
 	ao2_lock(bridge0);
 	ao2_lock(bridge1);
@@ -1350,9 +1489,13 @@
 	}
 
 	/* If a thread is currently executing on bridge1 tell it to stop */
-	if (bridge1->thread) {
+	thread = AST_PTHREADT_NULL;
+	if (bridge1->thread != AST_PTHREADT_NULL) {
 		ast_debug(1, "Telling bridge thread on bridge %p to stop as it is being merged into %p\n", bridge1, bridge0);
-		bridge1->thread = AST_PTHREADT_STOP;
+		bridge1->stop = 1;
+		bridge_poke(bridge1);
+		thread = bridge1->thread;
+		bridge1->thread = AST_PTHREADT_NULL;
 	}
 
 	/* Move channels from bridge1 over to bridge0 */
@@ -1396,8 +1539,14 @@
 
 	ast_debug(1, "Merged channels from bridge %p into bridge %p\n", bridge1, bridge0);
 
+	ao2_unlock(bridge0);
 	ao2_unlock(bridge1);
-	ao2_unlock(bridge0);
+	if (thread != AST_PTHREADT_NULL) {
+		pthread_join(thread, NULL);
+		ao2_lock(bridge1);
+		bridge1->stop = 0;
+		ao2_unlock(bridge1);
+	}
 
 	return 0;
 }
@@ -1565,6 +1714,23 @@
 		features->talker_destructor_cb(features->talker_pvt_data);
 		features->talker_pvt_data = NULL;
 	}
+}
+
+struct ast_bridge_features *ast_bridge_features_new(void)
+{
+	struct ast_bridge_features *features;
+
+	features = ast_calloc(1, sizeof(*features));
+	return features;
+}
+
+void ast_bridge_features_destroy(struct ast_bridge_features *features)
+{
+	if (!features) {
+		return;
+	}
+	ast_bridge_features_cleanup(features);
+	ast_free(features);
 }
 
 int ast_bridge_dtmf_stream(struct ast_bridge *bridge, const char *dtmf, struct ast_channel *chan)

Modified: team/group/bridge_construction/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/main/features.c?view=diff&rev=380137&r1=380136&r2=380137
==============================================================================
--- team/group/bridge_construction/main/features.c (original)
+++ team/group/bridge_construction/main/features.c Fri Jan 25 16:08:15 2013
@@ -72,6 +72,7 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/cel.h"
 #include "asterisk/test.h"
+#include "asterisk/bridging.h"
 
 /*
  * Party A - transferee
@@ -3710,6 +3711,7 @@
 	return res;
 }
 
+#if 0//BUGBUG
 /*!
  * \brief Check the dynamic features
  * \param chan,peer,config,code,sense
@@ -3756,6 +3758,7 @@
 
 	return res;
 }
+#endif
 
 
 int ast_feature_detect(struct ast_channel *chan, struct ast_flags *features, const char *code, struct ast_call_feature *feature) {
@@ -3763,6 +3766,7 @@
 	return feature_interpret_helper(chan, NULL, NULL, code, 0, NULL, features, FEATURE_INTERPRET_DETECT, feature);
 }
 
+#if 0//BUGBUG
 /*! \brief Check if a feature exists */
 static int feature_check(struct ast_channel *chan, struct ast_flags *features, char *code) {
 	struct ast_str *chan_dynamic_features;
@@ -3781,6 +3785,7 @@
 
 	return res;
 }
+#endif
 
 static void set_config_flags(struct ast_channel *chan, struct ast_bridge_config *config)
 {
@@ -4309,6 +4314,78 @@
 }
 
 /*!
+ * \internal
+ * \brief Setup bridge channel features.
+ * \since 12.0.0
+ *
+ * \param features Bridge features to setup.
+ * \param flags DTMF features to enable.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.
+ */
+static int setup_bridge_channel_features(struct ast_bridge_features *features, struct ast_flags *flags)
+{
+	struct ast_call_feature *dtmf;
+	int res = 0;
+
+	if (ast_test_flag(flags, AST_FEATURE_REDIRECT)) {
+		/* Add atxfer and blind transfer. */
+		ast_rwlock_rdlock(&features_lock);
+		dtmf = ast_find_call_feature("blindxfer");
+		if (dtmf && !ast_strlen_zero(dtmf->exten)) {
+/* BUGBUG need to supply a blind transfer structure and destructor to use other than defaults */
+			res |= ast_bridge_features_enable(features, AST_BRIDGE_BUILTIN_BLINDTRANSFER, dtmf->exten, NULL);
+		}
+		dtmf = ast_find_call_feature("atxfer");
+		if (dtmf && !ast_strlen_zero(dtmf->exten)) {

[... 442 lines stripped ...]



More information about the asterisk-commits mailing list