Well, this is an interesting one, since exposing things through ARI has a few hoops to jump through that other parts of Asterisk don’t really have. (They’re good hoops, and they all exist for a reason - but it is a bit more involved.)
Anyway, a few wiki pages you could read up on:
-
How to write an ARI resource - this is the most appropriate. You won’t need to write an entire resource, but the act of updating an existing resource (in this case,
bridges
) will require a lot of the same steps. I’ll step through it next. - When you have a patch that you’d like to contribute back, you’ll want to take a look at the patch contribution process. I walked through how to contribute a patch in a series of blog posts on the Asterisk blog awhile back, which may also be helpful (there’s some administrative steps involved).
- Being aware of the project coding guidelines is also always good, if for no other reason than it helps you get through code review faster
Anyway, assuming you have the source code cloned from Gerrit, here’s how I’d approach this particular problem.
- In the Swagger specification -
rest-api/api-docs/bridges.json
- add a new parameter to either bridge creation or adding a channel to a bridge that allows an API user to specify how the DTMF should be passed through. I’d probably do it to"/bridges/{bridgeId}/addChannel"
, since DTMF absorption is handled on a channel-by-channel basis. Something like:
"parameters": [
{
"name": "bridgeId",
"description": "Bridge's id",
"paramType": "path",
"required": true,
"allowMultiple": false,
"dataType": "string"
},
{
"name": "channel",
"description": "Ids of channels to add to bridge",
"paramType": "query",
"required": true,
"allowMultiple": true,
"dataType": "string"
},
{
"name": "role",
"description": "Channel's role in the bridge",
"paramType": "query",
"required": false,
"allowMultiple": false,
"dataType": "string"
},
{
"name": "absorbDTMF",
"description": "Absorb DTMF from this channel, preventing it from passing through the bridge",
"paramType": "query",
"required": false,
"allowMultiple": false,
"dataType": "boolean",
"defaultValue": false
}
-
Once the Swagger specification has been updated, you can re-generate the HTTP bindings using
make ari-stubs
. This will look at your JSON file and re-buildres/res_ari_bridges.c
, along withres/ari/resource_bridges.h
. The generated header file defines the API that we have to write ourselves, in this case,res/ari/resource_bridges.c
. In particular, it will updatestruct ast_ari_bridges_add_channel_args
to contain a new parameter,absorb_dtmf
, that we specified in Swagger. The code inres_ari_bridges
will handle the HTTP callbacks from Asterisk’s HTTP server, and parse the query parameters into an instance ofast_ari_bridges_add_channel_args
. -
With the bindings re-generated, we have to implement things in
resource_bridges.c
. Look forast_ari_bridges_add_channel
, which starts the process of adding a channel to a bridge. This is where things get tricky: ARI is asynchronous, which we means we have to pass the information from the HTTP callback handler (ast_ari_bridges_add_channel
) to the thread servicing the channel in the Stasis application. You can see this occurring whenstasis_app_control_add_channel_to_bridge
callsapp_send_command_on_condition
, which passes thebridge
as thedata
pointer, withcontrol_add_channel_to_bridge
as the callback that will be executed. While we could wrap both thebridge
andabsorb_dtmf
value in a new struct, we’re probably better off just storing this on thestasis_app_control
data structure, as that would allow us to eventually allow other bridge features to be passed in as well. In fact, we could just put astruct ast_bridge_features
on there.
struct stasis_app_control {
...
struct ast_bridge_features features;
}
-
Since this is an opaque structure, we’d need to provide our own accessors for it in
res/stasis/control.h
. I’d probably just add something that allows us to set that DTMF should be absorbed, something likestasis_app_control_absorb_dtmf_in_bridge
. -
Make sure to initialize the bridge features via
ast_bridge_features_init
incontrol_create
and destroy them viaast_bridge_features_cleanup
incontrol_dtor
. -
We’d then have something like this in
ast_ari_bridges_add_channel
:
for (i = 0; i < list->count; i++) {
/* role stuff here */
if (args->absorb_dtmf) {
stasis_app_control_absorb_dtmf_in_bridge(lists->controls[i]);
}
}
for (i = 0; i < list->count; ++i) {
if ((has_error = check_add_remove_channel(response, list->controls[i],
stasis_app_control_add_channel_to_bridge(
list->controls[i], bridge)))) {
break;
}
}
- Now we have to make use of this in the Stasis application’s control handler for adding a channel to a bridge. In
res/stasis/control.c
'scontrol_swap_channel_in_bridge
, pass the bridge features on the control structure intoast_bridge_impart
. You can see where that is currentlyNULL
in the code today. With it now on the control structure, it would look like this:
res = ast_bridge_impart(bridge,
chan,
swap,
control->bridge_features,
AST_BRIDGE_IMPART_CHAN_DEPARTABLE);
I know that’s a lot of moving pieces and parts, but it won’t end up being a lot of code changes - just small ones in a variety of places to get the needed data down into the Stasis control code.
Since I haven’t actually written any of this, you may find that there are some drawbacks or issues with what I suggested, so if your mileage starts to vary, feel free to try something else. You can always reach out on the asterisk-dev mailing list or in #asterisk-dev as well if you’d like, as that’s where questions regarding coding issues are usually asked/answered.