aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--include/asterisk/channel.h16
-rw-r--r--main/autoservice.c28
-rw-r--r--main/channel.c56
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;
}