aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--apps/app_mixmonitor.c93
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)