diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h index 3d58c83f60..821ed09b0c 100644 --- a/include/asterisk/channel.h +++ b/include/asterisk/channel.h @@ -1135,7 +1135,24 @@ int ast_indicate_data(struct ast_channel *chan, int condition, const void *data, \return Returns < 0 on failure, 0 if nothing ever arrived, and the # of ms remaining otherwise */ int ast_waitfor(struct ast_channel *chan, int ms); -/*! \brief Wait for a specified amount of time, looking for hangups +/*! + * \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 * Waits for a specified amount of time, servicing the channel as required. diff --git a/main/autoservice.c b/main/autoservice.c index 4e1cd52033..0a963097d4 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 d8a83034d0..426e6a7d94 100644 --- a/main/channel.c +++ b/main/channel.c @@ -1330,12 +1330,41 @@ struct ast_channel *ast_channel_search_locked(int (*is_match)(struct ast_channel return c; } +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) { @@ -1343,6 +1372,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; } @@ -1357,7 +1387,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); + } } } @@ -1366,6 +1407,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; }