--- asterisk_orig/main/channel.c 2019-11-12 15:04:20.000000000 +0100 +++ asterisk/main/channel.c 2019-11-13 16:30:46.000000000 +0100 @@ -2960,6 +2960,30 @@ return (ast_tvdiff_ms(ast_tvnow(), ast_channel_answertime(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)) { @@ -2980,6 +3004,10 @@ { 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); } @@ -3848,13 +3876,14 @@ 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); res = ast_timer_get_event(ast_channel_timer(chan)); switch (res) { - case AST_TIMING_EVENT_EXPIRED: + case AST_TIMING_EVENT_EXPIRED: if (ast_timer_ack(ast_channel_timer(chan), 1) < 0) { ast_log(LOG_ERROR, "Failed to acknoweldge timer in ast_read\n"); goto done; @@ -3874,11 +3903,22 @@ func(data); if (got_ref) { ao2_ref(data, -1); + } + if (trigger_dtmf_emulating) { + 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 */ @@ -3916,6 +3956,7 @@ /* 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) { @@ -3924,6 +3965,7 @@ * some later time. */ if ( (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) && skip_dtmf) { + skipped_dtmf_frame = 1; continue; } @@ -3935,7 +3977,13 @@ 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 */ @@ -4068,6 +4116,12 @@ } 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; @@ -4109,12 +4163,24 @@ 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; @@ -4168,6 +4234,12 @@ 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;