Asterisk - The Open Source Telephony Project GIT-master-57e2743
Loading...
Searching...
No Matches
app_mixmonitor.c
Go to the documentation of this file.
1/*
2 * Asterisk -- An open source telephony toolkit.
3 *
4 * Copyright (C) 2005, Anthony Minessale II
5 * Copyright (C) 2005 - 2006, Digium, Inc.
6 *
7 * Mark Spencer <markster@digium.com>
8 * Kevin P. Fleming <kpfleming@digium.com>
9 *
10 * Based on app_muxmon.c provided by
11 * Anthony Minessale II <anthmct@yahoo.com>
12 *
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.
18 *
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.
22 */
23
24/*! \file
25 *
26 * \brief MixMonitor() - Record a call and mix the audio during the recording
27 * \ingroup applications
28 *
29 * \author Mark Spencer <markster@digium.com>
30 * \author Kevin P. Fleming <kpfleming@digium.com>
31 *
32 * \note Based on app_muxmon.c provided by
33 * Anthony Minessale II <anthmct@yahoo.com>
34 */
35
36/*** MODULEINFO
37 <use type="module">func_periodic_hook</use>
38 <support_level>core</support_level>
39 ***/
40
41#include "asterisk.h"
42
43#include "asterisk/paths.h" /* use ast_config_AST_MONITOR_DIR */
45#include "asterisk/file.h"
46#include "asterisk/audiohook.h"
47#include "asterisk/pbx.h"
48#include "asterisk/module.h"
49#include "asterisk/cli.h"
50#include "asterisk/app.h"
51#include "asterisk/channel.h"
52#include "asterisk/autochan.h"
53#include "asterisk/manager.h"
54#include "asterisk/stasis.h"
56#include "asterisk/callerid.h"
57#include "asterisk/mod_format.h"
59#include "asterisk/test.h"
60#include "asterisk/mixmonitor.h"
62#include "asterisk/beep.h"
63#include "asterisk/translate.h"
64
65/*** DOCUMENTATION
66 <application name="MixMonitor" language="en_US">
67 <since>
68 <version>1.2.0</version>
69 </since>
70 <synopsis>
71 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
72 to guarantee the audio file is available for processing during dialplan execution.
73 </synopsis>
74 <syntax>
75 <parameter name="file" required="true" argsep=".">
76 <argument name="filename" required="true">
77 <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
78 creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
79 </argument>
80 <argument name="extension" required="true" />
81 </parameter>
82 <parameter name="options">
83 <optionlist>
84 <option name="a">
85 <para>Append to the file instead of overwriting it.</para>
86 </option>
87 <option name="b">
88 <para>Only save audio to the file while the channel is bridged.</para>
89 <note><para>If you utilize this option inside a Local channel, you must make sure the Local
90 channel is not optimized away. To do this, be sure to call your Local channel with the
91 <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
92 </option>
93 <option name="B">
94 <para>Play a periodic beep while this call is being recorded.</para>
95 <argument name="interval"><para>Interval, in seconds. Default is 15.</para></argument>
96 </option>
97 <option name="c">
98 <para>Use the real Caller ID from the channel for the voicemail Caller ID.</para>
99 <para>By default, the Connected Line is used. If you want the channel caller's
100 real number, you may need to specify this option.</para>
101 </option>
102 <option name="d">
103 <para>Delete the recording file as soon as MixMonitor is done with it.</para>
104 <para>For example, if you use the m option to dispatch the recording to a voicemail box,
105 you can specify this option to delete the original copy of it afterwards.</para>
106 </option>
107 <option name="v">
108 <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
109 (range <literal>-4</literal> to <literal>4</literal>)</para>
110 <argument name="x" required="true" />
111 </option>
112 <option name="V">
113 <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
114 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
115 <argument name="x" required="true" />
116 </option>
117 <option name="W">
118 <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
119 of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
120 <argument name="x" required="true" />
121 </option>
122 <option name="r">
123 <argument name="file" required="true" />
124 <para>Use the specified file to record the <emphasis>receive</emphasis> audio feed.
125 Like with the basic filename argument, if an absolute path isn't given, it will create
126 the file in the configured monitoring directory.</para>
127 </option>
128 <option name="t">
129 <argument name="file" required="true" />
130 <para>Use the specified file to record the <emphasis>transmit</emphasis> audio feed.
131 Like with the basic filename argument, if an absolute path isn't given, it will create
132 the file in the configured monitoring directory.</para>
133 </option>
134 <option name="D">
135 <para>Interleave the audio coming from the channel and the audio
136 going to the channel and output it as a 2 channel (stereo)
137 raw stream rather than mixing it. You must use the
138 <literal>.raw</literal> file extension. Any other extension
139 will produce a corrupted file.</para>
140 </option>
141 <option name="n">
142 <para>When the <replaceable>r</replaceable> or <replaceable>t</replaceable> option is
143 used, MixMonitor will insert silence into the specified files to maintain
144 synchronization between them. Use this option to disable that behavior.</para>
145 </option>
146 <option name="i">
147 <argument name="chanvar" required="true" />
148 <para>Stores the MixMonitor's ID on this channel variable.</para>
149 </option>
150 <option name="p">
151 <para>Play a beep on the channel that starts the recording.</para>
152 </option>
153 <option name="P">
154 <para>Play a beep on the channel that stops the recording.</para>
155 </option>
156 <option name="m">
157 <argument name="mailbox" required="true" />
158 <para>Create a copy of the recording as a voicemail in the indicated <emphasis>mailbox</emphasis>(es)
159 separated by commas eg. m(1111@default,2222@default,...). Folders can be optionally specified using
160 the syntax: mailbox@context/folder</para>
161 </option>
162 <option name="s">
163 <argument name="seconds" required="true" />
164 <para>Don't record until <replaceable>seconds</replaceable> (can be fractional) have elapsed since MixMonitor was invoked.
165 No audio is written to the recording file during this time. If the call ends before this period,
166 no audio will be saved. This can be useful to avoid recording announcements,
167 ringback tones, or other non-essential early audio.</para>
168 </option>
169 </optionlist>
170 </parameter>
171 <parameter name="command">
172 <para>Will be executed when the recording is over.</para>
173 <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
174 <para>All variables will be evaluated at the time MixMonitor is called.</para>
175 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
176 or <variable>CALLERID(name)</variable> as part of the command parameters. You
177 risk a command injection attack executing arbitrary commands if the untrusted
178 strings aren't filtered to remove dangerous characters. See function
179 <variable>FILTER()</variable>.</para></warning>
180 </parameter>
181 </syntax>
182 <description>
183 <para>Records the audio on the current channel to the specified file.</para>
184 <para>This application does not automatically answer and should be preceded by
185 an application such as Answer or Progress().</para>
186 <note><para>MixMonitor runs as an audiohook.</para></note>
187 <note><para>If a filename passed to MixMonitor ends with
188 <literal>.wav49</literal>, Asterisk will silently convert the extension to
189 <literal>.WAV</literal> for legacy reasons. <variable>MIXMONITOR_FILENAME</variable>
190 will contain the actual filename that Asterisk is writing to, not necessarily the
191 value that was passed in.</para></note>
192 <variablelist>
193 <variable name="MIXMONITOR_FILENAME">
194 <para>Will contain the filename used to record.</para>
195 </variable>
196 </variablelist>
197 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
198 or <variable>CALLERID(name)</variable> as part of ANY of the application's
199 parameters. You risk a command injection attack executing arbitrary commands
200 if the untrusted strings aren't filtered to remove dangerous characters. See
201 function <variable>FILTER()</variable>.</para></warning>
202 <warning><para>When using the <literal>D</literal> option to save
203 interleaved audio, you MUST use <literal>.raw</literal> as the
204 file extension. Any other extension will produce a corrupted file.</para></warning>
205 </description>
206 <see-also>
207 <ref type="application">StopMixMonitor</ref>
208 </see-also>
209 </application>
210 <application name="StopMixMonitor" language="en_US">
211 <since>
212 <version>1.4.0</version>
213 </since>
214 <synopsis>
215 Stop recording a call through MixMonitor, and free the recording's file handle.
216 </synopsis>
217 <syntax>
218 <parameter name="MixMonitorID" required="false">
219 <para>If a valid ID is provided, then this command will stop only that specific
220 MixMonitor.</para>
221 </parameter>
222 </syntax>
223 <description>
224 <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
225 on the current channel.</para>
226 </description>
227 <see-also>
228 <ref type="application">MixMonitor</ref>
229 </see-also>
230 </application>
231 <manager name="MixMonitorMute" language="en_US">
232 <since>
233 <version>1.8.0</version>
234 </since>
235 <synopsis>
236 Mute / unMute a Mixmonitor recording.
237 </synopsis>
238 <syntax>
239 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
240 <parameter name="Channel" required="true">
241 <para>Used to specify the channel to mute.</para>
242 </parameter>
243 <parameter name="Direction">
244 <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
245 </parameter>
246 <parameter name="State">
247 <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
248 </parameter>
249 </syntax>
250 <description>
251 <para>This action may be used to mute a MixMonitor recording.</para>
252 </description>
253 </manager>
254 <manager name="MixMonitor" language="en_US">
255 <since>
256 <version>11.0.0</version>
257 </since>
258 <synopsis>
259 Record a call and mix the audio during the recording. Use of StopMixMonitor is required
260 to guarantee the audio file is available for processing during dialplan execution.
261 </synopsis>
262 <syntax>
263 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
264 <parameter name="Channel" required="true">
265 <para>Used to specify the channel to record.</para>
266 </parameter>
267 <parameter name="File">
268 <para>Is the name of the file created in the monitor spool directory.
269 Defaults to the same name as the channel (with slashes replaced with dashes).
270 This argument is optional if you specify to record unidirectional audio with
271 either the r(filename) or t(filename) options in the options field. If
272 neither MIXMONITOR_FILENAME or this parameter is set, the mixed stream won't
273 be recorded.</para>
274 </parameter>
275 <parameter name="Options">
276 <para>Options that apply to the MixMonitor in the same way as they
277 would apply if invoked from the MixMonitor application. For a list of
278 available options, see the documentation for the mixmonitor application. </para>
279 </parameter>
280 <parameter name="Command">
281 <para>Will be executed when the recording is over.
282 Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.
283 All variables will be evaluated at the time MixMonitor is called.</para>
284 <warning><para>Do not use untrusted strings such as <variable>CALLERID(num)</variable>
285 or <variable>CALLERID(name)</variable> as part of the command parameters. You
286 risk a command injection attack executing arbitrary commands if the untrusted
287 strings aren't filtered to remove dangerous characters. See function
288 <variable>FILTER()</variable>.</para></warning>
289 </parameter>
290 </syntax>
291 <description>
292 <para>This action records the audio on the current channel to the specified file.</para>
293 <variablelist>
294 <variable name="MIXMONITOR_FILENAME">
295 <para>Will contain the filename used to record the mixed stream.</para>
296 </variable>
297 </variablelist>
298 </description>
299 </manager>
300 <manager name="StopMixMonitor" language="en_US">
301 <since>
302 <version>11.0.0</version>
303 </since>
304 <synopsis>
305 Stop recording a call through MixMonitor, and free the recording's file handle.
306 </synopsis>
307 <syntax>
308 <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
309 <parameter name="Channel" required="true">
310 <para>The name of the channel monitored.</para>
311 </parameter>
312 <parameter name="MixMonitorID" required="false">
313 <para>If a valid ID is provided, then this command will stop only that specific
314 MixMonitor.</para>
315 </parameter>
316 </syntax>
317 <description>
318 <para>This action stops the audio recording that was started with the <literal>MixMonitor</literal>
319 action on the current channel.</para>
320 </description>
321 </manager>
322 <function name="MIXMONITOR" language="en_US">
323 <since>
324 <version>13.0.0</version>
325 </since>
326 <synopsis>
327 Retrieve data pertaining to specific instances of MixMonitor on a channel.
328 </synopsis>
329 <syntax>
330 <parameter name="id" required="true">
331 <para>The unique ID of the MixMonitor instance. The unique ID can be retrieved through the channel
332 variable used as an argument to the <replaceable>i</replaceable> option to MixMonitor.</para>
333 </parameter>
334 <parameter name="key" required="true">
335 <para>The piece of data to retrieve from the MixMonitor.</para>
336 <enumlist>
337 <enum name="filename" />
338 </enumlist>
339 </parameter>
340 </syntax>
341 </function>
342 <managerEvent language="en_US" name="MixMonitorStart">
343 <managerEventInstance class="EVENT_FLAG_CALL">
344 <since>
345 <version>16.17.0</version>
346 <version>18.3.0</version>
347 </since>
348 <synopsis>Raised when monitoring has started on a channel.</synopsis>
349 <syntax>
350 <channel_snapshot/>
351 </syntax>
352 <see-also>
353 <ref type="managerEvent">MixMonitorStop</ref>
354 <ref type="application">MixMonitor</ref>
355 <ref type="manager">MixMonitor</ref>
356 </see-also>
357 </managerEventInstance>
358 </managerEvent>
359 <managerEvent language="en_US" name="MixMonitorStop">
360 <managerEventInstance class="EVENT_FLAG_CALL">
361 <since>
362 <version>16.17.0</version>
363 <version>18.3.0</version>
364 </since>
365 <synopsis>Raised when monitoring has stopped on a channel.</synopsis>
366 <syntax>
367 <channel_snapshot/>
368 </syntax>
369 <see-also>
370 <ref type="managerEvent">MixMonitorStart</ref>
371 <ref type="application">StopMixMonitor</ref>
372 <ref type="manager">StopMixMonitor</ref>
373 </see-also>
374 </managerEventInstance>
375 </managerEvent>
376 <managerEvent language="en_US" name="MixMonitorMute">
377 <managerEventInstance class="EVENT_FLAG_CALL">
378 <since>
379 <version>16.17.0</version>
380 <version>18.3.0</version>
381 </since>
382 <synopsis>Raised when monitoring is muted or unmuted on a channel.</synopsis>
383 <syntax>
384 <channel_snapshot/>
385 <parameter name="Direction">
386 <para>Which part of the recording was muted or unmuted: read, write or both
387 (from channel, to channel or both directions).</para>
388 </parameter>
389 <parameter name="State">
390 <para>If the monitoring was muted or unmuted: 1 when muted, 0 when unmuted.</para>
391 </parameter>
392 </syntax>
393 <see-also>
394 <ref type="manager">MixMonitorMute</ref>
395 </see-also>
396 </managerEventInstance>
397 </managerEvent>
398
399
400 ***/
401
402#define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
403
404#define MIN_SKIP_SECONDS 1
405
406static const char * const app = "MixMonitor";
407
408static const char * const stop_app = "StopMixMonitor";
409
410static const char * const mixmonitor_spy_type = "MixMonitor";
411
412/*!
413 * \internal
414 * \brief This struct is a list item holds data needed to find a vm_recipient within voicemail
415 */
422
425 char *filename;
429 char *name;
431 unsigned int flags;
434
435 /* the below string fields describe data used for creating voicemails from the recording */
441 );
443
444 /* Number of seconds (can be fractional) to skip at the start of recording */
446
447 /* FUTURE DEVELOPMENT NOTICE
448 * recipient_list will need locks if we make it editable after the monitor is started */
450};
451
473
488
508});
509
511 unsigned int destruction_ok;
514
515 /* The filestream is held in the datastore so it can be stopped
516 * immediately during stop_mixmonitor or channel destruction. */
518
522
525
526 unsigned int samp_rate;
527 char *filename;
528 char *beep_id;
529};
530
531/*!
532 * \internal
533 * \pre mixmonitor_ds must be locked before calling this function
534 */
536{
537 unsigned char quitting = 0;
538
539 if (mixmonitor_ds->fs) {
540 quitting = 1;
543 ast_verb(2, "MixMonitor close filestream (mixed)\n");
544 }
545
546 if (mixmonitor_ds->fs_read) {
547 quitting = 1;
550 ast_verb(2, "MixMonitor close filestream (read)\n");
551 }
552
553 if (mixmonitor_ds->fs_write) {
554 quitting = 1;
557 ast_verb(2, "MixMonitor close filestream (write)\n");
558 }
559
560 if (quitting) {
562 }
563}
564
578
580 .type = "mixmonitor",
581 .destroy = mixmonitor_ds_destroy,
582};
583
597
598static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
599{
600 if (!chan) {
601 return -1;
602 }
603
604 return ast_audiohook_attach(chan, audiohook);
605}
606
607/*!
608 * \internal
609 * \brief adds recipients to a mixmonitor's recipient list
610 * \param mixmonitor mixmonitor being affected
611 * \param vm_recipients string containing the desired recipients to add
612 */
613static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
614{
615 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
616 char *cur_mailbox = ast_strdupa(vm_recipients);
617 char *cur_context;
618 char *cur_folder;
619 char *next;
620 int elements_processed = 0;
621
622 while (!ast_strlen_zero(cur_mailbox)) {
623 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
624 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
625 *(next++) = '\0';
626 }
627
628 if ((cur_folder = strchr(cur_mailbox, '/'))) {
629 *(cur_folder++) = '\0';
630 } else {
631 cur_folder = "INBOX";
632 }
633
634 if ((cur_context = strchr(cur_mailbox, '@'))) {
635 *(cur_context++) = '\0';
636 } else {
637 cur_context = "default";
638 }
639
640 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
641 struct vm_recipient *recipient;
642 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
643 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
644 return;
645 }
646 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
647 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
648 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
649
650 /* Add to list */
651 ast_verb(4, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
653 } else {
654 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
655 }
656
657 cur_mailbox = next;
658 elements_processed++;
659 }
660}
661
663{
664 struct vm_recipient *current;
666 /* Clear list element data */
668 }
669}
670
671#define SAMPLES_PER_FRAME 160
672
673/* This will allocate and free the translator path as needed so it can be re-used on subsequent calls */
674static void fill_frame_buffer(struct ast_frame *source_frame,
675 struct ast_format *target_format,
676 struct ast_format **last_source_format,
678 short *dest_buf,
679 size_t dest_buf_size,
680 const char *direction)
681{
682 struct ast_frame *converted_frame = NULL;
683
684 if (!source_frame) {
685 memset(dest_buf, 0, dest_buf_size);
686 return;
687 }
688
689 /*
690 * We only need to worry about translating if the frame format does not match
691 * the expected format of the frame we are writing.
692 */
693 if (ast_format_cmp(target_format, source_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
694 /*
695 * If the format changed from the last frame or if this is the first frame
696 * that does not match the native format, we need to set up a new
697 * translator path.
698 */
699 if (ast_format_cmp(*last_source_format, source_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL
700 || !*translator_path) {
701 ast_debug(3, "%s frame format changed from %s to %s, building translator path to %s\n",
702 direction,
703 *last_source_format ? ast_format_get_name(*last_source_format) : "none",
704 ast_format_get_name(source_frame->subclass.format),
705 ast_format_get_name(target_format));
706
707 if (*translator_path) {
709 }
710
711 *translator_path = ast_translator_build_path(target_format, source_frame->subclass.format);
712 }
713
714 if (*translator_path) {
715 converted_frame = ast_translate(*translator_path, source_frame, 0);
716 }
717
718 /*
719 * If creating the translated frame or translator path failed, write silence for this frame. We will
720 * try rebuilding the translator path later if it is still needed.
721 */
722 if (converted_frame) {
723 memcpy(dest_buf, converted_frame->data.ptr, dest_buf_size);
724 ast_frame_free(converted_frame, 1);
725 } else {
726 memset(dest_buf, 0, dest_buf_size);
727 }
728 } else {
729 memcpy(dest_buf, source_frame->data.ptr, dest_buf_size);
730
731 /*
732 * If we are doing native frame copying, we can free the translator path if
733 * it exists. We will create a new one later if needed.
734 */
735 if (*translator_path) {
738 ast_debug(3, "%s frame format changed from %s to %s, translator path no longer needed\n",
739 direction,
740 *last_source_format ? ast_format_get_name(*last_source_format) : "none",
741 ast_format_get_name(source_frame->subclass.format));
742 }
743 }
744
745 *last_source_format = source_frame->subclass.format;
746}
747
772
773/*!
774 * \internal
775 * \brief Copies the mixmonitor to all voicemail recipients
776 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
777 * \param ext Format of the file that was saved
778 * \param filename
779 */
780static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
781{
782 struct vm_recipient *recipient = NULL;
785 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
786 return;
787 }
788
789 /* Copy strings to stringfields that will be used for all recipients */
796 /* and call_priority gets copied too */
797 recording_data.call_priority = mixmonitor->call_priority;
798
799 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
800 /* context, mailbox, and folder need to be set per recipient */
804
805 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
806 recording_data.context);
808 }
809
810 /* Free the string fields for recording_data before exiting the function. */
812}
813
814static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
815{
816 /* Initialize the file if not already done so */
817 char *last_slash = NULL;
818 if (!ast_strlen_zero(filename)) {
819 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
820 *oflags = O_CREAT | O_WRONLY;
821 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
822
823 last_slash = strrchr(filename, '/');
824
825 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
826 **ext = '\0';
827 *ext = *ext + 1;
828 } else {
829 *ext = "raw";
830 }
831
832 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
833 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
834 *errflag = 1;
835 } else {
836 struct ast_filestream *tmp = *fs;
838 }
839 }
840 }
841}
842
844{
845 int is_bridged;
846
848 is_bridged = ast_channel_is_bridged(autochan->chan);
850 return is_bridged;
851}
852
853/* Forward declaration */
854static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id);
855
856static void *mixmonitor_thread(void *obj)
857{
858 struct mixmonitor *mixmonitor = obj;
859 char *fs_ext = "";
860 char *fs_read_ext = "";
861 char *fs_write_ext = "";
862
863 struct ast_filestream **fs = NULL;
864 struct ast_filestream **fs_read = NULL;
865 struct ast_filestream **fs_write = NULL;
866
867 struct ast_format *last_format_read = NULL;
868 struct ast_format *last_format_write = NULL;
869 struct ast_trans_pvt *trans_pvt_read = NULL;
870 struct ast_trans_pvt *trans_pvt_write = NULL;
871
872 unsigned int oflags;
873 int errflag = 0;
874 struct ast_format *format_slin;
875
876 struct timeval skip_start = ast_tvnow();
877
878 /* Keep callid association before any log messages */
879 if (mixmonitor->callid) {
881 }
882
884 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
885 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
886
888 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
889 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
890 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
891
893
894 if (errflag) {
895 /* Tear everything down and exit. */
898
900
903
907
911 }
913
916
918 return NULL;
919 }
920
921 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
922
924
925 if (mixmonitor->skip_seconds > 0.0) {
926 ast_debug(3, "%s skipping initial %.3f seconds\n",
928 }
929
930 /* The audiohook must enter and exit the loop locked */
933 struct ast_frame *fr = NULL;
934 struct ast_frame *fr_read = NULL;
935 struct ast_frame *fr_write = NULL;
936
938 &fr_read, &fr_write))) {
940
942 break;
943 }
944 continue;
945 }
946
947 /* audiohook lock is not required for the next block.
948 * Unlock it, but remember to lock it before looping or exiting */
950
954
955 /* Skip writing audio for the first N seconds */
956 if (mixmonitor->skip_seconds > 0.0) {
957 struct timeval now = ast_tvnow();
958 double elapsed = ast_tvdiff_ms(now, skip_start) / 1000.0;
959
960 if (elapsed < mixmonitor->skip_seconds) {
962 /* Skip this frame and continue */
963 goto frame_cleanup;
964 } else {
965 ast_debug(3, "%s skip period %.3f seconds elapsed; starting to write audio\n",
968 }
969 }
970
971 /* Write out the frame(s) */
972 if ((*fs_read) && (fr_read)) {
973 struct ast_frame *cur;
974
975 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
976 ast_writestream(*fs_read, cur);
977 }
978 }
979
980 if ((*fs_write) && (fr_write)) {
981 struct ast_frame *cur;
982
983 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
984 ast_writestream(*fs_write, cur);
985 }
986 }
987
989 /* The 'D' option is set, so mix the frame as an interleaved dual channel frame */
990 int i;
991 /*
992 * We are fed by a call to ast_audiohook_read_frame_all specifying format_slin. However
993 * we may get frames back in a different format. Either way, we we will translate them
994 * to the format with the correct frame size before writing into these buffers.
995 */
996 short read_buf[SAMPLES_PER_FRAME];
998 short stereo_buf[SAMPLES_PER_FRAME * 2];
999 struct ast_frame stereo_frame = {
1001 .datalen = sizeof(stereo_buf),
1003 };
1004
1005 if (fr) {
1006 ast_frame_free(fr, 0);
1007 fr = NULL;
1008 }
1009
1010 /*
1011 * Depending on the input codec's rate (which may change during the call) we may get frames in
1012 * a slin format that does not match the native format_slin of the mixmonitor. In that case
1013 * we need to translate.
1014 */
1015 fill_frame_buffer(fr_read, format_slin, &last_format_read, &trans_pvt_read,
1016 read_buf, sizeof(read_buf), "Read");
1017
1018 fill_frame_buffer(fr_write, format_slin, &last_format_write, &trans_pvt_write,
1019 write_buf, sizeof(write_buf), "Write");
1020
1021 for (i = 0; i < SAMPLES_PER_FRAME; i++) {
1022 stereo_buf[i * 2] = read_buf[i];
1023 stereo_buf[i * 2 + 1] = write_buf[i];
1024 }
1025
1026 stereo_frame.data.ptr = stereo_buf;
1027 stereo_frame.subclass.format = format_slin;
1028
1029 fr = ast_frdup(&stereo_frame);
1030 }
1031
1032 if ((*fs) && (fr)) {
1033 struct ast_frame *cur;
1034
1035 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
1036 ast_writestream(*fs, cur);
1037 }
1038 }
1040 }
1041
1042frame_cleanup:
1043 /* All done! free it. */
1044 if (fr) {
1045 ast_frame_free(fr, 0);
1046 }
1047 if (fr_read) {
1048 ast_frame_free(fr_read, 0);
1049 }
1050 if (fr_write) {
1051 ast_frame_free(fr_write, 0);
1052 }
1053
1054 fr = NULL;
1055 fr_write = NULL;
1056 fr_read = NULL;
1057
1059 }
1060
1062
1067 }
1068
1070
1071 /* Datastore cleanup. close the filestream and wait for ds destruction */
1076 }
1077
1079
1080 /* Free the translate paths */
1081 if (trans_pvt_read) {
1082 ast_translator_free_path(trans_pvt_read);
1083 trans_pvt_read = NULL;
1084 }
1085 if (trans_pvt_write) {
1086 ast_translator_free_path(trans_pvt_write);
1087 trans_pvt_write = NULL;
1088 }
1089
1090 /* kill the audiohook */
1092
1093 if (mixmonitor->post_process) {
1094 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
1096 }
1097
1098 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
1099 ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
1100
1102 if (ast_strlen_zero(fs_ext)) {
1103 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
1104 mixmonitor -> name);
1105 } else {
1106 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
1108 }
1109 if (!ast_strlen_zero(fs_read_ext)) {
1110 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
1112 }
1113 if (!ast_strlen_zero(fs_write_ext)) {
1114 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
1116 }
1117 } else {
1118 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
1119 }
1120
1122 ast_debug(3, "Deleting our copies of recording files\n");
1123 if (!ast_strlen_zero(fs_ext)) {
1125 }
1126 if (!ast_strlen_zero(fs_read_ext)) {
1128 }
1129 if (!ast_strlen_zero(fs_write_ext)) {
1131 }
1132 }
1133
1135
1137 return NULL;
1138}
1139
1140static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
1141{
1142 struct ast_datastore *datastore = NULL;
1144
1145 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
1146 return -1;
1147 }
1148
1149 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
1150 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
1152 return -1;
1153 }
1154
1157
1158 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
1162 return -1;
1163 }
1164
1169 }
1170
1171 mixmonitor_ds->samp_rate = 8000;
1174 if (!ast_strlen_zero(beep_id)) {
1176 }
1177 datastore->data = mixmonitor_ds;
1178
1179 ast_channel_lock(chan);
1180 ast_channel_datastore_add(chan, datastore);
1181 ast_channel_unlock(chan);
1182
1184 return 0;
1185}
1186
1187static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
1188{
1189 struct ast_datastore *datastore;
1190
1191 ast_channel_lock(chan);
1192
1193 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, datastore_id);
1194
1195 if (datastore) {
1196 ast_channel_datastore_remove(chan, datastore);
1197 ast_datastore_free(datastore);
1198 }
1199
1200 ast_channel_unlock(chan);
1201}
1202
1203static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
1204 unsigned int flags, int readvol, int writevol,
1205 const char *post_process, const char *filename_write,
1206 char *filename_read, const char *uid_channel_var,
1207 const char *recipients, const char *beep_id, double skip_seconds)
1208{
1209 pthread_t thread;
1210 struct mixmonitor *mixmonitor;
1211 char postprocess2[1024] = "";
1212 char *datastore_id = NULL;
1213
1214 postprocess2[0] = 0;
1215 /* If a post process system command is given attach it to the structure */
1217 char *p1, *p2;
1218
1220 for (p2 = p1; *p2; p2++) {
1221 if (*p2 == '^' && *(p2+1) == '{') {
1222 *p2 = '$';
1223 }
1224 }
1225 ast_channel_lock(chan);
1226 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
1227 ast_channel_unlock(chan);
1228 }
1229
1230 /* Pre-allocate mixmonitor structure and spy */
1231 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
1232 return -1;
1233 }
1234
1235 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
1238 return -1;
1239 }
1240
1241 /* Setup the actual spy before creating our thread */
1244 return -1;
1245 }
1246
1247 /* Copy over flags and channel name */
1250 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
1252 return -1;
1253 }
1254
1255 if (!ast_strlen_zero(filename)) {
1257 }
1258
1261 }
1262
1265 }
1266
1267 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1270 ast_free(datastore_id);
1271 return -1;
1272 }
1273
1274 if (!ast_strlen_zero(uid_channel_var)) {
1275 if (datastore_id) {
1276 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
1277 }
1278 }
1279
1281
1282 if (!ast_strlen_zero(postprocess2)) {
1283 mixmonitor->post_process = ast_strdup(postprocess2);
1284 }
1285
1286 if (!ast_strlen_zero(recipients)) {
1287 char callerid[256];
1288
1289 ast_channel_lock(chan);
1290
1291 /* We use the connected line of the invoking channel for caller ID,
1292 * unless we've been told to use the Caller ID.
1293 * The initial use for this relied on Connected Line to get the
1294 * actual number for recording with Digium phones,
1295 * but in generic use the Caller ID is likely what people want.
1296 */
1297
1299 struct ast_party_caller *caller;
1300 caller = ast_channel_caller(chan);
1301 ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
1302 caller->id.name.str, caller->id.number.valid,
1303 caller->id.number.str);
1304 ast_callerid_merge(callerid, sizeof(callerid),
1305 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
1306 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
1307 "Unknown");
1308 } else {
1311 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
1312 connected->id.name.str, connected->id.number.valid,
1313 connected->id.number.str);
1314 ast_callerid_merge(callerid, sizeof(callerid),
1315 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
1316 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
1317 "Unknown");
1318 }
1319
1321 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
1322 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
1323 ast_string_field_set(mixmonitor, call_callerid, callerid);
1325
1326 ast_channel_unlock(chan);
1327
1329 }
1330
1334 }
1335
1336 if (readvol)
1338 if (writevol)
1340
1341 if (startmon(chan, &mixmonitor->audiohook)) {
1342 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
1344 mixmonitor_ds_remove_and_free(chan, datastore_id);
1345 ast_free(datastore_id);
1349 return -1;
1350 }
1351
1352 mixmonitor->mixmonitor_ds->datastore_id = datastore_id; /* Steal the reference */
1353
1354 /* reference be released at mixmonitor destruction */
1356
1358}
1359
1360/* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
1361/* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
1362static char *filename_parse(char *filename, char *buffer, size_t len)
1363{
1364 char *slash;
1365 char *ext;
1366
1367 ast_assert(len > 0);
1368
1369 if (ast_strlen_zero(filename)) {
1370 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
1371 buffer[0] = 0;
1372 return buffer;
1373 }
1374
1375 /* If we don't have an absolute path, make one */
1376 if (*filename != '/') {
1377 char *build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1378 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
1379 filename = build;
1380 }
1381
1382 ast_copy_string(buffer, filename, len);
1383
1384 /* If the provided filename has a .wav49 extension, we need to convert it to .WAV to
1385 match the behavior of build_filename in main/file.c. Otherwise MIXMONITOR_FILENAME
1386 ends up referring to a file that does not/will not exist */
1387 ext = strrchr(buffer, '.');
1388 if (ext && !strcmp(ext, ".wav49")) {
1389 /* Change to WAV - we know we have at least 6 writeable bytes where 'ext' points,
1390 * so this is safe */
1391 memcpy(ext, ".WAV", sizeof(".WAV"));
1392 }
1393
1394 if ((slash = strrchr(filename, '/'))) {
1395 *slash = '\0';
1396 }
1397 ast_mkdir(filename, 0777);
1398
1399 return buffer;
1400}
1401
1402static int mixmonitor_exec(struct ast_channel *chan, const char *data)
1403{
1404 int x, readvol = 0, writevol = 0;
1405 double skip_seconds = 0.0;
1406 char *filename_read = NULL;
1407 char *filename_write = NULL;
1408 char filename_buffer[1024] = "";
1409 char *uid_channel_var = NULL;
1410 char beep_id[64] = "";
1411
1412 struct ast_flags flags = { 0 };
1413 char *recipients = NULL;
1414 char *parse;
1417 AST_APP_ARG(filename);
1419 AST_APP_ARG(post_process);
1420 );
1421
1422 if (ast_strlen_zero(data)) {
1423 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1424 return -1;
1425 }
1426
1427 parse = ast_strdupa(data);
1428
1430
1431 if (args.options) {
1432 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1433
1435
1437 ast_log(LOG_NOTICE, "The synchronization behavior enabled by the 'S' option is now the default"
1438 " and does not need to be specified.\n");
1439 }
1440
1443 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
1444 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1445 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1446 } else {
1447 readvol = get_volfactor(x);
1448 }
1449 }
1450
1453 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
1454 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1455 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1456 } else {
1457 writevol = get_volfactor(x);
1458 }
1459 }
1460
1462 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1463 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
1464 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1465 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1466 } else {
1467 readvol = writevol = get_volfactor(x);
1468 }
1469 }
1470
1473 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1474 } else {
1475 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1476 }
1477 }
1478
1480 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1481 }
1482
1484 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1485 }
1486
1488 uid_channel_var = opts[OPT_ARG_UID];
1489 }
1490
1492 const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
1493 unsigned int interval = 15;
1494
1495 if (sscanf(interval_str, "%30u", &interval) != 1) {
1496 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
1497 interval_str, interval);
1498 }
1499
1500 if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
1501 ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1502 return -1;
1503 }
1504 }
1505
1507 if (ast_strlen_zero(opts[OPT_ARG_SKIP])) {
1508 ast_log(LOG_WARNING, "No skip value provided for the 's' (skip) option; skipping will be ignored as no default exists.\n");
1509 } else {
1510 char *endptr = NULL;
1511 double val = strtod(opts[OPT_ARG_SKIP], &endptr);
1512 if (endptr == opts[OPT_ARG_SKIP] || *endptr != '\0') {
1513 ast_log(LOG_WARNING, "Skip value '%s' is not a valid number; ignoring skip.\n", opts[OPT_ARG_SKIP]);
1514 } else if (val < (double) MIN_SKIP_SECONDS) {
1515 ast_log(LOG_WARNING, "Skip value %.3f is below minimum %d; ignoring skip.\n", val, MIN_SKIP_SECONDS);
1516 } else {
1517 skip_seconds = val;
1518 }
1519 }
1520 }
1521 }
1522 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1523
1525 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1526 return -1;
1527 }
1528
1529 /* If filename exists, try to create directories for it */
1530 if (!(ast_strlen_zero(args.filename))) {
1531 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1532 }
1533
1534 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1535
1536 /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
1538 if (launch_monitor_thread(chan,
1539 args.filename,
1540 flags.flags,
1541 readvol,
1542 writevol,
1543 args.post_process,
1544 filename_write,
1545 filename_read,
1546 uid_channel_var,
1547 recipients,
1548 beep_id,
1549 skip_seconds)) {
1551 }
1552
1555 if (message) {
1557 }
1558
1559 return 0;
1560}
1561
1562static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1563{
1564 struct ast_datastore *datastore = NULL;
1565 char *parse = "";
1567 const char *beep_id = NULL;
1569
1571 AST_APP_ARG(mixmonid);
1572 );
1573
1574 if (!ast_strlen_zero(data)) {
1575 parse = ast_strdupa(data);
1576 }
1577
1579
1580 ast_channel_lock(chan);
1581
1583 S_OR(args.mixmonid, NULL));
1584 if (!datastore) {
1585 ast_channel_unlock(chan);
1586 return -1;
1587 }
1588 mixmonitor_ds = datastore->data;
1589
1591
1592 /* closing the filestream here guarantees the file is available to the dialplan
1593 * after calling StopMixMonitor */
1595
1596 /* The mixmonitor thread may be waiting on the audiohook trigger.
1597 * In order to exit from the mixmonitor loop before waiting on channel
1598 * destruction, poke the audiohook trigger. */
1599 if (mixmonitor_ds->audiohook) {
1602 }
1607 }
1608
1611 }
1612
1614
1615 /* Remove the datastore so the monitor thread can exit */
1616 if (!ast_channel_datastore_remove(chan, datastore)) {
1617 ast_datastore_free(datastore);
1618 }
1619
1620 ast_channel_unlock(chan);
1621
1622 if (!ast_strlen_zero(beep_id)) {
1623 ast_beep_stop(chan, beep_id);
1624 }
1625
1628 NULL);
1629 if (message) {
1631 }
1632
1633 return 0;
1634}
1635
1636static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1637{
1638 stop_mixmonitor_full(chan, data);
1639 return 0;
1640}
1641
1642static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1643{
1644 struct ast_channel *chan;
1645 struct ast_datastore *datastore = NULL;
1647
1648 switch (cmd) {
1649 case CLI_INIT:
1650 e->command = "mixmonitor {start|stop|list}";
1651 e->usage =
1652 "Usage: mixmonitor start <chan_name> [args]\n"
1653 " The optional arguments are passed to the MixMonitor application.\n"
1654 " mixmonitor stop <chan_name> [args]\n"
1655 " The optional arguments are passed to the StopMixMonitor application.\n"
1656 " mixmonitor list <chan_name>\n";
1657 return NULL;
1658 case CLI_GENERATE:
1659 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1660 }
1661
1662 if (a->argc < 3) {
1663 return CLI_SHOWUSAGE;
1664 }
1665
1666 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1667 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1668 /* Technically this is a failure, but we don't want 2 errors printing out */
1669 return CLI_SUCCESS;
1670 }
1671
1672 if (!strcasecmp(a->argv[1], "start")) {
1673 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1674 } else if (!strcasecmp(a->argv[1], "stop")){
1675 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1676 } else if (!strcasecmp(a->argv[1], "list")) {
1677 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1678 ast_cli(a->fd, "=========================================================================\n");
1679 ast_channel_lock(chan);
1680 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1681 if (datastore->info == &mixmonitor_ds_info) {
1682 char *filename = "";
1683 char *filename_read = "";
1684 char *filename_write = "";
1685
1686 mixmonitor_ds = datastore->data;
1687 if (mixmonitor_ds->fs) {
1689 }
1690 if (mixmonitor_ds->fs_read) {
1691 filename_read = mixmonitor_ds->fs_read->filename;
1692 }
1693 if (mixmonitor_ds->fs_write) {
1694 filename_write = mixmonitor_ds->fs_write->filename;
1695 }
1696 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1697 }
1698 }
1699 ast_channel_unlock(chan);
1700 } else {
1701 chan = ast_channel_unref(chan);
1702 return CLI_SHOWUSAGE;
1703 }
1704
1705 chan = ast_channel_unref(chan);
1706
1707 return CLI_SUCCESS;
1708}
1709
1710/*! \brief Mute / unmute an individual MixMonitor by id */
1711static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
1712 enum ast_audiohook_flags flag, int clearmute)
1713{
1714 struct ast_datastore *datastore = NULL;
1715 char *parse = "";
1717
1719 AST_APP_ARG(mixmonid);
1720 );
1721
1722 if (!ast_strlen_zero(data)) {
1723 parse = ast_strdupa(data);
1724 }
1725
1727
1728 ast_channel_lock(chan);
1729
1731 S_OR(args.mixmonid, NULL));
1732 if (!datastore) {
1733 ast_channel_unlock(chan);
1734 return -1;
1735 }
1736 mixmonitor_ds = datastore->data;
1737
1739
1740 if (mixmonitor_ds->audiohook) {
1741 if (clearmute) {
1743 } else {
1745 }
1746 }
1747
1749 ast_channel_unlock(chan);
1750
1751 return 0;
1752}
1753
1754/*! \brief Mute / unmute a MixMonitor channel */
1755static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1756{
1757 struct ast_channel *c;
1758 const char *name = astman_get_header(m, "Channel");
1759 const char *id = astman_get_header(m, "ActionID");
1760 const char *state = astman_get_header(m, "State");
1761 const char *direction = astman_get_header(m,"Direction");
1762 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1763 int clearmute = 1, mutedcount = 0;
1766 RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
1767
1769 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1770 return AMI_SUCCESS;
1771 }
1772
1773 if (!strcasecmp(direction, "read")) {
1775 } else if (!strcasecmp(direction, "write")) {
1777 } else if (!strcasecmp(direction, "both")) {
1779 } else {
1780 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1781 return AMI_SUCCESS;
1782 }
1783
1784 if (ast_strlen_zero(name)) {
1785 astman_send_error(s, m, "No channel specified");
1786 return AMI_SUCCESS;
1787 }
1788
1789 if (ast_strlen_zero(state)) {
1790 astman_send_error(s, m, "No state specified");
1791 return AMI_SUCCESS;
1792 }
1793
1794 clearmute = ast_false(state);
1795
1797 if (!c) {
1798 astman_send_error(s, m, "No such channel");
1799 return AMI_SUCCESS;
1800 }
1801
1802 if (ast_strlen_zero(mixmonitor_id)) {
1803 mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
1804 if (mutedcount < 0) {
1806 astman_send_error(s, m, "Cannot set mute flag");
1807 return AMI_SUCCESS;
1808 }
1809 } else {
1810 if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
1812 astman_send_error(s, m, "Cannot set mute flag");
1813 return AMI_SUCCESS;
1814 }
1815 mutedcount = 1;
1816 }
1817
1818
1819 stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
1820 "direction", direction,
1821 "state", ast_true(state),
1822 "mixmonitorid", mixmonitor_id,
1823 "count", mutedcount);
1824
1826 ast_channel_mixmonitor_mute_type(), stasis_message_blob);
1827
1828 if (stasis_message) {
1830 }
1831
1832 astman_append(s, "Response: Success\r\n");
1833
1834 if (!ast_strlen_zero(id)) {
1835 astman_append(s, "ActionID: %s\r\n", id);
1836 }
1837
1838 astman_append(s, "\r\n");
1839
1841
1842 return AMI_SUCCESS;
1843}
1844
1845static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
1846{
1847 char args[PATH_MAX];
1848
1849 if (ast_strlen_zero(options)) {
1850 snprintf(args, sizeof(args), "%s", filename);
1851 } else {
1852 snprintf(args, sizeof(args), "%s,%s", filename, options);
1853 }
1854
1855 return mixmonitor_exec(chan, args);
1856}
1857
1858static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
1859{
1860 return stop_mixmonitor_full(chan, mixmonitor_id);
1861}
1862
1863static int manager_mixmonitor(struct mansession *s, const struct message *m)
1864{
1865 struct ast_channel *c;
1866 const char *name = astman_get_header(m, "Channel");
1867 const char *id = astman_get_header(m, "ActionID");
1868 const char *file = astman_get_header(m, "File");
1869 const char *options = astman_get_header(m, "Options");
1870 const char *command = astman_get_header(m, "Command");
1871 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1872 struct ast_flags flags = { 0 };
1873 char *uid_channel_var = NULL;
1874 const char *mixmonitor_id = NULL;
1875 int res;
1876 char args[PATH_MAX];
1877
1878 if (ast_strlen_zero(name)) {
1879 astman_send_error(s, m, "No channel specified");
1880 return AMI_SUCCESS;
1881 }
1882
1884 if (!c) {
1885 astman_send_error(s, m, "No such channel");
1886 return AMI_SUCCESS;
1887 }
1888
1889 if (!ast_strlen_zero(options)) {
1891 }
1892
1893 snprintf(args, sizeof(args), "%s,%s,%s", file, options, command);
1894
1895 res = mixmonitor_exec(c, args);
1896
1898 uid_channel_var = opts[OPT_ARG_UID];
1900 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1901 mixmonitor_id = ast_strdupa(S_OR(mixmonitor_id, ""));
1903 }
1904
1905 if (res) {
1907 astman_send_error(s, m, "Could not start monitoring channel");
1908 return AMI_SUCCESS;
1909 }
1910
1911 astman_append(s, "Response: Success\r\n");
1912
1913 if (!ast_strlen_zero(id)) {
1914 astman_append(s, "ActionID: %s\r\n", id);
1915 }
1916
1917 if (!ast_strlen_zero(mixmonitor_id)) {
1918 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1919 }
1920
1921 astman_append(s, "\r\n");
1922
1924
1925 return AMI_SUCCESS;
1926}
1927
1928static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1929{
1930 struct ast_channel *c;
1931 const char *name = astman_get_header(m, "Channel");
1932 const char *id = astman_get_header(m, "ActionID");
1933 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1934 int res;
1935
1936 if (ast_strlen_zero(name)) {
1937 astman_send_error(s, m, "No channel specified");
1938 return AMI_SUCCESS;
1939 }
1940
1942 if (!c) {
1943 astman_send_error(s, m, "No such channel");
1944 return AMI_SUCCESS;
1945 }
1946
1947 res = stop_mixmonitor_full(c, mixmonitor_id);
1948 if (res) {
1950 astman_send_error(s, m, "Could not stop monitoring channel");
1951 return AMI_SUCCESS;
1952 }
1953
1954 astman_append(s, "Response: Success\r\n");
1955
1956 if (!ast_strlen_zero(id)) {
1957 astman_append(s, "ActionID: %s\r\n", id);
1958 }
1959
1960 astman_append(s, "\r\n");
1961
1963
1964 return AMI_SUCCESS;
1965}
1966
1967static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data,
1968 char *buf, size_t len)
1969{
1970 struct ast_datastore *datastore;
1971 struct mixmonitor_ds *ds_data;
1973 AST_APP_ARG(id);
1974 AST_APP_ARG(key);
1975 );
1976
1978
1979 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1980 ast_log(LOG_WARNING, "Not enough arguments provided to %s. "
1981 "An ID and key must be provided\n", cmd);
1982 return -1;
1983 }
1984
1985 ast_channel_lock(chan);
1986 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.id);
1987 ast_channel_unlock(chan);
1988
1989 if (!datastore) {
1990 ast_log(LOG_WARNING, "Could not find MixMonitor with ID %s\n", args.id);
1991 return -1;
1992 }
1993
1994 ds_data = datastore->data;
1995
1996 if (!strcasecmp(args.key, "filename")) {
1997 ast_copy_string(buf, ds_data->filename, len);
1998 } else {
1999 ast_log(LOG_WARNING, "Unrecognized %s option %s\n", cmd, args.key);
2000 return -1;
2001 }
2002 return 0;
2003}
2004
2006 .name = "MIXMONITOR",
2007 .read = func_mixmonitor_read,
2008};
2009
2011 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
2012};
2013
2023
2025{
2027}
2028
2029static int unload_module(void)
2030{
2031 int res;
2032
2036 res |= ast_manager_unregister("MixMonitorMute");
2037 res |= ast_manager_unregister("MixMonitor");
2038 res |= ast_manager_unregister("StopMixMonitor");
2041
2042 return res;
2043}
2044
2060
2061AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mixed Audio Monitoring Application",
2062 .support_level = AST_MODULE_SUPPORT_CORE,
2063 .load = load_module,
2064 .unload = unload_module,
2065 .optional_modules = "func_periodic_hook",
void ast_cli_unregister_multiple(void)
Definition ael_main.c:408
static int mixmonitor_exec(struct ast_channel *chan, const char *data)
static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
static char * filename_parse(char *filename, char *buffer, size_t len)
static struct ast_cli_entry cli_mixmonitor[]
static struct ast_custom_function mixmonitor_function
static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
static const struct ast_datastore_info mixmonitor_ds_info
mixmonitor_args
@ OPT_ARG_SKIP
@ OPT_ARG_WRITEVOLUME
@ OPT_ARG_UID
@ OPT_ARG_VMRECIPIENTS
@ OPT_ARG_DEPRECATED_RWSYNC
@ OPT_ARG_NO_RWSYNC
@ OPT_ARG_WRITENAME
@ OPT_ARG_READNAME
@ OPT_ARG_BEEP_INTERVAL
@ OPT_ARG_READVOLUME
@ OPT_ARG_VOLUME
@ OPT_ARG_ARRAY_SIZE
static int set_mixmonitor_methods(void)
static const struct ast_app_option mixmonitor_opts[128]
static const char *const app
static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
static int mixmonitor_autochan_is_bridged(struct ast_autochan *autochan)
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
Mute / unmute a MixMonitor channel.
static int clear_mixmonitor_methods(void)
static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data, enum ast_audiohook_flags flag, int clearmute)
Mute / unmute an individual MixMonitor by id.
static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
static char * handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define get_volfactor(x)
static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
#define MIN_SKIP_SECONDS
#define SAMPLES_PER_FRAME
static void mixmonitor_free(struct mixmonitor *mixmonitor)
static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len)
static const char *const stop_app
static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
static void * mixmonitor_thread(void *obj)
static int load_module(void)
static int launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process, const char *filename_write, char *filename_read, const char *uid_channel_var, const char *recipients, const char *beep_id, double skip_seconds)
static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
static int manager_mixmonitor(struct mansession *s, const struct message *m)
static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
static void fill_frame_buffer(struct ast_frame *source_frame, struct ast_format *target_format, struct ast_format **last_source_format, struct ast_trans_pvt **translator_path, short *dest_buf, size_t dest_buf_size, const char *direction)
static int unload_module(void)
static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
mixmonitor_flags
@ MUXFLAG_SKIP
@ MUXFLAG_VMRECIPIENTS
@ MUXFLAG_READ
@ MUXFLAG_AUTO_DELETE
@ MUXFLAG_DEPRECATED_RWSYNC
@ MUXFLAG_REAL_CALLERID
@ MUXFLAG_WRITEVOLUME
@ MUXFLAG_INTERLEAVED
@ MUXFLAG_NO_RWSYNC
@ MUXFLAG_VOLUME
@ MUXFLAG_BEEP_STOP
@ MUXFLAG_UID
@ MUXFLAG_APPEND
@ MUXFLAG_BEEP_START
@ MUXFLAG_WRITE
@ MUXFLAG_COMBINED
@ MUXFLAG_BEEP
@ MUXFLAG_BRIDGED
@ MUXFLAG_READVOLUME
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
static const char *const mixmonitor_spy_type
static void mixmonitor_ds_destroy(void *data)
static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
static void clear_mixmonitor_recipient_list(struct mixmonitor *mixmonitor)
pthread_t thread
Definition app_sla.c:335
Asterisk main include file. File version handling, generic pbx functions.
#define PATH_MAX
Definition asterisk.h:40
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition astmm.h:288
#define ast_free(a)
Definition astmm.h:180
#define ast_strdup(str)
A wrapper for strdup()
Definition astmm.h:241
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition astmm.h:298
#define ast_asprintf(ret, fmt,...)
A wrapper for asprintf()
Definition astmm.h:267
#define ast_calloc(num, len)
A wrapper for calloc()
Definition astmm.h:202
#define ast_malloc(len)
A wrapper for malloc()
Definition astmm.h:191
#define ast_log
Definition astobj2.c:42
#define ao2_cleanup(obj)
Definition astobj2.h:1934
Audiohooks Architecture.
struct ast_frame * ast_audiohook_read_frame_all(struct ast_audiohook *audiohook, size_t samples, struct ast_format *format, struct ast_frame **read_frame, struct ast_frame **write_frame)
Reads a frame in from the audiohook structure in mixed audio mode and copies read and write frame dat...
Definition audiohook.c:786
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source, enum ast_audiohook_init_flags flags)
Initialize an audiohook structure.
Definition audiohook.c:398
int ast_audiohook_remove(struct ast_channel *chan, struct ast_audiohook *audiohook)
Remove an audiohook from a specified channel.
Definition audiohook.c:1074
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
Wait for audiohook trigger to be triggered.
Definition audiohook.c:1462
void ast_audiohook_update_status(struct ast_audiohook *audiohook, enum ast_audiohook_status status)
Update audiohook's status.
Definition audiohook.c:885
#define ast_audiohook_lock(ah)
Lock an audiohook.
Definition audiohook.h:316
int ast_audiohook_set_mute_all(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear)
Mute frames read from or written for all audiohooks on a channel.
Definition audiohook.c:1763
ast_audiohook_flags
Definition audiohook.h:54
@ AST_AUDIOHOOK_MUTE_READ
Definition audiohook.h:64
@ AST_AUDIOHOOK_MUTE_WRITE
Definition audiohook.h:65
@ AST_AUDIOHOOK_SUBSTITUTE_SILENCE
Definition audiohook.h:68
@ AST_AUDIOHOOK_TRIGGER_SYNC
Definition audiohook.h:59
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition audiohook.c:895
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition audiohook.c:819
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition audiohook.c:422
#define ast_audiohook_unlock(ah)
Unlock an audiohook.
Definition audiohook.h:321
@ AST_AUDIOHOOK_TYPE_SPY
Definition audiohook.h:36
@ AST_AUDIOHOOK_STATUS_DONE
Definition audiohook.h:45
@ AST_AUDIOHOOK_STATUS_RUNNING
Definition audiohook.h:43
@ AST_AUDIOHOOK_STATUS_SHUTDOWN
Definition audiohook.h:44
"smart" channels that update automatically if a channel is masqueraded
struct ast_autochan * ast_autochan_setup(struct ast_channel *chan)
set up a new ast_autochan structure
Definition autochan.c:38
void ast_autochan_destroy(struct ast_autochan *autochan)
destroy an ast_autochan structure
Definition autochan.c:64
#define ast_autochan_channel_lock(autochan)
Lock the autochan's channel lock.
Definition autochan.h:75
#define ast_autochan_channel_unlock(autochan)
Definition autochan.h:84
Periodic beeps into the audio of a call.
CallerID (and other GR30) management and generation Includes code and algorithms from the Zapata libr...
char * ast_callerid_merge(char *buf, int bufsiz, const char *name, const char *num, const char *unknown)
Definition callerid.c:1273
static int connected
Definition cdr_pgsql.c:73
General Asterisk PBX channel definitions.
const char * ast_channel_name(const struct ast_channel *chan)
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition channel.c:2376
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition channel.c:2385
struct stasis_topic * ast_channel_topic(struct ast_channel *chan)
A topic which publishes the events for a particular channel.
#define ast_channel_lock(chan)
Definition channel.h:2983
int ast_channel_priority(const struct ast_channel *chan)
struct ast_party_connected_line * ast_channel_connected(struct ast_channel *chan)
const char * ast_channel_uniqueid(const struct ast_channel *chan)
const char * ast_channel_context(const struct ast_channel *chan)
struct ast_channel * ast_channel_get_by_name_prefix(const char *name, size_t name_len)
Find a channel by a name prefix.
Definition channel.c:1400
struct ast_channel * ast_channel_get_by_name(const char *search)
Find a channel by name or uniqueid.
Definition channel.c:1417
int ast_channel_is_bridged(const struct ast_channel *chan)
Determine if a channel is in a bridge.
Definition channel.c:10725
#define ast_channel_unref(c)
Decrease channel reference count.
Definition channel.h:3019
#define AST_MAX_CONTEXT
Definition channel.h:135
struct ast_party_caller * ast_channel_caller(struct ast_channel *chan)
const char * ast_channel_exten(const struct ast_channel *chan)
struct ast_datastore_list * ast_channel_datastores(struct ast_channel *chan)
#define ast_channel_unlock(chan)
Definition channel.h:2984
#define AST_MAX_EXTENSION
Definition channel.h:134
struct ast_datastore * ast_channel_datastore_find(struct ast_channel *chan, const struct ast_datastore_info *info, const char *uid)
Find a datastore on a channel.
Definition channel.c:2390
size_t current
Standard Command Line Interface.
#define CLI_SHOWUSAGE
Definition cli.h:45
#define CLI_SUCCESS
Definition cli.h:44
#define AST_CLI_DEFINE(fn, txt,...)
Definition cli.h:197
void ast_cli(int fd, const char *fmt,...)
Definition clicompat.c:6
char * ast_complete_channels(const char *line, const char *word, int pos, int state, int rpos)
Command completion for the list of active channels.
Definition main/cli.c:1950
@ CLI_INIT
Definition cli.h:152
@ CLI_GENERATE
Definition cli.h:153
#define ast_cli_register_multiple(e, len)
Register multiple commands.
Definition cli.h:265
#define ast_datastore_alloc(info, uid)
Definition datastore.h:85
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition datastore.c:68
void write_buf(int file, char *buffer, int num)
Definition eagi_proxy.c:312
char buf[BUFSIZE]
Definition eagi_proxy.c:66
long int flag
Definition f2c.h:83
Generic File Format Support. Should be included by clients of the file handling routines....
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
Writes a frame to a stream.
Definition file.c:265
struct ast_filestream * ast_writefile(const char *filename, const char *type, const char *comment, int flags, int check, mode_t mode)
Starts writing a file.
Definition file.c:1527
int ast_stream_and_wait(struct ast_channel *chan, const char *file, const char *digits)
stream file until digit If the file name is non-empty, try to play it.
Definition file.c:1982
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition file.c:1200
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition file.c:1230
unsigned int ast_format_get_sample_rate(const struct ast_format *format)
Get the sample rate of a media format.
Definition format.c:379
enum ast_format_cmp_res ast_format_cmp(const struct ast_format *format1, const struct ast_format *format2)
Compare two formats.
Definition format.c:201
@ AST_FORMAT_CMP_NOT_EQUAL
Definition format.h:38
const char * ast_format_get_name(const struct ast_format *format)
Get the name associated with a format.
Definition format.c:334
Media Format Cache API.
struct ast_format * ast_format_cache_get_slin_by_rate(unsigned int rate)
Retrieve the best signed linear format given a sample rate.
static const char name[]
Definition format_mp3.c:68
direction
int AST_OPTIONAL_API_NAME() ast_beep_start(struct ast_channel *chan, unsigned int interval, char *beep_id, size_t len)
int AST_OPTIONAL_API_NAME() ast_beep_stop(struct ast_channel *chan, const char *beep_id)
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition manager.c:1921
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1582
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1842
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7782
struct stasis_message_type * ast_channel_mixmonitor_mute_type(void)
Message type for muting or unmuting mixmonitor on a channel.
struct stasis_message_type * ast_channel_mixmonitor_stop_type(void)
Message type for stopping mixmonitor on a channel.
struct stasis_message * ast_channel_blob_create_from_cache(const char *uniqueid, struct stasis_message_type *type, struct ast_json *blob)
Create a ast_channel_blob message, pulling channel state from the cache.
struct stasis_message_type * ast_channel_mixmonitor_start_type(void)
Message type for starting mixmonitor on a channel.
const char * ext
Definition http.c:151
Application convenience functions, designed to give consistent look and feel to Asterisk apps.
#define AST_APP_ARG(name)
Define an application argument.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application's arguments.
int ast_app_copy_recording_to_vm(struct ast_vm_recording_data *vm_rec_data)
param[in] vm_rec_data Contains data needed to make the recording. retval 0 voicemail successfully cre...
Definition main/app.c:596
int ast_safe_system(const char *s)
Safely spawn an OS shell command while closing file descriptors.
Definition extconf.c:827
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the 'standard' argument separation process for an application.
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
int ast_app_parse_options(const struct ast_app_option *options, struct ast_flags *flags, char **args, char *optstr)
Parses a string containing application options and sets flags/arguments.
Definition main/app.c:3067
void ast_frame_free(struct ast_frame *frame, int cache)
Frees a frame or list of frames.
Definition main/frame.c:176
#define ast_frdup(fr)
Copies a frame.
#define ast_debug(level,...)
Log a DEBUG message.
ast_callid ast_read_threadstorage_callid(void)
extracts the callid from the thread
Definition logger.c:2287
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition logger.c:2309
unsigned int ast_callid
#define LOG_ERROR
#define ast_verb(level,...)
#define LOG_NOTICE
#define LOG_WARNING
void ast_json_unref(struct ast_json *value)
Decrease refcount on value. If refcount reaches zero, value is freed.
Definition json.c:73
struct ast_json * ast_json_pack(char const *format,...)
Helper for creating complex JSON values.
Definition json.c:612
A set of macros to manage forward-linked lists.
#define AST_LIST_HEAD_NOLOCK(name, type)
Defines a structure to be used to hold a list of specified type (with no lock).
#define AST_LIST_TRAVERSE(head, var, field)
Loops over (traverses) the entries in a list.
#define AST_LIST_EMPTY(head)
Checks whether the specified list contains any entries.
#define AST_LIST_ENTRY(type)
Declare a forward link structure inside a list entry.
#define AST_LIST_INSERT_HEAD(head, elm, field)
Inserts a list entry at the head of a list.
#define AST_LIST_REMOVE_HEAD(head, field)
Removes and returns the head entry from a list.
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
#define ast_cond_destroy(cond)
Definition lock.h:209
#define ast_cond_wait(cond, mutex)
Definition lock.h:212
#define ast_cond_init(cond, attr)
Definition lock.h:208
#define ast_mutex_init(pmutex)
Definition lock.h:193
#define ast_mutex_unlock(a)
Definition lock.h:197
pthread_cond_t ast_cond_t
Definition lock.h:185
#define ast_mutex_destroy(a)
Definition lock.h:195
#define ast_mutex_lock(a)
Definition lock.h:196
#define ast_cond_signal(cond)
Definition lock.h:210
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define AMI_SUCCESS
Definition manager.h:66
#define EVENT_FLAG_SYSTEM
Definition manager.h:75
#define ast_manager_register_xml(action, authority, func)
Register a manager callback using XML documentation to describe the manager.
Definition manager.h:193
#define EVENT_FLAG_CALL
Definition manager.h:76
static struct ast_mixmonitor_methods mixmonitor_methods
Definition mixmonitor.c:40
loadable MixMonitor functionality
int ast_set_mixmonitor_methods(struct ast_mixmonitor_methods *vmethod_table)
Setup MixMonitor virtual methods table. Use this to provide the MixMonitor functionality from a loada...
Definition mixmonitor.c:43
int ast_clear_mixmonitor_methods(void)
Clear the MixMonitor virtual methods table. Use this to cleanup function pointers provided by a modul...
Definition mixmonitor.c:59
Header for providers of file and format handling routines. Clients of these routines should include "...
Asterisk module definitions.
@ AST_MODFLAG_DEFAULT
Definition module.h:329
#define ast_module_unref(mod)
Release a reference to the module.
Definition module.h:483
#define ast_module_ref(mod)
Hold a reference to the module.
Definition module.h:457
#define AST_MODULE_INFO(keystr, flags_to_set, desc, fields...)
Definition module.h:557
@ AST_MODULE_SUPPORT_CORE
Definition module.h:121
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition module.h:46
int ast_unregister_application(const char *app)
Unregister an application.
Definition pbx_app.c:404
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition module.h:640
Asterisk file paths, configured in asterisk.conf.
const char * ast_config_AST_MONITOR_DIR
Definition options.c:156
Core PBX routines and definitions.
const char * pbx_builtin_getvar_helper(struct ast_channel *chan, const char *name)
Return a pointer to the value of the corresponding channel variable.
int pbx_builtin_setvar_helper(struct ast_channel *chan, const char *name, const char *value)
Add a variable to the channel variable stack, removing the most recently set value for the same name.
#define ast_custom_function_register(acf)
Register a custom function.
Definition pbx.h:1563
int ast_custom_function_unregister(struct ast_custom_function *acf)
Unregister a custom function.
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition ael_main.c:211
static struct @523 args
#define NULL
Definition resample.c:96
Stasis Message Bus API. See Stasis Message Bus API for detailed documentation.
void stasis_publish(struct stasis_topic *topic, struct stasis_message *message)
Publish a message to a topic's subscribers.
Definition stasis.c:1589
#define AST_DECLARE_STRING_FIELDS(field_list)
Declare the fields needed in a structure.
#define AST_STRING_FIELD(name)
Declare a string field.
#define ast_string_field_set(x, field, data)
Set a field to a simple string value.
#define ast_string_field_init(x, size)
Initialize a field pool and fields.
#define ast_string_field_free_memory(x)
free all memory - to be called before destroying the object
#define S_OR(a, b)
returns the equivalent of logic or for strings: first one if not empty, otherwise second one.
Definition strings.h:80
int attribute_pure ast_true(const char *val)
Make sure something is true. Determine if a string containing a boolean value is "true"....
Definition utils.c:2233
#define S_COR(a, b, c)
returns the equivalent of logic or for strings, with an additional boolean check: second one if not e...
Definition strings.h:87
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition strings.h:65
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is "false"....
Definition utils.c:2250
void ast_copy_string(char *dst, const char *src, size_t size)
Size-limited null-terminating string copy.
Definition strings.h:425
ast_cond_t trigger
Definition audiohook.h:106
struct ast_audiohook_options options
Definition audiohook.h:119
enum ast_audiohook_status status
Definition audiohook.h:108
struct ast_channel * chan
Definition autochan.h:33
Main Channel structure associated with a channel.
const char * data
struct ast_flags flags
descriptor for a cli entry.
Definition cli.h:171
char * command
Definition cli.h:186
const char * usage
Definition cli.h:177
Data structure associated with a custom dialplan function.
Definition pbx.h:118
const char * name
Definition pbx.h:119
Structure for a data store type.
Definition datastore.h:31
const char * type
Definition datastore.h:32
Structure for a data store object.
Definition datastore.h:64
const struct ast_datastore_info * info
Definition datastore.h:67
void * data
Definition datastore.h:66
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition mod_format.h:101
struct ast_format_def * fmt
Definition mod_format.h:103
Structure used to handle boolean flags.
Definition utils.h:220
unsigned int flags
Definition utils.h:221
struct ast_format * format
Definition mod_format.h:48
Definition of a media format.
Definition format.c:43
struct ast_format * format
Data structure associated with a single frame of data.
struct ast_frame_subclass subclass
enum ast_frame_type frametype
union ast_frame::@237 data
Abstract JSON element (object, array, string, int, ...).
MixMonitor virtual methods table definition.
Definition mixmonitor.h:58
ast_mixmonitor_start_fn start
Definition mixmonitor.h:59
struct ast_module * self
Definition module.h:356
Structure for mutex and tracking information.
Definition lock.h:142
Caller Party information.
Definition channel.h:420
struct ast_party_id id
Caller party ID.
Definition channel.h:422
Connected Line/Party information.
Definition channel.h:458
struct ast_party_name name
Subscriber name.
Definition channel.h:342
struct ast_party_number number
Subscriber phone number.
Definition channel.h:344
unsigned char valid
TRUE if the name information is valid/present.
Definition channel.h:281
char * str
Subscriber name (Malloced)
Definition channel.h:266
unsigned char valid
TRUE if the number information is valid/present.
Definition channel.h:299
char * str
Subscriber phone number (Malloced)
Definition channel.h:293
Default structure for translators, with the basic fields and buffers, all allocated as part of the sa...
Definition translate.h:213
Structure used for ast_copy_recording_to_vm in order to cleanly supply data needed for making the rec...
const ast_string_field recording_file
const ast_string_field call_callerchan
const ast_string_field context
const ast_string_field folder
const ast_string_field call_callerid
const ast_string_field call_context
const ast_string_field recording_ext
const ast_string_field call_extension
const ast_string_field mailbox
In case you didn't read that giant block of text above the mansession_session struct,...
Definition manager.c:314
unsigned int destruction_ok
struct ast_audiohook * audiohook
struct ast_filestream * fs_read
struct ast_filestream * fs_write
struct ast_filestream * fs
unsigned int samp_rate
ast_mutex_t lock
ast_cond_t destruction_condition
struct mixmonitor::@43 recipient_list
char * filename_write
const ast_string_field call_callerchan
ast_callid callid
const ast_string_field call_callerid
struct ast_autochan * autochan
char * post_process
struct ast_audiohook audiohook
const ast_string_field call_context
const ast_string_field call_extension
char * filename_read
double skip_seconds
unsigned int flags
struct mixmonitor_ds * mixmonitor_ds
char mailbox[AST_MAX_CONTEXT]
char context[AST_MAX_EXTENSION]
struct vm_recipient::@42 list
struct vm_recipient * next
Test Framework API.
#define ast_test_suite_event_notify(s, f,...)
Definition test.h:189
static struct test_options options
static struct test_val a
static struct test_val c
int64_t ast_tvdiff_ms(struct timeval end, struct timeval start)
Computes the difference (in milliseconds) between two struct timeval instances.
Definition time.h:107
struct timeval ast_tvnow(void)
Returns current timeval. Meant to replace calls to gettimeofday().
Definition time.h:159
Support for translation of data formats. translate.c.
struct ast_frame * ast_translate(struct ast_trans_pvt *tr, struct ast_frame *f, int consume)
translates one or more frames Apply an input frame into the translator and receive zero or one output...
Definition translate.c:623
void ast_translator_free_path(struct ast_trans_pvt *tr)
Frees a translator path Frees the given translator path structure.
Definition translate.c:533
struct ast_trans_pvt * ast_translator_build_path(struct ast_format *dest, struct ast_format *source)
Builds a translator path Build a path (possibly NULL) from source to dest.
Definition translate.c:543
#define ast_test_flag(p, flag)
Definition utils.h:64
#define RAII_VAR(vartype, varname, initval, dtor)
Declare a variable that will call a destructor function when it goes out of scope.
Definition utils.h:981
#define ast_assert(a)
Definition utils.h:779
#define ast_clear_flag(p, flag)
Definition utils.h:78
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition utils.c:2513
#define ast_pthread_create_detached_background(a, b, c, d)
Definition utils.h:637
#define ast_set_flag(p, flag)
Definition utils.h:71
#define ARRAY_LEN(a)
Definition utils.h:706
#define MAX(a, b)
Definition utils.h:254