diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index fed032a3d7494a8eef5b1fe14f217bddc1d3c130..ce9ef7aa2773f1e3d2a09d53c7e8d1312cb90cb0 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -3835,6 +3835,10 @@ void ast_channel_macroexten_set(struct ast_channel *chan, const char *value); char ast_channel_dtmf_digit_to_emulate(const struct ast_channel *chan); void ast_channel_dtmf_digit_to_emulate_set(struct ast_channel *chan, char value); +char ast_channel_sending_dtmf_digit(const struct ast_channel *chan); +void ast_channel_sending_dtmf_digit_set(struct ast_channel *chan, char value); +struct timeval ast_channel_sending_dtmf_tv(const struct ast_channel *chan); +void ast_channel_sending_dtmf_tv_set(struct ast_channel *chan, struct timeval value); int ast_channel_amaflags(const struct ast_channel *chan); void ast_channel_amaflags_set(struct ast_channel *chan, int value); int ast_channel_epfd(const struct ast_channel *chan); diff --git a/include/asterisk/features.h b/include/asterisk/features.h index 1619d54c4546e17231240146ea64d0012334f4ed..4f556138138bc8aade90d189a9d5473b6c50adee 100644 --- a/include/asterisk/features.h +++ b/include/asterisk/features.h @@ -169,6 +169,18 @@ int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const /*! \brief Determine system call pickup extension */ const char *ast_pickup_ext(void); +/*! + * \brief Simulate a DTMF end on a broken bridge channel. + * + * \param chan Channel sending DTMF that has not ended. + * \param digit DTMF digit to stop. + * \param start DTMF digit start time. + * \param why Reason bridge broken. + * + * \return Nothing + */ +void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why); + /*! \brief Bridge a call, optionally allowing redirection */ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config); diff --git a/main/channel.c b/main/channel.c index b0622fa9c4c6d2ed427d606fe29c41a4186f8a2c..126e44cf639c93738aec441636743e0e5d7f4f04 100644 --- a/main/channel.c +++ b/main/channel.c @@ -72,6 +72,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/global_datastores.h" #include "asterisk/data.h" #include "asterisk/channel_internal.h" +#include "asterisk/features.h" /*** DOCUMENTATION ***/ @@ -4778,6 +4779,11 @@ int ast_senddigit_begin(struct ast_channel *chan, char digit) if (!ast_channel_tech(chan)->send_digit_begin) return 0; + ast_channel_lock(chan); + ast_channel_sending_dtmf_digit_set(chan, digit); + ast_channel_sending_dtmf_tv_set(chan, ast_tvnow()); + ast_channel_unlock(chan); + if (!ast_channel_tech(chan)->send_digit_begin(chan, digit)) return 0; @@ -4804,6 +4810,12 @@ int ast_senddigit_end(struct ast_channel *chan, char digit, unsigned int duratio if (ast_channel_tech(chan)->send_digit_end) res = ast_channel_tech(chan)->send_digit_end(chan, digit, duration); + ast_channel_lock(chan); + if (ast_channel_sending_dtmf_digit(chan) == digit) { + ast_channel_sending_dtmf_digit_set(chan, 0); + } + ast_channel_unlock(chan); + if (res && ast_channel_generator(chan)) ast_playtones_stop(chan); @@ -6835,6 +6847,8 @@ int ast_do_masquerade(struct ast_channel *original) char orig[AST_CHANNEL_NAME]; char masqn[AST_CHANNEL_NAME]; char zombn[AST_CHANNEL_NAME]; + char clone_sending_dtmf_digit; + struct timeval clone_sending_dtmf_tv; /* XXX This operation is a bit odd. We're essentially putting the guts of * the clone channel into the original channel. Start by killing off the @@ -6963,6 +6977,10 @@ int ast_do_masquerade(struct ast_channel *original) free_translation(clonechan); free_translation(original); + /* Save the current DTMF digit being sent if any. */ + clone_sending_dtmf_digit = ast_channel_sending_dtmf_digit(clonechan); + clone_sending_dtmf_tv = ast_channel_sending_dtmf_tv(clonechan); + /* Save the original name */ ast_copy_string(orig, ast_channel_name(original), sizeof(orig)); /* Save the new name */ @@ -7207,6 +7225,15 @@ int ast_do_masquerade(struct ast_channel *original) ast_channel_unlock(original); ast_channel_unlock(clonechan); + if (clone_sending_dtmf_digit) { + /* + * The clonechan was sending a DTMF digit that was not completed + * before the masquerade. + */ + ast_bridge_end_dtmf(original, clone_sending_dtmf_digit, clone_sending_dtmf_tv, + "masquerade"); + } + /* * If an indication is currently playing, maintain it on the * channel that is taking the place of original. diff --git a/main/channel_internal_api.c b/main/channel_internal_api.c index e3543d8ec26fdbc6c92ecbd9ae1e5b758bb743bc..c7ed84c349ba0fca84b69e96242e626a824acb16 100644 --- a/main/channel_internal_api.c +++ b/main/channel_internal_api.c @@ -193,6 +193,8 @@ struct ast_channel { char macrocontext[AST_MAX_CONTEXT]; /*!< Macro: Current non-macro context. See app_macro.c */ char macroexten[AST_MAX_EXTENSION]; /*!< Macro: Current non-macro extension. See app_macro.c */ char dtmf_digit_to_emulate; /*!< Digit being emulated */ + char sending_dtmf_digit; /*!< Digit this channel is currently sending out. (zero if not sending) */ + struct timeval sending_dtmf_tv; /*!< The time this channel started sending the current digit. (Invalid if sending_dtmf_digit is zero.) */ }; /* AST_DATA definitions, which will probably have to be re-thought since the channel will be opaque */ @@ -523,6 +525,25 @@ void ast_channel_dtmf_digit_to_emulate_set(struct ast_channel *chan, char value) { chan->dtmf_digit_to_emulate = value; } + +char ast_channel_sending_dtmf_digit(const struct ast_channel *chan) +{ + return chan->sending_dtmf_digit; +} +void ast_channel_sending_dtmf_digit_set(struct ast_channel *chan, char value) +{ + chan->sending_dtmf_digit = value; +} + +struct timeval ast_channel_sending_dtmf_tv(const struct ast_channel *chan) +{ + return chan->sending_dtmf_tv; +} +void ast_channel_sending_dtmf_tv_set(struct ast_channel *chan, struct timeval value) +{ + chan->sending_dtmf_tv = value; +} + int ast_channel_amaflags(const struct ast_channel *chan) { return chan->amaflags; diff --git a/main/features.c b/main/features.c index e9f84eae1f761b08050beaebdb268795acf6a53c..847b29ee6116fec6962009bc91f14d044bf82335 100644 --- a/main/features.c +++ b/main/features.c @@ -4249,6 +4249,24 @@ static void clear_dialed_interfaces(struct ast_channel *chan) ast_channel_unlock(chan); } +void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why) +{ + int dead; + long duration; + + ast_channel_lock(chan); + dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE) || ast_check_hangup(chan); + ast_channel_unlock(chan); + if (dead) { + return; + } + + duration = ast_tvdiff_ms(ast_tvnow(), start); + ast_senddigit_end(chan, digit, duration); + ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n", + digit, ast_channel_name(chan), why, duration); +} + /*! * \brief bridge the call and set CDR * @@ -4698,6 +4716,15 @@ int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer, struct a ast_cel_report_event(chan, AST_CEL_BRIDGE_END, NULL, NULL, peer); before_you_go: + if (ast_channel_sending_dtmf_digit(chan)) { + ast_bridge_end_dtmf(chan, ast_channel_sending_dtmf_digit(chan), + ast_channel_sending_dtmf_tv(chan), "bridge end"); + } + if (ast_channel_sending_dtmf_digit(peer)) { + ast_bridge_end_dtmf(peer, ast_channel_sending_dtmf_digit(peer), + ast_channel_sending_dtmf_tv(peer), "bridge end"); + } + /* Just in case something weird happened and we didn't clean up the silence generator... */ if (silgen) { ast_channel_stop_silence_generator(who == chan ? peer : chan, silgen);