ari/bridge: Add mute, dtmf suppression controls
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
This commit is contained in:
parent
1505c1bb09
commit
e8bde6916a
|
@ -792,6 +792,38 @@ int stasis_app_control_add_channel_to_bridge(
|
||||||
int stasis_app_control_remove_channel_from_bridge(
|
int stasis_app_control_remove_channel_from_bridge(
|
||||||
struct stasis_app_control *control, struct ast_bridge *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
|
* \since 12
|
||||||
* \brief Gets the bridge currently associated with a control object.
|
* \brief Gets the bridge currently associated with a control object.
|
||||||
|
|
|
@ -218,6 +218,12 @@ void ast_ari_bridges_add_channel(struct ast_variable *headers,
|
||||||
return;
|
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) {
|
for (i = 0; i < list->count; ++i) {
|
||||||
|
|
|
@ -150,6 +150,10 @@ struct ast_ari_bridges_add_channel_args {
|
||||||
char *channel_parse;
|
char *channel_parse;
|
||||||
/*! Channel's role in the bridge */
|
/*! Channel's role in the bridge */
|
||||||
const char *role;
|
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.
|
* \brief Body parsing function for /bridges/{bridgeId}/addChannel.
|
||||||
|
|
|
@ -430,6 +430,14 @@ int ast_ari_bridges_add_channel_parse_body(
|
||||||
if (field) {
|
if (field) {
|
||||||
args->role = ast_json_string_get(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;
|
return 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -499,6 +507,12 @@ static void ast_ari_bridges_add_channel_cb(
|
||||||
if (strcmp(i->name, "role") == 0) {
|
if (strcmp(i->name, "role") == 0) {
|
||||||
args.role = (i->value);
|
args.role = (i->value);
|
||||||
} else
|
} 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) {
|
for (i = path_vars; i; i = i->next) {
|
||||||
|
|
|
@ -35,6 +35,7 @@
|
||||||
#include "asterisk/bridge.h"
|
#include "asterisk/bridge.h"
|
||||||
#include "asterisk/bridge_after.h"
|
#include "asterisk/bridge_after.h"
|
||||||
#include "asterisk/bridge_basic.h"
|
#include "asterisk/bridge_basic.h"
|
||||||
|
#include "asterisk/bridge_features.h"
|
||||||
#include "asterisk/frame.h"
|
#include "asterisk/frame.h"
|
||||||
#include "asterisk/pbx.h"
|
#include "asterisk/pbx.h"
|
||||||
#include "asterisk/musiconhold.h"
|
#include "asterisk/musiconhold.h"
|
||||||
|
@ -61,6 +62,10 @@ struct stasis_app_control {
|
||||||
* When a channel is in a bridge, the bridge that it is in.
|
* When a channel is in a bridge, the bridge that it is in.
|
||||||
*/
|
*/
|
||||||
struct ast_bridge *bridge;
|
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.
|
* Holding place for channel's PBX while imparted to a bridge.
|
||||||
*/
|
*/
|
||||||
|
@ -99,6 +104,8 @@ static void control_dtor(void *obj)
|
||||||
ast_cond_destroy(&control->wait_cond);
|
ast_cond_destroy(&control->wait_cond);
|
||||||
AST_LIST_HEAD_DESTROY(&control->add_rules);
|
AST_LIST_HEAD_DESTROY(&control->add_rules);
|
||||||
AST_LIST_HEAD_DESTROY(&control->remove_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)
|
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 control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap)
|
||||||
{
|
{
|
||||||
int res;
|
int res;
|
||||||
|
struct ast_bridge_features *features;
|
||||||
|
|
||||||
if (!control || !bridge) {
|
if (!control || !bridge) {
|
||||||
return -1;
|
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);
|
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);
|
ast_assert(stasis_app_get_bridge(control) == NULL);
|
||||||
/* We need to set control->bridge here since bridge_after_cb may be run
|
/* 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
|
* 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,
|
res = ast_bridge_impart(bridge,
|
||||||
chan,
|
chan,
|
||||||
swap,
|
swap,
|
||||||
NULL, /* features */
|
features, /* features */
|
||||||
AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
|
AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
|
||||||
if (res != 0) {
|
if (res != 0) {
|
||||||
/* ast_bridge_impart failed before it could spawn the depart
|
/* 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);
|
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)
|
void control_flush_queue(struct stasis_app_control *control)
|
||||||
{
|
{
|
||||||
struct ao2_iterator iter;
|
struct ao2_iterator iter;
|
||||||
|
|
|
@ -169,6 +169,24 @@
|
||||||
"required": false,
|
"required": false,
|
||||||
"allowMultiple": false,
|
"allowMultiple": false,
|
||||||
"dataType": "string"
|
"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": [
|
"errorResponses": [
|
||||||
|
|
Loading…
Reference in New Issue