How to STOP DTMF tones being heard in Bridge

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:

  1. 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.
  2. 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).
  3. 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 :slight_smile:

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-build res/res_ari_bridges.c, along with res/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 update struct ast_ari_bridges_add_channel_args to contain a new parameter, absorb_dtmf, that we specified in Swagger. The code in res_ari_bridges will handle the HTTP callbacks from Asterisk’s HTTP server, and parse the query parameters into an instance of ast_ari_bridges_add_channel_args.

  • With the bindings re-generated, we have to implement things in resource_bridges.c. Look for ast_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 when stasis_app_control_add_channel_to_bridge calls app_send_command_on_condition, which passes the bridge as the data pointer, with control_add_channel_to_bridge as the callback that will be executed. While we could wrap both the bridge and absorb_dtmf value in a new struct, we’re probably better off just storing this on the stasis_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 a struct 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 like stasis_app_control_absorb_dtmf_in_bridge.

  • Make sure to initialize the bridge features via ast_bridge_features_init in control_create and destroy them via ast_bridge_features_cleanup in control_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's control_swap_channel_in_bridge, pass the bridge features on the control structure into ast_bridge_impart. You can see where that is currently NULL 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.

1 Like