app_mf: Add full tech-agnostic MF support

Adds tech-agnostic support for MF signaling by adding
MF sender and receiver applications as well as Dial
integration.

ASTERISK-29496-mf #do-not-close

Change-Id: I61962b359b8ec4cfd05df877ddf9f5b8f71927a4
This commit is contained in:
Naveen Albert
2021-09-22 00:18:08 +00:00
committed by Friendly Automation
parent 55dd77b921
commit 2b2b708d43
7 changed files with 590 additions and 146 deletions

View File

@@ -156,6 +156,8 @@
<argument name="called" />
<argument name="calling" />
<argument name="progress" />
<argument name="mfprogress" />
<argument name="mfwink" />
<para>Send the specified DTMF strings <emphasis>after</emphasis> the called
party has answered, but before the call gets bridged. The
<replaceable>called</replaceable> DTMF string is sent to the called party, and the
@@ -163,6 +165,15 @@
can be used alone. If <replaceable>progress</replaceable> is specified, its DTMF is sent
to the called party immediately after receiving a <literal>PROGRESS</literal> message.</para>
<para>See <literal>SendDTMF</literal> for valid digits.</para>
<para>If <replaceable>mfprogress</replaceable> is specified, its MF is sent
to the called party immediately after receiving a <literal>PROGRESS</literal> message.
If <replaceable>mfwink</replaceable> is specified, its MF is sent
to the called party immediately after receiving a <literal>WINK</literal> message.</para>
<para>See <literal>SendMF</literal> for valid digits.</para>
</option>
<option name="E">
<para>Enable echoing of sent MF or SF digits back to caller (e.g. "hearpulsing").
Used in conjunction with the D option.</para>
</option>
<option name="e">
<para>Execute the <literal>h</literal> extension for peer after the call ends</para>
@@ -499,7 +510,6 @@
answered, if it has not already been answered. These two channels will then
be active in a bridged call. All other channels that were requested will then
be hung up.</para>
<para>Unless there is a timeout specified, the Dial application will wait
indefinitely until one of the called channels answers, the user hangs up, or
if all of the called channels are busy or unavailable. Dialplan execution will
@@ -512,7 +522,6 @@
If the <variable>OUTBOUND_GROUP_ONCE</variable> variable is set, all peer channels created by this
application will be put into that group (as in <literal>Set(GROUP()=...</literal>). Unlike <variable>OUTBOUND_GROUP</variable>,
however, the variable will be unset after use.</para>
<example title="Dial with 30 second timeout">
same => n,Dial(PJSIP/alice,30)
</example>
@@ -534,28 +543,22 @@
</example>
<example title="Dial with pre-dial subroutines">
[default]
exten => callee_channel,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})
same => n,Log(NOTICE, I'm called on channel ${CHANNEL} prior to it starting the dial attempt)
same => n,Return()
exten => called_channel,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})
same => n,Log(NOTICE, I'm called on outbound channel ${CHANNEL} prior to it being used to dial someone)
same => n,Return()
exten => _X.,1,NoOp()
same => n,Dial(PJSIP/alice,,b(default^called_channel^1(my_gosub_arg1^my_gosub_arg2))B(default^callee_channel^1(my_gosub_arg1^my_gosub_arg2)))
same => n,Hangup()
</example>
<example title="Dial with post-answer subroutine executed on outbound channel">
[my_gosub_routine]
exten => s,1,NoOp(ARG1=${ARG1} ARG2=${ARG2})
same => n,Playback(hello)
same => n,Return()
[default]
exten => _X.,1,NoOp()
same => n,Dial(PJSIP/alice,,U(my_gosub_routine^my_gosub_arg1^my_gosub_arg2))
same => n,Hangup()
@@ -717,6 +720,7 @@ enum {
#define OPT_PREDIAL_CALLER (1LLU << 42)
#define OPT_RING_WITH_EARLY_MEDIA (1LLU << 43)
#define OPT_HANGUPCAUSE (1LLU << 44)
#define OPT_HEARPULSING (1LLU << 45)
enum {
OPT_ARG_ANNOUNCE = 0,
@@ -752,6 +756,7 @@ AST_APP_OPTIONS(dial_exec_options, BEGIN_OPTIONS
AST_APP_OPTION('c', OPT_CANCEL_ELSEWHERE),
AST_APP_OPTION('d', OPT_DTMF_EXIT),
AST_APP_OPTION_ARG('D', OPT_SENDDTMF, OPT_ARG_SENDDTMF),
AST_APP_OPTION('E', OPT_HEARPULSING),
AST_APP_OPTION('e', OPT_PEER_H),
AST_APP_OPTION_ARG('f', OPT_FORCECLID, OPT_ARG_FORCECLID),
AST_APP_OPTION_ARG('F', OPT_CALLEE_GO_ON, OPT_ARG_CALLEE_GO_ON),
@@ -1208,6 +1213,8 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
char *opt_args[],
struct privacy_args *pa,
const struct cause_args *num_in, int *result, char *dtmf_progress,
char *mf_progress, char *mf_wink,
const int hearpulsing,
const int ignore_cc,
struct ast_party_id *forced_clid, struct ast_party_id *stored_clid,
struct ast_bridge_config *config)
@@ -1227,7 +1234,7 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
int cc_frame_received = 0;
int num_ringing = 0;
int sent_ring = 0;
int sent_progress = 0;
int sent_progress = 0, sent_wink = 0;
struct timeval start = ast_tvnow();
SCOPE_ENTER(3, "%s\n", ast_channel_name(in));
@@ -1565,6 +1572,14 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
ast_channel_unlock(in);
sent_progress = 1;
if (!ast_strlen_zero(mf_progress)) {
ast_verb(3,
"Sending MF '%s' to %s as result of "
"receiving a PROGRESS message.\n",
mf_progress, hearpulsing ? "parties" : "called party");
ast_mf_stream(c, (hearpulsing ? NULL : in),
(hearpulsing ? in : NULL), mf_progress, 50, 55, 120, 65, 0);
}
if (!ast_strlen_zero(dtmf_progress)) {
ast_verb(3,
"Sending DTMF '%s' to the called party as result of "
@@ -1575,6 +1590,20 @@ static struct ast_channel *wait_for_answer(struct ast_channel *in,
}
ast_channel_publish_dial(in, c, NULL, "PROGRESS");
break;
case AST_CONTROL_WINK:
ast_verb(3, "%s winked, passing it to %s\n", ast_channel_name(c), ast_channel_name(in));
if (!sent_wink) {
sent_wink = 1;
if (!ast_strlen_zero(mf_wink)) {
ast_verb(3,
"Sending MF '%s' to %s as result of "
"receiving a WINK message.\n",
mf_wink, (hearpulsing ? "parties" : "called party"));
ast_mf_stream(c, (hearpulsing ? NULL : in),
(hearpulsing ? in : NULL), mf_wink, 50, 55, 120, 65, 0);
}
}
break;
case AST_CONTROL_VIDUPDATE:
case AST_CONTROL_SRCUPDATE:
case AST_CONTROL_SRCCHANGE:
@@ -2132,9 +2161,7 @@ static int setup_privacy_args(struct privacy_args *pa,
/* the file doesn't exist yet. Let the caller submit his
vocal intro for posterity */
/* priv-recordintro script:
"At the tone, please say your name:"
*/
int silencethreshold = ast_dsp_get_threshold_from_settings(THRESHOLD_SILENCE);
ast_answer(chan);
@@ -2249,7 +2276,8 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
struct ast_bridge_config config = { { 0, } };
struct timeval calldurationlimit = { 0, };
char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress=NULL;
char *dtmfcalled = NULL, *dtmfcalling = NULL, *dtmf_progress = NULL;
char *mf_progress = NULL, *mf_wink = NULL;
struct privacy_args pa = {
.sentringing = 0,
.privdb_val = 0,
@@ -2385,9 +2413,11 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
}
if (ast_test_flag64(&opts, OPT_SENDDTMF) && !ast_strlen_zero(opt_args[OPT_ARG_SENDDTMF])) {
dtmf_progress = opt_args[OPT_ARG_SENDDTMF];
dtmfcalled = strsep(&dtmf_progress, ":");
dtmfcalling = strsep(&dtmf_progress, ":");
mf_wink = opt_args[OPT_ARG_SENDDTMF];
dtmfcalled = strsep(&mf_wink, ":");
dtmfcalling = strsep(&mf_wink, ":");
dtmf_progress = strsep(&mf_wink, ":");
mf_progress = strsep(&mf_wink, ":");
}
if (ast_test_flag64(&opts, OPT_DURATION_LIMIT) && !ast_strlen_zero(opt_args[OPT_ARG_DURATION_LIMIT])) {
@@ -2864,7 +2894,8 @@ static int dial_exec_full(struct ast_channel *chan, const char *data, struct ast
}
peer = wait_for_answer(chan, &out_chans, &to, peerflags, opt_args, &pa, &num, &result,
dtmf_progress, ignore_cc, &forced_clid, &stored_clid, &config);
dtmf_progress, mf_progress, mf_wink, (ast_test_flag64(&opts, OPT_HEARPULSING) ? 1 : 0),
ignore_cc, &forced_clid, &stored_clid, &config);
if (!peer) {
if (result) {
@@ -3542,4 +3573,4 @@ AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Dialing Application",
.load = load_module,
.unload = unload_module,
.requires = "ccss",
);
);