From 5b7a769fd8b02dcb1d6326ebbe1d4eafcdc75491 Mon Sep 17 00:00:00 2001 From: Russell Bryant <russell@russellbryant.com> Date: Tue, 15 Apr 2014 23:21:19 +0000 Subject: [PATCH] (mix)monitor: Add options to enable a periodic beep Add an option to enable a periodic beep to be played into a call if it is being recorded. If enabled, it uses the PERIODIC_HOOK() function internally to play the 'beep' prompt into the call at a specified interval. This option is provided for both Monitor() and MixMonitor(). Review: https://reviewboard.asterisk.org/r/3424/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@412427 65c4cc65-6c06-0410-ace0-fbb531ad65f3 --- CHANGES | 4 +++ apps/app_mixmonitor.c | 47 ++++++++++++++++++++++++++++--- apps/app_queue.c | 6 ++-- bridges/bridge_builtin_features.c | 2 +- funcs/func_periodic_hook.c | 30 ++++++++++++++++++++ include/asterisk/beep.h | 45 +++++++++++++++++++++++++++++ include/asterisk/monitor.h | 4 ++- res/res_monitor.c | 47 +++++++++++++++++++++++++++---- 8 files changed, 171 insertions(+), 14 deletions(-) create mode 100644 include/asterisk/beep.h diff --git a/CHANGES b/CHANGES index 8c3a253471..b4e0d4e91b 100644 --- a/CHANGES +++ b/CHANGES @@ -16,6 +16,8 @@ Applications -------------------------- * Record application now has an option 'o' which allows 0 to act as an exit key setting the RECORD_STATUS variable to 'OPERATOR' instead of 'DTMF' + * Monitor() - A new option, B(), has been added that will turn on a periodic + beep while the call is being recorded. Functions -------------------------- @@ -85,6 +87,8 @@ MixMonitor ------------------------- * A new function, MIXMONITOR, has been added to allow access to individual instances of MixMonitor on a channel. + * A new option, B(), has been added that will turn on a periodic beep while the + call is being recorded. Channel Drivers diff --git a/apps/app_mixmonitor.c b/apps/app_mixmonitor.c index a5d383cbc2..6a2a0fcece 100644 --- a/apps/app_mixmonitor.c +++ b/apps/app_mixmonitor.c @@ -57,6 +57,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/linkedlists.h" #include "asterisk/test.h" #include "asterisk/mixmonitor.h" +#include "asterisk/beep.h" /*** DOCUMENTATION <application name="MixMonitor" language="en_US"> @@ -83,6 +84,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") channel is not optimized away. To do this, be sure to call your Local channel with the <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note> </option> + <option name="B"> + <para>Play a periodic beep while this call is being recorded.</para> + <argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument> + </option> <option name="v"> <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para> @@ -319,6 +324,7 @@ enum mixmonitor_flags { MUXFLAG_COMBINED = (1 << 8), MUXFLAG_UID = (1 << 9), MUXFLAG_VMRECIPIENTS = (1 << 10), + MUXFLAG_BEEP = (1 << 11), }; enum mixmonitor_args { @@ -329,12 +335,14 @@ enum mixmonitor_args { OPT_ARG_READNAME, OPT_ARG_UID, OPT_ARG_VMRECIPIENTS, + OPT_ARG_BEEP_INTERVAL, OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */ }; AST_APP_OPTIONS(mixmonitor_opts, { AST_APP_OPTION('a', MUXFLAG_APPEND), AST_APP_OPTION('b', MUXFLAG_BRIDGED), + AST_APP_OPTION_ARG('B', MUXFLAG_BEEP, OPT_ARG_BEEP_INTERVAL), AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME), AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME), AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME), @@ -361,6 +369,7 @@ struct mixmonitor_ds { unsigned int samp_rate; char *filename; + char *beep_id; }; /*! @@ -405,6 +414,7 @@ static void mixmonitor_ds_destroy(void *data) mixmonitor_ds->audiohook = NULL; mixmonitor_ds->destruction_ok = 1; ast_free(mixmonitor_ds->filename); + ast_free(mixmonitor_ds->beep_id); ast_cond_signal(&mixmonitor_ds->destruction_condition); ast_mutex_unlock(&mixmonitor_ds->lock); } @@ -772,7 +782,7 @@ static void *mixmonitor_thread(void *obj) return NULL; } -static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id) +static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id) { struct ast_datastore *datastore = NULL; struct mixmonitor_ds *mixmonitor_ds; @@ -799,6 +809,9 @@ static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel mixmonitor_ds->samp_rate = 8000; mixmonitor_ds->audiohook = &mixmonitor->audiohook; mixmonitor_ds->filename = ast_strdup(mixmonitor->filename); + if (!ast_strlen_zero(beep_id)) { + mixmonitor_ds->beep_id = ast_strdup(beep_id); + } datastore->data = mixmonitor_ds; ast_channel_lock(chan); @@ -813,7 +826,7 @@ static int 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, char *filename_read, const char *uid_channel_var, - const char *recipients) + const char *recipients, const char *beep_id) { pthread_t thread; struct mixmonitor *mixmonitor; @@ -872,7 +885,7 @@ static int launch_monitor_thread(struct ast_channel *chan, const char *filename, mixmonitor->filename_read = ast_strdup(filename_read); } - if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) { + if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) { ast_autochan_destroy(mixmonitor->autochan); mixmonitor_free(mixmonitor); ast_free(datastore_id); @@ -973,6 +986,7 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data) char *filename_write = NULL; char filename_buffer[1024] = ""; char *uid_channel_var = NULL; + char beep_id[64] = ""; struct ast_flags flags = { 0 }; char *recipients = NULL; @@ -1046,6 +1060,21 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data) if (ast_test_flag(&flags, MUXFLAG_UID)) { uid_channel_var = opts[OPT_ARG_UID]; } + + if (ast_test_flag(&flags, MUXFLAG_BEEP)) { + const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15"); + unsigned int interval = 15; + + if (sscanf(interval_str, "%30u", &interval) != 1) { + ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n", + interval_str, interval); + } + + if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) { + ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n"); + return -1; + } + } } /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */ @@ -1072,7 +1101,8 @@ static int mixmonitor_exec(struct ast_channel *chan, const char *data) filename_write, filename_read, uid_channel_var, - recipients)) { + recipients, + beep_id)) { ast_module_unref(ast_module_info->self); } @@ -1084,6 +1114,7 @@ static int stop_mixmonitor_full(struct ast_channel *chan, const char *data) struct ast_datastore *datastore = NULL; char *parse = ""; struct mixmonitor_ds *mixmonitor_ds; + const char *beep_id = NULL; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(mixmonid); @@ -1123,6 +1154,10 @@ static int stop_mixmonitor_full(struct ast_channel *chan, const char *data) mixmonitor_ds->audiohook = NULL; } + if (!ast_strlen_zero(mixmonitor_ds->beep_id)) { + beep_id = ast_strdupa(mixmonitor_ds->beep_id); + } + ast_mutex_unlock(&mixmonitor_ds->lock); /* Remove the datastore so the monitor thread can exit */ @@ -1131,6 +1166,10 @@ static int stop_mixmonitor_full(struct ast_channel *chan, const char *data) } ast_channel_unlock(chan); + if (!ast_strlen_zero(beep_id)) { + ast_beep_stop(chan, beep_id); + } + return 0; } diff --git a/apps/app_queue.c b/apps/app_queue.c index a2b3b762da..4d97db4059 100644 --- a/apps/app_queue.c +++ b/apps/app_queue.c @@ -6562,13 +6562,13 @@ static int try_calling(struct queue_ent *qe, struct ast_flags opts, char **opt_a } ast_channel_unlock(qe->chan); if (monitorfilename) { - ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT); + ast_monitor_start(which, qe->parent->monfmt, monitorfilename, 1, X_REC_IN | X_REC_OUT, NULL); } else if (qe->chan) { - ast_monitor_start(which, qe->parent->monfmt, ast_channel_uniqueid(qe->chan), 1, X_REC_IN | X_REC_OUT); + ast_monitor_start(which, qe->parent->monfmt, ast_channel_uniqueid(qe->chan), 1, X_REC_IN | X_REC_OUT, NULL); } else { /* Last ditch effort -- no channel, make up something */ snprintf(tmpid, sizeof(tmpid), "chan-%lx", ast_random()); - ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT); + ast_monitor_start(which, qe->parent->monfmt, tmpid, 1, X_REC_IN | X_REC_OUT, NULL); } if (!ast_strlen_zero(monexec)) { ast_monitor_setjoinfiles(which, 1); diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c index ee4a69682b..b300052570 100644 --- a/bridges/bridge_builtin_features.c +++ b/bridges/bridge_builtin_features.c @@ -196,7 +196,7 @@ static void start_automonitor(struct ast_bridge_channel *bridge_channel, struct ast_verb(3, "AutoMonitor used to record call. Filename: %s\n", touch_filename); - if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT)) { + if (ast_monitor_start(peer_chan, touch_format, touch_filename, 1, X_REC_IN | X_REC_OUT, NULL)) { ast_verb(3, "automon feature was tried by '%s' but monitor failed to start.\n", ast_channel_name(bridge_channel->chan)); return; diff --git a/funcs/func_periodic_hook.c b/funcs/func_periodic_hook.c index 661ebcbed1..39dfab3967 100644 --- a/funcs/func_periodic_hook.c +++ b/funcs/func_periodic_hook.c @@ -42,6 +42,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/pbx.h" #include "asterisk/app.h" #include "asterisk/audiohook.h" +#define AST_API_MODULE +#include "asterisk/beep.h" /*** DOCUMENTATION <function name="PERIODIC_HOOK" language="en_US"> @@ -93,6 +95,8 @@ static const char context_name[] = "__func_periodic_hook_context__"; static const char exten_name[] = "hook"; static const char full_exten_name[] = "hook@__func_periodic_hook_context__"; +static const char beep_exten[] = "beep"; + /*! * \brief Last used hook ID * @@ -485,9 +489,35 @@ static int load_module(void) ast_add_extension(context_name, 1, exten_name, 6, "", "", "ChanSpy", "${ChannelToSpy},qEB", NULL, AST_MODULE); + res = ast_add_extension(context_name, 1, beep_exten, 1, "", "", + "Answer", "", NULL, AST_MODULE); + res |= ast_add_extension(context_name, 1, beep_exten, 2, "", "", + "Playback", "beep", NULL, AST_MODULE); + res = ast_custom_function_register_escalating(&hook_function, AST_CFE_BOTH); return res ? AST_MODULE_LOAD_DECLINE : AST_MODULE_LOAD_SUCCESS; } +int AST_OPTIONAL_API_NAME(ast_beep_start)(struct ast_channel *chan, + unsigned int interval, char *beep_id, size_t len) +{ + char args[AST_MAX_EXTENSION + AST_MAX_CONTEXT + 32]; + + snprintf(args, sizeof(args), "%s,%s,%u", + context_name, beep_exten, interval); + + if (hook_read(chan, NULL, args, beep_id, len)) { + ast_log(LOG_WARNING, "Failed to enable periodic beep.\n"); + return -1; + } + + return 0; +} + +int AST_OPTIONAL_API_NAME(ast_beep_stop)(struct ast_channel *chan, const char *beep_id) +{ + return hook_write(chan, NULL, (char *) beep_id, "off"); +} + AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Periodic dialplan hooks."); diff --git a/include/asterisk/beep.h b/include/asterisk/beep.h new file mode 100644 index 0000000000..f436e9cabd --- /dev/null +++ b/include/asterisk/beep.h @@ -0,0 +1,45 @@ +/* + * Asterisk -- An open source telephony toolkit. + * + * Copyright (C) 2014, Russell Bryant + * + * Russell Bryant <russell@russellbryant.net> + * + * See http://www.asterisk.org for more information about + * the Asterisk project. Please do not directly contact + * any of the maintainers of this project for assistance; + * the project provides a web site, mailing lists and IRC + * channels for your use. + * + * This program is free software, distributed under the terms of + * the GNU General Public License Version 2. See the LICENSE file + * at the top of the source tree. + */ + +/*! + * \file + * \brief Periodic beeps into the audio of a call + */ + +#ifndef _ASTERISK_BEEP_H +#define _ASTERISK_BEEP_H + +#include "asterisk/optional_api.h" + +#if defined(__cplusplus) || defined(c_plusplus) +extern "C" { +#endif + +AST_OPTIONAL_API(int, ast_beep_start, + (struct ast_channel *chan, unsigned int interval, char *beep_id, size_t len), + { return -1; }); + +AST_OPTIONAL_API(int, ast_beep_stop, + (struct ast_channel *chan, const char *beep_id), + { return -1; }); + +#if defined(__cplusplus) || defined(c_plusplus) +} +#endif + +#endif /* _ASTERISK_BEEP_H */ diff --git a/include/asterisk/monitor.h b/include/asterisk/monitor.h index e29622ea8d..6030221a2f 100644 --- a/include/asterisk/monitor.h +++ b/include/asterisk/monitor.h @@ -43,6 +43,7 @@ struct ast_channel_monitor { char read_filename[FILENAME_MAX]; char write_filename[FILENAME_MAX]; char filename_base[FILENAME_MAX]; + char beep_id[64]; int filename_changed; char *format; int joinfiles; @@ -53,7 +54,8 @@ struct ast_channel_monitor { /* Start monitoring a channel */ AST_OPTIONAL_API(int, ast_monitor_start, (struct ast_channel *chan, const char *format_spec, - const char *fname_base, int need_lock, int stream_action), + const char *fname_base, int need_lock, int stream_action, + const char *beep_id), { return -1; }); /* Stop monitoring a channel */ diff --git a/res/res_monitor.c b/res/res_monitor.c index 2eafcb2385..f815072850 100644 --- a/res/res_monitor.c +++ b/res/res_monitor.c @@ -50,6 +50,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") #include "asterisk/utils.h" #include "asterisk/config.h" #include "asterisk/options.h" +#include "asterisk/beep.h" /*** DOCUMENTATION <application name="Monitor" language="en_US"> @@ -84,6 +85,10 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$") <option name="b"> <para>Don't begin recording unless a call is bridged to another channel.</para> </option> + <option name="B"> + <para>Play a periodic beep while this call is being recorded.</para> + <argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument> + </option> <option name="i"> <para>Skip recording of input stream (disables <literal>m</literal> option).</para> </option> @@ -290,7 +295,8 @@ static int ast_monitor_set_state(struct ast_channel *chan, int state) * \retval -1 on failure */ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const char *format_spec, - const char *fname_base, int need_lock, int stream_action) + const char *fname_base, int need_lock, int stream_action, + const char *beep_id) { int res = 0; RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup); @@ -309,6 +315,10 @@ int AST_OPTIONAL_API_NAME(ast_monitor_start)(struct ast_channel *chan, const cha return -1; } + if (!ast_strlen_zero(beep_id)) { + ast_copy_string(monitor->beep_id, beep_id, sizeof(monitor->beep_id)); + } + /* Determine file names */ if (!ast_strlen_zero(fname_base)) { int directory = strchr(fname_base, '/') ? 1 : 0; @@ -511,7 +521,11 @@ int AST_OPTIONAL_API_NAME(ast_monitor_stop)(struct ast_channel *chan, int need_l if (ast_safe_system(tmp) == -1) ast_log(LOG_WARNING, "Execute of %s failed.\n",tmp); } - + + if (!ast_strlen_zero(ast_channel_monitor(chan)->beep_id)) { + ast_beep_stop(chan, ast_channel_monitor(chan)->beep_id); + } + ast_free(ast_channel_monitor(chan)->format); ast_free(ast_channel_monitor(chan)); ast_channel_monitor_set(chan, NULL); @@ -644,6 +658,12 @@ enum { MON_FLAG_MIX = (1 << 1), MON_FLAG_DROP_IN = (1 << 2), MON_FLAG_DROP_OUT = (1 << 3), + MON_FLAG_BEEP = (1 << 4), +}; + +enum { + OPT_ARG_BEEP_INTERVAL, + OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */ }; AST_APP_OPTIONS(monitor_opts, { @@ -651,6 +671,7 @@ AST_APP_OPTIONS(monitor_opts, { AST_APP_OPTION('m', MON_FLAG_MIX), AST_APP_OPTION('i', MON_FLAG_DROP_IN), AST_APP_OPTION('o', MON_FLAG_DROP_OUT), + AST_APP_OPTION_ARG('B', MON_FLAG_BEEP, OPT_ARG_BEEP_INTERVAL), }); /*! @@ -672,6 +693,8 @@ static int start_monitor_exec(struct ast_channel *chan, const char *data) int res = 0; char *parse; struct ast_flags flags = { 0 }; + char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; + char beep_id[64] = ""; AST_DECLARE_APP_ARGS(args, AST_APP_ARG(format); AST_APP_ARG(fname_base); @@ -688,7 +711,7 @@ static int start_monitor_exec(struct ast_channel *chan, const char *data) AST_STANDARD_APP_ARGS(args, parse); if (!ast_strlen_zero(args.options)) { - ast_app_parse_options(monitor_opts, &flags, NULL, args.options); + ast_app_parse_options(monitor_opts, &flags, opts, args.options); if (ast_test_flag(&flags, MON_FLAG_MIX)) { stream_action |= X_JOIN; @@ -699,6 +722,20 @@ static int start_monitor_exec(struct ast_channel *chan, const char *data) if (ast_test_flag(&flags, MON_FLAG_DROP_OUT)) { stream_action &= ~X_REC_OUT; } + if (ast_test_flag(&flags, MON_FLAG_BEEP)) { + const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15"); + unsigned int interval = 15; + + if (sscanf(interval_str, "%30u", &interval) != 1) { + ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n", + interval_str, interval); + } + + if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) { + ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n"); + return -1; + } + } } arg = strchr(args.format, ':'); @@ -731,7 +768,7 @@ static int start_monitor_exec(struct ast_channel *chan, const char *data) return 0; } - res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action); + res = ast_monitor_start(chan, args.format, args.fname_base, 1, stream_action, beep_id); if (res < 0) res = ast_monitor_change_fname(chan, args.fname_base, 1); @@ -790,7 +827,7 @@ static int start_monitor_action(struct mansession *s, const struct message *m) } } - if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT)) { + if (ast_monitor_start(c, format, fname, 1, X_REC_IN | X_REC_OUT, NULL)) { if (ast_monitor_change_fname(c, fname, 1)) { astman_send_error(s, m, "Could not start monitoring channel"); c = ast_channel_unref(c); -- GitLab