diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 8ccfe7700c..d54b307094 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -428,7 +428,7 @@ struct ast_channel { char emulate_dtmf_digit; /*!< Digit being emulated */ unsigned int emulate_dtmf_duration; /*!< Number of ms left to emulate DTMF for */ - struct timeval dtmf_begin_tv; /*!< The time that an in process digit began */ + struct timeval dtmf_tv; /*!< The time that an in process digit began, or the last digit ended */ /*! \brief Data stores on the channel */ AST_LIST_HEAD_NOLOCK(datastores, ast_datastore) datastores; diff --git a/main/channel.c b/main/channel.c index f0215851b6..095471598f 100644 --- a/main/channel.c +++ b/main/channel.c @@ -104,9 +104,17 @@ unsigned long global_fin, global_fout; AST_THREADSTORAGE(state2str_threadbuf, state2str_threadbuf_init); #define STATE2STR_BUFSIZE 32 -/*! 100ms */ +/*! Default amount of time to use when emulating a digit as a begin and end + * 100ms */ #define AST_DEFAULT_EMULATE_DTMF_DURATION 100 +/*! Minimum allowed digit length - 80ms */ +#define AST_MIN_DTMF_DURATION 80 + +/*! Minimum amount of time between the end of the last digit and the beginning + * of a new one - 45ms */ +#define AST_MIN_DTMF_GAP 45 + struct chanlist { const struct ast_channel_tech *tech; AST_LIST_ENTRY(chanlist) list; @@ -2113,7 +2121,8 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) prestate = chan->_state; if (!ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF | AST_FLAG_IN_DTMF) && - !ast_strlen_zero(chan->dtmfq)) { + !ast_strlen_zero(chan->dtmfq) && + (ast_tvzero(chan->dtmf_tv) || ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) > AST_MIN_DTMF_GAP) ) { /* We have DTMF that has been deferred. Return it now */ chan->dtmff.subclass = chan->dtmfq[0]; /* Drop first digit from the buffer */ @@ -2126,8 +2135,8 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); chan->emulate_dtmf_digit = f->subclass; chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION; - chan->dtmf_begin_tv = ast_tvnow(); } + chan->dtmf_tv = ast_tvnow(); goto done; } @@ -2264,34 +2273,50 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) ast_frfree(f); f = &ast_null_frame; } else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF | AST_FLAG_END_DTMF_ONLY)) { - f->frametype = AST_FRAME_DTMF_BEGIN; - ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); - chan->emulate_dtmf_digit = f->subclass; - chan->dtmf_begin_tv = ast_tvnow(); - if (f->len && f->len > AST_DEFAULT_EMULATE_DTMF_DURATION) - chan->emulate_dtmf_duration = f->len; - else - chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION; - } else { - ast_clear_flag(chan, AST_FLAG_IN_DTMF); - if (!f->len) - f->len = ast_tvdiff_ms(ast_tvnow(), chan->dtmf_begin_tv); - if (f->len < AST_DEFAULT_EMULATE_DTMF_DURATION) { + if (!ast_tvzero(chan->dtmf_tv) && + ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) { + /* If it hasn't been long enough, defer this digit */ + if (strlen(chan->dtmfq) < sizeof(chan->dtmfq) - 2) + chan->dtmfq[strlen(chan->dtmfq)] = f->subclass; + else + ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name); + ast_frfree(f); + f = &ast_null_frame; + } else { + /* There was no begin, turn this into a begin and send the end later */ + f->frametype = AST_FRAME_DTMF_BEGIN; ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); chan->emulate_dtmf_digit = f->subclass; - chan->emulate_dtmf_duration = AST_DEFAULT_EMULATE_DTMF_DURATION - f->len; - f = &ast_null_frame; + chan->dtmf_tv = ast_tvnow(); + if (f->len && f->len > AST_MIN_DTMF_DURATION) + chan->emulate_dtmf_duration = f->len; + else + chan->emulate_dtmf_duration = AST_MIN_DTMF_DURATION; } + } else { + struct timeval now = ast_tvnow(); + ast_clear_flag(chan, AST_FLAG_IN_DTMF); + if (!f->len) + f->len = ast_tvdiff_ms(now, chan->dtmf_tv); + if (f->len < AST_MIN_DTMF_DURATION) { + ast_set_flag(chan, AST_FLAG_EMULATE_DTMF); + chan->emulate_dtmf_digit = f->subclass; + chan->emulate_dtmf_duration = AST_MIN_DTMF_DURATION - f->len; + f = &ast_null_frame; + } else + chan->dtmf_tv = now; } break; 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)) { + if ( ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_END_DTMF_ONLY) || + (!ast_tvzero(chan->dtmf_tv) && + ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < AST_MIN_DTMF_GAP) ) { ast_frfree(f); f = &ast_null_frame; } else { ast_set_flag(chan, AST_FLAG_IN_DTMF); - chan->dtmf_begin_tv = ast_tvnow(); + chan->dtmf_tv = ast_tvnow(); } break; case AST_FRAME_VOICE: @@ -2309,10 +2334,12 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) f = &ast_null_frame; } else if (ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) { if ((f->samples / 8) >= chan->emulate_dtmf_duration) { /* XXX 8kHz */ + struct timeval now = ast_tvnow(); chan->emulate_dtmf_duration = 0; f->frametype = AST_FRAME_DTMF_END; f->subclass = chan->emulate_dtmf_digit; - f->len = ast_tvdiff_ms(ast_tvnow(), chan->dtmf_begin_tv); + f->len = ast_tvdiff_ms(now, chan->dtmf_tv); + chan->dtmf_tv = now; } else { chan->emulate_dtmf_duration -= f->samples / 8; /* XXX 8kHz */ ast_frfree(f);