diff --git a/main/channel.c b/main/channel.c index f09608731596712ee19e76212e326a40a9a8b2c8..c350177dbcc0ed9c497fda164b3478c1e7e72013 100644 --- a/main/channel.c +++ b/main/channel.c @@ -2819,6 +2819,33 @@ int ast_channel_get_up_time(struct ast_channel *chan) return (ast_channel_get_up_time_ms(chan) / 1000); } +/*! + * \brief Determine whether or not we have to trigger dtmf emulating using 50 fps timer events + * especially when no voice frames are received during dtmf processing (direct media or muted + * sender case using SIP INFO) + */ +static inline int should_trigger_dtmf_emulating(struct ast_channel *chan) +{ + if (ast_test_flag(ast_channel_flags(chan), AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF)) { + /* We're in the middle of emulating a digit, or DTMF has been + * explicitly deferred. Trigger dtmf with periodic 50 pfs timer events, then. */ + return 1; + } + + if (!ast_tvzero(*ast_channel_dtmf_tv(chan)) && + ast_tvdiff_ms(ast_tvnow(), *ast_channel_dtmf_tv(chan)) < 2*AST_MIN_DTMF_GAP) { + /* + * We're not in the middle of a digit, but it hasn't been long enough + * since the last digit, so we'll have to trigger DTMF furtheron. + * Using 2 times AST_MIN_DTMF_GAP to trigger readq reading for possible + * buffered next dtmf event + */ + return 1; + } + + return 0; +} + static void deactivate_generator_nolock(struct ast_channel *chan) { if (ast_channel_generatordata(chan)) { @@ -2839,6 +2866,10 @@ void ast_deactivate_generator(struct ast_channel *chan) { ast_channel_lock(chan); deactivate_generator_nolock(chan); + if (should_trigger_dtmf_emulating(chan)) { + /* if in the middle of dtmf emulation keep 50 tick per sec timer on rolling */ + ast_timer_set_rate(ast_channel_timer(chan), 50); + } ast_channel_unlock(chan); } @@ -3508,6 +3539,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int if (ast_channel_timingfd(chan) > -1 && ast_channel_fdno(chan) == AST_TIMING_FD) { enum ast_timer_event res; + int trigger_dtmf_emulating = should_trigger_dtmf_emulating(chan); ast_clear_flag(ast_channel_flags(chan), AST_FLAG_EXCEPTION); @@ -3535,10 +3567,26 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int if (got_ref) { ao2_ref(data, -1); } + + if (trigger_dtmf_emulating) { + /* + * Since we're breaking out of this switch block and not + * returning, we need to re-lock the channel. + */ + ast_channel_lock(chan); + /* generate null frame to trigger dtmf emulating */ + f = &ast_null_frame; + break; + } + } else if (trigger_dtmf_emulating) { + /* generate null frame to trigger dtmf emualating */ + f = &ast_null_frame; + break; } else { ast_timer_set_rate(ast_channel_timer(chan), 0); - ast_channel_fdno_set(chan, -1); - ast_channel_unlock(chan); + /* generate very last null frame to trigger dtmf emulating */ + f = &ast_null_frame; + break; } /* cannot 'goto done' because the channel is already unlocked */ @@ -3576,6 +3624,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int /* Check for pending read queue */ if (!AST_LIST_EMPTY(ast_channel_readq(chan))) { + int skipped_dtmf_frame = 0; int skip_dtmf = should_skip_dtmf(chan); AST_LIST_TRAVERSE_SAFE_BEGIN(ast_channel_readq(chan), f, frame_list) { @@ -3584,6 +3633,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int * some later time. */ if ( (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) && skip_dtmf) { + skipped_dtmf_frame = 1; continue; } @@ -3595,7 +3645,19 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int if (!f) { /* There were no acceptable frames on the readq. */ f = &ast_null_frame; - ast_channel_alert_write(chan); + if (!skipped_dtmf_frame) { + /* + * Do not trigger alert pipe if only buffered dtmf begin or end frames + * are left in the readq. + */ + ast_channel_alert_write(chan); + } else { + /* + * Safely disable continous timer events if only buffered dtmf begin or end + * frames are left in the readq. + */ + ast_timer_disable_continuous(ast_channel_timer(chan)); + } } /* Interpret hangup and end-of-Q frames to return NULL */ @@ -3801,6 +3863,16 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int } else ast_channel_emulate_dtmf_duration_set(chan, AST_DEFAULT_EMULATE_DTMF_DURATION); ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %u queued on %s\n", f->subclass.integer, ast_channel_emulate_dtmf_duration(chan), ast_channel_name(chan)); + + /* + * Start generating 50 fps timer events (null frames) for dtmf emulating + * independently from any existing incoming voice frames. + * If channel generator is already activated in regular mode use these + * timer events to generate null frames. + */ + if (!ast_channel_generator(chan)) { + ast_timer_set_rate(ast_channel_timer(chan), 50); + } } if (ast_channel_audiohooks(chan)) { struct ast_frame *old_frame = f; @@ -3842,12 +3914,30 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int ast_channel_emulate_dtmf_duration_set(chan, option_dtmfminduration - f->len); ast_frfree(f); f = &ast_null_frame; + + /* Start generating 50 fps timer events (null frames) for dtmf emulating + * independently from any existing incoming voice frames. + * If channel generator is already activated in regular mode use these + * timer events to generate null frames. + */ + if (!ast_channel_generator(chan)) { + ast_timer_set_rate(ast_channel_timer(chan), 50); + } } else { ast_log(LOG_DTMF, "DTMF end passthrough '%c' on %s\n", f->subclass.integer, ast_channel_name(chan)); if (f->len < option_dtmfminduration) { f->len = option_dtmfminduration; } ast_channel_dtmf_tv_set(chan, &now); + + /* Start generating 50 fps timer events (null frames) for dtmf emulating + * independently from any existing incoming voice frames. + * If channel generator is already activated in regular mode use these + * timer events to generate null frames. + */ + if (!ast_channel_generator(chan)) { + ast_timer_set_rate(ast_channel_timer(chan), 50); + } } if (ast_channel_audiohooks(chan)) { struct ast_frame *old_frame = f; @@ -3901,6 +3991,15 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio, int ast_frfree(old_frame); } } + + /* Start generating 50 fps timer events (null frames) for dtmf emulating + * independently from any existing incoming voice frames. + * If channel generator is already activated in regular mode use these + * timer events to generate null frames. + */ + if (!ast_channel_generator(chan)) { + ast_timer_set_rate(ast_channel_timer(chan), 50); + } } } break;