2 * Asterisk -- An open source telephony toolkit.
4 * Copyright (C) 2005, Anthony Minessale II
5 * Copyright (C) 2005 - 2006, Digium, Inc.
7 * Mark Spencer <markster@digium.com>
8 * Kevin P. Fleming <kpfleming@digium.com>
10 * Based on app_muxmon.c provided by
11 * Anthony Minessale II <anthmct@yahoo.com>
13 * See http://www.asterisk.org for more information about
14 * the Asterisk project. Please do not directly contact
15 * any of the maintainers of this project for assistance;
16 * the project provides a web site, mailing lists and IRC
17 * channels for your use.
19 * This program is free software, distributed under the terms of
20 * the GNU General Public License Version 2. See the LICENSE file
21 * at the top of the source tree.
26 * \brief MixMonitor() - Record a call and mix the audio during the recording
27 * \ingroup applications
29 * \author Mark Spencer <markster@digium.com>
30 * \author Kevin P. Fleming <kpfleming@digium.com>
32 * \note Based on app_muxmon.c provided by
33 * Anthony Minessale II <anthmct@yahoo.com>
37 <support_level>core</support_level>
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
44 #include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
45 #include "asterisk/stringfields.h"
46 #include "asterisk/file.h"
47 #include "asterisk/audiohook.h"
48 #include "asterisk/pbx.h"
49 #include "asterisk/module.h"
50 #include "asterisk/cli.h"
51 #include "asterisk/app.h"
52 #include "asterisk/channel.h"
53 #include "asterisk/autochan.h"
54 #include "asterisk/manager.h"
55 #include "asterisk/callerid.h"
56 #include "asterisk/mod_format.h"
57 #include "asterisk/linkedlists.h"
60 <application name="MixMonitor" language="en_US">
62 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
63 to guarantee the audio file is available for processing during dialplan execution.
66 <parameter name="file" required="true" argsep=".">
67 <argument name="filename" required="true">
68 <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
69 creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
71 <argument name="extension" required="true" />
73 <parameter name="options">
76 <para>Append to the file instead of overwriting it.</para>
79 <para>Only save audio to the file while the channel is bridged.</para>
80 <note><para>Does not include conferences or sounds played to each bridged party</para></note>
81 <note><para>If you utilize this option inside a Local channel, you must make sure the Local
82 channel is not optimized away. To do this, be sure to call your Local channel with the
83 <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
86 <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
87 (range <literal>-4</literal> to <literal>4</literal>)</para>
88 <argument name="x" required="true" />
91 <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
92 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
93 <argument name="x" required="true" />
96 <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
97 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
98 <argument name="x" required="true" />
101 <argument name="file" required="true" />
102 <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
103 Like with the basic filename argument, if an absolute path isn't given, it will create
104 the file in the configured monitoring directory.</para>
108 <argument name="file" required="true" />
109 <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
110 Like with the basic filename argument, if an absolute path isn't given, it will create
111 the file in the configured monitoring directory.</para>
114 <argument name="chanvar" required="true" />
115 <para>Stores the MixMonitor's ID on this channel variable.</para>
118 <argument name="mailbox" required="true" />
119 <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
120 separated by commas eg. m(1111@default,2222@default,...). Folders can be optionally specified using
121 the syntax: mailbox@context/folder</para>
125 <parameter name="command">
126 <para>Will be executed when the recording is over.</para>
127 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
128 <para>All variables will be evaluated at the time MixMonitor is called.</para>
132 <para>Records the audio on the current channel to the specified file.</para>
133 <para>This application does not automatically answer and should be preceeded by
134 an application such as Answer or Progress().</para>
136 <variable name="MIXMONITOR_FILENAME">
137 <para>Will contain the filename used to record.</para>
142 <ref type="application">Monitor</ref>
143 <ref type="application">StopMixMonitor</ref>
144 <ref type="application">PauseMonitor</ref>
145 <ref type="application">UnpauseMonitor</ref>
148 <application name="StopMixMonitor" language="en_US">
150 Stop recording a call through MixMonitor, and free the recording's file handle.
153 <parameter name="MixMonitorID" required="false">
154 <para>If a valid ID is provided, then this command will stop only that specific
159 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
160 on the current channel.</para>
163 <ref type="application">MixMonitor</ref>
166 <manager name="MixMonitorMute" language="en_US">
168 Mute / unMute a Mixmonitor recording.
171 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
172 <parameter name="Channel" required="true">
173 <para>Used to specify the channel to mute.</para>
175 <parameter name="Direction">
176 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
178 <parameter name="State">
179 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
183 <para>This action may be used to mute a MixMonitor recording.</para>
186 <manager name="MixMonitor" language="en_US">
188 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
189 to guarantee the audio file is available for processing during dialplan execution.
192 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
193 <parameter name="Channel" required="true">
194 <para>Used to specify the channel to record.</para>
196 <parameter name="File">
197 <para>Is the name of the file created in the monitor spool directory.
198 Defaults to the same name as the channel (with slashes replaced with dashes).
199 This argument is optional if you specify to record unidirectional audio with
200 either the r(filename) or t(filename) options in the options field. If
201 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
204 <parameter name="options">
205 <para>Options that apply to the MixMonitor in the same way as they
206 would apply if invoked from the MixMonitor application. For a list of
207 available options, see the documentation for the mixmonitor application. </para>
211 <para>This action records the audio on the current channel to the specified file.</para>
213 <variable name="MIXMONITOR_FILENAME">
214 <para>Will contain the filename used to record the mixed stream.</para>
219 <manager name="StopMixMonitor" language="en_US">
221 Stop recording a call through MixMonitor, and free the recording's file handle.
224 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
225 <parameter name="Channel" required="true">
226 <para>The name of the channel monitored.</para>
228 <parameter name="MixMonitorID" required="false">
229 <para>If a valid ID is provided, then this command will stop only that specific
234 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
235 action on the current channel.</para>
241 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
243 static const char * const app = "MixMonitor";
245 static const char * const stop_app = "StopMixMonitor";
247 static const char * const mixmonitor_spy_type = "MixMonitor";
251 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
253 struct vm_recipient {
254 char mailbox[AST_MAX_CONTEXT];
255 char context[AST_MAX_EXTENSION];
257 AST_LIST_ENTRY(vm_recipient) list;
261 struct ast_audiohook audiohook;
262 struct ast_callid *callid;
265 char *filename_write;
269 struct ast_autochan *autochan;
270 struct mixmonitor_ds *mixmonitor_ds;
272 /* the below string fields describe data used for creating voicemails from the recording */
273 AST_DECLARE_STRING_FIELDS(
274 AST_STRING_FIELD(call_context);
275 AST_STRING_FIELD(call_macrocontext);
276 AST_STRING_FIELD(call_extension);
277 AST_STRING_FIELD(call_callerchan);
278 AST_STRING_FIELD(call_callerid);
282 /* FUTURE DEVELOPMENT NOTICE
283 * recipient_list will need locks if we make it editable after the monitor is started */
284 AST_LIST_HEAD_NOLOCK(, vm_recipient) recipient_list;
287 enum mixmonitor_flags {
288 MUXFLAG_APPEND = (1 << 1),
289 MUXFLAG_BRIDGED = (1 << 2),
290 MUXFLAG_VOLUME = (1 << 3),
291 MUXFLAG_READVOLUME = (1 << 4),
292 MUXFLAG_WRITEVOLUME = (1 << 5),
293 MUXFLAG_READ = (1 << 6),
294 MUXFLAG_WRITE = (1 << 7),
295 MUXFLAG_COMBINED = (1 << 8),
296 MUXFLAG_UID = (1 << 9),
297 MUXFLAG_VMRECIPIENTS = (1 << 10),
300 enum mixmonitor_args {
301 OPT_ARG_READVOLUME = 0,
307 OPT_ARG_VMRECIPIENTS,
308 OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
311 AST_APP_OPTIONS(mixmonitor_opts, {
312 AST_APP_OPTION('a', MUXFLAG_APPEND),
313 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
314 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
315 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
316 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
317 AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
318 AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
319 AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
320 AST_APP_OPTION_ARG('m', MUXFLAG_VMRECIPIENTS, OPT_ARG_VMRECIPIENTS),
323 struct mixmonitor_ds {
324 unsigned int destruction_ok;
325 ast_cond_t destruction_condition;
328 /* The filestream is held in the datastore so it can be stopped
329 * immediately during stop_mixmonitor or channel destruction. */
332 struct ast_filestream *fs;
333 struct ast_filestream *fs_read;
334 struct ast_filestream *fs_write;
336 struct ast_audiohook *audiohook;
338 unsigned int samp_rate;
343 * \pre mixmonitor_ds must be locked before calling this function
345 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
347 unsigned char quitting = 0;
349 if (mixmonitor_ds->fs) {
351 ast_closestream(mixmonitor_ds->fs);
352 mixmonitor_ds->fs = NULL;
353 ast_verb(2, "MixMonitor close filestream (mixed)\n");
356 if (mixmonitor_ds->fs_read) {
358 ast_closestream(mixmonitor_ds->fs_read);
359 mixmonitor_ds->fs_read = NULL;
360 ast_verb(2, "MixMonitor close filestream (read)\n");
363 if (mixmonitor_ds->fs_write) {
365 ast_closestream(mixmonitor_ds->fs_write);
366 mixmonitor_ds->fs_write = NULL;
367 ast_verb(2, "MixMonitor close filestream (write)\n");
371 mixmonitor_ds->fs_quit = 1;
375 static void mixmonitor_ds_destroy(void *data)
377 struct mixmonitor_ds *mixmonitor_ds = data;
379 ast_mutex_lock(&mixmonitor_ds->lock);
380 mixmonitor_ds->audiohook = NULL;
381 mixmonitor_ds->destruction_ok = 1;
382 ast_cond_signal(&mixmonitor_ds->destruction_condition);
383 ast_mutex_unlock(&mixmonitor_ds->lock);
386 static struct ast_datastore_info mixmonitor_ds_info = {
387 .type = "mixmonitor",
388 .destroy = mixmonitor_ds_destroy,
391 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
393 if (mixmonitor->mixmonitor_ds) {
394 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
395 mixmonitor->mixmonitor_ds->audiohook = NULL;
396 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
398 /* kill the audiohook.*/
399 ast_audiohook_lock(&mixmonitor->audiohook);
400 ast_audiohook_detach(&mixmonitor->audiohook);
401 ast_audiohook_unlock(&mixmonitor->audiohook);
402 ast_audiohook_destroy(&mixmonitor->audiohook);
405 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
407 struct ast_channel *peer = NULL;
413 ast_audiohook_attach(chan, audiohook);
415 if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
416 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
423 * \brief adds recipients to a mixmonitor's recipient list
424 * \param mixmonitor mixmonitor being affected
425 * \param vm_recipients string containing the desired recipients to add
427 static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
429 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
430 char *cur_mailbox = ast_strdupa(vm_recipients);
434 int elements_processed = 0;
436 while (!ast_strlen_zero(cur_mailbox)) {
437 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
438 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
442 if ((cur_folder = strchr(cur_mailbox, '/'))) {
443 *(cur_folder++) = '\0';
445 cur_folder = "INBOX";
448 if ((cur_context = strchr(cur_mailbox, '@'))) {
449 *(cur_context++) = '\0';
451 cur_context = "default";
454 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
455 struct vm_recipient *recipient;
456 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
457 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
460 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
461 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
462 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
465 ast_verb(5, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
466 AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
468 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
472 elements_processed++;
476 static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
478 struct vm_recipient *current;
479 while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
480 /* Clear list element data */
485 #define SAMPLES_PER_FRAME 160
487 static void mixmonitor_free(struct mixmonitor *mixmonitor)
490 if (mixmonitor->mixmonitor_ds) {
491 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
492 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
493 ast_free(mixmonitor->filename_write);
494 ast_free(mixmonitor->filename_read);
495 ast_free(mixmonitor->mixmonitor_ds);
496 ast_free(mixmonitor->name);
497 ast_free(mixmonitor->post_process);
500 /* Free everything in the recipient list */
501 clear_mixmonitor_recipient_list(mixmonitor);
503 /* clean stringfields */
504 ast_string_field_free_memory(mixmonitor);
506 if (mixmonitor->callid) {
507 ast_callid_unref(mixmonitor->callid);
509 ast_free(mixmonitor);
515 * \brief Copies the mixmonitor to all voicemail recipients
516 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
517 * \param ext Format of the file that was saved
519 static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
521 struct vm_recipient *recipient = NULL;
522 struct ast_vm_recording_data recording_data;
523 if (ast_string_field_init(&recording_data, 512)) {
524 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
528 /* Copy strings to stringfields that will be used for all recipients */
529 ast_string_field_set(&recording_data, recording_file, filename);
530 ast_string_field_set(&recording_data, recording_ext, ext);
531 ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
532 ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
533 ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
534 ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
535 ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
536 /* and call_priority gets copied too */
537 recording_data.call_priority = mixmonitor->call_priority;
539 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
540 /* context, mailbox, and folder need to be set per recipient */
541 ast_string_field_set(&recording_data, context, recipient->context);
542 ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
543 ast_string_field_set(&recording_data, folder, recipient->folder);
545 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
546 recording_data.context);
547 ast_app_copy_recording_to_vm(&recording_data);
550 /* Free the string fields for recording_data before exiting the function. */
551 ast_string_field_free_memory(&recording_data);
554 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
556 /* Initialize the file if not already done so */
557 char *last_slash = NULL;
558 if (!ast_strlen_zero(filename)) {
559 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
560 *oflags = O_CREAT | O_WRONLY;
561 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
563 last_slash = strrchr(filename, '/');
565 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
572 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
573 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
576 struct ast_filestream *tmp = *fs;
577 mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
583 static void *mixmonitor_thread(void *obj)
585 struct mixmonitor *mixmonitor = obj;
587 char *fs_read_ext = "";
588 char *fs_write_ext = "";
590 struct ast_filestream **fs = NULL;
591 struct ast_filestream **fs_read = NULL;
592 struct ast_filestream **fs_write = NULL;
596 struct ast_format format_slin;
598 /* Keep callid association before any log messages */
599 if (mixmonitor->callid) {
600 ast_callid_threadassoc_add(mixmonitor->callid);
603 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
605 fs = &mixmonitor->mixmonitor_ds->fs;
606 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
607 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
609 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
610 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
611 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
612 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
614 ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
616 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
619 /* The audiohook must enter and exit the loop locked */
620 ast_audiohook_lock(&mixmonitor->audiohook);
621 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
622 struct ast_frame *fr = NULL;
623 struct ast_frame *fr_read = NULL;
624 struct ast_frame *fr_write = NULL;
626 if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
627 &fr_read, &fr_write))) {
628 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
630 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
636 /* audiohook lock is not required for the next block.
637 * Unlock it, but remember to lock it before looping or exiting */
638 ast_audiohook_unlock(&mixmonitor->audiohook);
640 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
641 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
643 /* Write out the frame(s) */
644 if ((*fs_read) && (fr_read)) {
645 struct ast_frame *cur;
647 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
648 ast_writestream(*fs_read, cur);
652 if ((*fs_write) && (fr_write)) {
653 struct ast_frame *cur;
655 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
656 ast_writestream(*fs_write, cur);
661 struct ast_frame *cur;
663 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
664 ast_writestream(*fs, cur);
667 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
669 /* All done! free it. */
671 ast_frame_free(fr, 0);
674 ast_frame_free(fr_read, 0);
677 ast_frame_free(fr_write, 0);
684 ast_audiohook_lock(&mixmonitor->audiohook);
686 ast_audiohook_unlock(&mixmonitor->audiohook);
688 ast_autochan_destroy(mixmonitor->autochan);
690 /* Datastore cleanup. close the filestream and wait for ds destruction */
691 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
692 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
693 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
694 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
696 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
698 /* kill the audiohook */
699 destroy_monitor_audiohook(mixmonitor);
701 if (mixmonitor->post_process) {
702 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
703 ast_safe_system(mixmonitor->post_process);
706 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
708 if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
709 if (ast_strlen_zero(fs_ext)) {
710 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
713 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
714 copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
716 if (!ast_strlen_zero(fs_read_ext)) {
717 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
718 copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
720 if (!ast_strlen_zero(fs_write_ext)) {
721 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
722 copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
725 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
728 mixmonitor_free(mixmonitor);
732 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
734 struct ast_datastore *datastore = NULL;
735 struct mixmonitor_ds *mixmonitor_ds;
737 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
741 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
742 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
745 ast_mutex_init(&mixmonitor_ds->lock);
746 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
748 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
749 ast_mutex_destroy(&mixmonitor_ds->lock);
750 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
751 ast_free(mixmonitor_ds);
756 mixmonitor_ds->samp_rate = 8000;
757 mixmonitor_ds->audiohook = &mixmonitor->audiohook;
758 datastore->data = mixmonitor_ds;
760 ast_channel_lock(chan);
761 ast_channel_datastore_add(chan, datastore);
762 ast_channel_unlock(chan);
764 mixmonitor->mixmonitor_ds = mixmonitor_ds;
768 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
769 unsigned int flags, int readvol, int writevol,
770 const char *post_process, const char *filename_write,
771 char *filename_read, const char *uid_channel_var,
772 const char *recipients)
775 struct mixmonitor *mixmonitor;
776 char postprocess2[1024] = "";
777 char *datastore_id = NULL;
780 /* If a post process system command is given attach it to the structure */
781 if (!ast_strlen_zero(post_process)) {
784 p1 = ast_strdupa(post_process);
785 for (p2 = p1; *p2; p2++) {
786 if (*p2 == '^' && *(p2+1) == '{') {
790 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
793 /* Pre-allocate mixmonitor structure and spy */
794 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
798 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
799 if (ast_string_field_init(mixmonitor, 512)) {
800 mixmonitor_free(mixmonitor);
804 /* Setup the actual spy before creating our thread */
805 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
806 mixmonitor_free(mixmonitor);
810 /* Copy over flags and channel name */
811 mixmonitor->flags = flags;
812 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
813 mixmonitor_free(mixmonitor);
817 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
818 ast_autochan_destroy(mixmonitor->autochan);
819 mixmonitor_free(mixmonitor);
820 ast_free(datastore_id);
824 if (!ast_strlen_zero(uid_channel_var)) {
826 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
829 ast_free(datastore_id);
831 mixmonitor->name = ast_strdup(ast_channel_name(chan));
833 if (!ast_strlen_zero(postprocess2)) {
834 mixmonitor->post_process = ast_strdup(postprocess2);
837 if (!ast_strlen_zero(filename)) {
838 mixmonitor->filename = ast_strdup(filename);
841 if (!ast_strlen_zero(filename_write)) {
842 mixmonitor->filename_write = ast_strdup(filename_write);
845 if (!ast_strlen_zero(filename_read)) {
846 mixmonitor->filename_read = ast_strdup(filename_read);
849 if (!ast_strlen_zero(recipients)) {
851 struct ast_party_connected_line *connected;
853 ast_channel_lock(chan);
855 /* We use the connected line of the invoking channel for caller ID. */
857 connected = ast_channel_connected(chan);
858 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
859 connected->id.name.str, connected->id.number.valid,
860 connected->id.number.str);
861 ast_callerid_merge(callerid, sizeof(callerid),
862 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
863 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
866 ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
867 ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
868 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
869 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
870 ast_string_field_set(mixmonitor, call_callerid, callerid);
871 mixmonitor->call_priority = ast_channel_priority(chan);
873 ast_channel_unlock(chan);
875 add_vm_recipients_from_string(mixmonitor, recipients);
878 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
881 mixmonitor->audiohook.options.read_volume = readvol;
883 mixmonitor->audiohook.options.write_volume = writevol;
885 if (startmon(chan, &mixmonitor->audiohook)) {
886 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
887 mixmonitor_spy_type, ast_channel_name(chan));
888 ast_audiohook_destroy(&mixmonitor->audiohook);
889 mixmonitor_free(mixmonitor);
893 /* reference be released at mixmonitor destruction */
894 mixmonitor->callid = ast_read_threadstorage_callid();
896 ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
899 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
900 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
901 static char *filename_parse(char *filename, char *buffer, size_t len)
904 if (ast_strlen_zero(filename)) {
905 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
906 } else if (filename[0] != '/') {
908 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
909 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
913 ast_copy_string(buffer, filename, len);
915 if ((slash = strrchr(filename, '/'))) {
918 ast_mkdir(filename, 0777);
923 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
925 int x, readvol = 0, writevol = 0;
926 char *filename_read = NULL;
927 char *filename_write = NULL;
928 char filename_buffer[1024] = "";
929 char *uid_channel_var = NULL;
931 struct ast_flags flags = { 0 };
932 char *recipients = NULL;
934 AST_DECLARE_APP_ARGS(args,
935 AST_APP_ARG(filename);
936 AST_APP_ARG(options);
937 AST_APP_ARG(post_process);
940 if (ast_strlen_zero(data)) {
941 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
945 parse = ast_strdupa(data);
947 AST_STANDARD_APP_ARGS(args, parse);
950 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
952 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
954 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
955 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
956 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
957 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
958 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
960 readvol = get_volfactor(x);
964 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
965 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
966 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
967 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
968 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
970 writevol = get_volfactor(x);
974 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
975 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
976 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
977 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
978 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
980 readvol = writevol = get_volfactor(x);
984 if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
985 if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
986 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
988 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
992 if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
993 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
996 if (ast_test_flag(&flags, MUXFLAG_READ)) {
997 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1000 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1001 uid_channel_var = opts[OPT_ARG_UID];
1004 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1006 if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
1007 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1011 /* If filename exists, try to create directories for it */
1012 if (!(ast_strlen_zero(args.filename))) {
1013 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1016 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1017 launch_monitor_thread(chan,
1031 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1033 struct ast_datastore *datastore = NULL;
1035 struct mixmonitor_ds *mixmonitor_ds;
1037 AST_DECLARE_APP_ARGS(args,
1038 AST_APP_ARG(mixmonid);
1041 if (!ast_strlen_zero(data)) {
1042 parse = ast_strdupa(data);
1045 AST_STANDARD_APP_ARGS(args, parse);
1047 ast_channel_lock(chan);
1049 if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
1050 ast_channel_unlock(chan);
1053 mixmonitor_ds = datastore->data;
1055 ast_mutex_lock(&mixmonitor_ds->lock);
1057 /* closing the filestream here guarantees the file is avaliable to the dialplan
1058 * after calling StopMixMonitor */
1059 mixmonitor_ds_close_fs(mixmonitor_ds);
1061 /* The mixmonitor thread may be waiting on the audiohook trigger.
1062 * In order to exit from the mixmonitor loop before waiting on channel
1063 * destruction, poke the audiohook trigger. */
1064 if (mixmonitor_ds->audiohook) {
1065 if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
1066 ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
1068 ast_audiohook_lock(mixmonitor_ds->audiohook);
1069 ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
1070 ast_audiohook_unlock(mixmonitor_ds->audiohook);
1071 mixmonitor_ds->audiohook = NULL;
1074 ast_mutex_unlock(&mixmonitor_ds->lock);
1076 /* Remove the datastore so the monitor thread can exit */
1077 if (!ast_channel_datastore_remove(chan, datastore)) {
1078 ast_datastore_free(datastore);
1080 ast_channel_unlock(chan);
1085 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1087 struct ast_channel *chan;
1088 struct ast_datastore *datastore = NULL;
1089 struct mixmonitor_ds *mixmonitor_ds = NULL;
1093 e->command = "mixmonitor {start|stop|list}";
1095 "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
1096 " The optional arguments are passed to the MixMonitor\n"
1097 " application when the 'start' command is used.\n";
1100 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1104 return CLI_SHOWUSAGE;
1107 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1108 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1109 /* Technically this is a failure, but we don't want 2 errors printing out */
1113 ast_channel_lock(chan);
1115 if (!strcasecmp(a->argv[1], "start")) {
1116 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1117 ast_channel_unlock(chan);
1118 } else if (!strcasecmp(a->argv[1], "stop")){
1119 ast_channel_unlock(chan);
1120 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1121 } else if (!strcasecmp(a->argv[1], "list")) {
1122 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1123 ast_cli(a->fd, "=========================================================================\n");
1124 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1125 if (datastore->info == &mixmonitor_ds_info) {
1126 char *filename = "";
1127 char *filename_read = "";
1128 char *filename_write = "";
1129 mixmonitor_ds = datastore->data;
1130 if (mixmonitor_ds->fs)
1131 filename = ast_strdupa(mixmonitor_ds->fs->filename);
1132 if (mixmonitor_ds->fs_read)
1133 filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
1134 if (mixmonitor_ds->fs_write)
1135 filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
1136 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1139 ast_channel_unlock(chan);
1141 ast_channel_unlock(chan);
1142 chan = ast_channel_unref(chan);
1143 return CLI_SHOWUSAGE;
1146 chan = ast_channel_unref(chan);
1151 /*! \brief Mute / unmute a MixMonitor channel */
1152 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1154 struct ast_channel *c = NULL;
1156 const char *name = astman_get_header(m, "Channel");
1157 const char *id = astman_get_header(m, "ActionID");
1158 const char *state = astman_get_header(m, "State");
1159 const char *direction = astman_get_header(m,"Direction");
1163 enum ast_audiohook_flags flag;
1165 if (ast_strlen_zero(direction)) {
1166 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1170 if (!strcasecmp(direction, "read")) {
1171 flag = AST_AUDIOHOOK_MUTE_READ;
1172 } else if (!strcasecmp(direction, "write")) {
1173 flag = AST_AUDIOHOOK_MUTE_WRITE;
1174 } else if (!strcasecmp(direction, "both")) {
1175 flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
1177 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1181 if (ast_strlen_zero(name)) {
1182 astman_send_error(s, m, "No channel specified");
1186 if (ast_strlen_zero(state)) {
1187 astman_send_error(s, m, "No state specified");
1191 clearmute = ast_false(state);
1192 c = ast_channel_get_by_name(name);
1195 astman_send_error(s, m, "No such channel");
1199 if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
1200 c = ast_channel_unref(c);
1201 astman_send_error(s, m, "Cannot set mute flag");
1205 astman_append(s, "Response: Success\r\n");
1207 if (!ast_strlen_zero(id)) {
1208 astman_append(s, "ActionID: %s\r\n", id);
1211 astman_append(s, "\r\n");
1213 c = ast_channel_unref(c);
1218 static int manager_mixmonitor(struct mansession *s, const struct message *m)
1220 struct ast_channel *c = NULL;
1222 const char *name = astman_get_header(m, "Channel");
1223 const char *id = astman_get_header(m, "ActionID");
1224 const char *file = astman_get_header(m, "File");
1225 const char *options = astman_get_header(m, "Options");
1226 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1227 struct ast_flags flags = { 0 };
1228 char *uid_channel_var = NULL;
1229 const char *mixmonitor_id = NULL;
1232 char args[PATH_MAX] = "";
1233 if (ast_strlen_zero(name)) {
1234 astman_send_error(s, m, "No channel specified");
1238 c = ast_channel_get_by_name(name);
1241 astman_send_error(s, m, "No such channel");
1245 if (!ast_strlen_zero(options)) {
1246 ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
1249 snprintf(args, sizeof(args), "%s,%s", file, options);
1251 ast_channel_lock(c);
1252 res = mixmonitor_exec(c, args);
1254 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1255 uid_channel_var = opts[OPT_ARG_UID];
1256 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1258 ast_channel_unlock(c);
1261 c = ast_channel_unref(c);
1262 astman_send_error(s, m, "Could not start monitoring channel");
1266 astman_append(s, "Response: Success\r\n");
1268 if (!ast_strlen_zero(id)) {
1269 astman_append(s, "ActionID: %s\r\n", id);
1272 if (!ast_strlen_zero(mixmonitor_id)) {
1273 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1276 astman_append(s, "\r\n");
1278 c = ast_channel_unref(c);
1283 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1285 struct ast_channel *c = NULL;
1287 const char *name = astman_get_header(m, "Channel");
1288 const char *id = astman_get_header(m, "ActionID");
1289 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1292 if (ast_strlen_zero(name)) {
1293 astman_send_error(s, m, "No channel specified");
1297 c = ast_channel_get_by_name(name);
1300 astman_send_error(s, m, "No such channel");
1304 res = stop_mixmonitor_exec(c, mixmonitor_id);
1307 astman_send_error(s, m, "Could not stop monitoring channel");
1311 astman_append(s, "Response: Success\r\n");
1313 if (!ast_strlen_zero(id)) {
1314 astman_append(s, "ActionID: %s\r\n", id);
1317 astman_append(s, "\r\n");
1319 c = ast_channel_unref(c);
1324 static struct ast_cli_entry cli_mixmonitor[] = {
1325 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1328 static int unload_module(void)
1332 ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1333 res = ast_unregister_application(stop_app);
1334 res |= ast_unregister_application(app);
1335 res |= ast_manager_unregister("MixMonitorMute");
1336 res |= ast_manager_unregister("MixMonitor");
1337 res |= ast_manager_unregister("StopMixMonitor");
1342 static int load_module(void)
1346 ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1347 res = ast_register_application_xml(app, mixmonitor_exec);
1348 res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
1349 res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
1350 res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
1351 res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
1356 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");