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>
135 <note><para>MixMonitor runs as an audiohook. In order to keep it running through
136 a transfer, AUDIOHOOK_INHERIT must be set for the channel which ran mixmonitor.
137 For more information, including dialplan configuration set for using
138 AUDIOHOOK_INHERIT with MixMonitor, see the function documentation for
139 AUDIOHOOK_INHERIT.</para></note>
141 <variable name="MIXMONITOR_FILENAME">
142 <para>Will contain the filename used to record.</para>
147 <ref type="application">Monitor</ref>
148 <ref type="application">StopMixMonitor</ref>
149 <ref type="application">PauseMonitor</ref>
150 <ref type="application">UnpauseMonitor</ref>
151 <ref type="function">AUDIOHOOK_INHERIT</ref>
154 <application name="StopMixMonitor" language="en_US">
156 Stop recording a call through MixMonitor, and free the recording's file handle.
159 <parameter name="MixMonitorID" required="false">
160 <para>If a valid ID is provided, then this command will stop only that specific
165 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
166 on the current channel.</para>
169 <ref type="application">MixMonitor</ref>
172 <manager name="MixMonitorMute" language="en_US">
174 Mute / unMute a Mixmonitor recording.
177 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
178 <parameter name="Channel" required="true">
179 <para>Used to specify the channel to mute.</para>
181 <parameter name="Direction">
182 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
184 <parameter name="State">
185 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
189 <para>This action may be used to mute a MixMonitor recording.</para>
192 <manager name="MixMonitor" language="en_US">
194 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
195 to guarantee the audio file is available for processing during dialplan execution.
198 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
199 <parameter name="Channel" required="true">
200 <para>Used to specify the channel to record.</para>
202 <parameter name="File">
203 <para>Is the name of the file created in the monitor spool directory.
204 Defaults to the same name as the channel (with slashes replaced with dashes).
205 This argument is optional if you specify to record unidirectional audio with
206 either the r(filename) or t(filename) options in the options field. If
207 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
210 <parameter name="options">
211 <para>Options that apply to the MixMonitor in the same way as they
212 would apply if invoked from the MixMonitor application. For a list of
213 available options, see the documentation for the mixmonitor application. </para>
217 <para>This action records the audio on the current channel to the specified file.</para>
219 <variable name="MIXMONITOR_FILENAME">
220 <para>Will contain the filename used to record the mixed stream.</para>
225 <manager name="StopMixMonitor" language="en_US">
227 Stop recording a call through MixMonitor, and free the recording's file handle.
230 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
231 <parameter name="Channel" required="true">
232 <para>The name of the channel monitored.</para>
234 <parameter name="MixMonitorID" required="false">
235 <para>If a valid ID is provided, then this command will stop only that specific
240 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
241 action on the current channel.</para>
247 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
249 static const char * const app = "MixMonitor";
251 static const char * const stop_app = "StopMixMonitor";
253 static const char * const mixmonitor_spy_type = "MixMonitor";
257 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
259 struct vm_recipient {
260 char mailbox[AST_MAX_CONTEXT];
261 char context[AST_MAX_EXTENSION];
263 AST_LIST_ENTRY(vm_recipient) list;
267 struct ast_audiohook audiohook;
268 struct ast_callid *callid;
271 char *filename_write;
275 struct ast_autochan *autochan;
276 struct mixmonitor_ds *mixmonitor_ds;
278 /* the below string fields describe data used for creating voicemails from the recording */
279 AST_DECLARE_STRING_FIELDS(
280 AST_STRING_FIELD(call_context);
281 AST_STRING_FIELD(call_macrocontext);
282 AST_STRING_FIELD(call_extension);
283 AST_STRING_FIELD(call_callerchan);
284 AST_STRING_FIELD(call_callerid);
288 /* FUTURE DEVELOPMENT NOTICE
289 * recipient_list will need locks if we make it editable after the monitor is started */
290 AST_LIST_HEAD_NOLOCK(, vm_recipient) recipient_list;
293 enum mixmonitor_flags {
294 MUXFLAG_APPEND = (1 << 1),
295 MUXFLAG_BRIDGED = (1 << 2),
296 MUXFLAG_VOLUME = (1 << 3),
297 MUXFLAG_READVOLUME = (1 << 4),
298 MUXFLAG_WRITEVOLUME = (1 << 5),
299 MUXFLAG_READ = (1 << 6),
300 MUXFLAG_WRITE = (1 << 7),
301 MUXFLAG_COMBINED = (1 << 8),
302 MUXFLAG_UID = (1 << 9),
303 MUXFLAG_VMRECIPIENTS = (1 << 10),
306 enum mixmonitor_args {
307 OPT_ARG_READVOLUME = 0,
313 OPT_ARG_VMRECIPIENTS,
314 OPT_ARG_ARRAY_SIZE, /* Always last element of the enum */
317 AST_APP_OPTIONS(mixmonitor_opts, {
318 AST_APP_OPTION('a', MUXFLAG_APPEND),
319 AST_APP_OPTION('b', MUXFLAG_BRIDGED),
320 AST_APP_OPTION_ARG('v', MUXFLAG_READVOLUME, OPT_ARG_READVOLUME),
321 AST_APP_OPTION_ARG('V', MUXFLAG_WRITEVOLUME, OPT_ARG_WRITEVOLUME),
322 AST_APP_OPTION_ARG('W', MUXFLAG_VOLUME, OPT_ARG_VOLUME),
323 AST_APP_OPTION_ARG('r', MUXFLAG_READ, OPT_ARG_READNAME),
324 AST_APP_OPTION_ARG('t', MUXFLAG_WRITE, OPT_ARG_WRITENAME),
325 AST_APP_OPTION_ARG('i', MUXFLAG_UID, OPT_ARG_UID),
326 AST_APP_OPTION_ARG('m', MUXFLAG_VMRECIPIENTS, OPT_ARG_VMRECIPIENTS),
329 struct mixmonitor_ds {
330 unsigned int destruction_ok;
331 ast_cond_t destruction_condition;
334 /* The filestream is held in the datastore so it can be stopped
335 * immediately during stop_mixmonitor or channel destruction. */
338 struct ast_filestream *fs;
339 struct ast_filestream *fs_read;
340 struct ast_filestream *fs_write;
342 struct ast_audiohook *audiohook;
344 unsigned int samp_rate;
349 * \pre mixmonitor_ds must be locked before calling this function
351 static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
353 unsigned char quitting = 0;
355 if (mixmonitor_ds->fs) {
357 ast_closestream(mixmonitor_ds->fs);
358 mixmonitor_ds->fs = NULL;
359 ast_verb(2, "MixMonitor close filestream (mixed)\n");
362 if (mixmonitor_ds->fs_read) {
364 ast_closestream(mixmonitor_ds->fs_read);
365 mixmonitor_ds->fs_read = NULL;
366 ast_verb(2, "MixMonitor close filestream (read)\n");
369 if (mixmonitor_ds->fs_write) {
371 ast_closestream(mixmonitor_ds->fs_write);
372 mixmonitor_ds->fs_write = NULL;
373 ast_verb(2, "MixMonitor close filestream (write)\n");
377 mixmonitor_ds->fs_quit = 1;
381 static void mixmonitor_ds_destroy(void *data)
383 struct mixmonitor_ds *mixmonitor_ds = data;
385 ast_mutex_lock(&mixmonitor_ds->lock);
386 mixmonitor_ds->audiohook = NULL;
387 mixmonitor_ds->destruction_ok = 1;
388 ast_cond_signal(&mixmonitor_ds->destruction_condition);
389 ast_mutex_unlock(&mixmonitor_ds->lock);
392 static const struct ast_datastore_info mixmonitor_ds_info = {
393 .type = "mixmonitor",
394 .destroy = mixmonitor_ds_destroy,
397 static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
399 if (mixmonitor->mixmonitor_ds) {
400 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
401 mixmonitor->mixmonitor_ds->audiohook = NULL;
402 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
404 /* kill the audiohook.*/
405 ast_audiohook_lock(&mixmonitor->audiohook);
406 ast_audiohook_detach(&mixmonitor->audiohook);
407 ast_audiohook_unlock(&mixmonitor->audiohook);
408 ast_audiohook_destroy(&mixmonitor->audiohook);
411 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
413 struct ast_channel *peer = NULL;
419 ast_audiohook_attach(chan, audiohook);
421 if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
422 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);
429 * \brief adds recipients to a mixmonitor's recipient list
430 * \param mixmonitor mixmonitor being affected
431 * \param vm_recipients string containing the desired recipients to add
433 static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
435 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
436 char *cur_mailbox = ast_strdupa(vm_recipients);
440 int elements_processed = 0;
442 while (!ast_strlen_zero(cur_mailbox)) {
443 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
444 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
448 if ((cur_folder = strchr(cur_mailbox, '/'))) {
449 *(cur_folder++) = '\0';
451 cur_folder = "INBOX";
454 if ((cur_context = strchr(cur_mailbox, '@'))) {
455 *(cur_context++) = '\0';
457 cur_context = "default";
460 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
461 struct vm_recipient *recipient;
462 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
463 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
466 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
467 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
468 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
471 ast_verb(5, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
472 AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
474 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
478 elements_processed++;
482 static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
484 struct vm_recipient *current;
485 while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
486 /* Clear list element data */
491 #define SAMPLES_PER_FRAME 160
493 static void mixmonitor_free(struct mixmonitor *mixmonitor)
496 if (mixmonitor->mixmonitor_ds) {
497 ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
498 ast_cond_destroy(&mixmonitor->mixmonitor_ds->destruction_condition);
499 ast_free(mixmonitor->filename_write);
500 ast_free(mixmonitor->filename_read);
501 ast_free(mixmonitor->mixmonitor_ds);
502 ast_free(mixmonitor->name);
503 ast_free(mixmonitor->post_process);
506 /* Free everything in the recipient list */
507 clear_mixmonitor_recipient_list(mixmonitor);
509 /* clean stringfields */
510 ast_string_field_free_memory(mixmonitor);
512 if (mixmonitor->callid) {
513 ast_callid_unref(mixmonitor->callid);
515 ast_free(mixmonitor);
521 * \brief Copies the mixmonitor to all voicemail recipients
522 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
523 * \param ext Format of the file that was saved
525 static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
527 struct vm_recipient *recipient = NULL;
528 struct ast_vm_recording_data recording_data;
529 if (ast_string_field_init(&recording_data, 512)) {
530 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
534 /* Copy strings to stringfields that will be used for all recipients */
535 ast_string_field_set(&recording_data, recording_file, filename);
536 ast_string_field_set(&recording_data, recording_ext, ext);
537 ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
538 ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
539 ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
540 ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
541 ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
542 /* and call_priority gets copied too */
543 recording_data.call_priority = mixmonitor->call_priority;
545 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
546 /* context, mailbox, and folder need to be set per recipient */
547 ast_string_field_set(&recording_data, context, recipient->context);
548 ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
549 ast_string_field_set(&recording_data, folder, recipient->folder);
551 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
552 recording_data.context);
553 ast_app_copy_recording_to_vm(&recording_data);
556 /* Free the string fields for recording_data before exiting the function. */
557 ast_string_field_free_memory(&recording_data);
560 static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
562 /* Initialize the file if not already done so */
563 char *last_slash = NULL;
564 if (!ast_strlen_zero(filename)) {
565 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
566 *oflags = O_CREAT | O_WRONLY;
567 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
569 last_slash = strrchr(filename, '/');
571 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
578 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
579 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
582 struct ast_filestream *tmp = *fs;
583 mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
589 static void *mixmonitor_thread(void *obj)
591 struct mixmonitor *mixmonitor = obj;
593 char *fs_read_ext = "";
594 char *fs_write_ext = "";
596 struct ast_filestream **fs = NULL;
597 struct ast_filestream **fs_read = NULL;
598 struct ast_filestream **fs_write = NULL;
602 struct ast_format format_slin;
604 /* Keep callid association before any log messages */
605 if (mixmonitor->callid) {
606 ast_callid_threadassoc_add(mixmonitor->callid);
609 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
611 fs = &mixmonitor->mixmonitor_ds->fs;
612 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
613 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
615 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
616 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
617 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
618 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
620 ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);
622 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
625 /* The audiohook must enter and exit the loop locked */
626 ast_audiohook_lock(&mixmonitor->audiohook);
627 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
628 struct ast_frame *fr = NULL;
629 struct ast_frame *fr_read = NULL;
630 struct ast_frame *fr_write = NULL;
632 if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
633 &fr_read, &fr_write))) {
634 ast_audiohook_trigger_wait(&mixmonitor->audiohook);
636 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
642 /* audiohook lock is not required for the next block.
643 * Unlock it, but remember to lock it before looping or exiting */
644 ast_audiohook_unlock(&mixmonitor->audiohook);
646 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
647 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
649 /* Write out the frame(s) */
650 if ((*fs_read) && (fr_read)) {
651 struct ast_frame *cur;
653 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
654 ast_writestream(*fs_read, cur);
658 if ((*fs_write) && (fr_write)) {
659 struct ast_frame *cur;
661 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
662 ast_writestream(*fs_write, cur);
667 struct ast_frame *cur;
669 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
670 ast_writestream(*fs, cur);
673 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
675 /* All done! free it. */
677 ast_frame_free(fr, 0);
680 ast_frame_free(fr_read, 0);
683 ast_frame_free(fr_write, 0);
690 ast_audiohook_lock(&mixmonitor->audiohook);
692 ast_audiohook_unlock(&mixmonitor->audiohook);
694 ast_autochan_destroy(mixmonitor->autochan);
696 /* Datastore cleanup. close the filestream and wait for ds destruction */
697 ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
698 mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
699 if (!mixmonitor->mixmonitor_ds->destruction_ok) {
700 ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
702 ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
704 /* kill the audiohook */
705 destroy_monitor_audiohook(mixmonitor);
707 if (mixmonitor->post_process) {
708 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
709 ast_safe_system(mixmonitor->post_process);
712 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
714 if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
715 if (ast_strlen_zero(fs_ext)) {
716 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
719 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
720 copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
722 if (!ast_strlen_zero(fs_read_ext)) {
723 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
724 copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
726 if (!ast_strlen_zero(fs_write_ext)) {
727 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
728 copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
731 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
734 mixmonitor_free(mixmonitor);
738 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
740 struct ast_datastore *datastore = NULL;
741 struct mixmonitor_ds *mixmonitor_ds;
743 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
747 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
748 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
751 ast_mutex_init(&mixmonitor_ds->lock);
752 ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
754 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
755 ast_mutex_destroy(&mixmonitor_ds->lock);
756 ast_cond_destroy(&mixmonitor_ds->destruction_condition);
757 ast_free(mixmonitor_ds);
762 mixmonitor_ds->samp_rate = 8000;
763 mixmonitor_ds->audiohook = &mixmonitor->audiohook;
764 datastore->data = mixmonitor_ds;
766 ast_channel_lock(chan);
767 ast_channel_datastore_add(chan, datastore);
768 ast_channel_unlock(chan);
770 mixmonitor->mixmonitor_ds = mixmonitor_ds;
774 static void launch_monitor_thread(struct ast_channel *chan, const char *filename,
775 unsigned int flags, int readvol, int writevol,
776 const char *post_process, const char *filename_write,
777 char *filename_read, const char *uid_channel_var,
778 const char *recipients)
781 struct mixmonitor *mixmonitor;
782 char postprocess2[1024] = "";
783 char *datastore_id = NULL;
786 /* If a post process system command is given attach it to the structure */
787 if (!ast_strlen_zero(post_process)) {
790 p1 = ast_strdupa(post_process);
791 for (p2 = p1; *p2; p2++) {
792 if (*p2 == '^' && *(p2+1) == '{') {
796 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
799 /* Pre-allocate mixmonitor structure and spy */
800 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
804 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
805 if (ast_string_field_init(mixmonitor, 512)) {
806 mixmonitor_free(mixmonitor);
810 /* Setup the actual spy before creating our thread */
811 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
812 mixmonitor_free(mixmonitor);
816 /* Copy over flags and channel name */
817 mixmonitor->flags = flags;
818 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
819 mixmonitor_free(mixmonitor);
823 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
824 ast_autochan_destroy(mixmonitor->autochan);
825 mixmonitor_free(mixmonitor);
826 ast_free(datastore_id);
830 if (!ast_strlen_zero(uid_channel_var)) {
832 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
835 ast_free(datastore_id);
837 mixmonitor->name = ast_strdup(ast_channel_name(chan));
839 if (!ast_strlen_zero(postprocess2)) {
840 mixmonitor->post_process = ast_strdup(postprocess2);
843 if (!ast_strlen_zero(filename)) {
844 mixmonitor->filename = ast_strdup(filename);
847 if (!ast_strlen_zero(filename_write)) {
848 mixmonitor->filename_write = ast_strdup(filename_write);
851 if (!ast_strlen_zero(filename_read)) {
852 mixmonitor->filename_read = ast_strdup(filename_read);
855 if (!ast_strlen_zero(recipients)) {
857 struct ast_party_connected_line *connected;
859 ast_channel_lock(chan);
861 /* We use the connected line of the invoking channel for caller ID. */
863 connected = ast_channel_connected(chan);
864 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
865 connected->id.name.str, connected->id.number.valid,
866 connected->id.number.str);
867 ast_callerid_merge(callerid, sizeof(callerid),
868 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
869 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
872 ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
873 ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
874 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
875 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
876 ast_string_field_set(mixmonitor, call_callerid, callerid);
877 mixmonitor->call_priority = ast_channel_priority(chan);
879 ast_channel_unlock(chan);
881 add_vm_recipients_from_string(mixmonitor, recipients);
884 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);
887 mixmonitor->audiohook.options.read_volume = readvol;
889 mixmonitor->audiohook.options.write_volume = writevol;
891 if (startmon(chan, &mixmonitor->audiohook)) {
892 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
893 mixmonitor_spy_type, ast_channel_name(chan));
894 ast_audiohook_destroy(&mixmonitor->audiohook);
895 mixmonitor_free(mixmonitor);
899 /* reference be released at mixmonitor destruction */
900 mixmonitor->callid = ast_read_threadstorage_callid();
902 ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
905 /* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
906 /* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
907 static char *filename_parse(char *filename, char *buffer, size_t len)
910 if (ast_strlen_zero(filename)) {
911 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
912 } else if (filename[0] != '/') {
914 build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
915 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
919 ast_copy_string(buffer, filename, len);
921 if ((slash = strrchr(filename, '/'))) {
924 ast_mkdir(filename, 0777);
929 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
931 int x, readvol = 0, writevol = 0;
932 char *filename_read = NULL;
933 char *filename_write = NULL;
934 char filename_buffer[1024] = "";
935 char *uid_channel_var = NULL;
937 struct ast_flags flags = { 0 };
938 char *recipients = NULL;
940 AST_DECLARE_APP_ARGS(args,
941 AST_APP_ARG(filename);
942 AST_APP_ARG(options);
943 AST_APP_ARG(post_process);
946 if (ast_strlen_zero(data)) {
947 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
951 parse = ast_strdupa(data);
953 AST_STANDARD_APP_ARGS(args, parse);
956 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
958 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
960 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
961 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
962 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
963 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
964 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
966 readvol = get_volfactor(x);
970 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
971 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
972 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
973 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
974 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
976 writevol = get_volfactor(x);
980 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
981 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
982 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
983 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
984 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
986 readvol = writevol = get_volfactor(x);
990 if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
991 if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
992 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
994 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
998 if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
999 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1002 if (ast_test_flag(&flags, MUXFLAG_READ)) {
1003 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1006 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1007 uid_channel_var = opts[OPT_ARG_UID];
1010 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1012 if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
1013 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1017 /* If filename exists, try to create directories for it */
1018 if (!(ast_strlen_zero(args.filename))) {
1019 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1022 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1023 launch_monitor_thread(chan,
1037 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1039 struct ast_datastore *datastore = NULL;
1041 struct mixmonitor_ds *mixmonitor_ds;
1043 AST_DECLARE_APP_ARGS(args,
1044 AST_APP_ARG(mixmonid);
1047 if (!ast_strlen_zero(data)) {
1048 parse = ast_strdupa(data);
1051 AST_STANDARD_APP_ARGS(args, parse);
1053 ast_channel_lock(chan);
1055 if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
1056 ast_channel_unlock(chan);
1059 mixmonitor_ds = datastore->data;
1061 ast_mutex_lock(&mixmonitor_ds->lock);
1063 /* closing the filestream here guarantees the file is avaliable to the dialplan
1064 * after calling StopMixMonitor */
1065 mixmonitor_ds_close_fs(mixmonitor_ds);
1067 /* The mixmonitor thread may be waiting on the audiohook trigger.
1068 * In order to exit from the mixmonitor loop before waiting on channel
1069 * destruction, poke the audiohook trigger. */
1070 if (mixmonitor_ds->audiohook) {
1071 if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
1072 ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
1074 ast_audiohook_lock(mixmonitor_ds->audiohook);
1075 ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
1076 ast_audiohook_unlock(mixmonitor_ds->audiohook);
1077 mixmonitor_ds->audiohook = NULL;
1080 ast_mutex_unlock(&mixmonitor_ds->lock);
1082 /* Remove the datastore so the monitor thread can exit */
1083 if (!ast_channel_datastore_remove(chan, datastore)) {
1084 ast_datastore_free(datastore);
1086 ast_channel_unlock(chan);
1091 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1093 struct ast_channel *chan;
1094 struct ast_datastore *datastore = NULL;
1095 struct mixmonitor_ds *mixmonitor_ds = NULL;
1099 e->command = "mixmonitor {start|stop|list}";
1101 "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
1102 " The optional arguments are passed to the MixMonitor\n"
1103 " application when the 'start' command is used.\n";
1106 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1110 return CLI_SHOWUSAGE;
1113 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1114 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1115 /* Technically this is a failure, but we don't want 2 errors printing out */
1119 ast_channel_lock(chan);
1121 if (!strcasecmp(a->argv[1], "start")) {
1122 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1123 ast_channel_unlock(chan);
1124 } else if (!strcasecmp(a->argv[1], "stop")){
1125 ast_channel_unlock(chan);
1126 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1127 } else if (!strcasecmp(a->argv[1], "list")) {
1128 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1129 ast_cli(a->fd, "=========================================================================\n");
1130 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1131 if (datastore->info == &mixmonitor_ds_info) {
1132 char *filename = "";
1133 char *filename_read = "";
1134 char *filename_write = "";
1135 mixmonitor_ds = datastore->data;
1136 if (mixmonitor_ds->fs)
1137 filename = ast_strdupa(mixmonitor_ds->fs->filename);
1138 if (mixmonitor_ds->fs_read)
1139 filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
1140 if (mixmonitor_ds->fs_write)
1141 filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
1142 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1145 ast_channel_unlock(chan);
1147 ast_channel_unlock(chan);
1148 chan = ast_channel_unref(chan);
1149 return CLI_SHOWUSAGE;
1152 chan = ast_channel_unref(chan);
1157 /*! \brief Mute / unmute a MixMonitor channel */
1158 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1160 struct ast_channel *c = NULL;
1162 const char *name = astman_get_header(m, "Channel");
1163 const char *id = astman_get_header(m, "ActionID");
1164 const char *state = astman_get_header(m, "State");
1165 const char *direction = astman_get_header(m,"Direction");
1169 enum ast_audiohook_flags flag;
1171 if (ast_strlen_zero(direction)) {
1172 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1176 if (!strcasecmp(direction, "read")) {
1177 flag = AST_AUDIOHOOK_MUTE_READ;
1178 } else if (!strcasecmp(direction, "write")) {
1179 flag = AST_AUDIOHOOK_MUTE_WRITE;
1180 } else if (!strcasecmp(direction, "both")) {
1181 flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
1183 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1187 if (ast_strlen_zero(name)) {
1188 astman_send_error(s, m, "No channel specified");
1192 if (ast_strlen_zero(state)) {
1193 astman_send_error(s, m, "No state specified");
1197 clearmute = ast_false(state);
1198 c = ast_channel_get_by_name(name);
1201 astman_send_error(s, m, "No such channel");
1205 if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
1206 c = ast_channel_unref(c);
1207 astman_send_error(s, m, "Cannot set mute flag");
1211 astman_append(s, "Response: Success\r\n");
1213 if (!ast_strlen_zero(id)) {
1214 astman_append(s, "ActionID: %s\r\n", id);
1217 astman_append(s, "\r\n");
1219 c = ast_channel_unref(c);
1224 static int manager_mixmonitor(struct mansession *s, const struct message *m)
1226 struct ast_channel *c = NULL;
1228 const char *name = astman_get_header(m, "Channel");
1229 const char *id = astman_get_header(m, "ActionID");
1230 const char *file = astman_get_header(m, "File");
1231 const char *options = astman_get_header(m, "Options");
1232 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1233 struct ast_flags flags = { 0 };
1234 char *uid_channel_var = NULL;
1235 const char *mixmonitor_id = NULL;
1238 char args[PATH_MAX] = "";
1239 if (ast_strlen_zero(name)) {
1240 astman_send_error(s, m, "No channel specified");
1244 c = ast_channel_get_by_name(name);
1247 astman_send_error(s, m, "No such channel");
1251 if (!ast_strlen_zero(options)) {
1252 ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
1255 snprintf(args, sizeof(args), "%s,%s", file, options);
1257 ast_channel_lock(c);
1258 res = mixmonitor_exec(c, args);
1260 if (ast_test_flag(&flags, MUXFLAG_UID)) {
1261 uid_channel_var = opts[OPT_ARG_UID];
1262 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1264 ast_channel_unlock(c);
1267 c = ast_channel_unref(c);
1268 astman_send_error(s, m, "Could not start monitoring channel");
1272 astman_append(s, "Response: Success\r\n");
1274 if (!ast_strlen_zero(id)) {
1275 astman_append(s, "ActionID: %s\r\n", id);
1278 if (!ast_strlen_zero(mixmonitor_id)) {
1279 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1282 astman_append(s, "\r\n");
1284 c = ast_channel_unref(c);
1289 static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1291 struct ast_channel *c = NULL;
1293 const char *name = astman_get_header(m, "Channel");
1294 const char *id = astman_get_header(m, "ActionID");
1295 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1298 if (ast_strlen_zero(name)) {
1299 astman_send_error(s, m, "No channel specified");
1303 c = ast_channel_get_by_name(name);
1306 astman_send_error(s, m, "No such channel");
1310 res = stop_mixmonitor_exec(c, mixmonitor_id);
1313 astman_send_error(s, m, "Could not stop monitoring channel");
1317 astman_append(s, "Response: Success\r\n");
1319 if (!ast_strlen_zero(id)) {
1320 astman_append(s, "ActionID: %s\r\n", id);
1323 astman_append(s, "\r\n");
1325 c = ast_channel_unref(c);
1330 static struct ast_cli_entry cli_mixmonitor[] = {
1331 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1334 static int unload_module(void)
1338 ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1339 res = ast_unregister_application(stop_app);
1340 res |= ast_unregister_application(app);
1341 res |= ast_manager_unregister("MixMonitorMute");
1342 res |= ast_manager_unregister("MixMonitor");
1343 res |= ast_manager_unregister("StopMixMonitor");
1348 static int load_module(void)
1352 ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
1353 res = ast_register_application_xml(app, mixmonitor_exec);
1354 res |= ast_register_application_xml(stop_app, stop_mixmonitor_exec);
1355 res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
1356 res |= ast_manager_register_xml("MixMonitor", 0, manager_mixmonitor);
1357 res |= ast_manager_register_xml("StopMixMonitor", 0, manager_stop_mixmonitor);
1362 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");