[asterisk-commits] jrose: branch jrose/bridge_projects r380850 - in /team/jrose/bridge_projects:...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Feb 5 11:42:28 CST 2013


Author: jrose
Date: Tue Feb  5 11:42:27 2013
New Revision: 380850

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=380850
Log:
Interval Hooks: experimental phase 2

Modified:
    team/jrose/bridge_projects/include/asterisk/bridging.h
    team/jrose/bridge_projects/include/asterisk/bridging_features.h
    team/jrose/bridge_projects/main/bridging.c
    team/jrose/bridge_projects/main/features.c

Modified: team/jrose/bridge_projects/include/asterisk/bridging.h
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/include/asterisk/bridging.h?view=diff&rev=380850&r1=380849&r2=380850
==============================================================================
--- team/jrose/bridge_projects/include/asterisk/bridging.h (original)
+++ team/jrose/bridge_projects/include/asterisk/bridging.h Tue Feb  5 11:42:27 2013
@@ -164,6 +164,8 @@
 enum ast_bridge_action_type {
 	/*! Bridged channel is to detect a feature hook */
 	AST_BRIDGE_ACTION_FEATURE,
+	/*! Bridged channel is to act on an interval hook */
+	AST_BRIDGE_ACTION_INTERVAL,
 	/*! Bridged channel is to send a DTMF stream out */
 	AST_BRIDGE_ACTION_DTMF_STREAM,
 	/*! Bridged channel is to indicate talking start */

Modified: team/jrose/bridge_projects/include/asterisk/bridging_features.h
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/include/asterisk/bridging_features.h?view=diff&rev=380850&r1=380849&r2=380850
==============================================================================
--- team/jrose/bridge_projects/include/asterisk/bridging_features.h (original)
+++ team/jrose/bridge_projects/include/asterisk/bridging_features.h Tue Feb  5 11:42:27 2013
@@ -128,8 +128,14 @@
  * \brief Structure that is the essence of a features hook
  */
 struct ast_bridge_features_hook {
-	/*! DTMF String that is examined during a feature hook lookup */
-	char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
+	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;
 	/*! Callback to destroy hook_pvt data right before destruction. */
@@ -138,7 +144,13 @@
 	void *hook_pvt;
 	/*! Linked list information */
 	AST_LIST_ENTRY(ast_bridge_features_hook) entry;
-};
+	/*! Heap index for interval hooks */
+	ssize_t __heap_index;
+	/*! Bit to indicate that we should take into account the time spent executing the callback when rescheduling the interval hook */
+	unsigned int interval_strict:1;
+};
+
+#define BRIDGE_FEATURES_INTERVAL_RATE 10
 
 /*!
  * \brief Structure that contains features information
@@ -146,10 +158,14 @@
 struct ast_bridge_features {
 	/*! Attached DTMF based feature hooks */
 	AST_LIST_HEAD_NOLOCK(, ast_bridge_features_hook) hooks;
+	/*! Attached interval based feature hooks */
+	struct ast_heap *interval_hooks;
 	/*! 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 */
@@ -187,6 +203,22 @@
 };
 
 /*!
+ * \brief Structure that contains configuration information for the limits feature
+ */
+struct ast_bridge_features_limits {
+	/*! Maximum duration that the channel is allowed to be in the bridge (specified in milliseconds) */
+	unsigned int duration;
+	/*! Duration into the call when warnings should begin (specified in milliseconds or 0 to disable) */
+	unsigned int warning;
+	/*! Interval between the warnings (specified in milliseconds or 0 to disable) */
+	unsigned int frequency;
+	/*! Sound file to play when the maximum duration is reached (if empty, then nothing will be played) */
+	char duration_sound[256];
+	/*! Sound file to play when the warning time is reached (if empty, then the remaining time will be played) */
+	char warning_sound[256];
+};
+
+/*!
  * \brief Register a handler for a built in feature
  *
  * \param feature The feature that the handler will be responsible for
@@ -258,6 +290,58 @@
 	void *hook_pvt,
 	ast_bridge_features_hook_pvt_destructor destructor);
 
+/*! \brief attach a custom 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
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_interval_hook(&features, 1000, 0, 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.
+ *
+ * \note It is important that the callback set the bridge channel state back to
+ *       AST_BRIDGE_CHANNEL_STATE_WAIT or the bridge thread will not service the channel.
+ */
+int ast_bridge_features_interval_hook(struct ast_bridge_features *features,
+	unsigned int interval,
+	unsigned int strict,
+	ast_bridge_features_hook_callback callback,
+	void *hook_pvt,
+	ast_bridge_features_hook_pvt_destructor destructor);
+
+/*! \brief Update the interval on an interval hook that is currently executing a callback
+ *
+ * \param bridge_channel The bridge channel that is executing the callback
+ * \param interval The new interval value or 0 to remove the interval hook
+ *
+ * \retval 0 on success
+ * \retval 1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_features_interval_update(bridge_channel, 10000);
+ * \endcode
+ *
+ * This updates the executing interval hook so that it will be triggered next in 10 seconds.
+ *
+ * \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);
+
 /*!
  * \brief Set a callback on the features structure to receive talking notifications on.
  *
@@ -298,6 +382,27 @@
  */
 int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config);
 
+/*! \brief Limit the amount of time a channel may stay in the bridge and optionally play warning messages as time runs out
+ *
+ * \param features Bridge features structure
+ * \param limits Configured limits applicable to the channel
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * struct ast_bridge_features_limits limits = { .duration = 10000, };
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_set_limits(&features, &limits);
+ * \endcode
+ *
+ * This sets the maximum time the channel can be in the bridge to 10 seconds and does not play any warnings.
+ *
+ * \note This API call can only be used on a features structure that will be used in association with a bridge channel.
+ * \note The ast_bridge_features_limits structure must remain accessible for the lifetime of the features structure.
+ */
+void ast_bridge_features_set_limits(struct ast_bridge_features *features, struct ast_bridge_features_limits *limits);
+
 /*!
  * \brief Set a flag on a bridge features structure
  *

Modified: team/jrose/bridge_projects/main/bridging.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/main/bridging.c?view=diff&rev=380850&r1=380849&r2=380850
==============================================================================
--- team/jrose/bridge_projects/main/bridging.c (original)
+++ team/jrose/bridge_projects/main/bridging.c Tue Feb  5 11:42:27 2013
@@ -47,6 +47,11 @@
 #include "asterisk/astobj2.h"
 #include "asterisk/test.h"
 
+#include "asterisk/heap.h"
+#include "asterisk/say.h"
+#include "asterisk/timing.h"
+#include "asterisk/musiconhold.h"
+
 static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
 
 /* Initial starting point for the bridge array of channels */
@@ -363,6 +368,23 @@
 	}
 
 	return frame;
+}
+
+static int bridge_channel_interval_ready(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features_hook *hook;
+
+	if (!bridge_channel->features || !bridge_channel->features->usable || !ast_heap_size(bridge_channel->features->interval_hooks)) {
+		return 0;
+	}
+
+	hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1);
+
+	if (!hook || (ast_tvdiff_ms(hook->interval_trip_time, ast_tvnow()) > 0)) {
+		return 0;
+	}
+
+	return 1;
 }
 
 /*! \brief Internal function used to determine whether a control frame should be dropped or not */
@@ -404,9 +426,24 @@
 
 void ast_bridge_handle_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_channel *chan, int outfd)
 {
+	struct ast_timer *interval_timer;
+
 	/* If no bridge channel has been provided and the actual channel has been provided find it */
 	if (chan && !bridge_channel) {
 		bridge_channel = find_bridge_channel(bridge, chan);
+	}
+
+	if (bridge_channel && bridge_channel->features && (interval_timer = bridge_channel->features->interval_timer)) {
+		if (ast_wait_for_input(ast_timer_fd(interval_timer), 0) == 1) {
+			ast_timer_ack(interval_timer, 1);
+			if (bridge_channel_interval_ready(bridge_channel)) {
+				struct ast_frame interval_action = {
+					.frametype = AST_FRAME_BRIDGE_ACTION,
+					.subclass.integer = AST_BRIDGE_ACTION_INTERVAL,
+				};
+				ast_bridge_channel_queue_action(bridge_channel, &interval_action);
+			}
+		}
 	}
 
 	/* If a bridge channel with actual channel is present read a frame and handle it */
@@ -1009,6 +1046,48 @@
 	}
 }
 
+/*! \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;
+
+	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) {
+			ast_debug(1, "Hook '%p' on '%p' wants to happen in the future, stopping our traversal\n", hook, bridge_channel);
+			break;
+		}
+
+		ast_debug(1, "Executing hook '%p' on channel '%p'\n", hook, bridge_channel);
+		res = hook->callback(bridge, bridge_channel, hook->hook_pvt);
+
+		ast_heap_pop(bridge_channel->features->interval_hooks);
+
+		if (res || !hook->interval) {
+			ast_debug(1, "Hook '%p' is being removed from '%p'\n", hook, bridge_channel);
+			ast_free(hook);
+			continue;
+		}
+
+		ast_debug(1, "Updating hook '%p' and adding it back to '%p'\n", hook, bridge_channel);
+
+		if (hook->interval_strict) {
+			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));
+
+		ast_debug(1, "Sticking hook '%p' in heap on '%p'\n", hook, bridge_channel->features);
+		ast_heap_push(bridge_channel->features->interval_hooks, hook);
+	}
+}
+
 /*!
  * \brief Internal function that executes a feature on a bridge channel
  * \note Neither the bridge nor the bridge_channel locks should be held when entering
@@ -1126,6 +1205,13 @@
 static void bridge_channel_action_bridge(struct ast_bridge_channel *bridge_channel, struct ast_frame *action)
 {
 	switch (action->subclass.integer) {
+	case AST_BRIDGE_ACTION_INTERVAL:
+		bridge_channel_suspend(bridge_channel->bridge, bridge_channel);
+		ao2_unlock(bridge_channel->bridge);
+		bridge_channel_interval(bridge_channel->bridge, bridge_channel);
+		ao2_lock(bridge_channel->bridge);
+		bridge_channel_unsuspend(bridge_channel->bridge, bridge_channel);
+		break;
 	case AST_BRIDGE_ACTION_FEATURE:
 		bridge_channel_suspend(bridge_channel->bridge, bridge_channel);
 		ao2_unlock(bridge_channel->bridge);
@@ -1544,6 +1630,9 @@
 			break;
 		}
 
+
+/* XXX What I want to check in the morning is whether or not the block below is ever entered at all, just just the Hi part. */
+
 		/* Was the channel already removed from the bridge? */
 		AST_LIST_TRAVERSE_SAFE_BEGIN(&bridge->depart_wait, bridge_channel, entry) {
 			if (bridge_channel->chan == chan) {
@@ -1797,6 +1886,54 @@
 	features->talker_pvt_data = pvt_data;
 }
 
+int ast_bridge_features_interval_hook(struct ast_bridge_features *features,
+	unsigned int interval,
+	unsigned int strict,
+	ast_bridge_features_hook_callback callback,
+	void *hook_pvt,
+	ast_bridge_features_hook_pvt_destructor destructor)
+{
+	struct ast_bridge_features_hook *hook = NULL;
+
+	if (!interval || !callback || !(hook = ast_calloc(1, sizeof(*hook)))) {
+		return -1;
+	}
+
+	if (!features->interval_timer) {
+		if (!(features->interval_timer = ast_timer_open())) {
+			ast_log(LOG_ERROR, "Failed to open a timer when adding a timed bridging feature.\n");
+			return -1;
+		}
+		ast_timer_set_rate(features->interval_timer, BRIDGE_FEATURES_INTERVAL_RATE);
+	}
+
+	hook->interval = interval;
+	hook->callback = callback;
+	hook->destructor = destructor;
+	hook->hook_pvt = hook_pvt;
+	hook->interval_strict = strict ? 1 : 0;
+
+	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));
+	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;
+	if (!bridge_channel->features || !bridge_channel->features->usable || !ast_heap_size(bridge_channel->features->interval_hooks)) {
+		return -1;
+	}
+
+	hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1);
+	hook->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 */
@@ -1819,12 +1956,128 @@
 	return ast_bridge_features_hook(features, dtmf, builtin_features_handlers[feature], config, NULL);
 }
 
+static int bridge_features_duration_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	struct ast_bridge_features_limits *limits = hook_pvt;
+
+	if (!ast_strlen_zero(limits->duration_sound)) {
+		ast_stream_and_wait(bridge_channel->chan, limits->duration_sound, "");
+	}
+
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+
+	return -1;
+}
+
+static int bridge_features_warning_sound_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	struct ast_bridge_features_limits *limits = hook_pvt;
+
+	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_END) {
+		ast_debug(1, "Skipping warning, the channel state is already set to end.\n");
+		return -1;
+	}
+
+	ast_stream_and_wait(bridge_channel->chan, limits->warning_sound, "");
+
+	/* It may be necessary to resume music on hold after we play the sound file. */
+	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
+		ast_moh_start(bridge_channel->chan, NULL, NULL);
+	}
+
+	if (limits->frequency) {
+		ast_bridge_features_interval_update(bridge_channel, limits->frequency);
+	}
+
+
+	return !limits->frequency ? -1 : 0;
+}
+
+static int bridge_features_warning_time_left_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+	struct ast_bridge_features_limits *limits = hook_pvt;
+	struct ast_bridge_features_hook *interval_hook;
+	unsigned int remaining = 0;
+	int heap_traversal_index, heap_size;
+
+	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_END) {
+		ast_debug(1, "Skipping warning, the channel state is already set to end.\n");
+		return -1;
+	}
+
+	heap_size = ast_heap_size(bridge_channel->features->interval_hooks);
+
+	for (heap_traversal_index = 0; heap_traversal_index < heap_size; heap_traversal_index++) {
+		interval_hook = ast_heap_peek(bridge_channel->features->interval_hooks, heap_traversal_index + 1);
+		if (interval_hook->callback == bridge_features_duration_callback) {
+			remaining = ast_tvdiff_ms(interval_hook->interval_trip_time, ast_tvnow()) / 1000;
+			break;
+		}
+	}
+
+	if (remaining > 0) {
+		unsigned int min = 0, sec = 0;
+
+		if ((remaining / 60) > 1) {
+			min = remaining / 60;
+			sec = remaining % 60;
+		} else {
+			sec = remaining;
+		}
+
+		ast_stream_and_wait(bridge_channel->chan, "vm-youhave", "");
+
+		if (min) {
+			ast_say_number(bridge_channel->chan, min, AST_DIGIT_ANY, ast_channel_language(bridge_channel->chan), NULL);
+			ast_stream_and_wait(bridge_channel->chan, "queue-minutes", "");
+		}
+
+		if (sec) {
+			ast_say_number(bridge_channel->chan, sec, AST_DIGIT_ANY, ast_channel_language(bridge_channel->chan), NULL);
+			ast_stream_and_wait(bridge_channel->chan, "queue-seconds", "");
+		}
+	}
+
+	/* It may be necessary to resume music on hold after we finish playing the announcment. */
+	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
+		ast_moh_start(bridge_channel->chan, NULL, NULL);
+	}
+
+	if (limits->frequency) {
+		ast_bridge_features_interval_update(bridge_channel, limits->frequency);
+	}
+
+
+	return !limits->frequency ? -1 : 0;
+}
+
+void ast_bridge_features_set_limits(struct ast_bridge_features *features, struct ast_bridge_features_limits *limits)
+{
+	if (!limits->duration) {
+		return;
+	}
+
+	ast_bridge_features_interval_hook(features, limits->duration, 0, bridge_features_duration_callback, limits, NULL);
+
+	if (limits->warning && limits->warning < limits->duration) {
+		ast_bridge_features_interval_hook(features, limits->warning, 1,
+						  !ast_strlen_zero(limits->warning_sound) ? bridge_features_warning_sound_callback : bridge_features_warning_time_left_callback,
+						  limits, NULL);
+	}
+}
+
 void ast_bridge_features_set_flag(struct ast_bridge_features *features, enum ast_bridge_feature_flags flag)
 {
 	ast_set_flag(&features->feature_flags, flag);
 	features->usable = 1;
 }
 
+static int interval_hook_time_cmp(void *a, void *b)
+{
+	int ret = ast_tvcmp(((struct ast_bridge_features_hook *) b)->interval_trip_time, ((struct ast_bridge_features_hook *) a)->interval_trip_time);
+	return ret;
+}
+
 int ast_bridge_features_init(struct ast_bridge_features *features)
 {
 	/* Zero out the structure */
@@ -1832,6 +2085,11 @@
 
 	/* Initialize the hooks list, just in case */
 	AST_LIST_HEAD_INIT_NOLOCK(&features->hooks);
+
+	/* Initialize the interval hook heap */
+	if (!features->interval_hooks) {
+		features->interval_hooks = ast_heap_create(8, interval_hook_time_cmp, offsetof(struct ast_bridge_features_hook, __heap_index));
+	}
 
 	return 0;
 }
@@ -1847,17 +2105,39 @@
 		}
 		ast_free(hook);
 	}
+
+	/* Interval hooks heap needs to be broken down in a similar fashion. */
+	if (features->interval_hooks) {
+		while ((hook = ast_heap_pop(features->interval_hooks))) {
+			if (hook->destructor) {
+				hook->destructor(hook->hook_pvt);
+			}
+			ast_free(hook);
+		}
+
+		ast_heap_destroy(features->interval_hooks);
+	}
+
 	if (features->talker_destructor_cb && features->talker_pvt_data) {
 		features->talker_destructor_cb(features->talker_pvt_data);
 		features->talker_pvt_data = NULL;
 	}
+
+	if (features->interval_timer) {
+		ast_timer_close(features->interval_timer);
+		features->interval_timer = NULL;
+	}
 }
 
 struct ast_bridge_features *ast_bridge_features_new(void)
 {
 	struct ast_bridge_features *features;
 
-	features = ast_calloc(1, sizeof(*features));
+	features = ast_malloc(sizeof(*features));
+	if (features) {
+		ast_bridge_features_init(features);
+	}
+
 	return features;
 }
 

Modified: team/jrose/bridge_projects/main/features.c
URL: http://svnview.digium.com/svn/asterisk/team/jrose/bridge_projects/main/features.c?view=diff&rev=380850&r1=380849&r2=380850
==============================================================================
--- team/jrose/bridge_projects/main/features.c (original)
+++ team/jrose/bridge_projects/main/features.c Tue Feb  5 11:42:27 2013
@@ -4385,6 +4385,42 @@
 /* BUGBUG dynamic features not handled yet.  App run returns non-zero breaks bridge and ast_bridge_call returns 0.  App returns zero continues bridge. */
 }
 
+static void bridge_config_set_limits_warning_values(struct ast_bridge_config *config, struct ast_bridge_features_limits *limits)
+{
+	ast_copy_string(limits->duration_sound, config->end_sound, sizeof(limits->duration_sound));
+
+	/* XXX 'timeleft' is a hard-coded value that is set when the argument isn't provided. This should probably be changed. */
+	if (strcmp("timeleft", config->warning_sound)) {
+		ast_copy_string(limits->warning_sound, config->warning_sound, sizeof(limits->warning_sound));
+	}
+
+	limits->frequency = config->warning_freq;
+	limits->warning = config->play_warning;
+}
+
+/*!
+ * \internal brief Setup limit hook structures on calls that need limits
+ *
+ * \param config ast_bridge_config which provides the limit data
+ * \param caller_limits pointer to an ast_bridge_features_limits struct which will store the caller side limits
+ * \param callee_limits pointer to an ast_bridge_features_limits struct which will store the callee side limits
+ */
+static void bridge_config_set_limits(struct ast_bridge_config *config, struct ast_bridge_features_limits *caller_limits, struct ast_bridge_features_limits *callee_limits)
+{
+	//ast_set_flag(&(config->features_callee), AST_FEATURE_PLAY_WARNING);
+	/* = {.duration = 20000, .duration_sound = "tt-weasels", .warning = 9800, .frequency = 10000}; */
+
+	if (ast_test_flag(&config->features_caller, AST_FEATURE_PLAY_WARNING)) {
+		bridge_config_set_limits_warning_values(config, caller_limits);
+	}
+
+	if (ast_test_flag(&config->features_callee, AST_FEATURE_PLAY_WARNING)) {
+		bridge_config_set_limits_warning_values(config, callee_limits);
+	}
+
+	caller_limits->duration = callee_limits->duration = config->timelimit;
+}
+
 /*!
  * \brief bridge the call and set CDR
  *
@@ -4417,6 +4453,9 @@
 	struct ast_bridge_features chan_features;
 	struct ast_bridge_features *peer_features;
 
+	/* These are used for the 'L' option of Dial/Bridge */
+	struct ast_bridge_features_limits call_duration_limits_chan = {0};
+	struct ast_bridge_features_limits call_duration_limits_peer = {0};
 
 	pbx_builtin_setvar_helper(chan, "BRIDGEPEER", ast_channel_name(peer));
 	pbx_builtin_setvar_helper(peer, "BRIDGEPEER", ast_channel_name(chan));
@@ -4592,6 +4631,12 @@
 			ast_cdr_discard(bridge_cdr);
 		}
 		return -1;
+	}
+
+	if (config->timelimit) {
+		bridge_config_set_limits(config, &call_duration_limits_chan, &call_duration_limits_peer);
+		ast_bridge_features_set_limits(&chan_features, &call_duration_limits_chan);
+		ast_bridge_features_set_limits(peer_features, &call_duration_limits_peer);
 	}
 
 	ast_cel_report_event(chan, AST_CEL_BRIDGE_START, NULL, NULL, peer);/* BUGBUG expected to go away. */




More information about the asterisk-commits mailing list