diff options
-rw-r--r-- | include/asterisk/channel.h | 16 | ||||
-rw-r--r-- | main/autoservice.c | 28 | ||||
-rw-r--r-- | main/channel.c | 56 |
3 files changed, 73 insertions, 27 deletions
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 5aa5f1ff3..c33a4ae7a 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -1476,6 +1476,22 @@ int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, int ast_waitfor(struct ast_channel *chan, int ms); /*! + * \brief Should we keep this frame for later? + * + * There are functions such as ast_safe_sleep which will + * service a channel to ensure that it does not have a + * have a large backlog of queued frames. When this happens, + * we want to hold on to specific frame types and just drop + * others. This function will tell if the frame we just + * read should be held onto. + * + * \param frame The frame we just read + * \retval 1 frame should be kept + * \retval 0 frame should be dropped + */ +int ast_is_deferrable_frame(const struct ast_frame *frame); + +/*! * \brief Wait for a specified amount of time, looking for hangups * \param chan channel to wait for * \param ms length of time in milliseconds to sleep diff --git a/main/autoservice.c b/main/autoservice.c index 8e3465417..4b6449783 100644 --- a/main/autoservice.c +++ b/main/autoservice.c @@ -128,32 +128,8 @@ static void *autoservice_run(void *ign) * thread in charge of this channel will know. */ defer_frame = &hangup_frame; - } else { - - /* Do not add a default entry in this switch statement. Each new - * frame type should be addressed directly as to whether it should - * be queued up or not. */ - - switch (f->frametype) { - /* Save these frames */ - case AST_FRAME_DTMF_END: - case AST_FRAME_CONTROL: - case AST_FRAME_TEXT: - case AST_FRAME_IMAGE: - case AST_FRAME_HTML: - defer_frame = f; - break; - - /* Throw these frames away */ - case AST_FRAME_DTMF_BEGIN: - case AST_FRAME_VOICE: - case AST_FRAME_VIDEO: - case AST_FRAME_NULL: - case AST_FRAME_IAX: - case AST_FRAME_CNG: - case AST_FRAME_MODEM: - break; - } + } else if (ast_is_deferrable_frame(f)) { + defer_frame = f; } if (defer_frame) { diff --git a/main/channel.c b/main/channel.c index 7866b14e0..908b65777 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1473,12 +1473,41 @@ struct ast_channel *ast_channel_get_by_exten(const char *exten, const char *cont return ast_channel_get_full(NULL, 0, exten, context); } +int ast_is_deferrable_frame(const struct ast_frame *frame) +{ + /* Do not add a default entry in this switch statement. Each new + * frame type should be addressed directly as to whether it should + * be queued up or not. + */ + switch (frame->frametype) { + case AST_FRAME_DTMF_END: + case AST_FRAME_CONTROL: + case AST_FRAME_TEXT: + case AST_FRAME_IMAGE: + case AST_FRAME_HTML: + return 1; + + case AST_FRAME_DTMF_BEGIN: + case AST_FRAME_VOICE: + case AST_FRAME_VIDEO: + case AST_FRAME_NULL: + case AST_FRAME_IAX: + case AST_FRAME_CNG: + case AST_FRAME_MODEM: + return 0; + } + return 0; +} + /*! \brief Wait, look for hangups and condition arg */ int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(void*), void *data) { struct ast_frame *f; struct ast_silence_generator *silgen = NULL; int res = 0; + AST_LIST_HEAD_NOLOCK(, ast_frame) deferred_frames; + + AST_LIST_HEAD_INIT_NOLOCK(&deferred_frames); /* If no other generator is present, start silencegen while waiting */ if (ast_opt_transmit_silence && !chan->generatordata) { @@ -1486,6 +1515,7 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(voi } while (ms > 0) { + struct ast_frame *dup_f = NULL; if (cond && ((*cond)(data) == 0)) { break; } @@ -1500,7 +1530,18 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(voi res = -1; break; } - ast_frfree(f); + + if (!ast_is_deferrable_frame(f)) { + ast_frfree(f); + continue; + } + + if ((dup_f = ast_frisolate(f))) { + if (dup_f != f) { + ast_frfree(f); + } + AST_LIST_INSERT_HEAD(&deferred_frames, dup_f, frame_list); + } } } @@ -1509,6 +1550,19 @@ int ast_safe_sleep_conditional(struct ast_channel *chan, int ms, int (*cond)(voi ast_channel_stop_silence_generator(chan, silgen); } + /* We need to free all the deferred frames, but we only need to + * queue the deferred frames if there was no error and no + * hangup was received + */ + ast_channel_lock(chan); + while ((f = AST_LIST_REMOVE_HEAD(&deferred_frames, frame_list))) { + if (!res) { + ast_queue_frame_head(chan, f); + } + ast_frfree(f); + } + ast_channel_unlock(chan); + return res; } |