[asterisk-commits] kmoore: branch kmoore/stasis-bridging-channel_events r385167 - in /team/kmoor...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Apr 10 07:51:57 CDT 2013


Author: kmoore
Date: Wed Apr 10 07:51:52 2013
New Revision: 385167

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=385167
Log:
Multiple revisions 384991,385010,385034-385038,385051,385089,385124,385143

........
  r384991 | root | 2013-04-08 14:17:20 -0500 (Mon, 08 Apr 2013) | 7 lines
  
  Clean up Makefile "warning" clutter when makeopts doesn't exist.
  
  Review: https://reviewboard.asterisk.org/r/2304
  ........
  
  Merged revisions 384989 from file:///srv/subversion/repos/asterisk/trunk
........
  r385010 | rmudgett | 2013-04-08 15:04:41 -0500 (Mon, 08 Apr 2013) | 10 lines
  
  Add support for playing a file and running an application on a bridge channel.
  
  * Added AST_BRIDGE_ACTION_PLAY_FILE and AST_BRIDGE_ACTION_RUN_APP.
  
  * Refactored bridge write frame code to make it easier to put action and
  control frames into the bridge.
  
  * Refactored code to make it easier to put action frames onto the bridge
  channel write queue.
........
  r385034 | rmudgett | 2013-04-08 15:26:16 -0500 (Mon, 08 Apr 2013) | 1 line
  
  Make ast_bridge_channel_lock_bridge() more public.
........
  r385035 | rmudgett | 2013-04-08 15:32:35 -0500 (Mon, 08 Apr 2013) | 1 line
  
  Move bridge_channel wr_queue flushing to the destructor.
........
  r385036 | rmudgett | 2013-04-08 15:42:59 -0500 (Mon, 08 Apr 2013) | 2 lines
  
  Add hooks for successful bridge join and leave.
........
  r385037 | rmudgett | 2013-04-08 15:50:26 -0500 (Mon, 08 Apr 2013) | 1 line
  
  Add AST_BRIDGE_CAPABILITY_EARLY in anticipation of early bridge.
........
  r385038 | rmudgett | 2013-04-08 15:55:07 -0500 (Mon, 08 Apr 2013) | 1 line
  
  Make channel point back to the correct bridge_channel after atxfer.
........
  r385051 | root | 2013-04-08 19:17:19 -0500 (Mon, 08 Apr 2013) | 13 lines
  
  Modified the list of keys for the driver backends for sake of sample clarity
  
  Added a line showing the mapping of "mysql" to res_config_mysql available in add-ons. We used "mysql" as an example driver key in the sample, but didn't show what module it mapped too. Also added a subtitle above the list of keys for driver backends.
  ........
  
  Merged revisions 385047 from http://svn.asterisk.org/svn/asterisk/branches/1.8
  ........
  
  Merged revisions 385048 from http://svn.asterisk.org/svn/asterisk/branches/11
  ........
  
  Merged revisions 385049 from file:///srv/subversion/repos/asterisk/trunk
........
  r385089 | root | 2013-04-09 01:17:20 -0500 (Tue, 09 Apr 2013) | 11 lines
  
  Add inheritance support to FEATURE()/FEATUREMAP().
  
  The settings saved on the channel for FEATURE()/FEATUREMAP() were only
  for that channel.  This patch adds the ability to have these settings
  inherited to child channels if you set FEATURE(inherit)=yes.
  
  Review: https://reviewboard.asterisk.org/r/2415/
  ........
  
  Merged revisions 385088 from file:///srv/subversion/repos/asterisk/trunk
........
  r385124 | root | 2013-04-09 14:17:21 -0500 (Tue, 09 Apr 2013) | 9 lines
  
  Backported app_stasis fix from stasis-http branch.
  
  The hash and compare functions for the control container was reusing
  the wrong ones, causing some problems. I fixed it, but in the wrong
  branch. Oh well, it happens.
  ........
  
  Merged revisions 385116 from file:///srv/subversion/repos/asterisk/trunk
........
  r385143 | root | 2013-04-09 15:17:20 -0500 (Tue, 09 Apr 2013) | 7 lines
  
  Rename struct feature_ds to struct feature_datastore.
  
  Because "struct feature_ds *feature_ds" is not a good thing.
  ........
  
  Merged revisions 385142 from file:///srv/subversion/repos/asterisk/trunk
........

Merged revisions 384991,385010,385034-385038,385051,385089,385124,385143 from http://svn.asterisk.org/svn/asterisk/team/group/bridge_construction

Modified:
    team/kmoore/stasis-bridging-channel_events/   (props changed)
    team/kmoore/stasis-bridging-channel_events/apps/app_stasis.c
    team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_features.c
    team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_interval_features.c
    team/kmoore/stasis-bridging-channel_events/bridges/bridge_softmix.c
    team/kmoore/stasis-bridging-channel_events/configs/features.conf.sample
    team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging.h
    team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging_features.h
    team/kmoore/stasis-bridging-channel_events/main/bridging.c
    team/kmoore/stasis-bridging-channel_events/main/features.c

Propchange: team/kmoore/stasis-bridging-channel_events/
            ('bridge_construction-integrated' removed)

Propchange: team/kmoore/stasis-bridging-channel_events/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Wed Apr 10 07:51:52 2013
@@ -1,1 +1,1 @@
-/team/group/bridge_construction:1-384989
+/team/group/bridge_construction:1-385166

Modified: team/kmoore/stasis-bridging-channel_events/apps/app_stasis.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/apps/app_stasis.c?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/apps/app_stasis.c (original)
+++ team/kmoore/stasis-bridging-channel_events/apps/app_stasis.c Wed Apr 10 07:51:52 2013
@@ -35,6 +35,7 @@
 #include "asterisk/app_stasis.h"
 #include "asterisk/astobj2.h"
 #include "asterisk/channel.h"
+#include "asterisk/lock.h"
 #include "asterisk/module.h"
 #include "asterisk/stasis.h"
 #include "asterisk/stasis_message_router.h"
@@ -50,7 +51,8 @@
 				<para>Name of the application to invoke.</para>
 			</parameter>
 			<parameter name="args">
-				<para>Optional comma-delimited arguments for the application invocation.</para>
+				<para>Optional comma-delimited arguments for the
+				application invocation.</para>
 			</parameter>
 		</syntax>
 		<description>
@@ -223,7 +225,7 @@
 	 */
 	int continue_to_dialplan:1;
 	/*! Uniqueid of the associated channel */
-	char channel_uniqueid[];
+	char channel_id[];
 };
 
 static struct stasis_app_control *control_create(const char *uniqueid)
@@ -237,9 +239,33 @@
 		return NULL;
 	}
 
-	strncpy(control->channel_uniqueid, uniqueid, size - sizeof(*control));
+	strncpy(control->channel_id, uniqueid, size - sizeof(*control));
 
 	return control;
+}
+
+/*! AO2 hash function for \ref stasis_app_control */
+static int control_hash(const void *obj, const int flags)
+{
+	const struct stasis_app_control *control = obj;
+	const char *id = flags & OBJ_KEY ? obj : control->channel_id;
+
+	return ast_str_hash(id);
+}
+
+/*! AO2 comparison function for \ref stasis_app_control */
+static int control_compare(void *lhs, void *rhs, int flags)
+{
+	const struct stasis_app_control *lhs_control = lhs;
+	const struct stasis_app_control *rhs_control = rhs;
+	const char *rhs_name =
+		flags & OBJ_KEY ? rhs : rhs_control->channel_id;
+
+	if (strcmp(lhs_control->channel_id, rhs_name) == 0) {
+		return CMP_MATCH | CMP_STOP;
+	} else {
+		return 0;
+	}
 }
 
 struct stasis_app_control *stasis_app_control_find_by_channel(
@@ -635,7 +661,8 @@
 	}
 
 	controls = app_controls();
-	ao2_unlink_flags(controls, control, OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
+	ao2_unlink_flags(controls, control,
+			 OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
 	ao2_cleanup(control);
 }
 
@@ -668,7 +695,8 @@
 
 	app = ao2_find(apps, args.app_name, OBJ_KEY);
 	if (!app) {
-		ast_log(LOG_ERROR, "Stasis app '%s' not registered\n", args.app_name);
+		ast_log(LOG_ERROR,
+			"Stasis app '%s' not registered\n", args.app_name);
 		return -1;
 	}
 
@@ -686,7 +714,8 @@
 
 	res = send_start_msg(app, chan, args.argc - 1, args.app_argv);
 	if (res != 0) {
-		ast_log(LOG_ERROR, "Error sending start message to %s\n", args.app_name);
+		ast_log(LOG_ERROR,
+			"Error sending start message to %s\n", args.app_name);
 		return res;
 	}
 
@@ -698,14 +727,16 @@
 	while (!hungup && !control_continue_test_and_reset(control) && ast_waitfor(chan, -1) > -1) {
 		RAII_VAR(struct ast_frame *, f, ast_read(chan), ast_frame_dtor);
 		if (!f) {
-			ast_debug(3, "%s: No more frames. Must be done, I guess.\n", ast_channel_uniqueid(chan));
+			ast_debug(3, "%s: No more frames. Must be done, I guess.\n",
+				  ast_channel_uniqueid(chan));
 			break;
 		}
 
 		switch (f->frametype) {
 		case AST_FRAME_CONTROL:
 			if (f->subclass.integer == AST_CONTROL_HANGUP) {
-				ast_debug(3, "%s: Received hangup\n", ast_channel_uniqueid(chan));
+				ast_debug(3, "%s: Received hangup\n",
+					  ast_channel_uniqueid(chan));
 				hungup = 1;
 			}
 			break;
@@ -718,7 +749,8 @@
 	app_remove_channel(app, chan);
 	res = send_end_msg(app, chan);
 	if (res != 0) {
-		ast_log(LOG_ERROR, "Error sending end message to %s\n", args.app_name);
+		ast_log(LOG_ERROR,
+			"Error sending end message to %s\n", args.app_name);
 		return res;
 	}
 
@@ -733,10 +765,11 @@
 	app = ao2_find(apps, app_name, OBJ_KEY);
 
 	if (!app) {
-		/* XXX We can do a better job handling late binding, queueing up the call for a few seconds
-		 * to wait for the app to register.
+		/* XXX We can do a better job handling late binding, queueing up
+		 * the call for a few seconds to wait for the app to register.
 		 */
-		ast_log(LOG_WARNING, "Stasis app '%s' not registered\n", app_name);
+		ast_log(LOG_WARNING,
+			"Stasis app '%s' not registered\n", app_name);
 		return -1;
 	}
 
@@ -790,12 +823,14 @@
 {
 	int r = 0;
 
-	__apps_registry = ao2_container_alloc(APPS_NUM_BUCKETS, app_hash, app_compare);
+	__apps_registry =
+		ao2_container_alloc(APPS_NUM_BUCKETS, app_hash, app_compare);
 	if (__apps_registry == NULL) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
 
-	__app_controls = ao2_container_alloc(CONTROLS_NUM_BUCKETS, app_hash, app_compare);
+	__app_controls = ao2_container_alloc(CONTROLS_NUM_BUCKETS,
+					     control_hash, control_compare);
 	if (__app_controls == NULL) {
 		return AST_MODULE_LOAD_FAILURE;
 	}
@@ -829,6 +864,7 @@
 	return r;
 }
 
-AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS, "Stasis dialplan application",
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS,
+		"Stasis dialplan application",
 		.load = load_module,
 		.unload = unload_module);

Modified: team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_features.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_features.c?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_features.c (original)
+++ team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_features.c Wed Apr 10 07:51:52 2013
@@ -329,6 +329,18 @@
 	 */
 	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);

Modified: team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_interval_features.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_interval_features.c?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_interval_features.c (original)
+++ team/kmoore/stasis-bridging-channel_events/bridges/bridge_builtin_interval_features.c Wed Apr 10 07:51:52 2013
@@ -98,7 +98,13 @@
 		ast_stream_and_wait(bridge_channel->chan, file, AST_DIGIT_NONE);
 	}
 
-	/* It may be necessary to resume music on hold after we finish playing the announcment. */
+	/*
+	 * It may be necessary to resume music on hold after we finish
+	 * playing the announcment.
+	 *
+	 * XXX We have no idea what MOH class was in use before playing
+	 * the file.
+	 */
 	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
 		ast_moh_start(bridge_channel->chan, NULL, NULL);
 	}

Modified: team/kmoore/stasis-bridging-channel_events/bridges/bridge_softmix.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/bridges/bridge_softmix.c?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/bridges/bridge_softmix.c (original)
+++ team/kmoore/stasis-bridging-channel_events/bridges/bridge_softmix.c Wed Apr 10 07:51:52 2013
@@ -382,12 +382,7 @@
  */
 static void softmix_src_change(struct ast_bridge_channel *bridge_channel)
 {
-	struct ast_frame frame = {
-		.frametype = AST_FRAME_CONTROL,
-		.subclass.integer = AST_CONTROL_SRCCHANGE
-	};
-
-	ast_bridge_channel_queue_frame(bridge_channel, &frame);
+	ast_bridge_channel_queue_control_data(bridge_channel, AST_CONTROL_SRCCHANGE, NULL, 0);
 }
 
 /*! \brief Function called when a channel is joined into the bridge */

Modified: team/kmoore/stasis-bridging-channel_events/configs/features.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/configs/features.conf.sample?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/configs/features.conf.sample (original)
+++ team/kmoore/stasis-bridging-channel_events/configs/features.conf.sample Wed Apr 10 07:51:52 2013
@@ -157,7 +157,7 @@
 ;
 ;    Set(__DYNAMIC_FEATURES=myfeature1#myfeature2#myfeature3)
 ;
-; (Note: The two leading underscores allow these feature settings to be set on
+; (Note: The two leading underscores allow these feature settings to be set
 ;  on the outbound channels, as well.  Otherwise, only the original channel
 ;  will have access to these features.)
 ;

Modified: team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging.h?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging.h (original)
+++ team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging.h Wed Apr 10 07:51:52 2013
@@ -78,12 +78,14 @@
 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 << 1),
+	AST_BRIDGE_CAPABILITY_NATIVE = (1 << 2),
 	/*! Bridge is only capable of mixing 2 channels */
-	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 2),
+	AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 3),
 	/*! Bridge is capable of mixing 2 or more channels */
-	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 3),
+	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 4),
 };
 
 /*! \brief State information about a bridged channel */
@@ -188,6 +190,10 @@
 	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
@@ -784,6 +790,25 @@
 }
 
 /*!
+ * \brief Lock the bridge associated with the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel that wants to lock the bridge.
+ *
+ * \details
+ * This is an upstream lock operation.  The defined locking
+ * order is bridge then bridge_channel.
+ *
+ * \note On entry, neither the bridge nor bridge_channel is locked.
+ *
+ * \note The bridge_channel->bridge pointer changes because of a
+ * bridge-merge/channel-move operation between bridges.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel);
+
+/*!
  * \brief Set bridge channel state to leave bridge (if not leaving already) with no lock.
  *
  * \param bridge_channel Channel to change the state on
@@ -848,6 +873,173 @@
  * \note BUGBUG This may get moved.
  */
 int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr);
+
+/*!
+ * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel work with.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
+
+/*!
+ * \brief Queue an action frame onto the bridge channel with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
+
+/*!
+ * \brief Write an action frame into the bridge with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
+
+/*!
+ * \brief Queue a control frame onto the bridge channel with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param control Type of control frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen);
+
+/*!
+ * \brief Write a control frame into the bridge with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge.
+ * \param control Type of control frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen);
+
+/*!
+ * \brief Run an application on the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to run the application on.
+ * \param app_name Dialplan application name.
+ * \param app_args Arguments for the application. (NULL tolerant)
+ * \param moh_class MOH class to request bridge peers to hear while application is running.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
+
+/*!
+ * \brief Write a bridge action run application frame into the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge
+ * \param app_name Dialplan application name.
+ * \param app_args Arguments for the application. (NULL or empty for no arguments)
+ * \param moh_class MOH class to request bridge peers to hear while application is running.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
+
+/*!
+ * \brief Queue a bridge action run application frame onto the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to put the frame onto
+ * \param app_name Dialplan application name.
+ * \param app_args Arguments for the application. (NULL or empty for no arguments)
+ * \param moh_class MOH class to request bridge peers to hear while application is running.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
+
+/*!
+ * \brief Play a file on the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to play the file on
+ * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
+ * \param playfile Sound filename to play.
+ * \param moh_class MOH class to request bridge peers to hear while file is played.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, void (*custom_play)(const char *playfile), const char *playfile, const char *moh_class);
+
+/*!
+ * \brief Write a bridge action play file frame into the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge
+ * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
+ * \param playfile Sound filename to play.
+ * \param moh_class MOH class to request bridge peers to hear while file is played.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, void (*custom_play)(const char *playfile), const char *playfile, const char *moh_class);
+
+/*!
+ * \brief Queue a bridge action play file frame onto the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to put the frame onto.
+ * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
+ * \param playfile Sound filename to play.
+ * \param moh_class MOH class to request bridge peers to hear while file is played.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, void (*custom_play)(const char *playfile), const char *playfile, const char *moh_class);
 
 /*!
  * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join

Modified: team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging_features.h
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging_features.h?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging_features.h (original)
+++ team/kmoore/stasis-bridging-channel_events/include/asterisk/bridging_features.h Wed Apr 10 07:51:52 2013
@@ -197,6 +197,10 @@
 	struct ao2_container *dtmf_hooks;
 	/*! Attached hangup interception hooks container */
 	struct ao2_container *hangup_hooks;
+	/*! Attached bridge channel join interception hooks container */
+	struct ao2_container *join_hooks;
+	/*! Attached bridge channel leave interception hooks container */
+	struct ao2_container *leave_hooks;
 	/*! Attached interval hooks */
 	struct ast_heap *interval_hooks;
 	/*! Used to determine when interval based features should be checked */
@@ -358,6 +362,66 @@
 int ast_bridge_interval_unregister(enum ast_bridge_builtin_interval interval);
 
 /*!
+ * \brief Attach a bridge channel join hook to a bridge features structure
+ *
+ * \param features Bridge features structure
+ * \param callback Function to execute upon activation
+ * \param hook_pvt Unique data
+ * \param destructor Optional destructor callback for hook_pvt data
+ * \param remove_on_pull TRUE if remove the hook when the channel is pulled from the bridge.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_join_hook(&features, join_callback, NULL, NULL, 0);
+ * \endcode
+ *
+ * This makes the bridging core call join_callback when a
+ * channel successfully joins the bridging system.  A pointer to
+ * useful data may be provided to the hook_pvt parameter.
+ */
+int ast_bridge_join_hook(struct ast_bridge_features *features,
+	ast_bridge_hook_callback callback,
+	void *hook_pvt,
+	ast_bridge_hook_pvt_destructor destructor,
+	int remove_on_pull);
+
+/*!
+ * \brief Attach a bridge channel leave hook to a bridge features structure
+ *
+ * \param features Bridge features structure
+ * \param callback Function to execute upon activation
+ * \param hook_pvt Unique data
+ * \param destructor Optional destructor callback for hook_pvt data
+ * \param remove_on_pull TRUE if remove the hook when the channel is pulled from the bridge.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_leave_hook(&features, leave_callback, NULL, NULL, 0);
+ * \endcode
+ *
+ * This makes the bridging core call leave_callback when a
+ * channel successfully leaves the bridging system.  A pointer
+ * to useful data may be provided to the hook_pvt parameter.
+ */
+int ast_bridge_leave_hook(struct ast_bridge_features *features,
+	ast_bridge_hook_callback callback,
+	void *hook_pvt,
+	ast_bridge_hook_pvt_destructor destructor,
+	int remove_on_pull);
+
+/*!
  * \brief Attach a hangup hook to a bridge features structure
  *
  * \param features Bridge features structure

Modified: team/kmoore/stasis-bridging-channel_events/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/kmoore/stasis-bridging-channel_events/main/bridging.c?view=diff&rev=385167&r1=385166&r2=385167
==============================================================================
--- team/kmoore/stasis-bridging-channel_events/main/bridging.c (original)
+++ team/kmoore/stasis-bridging-channel_events/main/bridging.c Wed Apr 10 07:51:52 2013
@@ -193,25 +193,7 @@
 	return current ? 0 : -1;
 }
 
-/*!
- * \internal
- * \brief Lock the bridge associated with the bridge channel.
- * \since 12.0.0
- *
- * \param bridge_channel Channel that wants to lock the bridge.
- *
- * \details
- * This is an upstream lock operation.  The defined locking
- * order is bridge then bridge_channel.
- *
- * \note On entry, neither the bridge nor bridge_channel is locked.
- *
- * \note The bridge_channel->bridge pointer changes because of a
- * bridge-merge/channel-move operation between bridges.
- *
- * \return Nothing
- */
-static void bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
+void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_bridge *bridge;
 
@@ -325,6 +307,30 @@
 	}
 	ast_bridge_channel_unlock(bridge_channel);
 	return 0;
+}
+
+void ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = action,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	ast_bridge_channel_queue_frame(bridge_channel, &frame);
+}
+
+void ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_CONTROL,
+		.subclass.integer = control,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	ast_bridge_channel_queue_frame(bridge_channel, &frame);
 }
 
 void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel)
@@ -638,6 +644,225 @@
 	};
 
 	ast_bridge_channel_queue_frame(bridge_channel, &action);
+}
+
+static void bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+	ast_bridge_channel_lock_bridge(bridge_channel);
+	bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame);
+	ast_bridge_unlock(bridge_channel->bridge);
+}
+
+void ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = action,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	bridge_channel_write_frame(bridge_channel, &frame);
+}
+
+void ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_CONTROL,
+		.subclass.integer = control,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	bridge_channel_write_frame(bridge_channel, &frame);
+}
+
+static void run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args)
+{
+	if (!strcasecmp("Gosub", app_name)) {
+		ast_app_exec_sub(NULL, chan, app_args, 0);
+	} else if (!strcasecmp("Macro", app_name)) {
+		ast_app_exec_macro(NULL, chan, app_args);
+	} else {
+		struct ast_app *app;
+
+		app = pbx_findapp(app_name);
+		if (!app) {
+			ast_log(LOG_WARNING, "Could not find application (%s)\n", app_name);
+		} else {
+			pbx_exec(chan, app, app_args);
+		}
+	}
+}
+
+void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	if (moh_class) {
+		if (ast_strlen_zero(moh_class)) {
+			ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
+				NULL, 0);
+		} else {
+			ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
+				moh_class, strlen(moh_class) + 1);
+		}
+	}
+	run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""));
+	if (moh_class) {
+		ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD,
+			NULL, 0);
+	}
+}
+
+struct bridge_run_app {
+	/*! Offset into app_name[] where the MOH class name starts.  (zero if no MOH)*/
+	int moh_offset;
+	/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
+	int app_args_offset;
+	/*! Application name to run. */
+	char app_name[0];
+};
+
+/*!
+ * \internal
+ * \brief Handle the run application bridge action.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to run the application on.
+ * \param data Action frame data to run the application.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, struct bridge_run_app *data)
+{
+	ast_bridge_channel_run_app(bridge_channel, data->app_name,
+		data->app_args_offset ? &data->app_name[data->app_args_offset] : NULL,
+		data->moh_offset ? &data->app_name[data->moh_offset] : NULL);
+}
+
+static void payload_helper_app(ast_bridge_channel_post_action_data post_it,
+	struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	struct bridge_run_app *app_data;
+	size_t len_name = strlen(app_name) + 1;
+	size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1;
+	size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1;
+	size_t len_data = sizeof(*app_data) + len_name + len_args + len_moh;
+
+	/* Fill in application run frame data. */
+	app_data = alloca(len_data);
+	app_data->app_args_offset = len_args ? len_name : 0;
+	app_data->moh_offset = len_moh ? len_name + len_args : 0;
+	strcpy(app_data->app_name, app_name);/* Safe */
+	if (len_args) {
+		strcpy(&app_data->app_name[app_data->app_args_offset], app_args);/* Safe */
+	}
+	if (moh_class) {
+		strcpy(&app_data->app_name[app_data->moh_offset], moh_class);/* Safe */
+	}
+
+	post_it(bridge_channel, AST_BRIDGE_ACTION_RUN_APP, app_data, len_data);
+}
+
+void ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	payload_helper_app(ast_bridge_channel_write_action_data,
+		bridge_channel, app_name, app_args, moh_class);
+}
+
+void ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	payload_helper_app(ast_bridge_channel_queue_action_data,
+		bridge_channel, app_name, app_args, moh_class);
+}
+
+void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, void (*custom_play)(const char *playfile), const char *playfile, const char *moh_class)
+{
+	if (moh_class) {
+		if (ast_strlen_zero(moh_class)) {
+			ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
+				NULL, 0);
+		} else {
+			ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
+				moh_class, strlen(moh_class) + 1);
+		}
+	}
+	if (custom_play) {
+		custom_play(playfile);
+	} else {
+		ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
+	}
+	if (moh_class) {
+		ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD,
+			NULL, 0);
+	}
+
+	/*
+	 * It may be necessary to resume music on hold after we finish
+	 * playing the announcment.
+	 *
+	 * XXX We have no idea what MOH class was in use before playing
+	 * the file.
+	 */
+	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
+		ast_moh_start(bridge_channel->chan, NULL, NULL);
+	}
+}
+
+struct bridge_playfile {
+	/*! Call this function to play the playfile. (NULL if normal sound file to play) */
+	void (*custom_play)(const char *playfile);
+	/*! Offset into playfile[] where the MOH class name starts.  (zero if no MOH)*/
+	int moh_offset;
+	/*! Filename to play. */
+	char playfile[0];
+};
+
+/*!
+ * \internal
+ * \brief Handle the playfile bridge action.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to play a file on.
+ * \param payload Action frame payload to play a file.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, struct bridge_playfile *payload)
+{
+	ast_bridge_channel_playfile(bridge_channel, payload->custom_play, payload->playfile,
+		payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL);
+}
+
+static void payload_helper_playfile(ast_bridge_channel_post_action_data post_it,
+	struct ast_bridge_channel *bridge_channel, void (*custom_play)(const char *playfile), const char *playfile, const char *moh_class)
+{
+	struct bridge_playfile *payload;
+	size_t len_name = strlen(playfile) + 1;
+	size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1;
+	size_t len_payload = sizeof(*payload) + len_name + len_moh;
+
+	/* Fill in play file frame data. */
+	payload = alloca(len_payload);
+	payload->custom_play = custom_play;
+	payload->moh_offset = len_moh ? len_name : 0;
+	strcpy(payload->playfile, playfile);/* Safe */
+	if (moh_class) {
+		strcpy(&payload->playfile[payload->moh_offset], moh_class);/* Safe */
+	}
+
+	post_it(bridge_channel, AST_BRIDGE_ACTION_PLAY_FILE, payload, len_payload);
+}
+
+void ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, void (*custom_play)(const char *playfile), const char *playfile, const char *moh_class)
+{
+	payload_helper_playfile(ast_bridge_channel_write_action_data,
+		bridge_channel, custom_play, playfile, moh_class);
+}
+
+void ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, void (*custom_play)(const char *playfile), const char *playfile, const char *moh_class)
+{
+	payload_helper_playfile(ast_bridge_channel_queue_action_data,
+		bridge_channel, custom_play, playfile, moh_class);
 }
 
 /*!
@@ -696,11 +921,9 @@
 /* BUGBUG bridge join or impart needs to do CONNECTED_LINE updates if the channels are being swapped and it is a 1-1 bridge. */
 
 	/* Simply write the frame out to the bridge technology. */
-	bridge_channel_lock_bridge(bridge_channel);
 /* BUGBUG The tech is where AST_CONTROL_ANSWER hook should go. (early bridge) */
 /* BUGBUG The tech is where incoming BUSY/CONGESTION hangup should happen? (early bridge) */
-	bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame);
-	ast_bridge_unlock(bridge_channel->bridge);
+	bridge_channel_write_frame(bridge_channel, frame);
 	ast_frfree(frame);
 }
 
@@ -1428,7 +1651,7 @@
  */
 static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel)
 {
-	bridge_channel_lock_bridge(bridge_channel);
+	ast_bridge_channel_lock_bridge(bridge_channel);
 	bridge_channel_suspend_nolock(bridge_channel);
 	ast_bridge_unlock(bridge_channel->bridge);
 }
@@ -1471,7 +1694,7 @@
  */
 static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
 {
-	bridge_channel_lock_bridge(bridge_channel);
+	ast_bridge_channel_lock_bridge(bridge_channel);
 	bridge_channel_unsuspend_nolock(bridge_channel);
 	ast_bridge_unlock(bridge_channel->bridge);
 }
@@ -1550,16 +1773,8 @@
 
 static void bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf)
 {
-	struct ast_frame action = {
-		.frametype = AST_FRAME_BRIDGE_ACTION,
-		.subclass.integer = AST_BRIDGE_ACTION_DTMF_STREAM,
-		.datalen = strlen(dtmf) + 1,
-		.data.ptr = (char *) dtmf,
-	};
-
-	bridge_channel_lock_bridge(bridge_channel);
-	bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, &action);
-	ast_bridge_unlock(bridge_channel->bridge);
+	ast_bridge_channel_write_action_data(bridge_channel,
+		AST_BRIDGE_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1);
 }
 
 /*!
@@ -1707,6 +1922,20 @@
 	case AST_BRIDGE_ACTION_TALKING_STOP:
 		bridge_channel_talking(bridge_channel,
 			action->subclass.integer == AST_BRIDGE_ACTION_TALKING_START);
+		break;
+	case AST_BRIDGE_ACTION_PLAY_FILE:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_playfile(bridge_channel, action->data.ptr);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_RUN_APP:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_run_app(bridge_channel, action->data.ptr);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
 		break;
 	default:
 		break;
@@ -1927,11 +2156,79 @@

[... 358 lines stripped ...]



More information about the asterisk-commits mailing list