diff options
-rw-r--r-- | apps/app_chanspy.c | 18 | ||||
-rw-r--r-- | apps/app_meetme.c | 16 | ||||
-rw-r--r-- | apps/app_mixmonitor.c | 13 | ||||
-rw-r--r-- | autoconf/ast_gcc_attribute.m4 | 11 | ||||
-rwxr-xr-x | configure | 79 | ||||
-rw-r--r-- | configure.ac | 2 | ||||
-rw-r--r-- | include/asterisk/channel.h | 23 | ||||
-rw-r--r-- | include/asterisk/frame.h | 9 | ||||
-rw-r--r-- | include/asterisk/linkedlists.h | 32 | ||||
-rw-r--r-- | main/autoservice.c | 17 | ||||
-rw-r--r-- | main/channel.c | 235 | ||||
-rw-r--r-- | main/file.c | 18 | ||||
-rw-r--r-- | main/frame.c | 121 | ||||
-rw-r--r-- | main/slinfactory.c | 27 |
14 files changed, 351 insertions, 270 deletions
diff --git a/apps/app_chanspy.c b/apps/app_chanspy.c index c5a26772d..4c1bf0e4b 100644 --- a/apps/app_chanspy.c +++ b/apps/app_chanspy.c @@ -192,7 +192,7 @@ static void spy_release(struct ast_channel *chan, void *data) static int spy_generate(struct ast_channel *chan, void *data, int len, int samples) { struct chanspy_translation_helper *csth = data; - struct ast_frame *f = NULL; + struct ast_frame *f, *cur; ast_audiohook_lock(&csth->spy_audiohook); if (csth->spy_audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) { @@ -208,14 +208,16 @@ static int spy_generate(struct ast_channel *chan, void *data, int len, int sampl if (!f) return 0; - if (ast_write(chan, f)) { - ast_frfree(f); - return -1; - } + for (cur = f; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + if (ast_write(chan, cur)) { + ast_frfree(f); + return -1; + } - if (csth->fd) { - if (write(csth->fd, f->data, f->datalen) < 0) { - ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); + if (csth->fd) { + if (write(csth->fd, cur->data, cur->datalen) < 0) { + ast_log(LOG_WARNING, "write() failed: %s\n", strerror(errno)); + } } } diff --git a/apps/app_meetme.c b/apps/app_meetme.c index c4b5a1df2..bbbd87f90 100644 --- a/apps/app_meetme.c +++ b/apps/app_meetme.c @@ -2700,9 +2700,19 @@ static int conf_run(struct ast_channel *chan, struct ast_conference *conf, int c } } if (conf->transframe[index]) { - if (conf->transframe[index]->frametype != AST_FRAME_NULL) { - if (can_write(chan, confflags) && ast_write(chan, conf->transframe[index])) - ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name); + if ((conf->transframe[index]->frametype != AST_FRAME_NULL) && + can_write(chan, confflags)) { + struct ast_frame *cur; + + /* the translator may have returned a list of frames, so + write each one onto the channel + */ + for (cur = conf->transframe[index]; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + if (ast_write(chan, cur)) { + ast_log(LOG_WARNING, "Unable to write frame to channel %s\n", chan->name); + break; + } + } } } else { ast_mutex_unlock(&conf->listenlock); diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c index 8f9a26e8e..2f659a7b4 100644 --- a/apps/app_mixmonitor.c +++ b/apps/app_mixmonitor.c @@ -222,10 +222,15 @@ static void *mixmonitor_thread(void *obj) errflag = 1; } } - - /* Write out frame */ - if (fs) - ast_writestream(fs, fr); + + /* Write out the frame(s) */ + if (fs) { + struct ast_frame *cur; + + for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + ast_writestream(fs, cur); + } + } } else { ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); } diff --git a/autoconf/ast_gcc_attribute.m4 b/autoconf/ast_gcc_attribute.m4 index f08b2545b..d3f05be5b 100644 --- a/autoconf/ast_gcc_attribute.m4 +++ b/autoconf/ast_gcc_attribute.m4 @@ -7,17 +7,10 @@ AC_MSG_CHECKING(for compiler 'attribute $1' support) saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" -if test "x$3" = "x" -then -attribute_scope="static" -else -attribute_scope="$3" -fi - if test "x$2" = "x" then AC_COMPILE_IFELSE( - AC_LANG_PROGRAM([$attribute_scope void __attribute__(($1)) *test(void *muffin, ...) {return (void *) 0;}], + AC_LANG_PROGRAM([$3 void __attribute__(($1)) *test(void *muffin, ...) {return (void *) 0;}], []), AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED([HAVE_ATTRIBUTE_$1], 1, [Define to 1 if your GCC C compiler supports the '$1' attribute.]), @@ -25,7 +18,7 @@ AC_COMPILE_IFELSE( ) else AC_COMPILE_IFELSE( - AC_LANG_PROGRAM([$attribute_scope void __attribute__(($2)) *test(void *muffin, ...) {return (void *) 0;}], + AC_LANG_PROGRAM([$3 void __attribute__(($2)) *test(void *muffin, ...) {return (void *) 0;}], []), AC_MSG_RESULT(yes) AC_DEFINE_UNQUOTED([HAVE_ATTRIBUTE_$1], 1, [Define to 1 if your GCC C compiler supports the '$1' attribute.]), @@ -1,5 +1,5 @@ #! /bin/sh -# From configure.ac Revision: 191368 . +# From configure.ac Revision: 200986 . # Guess values for system-dependent variables and create Makefiles. # Generated by GNU Autoconf 2.63 for asterisk 1.6. # @@ -17398,20 +17398,13 @@ CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" if test "x" = "x" then -attribute_scope="static" -else -attribute_scope="" -fi - -if test "x" = "x" -then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__((pure)) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__((pure)) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17462,7 +17455,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17519,20 +17512,13 @@ CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" if test "x" = "x" then -attribute_scope="static" -else -attribute_scope="" -fi - -if test "x" = "x" -then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__((malloc)) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__((malloc)) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17583,7 +17569,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17640,20 +17626,13 @@ CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" if test "x" = "x" then -attribute_scope="static" -else -attribute_scope="" -fi - -if test "x" = "x" -then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__((const)) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__((const)) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17704,7 +17683,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17761,20 +17740,13 @@ CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" if test "x" = "x" then -attribute_scope="static" -else -attribute_scope="" -fi - -if test "x" = "x" -then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__((unused)) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__((unused)) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17825,7 +17797,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17882,20 +17854,13 @@ CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" if test "x" = "x" then -attribute_scope="static" -else -attribute_scope="" -fi - -if test "x" = "x" -then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__((always_inline)) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__((always_inline)) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -17946,7 +17911,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -18003,20 +17968,13 @@ CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" if test "x" = "x" then -attribute_scope="static" -else -attribute_scope="" -fi - -if test "x" = "x" -then cat >conftest.$ac_ext <<_ACEOF /* confdefs.h. */ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__((deprecated)) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__((deprecated)) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -18067,7 +18025,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -18122,13 +18080,6 @@ $as_echo_n "checking for compiler 'attribute weak' support... " >&6; } saved_CFLAGS="$CFLAGS" CFLAGS="$CFLAGS -Wall -Wno-unused -Werror" -if test "x""" = "x" -then -attribute_scope="static" -else -attribute_scope="""" -fi - if test "x" = "x" then cat >conftest.$ac_ext <<_ACEOF @@ -18137,7 +18088,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__((weak)) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__((weak)) *test(void *muffin, ...) {return (void *) 0;} int main () { @@ -18188,7 +18139,7 @@ _ACEOF cat confdefs.h >>conftest.$ac_ext cat >>conftest.$ac_ext <<_ACEOF /* end confdefs.h. */ -$attribute_scope void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} + void __attribute__(()) *test(void *muffin, ...) {return (void *) 0;} int main () { diff --git a/configure.ac b/configure.ac index f1801231b..ce3f23ea8 100644 --- a/configure.ac +++ b/configure.ac @@ -484,7 +484,7 @@ AST_GCC_ATTRIBUTE(const) AST_GCC_ATTRIBUTE(unused) AST_GCC_ATTRIBUTE(always_inline) AST_GCC_ATTRIBUTE(deprecated) -AST_GCC_ATTRIBUTE(weak, , "") +AST_GCC_ATTRIBUTE(weak) AC_MSG_CHECKING(for -ffunction-sections support) saved_CFLAGS="${CFLAGS}" diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 54710379e..c9a70faca 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -741,20 +741,27 @@ struct ast_channel * attribute_malloc __attribute__((format(printf, 12, 13))) __ast_channel_alloc(needqueue, state, cid_num, cid_name, acctcode, exten, context, amaflag, \ __FILE__, __LINE__, __FUNCTION__, __VA_ARGS__) -/*! - * \brief Queue an outgoing frame +/*! + * \brief Queue one or more frames to a channel's frame queue * - * \note The channel does not need to be locked before calling this function. + * \param chan the channel to queue the frame(s) on + * \param f the frame(s) to queue. Note that the frame(s) will be duplicated + * by this function. It is the responsibility of the caller to handle + * freeing the memory associated with the frame(s) being passed if + * necessary. + * + * \retval 0 success + * \retval non-zero failure */ int ast_queue_frame(struct ast_channel *chan, struct ast_frame *f); /*! - * \brief Queue an outgoing frame to the head of the frame queue + * \brief Queue one or more frames to the head of a channel's frame queue * - * \param chan the channel to queue the frame on - * \param f the frame to queue. Note that this frame will be duplicated by - * this function. It is the responsibility of the caller to handle - * freeing the memory associated with the frame being passed if + * \param chan the channel to queue the frame(s) on + * \param f the frame(s) to queue. Note that the frame(s) will be duplicated + * by this function. It is the responsibility of the caller to handle + * freeing the memory associated with the frame(s) being passed if * necessary. * * \retval 0 success diff --git a/include/asterisk/frame.h b/include/asterisk/frame.h index 430c79b17..144040525 100644 --- a/include/asterisk/frame.h +++ b/include/asterisk/frame.h @@ -424,9 +424,9 @@ struct ast_frame *ast_fralloc(char *source, int len); #endif /*! - * \brief Frees a frame + * \brief Frees a frame or list of frames * - * \param fr Frame to free + * \param fr Frame to free, or head of list to free * \param cache Whether to consider this frame for frame caching */ void ast_frame_free(struct ast_frame *fr, int cache); @@ -440,6 +440,11 @@ void ast_frame_free(struct ast_frame *fr, int cache); * data malloc'd. If you need to store frames, say for queueing, then * you should call this function. * \return Returns a frame on success, NULL on error + * \note This function may modify the frame passed to it, so you must + * not assume the frame will be intact after the isolated frame has + * been produced. In other words, calling this function on a frame + * should be the last operation you do with that frame before freeing + * it (or exiting the block, if the frame is on the stack.) */ struct ast_frame *ast_frisolate(struct ast_frame *fr); diff --git a/include/asterisk/linkedlists.h b/include/asterisk/linkedlists.h index 3c89e7117..d35cfad3e 100644 --- a/include/asterisk/linkedlists.h +++ b/include/asterisk/linkedlists.h @@ -744,13 +744,37 @@ struct { \ #define AST_RWLIST_APPEND_LIST AST_LIST_APPEND_LIST /*! - \brief Removes and returns the head entry from a list. + \brief Inserts a whole list after a specific entry in a list \param head This is a pointer to the list head structure + \param list This is a pointer to the list to be inserted. + \param elm This is a pointer to the entry after which the new list should + be inserted. \param field This is the name of the field (declared using AST_LIST_ENTRY()) - used to link entries of this list together. + used to link entries of the lists together. - Removes the head entry from the list, and returns a pointer to it. - This macro is safe to call on an empty list. + Note: The source list (the \a list parameter) will be empty after + calling this macro (the list entries are \b moved to the target list). + */ +#define AST_LIST_INSERT_LIST_AFTER(head, list, elm, field) do { \ + (list)->last->field.next = (elm)->field.next; \ + (elm)->field.next = (list)->first; \ + if ((head)->last == elm) { \ + (head)->last = (list)->last; \ + } \ + (list)->first = NULL; \ + (list)->last = NULL; \ +} while(0) + +#define AST_RWLIST_INSERT_LIST_AFTER AST_LIST_INSERT_LIST_AFTER + +/*! + * \brief Removes and returns the head entry from a list. + * \param head This is a pointer to the list head structure + * \param field This is the name of the field (declared using AST_LIST_ENTRY()) + * used to link entries of this list together. + * + * Removes the head entry from the list, and returns a pointer to it. + * This macro is safe to call on an empty list. */ #define AST_LIST_REMOVE_HEAD(head, field) ({ \ typeof((head)->first) cur = (head)->first; \ diff --git a/main/autoservice.c b/main/autoservice.c index 26cb08582..7ed271016 100644 --- a/main/autoservice.c +++ b/main/autoservice.c @@ -163,15 +163,22 @@ static void *autoservice_run(void *ign) continue; } - if ((dup_f = ast_frdup(defer_frame))) { - AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list); + if (defer_frame != f) { + if ((dup_f = ast_frdup(defer_frame))) { + AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list); + } + } else { + if ((dup_f = ast_frisolate(defer_frame))) { + if (dup_f != defer_frame) { + ast_frfree(defer_frame); + } + AST_LIST_INSERT_HEAD(&ents[i]->deferred_frames, dup_f, frame_list); + } } break; } - } - - if (f) { + } else if (f) { ast_frfree(f); } } diff --git a/main/channel.c b/main/channel.c index 3bcccea02..ddeee3310 100644 --- a/main/channel.c +++ b/main/channel.c @@ -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; } + /* 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; + } + + AST_LIST_INSERT_TAIL(&frames, f, frame_list); + new_frames++; + if (f->frametype == AST_FRAME_VOICE) { + new_voice_frames++; + } + } + /* Count how many frames exist on the queue */ AST_LIST_TRAVERSE(&chan->readq, cur, frame_list) { - qlen++; + queued_frames++; + if (cur->frametype == AST_FRAME_VOICE) { + queued_voice_frames++; + } } - /* 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); + 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; } + ast_channel_unlock(chan); + return 0; } - if (head) { - AST_LIST_INSERT_HEAD(&chan->readq, f, frame_list); + 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: diff --git a/main/file.c b/main/file.c index 799410b59..67c8c77c5 100644 --- a/main/file.c +++ b/main/file.c @@ -186,14 +186,20 @@ int ast_writestream(struct ast_filestream *fs, struct ast_frame *f) struct ast_frame *trf; fs->lastwriteformat = f->subclass; /* Get the translated frame but don't consume the original in case they're using it on another stream */ - trf = ast_translate(fs->trans, f, 0); - if (trf) { - res = fs->fmt->write(fs, trf); + if ((trf = ast_translate(fs->trans, f, 0))) { + struct ast_frame *cur; + + /* the translator may have returned multiple frames, so process them */ + for (cur = trf; cur; cur = AST_LIST_NEXT(cur, frame_list)) { + if ((res = fs->fmt->write(fs, trf))) { + ast_log(LOG_WARNING, "Translated frame write failed\n"); + break; + } + } ast_frfree(trf); - if (res) - ast_log(LOG_WARNING, "Translated frame write failed\n"); - } else + } else { res = 0; + } } } return res; diff --git a/main/frame.c b/main/frame.c index 269370d2a..c394718db 100644 --- a/main/frame.c +++ b/main/frame.c @@ -40,11 +40,6 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/dsp.h" #include "asterisk/file.h" -#ifdef TRACE_FRAMES -static int headers; -static AST_LIST_HEAD_STATIC(headerlist, ast_frame); -#endif - #if !defined(LOW_MEMORY) static void frame_cache_cleanup(void *data); @@ -316,12 +311,6 @@ static struct ast_frame *ast_frame_header_new(void) #endif f->mallocd_hdr_len = sizeof(*f); -#ifdef TRACE_FRAMES - AST_LIST_LOCK(&headerlist); - headers++; - AST_LIST_INSERT_HEAD(&headerlist, f, frame_list); - AST_LIST_UNLOCK(&headerlist); -#endif return f; } @@ -339,7 +328,7 @@ static void frame_cache_cleanup(void *data) } #endif -void ast_frame_free(struct ast_frame *fr, int cache) +static void __frame_free(struct ast_frame *fr, int cache) { if (ast_test_flag(fr, AST_FRFLAG_FROM_TRANSLATOR)) { ast_translate_frame_freed(fr); @@ -358,8 +347,8 @@ void ast_frame_free(struct ast_frame *fr, int cache) * to keep things simple... */ struct ast_frame_cache *frames; - if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) - && frames->size < FRAME_CACHE_MAX_SIZE) { + if ((frames = ast_threadstorage_get(&frame_cache, sizeof(*frames))) && + (frames->size < FRAME_CACHE_MAX_SIZE)) { AST_LIST_INSERT_HEAD(&frames->list, fr, frame_list); frames->size++; return; @@ -373,19 +362,25 @@ void ast_frame_free(struct ast_frame *fr, int cache) } if (fr->mallocd & AST_MALLOCD_SRC) { if (fr->src) - ast_free((char *)fr->src); + ast_free((void *) fr->src); } if (fr->mallocd & AST_MALLOCD_HDR) { -#ifdef TRACE_FRAMES - AST_LIST_LOCK(&headerlist); - headers--; - AST_LIST_REMOVE(&headerlist, fr, frame_list); - AST_LIST_UNLOCK(&headerlist); -#endif ast_free(fr); } } + +void ast_frame_free(struct ast_frame *frame, int cache) +{ + struct ast_frame *next; + + for (next = AST_LIST_NEXT(frame, frame_list); + frame; + frame = next, next = frame ? AST_LIST_NEXT(frame, frame_list) : NULL) { + __frame_free(frame, cache); + } +} + /*! * \brief 'isolates' a frame by duplicating non-malloc'ed components * (header, src, data). @@ -396,19 +391,29 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr) struct ast_frame *out; void *newdata; - ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR); - ast_clear_flag(fr, AST_FRFLAG_FROM_DSP); + /* if none of the existing frame is malloc'd, let ast_frdup() do it + since it is more efficient + */ + if (fr->mallocd == 0) { + return ast_frdup(fr); + } + + /* if everything is already malloc'd, we are done */ + if ((fr->mallocd & (AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA)) == + (AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA)) { + return fr; + } if (!(fr->mallocd & AST_MALLOCD_HDR)) { /* Allocate a new header if needed */ - if (!(out = ast_frame_header_new())) + if (!(out = ast_frame_header_new())) { return NULL; + } out->frametype = fr->frametype; out->subclass = fr->subclass; out->datalen = fr->datalen; out->samples = fr->samples; out->offset = fr->offset; - out->data = fr->data; /* Copy the timing data */ ast_copy_flags(out, fr, AST_FRFLAG_HAS_TIMING_INFO); if (ast_test_flag(fr, AST_FRFLAG_HAS_TIMING_INFO)) { @@ -416,26 +421,34 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr) out->len = fr->len; out->seqno = fr->seqno; } - } else + } else { + ast_clear_flag(fr, AST_FRFLAG_FROM_TRANSLATOR); + ast_clear_flag(fr, AST_FRFLAG_FROM_DSP); + ast_clear_flag(fr, AST_FRFLAG_FROM_FILESTREAM); out = fr; + } - if (!(fr->mallocd & AST_MALLOCD_SRC)) { - if (fr->src) { - if (!(out->src = ast_strdup(fr->src))) { - if (out != fr) - ast_free(out); - return NULL; + if (!(fr->mallocd & AST_MALLOCD_SRC) && fr->src) { + if (!(out->src = ast_strdup(fr->src))) { + if (out != fr) { + ast_free(out); } + return NULL; } - } else + } else { out->src = fr->src; + fr->src = NULL; + fr->mallocd &= ~AST_MALLOCD_SRC; + } if (!(fr->mallocd & AST_MALLOCD_DATA)) { if (!(newdata = ast_malloc(fr->datalen + AST_FRIENDLY_OFFSET))) { - if (out->src != fr->src) + if (out->src != fr->src) { ast_free((void *) out->src); - if (out != fr) + } + if (out != fr) { ast_free(out); + } return NULL; } newdata += AST_FRIENDLY_OFFSET; @@ -443,6 +456,10 @@ struct ast_frame *ast_frisolate(struct ast_frame *fr) out->datalen = fr->datalen; memcpy(newdata, fr->data, fr->datalen); out->data = newdata; + } else { + out->data = fr->data; + memset(&fr->data, 0, sizeof(fr->data)); + fr->mallocd &= ~AST_MALLOCD_DATA; } out->mallocd = AST_MALLOCD_HDR | AST_MALLOCD_SRC | AST_MALLOCD_DATA; @@ -933,44 +950,10 @@ void ast_frame_dump(const char *name, struct ast_frame *f, char *prefix) } -#ifdef TRACE_FRAMES -static char *show_frame_stats(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a) -{ - struct ast_frame *f; - int x=1; - - switch (cmd) { - case CLI_INIT: - e->command = "core show frame stats"; - e->usage = - "Usage: core show frame stats\n" - " Displays debugging statistics from framer\n"; - return NULL; - case CLI_GENERATE: - return NULL; - } - - if (a->argc != 4) - return CLI_SHOWUSAGE; - AST_LIST_LOCK(&headerlist); - ast_cli(a->fd, " Framer Statistics \n"); - ast_cli(a->fd, "---------------------------\n"); - ast_cli(a->fd, "Total allocated headers: %d\n", headers); - ast_cli(a->fd, "Queue Dump:\n"); - AST_LIST_TRAVERSE(&headerlist, f, frame_list) - ast_cli(a->fd, "%d. Type %d, subclass %d from %s\n", x++, f->frametype, f->subclass, f->src ? f->src : "<Unknown>"); - AST_LIST_UNLOCK(&headerlist); - return CLI_SUCCESS; -} -#endif - /* Builtin Asterisk CLI-commands for debugging */ static struct ast_cli_entry my_clis[] = { AST_CLI_DEFINE(show_codecs, "Displays a list of codecs"), AST_CLI_DEFINE(show_codec_n, "Shows a specific codec"), -#ifdef TRACE_FRAMES - AST_CLI_DEFINE(show_frame_stats, "Shows frame statistics"), -#endif }; int init_framer(void) diff --git a/main/slinfactory.c b/main/slinfactory.c index 4b71db42d..529c2557c 100644 --- a/main/slinfactory.c +++ b/main/slinfactory.c @@ -54,7 +54,7 @@ void ast_slinfactory_destroy(struct ast_slinfactory *sf) int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) { struct ast_frame *begin_frame = f, *duped_frame = NULL, *frame_ptr; - unsigned int x; + unsigned int x = 0; /* In some cases, we can be passed a frame which has no data in it, but * which has a positive number of samples defined. Once such situation is @@ -80,15 +80,17 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) sf->format = f->subclass; } - if (!(begin_frame = ast_translate(sf->trans, f, 0))) + if (!(begin_frame = ast_translate(sf->trans, f, 0))) { return 0; + } - duped_frame = ast_frdup(begin_frame); - - ast_frfree(begin_frame); - - if (!duped_frame) + if (!(duped_frame = ast_frisolate(begin_frame))) { return 0; + } + + if (duped_frame != begin_frame) { + ast_frfree(begin_frame); + } } else { if (sf->trans) { ast_translator_free_path(sf->trans); @@ -98,13 +100,16 @@ int ast_slinfactory_feed(struct ast_slinfactory *sf, struct ast_frame *f) return 0; } - x = 0; AST_LIST_TRAVERSE(&sf->queue, frame_ptr, frame_list) x++; - AST_LIST_INSERT_TAIL(&sf->queue, duped_frame, frame_list); - - sf->size += duped_frame->samples; + /* if the frame was translated, the translator may have returned multiple + frames, so process each of them + */ + for (begin_frame = duped_frame; begin_frame; begin_frame = AST_LIST_NEXT(begin_frame, frame_list)) { + AST_LIST_INSERT_TAIL(&sf->queue, begin_frame, frame_list); + sf->size += begin_frame->samples; + } return x; } |