aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/channel.h16
-rw-r--r--main/autoservice.c9
-rw-r--r--main/channel.c133
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 {