summaryrefslogtreecommitdiff
path: root/main/channel.c
diff options
context:
space:
mode:
authorMark Michelson <mmichelson@digium.com>2010-05-21 16:44:27 +0000
committerMark Michelson <mmichelson@digium.com>2010-05-21 16:44:27 +0000
commit73e8c7572e5558878abcbcb121ac6128853f7ed6 (patch)
tree61ecb857fca45a0e7d2ae5d7e2333104aa3fc330 /main/channel.c
parent0a63e3fa10d25273e5cc658c88a0dc54c93d4e7d (diff)
Merged revisions 264996 via svnmerge from
https://origsvn.digium.com/svn/asterisk/branches/1.4 ........ r264996 | mmichelson | 2010-05-21 11:28:34 -0500 (Fri, 21 May 2010) | 32 lines Allow ast_safe_sleep to defer specific frames until after the sleep has concluded. From reviewboard Background: A Digium customer discovered a somewhat odd bug. The setup is that parties A and B are bridged, and party A places party B on hold. While party B is listening to hold music, he mashes a bunch of DTMF. Party A takes party B off hold while this is happening, but party B continues to hear hold music. I could reproduce this about 1 in 5 times. The issue: When DTMF features are enabled and a user presses keys, the channel that the DTMF is streamed to is placed in an ast_safe_sleep for 100 ms, the duration of the emulated tone. If an AST_CONTROL_UNHOLD frame is read from the channel during the sleep, the frame is dropped. Thus the unhold indication is never made to the channel that was originally placed on hold. The fix: Originally, I discussed with Kevin possible ways of fixing the specific problem reported. However, we determined that the same type of problem could happen in other situations where ast_safe_sleep() is used. Using autoservice as a model, I modified ast_safe_sleep_conditional() to defer specific frame types so they can be re-queued once the sleep has finished. I made a common function for determining if a frame should be deferred so that there are not two identical switch blocks to maintain. Review: https://reviewboard.asterisk.org/r/674/ ........ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@264997 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'main/channel.c')
-rw-r--r--main/channel.c56
1 files changed, 55 insertions, 1 deletions
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;
}