summaryrefslogtreecommitdiff
path: root/main/bridging.c
diff options
context:
space:
mode:
authorRichard Mudgett <rmudgett@digium.com>2013-06-06 22:46:54 +0000
committerRichard Mudgett <rmudgett@digium.com>2013-06-06 22:46:54 +0000
commitbad8caa8c65ac1937ae1c85a0c5fb45f3e8a7fe4 (patch)
treed077d5d84638645d10508c15bb6f00538e8e1f64 /main/bridging.c
parent2dc8a060064f359a17f5ebcd515d85fe5203c019 (diff)
Reimplement bridging and DTMF features related channel variables in the bridging core.
* The channel variable ATTENDED_TRANSFER_COMPLETE_SOUND is no longer channel driver specific. If the channel variable is set on the transferrer channel, the sound will be played to the target of an attended transfer. * The channel variable BRIDGEPEER becomes a comma separated list of peers in a multi-party bridge. The BRIDGEPEER value can have a maximum of 10 peers listed. Any more peers in the bridge will not be included in the list. BRIDGEPEER is not valid in holding bridges like parking since those channels do not talk to each other even though they are in a bridge. * The channel variable BRIDGEPVTCALLID is only valid for two party bridges and will contain a value if the BRIDGEPEER's channel driver supports it. * The channel variable DYNAMIC_PEERNAME is redundant with BRIDGEPEER and is removed. The more useful DYNAMIC_WHO_ACTIVATED gives the channel name that activated the dynamic feature. * The channel variables DYNAMIC_FEATURENAME and DYNAMIC_WHO_ACTIVATED are set only on the channel executing the dynamic feature. Executing a dynamic feature on the bridge peer in a multi-party bridge will execute it on all peers of the activating channel. (closes issue ASTERISK-21555) Reported by: Matt Jordan Review: https://reviewboard.asterisk.org/r/2582/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@390771 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/bridging.c')
-rw-r--r--main/bridging.c361
1 files changed, 357 insertions, 4 deletions
diff --git a/main/bridging.c b/main/bridging.c
index c59638736..d5d17ae9e 100644
--- a/main/bridging.c
+++ b/main/bridging.c
@@ -588,6 +588,9 @@ static int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
bridge_channel_pull(swap);
}
+ /* Clear any BLINDTRANSFER since the transfer has completed. */
+ pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL);
+
bridge->reconfigured = 1;
ast_bridge_publish_enter(bridge, bridge_channel->chan);
return 0;
@@ -922,6 +925,68 @@ void ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel
bridge_channel, custom_play, playfile, moh_class);
}
+struct bridge_custom_callback {
+ /*! Call this function on the bridge channel thread. */
+ ast_bridge_custom_callback_fn callback;
+ /*! Size of the payload if it exists. A number otherwise. */
+ size_t payload_size;
+ /*! Nonzero if the payload exists. */
+ char payload_exists;
+ /*! Payload to give to callback. */
+ char payload[0];
+};
+
+/*!
+ * \internal
+ * \brief Handle the do custom callback 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_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data)
+{
+ data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size);
+}
+
+static void payload_helper_cb(ast_bridge_channel_post_action_data post_it,
+ struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+{
+ struct bridge_custom_callback *cb_data;
+ size_t len_data = sizeof(*cb_data) + (payload ? payload_size : 0);
+
+ /* Sanity check. */
+ if (!callback) {
+ ast_assert(0);
+ return;
+ }
+
+ /* Fill in custom callback frame data. */
+ cb_data = alloca(len_data);
+ cb_data->callback = callback;
+ cb_data->payload_size = payload_size;
+ cb_data->payload_exists = payload && payload_size;
+ if (cb_data->payload_exists) {
+ memcpy(cb_data->payload, payload, payload_size);/* Safe */
+ }
+
+ post_it(bridge_channel, AST_BRIDGE_ACTION_CALLBACK, cb_data, len_data);
+}
+
+void ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+{
+ payload_helper_cb(ast_bridge_channel_write_action_data,
+ bridge_channel, callback, payload, payload_size);
+}
+
+void ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+{
+ payload_helper_cb(ast_bridge_channel_queue_action_data,
+ bridge_channel, callback, payload, payload_size);
+}
+
struct bridge_park {
int parker_uuid_offset;
int app_data_offset;
@@ -1715,6 +1780,253 @@ static int smart_bridge_operation(struct ast_bridge *bridge)
/*!
* \internal
+ * \brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to check.
+ *
+ * \return Nothing
+ */
+static void check_bridge_play_sound(struct ast_bridge_channel *bridge_channel)
+{
+ const char *play_file;
+
+ ast_channel_lock(bridge_channel->chan);
+ play_file = pbx_builtin_getvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND");
+ if (!ast_strlen_zero(play_file)) {
+ play_file = ast_strdupa(play_file);
+ pbx_builtin_setvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND", NULL);
+ } else {
+ play_file = NULL;
+ }
+ ast_channel_unlock(bridge_channel->chan);
+
+ if (play_file) {
+ ast_bridge_channel_queue_playfile(bridge_channel, NULL, play_file, NULL);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ *
+ * \note On entry, the bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void check_bridge_play_sounds(struct ast_bridge *bridge)
+{
+ struct ast_bridge_channel *bridge_channel;
+
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ check_bridge_play_sound(bridge_channel);
+ }
+}
+
+static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
+{
+ pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name);
+ pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid);
+}
+
+/*!
+ * \internal
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge.
+ * \since 12.0.0
+ *
+ * \param c0 Party of the first part.
+ * \param c1 Party of the second part.
+ *
+ * \note On entry, the bridge is already locked.
+ * \note The bridge is expected to have exactly two parties.
+ *
+ * \return Nothing
+ */
+static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_channel *c1)
+{
+ const char *c0_name;
+ const char *c1_name;
+ const char *c0_pvtid = NULL;
+ const char *c1_pvtid = NULL;
+#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid) \
+ do { \
+ name = ast_strdupa(ast_channel_name(chan)); \
+ if (ast_channel_tech(chan)->get_pvt_uniqueid) { \
+ pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan)); \
+ } \
+ } while (0)
+
+ ast_channel_lock(c1);
+ UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid);
+ ast_channel_unlock(c1);
+
+ ast_channel_lock(c0);
+ update_bridge_vars_set(c0, c1_name, c1_pvtid);
+ UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid);
+ ast_channel_unlock(c0);
+
+ ast_channel_lock(c1);
+ update_bridge_vars_set(c1, c0_name, c0_pvtid);
+ ast_channel_unlock(c1);
+}
+
+/*!
+ * \internal
+ * \brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names.
+ * \since 12.0.0
+ *
+ * \param buf Buffer to fill. The caller must guarantee the buffer is large enough.
+ * \param cur_idx Which index into names[] to skip.
+ * \param names Channel names to put in the buffer.
+ * \param num_names Number of names in the array.
+ *
+ * \return Nothing
+ */
+static void fill_bridgepeer_buf(char *buf, unsigned int cur_idx, const char *names[], unsigned int num_names)
+{
+ int need_separator = 0;
+ unsigned int idx;
+ const char *src;
+ char *pos;
+
+ pos = buf;
+ for (idx = 0; idx < num_names; ++idx) {
+ if (idx == cur_idx) {
+ continue;
+ }
+
+ if (need_separator) {
+ *pos++ = ',';
+ }
+ need_separator = 1;
+
+ /* Copy name into buffer. */
+ src = names[idx];
+ while (*src) {
+ *pos++ = *src++;
+ }
+ }
+ *pos = '\0';
+}
+
+/*!
+ * \internal
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi-party bridge.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ *
+ * \note On entry, the bridge is already locked.
+ * \note The bridge is expected to have more than two parties.
+ *
+ * \return Nothing
+ */
+static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge)
+{
+/*
+ * Set a maximum number of channel names for the BRIDGEPEER
+ * list. The plus one is for the current channel which is not
+ * put in the list.
+ */
+#define MAX_BRIDGEPEER_CHANS (10 + 1)
+
+ unsigned int idx;
+ unsigned int num_names;
+ unsigned int len;
+ const char **names;
+ char *buf;
+ struct ast_bridge_channel *bridge_channel;
+
+ /* Get first MAX_BRIDGEPEER_CHANS channel names. */
+ num_names = MIN(bridge->num_channels, MAX_BRIDGEPEER_CHANS);
+ names = ast_alloca(num_names * sizeof(*names));
+ idx = 0;
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ if (num_names <= idx) {
+ break;
+ }
+ ast_channel_lock(bridge_channel->chan);
+ names[idx++] = ast_strdupa(ast_channel_name(bridge_channel->chan));
+ ast_channel_unlock(bridge_channel->chan);
+ }
+
+ /* Determine maximum buf size needed. */
+ len = num_names;
+ for (idx = 0; idx < num_names; ++idx) {
+ len += strlen(names[idx]);
+ }
+ buf = ast_alloca(len);
+
+ /* Set the bridge channel variables. */
+ idx = 0;
+ buf[0] = '\0';
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ if (idx < num_names) {
+ fill_bridgepeer_buf(buf, idx, names, num_names);
+ }
+ ++idx;
+
+ ast_channel_lock(bridge_channel->chan);
+ update_bridge_vars_set(bridge_channel->chan, buf, NULL);
+ ast_channel_unlock(bridge_channel->chan);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ *
+ * \note On entry, the bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void set_bridge_peer_vars_holding(struct ast_bridge *bridge)
+{
+ struct ast_bridge_channel *bridge_channel;
+
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ ast_channel_lock(bridge_channel->chan);
+ update_bridge_vars_set(bridge_channel->chan, NULL, NULL);
+ ast_channel_unlock(bridge_channel->chan);
+ }
+}
+
+/*!
+ * \internal
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ *
+ * \note On entry, the bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void set_bridge_peer_vars(struct ast_bridge *bridge)
+{
+ if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) {
+ set_bridge_peer_vars_holding(bridge);
+ return;
+ }
+ if (bridge->num_channels < 2) {
+ return;
+ }
+ if (bridge->num_channels == 2) {
+ set_bridge_peer_vars_2party(AST_LIST_FIRST(&bridge->channels)->chan,
+ AST_LIST_LAST(&bridge->channels)->chan);
+ } else {
+ set_bridge_peer_vars_multiparty(bridge);
+ }
+}
+
+/*!
+ * \internal
* \brief Notify the bridge that it has been reconfigured.
* \since 12.0.0
*
@@ -1743,6 +2055,12 @@ static void bridge_reconfigured(struct ast_bridge *bridge)
return;
}
bridge_complete_join(bridge);
+
+ if (bridge->dissolved) {
+ return;
+ }
+ check_bridge_play_sounds(bridge);
+ set_bridge_peer_vars(bridge);
}
/*!
@@ -2123,17 +2441,24 @@ static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_chann
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
bridge_channel_unsuspend(bridge_channel);
break;
- case AST_BRIDGE_ACTION_PARK:
+ case AST_BRIDGE_ACTION_RUN_APP:
bridge_channel_suspend(bridge_channel);
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
- bridge_channel_park(bridge_channel, action->data.ptr);
+ bridge_channel_run_app(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:
+ case AST_BRIDGE_ACTION_CALLBACK:
bridge_channel_suspend(bridge_channel);
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
- bridge_channel_run_app(bridge_channel, action->data.ptr);
+ bridge_channel_do_callback(bridge_channel, action->data.ptr);
+ ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+ bridge_channel_unsuspend(bridge_channel);
+ break;
+ case AST_BRIDGE_ACTION_PARK:
+ bridge_channel_suspend(bridge_channel);
+ ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+ bridge_channel_park(bridge_channel, action->data.ptr);
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
bridge_channel_unsuspend(bridge_channel);
break;
@@ -5684,8 +6009,36 @@ enum ast_transfer_result ast_bridge_transfer_attended(struct ast_channel *to_tra
}
if (to_target_bridge_channel) {
+ const char *target_complete_sound;
+
/* Take off hold if they are on hold. */
ast_bridge_channel_write_unhold(to_target_bridge_channel);
+
+ /* Is there a courtesy sound to play to the target? */
+ ast_channel_lock(to_transfer_target);
+ target_complete_sound = pbx_builtin_getvar_helper(to_transfer_target,
+ "ATTENDED_TRANSFER_COMPLETE_SOUND");
+ if (!ast_strlen_zero(target_complete_sound)) {
+ target_complete_sound = ast_strdupa(target_complete_sound);
+ } else {
+ target_complete_sound = NULL;
+ }
+ ast_channel_unlock(to_transfer_target);
+ if (!target_complete_sound) {
+ ast_channel_lock(to_transferee);
+ target_complete_sound = pbx_builtin_getvar_helper(to_transferee,
+ "ATTENDED_TRANSFER_COMPLETE_SOUND");
+ if (!ast_strlen_zero(target_complete_sound)) {
+ target_complete_sound = ast_strdupa(target_complete_sound);
+ } else {
+ target_complete_sound = NULL;
+ }
+ ast_channel_unlock(to_transferee);
+ }
+ if (target_complete_sound) {
+ ast_bridge_channel_write_playfile(to_target_bridge_channel, NULL,
+ target_complete_sound, NULL);
+ }
}
/* Let's get the easy one out of the way first */