[svn-commits] rmudgett: branch group/bridge_construction r381657 - in /team/group/bridge_co...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Mon Feb 18 14:01:03 CST 2013


Author: rmudgett
Date: Mon Feb 18 14:00:59 2013
New Revision: 381657

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=381657
Log:
Bridge API enhancements: Refactor callers of ast_bridge_call to use Bridging API model

Merge work accomplishing this from team/rmudgett/bridge_phase.

* Callers of ast_bridge_call() do not access peer after the function
returns.

* A datastore on the channel is used to cause the channel to execute
dialplan at a specified location when the channel leaves the bridge.  This
is called an after bridge goto location.

* A bridge can now destroy itself when the last channel leaves the bridge
and there are no external references to the bridge.

* Disolved bridges now no longer allow any more channels to join.  These
late commer channels are immediately bounced back out of the bridge.

(closes issue ASTERISK-21051)
Reported by: Matt Jordan

Modified:
    team/group/bridge_construction/apps/app_channelredirect.c
    team/group/bridge_construction/apps/app_confbridge.c
    team/group/bridge_construction/apps/app_dial.c
    team/group/bridge_construction/apps/app_followme.c
    team/group/bridge_construction/apps/app_queue.c
    team/group/bridge_construction/bridges/bridge_builtin_features.c
    team/group/bridge_construction/funcs/func_channel.c
    team/group/bridge_construction/include/asterisk/bridging.h
    team/group/bridge_construction/include/asterisk/bridging_features.h
    team/group/bridge_construction/include/asterisk/bridging_technology.h
    team/group/bridge_construction/include/asterisk/channel.h
    team/group/bridge_construction/main/bridging.c
    team/group/bridge_construction/main/channel_internal_api.c
    team/group/bridge_construction/main/features.c
    team/group/bridge_construction/main/manager.c
    team/group/bridge_construction/main/pbx.c

Modified: team/group/bridge_construction/apps/app_channelredirect.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/app_channelredirect.c?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/apps/app_channelredirect.c (original)
+++ team/group/bridge_construction/apps/app_channelredirect.c Mon Feb 18 14:00:59 2013
@@ -96,10 +96,6 @@
 		return 0;
 	}
 
-	if (ast_channel_pbx(chan2)) {
-		ast_set_flag(ast_channel_flags(chan2), AST_FLAG_BRIDGE_HANGUP_DONT); /* don't let the after-bridge code run the h-exten */
-	}
-
 	res = ast_async_parseable_goto(chan2, args.label);
 
 	chan2 = ast_channel_unref(chan2);

Modified: team/group/bridge_construction/apps/app_confbridge.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/app_confbridge.c?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/apps/app_confbridge.c (original)
+++ team/group/bridge_construction/apps/app_confbridge.c Mon Feb 18 14:00:59 2013
@@ -627,7 +627,7 @@
 		chan = ast_channel_ref(conference_bridge->record_chan);
 		ast_answer(chan);
 		pbx_exec(chan, mixmonapp, ast_str_buffer(filename));
-		ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL);
+		ast_bridge_join(conference_bridge->bridge, chan, NULL, NULL, NULL, 0);
 
 		ast_hangup(chan); /* This will eat this thread's reference to the channel as well */
 		/* STOP has been called. Wait for either a START or an EXIT */
@@ -1751,7 +1751,8 @@
 		chan,
 		NULL,
 		&conference_bridge_user.features,
-		&conference_bridge_user.tech_args);
+		&conference_bridge_user.tech_args,
+		0);
 	send_leave_event(conference_bridge_user.chan, conference_bridge->name);
 
 	/* if we're shutting down, don't attempt to do further processing */

Modified: team/group/bridge_construction/apps/app_dial.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/app_dial.c?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/apps/app_dial.c (original)
+++ team/group/bridge_construction/apps/app_dial.c Mon Feb 18 14:00:59 2013
@@ -67,6 +67,7 @@
 #include "asterisk/ccss.h"
 #include "asterisk/indications.h"
 #include "asterisk/framehook.h"
+#include "asterisk/bridging.h"
 
 /*** DOCUMENTATION
 	<application name="Dial" language="en_US">
@@ -2054,6 +2055,40 @@
 	return res;
 }
 
+/*!
+ * \internal
+ * \brief Setup the after bridge goto location on the peer.
+ * \since 12.0.0
+ *
+ * \param chan Calling channel for bridge.
+ * \param peer Peer channel for bridge.
+ * \param opts Dialing option flags.
+ * \param opt_args Dialing option argument strings.
+ *
+ * \return Nothing
+ */
+static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags64 *opts, char *opt_args[])
+{
+	const char *context;
+	const char *extension;
+	int priority;
+
+	if (ast_test_flag64(opts, OPT_PEER_H)) {
+		ast_channel_lock(chan);
+		context = ast_strdupa(ast_channel_context(chan));
+		ast_channel_unlock(chan);
+		ast_after_bridge_set_h(peer, context);
+	} else if (ast_test_flag64(opts, OPT_CALLEE_GO_ON)) {
+		ast_channel_lock(chan);
+		context = ast_strdupa(ast_channel_context(chan));
+		extension = ast_strdupa(ast_channel_exten(chan));
+		priority = ast_channel_priority(chan);
+		ast_channel_unlock(chan);
+		ast_after_bridge_set_go_on(peer, context, extension, priority,
+			opt_args[OPT_ARG_CALLEE_GO_ON]);
+	}
+}
+
 static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast_flags64 *peerflags, int *continue_exec)
 {
 	int res = -1; /* default: error */
@@ -2989,6 +3024,14 @@
 		}
 
 		if (res) { /* some error */
+			if (!ast_check_hangup(chan) && ast_check_hangup(peer)) {
+				ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
+			}
+			setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
+			if (ast_after_bridge_goto_setup(peer)
+				|| ast_pbx_start(peer)) {
+				ast_autoservice_chan_hangup_peer(chan, peer);
+			}
 			res = -1;
 		} else {
 			if (ast_test_flag64(peerflags, OPT_CALLEE_TRANSFER))
@@ -3011,8 +3054,6 @@
 				ast_set_flag(&(config.features_callee), AST_FEATURE_AUTOMIXMON);
 			if (ast_test_flag64(peerflags, OPT_CALLER_MIXMONITOR))
 				ast_set_flag(&(config.features_caller), AST_FEATURE_AUTOMIXMON);
-			if (ast_test_flag64(peerflags, OPT_GO_ON))
-				ast_set_flag(&(config.features_caller), AST_FEATURE_NO_H_EXTEN);
 
 			config.end_bridge_callback = end_bridge_callback;
 			config.end_bridge_callback_data = chan;
@@ -3044,39 +3085,10 @@
 
 				ast_channel_setoption(chan, AST_OPTION_OPRMODE, &oprmode, sizeof(oprmode), 0);
 			}
-/* BUGBUG need to determine where peer is going to execute on bridge completion. */
+/* BUGBUG bridge needs to set hangup cause on chan when peer breaks the bridge. */
+			setup_peer_after_bridge_goto(chan, peer, &opts, opt_args);
 			res = ast_bridge_call(chan, peer, &config);
 		}
-
-		ast_channel_context_set(peer, ast_channel_context(chan));
-
-		if (ast_test_flag64(&opts, OPT_PEER_H)
-			&& ast_exists_extension(peer, ast_channel_context(peer), "h", 1,
-				S_COR(ast_channel_caller(peer)->id.number.valid, ast_channel_caller(peer)->id.number.str, NULL))) {
-			ast_autoservice_start(chan);
-			ast_pbx_h_exten_run(peer, ast_channel_context(peer));
-			ast_autoservice_stop(chan);
-		}
-		if (!ast_check_hangup(peer)) {
-			if (ast_test_flag64(&opts, OPT_CALLEE_GO_ON)) {
-				int goto_res;
-
-				if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
-					ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
-					goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
-				} else { /* F() */
-					goto_res = ast_goto_if_exists(peer, ast_channel_context(chan),
-						ast_channel_exten(chan), ast_channel_priority(chan) + 1);
-				}
-				if (!goto_res && !ast_pbx_start(peer)) {
-					/* The peer is now running its own PBX. */
-					goto out;
-				}
-			}
-		} else if (!ast_check_hangup(chan)) {
-			ast_channel_hangupcause_set(chan, ast_channel_hangupcause(peer));
-		}
-		ast_autoservice_chan_hangup_peer(chan, peer);
 	}
 out:
 	if (moh) {

Modified: team/group/bridge_construction/apps/app_followme.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/app_followme.c?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/apps/app_followme.c (original)
+++ team/group/bridge_construction/apps/app_followme.c Mon Feb 18 14:00:59 2013
@@ -1520,7 +1520,6 @@
 		}
 
 		res = ast_bridge_call(caller, outbound, &config);
-		ast_autoservice_chan_hangup_peer(caller, outbound);
 	}
 
 outrun:

Modified: team/group/bridge_construction/apps/app_queue.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/app_queue.c?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/apps/app_queue.c (original)
+++ team/group/bridge_construction/apps/app_queue.c Mon Feb 18 14:00:59 2013
@@ -106,6 +106,7 @@
 #include "asterisk/cel.h"
 #include "asterisk/data.h"
 #include "asterisk/term.h"
+#include "asterisk/bridging.h"
 
 /* Define, to debug reference counts on queues, without debugging reference counts on queue members */
 /* #define REF_DEBUG_ONLY_QUEUES */
@@ -4922,6 +4923,7 @@
 	TRANSFER
 };
 
+#if 0	// BUGBUG
 /*! \brief Send out AMI message with member call completion status information */
 static void send_agent_complete(const struct queue_ent *qe, const char *queuename,
 	const struct ast_channel *peer, const struct member *member, time_t callstart,
@@ -4985,6 +4987,7 @@
 		(long)(callstart - qe->start), (long)(time(NULL) - callstart), reason,
 		qe->parent->eventwhencalled == QUEUE_EVENT_VARIABLES ? vars2manager(qe->chan, vars, vars_len) : "");
 }
+#endif	// BUGBUG
 
 struct queue_transfer_ds {
 	struct queue_ent *qe;
@@ -5039,6 +5042,7 @@
 	}
 }
 
+#if 0	// BUGBUG
 /*! \brief mechanism to tell if a queue caller was atxferred by a queue member.
  *
  * When a caller is atxferred, then the queue_transfer_info datastore
@@ -5051,6 +5055,7 @@
 {
 	return ast_channel_datastore_find(chan, &queue_transfer_info, NULL) ? 0 : 1;
 }
+#endif	// BUGBUG
 
 /*! \brief create a datastore for storing relevant info to log attended transfers in the queue_log
  */
@@ -5105,6 +5110,35 @@
 		set_queue_variables(q, chan);
 		/* This unrefs the reference we made in try_calling when we allocated qeb */
 		queue_t_unref(q, "Expire bridge_config reference");
+	}
+}
+
+/*!
+ * \internal
+ * \brief Setup the after bridge goto location on the peer.
+ * \since 12.0.0
+ *
+ * \param chan Calling channel for bridge.
+ * \param peer Peer channel for bridge.
+ * \param opts Dialing option flags.
+ * \param opt_args Dialing option argument strings.
+ *
+ * \return Nothing
+ */
+static void setup_peer_after_bridge_goto(struct ast_channel *chan, struct ast_channel *peer, struct ast_flags *opts, char *opt_args[])
+{
+	const char *context;
+	const char *extension;
+	int priority;
+
+	if (ast_test_flag(opts, OPT_CALLEE_GO_ON)) {
+		ast_channel_lock(chan);
+		context = ast_strdupa(ast_channel_context(chan));
+		extension = ast_strdupa(ast_channel_exten(chan));
+		priority = ast_channel_priority(chan);
+		ast_channel_unlock(chan);
+		ast_after_bridge_set_go_on(peer, context, extension, priority,
+			opt_args[OPT_ARG_CALLEE_GO_ON]);
 	}
 }
 
@@ -5138,7 +5172,7 @@
  * \param[in] gosub the gosub passed as the seventh parameter to the Queue() application
  * \param[in] ringing 1 if the 'r' option is set, otherwise 0
  */
-static int try_calling(struct queue_ent *qe, const struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
+static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_args, char *announceoverride, const char *url, int *tries, int *noption, const char *agi, const char *macro, const char *gosub, int ringing)
 {
 	struct member *cur;
 	struct callattempt *outgoing = NULL; /* the list of calls we are building */
@@ -5210,9 +5244,6 @@
 	if (ast_test_flag(&opts, OPT_CALLER_AUTOMON)) {
 		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_AUTOMON);
 	}
-	if (ast_test_flag(&opts, OPT_GO_ON)) {
-		ast_set_flag(&(bridge_config.features_caller), AST_FEATURE_NO_H_EXTEN);
-	}
 	if (ast_test_flag(&opts, OPT_DATA_QUALITY)) {
 		nondataquality = 0;
 	}
@@ -5447,11 +5478,6 @@
 			}
 		}
 	} else { /* peer is valid */
-		/* These variables are used with the F option without arguments (callee jumps to next priority after queue) */
-		char *caller_context;
-		char *caller_extension;
-		int caller_priority;
-
 		/* Ah ha!  Someone answered within the desired timeframe.  Of course after this
 		   we will always return with -1 so that it is hung up properly after the
 		   conversation.  */
@@ -5605,11 +5631,8 @@
 		set_queue_variables(qe->parent, qe->chan);
 		set_queue_variables(qe->parent, peer);
 
+		setup_peer_after_bridge_goto(qe->chan, peer, &opts, opt_args);
 		ast_channel_lock(qe->chan);
-		/* Copy next destination data for 'F' option (no args) */
-		caller_context = ast_strdupa(ast_channel_context(qe->chan));
-		caller_extension = ast_strdupa(ast_channel_exten(qe->chan));
-		caller_priority = ast_channel_priority(qe->chan);
 		if ((monitorfilename = pbx_builtin_getvar_helper(qe->chan, "MONITOR_FILENAME"))) {
 				monitorfilename = ast_strdupa(monitorfilename);
 		}
@@ -5891,9 +5914,10 @@
 
 		time(&callstart);
 		transfer_ds = setup_transfer_datastore(qe, member, callstart, callcompletedinsl);
-/* BUGBUG need to determine where peer is going to execute on bridge completion. */
 		bridge = ast_bridge_call(qe->chan, peer, &bridge_config);
 
+/* BUGBUG need to do this queue logging a different way because we cannot reference peer anymore.  Likely needs to be made a subscriber of stasis transfer events. */
+#if 0	// BUGBUG
 		/* If the queue member did an attended transfer, then the TRANSFER already was logged in the queue_log
 		 * when the masquerade occurred. These other "ending" queue_log messages are unnecessary, except for
 		 * the AgentComplete manager event
@@ -5928,26 +5952,10 @@
 			/* We already logged the TRANSFER on the queue_log, but we still need to send the AgentComplete event */
 			send_agent_complete(qe, queuename, peer, member, callstart, vars, sizeof(vars), TRANSFER);
 		}
+#endif	// BUGBUG
 
 		if (transfer_ds) {
 			ast_datastore_free(transfer_ds);
-		}
-
-		if (!ast_check_hangup(peer) && ast_test_flag(&opts, OPT_CALLEE_GO_ON)) {
-			int goto_res;
-
-			if (!ast_strlen_zero(opt_args[OPT_ARG_CALLEE_GO_ON])) {
-				ast_replace_subargument_delimiter(opt_args[OPT_ARG_CALLEE_GO_ON]);
-				goto_res = ast_parseable_goto(peer, opt_args[OPT_ARG_CALLEE_GO_ON]);
-			} else { /* F() */
-				goto_res = ast_goto_if_exists(peer, caller_context, caller_extension,
-					caller_priority + 1);
-			}
-			if (goto_res || ast_pbx_start(peer)) {
-				ast_autoservice_chan_hangup_peer(qe->chan, peer);
-			}
-		} else {
-			ast_autoservice_chan_hangup_peer(qe->chan, peer);
 		}
 
 		res = bridge ? bridge : 1;

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=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/bridges/bridge_builtin_features.c (original)
+++ team/group/bridge_construction/bridges/bridge_builtin_features.c Mon Feb 18 14:00:59 2013
@@ -246,7 +246,7 @@
 /* BUGBUG we need to wait for Party C (peer) to answer before dumping into the transient B-C bridge. */
 
 	/* Create a bridge to use to talk to the person we are calling */
-	attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE,
+	attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX,
 		AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
 	if (!attended_bridge) {
 		ast_hangup(peer);
@@ -279,7 +279,7 @@
 		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);
+	attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL, 0);
 
 	/* Wait for peer thread to exit bridge and die. */
 	if (!ast_autoservice_start(bridge_channel->chan)) {

Modified: team/group/bridge_construction/funcs/func_channel.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/funcs/func_channel.c?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/funcs/func_channel.c (original)
+++ team/group/bridge_construction/funcs/func_channel.c Mon Feb 18 14:00:59 2013
@@ -329,6 +329,11 @@
 	</function>
  ***/
 
+/*
+ * BUGBUG add CHANNEL(after_bridge_goto)=<parseable-goto> Sets an after bridge goto datastore property on the channel.
+ * CHANNEL(after_bridge_goto)=<empty> Deletes any after bridge goto datastore property on the channel.
+ */
+
 #define locked_copy_string(chan, dest, source, len) \
 	do { \
 		ast_channel_lock(chan); \

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=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Mon Feb 18 14:00:59 2013
@@ -67,12 +67,12 @@
 
 /*! \brief Capabilities for a bridge technology */
 enum ast_bridge_capability {
+	/*! Bridge should natively bridge two channels if possible */
+	AST_BRIDGE_CAPABILITY_NATIVE = (1 << 1),
 	/*! Bridge is only capable of mixing 2 channels */
-	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 1),
+	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 2),
 	/*! 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),
+	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 3),
 	/*! Bridge should run using the multithreaded model */
 	AST_BRIDGE_CAPABILITY_MULTITHREADED = (1 << 4),
 	/*! Bridge should run a central bridge thread */
@@ -87,7 +87,7 @@
 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,
@@ -230,12 +230,14 @@
 	 * for bridge technologies that mix audio. When set to 0, the bridge tech must choose a
 	 * default interval for itself. */
 	unsigned int internal_mixing_interval;
-	/*! Bit to indicate that the bridge thread is waiting on channels in the bridge array */
+	/*! TRUE if the bridge thread is waiting on channels in the bridge array */
 	unsigned int waiting:1;
-	/*! Bit to indicate the bridge thread should stop */
+	/*! TRUE if the bridge thread should stop */
 	unsigned int stop:1;
-	/*! Bit to indicate the bridge thread should refresh itself */
+	/*! TRUE if the bridge thread should refresh itself */
 	unsigned int refresh:1;
+	/*! TRUE if the bridge has been dissolved.  Any channel that now tries to join is immediately ejected. */
+	unsigned int dissolved:1;
 	/*! Bridge flags to tweak behavior */
 	struct ast_flags feature_flags;
 	/*! Bridge technology that is handling the bridge */
@@ -352,13 +354,14 @@
  * \param swap Channel to swap out if swapping
  * \param features Bridge features structure
  * \param tech_args Optional Bridging tech optimization parameters for this channel.
+ * \param pass_reference TRUE if the bridge reference is being passed by the caller.
  *
  * \retval state that channel exited the bridge with
  *
  * Example usage:
  *
  * \code
- * ast_bridge_join(bridge, chan, NULL, NULL);
+ * ast_bridge_join(bridge, chan, NULL, NULL, NULL, 0);
  * \endcode
  *
  * This adds a channel pointed to by the chan pointer to the bridge pointed to by
@@ -376,7 +379,8 @@
 	struct ast_channel *chan,
 	struct ast_channel *swap,
 	struct ast_bridge_features *features,
-	struct ast_bridge_tech_optimizations *tech_args);
+	struct ast_bridge_tech_optimizations *tech_args,
+	int pass_reference);
 
 /*!
  * \brief Impart (non-blocking) a channel onto a bridge
@@ -647,6 +651,100 @@
  */
 void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan);
 
+/*!
+ * \brief Set channel to goto specific location after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Context to goto after bridge.
+ * \param exten Exten to goto after bridge.
+ * \param priority Priority to goto after bridge.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_set_goto(struct ast_channel *chan, const char *context, const char *exten, int priority);
+
+/*!
+ * \brief Set channel to run the h exten after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Context to goto after bridge.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_set_h(struct ast_channel *chan, const char *context);
+
+/*!
+ * \brief Set channel to go on in the dialplan after the bridge.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ * \param context Current context of the caller channel.
+ * \param exten Current exten of the caller channel.
+ * \param priority Current priority of the caller channel
+ * \param parseable_goto User specified goto string from dialplan.
+ *
+ * \details Add a channel datastore to setup the goto location
+ * when the channel leaves the bridge and run a PBX from there.
+ *
+ * If parseable_goto then use the given context/exten/priority
+ *   as the relative position for the parseable_goto.
+ * Else goto the given context/exten/priority+1.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_set_go_on(struct ast_channel *chan, const char *context, const char *exten, int priority, const char *parseable_goto);
+
+/*!
+ * \brief Setup any after bridge goto location to begin execution.
+ * \since 12.0.0
+ *
+ * \param chan Channel to setup after bridge goto location.
+ *
+ * \details Pull off any after bridge goto location datastore and
+ * setup for dialplan execution there.
+ *
+ * \retval 0 on success.  The goto location is set for a PBX to run it.
+ * \retval non-zero on error or no goto location.
+ *
+ * \note If the after bridge goto is set to run an h exten it is
+ * run here immediately.
+ */
+int ast_after_bridge_goto_setup(struct ast_channel *chan);
+
+/*!
+ * \brief Run a PBX on any after bridge goto location.
+ * \since 12.0.0
+ *
+ * \param chan Channel to execute after bridge goto location.
+ *
+ * \details Pull off any after bridge goto location datastore
+ * and run a PBX at that location.
+ *
+ * \note On return, the chan pointer is no longer valid because
+ * the channel has hung up.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_goto_run(struct ast_channel *chan);
+
+/*!
+ * \brief Discard channel after bridge goto location.
+ * \since 12.0.0
+ *
+ * \param chan Channel to discard after bridge goto location.
+ *
+ * \return Nothing
+ */
+void ast_after_bridge_goto_discard(struct ast_channel *chan);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

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=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_features.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_features.h Mon Feb 18 14:00:59 2013
@@ -30,12 +30,12 @@
 
 /*! \brief Flags used for bridge features */
 enum ast_bridge_feature_flags {
-	/*! Upon channel hangup the bridge should be ended. */
+	/*! Upon channel hangup all bridge participants should be kicked out. */
 	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),
+	/*! This channel leaves the bridge if all participants have this flag set. */
+	AST_BRIDGE_FLAG_LONELY = (1 << 2),
 };
 
 /*! \brief Built in DTMF features */

Modified: team/group/bridge_construction/include/asterisk/bridging_technology.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/bridging_technology.h?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_technology.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_technology.h Mon Feb 18 14:00:59 2013
@@ -73,7 +73,7 @@
 	int (*poke)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
 	/*! Formats that the bridge technology supports */
 	struct ast_format_cap *format_capabilities;
-	/*! Bit to indicate whether the bridge technology is currently suspended or not */
+	/*! TRUE if the bridge technology is currently suspended. */
 	unsigned int suspended:1;
 	/*! Module this bridge technology belongs to. Is used for reference counting when creating/destroying a bridge. */
 	struct ast_module *mod;

Modified: team/group/bridge_construction/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/include/asterisk/channel.h?view=diff&rev=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/include/asterisk/channel.h (original)
+++ team/group/bridge_construction/include/asterisk/channel.h Mon Feb 18 14:00:59 2013
@@ -895,10 +895,6 @@
 	 *  a message aimed at preventing a subsequent hangup exten being run at the pbx_run
 	 *  level */
 	AST_FLAG_BRIDGE_HANGUP_RUN = (1 << 17),
-	/*! This flag indicates that the hangup exten should NOT be run when the
-	 *  bridge terminates, this will allow the hangup in the pbx loop to be run instead.
-	 *  */
-	AST_FLAG_BRIDGE_HANGUP_DONT = (1 << 18),
 	/*! Disable certain workarounds.  This reintroduces certain bugs, but allows
 	 *  some non-traditional dialplans (like AGI) to continue to function.
 	 */
@@ -927,7 +923,6 @@
 	AST_FEATURE_AUTOMON =      (1 << 4),
 	AST_FEATURE_PARKCALL =     (1 << 5),
 	AST_FEATURE_AUTOMIXMON =   (1 << 6),
-	AST_FEATURE_NO_H_EXTEN =   (1 << 7),
 	AST_FEATURE_WARNING_ACTIVE = (1 << 8),
 };
 

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=381657&r1=381656&r2=381657
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Mon Feb 18 14:00:59 2013
@@ -45,6 +45,7 @@
 #include "asterisk/file.h"
 #include "asterisk/module.h"
 #include "asterisk/astobj2.h"
+#include "asterisk/pbx.h"
 #include "asterisk/test.h"
 
 #include "asterisk/heap.h"
@@ -52,6 +53,7 @@
 #include "asterisk/timing.h"
 #include "asterisk/stringfields.h"
 #include "asterisk/musiconhold.h"
+#include "asterisk/features.h"
 
 static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
 
@@ -137,6 +139,7 @@
 
 void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
 {
+/* BUGBUG need cause code for the bridge_channel leaving the bridge. */
 	ast_debug(1, "BUGBUG Setting bridge channel %p state from:%d to:%d\n",
 		bridge_channel, bridge_channel->state, new_state);
 
@@ -309,6 +312,9 @@
 {
 	struct ast_bridge_channel *bridge_channel;
 
+	bridge->dissolved = 1;
+
+/* BUGBUG need a cause code on the bridge for the later ejected channels. */
 	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
 		ao2_lock(bridge_channel);
 		switch (bridge_channel->state) {
@@ -322,7 +328,18 @@
 	}
 }
 
-/*! \brief Internal function to see whether a bridge should dissolve, and if so do it */
+/*!
+ * \internal
+ * \brief Check if a bridge should dissolve and then do it.
+ * \since 12.0.0
+ *
+ * \param bridge Bridge to check.
+ * \param bridge_channel Channel causing the check.
+ *
+ * \note On entry, bridge is already locked.
+ *
+ * \return Nothing
+ */
 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_HANGUP)
@@ -550,19 +567,25 @@
 	}
 }
 
-/*! \brief Bridge thread function */
+/*!
+ * \brief Bridge thread function
+ *
+ * \note The thread does not have its own reference to the
+ * bridge.  The bridge ao2 object destructor will stop the
+ * thread if it is running.
+ */
 static void *bridge_thread(void *data)
 {
 	struct ast_bridge *bridge = data;
 	int res = 0;
 
+	ast_debug(1, "Started bridge thread for %p\n", bridge);
+
 	ao2_lock(bridge);
 
 	if (bridge->callid) {
 		ast_callid_threadassoc_add(bridge->callid);
 	}
-
-	ast_debug(1, "Started bridge thread for %p\n", bridge);
 
 	/* Loop around until we are told to stop */
 	while (!bridge->stop) {
@@ -574,10 +597,6 @@
 
 		/* In case the refresh bit was set simply set it back to off */
 		bridge->refresh = 0;
-
-		ast_debug(1, "Launching bridge thread function %p for bridge %p\n",
-			bridge->technology->thread ? bridge->technology->thread : generic_thread_loop,
-			bridge);
 
 		/*
 		 * Execute the appropriate thread function.  If the technology
@@ -597,10 +616,9 @@
 		}
 	}
 
+	ao2_unlock(bridge);
+
 	ast_debug(1, "Ending bridge thread for %p\n", bridge);
-
-	ao2_unlock(bridge);
-	ao2_ref(bridge, -1);
 
 	return NULL;
 }
@@ -647,6 +665,16 @@
 	/* 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));
+
+	ao2_lock(bridge);
+	if (bridge->thread != AST_PTHREADT_NULL) {
+		bridge_stop(bridge);
+	}
+	ao2_unlock(bridge);
+
+	if (bridge->callid) {
+		bridge->callid = ast_callid_unref(bridge->callid);
+	}
 
 	/* Pass off the bridge to the technology to destroy if needed */
 	if (bridge->technology->destroy) {
@@ -746,23 +774,9 @@
 
 int ast_bridge_destroy(struct ast_bridge *bridge)
 {
+	ast_debug(1, "Telling all channels in bridge %p to leave the party\n", bridge);
 	ao2_lock(bridge);
-
-	if (bridge->callid) {
-/* BUGBUG the bridge callid needs to be verified. */
-		bridge->callid = ast_callid_unref(bridge->callid);
-	}
-
-	if (bridge->thread != AST_PTHREADT_NULL) {
-/* BUGBUG this needs to be moved to the last bridge_channel removal code if the bridge flag AST_BRIDGE_FLAG_DISSOLVE_EMPTY. */
-		bridge_stop(bridge);
-	}
-
-	ast_debug(1, "Telling all channels in bridge %p to leave the party\n", bridge);
-
-	/* Drop every bridged channel, the last one will cause the bridge thread (if it exists) to exit */
 	bridge_force_out_all(bridge);
-
 	ao2_unlock(bridge);
 
 	ao2_ref(bridge, -1);
@@ -996,7 +1010,7 @@
 		ao2_unlock(bridge_channel);
 		ao2_lock(bridge_channel->bridge);
 	} else if (bridge_channel->suspended) {
-		ast_debug(10, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n",
+		ast_debug(1, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n",
 			bridge_channel, bridge_channel->bridge);
 		ast_cond_wait(&bridge_channel->cond, ao2_object_get_lockaddr(bridge_channel));
 		ao2_unlock(bridge_channel);
@@ -1019,7 +1033,8 @@
 	ao2_unlock(bridge_channel->bridge);
 	ao2_lock(bridge_channel);
 	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-		ast_debug(1, "Going into a single threaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge);
+		ast_debug(1, "Going into a single threaded signal wait for bridge channel %p of bridge %p\n",
+			bridge_channel, bridge_channel->bridge);
 		ast_cond_wait(&bridge_channel->cond, ao2_object_get_lockaddr(bridge_channel));
 	}
 	ao2_unlock(bridge_channel);
@@ -1345,6 +1360,19 @@
 		}
 	}
 
+	if (bridge_channel->bridge->dissolved) {
+		/* Force out channel trying to join a dissolved bridge. */
+		ao2_lock(bridge_channel);
+		switch (bridge_channel->state) {
+		case AST_BRIDGE_CHANNEL_STATE_WAIT:
+			ast_bridge_change_state_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+			break;
+		default:
+			break;
+		}
+		ao2_unlock(bridge_channel);
+	}
+
 	/* Actually execute the respective threading model, and keep our bridge thread alive */
 	while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
 		/* Update bridge pointer on channel */
@@ -1361,10 +1389,8 @@
 		if (bridge_channel->bridge->thread == AST_PTHREADT_NULL
 			&& (bridge_channel->bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
 			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)) {
 				ast_debug(1, "Failed to create a bridge thread for bridge %p, giving it another go.\n", bridge_channel->bridge);
-				ao2_ref(bridge_channel->bridge, -1);
 				continue;
 			}
 		}
@@ -1407,8 +1433,6 @@
 		ao2_unlock(bridge_channel);
 	}
 
-	ast_channel_internal_bridge_set(bridge_channel->chan, NULL);
-
 	/* See if we need to dissolve the bridge itself if they hung up */
 	switch (bridge_channel->state) {
 	case AST_BRIDGE_CHANNEL_STATE_END:
@@ -1459,6 +1483,25 @@
 	}
 	ao2_unlock(bridge_channel);
 
+/* BUGBUG Revisit in regards to moving channels between bridges and local channel optimization. */
+	/* Complete any partial DTMF digit before exiting the bridge. */
+	if (ast_channel_sending_dtmf_digit(bridge_channel->chan)) {
+		ast_bridge_end_dtmf(bridge_channel->chan,
+			ast_channel_sending_dtmf_digit(bridge_channel->chan),
+			ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end");
+	}
+
+	/*
+	 * Wait for any dual redirect to complete.
+	 *
+	 * Must be done while "still in the bridge" for ast_async_goto()
+	 * to work right.
+	 */
+	while (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) {
+		sched_yield();
+	}
+	ast_channel_internal_bridge_set(bridge_channel->chan, NULL);
+
 	/* Restore original formats of the channel as they came in */
 	if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &formats[0]) == AST_FORMAT_CMP_NOT_EQUAL) {
 		ast_debug(1, "Bridge is returning %p to read format %s(%d)\n", bridge_channel, ast_getformatname(&formats[0]), formats[0].id);
@@ -1506,18 +1549,303 @@
 	return bridge_channel;
 }
 
+struct after_bridge_goto_ds {
+	/*! Goto string that can be parsed by ast_parseable_goto(). */
+	const char *parseable_goto;
+	/*! Specific goto context or default context for parseable_goto. */
+	const char *context;
+	/*! Specific goto exten or default exten for parseable_goto. */
+	const char *exten;
+	/*! Specific goto priority or default priority for parseable_goto. */
+	int priority;
+	/*! TRUE if the peer should run the h exten. */
+	unsigned int run_h_exten:1;
+	/*! Specific goto location */
+	unsigned int specific:1;
+};
+
+/*!
+ * \internal
+ * \brief Destroy the after bridge goto datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge goto data to destroy.
+ *
+ * \return Nothing
+ */
+static void after_bridge_goto_destroy(void *data)
+{
+	struct after_bridge_goto_ds *after_bridge = data;
+
+	ast_free((char *) after_bridge->parseable_goto);
+	ast_free((char *) after_bridge->context);
+	ast_free((char *) after_bridge->exten);
+}
+
+/*!
+ * \internal
+ * \brief Fixup the after bridge goto datastore.
+ * \since 12.0.0
+ *
+ * \param data After bridge goto data to fixup.
+ * \param old_chan The datastore is moving from this channel.
+ * \param new_chan The datastore is moving to this channel.
+ *
+ * \return Nothing
+ */
+static void after_bridge_goto_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
+{
+	/* There can be only one.  Discard any already on the new channel. */
+	ast_after_bridge_goto_discard(new_chan);
+}
+
+static const struct ast_datastore_info after_bridge_goto_info = {
+	.type = "after-bridge-goto",
+	.destroy = after_bridge_goto_destroy,
+	.chan_fixup = after_bridge_goto_fixup,
+};
+
+/*!
+ * \internal
+ * \brief Remove channel goto location after the bridge and return it.
+ * \since 12.0.0
+ *
+ * \param chan Channel to remove after bridge goto location.
+ *
+ * \retval datastore on success.
+ * \retval NULL on error or not found.
+ */
+static struct ast_datastore *after_bridge_goto_remove(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+
+	ast_channel_lock(chan);
+	datastore = ast_channel_datastore_find(chan, &after_bridge_goto_info, NULL);
+	if (datastore && ast_channel_datastore_remove(chan, datastore)) {
+		datastore = NULL;
+	}
+	ast_channel_unlock(chan);
+
+	return datastore;
+}
+
+void ast_after_bridge_goto_discard(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+
+	datastore = after_bridge_goto_remove(chan);
+	if (datastore) {
+		ast_datastore_free(datastore);
+	}
+}
+
+int ast_after_bridge_goto_setup(struct ast_channel *chan)
+{
+	struct ast_datastore *datastore;
+	struct after_bridge_goto_ds *after_bridge;
+	int goto_failed = -1;
+
+	/* Determine if we are going to setup a dialplan location and where. */
+	if (ast_channel_softhangup_internal_flag(chan) & AST_SOFTHANGUP_ASYNCGOTO) {
+		/* An async goto has already setup a location. */
+		ast_channel_clear_softhangup(chan, AST_SOFTHANGUP_ASYNCGOTO);
+		if (!ast_check_hangup(chan)) {
+			goto_failed = 0;
+		}
+		return goto_failed;
+	}
+
+	/* Get after bridge goto datastore. */
+	datastore = after_bridge_goto_remove(chan);
+	if (!datastore) {
+		return goto_failed;
+	}
+
+	after_bridge = datastore->data;
+	if (after_bridge->run_h_exten) {
+		if (ast_exists_extension(chan, after_bridge->context, "h", 1,
+			S_COR(ast_channel_caller(chan)->id.number.valid,
+				ast_channel_caller(chan)->id.number.str, NULL))) {
+			ast_debug(1, "Running after bridge goto h exten %s,h,1\n",
+				ast_channel_context(chan));
+			ast_pbx_h_exten_run(chan, after_bridge->context);
+		}
+	} else if (!ast_check_hangup(chan)) {
+		if (after_bridge->specific) {
+			goto_failed = ast_explicit_goto(chan, after_bridge->context,
+				after_bridge->exten, after_bridge->priority);
+		} else if (!ast_strlen_zero(after_bridge->parseable_goto)) {
+			char *context;
+			char *exten;
+			int priority;
+
+			/* Option F(x) for Bridge(), Dial(), and Queue() */
+
+			/* Save current dialplan location in case of failure. */
+			context = ast_strdupa(ast_channel_context(chan));

[... 1200 lines stripped ...]



More information about the svn-commits mailing list