summaryrefslogtreecommitdiff
path: root/apps/app_mixmonitor.c
diff options
context:
space:
mode:
authorJonathan Rose <jrose@digium.com>2012-01-23 18:16:20 +0000
committerJonathan Rose <jrose@digium.com>2012-01-23 18:16:20 +0000
commita1bef6041dba6e0e6fd54766493e2006711a31d0 (patch)
tree8cb996771276de9f1e9ed6781f5f0ece58b69d24 /apps/app_mixmonitor.c
parent74508e3bcabdb649ebffad129dfc3686fc283bff (diff)
Adds the ability to stop specific mixmonitors by using unique IDs set at monitor launch.
MixMonitor receives a new option i(channel_variable) which stores the unique id at said variable. StopMixMonitor now accepts ID as an optional argument, which if included will make StopMixMonitor specifically target the mixmonitor on that particular channel. CLI commands and AMI actions have been ammended to work with the IDs as well. In addition, monitors across a channel can now be listed be listed via CLI command "mixmonitor list <channel>" which will display all of the mixmonitors active on that channel along with the files they each have open. Created by Sergio González Martín. (closes issue ASTERISK-19096) Reported by: Sergio González Martín Review: https://reviewboard.asterisk.org/r/1643/ Review: https://reviewboard.asterisk.org/r/1682/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@352093 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'apps/app_mixmonitor.c')
-rw-r--r--apps/app_mixmonitor.c174
1 files changed, 135 insertions, 39 deletions
diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c
index 99dc7a4a8..4515805bc 100644
--- a/apps/app_mixmonitor.c
+++ b/apps/app_mixmonitor.c
@@ -52,6 +52,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
#include "asterisk/autochan.h"
#include "asterisk/manager.h"
#include "asterisk/mod_format.h"
+#include "asterisk/linkedlists.h"
/*** DOCUMENTATION
<application name="MixMonitor" language="en_US">
@@ -107,6 +108,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
Like with the basic filename argument, if an absolute path isn't given, it will create
the file in the configured monitoring directory.</para>
</option>
+ <option name="i">
+ <argument name="chanvar" required="true" />
+ <para>Stores the MixMonitor's ID on this channel variable.</para>
+ </option>
</optionlist>
</parameter>
<parameter name="command">
@@ -136,7 +141,12 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<synopsis>
Stop recording a call through MixMonitor, and free the recording's file handle.
</synopsis>
- <syntax />
+ <syntax>
+ <parameter name="MixMonitorID" required="false">
+ <para>If a valid ID is provided, then this command will stop only that specific
+ MixMonitor.</para>
+ </parameter>
+ </syntax>
<description>
<para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
on the current channel.</para>
@@ -207,6 +217,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
<parameter name="Channel" required="true">
<para>The name of the channel monitored.</para>
</parameter>
+ <parameter name="MixMonitorID" required="false">
+ <para>If a valid ID is provided, then this command will stop only that specific
+ MixMonitor.</para>
+ </parameter>
</syntax>
<description>
<para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
@@ -245,6 +259,7 @@ enum mixmonitor_flags {
MUXFLAG_READ = (1 << 6),
MUXFLAG_WRITE = (1 << 7),
MUXFLAG_COMBINED = (1 << 8),
+ MUXFLAG_UID = (1 << 9),
};
enum mixmonitor_args {
@@ -253,6 +268,7 @@ enum mixmonitor_args {
OPT_ARG_VOLUME,
OPT_ARG_WRITENAME,
OPT_ARG_READNAME,
+ OPT_ARG_UID,
OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
};
@@ -264,6 +280,7 @@ AST_APP_OPTIONS(mixmonitor_opts, {
AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
+ AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
});
struct mixmonitor_ds {
@@ -531,7 +548,7 @@ static void *mixmonitor_thread(void *obj)
return NULL;
}
-static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
+static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
{
struct ast_datastore *datastore = NULL;
struct mixmonitor_ds *mixmonitor_ds;
@@ -540,10 +557,14 @@ static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel
return -1;
}
+ if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
+ ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
+ }
+
ast_mutex_init(&mixmonitor_ds->lock);
ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
- if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
+ if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
ast_mutex_destroy(&mixmonitor_ds->lock);
ast_cond_destroy(&mixmonitor_ds->destruction_condition);
ast_free(mixmonitor_ds);
@@ -566,11 +587,12 @@ static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel
static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
unsigned int flags, int readvol, int writevol,
const char *post_process, const char *filename_write,
- const char *filename_read)
+ char *filename_read, const char *uid_channel_var)
{
pthread_t thread;
struct mixmonitor *mixmonitor;
char postprocess2[1024] = "";
+ char *datastore_id = NULL;
postprocess2[0] = 0;
/* If a post process system command is given attach it to the structure */
@@ -604,12 +626,21 @@ static void launch_monitor_thread(struct ast_channel *chan, const char *filename
return;
}
- if (setup_mixmonitor_ds(mixmonitor, chan)) {
+ if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
ast_autochan_destroy(mixmonitor->autochan);
mixmonitor_free(mixmonitor);
+ ast_free(datastore_id);
return;
}
+ if (!ast_strlen_zero(uid_channel_var)) {
+ if (datastore_id) {
+ pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
+ }
+ }
+ ast_free(datastore_id);
+
+
mixmonitor->name = ast_strdup(ast_channel_name(chan));
if (!ast_strlen_zero(postprocess2)) {
@@ -676,6 +707,7 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
char *filename_read = NULL;
char *filename_write = NULL;
char filename_buffer[1024] = "";
+ char *uid_channel_var = NULL;
struct ast_flags flags = { 0 };
char *parse;
@@ -684,7 +716,7 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
AST_APP_ARG(options);
AST_APP_ARG(post_process);
);
-
+
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
return -1;
@@ -708,7 +740,7 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
readvol = get_volfactor(x);
}
}
-
+
if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
@@ -718,7 +750,7 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
writevol = get_volfactor(x);
}
}
-
+
if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
@@ -736,8 +768,11 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
if (ast_test_flag(&flags, MUXFLAG_READ)) {
filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
}
- }
+ if (ast_test_flag(&flags, MUXFLAG_UID)) {
+ uid_channel_var = opts[OPT_ARG_UID];
+ }
+ }
/* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
@@ -751,7 +786,7 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
}
pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
- launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process, filename_write, filename_read);
+ launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process, filename_write, filename_read, uid_channel_var);
return 0;
}
@@ -759,34 +794,51 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data)
static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
{
struct ast_datastore *datastore = NULL;
+ char *parse = "";
+ struct mixmonitor_ds *mixmonitor_ds;
+
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(mixmonid);
+ );
+
+ if (!ast_strlen_zero(data)) {
+ parse = ast_strdupa(data);
+ }
+
+ AST_STANDARD_APP_ARGS(args, parse);
ast_channel_lock(chan);
- ast_audiohook_detach_source(chan, mixmonitor_spy_type);
- if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
- struct mixmonitor_ds *mixmonitor_ds = datastore->data;
-
- ast_mutex_lock(&mixmonitor_ds->lock);
-
- /* closing the filestream here guarantees the file is avaliable to the dialplan
- * after calling StopMixMonitor */
- mixmonitor_ds_close_fs(mixmonitor_ds);
-
- /* The mixmonitor thread may be waiting on the audiohook trigger.
- * In order to exit from the mixmonitor loop before waiting on channel
- * destruction, poke the audiohook trigger. */
- if (mixmonitor_ds->audiohook) {
- ast_audiohook_lock(mixmonitor_ds->audiohook);
- ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
- ast_audiohook_unlock(mixmonitor_ds->audiohook);
- mixmonitor_ds->audiohook = NULL;
- }
- ast_mutex_unlock(&mixmonitor_ds->lock);
+ if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
+ ast_channel_unlock(chan);
+ return -1;
+ }
+ mixmonitor_ds = datastore->data;
+
+ ast_mutex_lock(&mixmonitor_ds->lock);
+
+ /* closing the filestream here guarantees the file is avaliable to the dialplan
+ * after calling StopMixMonitor */
+ mixmonitor_ds_close_fs(mixmonitor_ds);
- /* Remove the datastore so the monitor thread can exit */
- if (!ast_channel_datastore_remove(chan, datastore)) {
- ast_datastore_free(datastore);
+ /* The mixmonitor thread may be waiting on the audiohook trigger.
+ * In order to exit from the mixmonitor loop before waiting on channel
+ * destruction, poke the audiohook trigger. */
+ if (mixmonitor_ds->audiohook) {
+ if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
+ ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
}
+ ast_audiohook_lock(mixmonitor_ds->audiohook);
+ ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
+ ast_audiohook_unlock(mixmonitor_ds->audiohook);
+ mixmonitor_ds->audiohook = NULL;
+ }
+
+ ast_mutex_unlock(&mixmonitor_ds->lock);
+
+ /* Remove the datastore so the monitor thread can exit */
+ if (!ast_channel_datastore_remove(chan, datastore)) {
+ ast_datastore_free(datastore);
}
ast_channel_unlock(chan);
@@ -796,12 +848,14 @@ static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
{
struct ast_channel *chan;
+ struct ast_datastore *datastore = NULL;
+ struct mixmonitor_ds *mixmonitor_ds = NULL;
switch (cmd) {
case CLI_INIT:
- e->command = "mixmonitor {start|stop}";
+ e->command = "mixmonitor {start|stop|list}";
e->usage =
- "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
+ "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
" The optional arguments are passed to the MixMonitor\n"
" application when the 'start' command is used.\n";
return NULL;
@@ -809,8 +863,9 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
}
- if (a->argc < 3)
+ if (a->argc < 3) {
return CLI_SHOWUSAGE;
+ }
if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
@@ -821,11 +876,34 @@ static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_
ast_channel_lock(chan);
if (!strcasecmp(a->argv[1], "start")) {
- mixmonitor_exec(chan, a->argv[3]);
+ mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
+ ast_channel_unlock(chan);
+ } else if (!strcasecmp(a->argv[1], "stop")){
+ ast_channel_unlock(chan);
+ stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
+ } else if (!strcasecmp(a->argv[1], "list")) {
+ ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
+ ast_cli(a->fd, "=========================================================================\n");
+ AST_LIST_TRAVERSE(&chan->datastores, datastore, entry) {
+ if (datastore->info == &mixmonitor_ds_info) {
+ char *filename = "";
+ char *filename_read = "";
+ char *filename_write = "";
+ mixmonitor_ds = datastore->data;
+ if (mixmonitor_ds->fs)
+ filename = ast_strdupa(mixmonitor_ds->fs->filename);
+ if (mixmonitor_ds->fs_read)
+ filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
+ if (mixmonitor_ds->fs_write)
+ filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
+ ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
+ }
+ }
ast_channel_unlock(chan);
} else {
ast_channel_unlock(chan);
- ast_audiohook_detach_source(chan, mixmonitor_spy_type);
+ chan = ast_channel_unref(chan);
+ return CLI_SHOWUSAGE;
}
chan = ast_channel_unref(chan);
@@ -908,6 +986,10 @@ static int manager_mixmonitor(struct mansession *s, const struct message *m)
const char *id = astman_get_header(m, "ActionID");
const char *file = astman_get_header(m, "File");
const char *options = astman_get_header(m, "Options");
+ char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
+ struct ast_flags flags = { 0 };
+ char *uid_channel_var = NULL;
+ const char *mixmonitor_id = NULL;
int res;
char args[PATH_MAX] = "";
@@ -923,10 +1005,19 @@ static int manager_mixmonitor(struct mansession *s, const struct message *m)
return AMI_SUCCESS;
}
+ if (!ast_strlen_zero(options)) {
+ ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
+ }
+
snprintf(args, sizeof(args), "%s,%s", file, options);
ast_channel_lock(c);
res = mixmonitor_exec(c, args);
+
+ if (ast_test_flag(&flags, MUXFLAG_UID)) {
+ uid_channel_var = opts[OPT_ARG_UID];
+ mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
+ }
ast_channel_unlock(c);
if (res) {
@@ -940,6 +1031,10 @@ static int manager_mixmonitor(struct mansession *s, const struct message *m)
astman_append(s, "ActionID: %s\r\n", id);
}
+ if (!ast_strlen_zero(mixmonitor_id)) {
+ astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
+ }
+
astman_append(s, "\r\n");
c = ast_channel_unref(c);
@@ -953,6 +1048,7 @@ static int manager_stop_mixmonitor(struct mansession *s, const struct message *m
const char *name = astman_get_header(m, "Channel");
const char *id = astman_get_header(m, "ActionID");
+ const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
int res;
if (ast_strlen_zero(name)) {
@@ -967,7 +1063,7 @@ static int manager_stop_mixmonitor(struct mansession *s, const struct message *m
return AMI_SUCCESS;
}
- res = stop_mixmonitor_exec(c, NULL);
+ res = stop_mixmonitor_exec(c, mixmonitor_id);
if (res) {
astman_send_error(s, m, "Could not stop monitoring channel");