diff options
-rw-r--r-- | apps/app_mixmonitor.c | 93 |
1 files changed, 88 insertions, 5 deletions
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c index c5a5e1095..7fdc4cb92 100644 --- a/apps/app_mixmonitor.c +++ b/apps/app_mixmonitor.c @@ -138,7 +138,7 @@ struct mixmonitor { char *post_process; char *name; unsigned int flags; - struct ast_channel *chan; + struct mixmonitor_ds *mixmonitor_ds; }; enum { @@ -164,6 +164,50 @@ AST_APP_OPTIONS(mixmonitor_opts, { AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME), }); +/* This structure is used as a means of making sure that our pointer to + * the channel we are monitoring remains valid. This is very similar to + * what is used in app_chanspy.c. + */ +struct mixmonitor_ds { + struct ast_channel *chan; + /* These condition variables are used to be sure that the channel + * hangup code completes before the mixmonitor thread attempts to + * free this structure. The combination of a bookean flag and a + * ast_cond_t ensure that no matter what order the threads run in, + * we are guaranteed to never have the waiting thread block forever + * in the case that the signaling thread runs first. + */ + unsigned int destruction_ok; + ast_cond_t destruction_condition; + ast_mutex_t lock; +}; + +static void mixmonitor_ds_destroy(void *data) +{ + struct mixmonitor_ds *mixmonitor_ds = data; + + ast_mutex_lock(&mixmonitor_ds->lock); + mixmonitor_ds->chan = NULL; + mixmonitor_ds->destruction_ok = 1; + ast_cond_signal(&mixmonitor_ds->destruction_condition); + ast_mutex_unlock(&mixmonitor_ds->lock); +} + +static void mixmonitor_ds_chan_fixup(void *data, struct ast_channel *old_chan, struct ast_channel *new_chan) +{ + struct mixmonitor_ds *mixmonitor_ds = data; + + ast_mutex_lock(&mixmonitor_ds->lock); + mixmonitor_ds->chan = new_chan; + ast_mutex_unlock(&mixmonitor_ds->lock); +} + +static struct ast_datastore_info mixmonitor_ds_info = { + .type = "mixmonitor", + .destroy = mixmonitor_ds_destroy, + .chan_fixup = mixmonitor_ds_chan_fixup, +}; + static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) { struct ast_channel *peer = NULL; @@ -205,7 +249,9 @@ static void *mixmonitor_thread(void *obj) if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) continue; - if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) { + ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); + if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) { + ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); /* Initialize the file if not already done so */ if (!fs && !errflag) { oflags = O_CREAT | O_WRONLY; @@ -225,6 +271,8 @@ static void *mixmonitor_thread(void *obj) /* Write out frame */ if (fs) ast_writestream(fs, fr); + } else { + ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); } /* All done! free it. */ @@ -246,12 +294,46 @@ static void *mixmonitor_thread(void *obj) ast_safe_system(mixmonitor->post_process); } + ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock); + if (!mixmonitor->mixmonitor_ds->destruction_ok) { + ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock); + } + ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock); + ast_free(mixmonitor->mixmonitor_ds); ast_free(mixmonitor); - return NULL; } +static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan) +{ + struct ast_datastore *datastore = NULL; + struct mixmonitor_ds *mixmonitor_ds; + + if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) { + return -1; + } + + ast_mutex_init(&mixmonitor_ds->lock); + ast_cond_init(&mixmonitor_ds->destruction_condition, NULL); + + if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) { + ast_free(mixmonitor_ds); + return -1; + } + + /* No need to lock mixmonitor_ds since this is still operating in the channel's thread */ + mixmonitor_ds->chan = chan; + datastore->data = mixmonitor_ds; + + ast_channel_lock(chan); + ast_channel_datastore_add(chan, datastore); + ast_channel_unlock(chan); + + mixmonitor->mixmonitor_ds = mixmonitor_ds; + return 0; +} + static void launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process) { @@ -285,7 +367,9 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename /* Copy over flags and channel name */ mixmonitor->flags = flags; - mixmonitor->chan = chan; + if (setup_mixmonitor_ds(mixmonitor, chan)) { + return; + } mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); strcpy(mixmonitor->name, chan->name); if (!ast_strlen_zero(postprocess2)) { @@ -318,7 +402,6 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename } ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor); - } static int mixmonitor_exec(struct ast_channel *chan, void *data) |