diff --git a/channel.c b/channel.c index 8a052852b..3db38b6cc 100644 --- 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); diff --git a/channels/chan_zap.c b/channels/chan_zap.c index d4a5c591d..a5dbf7874 100644 --- a/channels/chan_zap.c +++ b/channels/chan_zap.c @@ -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; diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 6323854b4..afdec0ae2 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -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 */