Merged revisions 201056,201090 via svnmerge from

https://origsvn.digium.com/svn/asterisk/trunk

................
  r201056 | kpfleming | 2009-06-16 13:54:30 -0500 (Tue, 16 Jun 2009) | 18 lines
  
  Merged revisions 200991 via svnmerge from 
  https://origsvn.digium.com/svn/asterisk/branches/1.4
  
  ........
    r200991 | kpfleming | 2009-06-16 12:05:38 -0500 (Tue, 16 Jun 2009) | 11 lines
    
    Improve support for media paths that can generate multiple frames at once.
    
    There are various media paths in Asterisk (codec translators and UDPTL, primarily)
    that can generate more than one frame to be generated when the application calling
    them expects only a single frame. This patch addresses a number of those cases,
    at least the primary ones to solve the known problems. In addition it removes the
    broken TRACE_FRAMES support, fixes a number of bugs in various frame-related API
    functions, and cleans up various code paths affected by these changes.
    
    https://reviewboard.asterisk.org/r/175/
  ........
................
  r201090 | kpfleming | 2009-06-16 14:27:12 -0500 (Tue, 16 Jun 2009) | 5 lines
  
  Another minor fix to compiler attribute checking.
  
  Defaulting to 'static' for the function scope was bad... so remove it.
................


git-svn-id: https://origsvn.digium.com/svn/asterisk/branches/1.6.0@201093 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
Kevin P. Fleming
2009-06-16 19:34:39 +00:00
parent c2d79c89bb
commit 968108c25c
14 changed files with 355 additions and 274 deletions

View File

@@ -981,56 +981,82 @@ struct ast_channel *__ast_channel_alloc(int needqueue, int state, const char *ci
return result;
}
/*! \brief Queue an outgoing media frame */
static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head)
static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, int head, struct ast_frame *after)
{
struct ast_frame *f;
struct ast_frame *cur;
int blah = 1;
int qlen = 0;
/* Build us a copy and free the original one */
if (!(f = ast_frdup(fin))) {
return -1;
}
unsigned int new_frames = 0;
unsigned int new_voice_frames = 0;
unsigned int queued_frames = 0;
unsigned int queued_voice_frames = 0;
AST_LIST_HEAD_NOLOCK(, ast_frame) frames;
ast_channel_lock(chan);
/* See if the last frame on the queue is a hangup, if so don't queue anything */
if ((cur = AST_LIST_LAST(&chan->readq)) && (cur->frametype == AST_FRAME_CONTROL) && (cur->subclass == AST_CONTROL_HANGUP)) {
ast_frfree(f);
if ((cur = AST_LIST_LAST(&chan->readq)) &&
(cur->frametype == AST_FRAME_CONTROL) &&
(cur->subclass == AST_CONTROL_HANGUP)) {
ast_channel_unlock(chan);
return 0;
}
/* Count how many frames exist on the queue */
AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) {
qlen++;
}
/* Build copies of all the frames and count them */
AST_LIST_HEAD_INIT_NOLOCK(&frames);
for (cur = fin; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
if (!(f = ast_frdup(cur))) {
ast_frfree(AST_LIST_FIRST(&frames));
return -1;
}
/* Allow up to 96 voice frames outstanding, and up to 128 total frames */
if (((fin->frametype == AST_FRAME_VOICE) && (qlen > 96)) || (qlen > 128)) {
if (fin->frametype != AST_FRAME_VOICE) {
ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
ast_assert(fin->frametype == AST_FRAME_VOICE);
} else {
ast_debug(1, "Dropping voice to exceptionally long queue on %s\n", chan->name);
ast_frfree(f);
ast_channel_unlock(chan);
return 0;
AST_LIST_INSERT_TAIL(&frames, f, frame_list);
new_frames++;
if (f->frametype == AST_FRAME_VOICE) {
new_voice_frames++;
}
}
if (head) {
AST_LIST_INSERT_HEAD(&chan->readq, f, frame_list);
/* Count how many frames exist on the queue */
AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) {
queued_frames++;
if (cur->frametype == AST_FRAME_VOICE) {
queued_voice_frames++;
}
}
if ((queued_frames + new_frames) > 128) {
ast_log(LOG_WARNING, "Exceptionally long queue length queuing to %s\n", chan->name);
while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
ast_frfree(f);
}
ast_channel_unlock(chan);
return 0;
}
if ((queued_voice_frames + new_voice_frames) > 96) {
ast_log(LOG_WARNING, "Exceptionally long voice queue length queuing to %s\n", chan->name);
while ((f = AST_LIST_REMOVE_HEAD(&frames, frame_list))) {
ast_frfree(f);
}
ast_channel_unlock(chan);
return 0;
}
if (after) {
AST_LIST_INSERT_LIST_AFTER(&chan->readq, &frames, after, frame_list);
} else {
AST_LIST_INSERT_TAIL(&chan->readq, f, frame_list);
if (head) {
AST_LIST_APPEND_LIST(&frames, &chan->readq, frame_list);
AST_LIST_HEAD_INIT_NOLOCK(&chan->readq);
}
AST_LIST_APPEND_LIST(&chan->readq, &frames, frame_list);
}
if (chan->alertpipe[1] > -1) {
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));
if (write(chan->alertpipe[1], &blah, new_frames * sizeof(blah)) != (new_frames * sizeof(blah))) {
ast_log(LOG_WARNING, "Unable to write to alert pipe on %s (qlen = %d): %s!\n",
chan->name, queued_frames, strerror(errno));
}
#ifdef HAVE_DAHDI
} else if (chan->timingfd > -1) {
@@ -1047,12 +1073,12 @@ static int __ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin, in
int ast_queue_frame(struct ast_channel *chan, struct ast_frame *fin)
{
return __ast_queue_frame(chan, fin, 0);
return __ast_queue_frame(chan, fin, 0, NULL);
}
int ast_queue_frame_head(struct ast_channel *chan, struct ast_frame *fin)
{
return __ast_queue_frame(chan, fin, 1);
return __ast_queue_frame(chan, fin, 1, NULL);
}
/*! \brief Queue a hangup frame for channel */
@@ -2701,13 +2727,14 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
chan->fdno = -1;
if (f) {
struct ast_frame *readq_tail = AST_LIST_LAST(&chan->readq);
/* if the channel driver returned more than one frame, stuff the excess
into the readq for the next ast_read call (note that we can safely assume
that the readq is empty, because otherwise we would not have called into
the channel driver and f would be only a single frame)
into the readq for the next ast_read call
*/
if (AST_LIST_NEXT(f, frame_list)) {
AST_LIST_HEAD_SET_NOLOCK(&chan->readq, AST_LIST_NEXT(f, frame_list));
ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list));
ast_frfree(AST_LIST_NEXT(f, frame_list));
AST_LIST_NEXT(f, frame_list) = NULL;
}
@@ -2927,12 +2954,30 @@ static struct ast_frame *__ast_read(struct ast_channel *chan, int dropaudio)
}
}
if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL)
if (chan->readtrans && (f = ast_translate(chan->readtrans, f, 1)) == NULL) {
f = &ast_null_frame;
else
/* Run generator sitting on the line if timing device not available
* and synchronous generation of outgoing frames is necessary */
ast_read_generator_actions(chan, f);
}
/* it is possible for the translation process on chan->readtrans to have
produced multiple frames from the single input frame we passed it; if
this happens, queue the additional frames *before* the frames we may
have queued earlier. if the readq was empty, put them at the head of
the queue, and if it was not, put them just after the frame that was
at the end of the queue.
*/
if (AST_LIST_NEXT(f, frame_list)) {
if (!readq_tail) {
ast_queue_frame_head(chan, AST_LIST_NEXT(f, frame_list));
} else {
__ast_queue_frame(chan, AST_LIST_NEXT(f, frame_list), 0, readq_tail);
}
ast_frfree(AST_LIST_NEXT(f, frame_list));
AST_LIST_NEXT(f, frame_list) = NULL;
}
/* Run generator sitting on the line if timing device not available
* and synchronous generation of outgoing frames is necessary */
ast_read_generator_actions(chan, f);
}
default:
/* Just pass it on! */
@@ -3268,7 +3313,7 @@ int ast_write_video(struct ast_channel *chan, struct ast_frame *fr)
int ast_write(struct ast_channel *chan, struct ast_frame *fr)
{
int res = -1;
struct ast_frame *f = NULL, *f2 = NULL;
struct ast_frame *f = NULL;
int count = 0;
/*Deadlock avoidance*/
@@ -3340,10 +3385,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
break;
case AST_FRAME_DTMF_END:
if (chan->audiohooks) {
struct ast_frame *old_frame = fr;
fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
if (old_frame != fr)
f = fr;
struct ast_frame *new_frame = fr;
new_frame = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
if (new_frame != fr) {
ast_frfree(new_frame);
}
}
send_dtmf_event(chan, "Sent", fr->subclass, "No", "Yes");
ast_clear_flag(chan, AST_FLAG_BLOCKING);
@@ -3378,14 +3425,6 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
if (chan->tech->write == NULL)
break; /*! \todo XXX should return 0 maybe ? */
/* If audiohooks are present, write the frame out */
if (chan->audiohooks) {
struct ast_frame *old_frame = fr;
fr = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, fr);
if (old_frame != fr)
f2 = fr;
}
/* If the frame is in the raw write format, then it's easy... just use the frame - otherwise we will have to translate */
if (fr->subclass == chan->rawwriteformat)
f = fr;
@@ -3397,37 +3436,82 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
break;
}
if (chan->audiohooks) {
struct ast_frame *new_frame, *cur;
for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
new_frame = ast_audiohook_write_list(chan, chan->audiohooks, AST_AUDIOHOOK_DIRECTION_WRITE, cur);
if (new_frame != cur) {
ast_frfree(new_frame);
}
}
}
/* If Monitor is running on this channel, then we have to write frames out there too */
/* the translator on chan->writetrans may have returned multiple frames
from the single frame we passed in; if so, feed each one of them to the
monitor */
if (chan->monitor && chan->monitor->write_stream) {
struct ast_frame *cur;
for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
/* XXX must explain this code */
#ifndef MONITOR_CONSTANT_DELAY
int jump = chan->insmpl - chan->outsmpl - 4 * f->samples;
if (jump >= 0) {
jump = chan->insmpl - chan->outsmpl;
if (ast_seekstream(chan->monitor->write_stream, jump, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump + f->samples;
} else
chan->outsmpl += f->samples;
int jump = chan->insmpl - chan->outsmpl - 4 * cur->samples;
if (jump >= 0) {
jump = chan->insmpl - chan->outsmpl;
if (ast_seekstream(chan->monitor->write_stream, jump, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump + cur->samples;
} else {
chan->outsmpl += cur->samples;
}
#else
int jump = chan->insmpl - chan->outsmpl;
if (jump - MONITOR_DELAY >= 0) {
if (ast_seekstream(chan->monitor->write_stream, jump - f->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump;
} else
chan->outsmpl += f->samples;
int jump = chan->insmpl - chan->outsmpl;
if (jump - MONITOR_DELAY >= 0) {
if (ast_seekstream(chan->monitor->write_stream, jump - cur->samples, SEEK_FORCECUR) == -1)
ast_log(LOG_WARNING, "Failed to perform seek in monitoring write stream, synchronization between the files may be broken\n");
chan->outsmpl += jump;
} else {
chan->outsmpl += cur->samples;
}
#endif
if (chan->monitor->state == AST_MONITOR_RUNNING) {
if (ast_writestream(chan->monitor->write_stream, f) < 0)
ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
if (chan->monitor->state == AST_MONITOR_RUNNING) {
if (ast_writestream(chan->monitor->write_stream, cur) < 0)
ast_log(LOG_WARNING, "Failed to write data to channel monitor write stream\n");
}
}
}
if (f)
res = chan->tech->write(chan,f);
else
res = 0;
/* the translator on chan->writetrans may have returned multiple frames
from the single frame we passed in; if so, feed each one of them to the
channel, freeing each one after it has been written */
if ((f != fr) && AST_LIST_NEXT(f, frame_list)) {
struct ast_frame *cur, *next;
unsigned int skip = 0;
for (cur = f, next = AST_LIST_NEXT(cur, frame_list);
cur;
cur = next, next = cur ? AST_LIST_NEXT(cur, frame_list) : NULL) {
if (!skip) {
if ((res = chan->tech->write(chan, cur)) < 0) {
chan->_softhangup |= AST_SOFTHANGUP_DEV;
skip = 1;
} else if (next) {
/* don't do this for the last frame in the list,
as the code outside the loop will do it once
*/
chan->fout = FRAMECOUNT_INC(chan->fout);
}
}
ast_frfree(cur);
}
/* reset f so the code below doesn't attempt to free it */
f = NULL;
} else {
res = chan->tech->write(chan, f);
}
break;
case AST_FRAME_NULL:
case AST_FRAME_IAX:
@@ -3444,13 +3528,12 @@ int ast_write(struct ast_channel *chan, struct ast_frame *fr)
if (f && f != fr)
ast_frfree(f);
if (f2)
ast_frfree(f2);
ast_clear_flag(chan, AST_FLAG_BLOCKING);
/* Consider a write failure to force a soft hangup */
if (res < 0)
if (res < 0) {
chan->_softhangup |= AST_SOFTHANGUP_DEV;
else {
} else {
chan->fout = FRAMECOUNT_INC(chan->fout);
}
done: