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"
58 #include "asterisk/test.h"
61 <application name="MixMonitor" language="en_US">
63 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
64 to guarantee the audio file is available for processing during dialplan execution.
67 <parameter name="file" required="true" argsep=".">
68 <argument name="filename" required="true">
69 <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
70 creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
72 <argument name="extension" required="true" />
74 <parameter name="options">
77 <para>Append to the file instead of overwriting it.</para>
80 <para>Only save audio to the file while the channel is bridged.</para>
81 <note><para>Does not include conferences or sounds played to each bridged party</para></note>
82 <note><para>If you utilize this option inside a Local channel, you must make sure the Local
83 channel is not optimized away. To do this, be sure to call your Local channel with the
84 <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
87 <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
88 (range <literal>-4</literal> to <literal>4</literal>)</para>
89 <argument name="x" required="true" />
92 <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
93 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
94 <argument name="x" required="true" />
97 <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
98 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
99 <argument name="x" required="true" />
102 <argument name="file" required="true" />
103 <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
104 Like with the basic filename argument, if an absolute path isn't given, it will create
105 the file in the configured monitoring directory.</para>
109 <argument name="file" required="true" />
110 <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
111 Like with the basic filename argument, if an absolute path isn't given, it will create
112 the file in the configured monitoring directory.</para>
115 <argument name="chanvar" required="true" />
116 <para>Stores the MixMonitor's ID on this channel variable.</para>
119 <argument name="mailbox" required="true" />
120 <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
121 separated by commas eg. m(1111@default,2222@default,...). Folders can be optionally specified using
122 the syntax: mailbox@context/folder</para>
126 <parameter name="command">
127 <para>Will be executed when the recording is over.</para>
128 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
129 <para>All variables will be evaluated at the time MixMonitor is called.</para>
133 <para>Records the audio on the current channel to the specified file.</para>
134 <para>This application does not automatically answer and should be preceeded by
135 an application such as Answer or Progress().</para>
136 <note><para>MixMonitor runs as an audiohook. In order to keep it running through
137 a transfer, AUDIOHOOK_INHERIT must be set for the channel which ran mixmonitor.
138 For more information, including dialplan configuration set for using
139 AUDIOHOOK_INHERIT with MixMonitor, see the function documentation for
140 AUDIOHOOK_INHERIT.</para></note>
142 <variable name="MIXMONITOR_FILENAME">
143 <para>Will contain the filename used to record.</para>
148 <ref type="application">Monitor</ref>
149 <ref type="application">StopMixMonitor</ref>
150 <ref type="application">PauseMonitor</ref>
151 <ref type="application">UnpauseMonitor</ref>
152 <ref type="function">AUDIOHOOK_INHERIT</ref>
155 <application name="StopMixMonitor" language="en_US">
157 Stop recording a call through MixMonitor, and free the recording's file handle.
160 <parameter name="MixMonitorID" required="false">
161 <para>If a valid ID is provided, then this command will stop only that specific
166 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
167 on the current channel.</para>
170 <ref type="application">MixMonitor</ref>
173 <manager name="MixMonitorMute" language="en_US">
175 Mute / unMute a Mixmonitor recording.
178 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
179 <parameter name="Channel" required="true">
180 <para>Used to specify the channel to mute.</para>
182 <parameter name="Direction">
183 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
185 <parameter name="State">
186 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
190 <para>This action may be used to mute a MixMonitor recording.</para>
193 <manager name="MixMonitor" language="en_US">
195 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
196 to guarantee the audio file is available for processing during dialplan execution.
199 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
200 <parameter name="Channel" required="true">
201 <para>Used to specify the channel to record.</para>
203 <parameter name="File">
204 <para>Is the name of the file created in the monitor spool directory.
205 Defaults to the same name as the channel (with slashes replaced with dashes).
206 This argument is optional if you specify to record unidirectional audio with
207 either the r(filename) or t(filename) options in the options field. If
208 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
211 <parameter name="options">
212 <para>Options that apply to the MixMonitor in the same way as they
213 would apply if invoked from the MixMonitor application. For a list of
214 available options, see the documentation for the mixmonitor application. </para>
218 <para>This action records the audio on the current channel to the specified file.</para>
220 <variable name="MIXMONITOR_FILENAME">
221 <para>Will contain the filename used to record the mixed stream.</para>
226 <manager name="StopMixMonitor" language="en_US">
228 Stop recording a call through MixMonitor, and free the recording's file handle.
231 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
232 <parameter name="Channel" required="true">
233 <para>The name of the channel monitored.</para>
235 <parameter name="MixMonitorID" required="false">
236 <para>If a valid ID is provided, then this command will stop only that specific
241 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
242 action on the current channel.</para>
248 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
250 static const char * const app = "MixMonitor";
252 static const char * const stop_app = "StopMixMonitor";
254 static const char * const mixmonitor_spy_type = "MixMonitor";
258 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
260 struct vm_recipient {
261 char mailbox[AST_MAX_CONTEXT];
262 char context[AST_MAX_EXTENSION];
264 AST_LIST_ENTRY(vm_recipient) list;
268 struct ast_audiohook audiohook;
269 struct ast_callid *callid;
272 char *filename_write;
276 struct ast_autochan *autochan;
277 struct mixmonitor_ds *mixmonitor_ds;
279 /* the below string fields describe data used for creating voicemails from the recording */
280 AST_DECLARE_STRING_FIELDS(
281 AST_STRING_FIELD(call_context);
282 AST_STRING_FIELD(call_macrocontext);
283 AST_STRING_FIELD(call_extension);
284 AST_STRING_FIELD(call_callerchan);
285 AST_STRING_FIELD(call_callerid);
289 /* FUTURE DEVELOPMENT NOTICE
290 * recipient_list will need locks if we make it editable after the monitor is started */
291 AST_LIST_HEAD_NOLOCK(, vm_recipient) recipient_list;
294 enum mixmonitor_flags {
295 MUXFLAG_APPEND = (1 << 1),
296 MUXFLAG_BRIDGED = (1 << 2),
297 MUXFLAG_VOLUME = (1 << 3),
298 MUXFLAG_READVOLUME = (1 << 4),
299 MUXFLAG_WRITEVOLUME = (1 << 5),
300 MUXFLAG_READ = (1 << 6),
301 MUXFLAG_WRITE = (1 << 7),
302 MUXFLAG_COMBINED = (1 << 8),
303 MUXFLAG_UID = (1 << 9),
304 MUXFLAG_VMRECIPIENTS = (1 << 10),
307 enum mixmonitor_args {
308 OPT_ARG_READVOLUME = 0,
314 OPT_ARG_VMRECIPIENTS,
315 OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
318 AST_APP_OPTIONS(mixmonitor_opts, {
319 AST_APP_OPTION('a', MUXFLAG_APPEND),
320 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
321 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
322 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
323 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
324 AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
325 AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
326 AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
327 AST_APP_OPTION_ARG('m', MUXFLAG_VMRECIPIENTS, OPT_ARG_VMRECIPIENTS),
330 struct mixmonitor_ds {
331 unsigned int destruction_ok;
332 ast_cond_t destruction_condition;
335 /* The filestream is held in the datastore so it can be stopped
336 * immediately during stop_mixmonitor or channel destruction. */
339 struct ast_filestream *fs;
340 struct ast_filestream *fs_read;
341 struct ast_filestream *fs_write;
343 struct ast_audiohook *audiohook;
345 unsigned int samp_rate;
350 * \pre mixmonitor_ds must be locked before calling this function
352 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
354 unsigned char quitting = 0;
356 if (mixmonitor_ds->fs) {
358 ast_closestream(mixmonitor_ds->fs);
359 mixmonitor_ds->fs = NULL;
360 ast_verb(2, "MixMonitor close filestream (mixed)\n");
363 if (mixmonitor_ds->fs_read) {
365 ast_closestream(mixmonitor_ds->fs_read);
366 mixmonitor_ds->fs_read = NULL;
367 ast_verb(2, "MixMonitor close filestream (read)\n");
370 if (mixmonitor_ds->fs_write) {
372 ast_closestream(mixmonitor_ds->fs_write);
373 mixmonitor_ds->fs_write = NULL;
374 ast_verb(2, "MixMonitor close filestream (write)\n");
378 mixmonitor_ds->fs_quit = 1;
382 static void mixmonitor_ds_destroy(void *data)
384 struct mixmonitor_ds *mixmonitor_ds = data;
386 ast_mutex_lock(&mixmonitor_ds->lock);
387 mixmonitor_ds->audiohook = NULL;
388 mixmonitor_ds->destruction_ok = 1;
389 ast_cond_signal(&mixmonitor_ds->destruction_condition);
390 ast_mutex_unlock(&mixmonitor_ds->lock);
393 static const struct ast_datastore_info mixmonitor_ds_info = {
394 .type = "mixmonitor",
395 .destroy = mixmonitor_ds_destroy,
398 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
400 if (mixmonitor->mixmonitor_ds) {
401 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
402 mixmonitor->mixmonitor_ds->audiohook = NULL;
403 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
405 /* kill the audiohook.*/
406 ast_audiohook_lock(&mixmonitor->audiohook);
407 ast_audiohook_detach(&mixmonitor->audiohook);
408 ast_audiohook_unlock(&mixmonitor->audiohook);
409 ast_audiohook_destroy(&mixmonitor->audiohook);
412 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
414 struct ast_channel *peer = NULL;
420 ast_audiohook_attach(chan, audiohook);
422 if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
423 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
430 * \brief adds recipients to a mixmonitor's recipient list
431 * \param mixmonitor mixmonitor being affected
432 * \param vm_recipients string containing the desired recipients to add
434 static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
436 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
437 char *cur_mailbox = ast_strdupa(vm_recipients);
441 int elements_processed = 0;
443 while (!ast_strlen_zero(cur_mailbox)) {
444 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
445 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
449 if ((cur_folder = strchr(cur_mailbox, '/'))) {
450 *(cur_folder++) = '\0';
452 cur_folder = "INBOX";
455 if ((cur_context = strchr(cur_mailbox, '@'))) {
456 *(cur_context++) = '\0';
458 cur_context = "default";
461 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
462 struct vm_recipient *recipient;
463 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
464 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
467 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
468 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
469 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
472 ast_verb(5, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
473 AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
475 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
479 elements_processed++;
483 static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
485 struct vm_recipient *current;
486 while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
487 /* Clear list element data */
492 #define SAMPLES_PER_FRAME 160
494 static void mixmonitor_free(struct mixmonitor *mixmonitor)
497 if (mixmonitor->mixmonitor_ds) {
498 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
499 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
500 ast_free(mixmonitor->filename_write);
501 ast_free(mixmonitor->filename_read);
502 ast_free(mixmonitor->mixmonitor_ds);
503 ast_free(mixmonitor->name);
504 ast_free(mixmonitor->post_process);
507 /* Free everything in the recipient list */
508 clear_mixmonitor_recipient_list(mixmonitor);
510 /* clean stringfields */
511 ast_string_field_free_memory(mixmonitor);
513 if (mixmonitor->callid) {
514 ast_callid_unref(mixmonitor->callid);
516 ast_free(mixmonitor);
522 * \brief Copies the mixmonitor to all voicemail recipients
523 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
524 * \param ext Format of the file that was saved
526 static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
528 struct vm_recipient *recipient = NULL;
529 struct ast_vm_recording_data recording_data;
530 if (ast_string_field_init(&recording_data, 512)) {
531 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
535 /* Copy strings to stringfields that will be used for all recipients */
536 ast_string_field_set(&recording_data, recording_file, filename);
537 ast_string_field_set(&recording_data, recording_ext, ext);
538 ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
539 ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
540 ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
541 ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
542 ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
543 /* and call_priority gets copied too */
544 recording_data.call_priority = mixmonitor->call_priority;
546 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
547 /* context, mailbox, and folder need to be set per recipient */
548 ast_string_field_set(&recording_data, context, recipient->context);
549 ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
550 ast_string_field_set(&recording_data, folder, recipient->folder);
552 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
553 recording_data.context);
554 ast_app_copy_recording_to_vm(&recording_data);
557 /* Free the string fields for recording_data before exiting the function. */
558 ast_string_field_free_memory(&recording_data);
561 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
563 /* Initialize the file if not already done so */
564 char *last_slash = NULL;
565 if (!ast_strlen_zero(filename)) {
566 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
567 *oflags = O_CREAT | O_WRONLY;
568 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
570 last_slash = strrchr(filename, '/');
572 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
579 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
580 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
583 struct ast_filestream *tmp = *fs;
584 mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
590 static void *mixmonitor_thread(void *obj)
592 struct mixmonitor *mixmonitor = obj;
594 char *fs_read_ext = "";
595 char *fs_write_ext = "";
597 struct ast_filestream **fs = NULL;
598 struct ast_filestream **fs_read = NULL;
599 struct ast_filestream **fs_write = NULL;
603 struct ast_format format_slin;
605 /* Keep callid association before any log messages */
606 if (mixmonitor->callid) {
607 ast_callid_threadassoc_add(mixmonitor->callid);
610 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
612 fs = &mixmonitor->mixmonitor_ds->fs;
613 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
614 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
616 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
617 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
618 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
619 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
621 ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
623 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
626 /* The audiohook must enter and exit the loop locked */
627 ast_audiohook_lock(&mixmonitor->audiohook);
628 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
629 struct ast_frame *fr = NULL;
630 struct ast_frame *fr_read = NULL;
631 struct ast_frame *fr_write = NULL;
633 if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
634 &fr_read, &fr_write))) {
635 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
637 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
643 /* audiohook lock is not required for the next block.
644 * Unlock it, but remember to lock it before looping or exiting */
645 ast_audiohook_unlock(&mixmonitor->audiohook);
647 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
648 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
650 /* Write out the frame(s) */
651 if ((*fs_read) && (fr_read)) {
652 struct ast_frame *cur;
654 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
655 ast_writestream(*fs_read, cur);
659 if ((*fs_write) && (fr_write)) {
660 struct ast_frame *cur;
662 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
663 ast_writestream(*fs_write, cur);
668 struct ast_frame *cur;
670 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
671 ast_writestream(*fs, cur);
674 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
676 /* All done! free it. */
678 ast_frame_free(fr, 0);
681 ast_frame_free(fr_read, 0);
684 ast_frame_free(fr_write, 0);
691 ast_audiohook_lock(&mixmonitor->audiohook);
695 ast_test_suite_event_notify("MIXMONITOR_END", "Channel: %s\r\n"
697 ast_channel_name(mixmonitor->autochan->chan),
698 mixmonitor->filename);
700 ast_audiohook_unlock(&mixmonitor->audiohook);
702 ast_autochan_destroy(mixmonitor->autochan);
704 /* Datastore cleanup. close the filestream and wait for ds destruction */
705 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
706 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
707 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
708 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
710 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
712 /* kill the audiohook */
713 destroy_monitor_audiohook(mixmonitor);
715 if (mixmonitor->post_process) {
716 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
717 ast_safe_system(mixmonitor->post_process);
720 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
722 if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
723 if (ast_strlen_zero(fs_ext)) {
724 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
727 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
728 copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
730 if (!ast_strlen_zero(fs_read_ext)) {
731 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
732 copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
734 if (!ast_strlen_zero(fs_write_ext)) {
735 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
736 copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
739 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
742 mixmonitor_free(mixmonitor);
746 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
748 struct ast_datastore *datastore = NULL;
749 struct mixmonitor_ds *mixmonitor_ds;
751 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
755 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
756 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
759 ast_mutex_init(&mixmonitor_ds->lock);
760 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
762 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
763 ast_mutex_destroy(&mixmonitor_ds->lock);
764 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
765 ast_free(mixmonitor_ds);
770 mixmonitor_ds->samp_rate = 8000;
771 mixmonitor_ds->audiohook = &mixmonitor->audiohook;
772 datastore->data = mixmonitor_ds;
774 ast_channel_lock(chan);
775 ast_channel_datastore_add(chan, datastore);
776 ast_channel_unlock(chan);
778 mixmonitor->mixmonitor_ds = mixmonitor_ds;
782 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
783 unsigned int flags, int readvol, int writevol,
784 const char *post_process, const char *filename_write,
785 char *filename_read, const char *uid_channel_var,
786 const char *recipients)
789 struct mixmonitor *mixmonitor;
790 char postprocess2[1024] = "";
791 char *datastore_id = NULL;
794 /* If a post process system command is given attach it to the structure */
795 if (!ast_strlen_zero(post_process)) {
798 p1 = ast_strdupa(post_process);
799 for (p2 = p1; *p2; p2++) {
800 if (*p2 == '^' && *(p2+1) == '{') {
804 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
807 /* Pre-allocate mixmonitor structure and spy */
808 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
812 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
813 if (ast_string_field_init(mixmonitor, 512)) {
814 mixmonitor_free(mixmonitor);
818 /* Setup the actual spy before creating our thread */
819 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
820 mixmonitor_free(mixmonitor);
824 /* Copy over flags and channel name */
825 mixmonitor->flags = flags;
826 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
827 mixmonitor_free(mixmonitor);
831 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
832 ast_autochan_destroy(mixmonitor->autochan);
833 mixmonitor_free(mixmonitor);
834 ast_free(datastore_id);
838 if (!ast_strlen_zero(uid_channel_var)) {
840 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
843 ast_free(datastore_id);
845 mixmonitor->name = ast_strdup(ast_channel_name(chan));
847 if (!ast_strlen_zero(postprocess2)) {
848 mixmonitor->post_process = ast_strdup(postprocess2);
851 if (!ast_strlen_zero(filename)) {
852 mixmonitor->filename = ast_strdup(filename);
855 if (!ast_strlen_zero(filename_write)) {
856 mixmonitor->filename_write = ast_strdup(filename_write);
859 if (!ast_strlen_zero(filename_read)) {
860 mixmonitor->filename_read = ast_strdup(filename_read);
863 if (!ast_strlen_zero(recipients)) {
865 struct ast_party_connected_line *connected;
867 ast_channel_lock(chan);
869 /* We use the connected line of the invoking channel for caller ID. */
871 connected = ast_channel_connected(chan);
872 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
873 connected->id.name.str, connected->id.number.valid,
874 connected->id.number.str);
875 ast_callerid_merge(callerid, sizeof(callerid),
876 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
877 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
880 ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
881 ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
882 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
883 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
884 ast_string_field_set(mixmonitor, call_callerid, callerid);
885 mixmonitor->call_priority = ast_channel_priority(chan);
887 ast_channel_unlock(chan);
889 add_vm_recipients_from_string(mixmonitor, recipients);
892 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
895 mixmonitor->audiohook.options.read_volume = readvol;
897 mixmonitor->audiohook.options.write_volume = writevol;
899 if (startmon(chan, &mixmonitor->audiohook)) {
900 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
901 mixmonitor_spy_type, ast_channel_name(chan));
902 ast_audiohook_destroy(&mixmonitor->audiohook);
903 mixmonitor_free(mixmonitor);
907 /* reference be released at mixmonitor destruction */
908 mixmonitor->callid = ast_read_threadstorage_callid();
910 ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
913 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
914 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
915 static char *filename_parse(char *filename, char *buffer, size_t len)
918 if (ast_strlen_zero(filename)) {
919 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
920 } else if (filename[0] != '/') {
922 build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
923 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
927 ast_copy_string(buffer, filename, len);
929 if ((slash = strrchr(filename, '/'))) {
932 ast_mkdir(filename, 0777);
937 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
939 int x, readvol = 0, writevol = 0;
940 char *filename_read = NULL;
941 char *filename_write = NULL;
942 char filename_buffer[1024] = "";
943 char *uid_channel_var = NULL;
945 struct ast_flags flags = { 0 };
946 char *recipients = NULL;
948 AST_DECLARE_APP_ARGS(args,
949 AST_APP_ARG(filename);
950 AST_APP_ARG(options);
951 AST_APP_ARG(post_process);
954 if (ast_strlen_zero(data)) {
955 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
959 parse = ast_strdupa(data);
961 AST_STANDARD_APP_ARGS(args, parse);
964 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
966 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
968 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
969 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
970 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
971 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
972 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
974 readvol = get_volfactor(x);
978 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
979 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
980 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
981 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
982 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
984 writevol = get_volfactor(x);
988 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
989 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
990 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
991 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
992 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
994 readvol = writevol = get_volfactor(x);
998 if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
999 if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
1000 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1002 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1006 if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
1007 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1010 if (ast_test_flag(&flags, MUXFLAG_READ)) {
1011 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1014 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1015 uid_channel_var = opts[OPT_ARG_UID];
1018 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1020 if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
1021 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1025 /* If filename exists, try to create directories for it */
1026 if (!(ast_strlen_zero(args.filename))) {
1027 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1030 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1031 launch_monitor_thread(chan,
1045 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1047 struct ast_datastore *datastore = NULL;
1049 struct mixmonitor_ds *mixmonitor_ds;
1051 AST_DECLARE_APP_ARGS(args,
1052 AST_APP_ARG(mixmonid);
1055 if (!ast_strlen_zero(data)) {
1056 parse = ast_strdupa(data);
1059 AST_STANDARD_APP_ARGS(args, parse);
1061 ast_channel_lock(chan);
1063 if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
1064 ast_channel_unlock(chan);
1067 mixmonitor_ds = datastore->data;
1069 ast_mutex_lock(&mixmonitor_ds->lock);
1071 /* closing the filestream here guarantees the file is avaliable to the dialplan
1072 * after calling StopMixMonitor */
1073 mixmonitor_ds_close_fs(mixmonitor_ds);
1075 /* The mixmonitor thread may be waiting on the audiohook trigger.
1076 * In order to exit from the mixmonitor loop before waiting on channel
1077 * destruction, poke the audiohook trigger. */
1078 if (mixmonitor_ds->audiohook) {
1079 if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
1080 ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
1082 ast_audiohook_lock(mixmonitor_ds->audiohook);
1083 ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
1084 ast_audiohook_unlock(mixmonitor_ds->audiohook);
1085 mixmonitor_ds->audiohook = NULL;
1088 ast_mutex_unlock(&mixmonitor_ds->lock);
1090 /* Remove the datastore so the monitor thread can exit */
1091 if (!ast_channel_datastore_remove(chan, datastore)) {
1092 ast_datastore_free(datastore);
1094 ast_channel_unlock(chan);
1099 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1101 struct ast_channel *chan;
1102 struct ast_datastore *datastore = NULL;
1103 struct mixmonitor_ds *mixmonitor_ds = NULL;
1107 e->command = "mixmonitor {start|stop|list}";
1109 "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
1110 " The optional arguments are passed to the MixMonitor\n"
1111 " application when the 'start' command is used.\n";
1114 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1118 return CLI_SHOWUSAGE;
1121 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1122 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1123 /* Technically this is a failure, but we don't want 2 errors printing out */
1127 ast_channel_lock(chan);
1129 if (!strcasecmp(a->argv[1], "start")) {
1130 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1131 ast_channel_unlock(chan);
1132 } else if (!strcasecmp(a->argv[1], "stop")){
1133 ast_channel_unlock(chan);
1134 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1135 } else if (!strcasecmp(a->argv[1], "list")) {
1136 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1137 ast_cli(a->fd, "=========================================================================\n");
1138 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1139 if (datastore->info == &mixmonitor_ds_info) {
1140 char *filename = "";
1141 char *filename_read = "";
1142 char *filename_write = "";
1143 mixmonitor_ds = datastore->data;
1144 if (mixmonitor_ds->fs)
1145 filename = ast_strdupa(mixmonitor_ds->fs->filename);
1146 if (mixmonitor_ds->fs_read)
1147 filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
1148 if (mixmonitor_ds->fs_write)
1149 filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
1150 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1153 ast_channel_unlock(chan);
1155 ast_channel_unlock(chan);
1156 chan = ast_channel_unref(chan);
1157 return CLI_SHOWUSAGE;
1160 chan = ast_channel_unref(chan);
1165 /*! \brief Mute / unmute a MixMonitor channel */
1166 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1168 struct ast_channel *c = NULL;
1170 const char *name = astman_get_header(m, "Channel");
1171 const char *id = astman_get_header(m, "ActionID");
1172 const char *state = astman_get_header(m, "State");
1173 const char *direction = astman_get_header(m,"Direction");
1177 enum ast_audiohook_flags flag;
1179 if (ast_strlen_zero(direction)) {
1180 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1184 if (!strcasecmp(direction, "read")) {
1185 flag = AST_AUDIOHOOK_MUTE_READ;
1186 } else if (!strcasecmp(direction, "write")) {
1187 flag = AST_AUDIOHOOK_MUTE_WRITE;
1188 } else if (!strcasecmp(direction, "both")) {
1189 flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
1191 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1195 if (ast_strlen_zero(name)) {
1196 astman_send_error(s, m, "No channel specified");
1200 if (ast_strlen_zero(state)) {
1201 astman_send_error(s, m, "No state specified");
1205 clearmute = ast_false(state);
1206 c = ast_channel_get_by_name(name);
1209 astman_send_error(s, m, "No such channel");
1213 if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
1214 c = ast_channel_unref(c);
1215 astman_send_error(s, m, "Cannot set mute flag");
1219 astman_append(s, "Response: Success\r\n");
1221 if (!ast_strlen_zero(id)) {
1222 astman_append(s, "ActionID: %s\r\n", id);
1225 astman_append(s, "\r\n");
1227 c = ast_channel_unref(c);
1232 static int manager_mixmonitor(struct mansession *s, const struct message *m)
1234 struct ast_channel *c = NULL;
1236 const char *name = astman_get_header(m, "Channel");
1237 const char *id = astman_get_header(m, "ActionID");
1238 const char *file = astman_get_header(m, "File");
1239 const char *options = astman_get_header(m, "Options");
1240 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1241 struct ast_flags flags = { 0 };
1242 char *uid_channel_var = NULL;
1243 const char *mixmonitor_id = NULL;
1246 char args[PATH_MAX] = "";
1247 if (ast_strlen_zero(name)) {
1248 astman_send_error(s, m, "No channel specified");
1252 c = ast_channel_get_by_name(name);
1255 astman_send_error(s, m, "No such channel");
1259 if (!ast_strlen_zero(options)) {
1260 ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
1263 snprintf(args, sizeof(args), "%s,%s", file, options);
1265 ast_channel_lock(c);
1266 res = mixmonitor_exec(c, args);
1268 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1269 uid_channel_var = opts[OPT_ARG_UID];
1270 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1272 ast_channel_unlock(c);
1275 c = ast_channel_unref(c);
1276 astman_send_error(s, m, "Could not start monitoring channel");
1280 astman_append(s, "Response: Success\r\n");
1282 if (!ast_strlen_zero(id)) {
1283 astman_append(s, "ActionID: %s\r\n", id);
1286 if (!ast_strlen_zero(mixmonitor_id)) {
1287 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1290 astman_append(s, "\r\n");
1292 c = ast_channel_unref(c);
1297 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1299 struct ast_channel *c = NULL;
1301 const char *name = astman_get_header(m, "Channel");
1302 const char *id = astman_get_header(m, "ActionID");
1303 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1306 if (ast_strlen_zero(name)) {
1307 astman_send_error(s, m, "No channel specified");
1311 c = ast_channel_get_by_name(name);
1314 astman_send_error(s, m, "No such channel");
1318 res = stop_mixmonitor_exec(c, mixmonitor_id);
1321 astman_send_error(s, m, "Could not stop monitoring channel");
1325 astman_append(s, "Response: Success\r\n");
1327 if (!ast_strlen_zero(id)) {
1328 astman_append(s, "ActionID: %s\r\n", id);
1331 astman_append(s, "\r\n");
1333 c = ast_channel_unref(c);
1338 static struct ast_cli_entry cli_mixmonitor[] = {
1339 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1342 static int unload_module(void)
1346 ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1347 res = ast_unregister_application(stop_app);
1348 res |= ast_unregister_application(app);
1349 res |= ast_manager_unregister("MixMonitorMute");
1350 res |= ast_manager_unregister("MixMonitor");
1351 res |= ast_manager_unregister("StopMixMonitor");
1356 static int load_module(void)
1360 ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1361 res = ast_register_application_xml(app, mixmonitor_exec);
1362 res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
1363 res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
1364 res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
1365 res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
1370 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");