Index: main/frame.c =================================================================== --- main/frame.c (revision 116556) +++ main/frame.c (working copy) @@ -490,7 +490,7 @@ /* Must have space since we allocated for it */ strcpy((char *)out->src, f->src); } - ast_copy_flags(out, f, AST_FRFLAG_HAS_TIMING_INFO); + ast_copy_flags(out, f, AST_FRFLAG_HAS_TIMING_INFO | AST_FRFLAG_READ); out->ts = f->ts; out->len = f->len; out->seqno = f->seqno; Index: main/autoservice.c =================================================================== --- main/autoservice.c (revision 116589) +++ main/autoservice.c (working copy) @@ -276,6 +276,9 @@ ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); while ((f = AST_LIST_REMOVE_HEAD(&dtmf_frames, frame_list))) { + /* Mark frame as already read so ast_read will not do any + additional processing (like DTMF emulation) with it */ + f->flags |= AST_FRFLAG_READ; ast_queue_frame(chan, f); ast_frfree(f); } Index: include/asterisk/frame.h =================================================================== --- include/asterisk/frame.h (revision 116556) +++ include/asterisk/frame.h (working copy) @@ -135,6 +135,13 @@ * The dsp cannot be free'd if the frame inside of it still has * this flag set. */ AST_FRFLAG_FROM_DSP = (1 << 2), + /*! Frame was already read from ast_read. Autoservice sets this flag + * on all frames it queues back to the channel to make sure ast_read + * won't do any special processing (like DTMF emulation) based on this + * frame. This is because the frame itself can be already a rusult + * of such a processing + */ + AST_FRFLAG_READ = (1 << 3), }; /*! \brief Data structure associated with a single frame of data Index: main/channel.c =================================================================== --- main/channel.c (revision 116556) +++ main/channel.c (working copy) @@ -1972,7 +1972,9 @@ if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) && !ast_strlen_zero(chan->dtmfq) && - (ast_tvzero(chan->dtmf_tv) || ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) > AST_MIN_DTMF_GAP) ) { + (ast_tvzero(chan->dtmf_tv) || ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) > AST_MIN_DTMF_GAP) && + AST_LIST_EMPTY(&chan->readq)) { + /* We have DTMF that has been deferred. Return it now */ chan->dtmff.subclass = chan->dtmfq[0]; /* Drop first digit from the buffer */ @@ -2056,6 +2058,17 @@ ast_frfree(f); f = NULL; } + + if (f && (f->flags & AST_FRFLAG_READ)) { + /* This frame was already read from us by autoservice/dsp so we do not need + do any processing on it because it may be actually a result of + such a processing (emulated DTMF_BEGIN for example). + However, reset AST_FRFLAG_READ to make sure the frame may be queued to another + channel and read from there with all necessary processing */ + f->flags &= ~AST_FRFLAG_READ; + goto done; + } + } else { chan->blocker = pthread_self(); if (ast_test_flag(chan, AST_FLAG_EXCEPTION)) { @@ -2114,10 +2127,10 @@ break; case AST_FRAME_DTMF_END: ast_log(LOG_DTMF, "DTMF end '%c' received on %s, duration %ld ms\n", f->subclass, chan->name, f->len); - /* Queue it up if DTMF is deffered, or if DTMF emulation is forced. - * However, only let emulation be forced if the other end cares about BEGIN frames */ - if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF) || - (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF) && !ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) ) { + /* Queue it up if DTMF is deffered, if DTMF emulation is forced or there are other DTMFs in the queue. */ + if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF) || + !ast_strlen_zero(chan->dtmfq) ) { + if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) { ast_log(LOG_DTMF, "DTMF end '%c' put into dtmf queue on %s\n", f->subclass, chan->name); chan->dtmfq[strlen(chan->dtmfq)] = f->subclass; @@ -2190,6 +2203,7 @@ case AST_FRAME_DTMF_BEGIN: ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name); if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY | AST_FLAG_EMULATE_DTMF) || + !ast_strlen_zero(chan->dtmfq) || (!ast_tvzero(chan->dtmf_tv) && ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) ) { ast_log(LOG_DTMF, "DTMF begin ignored '%c' on %s\n", f->subclass, chan->name);