[asterisk-commits] mjordan: branch mjordan/cdrs-of-doom r386309 - in /team/mjordan/cdrs-of-doom:...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Apr 22 13:16:36 CDT 2013


Author: mjordan
Date: Mon Apr 22 13:16:33 2013
New Revision: 386309

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=386309
Log:
Merge in bridge_construction

Modified:
    team/mjordan/cdrs-of-doom/   (props changed)
    team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c
    team/mjordan/cdrs-of-doom/channels/chan_local.c
    team/mjordan/cdrs-of-doom/include/asterisk/bridging.h
    team/mjordan/cdrs-of-doom/include/asterisk/srv.h
    team/mjordan/cdrs-of-doom/main/bridging.c
    team/mjordan/cdrs-of-doom/main/channel.c

Propchange: team/mjordan/cdrs-of-doom/
------------------------------------------------------------------------------
Binary property 'branch-11-merged' - no diff available.

Propchange: team/mjordan/cdrs-of-doom/
------------------------------------------------------------------------------
--- cdrs-of-doom-integrated (original)
+++ cdrs-of-doom-integrated Mon Apr 22 13:16:33 2013
@@ -1,1 +1,1 @@
-/team/group/bridge_construction:1-386287
+/team/group/bridge_construction:1-386308

Modified: team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c?view=diff&rev=386309&r1=386308&r2=386309
==============================================================================
--- team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c (original)
+++ team/mjordan/cdrs-of-doom/bridges/bridge_builtin_features.c Mon Apr 22 13:16:33 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,35 @@
 	/* 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);
-
+/* BUGBUG the timeout needs to be configurable from features.conf. */
+	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;
 }
 
@@ -76,10 +102,17 @@
 	int cause;
 
 	/* Fill the variable with the extension and context we want to call */
+/* BUGBUG if local channel optimization is using masquerades then this needs /n so the destination keeps its DTMF features.
+ * Or use /n to keep the peer channel stable until after the atxfer completes and remove the /n from the channel.
+ *
+ * Local channel optimization currently is disabled because I don't set the chan->bridge pointers.
+ */
 	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,67 +133,113 @@
 	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;
+
+/* 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,
+		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);
-		return 0;
-	}
+	if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+		return 0;
+	}
+
+/* BUGBUG just need to ast_async_goto the peer so this bridge will go away and not accumulate local channels and bridges if the destination is to an application. */
+/* ast_async_goto actually is a blind transfer. */
+/* BUGBUG Use the bridge count to determine if can do DTMF transfer features.  If count is not 2 then don't allow it. */
 
 	/* 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_channel->bridge, chan, bridge_channel->chan, NULL, 1)) {
+		ast_hangup(chan);
+		return 0;
+	}
+
+	return 0;
+}
+
+/*! Attended transfer code */
+enum atxfer_code {
+	/*! Party C hungup or other reason to abandon the transfer. */
+	ATXFER_INCOMPLETE,
+	/*! Transfer party C to party A. */
+	ATXFER_COMPLETE,
+	/*! Turn the transfer into a threeway call. */
+	ATXFER_THREEWAY,
+	/*! Hangup party C and return party B to the bridge. */
+	ATXFER_ABORT,
+};
+
+/*! \brief Attended transfer feature to complete transfer */
+static int attended_transfer_complete(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	enum atxfer_code *transfer_code = hook_pvt;
+
+	*transfer_code = ATXFER_COMPLETE;
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
 	return 0;
 }
 
 /*! \brief Attended transfer feature to turn it into a threeway call */
-static int attended_threeway_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
-{
-	/*
-	 * This is sort of abusing the depart state but in this instance
-	 * it is only going to be handled by feature_attended_transfer()
-	 * so it is okay.
-	 */
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
-	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);
-
+static int attended_transfer_threeway(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	enum atxfer_code *transfer_code = hook_pvt;
+
+	*transfer_code = ATXFER_THREEWAY;
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+	return 0;
+}
+
+/*! \brief Attended transfer feature to abort transfer */
+static int attended_transfer_abort(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	enum atxfer_code *transfer_code = hook_pvt;
+
+	*transfer_code = ATXFER_ABORT;
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
 	return 0;
 }
 
@@ -168,70 +247,156 @@
 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;
-	enum ast_bridge_channel_state attended_bridge_result;
+	struct ast_channel *peer;
+	struct ast_bridge *attended_bridge;
+	struct ast_bridge_features caller_features;
+	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;
+	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,
+		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)) {
+		ast_bridge_merge_inhibit(bridge, -1);
+		ao2_ref(bridge, -1);
 		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_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;
+	}
+
+/* BUGBUG bridging API features does not support features.conf featuremap */
+/* BUGBUG bridging API features does not support the features.conf atxfer bounce between C & B channels */
+	/* Setup a DTMF menu to control the transfer. */
+	if (ast_bridge_features_init(&caller_features)
+		|| ast_bridge_hangup_hook(&caller_features,
+			attended_transfer_complete, &transfer_code, NULL, 0)
+		|| ast_bridge_dtmf_hook(&caller_features,
+			attended_transfer && !ast_strlen_zero(attended_transfer->abort)
+				? attended_transfer->abort : "*1",
+			attended_transfer_abort, &transfer_code, NULL, 0)
+		|| ast_bridge_dtmf_hook(&caller_features,
+			attended_transfer && !ast_strlen_zero(attended_transfer->complete)
+				? attended_transfer->complete : "*2",
+			attended_transfer_complete, &transfer_code, NULL, 0)
+		|| ast_bridge_dtmf_hook(&caller_features,
+			attended_transfer && !ast_strlen_zero(attended_transfer->threeway)
+				? attended_transfer->threeway : "*3",
+			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;
 	}
 
 	/* 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_base_new(
+		AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX,
+		AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+	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 */
-	ast_bridge_impart(attended_bridge, chan, NULL, &called_features, 1);
-
-	/* 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);
-
-	/* 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 */
+/* BUGBUG we should impart the peer as an independent and move it to the original bridge. */
+	if (ast_bridge_impart(attended_bridge, peer, NULL, NULL, 0)) {
+		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;
+	}
+
+	/*
+	 * 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.
+	 */
+	ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL, 0);
+
+/*
+ * BUGBUG there is a small window where the channel does not point to the bridge_channel.
+ *
+ * This window is expected to go away when atxfer is redesigned
+ * to fully support existing functionality.  There will be one
+ * and only one ast_bridge_channel structure per channel.
+ */
+	/* Point the channel back to the original bridge_channel. */
+	ast_channel_lock(bridge_channel->chan);
+	ast_channel_internal_bridge_channel_set(bridge_channel->chan, bridge_channel);
+	ast_channel_unlock(bridge_channel->chan);
+
+	/* Wait for peer thread to exit bridge and die. */
+	if (!ast_autoservice_start(bridge_channel->chan)) {
+		ast_bridge_depart(peer);
+		ast_autoservice_stop(bridge_channel->chan);
+	} else {
+		ast_bridge_depart(peer);
+	}
+
+	/* Now that all channels are out of it we can destroy the bridge and the feature structures */
+	ast_bridge_destroy(attended_bridge);
 	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);
+	xfer_failed = -1;
+	switch (transfer_code) {
+	case ATXFER_INCOMPLETE:
+		/* Peer hungup */
+		break;
+	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_channel->bridge, peer, bridge_channel->chan, NULL, 1);
+		break;
+	case ATXFER_THREEWAY:
+		/*
+		 * 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_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)) {
+			ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_NONE);
 		}
-	} 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);
+	}
 
 	return 0;
 }

Modified: team/mjordan/cdrs-of-doom/channels/chan_local.c
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/channels/chan_local.c?view=diff&rev=386309&r1=386308&r2=386309
==============================================================================
--- team/mjordan/cdrs-of-doom/channels/chan_local.c (original)
+++ team/mjordan/cdrs-of-doom/channels/chan_local.c Mon Apr 22 13:16:33 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">
@@ -108,7 +109,6 @@
 static int local_sendhtml(struct ast_channel *ast, int subclass, const char *data, int datalen);
 static int local_sendtext(struct ast_channel *ast, const char *text);
 static int local_devicestate(const char *data);
-static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
 static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen);
 static int local_setoption(struct ast_channel *chan, int option, void *data, int datalen);
 
@@ -131,7 +131,6 @@
 	.send_html = local_sendhtml,
 	.send_text = local_sendtext,
 	.devicestate = local_devicestate,
-	.bridged_channel = local_bridgedchannel,
 	.queryoption = local_queryoption,
 	.setoption = local_setoption,
 };
@@ -153,11 +152,9 @@
 	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_BRIDGE          (1 << 3) /*!< Report back the "true" channel as being bridged to */
-#define LOCAL_MOH_PASSTHRU    (1 << 4) /*!< Pass through music on hold start/stop frames */
+#define LOCAL_LAUNCHED_PBX    (1 << 0) /*!< PBX was launched */
+#define LOCAL_NO_OPTIMIZATION (1 << 1) /*!< Do not optimize using masquerading */
+#define LOCAL_MOH_PASSTHRU    (1 << 2) /*!< Pass through music on hold start/stop frames */
 
 /*!
  * \brief Send a pvt in with no locks held and get all locks
@@ -329,37 +326,6 @@
 	return res;
 }
 
-/*! \brief Return the bridged channel of a Local channel */
-static struct ast_channel *local_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
-{
-	struct local_pvt *p = ast_channel_tech_pvt(bridge);
-	struct ast_channel *bridged = bridge;
-
-	if (!p) {
-		ast_debug(1, "Asked for bridged channel on '%s'/'%s', returning <none>\n",
-			ast_channel_name(chan), ast_channel_name(bridge));
-		return NULL;
-	}
-
-	ao2_lock(p);
-
-	if (ast_test_flag(p, LOCAL_BRIDGE)) {
-		/* Find the opposite channel */
-		bridged = (bridge == p->owner ? p->chan : p->owner);
-
-		/* Now see if the opposite channel is bridged to anything */
-		if (!bridged) {
-			bridged = bridge;
-		} else if (ast_channel_internal_bridged_channel(bridged)) {
-			bridged = ast_channel_internal_bridged_channel(bridged);
-		}
-	}
-
-	ao2_unlock(p);
-
-	return bridged;
-}
-
 /* Called with ast locked */
 static int local_queryoption(struct ast_channel *ast, int option, void *data, int *datalen)
 {
@@ -483,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 got_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);
+	}
+	/* ast is not valid to optimize. */
+	return 0;
 }
 
 static struct ast_frame  *local_read(struct ast_channel *ast)
@@ -634,28 +486,24 @@
 {
 	struct local_pvt *p = ast_channel_tech_pvt(ast);
 	int res = -1;
-	int isoutbound;
 
 	if (!p) {
 		return -1;
 	}
 
 	/* 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)) {
-		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;
+	switch (f->frametype) {
+	case AST_FRAME_VOICE:
+	case AST_FRAME_VIDEO:
+		if (got_optimized_out(ast, p)) {
+			break;
+		}
+		/* fall through */
+	default:
+		res = local_queue_frame(p, IS_OUTBOUND(ast, p), f, ast, 1);
+		break;
 	}
 	ao2_unlock(p);
 	ao2_ref(p, -1);
@@ -928,6 +776,7 @@
 	ast_channel_language_set(chan, ast_channel_language(owner));
 	ast_channel_accountcode_set(chan, ast_channel_accountcode(owner));
 	ast_channel_musicclass_set(chan, ast_channel_musicclass(owner));
+	ast_cdr_update(chan);
 
 	ast_channel_cc_params_init(chan, ast_channel_get_cc_config_params(owner));
 
@@ -1191,9 +1040,6 @@
 					"to use the 'j' option to enable the jitterbuffer\n");
 			}
 		}
-		if (strchr(opts, 'b')) {
-			ast_set_flag(tmp, LOCAL_BRIDGE);
-		}
 		if (strchr(opts, 'm')) {
 			ast_set_flag(tmp, LOCAL_MOH_PASSTHRU);
 		}

Modified: team/mjordan/cdrs-of-doom/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/mjordan/cdrs-of-doom/include/asterisk/bridging.h?view=diff&rev=386309&r1=386308&r2=386309
==============================================================================
--- team/mjordan/cdrs-of-doom/include/asterisk/bridging.h (original)
+++ team/mjordan/cdrs-of-doom/include/asterisk/bridging.h Mon Apr 22 13:16:33 2013
@@ -1,8 +1,9 @@
 /*
  * Asterisk -- An open source telephony toolkit.
  *
- * Copyright (C) 2007 - 2009, Digium, Inc.
- *
+ * Copyright (C) 2007 - 2009, 2013 Digium, Inc.
+ *
+ * Richard Mudgett <rmudgett at digium.com>
  * Joshua Colp <jcolp at digium.com>
  *
  * See http://www.asterisk.org for more information about
@@ -16,10 +17,16 @@
  * at the top of the source tree.
  */
 
-/*! \file
+/*!
+ * \file
  * \brief Channel Bridging API
+ *
+ * \author Richard Mudgett <rmudgett at digium.com>
  * \author Joshua Colp <jcolp at digium.com>
  * \ref AstBridging
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
  */
 
 /*!
@@ -63,54 +70,41 @@
 #endif
 
 #include "asterisk/bridging_features.h"
+#include "asterisk/bridging_roles.h"
 #include "asterisk/dsp.h"
+#include "asterisk/uuid.h"
 
 /*! \brief Capabilities for a bridge technology */
 enum ast_bridge_capability {
+	/*! Bridge technology can service calls on hold */
+	AST_BRIDGE_CAPABILITY_HOLDING = (1 << 0),
+	/*! Bridge waits for channel to answer.  Passes early media. */
+	AST_BRIDGE_CAPABILITY_EARLY = (1 << 1),
+	/*! Bridge should natively bridge two channels if possible */
+	AST_BRIDGE_CAPABILITY_NATIVE = (1 << 2),
 	/*! Bridge is only capable of mixing 2 channels */
-	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 1),
+	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 3),
 	/*! Bridge is capable of mixing 2 or more channels */
-	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 2),
-	/*! Bridge should natively bridge two channels if possible */
-	AST_BRIDGE_CAPABILITY_NATIVE = (1 << 3),
-	/*! Bridge should run using the multithreaded model */
-	AST_BRIDGE_CAPABILITY_MULTITHREADED = (1 << 4),
-	/*! Bridge should run a central bridge thread */
-	AST_BRIDGE_CAPABILITY_THREAD = (1 << 5),
-	/*! Bridge technology can do video mixing (or something along those lines) */
-	AST_BRIDGE_CAPABILITY_VIDEO = (1 << 6),
-	/*! Bridge technology can optimize things based on who is talking */
-	AST_BRIDGE_CAPABILITY_OPTIMIZE = (1 << 7),
+	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 4),
 };
 
 /*! \brief State information about a bridged channel */
 enum ast_bridge_channel_state {
 	/*! Waiting for a signal (Channel in the bridge) */
 	AST_BRIDGE_CHANNEL_STATE_WAIT = 0,
-	/*! Bridged channel has ended itself (it has hung up) */
+	/*! Bridged channel was forced out and should be hung up (Bridge may dissolve.) */
 	AST_BRIDGE_CHANNEL_STATE_END,
 	/*! Bridged channel was forced out and should be hung up */
 	AST_BRIDGE_CHANNEL_STATE_HANGUP,
-	/*! Bridged channel was ast_bridge_depart() from the bridge without being hung up */
-	AST_BRIDGE_CHANNEL_STATE_DEPART,
-	/*! Bridged channel is executing a feature hook */
-	AST_BRIDGE_CHANNEL_STATE_FEATURE,
-	/*! Bridged channel is sending a DTMF stream out */
-	AST_BRIDGE_CHANNEL_STATE_DTMF,
-	/*! Bridged channel began talking */
-	AST_BRIDGE_CHANNEL_STATE_START_TALKING,
-	/*! Bridged channel has stopped talking */
-	AST_BRIDGE_CHANNEL_STATE_STOP_TALKING,
 };
 
-/*! \brief Return values for bridge technology write function */
-enum ast_bridge_write_result {
-	/*! Bridge technology wrote out frame fine */
-	AST_BRIDGE_WRITE_SUCCESS = 0,
-	/*! Bridge technology attempted to write out the frame but failed */
-	AST_BRIDGE_WRITE_FAILED,
-	/*! Bridge technology does not support writing out a frame of this type */
-	AST_BRIDGE_WRITE_UNSUPPORTED,
+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;
@@ -136,6 +130,7 @@
  * \brief Structure that contains information regarding a channel in a bridge
  */
 struct ast_bridge_channel {
+/* BUGBUG cond is only here because of external party suspend/unsuspend support. */
 	/*! Condition, used if we want to wake up a thread waiting on the bridged channel */
 	ast_cond_t cond;
 	/*! Current bridged channel state */
@@ -144,29 +139,99 @@
 	struct ast_channel *chan;
 	/*! Asterisk channel we are swapping with (if swapping) */
 	struct ast_channel *swap;
-	/*! Bridge this channel is participating in */
+	/*!
+	 * \brief Bridge this channel is participating in
+	 *
+	 * \note The bridge pointer cannot change while the bridge or
+	 * bridge_channel is locked.
+	 */
 	struct ast_bridge *bridge;
-	/*! Private information unique to the bridge technology */
+	/*!
+	 * \brief Bridge class private channel data.
+	 *
+	 * \note This information is added when the channel is pushed
+	 * into the bridge and removed when it is pulled from the
+	 * bridge.
+	 */
 	void *bridge_pvt;
-	/*! Thread handling the bridged channel */
+	/*!
+	 * \brief Private information unique to the bridge technology.
+	 *
+	 * \note This information is added when the channel joins the
+	 * bridge's technology and removed when it leaves the bridge's
+	 * technology.
+	 */
+	void *tech_pvt;
+	/*! Thread handling the bridged channel (Needed by ast_bridge_depart) */
 	pthread_t thread;
-	/*! Additional file descriptors to look at */
-	int fds[4];
-	/*! Bit to indicate whether the channel is suspended from the bridge or not */
+	/* v-- These flags change while the bridge is locked or before the channel is in the bridge. */
+	/*! TRUE if the channel is in a bridge. */
+	unsigned int in_bridge:1;
+	/*! TRUE if the channel just joined the bridge. */
+	unsigned int just_joined:1;
+	/*! TRUE if the channel is suspended from the bridge. */
 	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 channel must wait for an ast_bridge_depart to reclaim the channel. */
+	unsigned int depart_wait:1;
+	/* ^-- These flags change while the bridge is locked or before the channel is in the bridge. */
 	/*! Features structure for features that are specific to this channel */
 	struct ast_bridge_features *features;
 	/*! Technology optimization parameters used by bridging technologies capable of
 	 *  optimizing based upon talk detection. */
 	struct ast_bridge_tech_optimizations tech_args;
-	/*! Queue of DTMF digits used for DTMF streaming */
-	char dtmf_stream_q[8];
+	/*! Copy of read format used by chan before join */
+	struct ast_format read_format;
+	/*! Copy of write format used by chan before join */
+	struct ast_format write_format;
 	/*! Call ID associated with bridge channel */
 	struct ast_callid *callid;
+	/*! A clone of the roles living on chan when the bridge channel joins the bridge. This may require some opacification */
+	struct bridge_roles_datastore *bridge_roles;
 	/*! Linked list information */
 	AST_LIST_ENTRY(ast_bridge_channel) entry;
+	/*! Queue of outgoing frames to the channel. */
+	AST_LIST_HEAD_NOLOCK(, ast_frame) wr_queue;
+	/*! Pipe to alert thread when frames are put into the wr_queue. */
+	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 {
+	/*! Bridged channel is to detect a feature hook */
+	AST_BRIDGE_ACTION_FEATURE,
+	/*! Bridged channel is to act on an interval hook */
+	AST_BRIDGE_ACTION_INTERVAL,
+	/*! Bridged channel is to send a DTMF stream out */
+	AST_BRIDGE_ACTION_DTMF_STREAM,
+	/*! Bridged channel is to indicate talking start */
+	AST_BRIDGE_ACTION_TALKING_START,
+	/*! Bridged channel is to indicate talking stop */
+	AST_BRIDGE_ACTION_TALKING_STOP,
+	/*! Bridge channel is to play the indicated sound file. */
+	AST_BRIDGE_ACTION_PLAY_FILE,
+	/*! Bridge channel is to run the indicated application. */
+	AST_BRIDGE_ACTION_RUN_APP,
+
+	/*
+	 * Bridge actions put after this comment must never be put onto
+	 * the bridge_channel wr_queue because they have other resources
+	 * that must be freed.
+	 */
+
+	/*! Bridge reconfiguration deferred technology destruction. */
+	AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY = 1000,
+	/*! Bridge deferred dissolving. */
+	AST_BRIDGE_ACTION_DEFERRED_DISSOLVING,
 };
 
 enum ast_bridge_video_mode_type {
@@ -207,13 +272,151 @@
 };
 
 /*!
+ * \brief Destroy the bridge.
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_destructor_fn)(struct ast_bridge *self);
+
+/*!
+ * \brief The bridge is being dissolved.
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \details

[... 6668 lines stripped ...]



More information about the asterisk-commits mailing list