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:
parent
e3ff42aee6
commit
50716bb3e4
|
@ -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",
|
||||
);
|
||||
);
|
402
apps/app_mf.c
402
apps/app_mf.c
|
@ -18,7 +18,7 @@
|
|||
|
||||
/*! \file
|
||||
*
|
||||
* \brief App to send MF digits
|
||||
* \brief MF sender and receiver applications
|
||||
*
|
||||
* \author Naveen Albert <asterisk@phreaknet.org>
|
||||
*
|
||||
|
@ -31,22 +31,93 @@
|
|||
|
||||
#include "asterisk.h"
|
||||
|
||||
#include "asterisk/file.h"
|
||||
#include "asterisk/pbx.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/channel.h"
|
||||
#include "asterisk/dsp.h"
|
||||
#include "asterisk/app.h"
|
||||
#include "asterisk/module.h"
|
||||
#include "asterisk/indications.h"
|
||||
#include "asterisk/conversions.h"
|
||||
|
||||
/*** DOCUMENTATION
|
||||
<application name="ReceiveMF" language="en_US">
|
||||
<synopsis>
|
||||
Detects MF digits on a channel and saves them to a variable.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="variable" required="true">
|
||||
<para>The input digits will be stored in the given
|
||||
<replaceable>variable</replaceable> name.</para>
|
||||
</parameter>
|
||||
<parameter name="timeout">
|
||||
<para>The number of seconds to wait for all digits, if greater
|
||||
than <literal>0</literal>. Can be floating point. Default
|
||||
is no timeout.</para>
|
||||
</parameter>
|
||||
<parameter name="options">
|
||||
<optionlist>
|
||||
<option name="d">
|
||||
<para>Delay audio by a frame to try to extra quelch.</para>
|
||||
</option>
|
||||
<option name="l">
|
||||
<para>Receive digits even if a key pulse (KP) has not yet
|
||||
been received. By default, this application will ignore
|
||||
all other digits until a KP has been received.</para>
|
||||
</option>
|
||||
<option name="k">
|
||||
<para>Do not return a character for the KP digit.</para>
|
||||
</option>
|
||||
<option name="m">
|
||||
<para>Mute conference.</para>
|
||||
</option>
|
||||
<option name="o">
|
||||
<para>Enable override. Repeated KPs will clear all previous digits.</para>
|
||||
</option>
|
||||
<option name="q">
|
||||
<para>Quelch MF from in-band.</para>
|
||||
</option>
|
||||
<option name="r">
|
||||
<para>"Radio" mode (relaxed MF).</para>
|
||||
</option>
|
||||
<option name="s">
|
||||
<para>Do not return a character for ST digits.</para>
|
||||
</option>
|
||||
</optionlist>
|
||||
</parameter>
|
||||
</syntax>
|
||||
<description>
|
||||
<para>Reads a ST, STP, ST2P, or ST3P-terminated string of MF digits from
|
||||
the user in to the given <replaceable>variable</replaceable>.</para>
|
||||
<para>This application does not automatically answer the channel and
|
||||
should be preceded with <literal>Answer</literal> or
|
||||
<literal>Progress</literal> as needed.</para>
|
||||
<variablelist>
|
||||
<variable name="RECEIVEMFSTATUS">
|
||||
<para>This is the status of the read operation.</para>
|
||||
<value name="START" />
|
||||
<value name="ERROR" />
|
||||
<value name="HANGUP" />
|
||||
<value name="MAXDIGITS" />
|
||||
<value name="TIMEOUT" />
|
||||
</variable>
|
||||
</variablelist>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">Read</ref>
|
||||
<ref type="application">SendMF</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<application name="SendMF" language="en_US">
|
||||
<synopsis>
|
||||
Sends arbitrary MF digits
|
||||
Sends arbitrary MF digits on the current or specified channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<parameter name="digits" required="true">
|
||||
<para>List of digits 0-9,*#ABC to send; also f or F for a flash-hook
|
||||
if the channel supports flash-hook, and w or W for a wink if the channel
|
||||
supports wink.</para>
|
||||
<para>List of digits 0-9,*#ABC to send; w for a half-second pause,
|
||||
also f or F for a flash-hook if the channel supports flash-hook,
|
||||
h or H for 250 ms of 2600 Hz,
|
||||
and W for a wink if the channel supports wink.</para>
|
||||
<para>Key pulse and start digits are not included automatically.
|
||||
* is used for KP, # for ST, A for STP, B for ST2P, and C for ST3P.</para>
|
||||
</parameter>
|
||||
|
@ -70,12 +141,13 @@
|
|||
<para>It will send all digits or terminate if it encounters an error.</para>
|
||||
</description>
|
||||
<see-also>
|
||||
<ref type="application">ReceiveMF</ref>
|
||||
<ref type="application">SendDTMF</ref>
|
||||
</see-also>
|
||||
</application>
|
||||
<manager name="PlayMF" language="en_US">
|
||||
<synopsis>
|
||||
Play MF signal on a specific channel.
|
||||
Play MF digit on a specific channel.
|
||||
</synopsis>
|
||||
<syntax>
|
||||
<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
|
||||
|
@ -95,140 +167,218 @@
|
|||
</manager>
|
||||
***/
|
||||
|
||||
enum read_option_flags {
|
||||
OPT_DELAY = (1 << 0),
|
||||
OPT_MUTE = (1 << 1),
|
||||
OPT_QUELCH = (1 << 2),
|
||||
OPT_RELAXED = (1 << 3),
|
||||
OPT_LAX_KP = (1 << 4),
|
||||
OPT_PROCESS = (1 << 5),
|
||||
OPT_NO_KP = (1 << 6),
|
||||
OPT_NO_ST = (1 << 7),
|
||||
OPT_KP_OVERRIDE = (1 << 8),
|
||||
};
|
||||
|
||||
AST_APP_OPTIONS(read_app_options, {
|
||||
AST_APP_OPTION('d', OPT_DELAY),
|
||||
AST_APP_OPTION('l', OPT_LAX_KP),
|
||||
AST_APP_OPTION('k', OPT_NO_KP),
|
||||
AST_APP_OPTION('m', OPT_MUTE),
|
||||
AST_APP_OPTION('o', OPT_KP_OVERRIDE),
|
||||
AST_APP_OPTION('p', OPT_PROCESS),
|
||||
AST_APP_OPTION('q', OPT_QUELCH),
|
||||
AST_APP_OPTION('r', OPT_RELAXED),
|
||||
AST_APP_OPTION('s', OPT_NO_ST),
|
||||
});
|
||||
|
||||
static const char *readmf_name = "ReceiveMF";
|
||||
static const char sendmf_name[] = "SendMF";
|
||||
|
||||
#define DEFAULT_EMULATE_MF_DURATION 35
|
||||
#define MF_BETWEEN_MS 50
|
||||
#define MF_DURATION 55
|
||||
#define MF_KP_DURATION 120
|
||||
#define MF_ST_DURATION 65
|
||||
|
||||
static int senddigit_mf_begin(struct ast_channel *chan, char digit)
|
||||
{
|
||||
static const char * const mf_tones[] = {
|
||||
"1300+1500", /* 0 */
|
||||
"700+900", /* 1 */
|
||||
"700+1100", /* 2 */
|
||||
"900+1100", /* 3 */
|
||||
"700+1300", /* 4 */
|
||||
"900+1300", /* 5 */
|
||||
"1100+1300", /* 6 */
|
||||
"700+1500", /* 7 */
|
||||
"900+1500", /* 8 */
|
||||
"1100+1500", /* 9 */
|
||||
"1100+1700", /* * (KP) */
|
||||
"1500+1700", /* # (ST) */
|
||||
"900+1700", /* A (STP) */
|
||||
"1300+1700", /* B (ST2P) */
|
||||
"700+1700" /* C (ST3P) */
|
||||
};
|
||||
/*!
|
||||
* \brief Detects MF digits on channel using DSP, terminated by ST, STP, ST2P, or ST3P
|
||||
*
|
||||
* \param chan channel on which to read digits
|
||||
* \param buf Buffer in which to store digits
|
||||
* \param buflen Size of buffer
|
||||
* \param timeout ms to wait for all digits before giving up
|
||||
* \param features Any additional DSP features to use
|
||||
* \param override Start over if we receive additional KPs
|
||||
* \param no_kp Don't include KP in the output
|
||||
* \param no_st Don't include start digits in the output
|
||||
*
|
||||
* \retval 0 if successful
|
||||
* \retval -1 if unsuccessful.
|
||||
*/
|
||||
static int read_mf_digits(struct ast_channel *chan, char *buf, int buflen, int timeout, int features, int laxkp, int override, int no_kp, int no_st) {
|
||||
struct ast_dsp *dsp;
|
||||
struct ast_frame *frame = NULL;
|
||||
struct timeval start;
|
||||
int remaining_time = timeout;
|
||||
int digits_read = 0;
|
||||
int is_start_digit = 0;
|
||||
char *str = buf;
|
||||
|
||||
if (digit >= '0' && digit <='9') {
|
||||
ast_playtones_start(chan, 0, mf_tones[digit-'0'], 0);
|
||||
} else if (digit == '*') {
|
||||
ast_playtones_start(chan, 0, mf_tones[10], 0);
|
||||
} else if (digit == '#') {
|
||||
ast_playtones_start(chan, 0, mf_tones[11], 0);
|
||||
} else if (digit == 'A') {
|
||||
ast_playtones_start(chan, 0, mf_tones[12], 0);
|
||||
} else if (digit == 'B') {
|
||||
ast_playtones_start(chan, 0, mf_tones[13], 0);
|
||||
} else if (digit == 'C') {
|
||||
ast_playtones_start(chan, 0, mf_tones[14], 0);
|
||||
} else {
|
||||
/* not handled */
|
||||
ast_log(LOG_WARNING, "Unable to generate MF tone '%c' for '%s'\n", digit, ast_channel_name(chan));
|
||||
if (!(dsp = ast_dsp_new())) {
|
||||
ast_log(LOG_WARNING, "Unable to allocate DSP!\n");
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "ERROR");
|
||||
return -1;
|
||||
}
|
||||
ast_dsp_set_features(dsp, DSP_FEATURE_DIGIT_DETECT);
|
||||
ast_dsp_set_digitmode(dsp, DSP_DIGITMODE_MF | features);
|
||||
|
||||
start = ast_tvnow();
|
||||
*str = 0; /* start with empty output buffer */
|
||||
|
||||
/* based on app_read and generic_fax_exec from res_fax */
|
||||
while (timeout == 0 || remaining_time > 0) {
|
||||
if (timeout > 0) {
|
||||
remaining_time = ast_remaining_ms(start, timeout);
|
||||
if (remaining_time <= 0) {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "TIMEOUT");
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (digits_read >= (buflen - 1)) { /* we don't have room to store any more digits (very unlikely to happen for a legitimate reason) */
|
||||
/* This result will probably not be usable, so status should not be START */
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "MAXDIGITS");
|
||||
break;
|
||||
}
|
||||
/* ast_waitfordigit only waits for DTMF frames, we need to do DSP on voice frames */
|
||||
if (ast_waitfor(chan, 1000) > 0) {
|
||||
frame = ast_read(chan);
|
||||
if (!frame) {
|
||||
ast_debug(1, "Channel '%s' did not return a frame; probably hung up.\n", ast_channel_name(chan));
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "HANGUP");
|
||||
break;
|
||||
} else if (frame->frametype == AST_FRAME_VOICE) {
|
||||
frame = ast_dsp_process(chan, dsp, frame);
|
||||
/* AST_FRAME_DTMF is used all over the DSP code for DTMF, MF, fax, etc.
|
||||
It's used because we can use the frame to store the digit detected.
|
||||
All this means is that we received something we care about. */
|
||||
if (frame->frametype == AST_FRAME_DTMF) {
|
||||
char result = frame->subclass.integer;
|
||||
if (digits_read == 0 && !laxkp && result != '*') {
|
||||
ast_debug(1, "Received MF digit, but no KP yet, ignoring: %c\n", result);
|
||||
ast_frfree(frame);
|
||||
continue;
|
||||
}
|
||||
ast_debug(1, "Received MF digit: %c\n", result);
|
||||
if (result == '*') {
|
||||
/* We received an additional KP, start over? */
|
||||
if (override && digits_read > 0) {
|
||||
ast_debug(1, "Received another KP, starting over\n");
|
||||
str = buf;
|
||||
*str = 0;
|
||||
digits_read = 1; /* we just detected a KP */
|
||||
} else {
|
||||
digits_read++;
|
||||
}
|
||||
/* if we were told not to include the KP digit in the output string, then skip it */
|
||||
if (no_kp) {
|
||||
ast_frfree(frame);
|
||||
continue;
|
||||
}
|
||||
} else {
|
||||
digits_read++;
|
||||
}
|
||||
is_start_digit = (strchr("#", result) || strchr("A", result) || strchr("B", result) || strchr("C", result));
|
||||
/* if we were told not to include the ST digit in the output string, then skip it */
|
||||
if (!no_st || !is_start_digit) {
|
||||
*str++ = result; /* won't write past allotted memory, because of buffer check at top of loop */
|
||||
*str = 0;
|
||||
}
|
||||
/* we received a ST digit (ST, STP, ST2P, or ST3P), so we're done */
|
||||
if (is_start_digit) {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "START");
|
||||
ast_frfree(frame);
|
||||
break;
|
||||
}
|
||||
/* only free frame if it was a DSP match. The MF itself should not be muted. */
|
||||
ast_frfree(frame);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
pbx_builtin_setvar_helper(chan, "RECEIVEMFSTATUS", "HANGUP");
|
||||
}
|
||||
}
|
||||
ast_dsp_free(dsp);
|
||||
ast_debug(3, "channel '%s' - event loop stopped { timeout: %d, remaining_time: %d }\n", ast_channel_name(chan), timeout, remaining_time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int senddigit_mf_end(struct ast_channel *chan)
|
||||
static int read_mf_exec(struct ast_channel *chan, const char *data)
|
||||
{
|
||||
if (ast_channel_generator(chan)) {
|
||||
ast_playtones_stop(chan);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
#define BUFFER_SIZE 256
|
||||
char tmp[BUFFER_SIZE] = "";
|
||||
int to = 0;
|
||||
double tosec;
|
||||
struct ast_flags flags = {0};
|
||||
char *argcopy = NULL;
|
||||
int features = 0;
|
||||
|
||||
static int mysleep(struct ast_channel *chan, int ms, int is_external)
|
||||
{
|
||||
return is_external ? usleep(ms * 1000) : ast_safe_sleep(chan, ms);
|
||||
}
|
||||
AST_DECLARE_APP_ARGS(arglist,
|
||||
AST_APP_ARG(variable);
|
||||
AST_APP_ARG(timeout);
|
||||
AST_APP_ARG(options);
|
||||
);
|
||||
|
||||
static int senddigit_mf(struct ast_channel *chan, char digit, unsigned int duration,
|
||||
unsigned int durationkp, unsigned int durationst, int is_external)
|
||||
{
|
||||
if (duration < DEFAULT_EMULATE_MF_DURATION) {
|
||||
duration = DEFAULT_EMULATE_MF_DURATION;
|
||||
}
|
||||
if (ast_channel_tech(chan)->send_digit_begin) {
|
||||
if (digit == '*') {
|
||||
duration = durationkp;
|
||||
} else if (digit == '#' || digit == 'A' || digit == 'B' || digit == 'C') {
|
||||
duration = durationst;
|
||||
}
|
||||
senddigit_mf_begin(chan, digit);
|
||||
mysleep(chan, duration, is_external);
|
||||
}
|
||||
return senddigit_mf_end(chan);
|
||||
}
|
||||
|
||||
static int mf_stream(struct ast_channel *chan, const char *digits, int between, unsigned int duration,
|
||||
unsigned int durationkp, unsigned int durationst, int is_external)
|
||||
{
|
||||
const char *ptr;
|
||||
int res;
|
||||
struct ast_silence_generator *silgen = NULL;
|
||||
|
||||
if (!between) {
|
||||
between = 100;
|
||||
if (ast_strlen_zero(data)) {
|
||||
ast_log(LOG_WARNING, "ReceiveMF requires an argument (variable)\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Need a quiet time before sending digits. */
|
||||
if (ast_opt_transmit_silence) {
|
||||
silgen = ast_channel_start_silence_generator(chan);
|
||||
}
|
||||
res = mysleep(chan, 100, is_external);
|
||||
if (res) {
|
||||
goto mf_stream_cleanup;
|
||||
argcopy = ast_strdupa(data);
|
||||
|
||||
AST_STANDARD_APP_ARGS(arglist, argcopy);
|
||||
|
||||
if (!ast_strlen_zero(arglist.options)) {
|
||||
ast_app_parse_options(read_app_options, &flags, NULL, arglist.options);
|
||||
}
|
||||
|
||||
for (ptr = digits; *ptr; ptr++) {
|
||||
if (strchr("0123456789*#ABCwWfF", *ptr)) {
|
||||
if (*ptr == 'f' || *ptr == 'F') {
|
||||
/* ignore return values if not supported by channel */
|
||||
ast_indicate(chan, AST_CONTROL_FLASH);
|
||||
} else if (*ptr == 'w' || *ptr == 'W') {
|
||||
/* ignore return values if not supported by channel */
|
||||
ast_indicate(chan, AST_CONTROL_WINK);
|
||||
} else {
|
||||
/* Character represents valid MF */
|
||||
senddigit_mf(chan, *ptr, duration, durationkp, durationst, is_external);
|
||||
}
|
||||
/* pause between digits */
|
||||
/* The DSP code in Asterisk does not currently properly receive repeated tones
|
||||
if no audio is sent in the middle. Simply sending audio (even 0 Hz)
|
||||
works around this limitation and guarantees the correct behavior.
|
||||
*/
|
||||
ast_playtones_start(chan, 0, "0", 0);
|
||||
res = mysleep(chan, between, is_external);
|
||||
senddigit_mf_end(chan);
|
||||
if (res) {
|
||||
break;
|
||||
}
|
||||
if (!ast_strlen_zero(arglist.timeout)) {
|
||||
tosec = atof(arglist.timeout);
|
||||
if (tosec <= 0) {
|
||||
to = 0;
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Illegal MF character '%c' in string. (0-9*#ABCwWfF allowed)\n", *ptr);
|
||||
to = tosec * 1000.0;
|
||||
}
|
||||
}
|
||||
|
||||
mf_stream_cleanup:
|
||||
if (silgen) {
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
if (ast_strlen_zero(arglist.variable)) {
|
||||
ast_log(LOG_WARNING, "Invalid! Usage: ReceiveMF(variable[,timeout][,option])\n");
|
||||
return -1;
|
||||
}
|
||||
|
||||
return res;
|
||||
if (ast_test_flag(&flags, OPT_DELAY)) {
|
||||
features |= DSP_DIGITMODE_MUTEMAX;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_MUTE)) {
|
||||
features |= DSP_DIGITMODE_MUTECONF;
|
||||
}
|
||||
|
||||
if (!ast_test_flag(&flags, OPT_QUELCH)) {
|
||||
features |= DSP_DIGITMODE_NOQUELCH;
|
||||
}
|
||||
|
||||
if (ast_test_flag(&flags, OPT_RELAXED)) {
|
||||
features |= DSP_DIGITMODE_RELAXDTMF;
|
||||
}
|
||||
|
||||
read_mf_digits(chan, tmp, BUFFER_SIZE, to, features, (ast_test_flag(&flags, OPT_LAX_KP)),
|
||||
(ast_test_flag(&flags, OPT_KP_OVERRIDE)), (ast_test_flag(&flags, OPT_NO_KP)), (ast_test_flag(&flags, OPT_NO_ST)));
|
||||
pbx_builtin_setvar_helper(chan, arglist.variable, tmp);
|
||||
if (!ast_strlen_zero(tmp)) {
|
||||
ast_verb(3, "MF digits received: '%s'\n", tmp);
|
||||
} else {
|
||||
ast_verb(3, "No MF digits received.\n");
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sendmf_exec(struct ast_channel *chan, const char *vdata)
|
||||
|
@ -283,16 +433,9 @@ static int sendmf_exec(struct ast_channel *chan, const char *vdata)
|
|||
chan_autoservice = chan;
|
||||
}
|
||||
}
|
||||
if (chan_autoservice && ast_autoservice_start(chan_autoservice)) {
|
||||
ast_channel_cleanup(chan_found);
|
||||
return -1;
|
||||
}
|
||||
res = mf_stream(chan_dest, args.digits, dinterval <= 0 ? MF_BETWEEN_MS : dinterval,
|
||||
res = ast_mf_stream(chan_dest, chan_autoservice, NULL, args.digits, dinterval <= 0 ? MF_BETWEEN_MS : dinterval,
|
||||
duration <= 0 ? MF_DURATION : duration, durationkp <= 0 ? MF_KP_DURATION : durationkp,
|
||||
durationst <= 0 ? MF_ST_DURATION : durationst, 0);
|
||||
if (chan_autoservice && ast_autoservice_stop(chan_autoservice)) {
|
||||
res = -1;
|
||||
}
|
||||
ast_channel_cleanup(chan_found);
|
||||
|
||||
return chan_autoservice ? 0 : res;
|
||||
|
@ -330,7 +473,8 @@ static int manager_play_mf(struct mansession *s, const struct message *m)
|
|||
return 0;
|
||||
}
|
||||
|
||||
senddigit_mf(chan, *digit, duration_ms, duration_ms, duration_ms, 1);
|
||||
ast_mf_stream(chan, NULL, NULL, digit, 0, duration_ms, duration_ms, duration_ms, 1);
|
||||
|
||||
chan = ast_channel_unref(chan);
|
||||
|
||||
astman_send_ack(s, m, "MF successfully queued");
|
||||
|
@ -342,7 +486,8 @@ static int unload_module(void)
|
|||
{
|
||||
int res;
|
||||
|
||||
res = ast_unregister_application(sendmf_name);
|
||||
res = ast_unregister_application(readmf_name);
|
||||
res |= ast_unregister_application(sendmf_name);
|
||||
res |= ast_manager_unregister("PlayMF");
|
||||
|
||||
return res;
|
||||
|
@ -352,10 +497,11 @@ static int load_module(void)
|
|||
{
|
||||
int res;
|
||||
|
||||
res = ast_manager_register_xml("PlayMF", EVENT_FLAG_CALL, manager_play_mf);
|
||||
res = ast_register_application_xml(readmf_name, read_mf_exec);
|
||||
res |= ast_register_application_xml(sendmf_name, sendmf_exec);
|
||||
res |= ast_manager_register_xml("PlayMF", EVENT_FLAG_CALL, manager_play_mf);
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "Send MF digits Application");
|
||||
AST_MODULE_INFO_STANDARD_EXTENDED(ASTERISK_GPL_KEY, "MF Sender and Receiver Applications");
|
||||
|
|
|
@ -0,0 +1,5 @@
|
|||
Subject: app_mf
|
||||
|
||||
Adds MF receiver and sender applications to support
|
||||
the R1 MF signaling protocol, including integration
|
||||
with the Dial application.
|
|
@ -941,6 +941,39 @@ void ast_replace_sigchld(void);
|
|||
*/
|
||||
void ast_unreplace_sigchld(void);
|
||||
|
||||
/*!
|
||||
* \brief Send a string of MF digits to a channel
|
||||
*
|
||||
* \param chan The channel that will receive the MF digits.
|
||||
* \param peer (optional) Peer channel that will be autoserviced while the
|
||||
* primary channel is receiving MF
|
||||
* \param chan2 A second channel that will simultaneously receive MF digits.
|
||||
* This option may only be used if is_external is 0.
|
||||
* \param digits This is a string of characters representing the MF digits
|
||||
* to be sent to the channel. Valid characters are
|
||||
* "0123456789*#abcdABCD". Note: You can pass arguments 'f' or
|
||||
* 'F', if you want to Flash the channel (if supported by the
|
||||
* channel), or 'w' or 'W' to add a wink (if supported by the
|
||||
* channel).
|
||||
* \param between This is the number of milliseconds to wait in between each
|
||||
* MF digit. If zero milliseconds is specified, then the
|
||||
* default value of 50 will be used.
|
||||
* \param duration This is the duration that each numeric MF digit should have.
|
||||
* Default value is 55.
|
||||
* \param durationkp This is the duration that each KP digit should have. Default
|
||||
* is 120.
|
||||
* \param durationst This is the duration that each ST, STP, ST2P, or ST3P digit
|
||||
* should have. Default is 65.
|
||||
* \param is_external 1 if called by a thread that is not the channel's media
|
||||
* handler thread, 0 if called by the channel's media handler
|
||||
* thread.
|
||||
*
|
||||
* \retval 0 on success.
|
||||
* \retval -1 on failure or a channel hung up.
|
||||
*/
|
||||
int ast_mf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits,
|
||||
int between, unsigned int duration, unsigned int durationkp, unsigned int durationst, int is_external);
|
||||
|
||||
/*!
|
||||
* \brief Send a string of DTMF digits to a channel
|
||||
*
|
||||
|
|
|
@ -2242,6 +2242,30 @@ int ast_sendtext_data(struct ast_channel *chan, struct ast_msg_data *msg);
|
|||
*/
|
||||
int ast_recvchar(struct ast_channel *chan, int timeout);
|
||||
|
||||
/*!
|
||||
* \brief End sending an MF digit to a channel.
|
||||
* \param chan channel to act upon
|
||||
* \return Returns 0 on success, -1 on failure
|
||||
*/
|
||||
int ast_senddigit_mf_end(struct ast_channel *chan);
|
||||
|
||||
/*!
|
||||
* \brief Send an MF digit to a channel.
|
||||
*
|
||||
* \param chan channel to act upon
|
||||
* \param digit the MF digit to send, encoded in ASCII
|
||||
* \param duration the duration of a numeric digit ending in ms
|
||||
* \param duration the duration of a KP digit ending in ms
|
||||
* \param duration the duration of a ST, STP, ST2P, or ST3P digit ending in ms
|
||||
* \param is_external 1 if called by a thread that is not the channel's media
|
||||
* handler thread, 0 if called by the channel's media handler
|
||||
* thread.
|
||||
*
|
||||
* \return 0 on success, -1 on failure
|
||||
*/
|
||||
int ast_senddigit_mf(struct ast_channel *chan, char digit, unsigned int duration,
|
||||
unsigned int durationkp, unsigned int durationst, int is_external);
|
||||
|
||||
/*!
|
||||
* \brief Send a DTMF digit to a channel.
|
||||
*
|
||||
|
@ -2269,6 +2293,14 @@ int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration);
|
|||
*/
|
||||
int ast_senddigit_external(struct ast_channel *chan, char digit, unsigned int duration);
|
||||
|
||||
/*!
|
||||
* \brief Send an MF digit to a channel.
|
||||
* \param chan channel to act upon
|
||||
* \param digit the MF digit to send, encoded in ASCII
|
||||
* \return 0 on success, -1 on failure
|
||||
*/
|
||||
int ast_senddigit_mf_begin(struct ast_channel *chan, char digit);
|
||||
|
||||
/*!
|
||||
* \brief Send a DTMF digit to a channel.
|
||||
* \param chan channel to act upon
|
||||
|
|
123
main/app.c
123
main/app.c
|
@ -830,6 +830,115 @@ static int external_sleep(struct ast_channel *chan, int ms)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int mf_stream(struct ast_channel *chan, struct ast_channel *chan2, const char *digits, int between, unsigned int duration,
|
||||
unsigned int durationkp, unsigned int durationst, int is_external)
|
||||
{
|
||||
const char *ptr;
|
||||
int res;
|
||||
struct ast_silence_generator *silgen = NULL, *silgen2 = NULL;
|
||||
int (*my_sleep)(struct ast_channel *chan, int ms);
|
||||
|
||||
if (is_external) {
|
||||
my_sleep = external_sleep;
|
||||
} else {
|
||||
my_sleep = ast_safe_sleep;
|
||||
}
|
||||
|
||||
if (!between) {
|
||||
between = 100;
|
||||
}
|
||||
|
||||
/* Need a quiet time before sending digits. */
|
||||
if (ast_opt_transmit_silence) {
|
||||
silgen = ast_channel_start_silence_generator(chan);
|
||||
if (chan2) {
|
||||
silgen2 = ast_channel_start_silence_generator(chan2);
|
||||
}
|
||||
}
|
||||
if (chan2) {
|
||||
ast_autoservice_start(chan2);
|
||||
}
|
||||
res = my_sleep(chan, 100);
|
||||
if (chan2) {
|
||||
ast_autoservice_stop(chan2);
|
||||
}
|
||||
if (res) {
|
||||
goto mf_stream_cleanup;
|
||||
}
|
||||
|
||||
for (ptr = digits; *ptr; ptr++) {
|
||||
if (*ptr == 'w') {
|
||||
/* 'w' -- wait half a second */
|
||||
res = my_sleep(chan, 500);
|
||||
if (res) {
|
||||
break;
|
||||
}
|
||||
} else if (*ptr == 'h' || *ptr == 'H') {
|
||||
/* 'h' -- 2600 Hz for half a second, but
|
||||
only to far end of trunk, not near end */
|
||||
ast_playtones_start(chan, 0, "2600", 0);
|
||||
if (chan2) {
|
||||
ast_playtones_start(chan2, 0, "0", 0);
|
||||
ast_autoservice_start(chan2);
|
||||
}
|
||||
res = my_sleep(chan, 250);
|
||||
ast_senddigit_mf_end(chan);
|
||||
if (chan2) {
|
||||
ast_autoservice_stop(chan2);
|
||||
ast_senddigit_mf_end(chan2);
|
||||
}
|
||||
if (res) {
|
||||
break;
|
||||
}
|
||||
} else if (strchr("0123456789*#ABCwWfF", *ptr)) {
|
||||
if (*ptr == 'f' || *ptr == 'F') {
|
||||
/* ignore return values if not supported by channel */
|
||||
ast_indicate(chan, AST_CONTROL_FLASH);
|
||||
} else if (*ptr == 'W') {
|
||||
/* ignore return values if not supported by channel */
|
||||
ast_indicate(chan, AST_CONTROL_WINK);
|
||||
} else {
|
||||
/* Character represents valid MF */
|
||||
ast_senddigit_mf(chan, *ptr, duration, durationkp, durationst, is_external);
|
||||
if (chan2) {
|
||||
ast_senddigit_mf(chan2, *ptr, duration, durationkp, durationst, is_external);
|
||||
}
|
||||
}
|
||||
/* pause between digits */
|
||||
/* The DSP code in Asterisk does not currently properly receive repeated tones
|
||||
if no audio is sent in the middle. Simply sending audio (even 0 Hz)
|
||||
works around this limitation and guarantees the correct behavior.
|
||||
*/
|
||||
ast_playtones_start(chan, 0, "0", 0);
|
||||
if (chan2) {
|
||||
ast_playtones_start(chan2, 0, "0", 0);
|
||||
ast_autoservice_start(chan2);
|
||||
}
|
||||
res = my_sleep(chan, between);
|
||||
ast_senddigit_mf_end(chan);
|
||||
if (chan2) {
|
||||
ast_autoservice_stop(chan2);
|
||||
ast_senddigit_mf_end(chan2);
|
||||
}
|
||||
if (res) {
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
ast_log(LOG_WARNING, "Illegal MF character '%c' in string. (0-9*#ABCwWfFhH allowed)\n", *ptr);
|
||||
}
|
||||
}
|
||||
|
||||
mf_stream_cleanup:
|
||||
if (silgen) {
|
||||
ast_channel_stop_silence_generator(chan, silgen);
|
||||
}
|
||||
if (silgen2) {
|
||||
ast_channel_stop_silence_generator(chan2, silgen2);
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
||||
|
||||
static int dtmf_stream(struct ast_channel *chan, const char *digits, int between, unsigned int duration, int is_external)
|
||||
{
|
||||
const char *ptr;
|
||||
|
@ -898,6 +1007,20 @@ dtmf_stream_cleanup:
|
|||
return res;
|
||||
}
|
||||
|
||||
int ast_mf_stream(struct ast_channel *chan, struct ast_channel *peer, struct ast_channel *chan2, const char *digits,
|
||||
int between, unsigned int duration, unsigned int durationkp, unsigned int durationst, int is_external)
|
||||
{
|
||||
int res;
|
||||
if (!is_external && !chan2 && peer && ast_autoservice_start(peer)) {
|
||||
return -1;
|
||||
}
|
||||
res = mf_stream(chan, chan2, digits, between, duration, durationkp, durationst, is_external);
|
||||
if (!is_external && !chan2 && peer && ast_autoservice_stop(peer)) {
|
||||
res = -1;
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration)
|
||||
{
|
||||
int res;
|
||||
|
|
|
@ -97,10 +97,14 @@ unsigned long global_fin, global_fout;
|
|||
AST_THREADSTORAGE(state2str_threadbuf);
|
||||
#define STATE2STR_BUFSIZE 32
|
||||
|
||||
/*! Default amount of time to use when emulating a digit as a begin and end
|
||||
/*! Default amount of time to use when emulating a DTMF digit as a begin and end
|
||||
* 100ms */
|
||||
#define AST_DEFAULT_EMULATE_DTMF_DURATION 100
|
||||
|
||||
/*! Default amount of time to use when emulating an MF digit as a begin and end
|
||||
* 55ms */
|
||||
#define DEFAULT_EMULATE_MF_DURATION 55
|
||||
|
||||
#define DEFAULT_AMA_FLAGS AST_AMA_DOCUMENTATION
|
||||
|
||||
/*! Minimum amount of time between the end of the last digit and the beginning
|
||||
|
@ -4856,6 +4860,45 @@ int ast_sendtext(struct ast_channel *chan, const char *text)
|
|||
return rc;
|
||||
}
|
||||
|
||||
int ast_senddigit_mf_begin(struct ast_channel *chan, char digit)
|
||||
{
|
||||
static const char * const mf_tones[] = {
|
||||
"1300+1500", /* 0 */
|
||||
"700+900", /* 1 */
|
||||
"700+1100", /* 2 */
|
||||
"900+1100", /* 3 */
|
||||
"700+1300", /* 4 */
|
||||
"900+1300", /* 5 */
|
||||
"1100+1300", /* 6 */
|
||||
"700+1500", /* 7 */
|
||||
"900+1500", /* 8 */
|
||||
"1100+1500", /* 9 */
|
||||
"1100+1700", /* * (KP) */
|
||||
"1500+1700", /* # (ST) */
|
||||
"900+1700", /* A (STP) */
|
||||
"1300+1700", /* B (ST2P) */
|
||||
"700+1700" /* C (ST3P) */
|
||||
};
|
||||
|
||||
if (digit >= '0' && digit <='9') {
|
||||
ast_playtones_start(chan, 0, mf_tones[digit-'0'], 0);
|
||||
} else if (digit == '*') {
|
||||
ast_playtones_start(chan, 0, mf_tones[10], 0);
|
||||
} else if (digit == '#') {
|
||||
ast_playtones_start(chan, 0, mf_tones[11], 0);
|
||||
} else if (digit == 'A') {
|
||||
ast_playtones_start(chan, 0, mf_tones[12], 0);
|
||||
} else if (digit == 'B') {
|
||||
ast_playtones_start(chan, 0, mf_tones[13], 0);
|
||||
} else if (digit == 'C') {
|
||||
ast_playtones_start(chan, 0, mf_tones[14], 0);
|
||||
} else {
|
||||
/* not handled */
|
||||
ast_log(LOG_WARNING, "Unable to generate MF tone '%c' for '%s'\n", digit, ast_channel_name(chan));
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
int ast_senddigit_begin(struct ast_channel *chan, char digit)
|
||||
{
|
||||
/* Device does not support DTMF tones, lets fake
|
||||
|
@ -4925,6 +4968,37 @@ int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duratio
|
|||
return 0;
|
||||
}
|
||||
|
||||
int ast_senddigit_mf_end(struct ast_channel *chan)
|
||||
{
|
||||
if (ast_channel_generator(chan)) {
|
||||
ast_playtones_stop(chan);
|
||||
return 0;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
int ast_senddigit_mf(struct ast_channel *chan, char digit, unsigned int duration,
|
||||
unsigned int durationkp, unsigned int durationst, int is_external)
|
||||
{
|
||||
if (duration < DEFAULT_EMULATE_MF_DURATION) {
|
||||
duration = DEFAULT_EMULATE_MF_DURATION;
|
||||
}
|
||||
if (ast_channel_tech(chan)->send_digit_begin) {
|
||||
if (digit == '*') {
|
||||
duration = durationkp;
|
||||
} else if (digit == '#' || digit == 'A' || digit == 'B' || digit == 'C') {
|
||||
duration = durationst;
|
||||
}
|
||||
ast_senddigit_mf_begin(chan, digit);
|
||||
if (is_external) {
|
||||
usleep(duration * 1000);
|
||||
} else {
|
||||
ast_safe_sleep(chan, duration);
|
||||
}
|
||||
}
|
||||
return ast_senddigit_mf_end(chan);
|
||||
}
|
||||
|
||||
int ast_senddigit(struct ast_channel *chan, char digit, unsigned int duration)
|
||||
{
|
||||
if (duration < AST_DEFAULT_EMULATE_DTMF_DURATION) {
|
||||
|
|
Loading…
Reference in New Issue