make ast_read() able to handle channel read()/exception() methods that return a chain of frames
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: http://svn.digium.com/svn/asterisk/trunk@8850 f38db490-d61c-443f-a65b-d21fe96a405b
This commit is contained in:
parent
2cddcd249d
commit
d6dcf07573
210
channel.c
210
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);
|
||||
|
|
|
@ -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;
|
||||
|
|
|
@ -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 */
|
||||
|
|
Reference in New Issue