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

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Wed Mar 6 18:43:58 CST 2013


Author: rmudgett
Date: Wed Mar  6 18:43:54 2013
New Revision: 382567

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=382567
Log:
* Add hangup event hooks.

* Rework DTMF and interval hooks.  Involved renaming associated functions
and structs.  Relaid out struct ast_bridge_hook.

* Rework DTMF attended transfer to eliminate the need for specially
interpreting the ast_bridge_join() return value.  Completed the
elimination of AST_BRIDGE_CHANNEL_STATE_DEPART.

* Added missing destructor parameter to ast_bridge_features_enable().

Modified:
    team/group/bridge_construction/apps/confbridge/conf_config_parser.c
    team/group/bridge_construction/bridges/bridge_builtin_features.c
    team/group/bridge_construction/bridges/bridge_builtin_interval_features.c
    team/group/bridge_construction/include/asterisk/bridging.h
    team/group/bridge_construction/include/asterisk/bridging_features.h
    team/group/bridge_construction/main/bridging.c
    team/group/bridge_construction/main/features.c

Modified: team/group/bridge_construction/apps/confbridge/conf_config_parser.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/apps/confbridge/conf_config_parser.c?view=diff&rev=382567&r1=382566&r2=382567
==============================================================================
--- team/group/bridge_construction/apps/confbridge/conf_config_parser.c (original)
+++ team/group/bridge_construction/apps/confbridge/conf_config_parser.c Wed Mar  6 18:43:54 2013
@@ -2052,7 +2052,7 @@
 		ao2_ref(menu, +1);
 		pvt->menu = menu;
 
-		ast_bridge_features_hook(&conference_bridge_user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);
+		ast_bridge_dtmf_hook(&conference_bridge_user->features, pvt->menu_entry.dtmf, menu_hook_callback, pvt, menu_hook_destroy);
 	}
 
 	ao2_unlock(menu);

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=382567&r1=382566&r2=382567
==============================================================================
--- team/group/bridge_construction/bridges/bridge_builtin_features.c (original)
+++ team/group/bridge_construction/bridges/bridge_builtin_features.c Wed Mar  6 18:43:54 2013
@@ -201,15 +201,45 @@
 	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);
+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;
 }
 
@@ -220,10 +250,10 @@
 	struct ast_channel *peer;
 	struct ast_bridge *attended_bridge;
 	struct ast_bridge_features caller_features;
-	enum ast_bridge_channel_state attended_bridge_result;
 	int xfer_failed;
 	struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
 	const char *context;
+	enum atxfer_code transfer_code = ATXFER_INCOMPLETE;
 
 /* BUGBUG the peer needs to be put on hold for the transfer. */
 	ast_channel_lock(bridge_channel->chan);
@@ -264,22 +294,31 @@
 		return 0;
 	}
 
-	/* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
+	/* Setup a DTMF menu to control the transfer. */
 	ast_bridge_features_init(&caller_features);
 /* 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 */
-/* BUGBUG The atxfer feature hooks need to be passed a pointer to where to mark which hook happened.  Rather than relying on the bridge join return value. */
-	ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP,
+	ast_bridge_hangup_hook(&caller_features,
+		attended_transfer_complete, &transfer_code, NULL);
+	ast_bridge_dtmf_hook(&caller_features,
+		attended_transfer && !ast_strlen_zero(attended_transfer->abort)
+			? attended_transfer->abort : "*1",
+		attended_transfer_abort, &transfer_code, NULL);
+	ast_bridge_dtmf_hook(&caller_features,
 		attended_transfer && !ast_strlen_zero(attended_transfer->complete)
-			? attended_transfer->complete : "*1",
-		NULL);
-	ast_bridge_features_hook(&caller_features,
+			? attended_transfer->complete : "*2",
+		attended_transfer_complete, &transfer_code, NULL);
+	ast_bridge_dtmf_hook(&caller_features,
 		attended_transfer && !ast_strlen_zero(attended_transfer->threeway)
-			? attended_transfer->threeway : "*2",
-		attended_threeway_transfer, NULL, NULL);
-
-	/* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */
-	attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features, NULL, 0);
+			? attended_transfer->threeway : "*3",
+		attended_transfer_threeway, &transfer_code, NULL);
+
+	/*
+	 * 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);
 
 	/* Wait for peer thread to exit bridge and die. */
 	if (!ast_autoservice_start(bridge_channel->chan)) {
@@ -294,21 +333,16 @@
 	ast_bridge_destroy(attended_bridge);
 
 	xfer_failed = -1;
-	switch (attended_bridge_result) {
-	case AST_BRIDGE_CHANNEL_STATE_END:
-		if (!ast_check_hangup_locked(bridge_channel->chan)) {
-			/* Transferer aborted the transfer. */
-			break;
-		}
-
+	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, peer, bridge_channel->chan, NULL, 1);
 		break;
-	case AST_BRIDGE_CHANNEL_STATE_HANGUP:
-		/* Peer hungup */
-		break;
-	case AST_BRIDGE_CHANNEL_STATE_DEPART:
+	case ATXFER_THREEWAY:
 		/*
 		 * Transferer wants to convert to a threeway call.
 		 *
@@ -317,7 +351,8 @@
 		 */
 		xfer_failed = ast_bridge_impart(bridge, peer, NULL, NULL, 1);
 		break;
-	default:
+	case ATXFER_ABORT:
+		/* Transferer decided not to transfer the call after all. */
 		break;
 	}
 	if (xfer_failed) {

Modified: team/group/bridge_construction/bridges/bridge_builtin_interval_features.c
URL: http://svnview.digium.com/svn/asterisk/team/group/bridge_construction/bridges/bridge_builtin_interval_features.c?view=diff&rev=382567&r1=382566&r2=382567
==============================================================================
--- team/group/bridge_construction/bridges/bridge_builtin_interval_features.c (original)
+++ team/group/bridge_construction/bridges/bridge_builtin_interval_features.c Wed Mar  6 18:43:54 2013
@@ -134,7 +134,7 @@
 	}
 
 	if (limits->frequency) {
-		ast_bridge_features_interval_update(bridge_channel, limits->frequency);
+		ast_bridge_interval_hook_update(bridge_channel, limits->frequency);
 	}
 
 	return !limits->frequency ? -1 : 0;
@@ -177,7 +177,7 @@
 	copy_bridge_features_limits(feature_limits, limits);
 	features->limits = feature_limits;
 
-	if (ast_bridge_features_interval_hook(features, feature_limits->duration,
+	if (ast_bridge_interval_hook(features, feature_limits->duration,
 		bridge_features_duration_callback, feature_limits, NULL)) {
 		ast_log(LOG_ERROR, "Failed to schedule the duration limiter to the bridge channel.\n");
 		return -1;
@@ -186,15 +186,15 @@
 	feature_limits->quitting_time = ast_tvadd(ast_tvnow(), ast_samp2tv(feature_limits->duration, 1000));
 
 	if (!ast_strlen_zero(feature_limits->connect_sound)) {
-		if (ast_bridge_features_interval_hook(features, 1, bridge_features_connect_callback, feature_limits, NULL)) {
+		if (ast_bridge_interval_hook(features, 1,
+			bridge_features_connect_callback, feature_limits, NULL)) {
 			ast_log(LOG_WARNING, "Failed to schedule connect sound to the bridge channel.\n");
 		}
 	}
 
 	if (feature_limits->warning && feature_limits->warning < feature_limits->duration) {
-		if (ast_bridge_features_interval_hook(features, feature_limits->duration - feature_limits->warning,
-			bridge_features_warning_callback,
-			feature_limits, NULL)) {
+		if (ast_bridge_interval_hook(features, feature_limits->duration - feature_limits->warning,
+			bridge_features_warning_callback, feature_limits, NULL)) {
 			ast_log(LOG_WARNING, "Failed to schedule warning sound playback to the bridge channel.\n");
 		}
 	}

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=382567&r1=382566&r2=382567
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging.h Wed Mar  6 18:43:54 2013
@@ -92,8 +92,6 @@
 	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,
 };
 
 /*! \brief Return values for bridge technology write function */
@@ -531,7 +529,6 @@
  *
  * \return Nothing
  */
-
 void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request);
 
 /*!

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=382567&r1=382566&r2=382567
==============================================================================
--- team/group/bridge_construction/include/asterisk/bridging_features.h (original)
+++ team/group/bridge_construction/include/asterisk/bridging_features.h Wed Mar  6 18:43:54 2013
@@ -97,23 +97,23 @@
 struct ast_bridge_channel;
 
 /*!
- * \brief Features hook callback type
+ * \brief Hook callback type
  *
  * \param bridge The bridge that the channel is part of
  * \param bridge_channel Channel executing the feature
  * \param hook_pvt Private data passed in when the hook was created
  *
- * \retval 0 success
- * \retval -1 failure The callback hook is removed.
- */
-typedef int (*ast_bridge_features_hook_callback)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt);
-
-/*!
- * \brief Features hook pvt destructor callback
- *
- * \param hook_pvt Private data passed in when the hook was create to destroy
- */
-typedef void (*ast_bridge_features_hook_pvt_destructor)(void *hook_pvt);
+ * \retval 0 Keep the callback hook.
+ * \retval -1 Remove the callback hook.
+ */
+typedef int (*ast_bridge_hook_callback)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt);
+
+/*!
+ * \brief Hook pvt destructor callback
+ *
+ * \param hook_pvt Private data passed in when the hook was created to destroy
+ */
+typedef void (*ast_bridge_hook_pvt_destructor)(void *hook_pvt);
 
 /*!
  * \brief Talking indicator callback
@@ -138,30 +138,43 @@
  */
 #define MAXIMUM_DTMF_FEATURE_STRING 8
 
-/*!
- * \brief Structure that is the essence of a features hook
- */
-struct ast_bridge_features_hook {
-	union {
-		/*! DTMF String that is examined during a feature hook lookup */
-		char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
-		/*! Interval that the feature hook should execute at in milliseconds */
-		unsigned int interval;
-	};
-	/*! Time at which the interval should actually trip */
-	struct timeval interval_trip_time;
-	/*! Callback that is called when DTMF string is matched */
-	ast_bridge_features_hook_callback callback;
+/*! Extra parameters for a DTMF feature hook. */
+struct ast_bridge_hook_dtmf {
+	/*! DTMF String that is examined during a feature hook lookup */
+	char code[MAXIMUM_DTMF_FEATURE_STRING];
+};
+
+/*! Extra parameters for an interval timer hook. */
+struct ast_bridge_hook_timer {
+	/*! Time at which the hook should actually trip */
+	struct timeval trip_time;
+	/*! Heap index for interval hook */
+	ssize_t __heap_index;
+	/*! Interval that the hook should execute at in milliseconds */
+	unsigned int interval;
+	/*! Sequence number for the hook to ensure expiration ordering */
+	unsigned int seqno;
+};
+
+/*!
+ * \brief Structure that is the essence of a feature hook.
+ */
+struct ast_bridge_hook {
+	/*! Linked list information */
+	AST_LIST_ENTRY(ast_bridge_hook) entry;
+	/*! Callback that is called when hook is tripped */
+	ast_bridge_hook_callback callback;
 	/*! Callback to destroy hook_pvt data right before destruction. */
-	ast_bridge_features_hook_pvt_destructor destructor;
+	ast_bridge_hook_pvt_destructor destructor;
 	/*! Unique data that was passed into us */
 	void *hook_pvt;
-	/*! Sequence number for the hook if it is an interval hook */
-	unsigned int seqno;
-	/*! Linked list information */
-	AST_LIST_ENTRY(ast_bridge_features_hook) entry;
-	/*! Heap index for interval hooks */
-	ssize_t __heap_index;
+	/*! Extra hook parameters. */
+	union {
+		/*! Extra parameters for a DTMF feature hook. */
+		struct ast_bridge_hook_dtmf dtmf;
+		/*! Extra parameters for an interval timer hook. */
+		struct ast_bridge_hook_timer timer;
+	} parms;
 };
 
 #define BRIDGE_FEATURES_INTERVAL_RATE 10
@@ -170,22 +183,24 @@
  * \brief Structure that contains features information
  */
 struct ast_bridge_features {
-	/*! Attached DTMF based feature hooks */
-	AST_LIST_HEAD_NOLOCK(, ast_bridge_features_hook) hooks;
-	/*! Attached interval based feature hooks */
+	/*! Attached DTMF feature hooks */
+	AST_LIST_HEAD_NOLOCK(, ast_bridge_hook) dtmf_hooks;
+	/*! Attached hangup interception hooks */
+	AST_LIST_HEAD_NOLOCK(, ast_bridge_hook) hangup_hooks;
+	/*! Attached interval hooks */
 	struct ast_heap *interval_hooks;
+	/*! Used to determine when interval based features should be checked */
+	struct ast_timer *interval_timer;
+	/*! Limits feature data */
+	struct ast_bridge_features_limits *limits;
 	/*! Callback to indicate when a bridge channel has started and stopped talking */
 	ast_bridge_talking_indicate_callback talker_cb;
 	/*! Callback to destroy any pvt data stored for the talker. */
 	ast_bridge_talking_indicate_destructor talker_destructor_cb;
-	/*! Used to determine when interval based features should be checked */
-	struct ast_timer *interval_timer;
 	/*! Talker callback pvt data */
 	void *talker_pvt_data;
 	/*! Feature flags that are enabled */
 	struct ast_flags feature_flags;
-	/*! Limits feature data */
-	struct ast_bridge_features_limits *limits;
 	/*! Used to assign the sequence number to the next interval hook added. */
 	unsigned int interval_sequence;
 	/*! Bit to indicate that the feature_flags and hook list is setup */
@@ -195,7 +210,6 @@
 /* BUGBUG why is dtmf_passthrough not a feature_flags bit? */
 	/*! Bit to indicate whether DTMF should be passed into the bridge tech or not.  */
 	unsigned int dtmf_passthrough:1;
-
 };
 
 /*!
@@ -214,6 +228,8 @@
 /* BUGBUG the context should be figured out based upon TRANSFER_CONTEXT channel variable of A/B or current context of A/B. More appropriate for when channel moved to other bridges. */
 	/*! Context to use for transfers */
 	char context[AST_MAX_CONTEXT];
+	/*! DTMF string used to abort the transfer */
+	char abort[MAXIMUM_DTMF_FEATURE_STRING];
 	/*! DTMF string used to turn the transfer into a three way conference */
 	char threeway[MAXIMUM_DTMF_FEATURE_STRING];
 	/*! DTMF string used to complete the transfer */
@@ -262,7 +278,7 @@
  * This registers the function bridge_builtin_attended_transfer as the function responsible for the built in
  * attended transfer feature.
  */
-int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_features_hook_callback callback, const char *dtmf);
+int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_hook_callback callback, const char *dtmf);
 
 /*!
  * \brief Unregister a handler for a built in feature
@@ -321,7 +337,35 @@
 int ast_bridge_interval_unregister(enum ast_bridge_builtin_interval interval);
 
 /*!
- * \brief Attach a custom hook to a bridge features structure
+ * \brief Attach a hangup 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
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_hangup_hook(&features, hangup_callback, NULL, NULL);
+ * \endcode
+ *
+ * This makes the bridging core call hangup_callback if a
+ * channel that has this hook hangs up.  A pointer to useful
+ * data may be provided to the hook_pvt parameter.
+ */
+int ast_bridge_hangup_hook(struct ast_bridge_features *features,
+	ast_bridge_hook_callback callback,
+	void *hook_pvt,
+	ast_bridge_hook_pvt_destructor destructor);
+
+/*!
+ * \brief Attach a DTMF hook to a bridge features structure
  *
  * \param features Bridge features structure
  * \param dtmf DTMF string to be activated upon
@@ -337,25 +381,24 @@
  * \code
  * struct ast_bridge_features features;
  * ast_bridge_features_init(&features);
- * ast_bridge_features_hook(&features, "#", pound_callback, NULL, NULL);
+ * ast_bridge_dtmf_hook(&features, "#", pound_callback, NULL, NULL);
  * \endcode
  *
  * This makes the bridging core call pound_callback if a channel that has this
  * feature structure inputs the DTMF string '#'. A pointer to useful data may be
  * provided to the hook_pvt parameter.
  */
-int ast_bridge_features_hook(struct ast_bridge_features *features,
+int ast_bridge_dtmf_hook(struct ast_bridge_features *features,
 	const char *dtmf,
-	ast_bridge_features_hook_callback callback,
+	ast_bridge_hook_callback callback,
 	void *hook_pvt,
-	ast_bridge_features_hook_pvt_destructor destructor);
-
-/*!
- * \brief attach a custom interval hook to a bridge features structure
+	ast_bridge_hook_pvt_destructor destructor);
+
+/*!
+ * \brief attach an interval hook to a bridge features structure
  *
  * \param features Bridge features structure
  * \param interval The interval that the hook should execute at in milliseconds
- * \param strict If set this takes into account the time spent executing the callback when rescheduling the interval hook
  * \param callback Function to execute upon activation
  * \param hook_pvt Unique data
  * \param destructor Optional destructor callback for hook_pvt data
@@ -366,17 +409,17 @@
  * \code
  * struct ast_bridge_features features;
  * ast_bridge_features_init(&features);
- * ast_bridge_features_interval_hook(&features, 1000, 0, playback_callback, NULL, NULL);
+ * ast_bridge_interval_hook(&features, 1000, playback_callback, NULL, NULL);
  * \endcode
  *
  * This makes the bridging core call playback_callback every second. A pointer to useful
  * data may be provided to the hook_pvt parameter.
  */
-int ast_bridge_features_interval_hook(struct ast_bridge_features *features,
+int ast_bridge_interval_hook(struct ast_bridge_features *features,
 	unsigned int interval,
-	ast_bridge_features_hook_callback callback,
+	ast_bridge_hook_callback callback,
 	void *hook_pvt,
-	ast_bridge_features_hook_pvt_destructor destructor);
+	ast_bridge_hook_pvt_destructor destructor);
 
 /*!
  * \brief Update the interval on an interval hook that is currently executing a callback
@@ -390,7 +433,7 @@
  * Example usage:
  *
  * \code
- * ast_bridge_features_interval_update(bridge_channel, 10000);
+ * ast_bridge_interval_hook_update(bridge_channel, 10000);
  * \endcode
  *
  * This updates the executing interval hook so that it will be triggered next in 10 seconds.
@@ -398,7 +441,7 @@
  * \note This can only be called from the context of the interval hook callback itself. If this
  *       is called outside the callback then behavior is undefined.
  */
-int ast_bridge_features_interval_update(struct ast_bridge_channel *bridge_channel, unsigned int interval);
+int ast_bridge_interval_hook_update(struct ast_bridge_channel *bridge_channel, unsigned int interval);
 
 /*!
  * \brief Set a callback on the features structure to receive talking notifications on.
@@ -422,23 +465,24 @@
  * \param feature Feature to enable
  * \param dtmf Optionally the DTMF stream to trigger the feature, if not specified it will be the default
  * \param config Configuration structure unique to the built in type
- *
- * \retval 0 on success
- * \retval -1 on failure
- *
- * Example usage:
- *
- * \code
- * struct ast_bridge_features features;
- * ast_bridge_features_init(&features);
- * ast_bridge_features_enable(&features, AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, NULL);
+ * \param destructor Optional destructor callback for config data
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_enable(&features, AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, NULL, NULL);
  * \endcode
  *
  * This enables the attended transfer DTMF option using the default DTMF string. An alternate
  * string may be provided using the dtmf parameter. Internally this is simply setting up a hook
  * to a built in feature callback function.
  */
-int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config);
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config, ast_bridge_hook_pvt_destructor destructor);
 
 /*!
  * \brief Constructor function for ast_bridge_features_limits

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=382567&r1=382566&r2=382567
==============================================================================
--- team/group/bridge_construction/main/bridging.c (original)
+++ team/group/bridge_construction/main/bridging.c Wed Mar  6 18:43:54 2013
@@ -553,7 +553,7 @@
 static struct ast_frame *bridge_handle_dtmf(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
 {
 	struct ast_bridge_features *features;
-	struct ast_bridge_features_hook *hook;
+	struct ast_bridge_hook *hook;
 
 	features = bridge_channel->features;
 	if (!features) {
@@ -567,8 +567,8 @@
 
 /* BUGBUG the feature hook matching needs to be done here.  Any matching feature hook needs to be queued onto the bridge_channel.  Also the feature hook digit timeout needs to be handled. */
 	/* See if this DTMF matches the beginnings of any feature hooks, if so we switch to the feature state to either execute the feature or collect more DTMF */
-	AST_LIST_TRAVERSE(&features->hooks, hook, entry) {
-		if (hook->dtmf[0] == frame->subclass.integer) {
+	AST_LIST_TRAVERSE(&features->dtmf_hooks, hook, entry) {
+		if (hook->parms.dtmf.code[0] == frame->subclass.integer) {
 			struct ast_frame action = {
 				.frametype = AST_FRAME_BRIDGE_ACTION,
 				.subclass.integer = AST_BRIDGE_ACTION_FEATURE,
@@ -584,9 +584,61 @@
 	return frame;
 }
 
+/*!
+ * \internal
+ * \brief Handle bridge hangup event.
+ * \since 12.0.0
+ *
+ * \param bridge Bridge involved in a hangup.
+ * \param bridge_channel Which channel is hanging up.
+ *
+ * \return Nothing
+ */
+static void bridge_handle_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features *features;
+
+	features = bridge_channel->features;
+	if (!features) {
+		features = &bridge->features;
+	}
+
+	if (features->usable) {
+		struct ast_bridge_hook *hook;
+
+		/* Run any hangup hooks. */
+		AST_LIST_TRAVERSE_SAFE_BEGIN(&features->hangup_hooks, hook, entry) {
+			int failed;
+
+			failed = hook->callback(bridge, bridge_channel, hook->hook_pvt);
+			if (failed) {
+				ast_debug(1, "Hangup hook %p is being removed from bridge channel %p(%s)\n",
+					hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+				AST_LIST_REMOVE_CURRENT(entry);
+				if (hook->destructor) {
+					hook->destructor(hook->hook_pvt);
+				}
+				ast_free(hook);
+			}
+		}
+		AST_LIST_TRAVERSE_SAFE_END;
+	}
+
+	/* Default hangup action. */
+	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_END);
+		break;
+	default:
+		break;
+	}
+	ao2_unlock(bridge_channel);
+}
+
 static int bridge_channel_interval_ready(struct ast_bridge_channel *bridge_channel)
 {
-	struct ast_bridge_features_hook *hook;
+	struct ast_bridge_hook *hook;
 
 	if (!bridge_channel->features || !bridge_channel->features->usable
 		|| !bridge_channel->features->interval_hooks) {
@@ -594,7 +646,7 @@
 	}
 
 	hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1);
-	if (!hook || ast_tvdiff_ms(hook->interval_trip_time, ast_tvnow()) > 0) {
+	if (!hook || ast_tvdiff_ms(hook->parms.timer.trip_time, ast_tvnow()) > 0) {
 		return 0;
 	}
 
@@ -674,16 +726,7 @@
 		}
 		/* This is pretty simple... see if they hung up */
 		if (!frame || (frame->frametype == AST_FRAME_CONTROL && frame->subclass.integer == AST_CONTROL_HANGUP)) {
-			/* Signal the thread that is handling the bridged channel that it should be ended */
-			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_END);
-				break;
-			default:
-				break;
-			}
-			ao2_unlock(bridge_channel);
+			bridge_handle_hangup(bridge, bridge_channel);
 		} else if (frame->frametype == AST_FRAME_CONTROL && bridge_drop_control_frame(frame->subclass.integer)) {
 			ast_debug(1, "Dropping control frame %d from bridge channel %p(%s)\n",
 				frame->subclass.integer, bridge_channel,
@@ -1413,14 +1456,14 @@
 /*! \brief Internal function that activates interval hooks on a bridge channel */
 static void bridge_channel_interval(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	struct ast_bridge_features_hook *hook;
+	struct ast_bridge_hook *hook;
 
 	while ((hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1))) {
 		int res;
 		struct timeval start = ast_tvnow();
 		int execution_time = 0;
 
-		if (ast_tvdiff_ms(hook->interval_trip_time, start) > 0) {
+		if (ast_tvdiff_ms(hook->parms.timer.trip_time, start) > 0) {
 			ast_debug(1, "Hook %p on bridge channel %p(%s) wants to happen in the future, stopping our traversal\n",
 				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
 			break;
@@ -1432,12 +1475,12 @@
 
 		/*
 		 * Must be popped after the callback.  The callback could call
-		 * ast_bridge_features_interval_update().
+		 * ast_bridge_interval_hook_update().
 		 */
 		ast_heap_pop(bridge_channel->features->interval_hooks);
 
-		if (res || !hook->interval) {
-			ast_debug(1, "Hook %p is being removed from bridge channel %p(%s)\n",
+		if (res || !hook->parms.timer.interval) {
+			ast_debug(1, "Interval hook %p is being removed from bridge channel %p(%s)\n",
 				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
 			if (hook->destructor) {
 				hook->destructor(hook->hook_pvt);
@@ -1446,17 +1489,18 @@
 			continue;
 		}
 
-		ast_debug(1, "Updating hook %p and adding it back to bridge channel %p(%s)\n",
-			hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+		ast_debug(1, "Updating interval hook %p with interval %u on bridge channel %p(%s)\n",
+			hook, hook->parms.timer.interval, bridge_channel,
+			ast_channel_name(bridge_channel->chan));
 
 		execution_time = ast_tvdiff_ms(ast_tvnow(), start);
 
 		/* resetting start */
 		start = ast_tvnow();
 
-		hook->interval_trip_time = ast_tvadd(start, ast_samp2tv(hook->interval - execution_time, 1000));
-
-		hook->seqno = ast_atomic_fetchadd_int((int *)&bridge_channel->features->interval_sequence, +1);
+		hook->parms.timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->parms.timer.interval - execution_time, 1000));
+
+		hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1);
 		ast_heap_push(bridge_channel->features->interval_hooks, hook);
 	}
 }
@@ -1469,7 +1513,7 @@
 static void bridge_channel_feature(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
 	struct ast_bridge_features *features = (bridge_channel->features ? bridge_channel->features : &bridge->features);
-	struct ast_bridge_features_hook *hook = NULL;
+	struct ast_bridge_hook *hook = NULL;
 	char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
 	int look_for_dtmf = 1, dtmf_len = 0;
 
@@ -1502,20 +1546,22 @@
 		look_for_dtmf = 0;
 
 		/* See if a DTMF feature hook matches or can match */
-		AST_LIST_TRAVERSE(&features->hooks, hook, entry) {
+		AST_LIST_TRAVERSE(&features->dtmf_hooks, hook, entry) {
 			/* If this hook matches just break out now */
-			if (!strcmp(hook->dtmf, dtmf)) {
+			if (!strcmp(hook->parms.dtmf.code, dtmf)) {
 				ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on bridge channel %p(%s)\n",
 					hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
 				look_for_dtmf = 0;
 				break;
-			} else if (!strncmp(hook->dtmf, dtmf, dtmf_len)) {
+			} else if (!strncmp(hook->parms.dtmf.code, dtmf, dtmf_len)) {
 				ast_debug(1, "DTMF feature hook %p can match DTMF string '%s', it wants '%s', on bridge channel %p(%s)\n",
-					hook, dtmf, hook->dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
+					hook, dtmf, hook->parms.dtmf.code, bridge_channel,
+					ast_channel_name(bridge_channel->chan));
 				look_for_dtmf = 1;
 			} else {
 				ast_debug(1, "DTMF feature hook %p does not match DTMF string '%s', it wants '%s', on bridge channel %p(%s)\n",
-					hook, dtmf, hook->dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
+					hook, dtmf, hook->parms.dtmf.code, bridge_channel,
+					ast_channel_name(bridge_channel->chan));
 			}
 		}
 
@@ -1530,7 +1576,27 @@
 
 	/* If a hook was actually matched execute it on this channel, otherwise stream up the DTMF to the other channels */
 	if (hook) {
-		hook->callback(bridge, bridge_channel, hook->hook_pvt);
+		int failed;
+
+		failed = hook->callback(bridge, bridge_channel, hook->hook_pvt);
+		if (failed) {
+			struct ast_bridge_hook *cur;
+
+			AST_LIST_TRAVERSE_SAFE_BEGIN(&features->dtmf_hooks, cur, entry) {
+				if (cur == hook) {
+					ast_debug(1, "DTMF hook %p is being removed from bridge channel %p(%s)\n",
+						hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+					AST_LIST_REMOVE_CURRENT(entry);
+					if (hook->destructor) {
+						hook->destructor(hook->hook_pvt);
+					}
+					ast_free(hook);
+					break;
+				}
+			}
+			AST_LIST_TRAVERSE_SAFE_END;
+		}
+
 		/*
 		 * If we are handing the channel off to an external hook for
 		 * ownership, we are not guaranteed what kind of state it will
@@ -1538,15 +1604,7 @@
 		 * here if the hook did not already change the state.
 		 */
 		if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
-			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_END);
-				break;
-			default:
-				break;
-			}
-			ao2_unlock(bridge_channel);
+			bridge_handle_hangup(bridge, bridge_channel);
 		}
 	} else {
 		ast_bridge_dtmf_stream(bridge, dtmf, bridge_channel->chan);
@@ -2170,18 +2228,16 @@
 {
 	struct ast_bridge_channel *bridge_channel = data;
 	struct ast_channel *chan;
-	enum ast_bridge_channel_state state;
 
 	if (bridge_channel->callid) {
 		ast_callid_threadassoc_add(bridge_channel->callid);
 	}
 
 	bridge_channel_join(bridge_channel);
-	state = bridge_channel->state;
 	chan = bridge_channel->chan;
 
 	/* cleanup */
-	ast_channel_internal_bridge_channel_set(bridge_channel->chan, NULL);
+	ast_channel_internal_bridge_channel_set(chan, NULL);
 	bridge_channel->chan = NULL;
 	bridge_channel->swap = NULL;
 	ast_bridge_features_destroy(bridge_channel->features);
@@ -2461,7 +2517,7 @@
 	technology->suspended = 0;
 }
 
-int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_features_hook_callback callback, const char *dtmf)
+int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_hook_callback callback, const char *dtmf)
 {
 	if (ARRAY_LEN(builtin_features_handlers) <= feature
 		|| builtin_features_handlers[feature]) {
@@ -2514,26 +2570,50 @@
 
 }
 
-int ast_bridge_features_hook(struct ast_bridge_features *features,
+int ast_bridge_dtmf_hook(struct ast_bridge_features *features,
 	const char *dtmf,
-	ast_bridge_features_hook_callback callback,
+	ast_bridge_hook_callback callback,
 	void *hook_pvt,
-	ast_bridge_features_hook_pvt_destructor destructor)
-{
-	struct ast_bridge_features_hook *hook;
+	ast_bridge_hook_pvt_destructor destructor)
+{
+	struct ast_bridge_hook *hook;
 
 	/* Allocate new memory and setup it's various variables */
 	hook = ast_calloc(1, sizeof(*hook));
 	if (!hook) {
 		return -1;
 	}
-	ast_copy_string(hook->dtmf, dtmf, sizeof(hook->dtmf));
+	ast_copy_string(hook->parms.dtmf.code, dtmf, sizeof(hook->parms.dtmf.code));
 	hook->callback = callback;
 	hook->destructor = destructor;
 	hook->hook_pvt = hook_pvt;
 
-	/* Once done we add it onto the list. Now it will be picked up when DTMF is used */
-	AST_LIST_INSERT_TAIL(&features->hooks, hook, entry);
+	/* Once done we add it onto the list. */
+	AST_LIST_INSERT_TAIL(&features->dtmf_hooks, hook, entry);
+
+	features->usable = 1;
+
+	return 0;
+}
+
+int ast_bridge_hangup_hook(struct ast_bridge_features *features,
+	ast_bridge_hook_callback callback,
+	void *hook_pvt,
+	ast_bridge_hook_pvt_destructor destructor)
+{
+	struct ast_bridge_hook *hook;
+
+	/* Allocate new memory and setup it's various variables */
+	hook = ast_calloc(1, sizeof(*hook));
+	if (!hook) {
+		return -1;
+	}
+	hook->callback = callback;
+	hook->destructor = destructor;
+	hook->hook_pvt = hook_pvt;
+
+	/* Once done we add it onto the list. */
+	AST_LIST_INSERT_TAIL(&features->hangup_hooks, hook, entry);
 
 	features->usable = 1;
 
@@ -2550,13 +2630,13 @@
 	features->talker_pvt_data = pvt_data;
 }
 
-int ast_bridge_features_interval_hook(struct ast_bridge_features *features,
+int ast_bridge_interval_hook(struct ast_bridge_features *features,
 	unsigned int interval,
-	ast_bridge_features_hook_callback callback,
+	ast_bridge_hook_callback callback,
 	void *hook_pvt,
-	ast_bridge_features_hook_pvt_destructor destructor)
-{
-	struct ast_bridge_features_hook *hook = NULL;
+	ast_bridge_hook_pvt_destructor destructor)
+{
+	struct ast_bridge_hook *hook = NULL;
 
 	if (!interval || !callback || !features || !features->interval_hooks
 		|| !(hook = ast_calloc(1, sizeof(*hook)))) {
@@ -2572,24 +2652,24 @@
 		ast_timer_set_rate(features->interval_timer, BRIDGE_FEATURES_INTERVAL_RATE);
 	}
 
-	hook->interval = interval;
+	hook->parms.timer.interval = interval;
 	hook->callback = callback;
 	hook->destructor = destructor;
 	hook->hook_pvt = hook_pvt;
 
-	ast_debug(1, "Putting interval hook %p in the interval hooks heap on features %p\n",
-		hook, features);
-	hook->interval_trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(hook->interval, 1000));
-	hook->seqno = ast_atomic_fetchadd_int((int *)&features->interval_sequence, +1);
+	ast_debug(1, "Putting interval hook %p with interval %u in the heap on features %p\n",
+		hook, hook->parms.timer.interval, features);
+	hook->parms.timer.trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(hook->parms.timer.interval, 1000));
+	hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &features->interval_sequence, +1);
 	ast_heap_push(features->interval_hooks, hook);
 	features->usable = 1;
 
 	return 0;
 }
 
-int ast_bridge_features_interval_update(struct ast_bridge_channel *bridge_channel, unsigned int interval)
-{
-	struct ast_bridge_features_hook *hook;
+int ast_bridge_interval_hook_update(struct ast_bridge_channel *bridge_channel, unsigned int interval)
+{
+	struct ast_bridge_hook *hook;
 
 	if (!bridge_channel->features || !bridge_channel->features->usable
 		|| !bridge_channel->features->interval_hooks) {
@@ -2600,14 +2680,13 @@
 	if (!hook) {
 		return -1;
 	}
-	hook->interval = interval;
+	hook->parms.timer.interval = interval;
 
 	return 0;
 }
 
-int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config)
-{
-/* BUGBUG a destructor for config is needed if it is going to be non-NULL */
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config, ast_bridge_hook_pvt_destructor destructor)
+{
 	if (ARRAY_LEN(builtin_features_handlers) <= feature
 		|| !builtin_features_handlers[feature]) {
 		return -1;
@@ -2624,8 +2703,11 @@
 		}
 	}
 
-	/* The rest is basically pretty easy. We create another hook using the built in feature's callback and DTMF, easy as pie. */
-	return ast_bridge_features_hook(features, dtmf, builtin_features_handlers[feature], config, NULL);
+	/*
+	 * The rest is basically pretty easy.  We create another hook
+	 * using the built in feature's DTMF callback.  Easy as pie.
+	 */
+	return ast_bridge_dtmf_hook(features, dtmf, builtin_features_handlers[feature], config, destructor);
 }
 
 int ast_bridge_features_limits_construct(struct ast_bridge_features_limits *limits)
@@ -2649,6 +2731,7 @@
 {
 	if (builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_LIMITS]) {
 		int (*bridge_features_set_limits_callback)(struct ast_bridge_features *features, struct ast_bridge_features_limits *limits);
+
 		bridge_features_set_limits_callback = builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_LIMITS];
 		return bridge_features_set_limits_callback(features, limits);
 	}
@@ -2665,15 +2748,16 @@
 
 static int interval_hook_time_cmp(void *a, void *b)
 {
-	struct ast_bridge_features_hook *hook_a = a;
-	struct ast_bridge_features_hook *hook_b = b;
-	int cmp = ast_tvcmp(hook_b->interval_trip_time, hook_a->interval_trip_time);
-

[... 144 lines stripped ...]



More information about the asterisk-commits mailing list