aboutsummaryrefslogtreecommitdiffstats
path: root/main/channel.c
diff options
context:
space:
mode:
Diffstat (limited to 'main/channel.c')
-rw-r--r--main/channel.c133
1 files changed, 92 insertions, 41 deletions
diff --git a/main/channel.c b/main/channel.c
index 3f8d69b02..59a98d7d4 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -899,7 +899,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;
@@ -908,9 +908,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 */
@@ -938,11 +938,18 @@ 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));
+ }
#ifdef HAVE_DAHDI
} else if (chan->timingfd > -1) {
ioctl(chan->timingfd, DAHDI_TIMERPING, &blah);
@@ -950,10 +957,22 @@ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
} 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)
{
@@ -1968,6 +1987,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 */
@@ -2001,28 +2056,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) {
@@ -2095,7 +2128,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(&chan->readq, 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) {
@@ -2152,26 +2213,16 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
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)) ) {
- 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 {