diff options
-rw-r--r-- | include/asterisk/channel.h | 16 | ||||
-rw-r--r-- | main/autoservice.c | 9 | ||||
-rw-r--r-- | main/channel.c | 133 |
3 files changed, 114 insertions, 44 deletions
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index ae785a298..93ef1f1b6 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -496,7 +496,7 @@ struct ast_channel { unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */ - char dtmfq[AST_MAX_EXTENSION]; /*!< Any/all queued DTMF characters */ + char unused_old_dtmfq[AST_MAX_EXTENSION]; /*!< (deprecated, use readq instead) Any/all queued DTMF characters */ char context[AST_MAX_CONTEXT]; /*!< Dialplan: Current extension context */ char exten[AST_MAX_EXTENSION]; /*!< Dialplan: Current extension number */ char macrocontext[AST_MAX_CONTEXT]; /*!< Macro: Current non-macro context. See app_macro.c */ @@ -711,6 +711,20 @@ struct ast_channel *ast_channel_alloc(int needqueue, int state, const char *cid_ */ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f); +/*! + * \brief Queue an outgoing frame to the head of the frame queue + * + * \param chan the channel to queue the frame on + * \param f the frame to queue. Note that this frame will be duplicated by + * this function. It is the responsibility of the caller to handle + * freeing the memory associated with the frame being passed if + * necessary. + * + * \retval 0 success + * \retval non-zero failure + */ +int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *f); + /*! * \brief Queue a hangup frame * diff --git a/main/autoservice.c b/main/autoservice.c index d3d0333d1..26cb08582 100644 --- a/main/autoservice.c +++ b/main/autoservice.c @@ -56,6 +56,9 @@ struct asent { * it gets stopped for the last time. */ unsigned int use_count; unsigned int orig_end_dtmf_flag:1; + /*! Frames go on at the head of deferred_frames, so we have the frames + * from newest to oldest. As we put them at the head of the readq, we'll + * end up with them in the right order for the channel's readq. */ AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames; AST_LIST_ENTRY(asent) list; }; @@ -161,7 +164,7 @@ static void *autoservice_run(void *ign) } if ((dup_f = ast_frdup(defer_frame))) { - AST_LIST_INSERT_TAIL(&ents[i]->deferred_frames, dup_f, frame_list); + AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list); } break; @@ -292,10 +295,12 @@ int ast_autoservice_stop(struct ast_channel *chan) ast_clear_flag(chan, AST_FLAG_END_DTMF_ONLY); } + ast_channel_lock(chan); while ((f = AST_LIST_REMOVE_HEAD(&as->deferred_frames, frame_list))) { - ast_queue_frame(chan, f); + ast_queue_frame_head(chan, f); ast_frfree(f); } + ast_channel_unlock(chan); free(as); diff --git a/main/channel.c b/main/channel.c index d4d24b6a1..3debac5d9 100644 --- a/main/channel.c +++ b/main/channel.c @@ -953,7 +953,7 @@ alertpipe_failed: } /*! \brief Queue an outgoing media frame */ -int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin) +static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head) { struct ast_frame *f; struct ast_frame *cur; @@ -962,9 +962,9 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin) /* Build us a copy and free the original one */ if (!(f = ast_frdup(fin))) { - ast_log(LOG_WARNING, "Unable to duplicate frame\n"); return -1; } + ast_channel_lock(chan); /* See if the last frame on the queue is a hangup, if so don't queue anything */ @@ -991,20 +991,39 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin) return 0; } } - AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list); + + if (head) { + AST_LIST_INSERT_HEAD(&chan->readq, f, frame_list); + } else { + AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list); + } + if (chan->alertpipe[1] > -1) { - if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah)) + if (write(chan->alertpipe[1], &blah, sizeof(blah)) != sizeof(blah)) { ast_log(LOG_WARNING, "Unable to write to alert pipe on %s, frametype/subclass %d/%d (qlen = %d): %s!\n", chan->name, f->frametype, f->subclass, qlen, strerror(errno)); + } } else if (chan->timingfd > -1) { ast_timer_enable_continuous(chan->timingfd); } else if (ast_test_flag(chan, AST_FLAG_BLOCKING)) { pthread_kill(chan->blocker, SIGURG); } + ast_channel_unlock(chan); + return 0; } +int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin) +{ + return __ast_queue_frame(chan, fin, 0); +} + +int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *fin) +{ + return __ast_queue_frame(chan, fin, 1); +} + /*! \brief Queue a hangup frame for channel */ int ast_queue_hangup(struct ast_channel *chan) { @@ -2370,6 +2389,42 @@ static void ast_read_generator_actions(struct ast_channel *chan, struct ast_fram } } +static inline void queue_dtmf_readq(struct ast_channel *chan, struct ast_frame *f) +{ + struct ast_frame *fr = &chan->dtmff; + + fr->frametype = AST_FRAME_DTMF_END; + fr->subclass = f->subclass; + fr->len = f->len; + + /* The only time this function will be called is for a frame that just came + * out of the channel driver. So, we want to stick it on the tail of the + * readq. */ + + ast_queue_frame(chan, fr); +} + +/*! + * \brief Determine whether or not we should ignore DTMF in the readq + */ +static inline int should_skip_dtmf(struct ast_channel *chan) +{ + if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF | AST_FLAG_EMULATE_DTMF)) { + /* We're in the middle of emulating a digit, or DTMF has been + * explicitly deferred. Skip this digit, then. */ + return 1; + } + + if (!ast_tvzero(chan->dtmf_tv) && + ast_tvdiff_ms(ast_tvnow(), chan->dtmf_tv) < 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 skip DTMF for now. */ + return 1; + } + + return 0; +} + static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) { struct ast_frame *f = NULL; /* the return value */ @@ -2403,28 +2458,6 @@ 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_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 */ - memmove(chan->dtmfq, chan->dtmfq + 1, sizeof(chan->dtmfq) - 1); - f = &chan->dtmff; - if (ast_test_flag(chan, AST_FLAG_END_DTMF_ONLY)) { - ast_log(LOG_DTMF, "DTMF end emulation of '%c' queued on %s\n", f->subclass, chan->name); - chan->dtmff.frametype = AST_FRAME_DTMF_END; - } else { - ast_log(LOG_DTMF, "DTMF begin emulation of '%c' with duration %d queued on %s\n", f->subclass, AST_DEFAULT_EMULATE_DTMF_DURATION, chan->name); - chan->dtmff.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; - } - chan->dtmf_tv = ast_tvnow(); - goto done; - } - /* Read and ignore anything on the alertpipe, but read only one sizeof(blah) per frame that we send from it */ if (chan->alertpipe[0] > -1) { @@ -2492,7 +2525,35 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) /* Check for pending read queue */ if (!AST_LIST_EMPTY(&chan->readq)) { - f = AST_LIST_REMOVE_HEAD(&chan->readq, frame_list); + int skip_dtmf = should_skip_dtmf(chan); + + AST_LIST_TRAVERSE_SAFE_BEGIN(&chan->readq, f, frame_list) { + /* We have to be picky about which frame we pull off of the readq because + * there are cases where we want to leave DTMF frames on the queue until + * some later time. */ + + if ( (f->frametype == AST_FRAME_DTMF_BEGIN || f->frametype == AST_FRAME_DTMF_END) && skip_dtmf) { + continue; + } + + AST_LIST_REMOVE_CURRENT(frame_list); + break; + } + AST_LIST_TRAVERSE_SAFE_END + + if (!f) { + /* There were no acceptable frames on the readq. */ + f = &ast_null_frame; + if (chan->alertpipe[0] > -1) { + int poke = 0; + /* Restore the state of the alertpipe since we aren't ready for any + * of the frames in the readq. */ + if (write(chan->alertpipe[1], &poke, sizeof(poke)) != sizeof(poke)) { + ast_log(LOG_ERROR, "Failed to write to alertpipe: %s\n", strerror(errno)); + } + } + } + /* Interpret hangup and return NULL */ /* XXX why not the same for frames from the channel ? */ if (f->frametype == AST_FRAME_CONTROL && f->subclass == AST_CONTROL_HANGUP) { @@ -2549,26 +2610,16 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio) case AST_FRAME_DTMF_END: send_dtmf_event(chan, "Received", f->subclass, "No", "Yes"); 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)) ) { - 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; - } else - ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name); + /* Queue it up if DTMF is deferred, or if DTMF emulation is forced. */ + if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF) || ast_test_flag(chan, AST_FLAG_EMULATE_DTMF)) { + queue_dtmf_readq(chan, f); ast_frfree(f); f = &ast_null_frame; } else if (!ast_test_flag(chan, AST_FLAG_IN_DTMF | AST_FLAG_END_DTMF_ONLY)) { 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) { - 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; - } else - ast_log(LOG_WARNING, "Dropping deferred DTMF digits on %s\n", chan->name); + queue_dtmf_readq(chan, f); ast_frfree(f); f = &ast_null_frame; } else { |