ari/bridge: Add mute, dtmf suppression controls
authorSeán C McCord <ulexus@gmail.com>
Sat, 7 Oct 2017 01:48:48 +0000 (21:48 -0400)
committerSeán C McCord <ulexus@gmail.com>
Wed, 11 Oct 2017 12:23:05 +0000 (08:23 -0400)
Add bridge_features structure to bridge creation.  Specifically, this
implements mute and DTMF suppression, but others should be able to be
easily added to the same structure.

ASTERISK-27322 #close
Reported by: Darren Sessions
Sponsored by: AVOXI

Change-Id: Id4002adfb65c9a8027ee9e1a5f477e0f01cf9d61

include/asterisk/stasis_app.h
res/ari/resource_bridges.c
res/ari/resource_bridges.h
res/res_ari_bridges.c
res/stasis/control.c
rest-api/api-docs/bridges.json

index ca5c251..8ef2bda 100644 (file)
@@ -793,6 +793,38 @@ int stasis_app_control_remove_channel_from_bridge(
        struct stasis_app_control *control, struct ast_bridge *bridge);
 
 /*!
+ * \brief Initialize bridge features into a channel control
+ *
+ * \note Bridge features on a control are destroyed after each bridge session,
+ *       so new features need to be initialized before each bridge add.
+ *
+ * \param control Control in which to store the features
+ *
+ * \return non-zero on failure
+ * \return zero on success
+ */
+int stasis_app_control_bridge_features_init(
+       struct stasis_app_control *control);
+
+/*!
+ * \brief Set whether DTMF from the channel is absorbed instead of passing through to the bridge
+ *
+ * \param control Control whose channel should have its DTMF absorbed when bridged
+ * \param absorb Whether DTMF should be absorbed (1) instead of passed through (0).
+ */
+void stasis_app_control_absorb_dtmf_in_bridge(
+       struct stasis_app_control *control, int absorb);
+
+/*!
+ * \brief Set whether audio from the channel is muted instead of passing through to the bridge
+ *
+ * \param control Control whose channel should have its audio muted when bridged
+ * \param mute Whether audio should be muted (1) instead of passed through (0).
+ */
+void stasis_app_control_mute_in_bridge(
+       struct stasis_app_control *control, int mute);
+
+/*!
  * \since 12
  * \brief Gets the bridge currently associated with a control object.
  *
index f243086..aab78ce 100644 (file)
@@ -218,6 +218,12 @@ void ast_ari_bridges_add_channel(struct ast_variable *headers,
                                return;
                        }
                }
+
+               /* Apply bridge features to each of the channel controls */
+               if (!stasis_app_control_bridge_features_init(list->controls[i])) {
+                       stasis_app_control_absorb_dtmf_in_bridge(list->controls[i], args->absorb_dtmf);
+                       stasis_app_control_mute_in_bridge(list->controls[i], args->mute);
+               }
        }
 
        for (i = 0; i < list->count; ++i) {
index e75d8e0..72dba1e 100644 (file)
@@ -150,6 +150,10 @@ struct ast_ari_bridges_add_channel_args {
        char *channel_parse;
        /*! Channel's role in the bridge */
        const char *role;
+       /*! Absorb DTMF coming from this channel, preventing it to pass through to the bridge */
+       int absorb_dtmf;
+       /*! Mute audio from this channel, preventing it to pass through to the bridge */
+       int mute;
 };
 /*!
  * \brief Body parsing function for /bridges/{bridgeId}/addChannel.
index 65bf7ed..35fd3bd 100644 (file)
@@ -430,6 +430,14 @@ int ast_ari_bridges_add_channel_parse_body(
        if (field) {
                args->role = ast_json_string_get(field);
        }
+       field = ast_json_object_get(body, "absorbDTMF");
+       if (field) {
+               args->absorb_dtmf = ast_json_is_true(field);
+       }
+       field = ast_json_object_get(body, "mute");
+       if (field) {
+               args->mute = ast_json_is_true(field);
+       }
        return 0;
 }
 
@@ -499,6 +507,12 @@ static void ast_ari_bridges_add_channel_cb(
                if (strcmp(i->name, "role") == 0) {
                        args.role = (i->value);
                } else
+               if (strcmp(i->name, "absorbDTMF") == 0) {
+                       args.absorb_dtmf = ast_true(i->value);
+               } else
+               if (strcmp(i->name, "mute") == 0) {
+                       args.mute = ast_true(i->value);
+               } else
                {}
        }
        for (i = path_vars; i; i = i->next) {
index 01c04df..dc005a1 100644 (file)
@@ -35,6 +35,7 @@
 #include "asterisk/bridge.h"
 #include "asterisk/bridge_after.h"
 #include "asterisk/bridge_basic.h"
+#include "asterisk/bridge_features.h"
 #include "asterisk/frame.h"
 #include "asterisk/pbx.h"
 #include "asterisk/musiconhold.h"
@@ -62,6 +63,10 @@ struct stasis_app_control {
         */
        struct ast_bridge *bridge;
        /*!
+        * Bridge features which should be applied to the channel when it enters the next bridge.  These only apply to the next bridge and will be emptied thereafter.
+        */
+       struct ast_bridge_features *bridge_features;
+       /*!
         * Holding place for channel's PBX while imparted to a bridge.
         */
        struct ast_pbx *pbx;
@@ -99,6 +104,8 @@ static void control_dtor(void *obj)
        ast_cond_destroy(&control->wait_cond);
        AST_LIST_HEAD_DESTROY(&control->add_rules);
        AST_LIST_HEAD_DESTROY(&control->remove_rules);
+       ast_bridge_features_destroy(control->bridge_features);
+
 }
 
 struct stasis_app_control *control_create(struct ast_channel *channel, struct stasis_app *app)
@@ -1161,6 +1168,7 @@ static void set_interval_hook(struct ast_channel *chan)
 int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap)
 {
        int res;
+       struct ast_bridge_features *features;
 
        if (!control || !bridge) {
                return -1;
@@ -1205,6 +1213,10 @@ int control_swap_channel_in_bridge(struct stasis_app_control *control, struct as
                ast_channel_pbx_set(chan, NULL);
        }
 
+       /* Pull bridge features from the control */
+       features = control->bridge_features;
+       control->bridge_features = NULL;
+
        ast_assert(stasis_app_get_bridge(control) == NULL);
        /* We need to set control->bridge here since bridge_after_cb may be run
         * before ast_bridge_impart returns.  bridge_after_cb gets a reason
@@ -1220,7 +1232,7 @@ int control_swap_channel_in_bridge(struct stasis_app_control *control, struct as
        res = ast_bridge_impart(bridge,
                chan,
                swap,
-               NULL, /* features */
+               features, /* features */
                AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
        if (res != 0) {
                /* ast_bridge_impart failed before it could spawn the depart
@@ -1316,6 +1328,31 @@ int stasis_app_control_queue_control(struct stasis_app_control *control,
        return ast_queue_control(control->channel, frame_type);
 }
 
+int stasis_app_control_bridge_features_init(
+       struct stasis_app_control *control)
+{
+       struct ast_bridge_features *features;
+
+       features = ast_bridge_features_new();
+       if (!features) {
+               return 1;
+       }
+       control->bridge_features = features;
+       return 0;
+}
+
+void stasis_app_control_absorb_dtmf_in_bridge(
+       struct stasis_app_control *control, int absorb)
+{
+       control->bridge_features->dtmf_passthrough = !absorb;
+}
+
+void stasis_app_control_mute_in_bridge(
+       struct stasis_app_control *control, int mute)
+{
+       control->bridge_features->mute = mute;
+}
+
 void control_flush_queue(struct stasis_app_control *control)
 {
        struct ao2_iterator iter;
index 877fdf8..cf8ee31 100644 (file)
                                                        "required": false,
                                                        "allowMultiple": false,
                                                        "dataType": "string"
+                                               },
+                                               {
+                                                       "name": "absorbDTMF",
+                                                       "description": "Absorb DTMF coming from this channel, preventing it to pass through to the bridge",
+                                                       "paramType": "query",
+                                                       "required": false,
+                                                       "allowMultiple": false,
+                                                       "dataType": "boolean",
+                                                       "defaultValue": false
+                                               },
+                                               {
+                                                       "name": "mute",
+                                                       "description": "Mute audio from this channel, preventing it to pass through to the bridge",
+                                                       "paramType": "query",
+                                                       "required": false,
+                                                       "allowMultiple": false,
+                                                       "dataType": "boolean",
+                                                       "defaultValue": false
                                                }
                                        ],
                                        "errorResponses": [