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.
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) {
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.
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;
}
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) {
#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"
* When a channel is in a bridge, the bridge that it is in.
*/
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.
*/
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)
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;
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
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
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;
"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": [