diff --git a/include/asterisk/app.h b/include/asterisk/app.h index 5b10b1c1c7..0865121e23 100644 --- a/include/asterisk/app.h +++ b/include/asterisk/app.h @@ -923,24 +923,51 @@ void ast_replace_sigchld(void); void ast_unreplace_sigchld(void); /*! - \brief Send DTMF to a channel - - \param chan The channel that will receive the DTMF frames - \param peer (optional) Peer channel that will be autoserviced while the - primary channel is receiving DTMF - \param digits This is a string of characters representing the DTMF 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' to add a 500 millisecond pause to the DTMF - sequence. - \param between This is the number of milliseconds to wait in between each - DTMF digit. If zero milliseconds is specified, then the - default value of 100 will be used. - \param duration This is the duration that each DTMF digit should have. -*/ + * \brief Send a string of DTMF digits to a channel + * + * \param chan The channel that will receive the DTMF frames + * \param peer (optional) Peer channel that will be autoserviced while the + * primary channel is receiving DTMF + * \param digits This is a string of characters representing the DTMF 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' to add a 500 millisecond pause to the DTMF + * sequence. + * \param between This is the number of milliseconds to wait in between each + * DTMF digit. If zero milliseconds is specified, then the + * default value of 100 will be used. + * \param duration This is the duration that each DTMF digit should have. + * + * \pre This must only be called by the channel's media handler thread. + * + * \retval 0 on success. + * \retval -1 on failure or a channel hung up. + */ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration); +/*! + * \brief Send a string of DTMF digits to a channel from an external thread. + * + * \param chan The channel that will receive the DTMF frames + * \param digits This is a string of characters representing the DTMF 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' to add a 500 millisecond pause to the DTMF + * sequence. + * \param between This is the number of milliseconds to wait in between each + * DTMF digit. If zero milliseconds is specified, then the + * default value of 100 will be used. + * \param duration This is the duration that each DTMF digit should have. + * + * \pre This must only be called by threads that are not the channel's + * media handler thread. + * + * \return Nothing + */ +void ast_dtmf_stream_external(struct ast_channel *chan, const char *digits, int between, unsigned int duration); + /*! \brief Stream a filename (or file descriptor) as a generator. */ int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, int allowoverride); diff --git a/main/app.c b/main/app.c index 331c827506..e7aab10b1f 100644 --- a/main/app.c +++ b/main/app.c @@ -875,25 +875,37 @@ int ast_vm_test_destroy_user(const char *context, const char *mailbox) } #endif -int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration) +static int external_sleep(struct ast_channel *chan, int ms) +{ + usleep(ms * 1000); + return 0; +} + +static int dtmf_stream(struct ast_channel *chan, const char *digits, int between, unsigned int duration, int is_external) { const char *ptr; int res; struct ast_silence_generator *silgen = NULL; + int (*my_sleep)(struct ast_channel *chan, int ms); + int (*my_senddigit)(struct ast_channel *chan, char digit, unsigned int duration); - if (!between) { - between = 100; + if (is_external) { + my_sleep = external_sleep; + my_senddigit = ast_senddigit_external; + } else { + my_sleep = ast_safe_sleep; + my_senddigit = ast_senddigit; } - if (peer && ast_autoservice_start(peer)) { - return -1; + if (!between) { + between = 100; } /* Need a quiet time before sending digits. */ if (ast_opt_transmit_silence) { silgen = ast_channel_start_silence_generator(chan); } - res = ast_safe_sleep(chan, 100); + res = my_sleep(chan, 100); if (res) { goto dtmf_stream_cleanup; } @@ -901,12 +913,14 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch for (ptr = digits; *ptr; ptr++) { if (*ptr == 'w') { /* 'w' -- wait half a second */ - if ((res = ast_safe_sleep(chan, 500))) { + res = my_sleep(chan, 500); + if (res) { break; } } else if (*ptr == 'W') { /* 'W' -- wait a second */ - if ((res = ast_safe_sleep(chan, 1000))) { + res = my_sleep(chan, 1000); + if (res) { break; } } else if (strchr("0123456789*#abcdfABCDF", *ptr)) { @@ -915,10 +929,11 @@ int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const ch ast_indicate(chan, AST_CONTROL_FLASH); } else { /* Character represents valid DTMF */ - ast_senddigit(chan, *ptr, duration); + my_senddigit(chan, *ptr, duration); } /* pause between digits */ - if ((res = ast_safe_sleep(chan, between))) { + res = my_sleep(chan, between); + if (res) { break; } } else { @@ -930,6 +945,18 @@ dtmf_stream_cleanup: if (silgen) { ast_channel_stop_silence_generator(chan, silgen); } + + return res; +} + +int ast_dtmf_stream(struct ast_channel *chan, struct ast_channel *peer, const char *digits, int between, unsigned int duration) +{ + int res; + + if (peer && ast_autoservice_start(peer)) { + return -1; + } + res = dtmf_stream(chan, digits, between, duration, 0); if (peer && ast_autoservice_stop(peer)) { res = -1; } @@ -937,6 +964,11 @@ dtmf_stream_cleanup: return res; } +void ast_dtmf_stream_external(struct ast_channel *chan, const char *digits, int between, unsigned int duration) +{ + dtmf_stream(chan, digits, between, duration, 1); +} + struct linear_state { int fd; int autoclose; diff --git a/res/stasis/control.c b/res/stasis/control.c index df97931a74..1821f20849 100644 --- a/res/stasis/control.c +++ b/res/stasis/control.c @@ -503,15 +503,21 @@ struct stasis_app_control_dtmf_data { char dtmf[]; }; -static int app_control_dtmf(struct stasis_app_control *control, - struct ast_channel *chan, void *data) +static void dtmf_in_bridge(struct ast_channel *chan, struct stasis_app_control_dtmf_data *dtmf_data) { - struct stasis_app_control_dtmf_data *dtmf_data = data; + if (dtmf_data->before) { + usleep(dtmf_data->before * 1000); + } - if (ast_channel_state(chan) != AST_STATE_UP) { - ast_indicate(chan, AST_CONTROL_PROGRESS); + ast_dtmf_stream_external(chan, dtmf_data->dtmf, dtmf_data->between, dtmf_data->duration); + + if (dtmf_data->after) { + usleep(dtmf_data->after * 1000); } +} +static void dtmf_no_bridge(struct ast_channel *chan, struct stasis_app_control_dtmf_data *dtmf_data) +{ if (dtmf_data->before) { ast_safe_sleep(chan, dtmf_data->before); } @@ -521,6 +527,22 @@ static int app_control_dtmf(struct stasis_app_control *control, if (dtmf_data->after) { ast_safe_sleep(chan, dtmf_data->after); } +} + +static int app_control_dtmf(struct stasis_app_control *control, + struct ast_channel *chan, void *data) +{ + struct stasis_app_control_dtmf_data *dtmf_data = data; + + if (ast_channel_state(chan) != AST_STATE_UP) { + ast_indicate(chan, AST_CONTROL_PROGRESS); + } + + if (stasis_app_get_bridge(control)) { + dtmf_in_bridge(chan, dtmf_data); + } else { + dtmf_no_bridge(chan, dtmf_data); + } return 0; }