How to STOP DTMF tones being heard in Bridge


#1

We are using Asterisk 13.8-cert1 on Ubuntu. We are using Stasis, with an external .Net box running the application.

Our application makes extensive use of bridges, and we use a specific DTMF sequence when a participant in the bridge needs help. Our problem is that everyone in the bridge can hear the tones being generated (this seems to be the reverse of most DTMF problems, where the tone is not being picked up). Our bridges are initialised with the mixing,dtmf_events option.

We’ve tried various combinations in pjsip,.conf for dtmf_mode (rfc4733, info, inband and auto) and we can still hear the tones.

How can we configure Asterisk running an ARI app so that it picks up the tones, but does not retransmit them?

Colin Dixon


#2

While the bridging framework in Asterisk does have the ability to toggle passthrough of DTMF, it doesn’t expose this ability through ARI. As you’ve discovered, the default is to allow the DTMF to pass through. In order to manipulate this, ARI would have to be updated to expose that either on bridge creation or when a channel is added to a bridge (the DTMF passthrough can be set on a channel-by-channel basis).


#4

Hello Matt, and thank you. Looking through the source, I can see that you are the person to ask!

I’m a C# developer, spending most of my time on .Net WCF apps, but I once was a C++ ATL developer, so I can maybe take a shot at this. I’m happy with Asterisk and compiling from source. Where could I find information about the entry points to the code to make the change, in order to turn off DTMF passthrough universally for an app (or in the worst case, universally. It’s the only app that will be running on the box)?


#5

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.


#6

I am extremely grateful for this, and looking forward to making an attempt at it. Thank you for taking the time to answer this question so very thoroughly.

Colin Dixon


#7

I had a go at this, and succeeded (up to a point). There is a problem creating the ast_bridge_features on the stasis_app_control. All goes well, but when I move the channel from one bridge to another (from ARI, again), execution stops dead in ast_bridge_features_destroy (bridge.c) when ast_free is called as part of cleaning up the bridge features prior to the move. I’ve solved the problem by creating a new ast_bridge_features in control_swap_channel_in_bridge, and setting the dtmf_passthrough property there. It’s not elegant, but it works for my use case, in which all conferences can cope without audible DTMF. I guess I could do a deep copy there, but my C is really rusty, and I’d be pushing it.

Thanks again for your help. It was a lot of fun. I’d like to do more of this.


#8

Hello all,

I am having the same problem, and I wonder if there is any configuration option that will make a default that all created bridges will not have dtmf pass-through (that will be honored by ARI too, of cource)?


#9

Patch is up here


#10

Feature is now in master