make ast_read() able to handle channel read()/exception() methods that return a chain...
authorKevin P. Fleming <kpfleming@digium.com>
Sun, 29 Jan 2006 05:07:04 +0000 (05:07 +0000)
committerKevin P. Fleming <kpfleming@digium.com>
Sun, 29 Jan 2006 05:07:04 +0000 (05:07 +0000)
cleanup code in ast_read()
add AST_FRAME_DTMF_BEGIN and AST_FRAME_DTMF_END so that variable-length DTMF events can be supported
teach chan_zap to send DTMF_BEGIN and DTMF_END when appropriate

git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@8850 65c4cc65-6c06-0410-ace0-fbb531ad65f3

channel.c
channels/chan_zap.c
include/asterisk/frame.h

index 8a05285..3db38b6 100644 (file)
--- a/channel.c
+++ b/channel.c
@@ -1904,6 +1904,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        if (chan->readq) {
                f = chan->readq;
                chan->readq = f->next;
+               f->next = NULL;
                /* Interpret hangup and return NULL */
                if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_HANGUP)) {
                        ast_frfree(f);
@@ -1928,103 +1929,126 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
                }
        }
 
+       if (f) {
+               /* if the channel driver returned more than one frame, stuff the excess
+                  into the readq for the next ast_read call
+               */
+               if (f->next) {
+                       chan->readq = f->next;
+                       f->next = NULL;
+               }
 
-       if (f && (f->frametype == AST_FRAME_VOICE)) {
-               if (dropaudio) {
-                       ast_frfree(f);
-                       f = &null_frame;
-               } else if (!(f->subclass & chan->nativeformats)) {
-                       /* This frame can't be from the current native formats -- drop it on the
-                          floor */
-                       ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n", chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
-                       ast_frfree(f);
-                       f = &null_frame;
-               } else {
-                       if (chan->spies)
-                               queue_frame_to_spies(chan, f, SPY_READ);
-
-                       if (chan->monitor && chan->monitor->read_stream ) {
+               switch (f->frametype) {
+               case AST_FRAME_CONTROL:
+                       if (f->subclass == AST_CONTROL_ANSWER) {
+                               if (prestate == AST_STATE_UP) {
+                                       ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
+                                       f = &null_frame;
+                               }
+                               /* Answer the CDR */
+                               ast_setstate(chan, AST_STATE_UP);
+                               ast_cdr_answer(chan->cdr);
+                       }
+                       break;
+               case AST_FRAME_DTMF:
+                       ast_log(LOG_DTMF, "DTMF '%c' received on %s\n", f->subclass, chan->name);
+                       if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF)) {
+                               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);
+                               f = &null_frame;
+                       }
+                       break;
+               case AST_FRAME_DTMF_BEGIN:
+                       ast_log(LOG_DTMF, "DTMF begin '%c' received on %s\n", f->subclass, chan->name);
+                       break;
+               case AST_FRAME_DTMF_END:
+                       ast_log(LOG_DTMF, "DTMF end '%c' received on %s\n", f->subclass, chan->name);
+                       break;
+               case AST_FRAME_VOICE:
+                       if (dropaudio) {
+                               ast_frfree(f);
+                               f = &null_frame;
+                       } else if (!(f->subclass & chan->nativeformats)) {
+                               /* This frame can't be from the current native formats -- drop it on the
+                                  floor */
+                               ast_log(LOG_NOTICE, "Dropping incompatible voice frame on %s of format %s since our native format has changed to %s\n",
+                                       chan->name, ast_getformatname(f->subclass), ast_getformatname(chan->nativeformats));
+                               ast_frfree(f);
+                               f = &null_frame;
+                       } else {
+                               if (chan->spies)
+                                       queue_frame_to_spies(chan, f, SPY_READ);
+                               
+                               if (chan->monitor && chan->monitor->read_stream ) {
 #ifndef MONITOR_CONSTANT_DELAY
-                               int jump = chan->outsmpl - chan->insmpl - 4 * f->samples;
-                               if (jump >= 0) {
-                                       if (ast_seekstream(chan->monitor->read_stream, jump + f->samples, SEEK_FORCECUR) == -1)
-                                               ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
-                                       chan->insmpl += jump + 4 * f->samples;
-                               } else
-                                       chan->insmpl+= f->samples;
+                                       int jump = chan->outsmpl - chan->insmpl - 4 * f->samples;
+                                       if (jump >= 0) {
+                                               if (ast_seekstream(chan->monitor->read_stream, jump + f->samples, SEEK_FORCECUR) == -1)
+                                                       ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
+                                               chan->insmpl += jump + 4 * f->samples;
+                                       } else
+                                               chan->insmpl+= f->samples;
 #else
-                               int jump = chan->outsmpl - chan->insmpl;
-                               if (jump - MONITOR_DELAY >= 0) {
-                                       if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
-                                               ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
-                                       chan->insmpl += jump;
-                               } else
-                                       chan->insmpl += f->samples;
+                                       int jump = chan->outsmpl - chan->insmpl;
+                                       if (jump - MONITOR_DELAY >= 0) {
+                                               if (ast_seekstream(chan->monitor->read_stream, jump - f->samples, SEEK_FORCECUR) == -1)
+                                                       ast_log(LOG_WARNING, "Failed to perform seek in monitoring read stream, synchronization between the files may be broken\n");
+                                               chan->insmpl += jump;
+                                       } else
+                                               chan->insmpl += f->samples;
 #endif
-                               if (chan->monitor->state == AST_MONITOR_RUNNING) {
-                                       if (ast_writestream(chan->monitor->read_stream, f) < 0)
-                                               ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+                                       if (chan->monitor->state == AST_MONITOR_RUNNING) {
+                                               if (ast_writestream(chan->monitor->read_stream, f) < 0)
+                                                       ast_log(LOG_WARNING, "Failed to write data to channel monitor read stream\n");
+                                       }
+                               }
+
+                               if (chan->readtrans) {
+                                       if (!(f = ast_translate(chan->readtrans, f, 1)))
+                                               f = &null_frame;
+                               }
+
+                               /* Run any generator sitting on the channel */
+                               if (chan->generatordata) {
+                                       /* Mask generator data temporarily and apply.  If there is a timing function, it
+                                          will be calling the generator instead */
+                                       void *tmp;
+                                       int res;
+                                       int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
+                                       
+                                       if (chan->timingfunc) {
+                                               ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
+                                               ast_settimeout(chan, 0, NULL, NULL);
+                                       }
+                                       tmp = chan->generatordata;
+                                       chan->generatordata = NULL;
+                                       generate = chan->generator->generate;
+                                       res = generate(chan, tmp, f->datalen, f->samples);
+                                       chan->generatordata = tmp;
+                                       if (res) {
+                                               ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
+                                               ast_deactivate_generator(chan);
+                                       }
+                               } else if (f->frametype == AST_FRAME_CNG) {
+                                       if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
+                                               ast_log(LOG_DEBUG, "Generator got CNG, switching to timed mode\n");
+                                               ast_settimeout(chan, 160, generator_force, chan);
+                                       }
                                }
-                       }
-                       if (chan->readtrans) {
-                               f = ast_translate(chan->readtrans, f, 1);
-                               if (!f)
-                                       f = &null_frame;
                        }
                }
-       }
-
-       /* Make sure we always return NULL in the future */
-       if (!f) {
+       } else {
+               /* Make sure we always return NULL in the future */
                chan->_softhangup |= AST_SOFTHANGUP_DEV;
                if (chan->generator)
                        ast_deactivate_generator(chan);
                /* End the CDR if appropriate */
                if (chan->cdr)
                        ast_cdr_end(chan->cdr);
-       } else if (ast_test_flag(chan, AST_FLAG_DEFER_DTMF) && f->frametype == AST_FRAME_DTMF) {
-               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);
-               f = &null_frame;
-       } else if ((f->frametype == AST_FRAME_CONTROL) && (f->subclass == AST_CONTROL_ANSWER)) {
-               if (prestate == AST_STATE_UP) {
-                       ast_log(LOG_DEBUG, "Dropping duplicate answer!\n");
-                       f = &null_frame;
-               }
-               /* Answer the CDR */
-               ast_setstate(chan, AST_STATE_UP);
-               ast_cdr_answer(chan->cdr);
-       } 
-
-       /* Run any generator sitting on the line */
-       if (f && (f->frametype == AST_FRAME_VOICE) && chan->generatordata) {
-               /* Mask generator data temporarily and apply.  If there is a timing function, it
-                  will be calling the generator instead */
-               void *tmp;
-               int res;
-               int (*generate)(struct ast_channel *chan, void *tmp, int datalen, int samples);
-
-               if (chan->timingfunc) {
-                       ast_log(LOG_DEBUG, "Generator got voice, switching to phase locked mode\n");
-                       ast_settimeout(chan, 0, NULL, NULL);
-               }
-               tmp = chan->generatordata;
-               chan->generatordata = NULL;
-               generate = chan->generator->generate;
-               res = generate(chan, tmp, f->datalen, f->samples);
-               chan->generatordata = tmp;
-               if (res) {
-                       ast_log(LOG_DEBUG, "Auto-deactivating generator\n");
-                       ast_deactivate_generator(chan);
-               }
-       } else if (f && (f->frametype == AST_FRAME_CNG)) {
-               if (chan->generator && !chan->timingfunc && (chan->timingfd > -1)) {
-                       ast_log(LOG_DEBUG, "Generator got CNG, switching to zap timed mode\n");
-                       ast_settimeout(chan, 160, generator_force, chan);
-               }
        }
+
        /* High bit prints debugging */
        if (chan->fin & 0x80000000)
                ast_frame_dump(chan->name, f, "<<");
@@ -2033,6 +2057,7 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
        else
                chan->fin++;
        ast_mutex_unlock(&chan->lock);
+
        return f;
 }
 
@@ -2269,6 +2294,11 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                /* XXX Interpret control frames XXX */
                ast_log(LOG_WARNING, "Don't know how to handle control frames yet\n");
                break;
+       case AST_FRAME_DTMF_BEGIN:
+       case AST_FRAME_DTMF_END:
+               /* nothing to do with these yet */
+               res = 0;
+               break;
        case AST_FRAME_DTMF:
                ast_clear_flag(chan, AST_FLAG_BLOCKING);
                ast_mutex_unlock(&chan->lock);
@@ -2295,7 +2325,7 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                else
                        res = 0;
                break;
-       default:
+       case AST_FRAME_VOICE:
                if (chan->tech->write) {
                        /* Bypass translator if we're writing format in the raw write format.  This
                           allows mixing of native / non-native formats */
@@ -2304,11 +2334,10 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                        else
                                f = (chan->writetrans) ? ast_translate(chan->writetrans, fr, 0) : fr;
                        if (f) {
-                               if (f->frametype == AST_FRAME_VOICE && chan->spies)
+                               if (chan->spies)
                                        queue_frame_to_spies(chan, f, SPY_WRITE);
 
-                               if( chan->monitor && chan->monitor->write_stream &&
-                                               f && ( f->frametype == AST_FRAME_VOICE ) ) {
+                               if (chan->monitor && chan->monitor->write_stream) {
 #ifndef MONITOR_CONSTANT_DELAY
                                        int jump = chan->insmpl - chan->outsmpl - 4 * f->samples;
                                        if (jump >= 0) {
@@ -2338,13 +2367,6 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
                }
        }
 
-       /* It's possible this is a translated frame */
-       if (f && f->frametype == AST_FRAME_DTMF) {
-               ast_log(LOG_DTMF, "%s : %c\n", chan->name, f->subclass);
-       } else if (fr->frametype == AST_FRAME_DTMF) {
-               ast_log(LOG_DTMF, "%s : %c\n", chan->name, fr->subclass);
-       }
-
        if (f && (f != fr))
                ast_frfree(f);
        ast_clear_flag(chan, AST_FLAG_BLOCKING);
index d4a5c59..a5dbf78 100644 (file)
@@ -3560,12 +3560,14 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
        pthread_t threadid;
        pthread_attr_t attr;
        struct ast_channel *chan;
+       struct ast_frame dtmf_frame = { .frametype = AST_FRAME_DTMF };
 
        pthread_attr_init(&attr);
        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED);
 
        index = zt_get_index(ast, p, 0);
        p->subs[index].f.frametype = AST_FRAME_NULL;
+       p->subs[index].f.subclass = 0;
        p->subs[index].f.datalen = 0;
        p->subs[index].f.samples = 0;
        p->subs[index].f.mallocd = 0;
@@ -3590,12 +3592,17 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
                ast_log(LOG_DEBUG, "Detected %sdigit '%c'\n", p->pulsedial ? "pulse ": "", res & 0xff);
 #ifdef ZAPATA_PRI
                if (!p->proceeding && p->sig == SIG_PRI && p->pri && p->pri->overlapdial) {
-                       p->subs[index].f.frametype = AST_FRAME_NULL;
-                       p->subs[index].f.subclass = 0;
+                       /* absorb event */
                } else {
 #endif
-                       p->subs[index].f.frametype = AST_FRAME_DTMF;
+                       /* Send a DTMF event for 'legacy' channels and all applications,
+                          and a DTMF_BEGIN event for channels that handle variable duration
+                          DTMF events
+                       */
+                       p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
                        p->subs[index].f.subclass = res & 0xff;
+                       dtmf_frame.subclass = res & 0xff;
+                       p->subs[index].f.next = ast_frdup(&dtmf_frame);
 #ifdef ZAPATA_PRI
                }
 #endif
@@ -3606,10 +3613,13 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
 
        if (res & ZT_EVENT_DTMFDOWN) {
                ast_log(LOG_DEBUG, "DTMF Down '%c'\n", res & 0xff);
-               p->subs[index].f.frametype = AST_FRAME_NULL;
-               p->subs[index].f.subclass = 0;
+               /* Mute conference */
                zt_confmute(p, 1);
-               /* Mute conference, return null frame */
+               /* Send a DTMF_BEGIN event for devices that want variable
+                  duration DTMF events
+               */
+               p->subs[index].f.frametype = AST_FRAME_DTMF_BEGIN;
+               p->subs[index].f.subclass = res & 0xff;
                return &p->subs[index].f;
        }
 
@@ -3705,8 +3715,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
                                                                alarm2str(res), p->channel);
                        /* fall through intentionally */
                case ZT_EVENT_ONHOOK:
-                       if (p->radio)
-                       {
+                       if (p->radio) {
                                p->subs[index].f.frametype = AST_FRAME_CONTROL;
                                p->subs[index].f.subclass = AST_CONTROL_RADIO_UNKEY;
                                break;
@@ -3820,8 +3829,7 @@ static struct ast_frame *zt_handle_event(struct ast_channel *ast)
                        break;
                case ZT_EVENT_RINGOFFHOOK:
                        if (p->inalarm) break;
-                       if (p->radio)
-                       {
+                       if (p->radio) {
                                p->subs[index].f.frametype = AST_FRAME_CONTROL;
                                p->subs[index].f.subclass = AST_CONTROL_RADIO_KEY;
                                break;
index 6323854..afdec0a 100644 (file)
@@ -148,13 +148,19 @@ struct ast_frame {
 #if defined(T38_SUPPORT)
 /*! Modem-over-IP data streams */
 #define AST_FRAME_MODEM                11
+#endif /* T38_SUPPORT */
+/*! DTMF begin event, subclass is the digit */
+#define AST_FRAME_DTMF_BEGIN   12
+/*! DTMF end event, subclass is the digit */
+#define AST_FRAME_DTMF_END     13
 
+#if defined(T38_SUPPORT)
 /* MODEM subclasses */
 /*! T.38 Fax-over-IP */
 #define AST_MODEM_T38          1
 /*! V.150 Modem-over-IP */
 #define AST_MODEM_V150         2
-#endif
+#endif /* T38_SUPPORT */
 
 /* HTML subclasses */
 /*! Sending a URL */