Asterisk - The Open Source Telephony Project GIT-master-6144b6b
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
524
525 unsigned int samp_rate;
526 char *filename;
527 char *beep_id;
528};
529
530/*!
531 * \internal
532 * \pre mixmonitor_ds must be locked before calling this function
533 */
535{
536 unsigned char quitting = 0;
537
538 if (mixmonitor_ds->fs) {
539 quitting = 1;
542 ast_verb(2, "MixMonitor close filestream (mixed)\n");
543 }
544
545 if (mixmonitor_ds->fs_read) {
546 quitting = 1;
549 ast_verb(2, "MixMonitor close filestream (read)\n");
550 }
551
552 if (mixmonitor_ds->fs_write) {
553 quitting = 1;
556 ast_verb(2, "MixMonitor close filestream (write)\n");
557 }
558
559 if (quitting) {
561 }
562}
563
576
578 .type = "mixmonitor",
579 .destroy = mixmonitor_ds_destroy,
580};
581
595
596static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
597{
598 if (!chan) {
599 return -1;
600 }
601
602 return ast_audiohook_attach(chan, audiohook);
603}
604
605/*!
606 * \internal
607 * \brief adds recipients to a mixmonitor's recipient list
608 * \param mixmonitor mixmonitor being affected
609 * \param vm_recipients string containing the desired recipients to add
610 */
611static void add_vm_recipients_from_string(struct mixmonitor *mixmonitor, const char *vm_recipients)
612{
613 /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
614 char *cur_mailbox = ast_strdupa(vm_recipients);
615 char *cur_context;
616 char *cur_folder;
617 char *next;
618 int elements_processed = 0;
619
620 while (!ast_strlen_zero(cur_mailbox)) {
621 ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
622 if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
623 *(next++) = '\0';
624 }
625
626 if ((cur_folder = strchr(cur_mailbox, '/'))) {
627 *(cur_folder++) = '\0';
628 } else {
629 cur_folder = "INBOX";
630 }
631
632 if ((cur_context = strchr(cur_mailbox, '@'))) {
633 *(cur_context++) = '\0';
634 } else {
635 cur_context = "default";
636 }
637
638 if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
639 struct vm_recipient *recipient;
640 if (!(recipient = ast_malloc(sizeof(*recipient)))) {
641 ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
642 return;
643 }
644 ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
645 ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
646 ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));
647
648 /* Add to list */
649 ast_verb(4, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
651 } else {
652 ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
653 }
654
655 cur_mailbox = next;
656 elements_processed++;
657 }
658}
659
661{
662 struct vm_recipient *current;
664 /* Clear list element data */
666 }
667}
668
669#define SAMPLES_PER_FRAME 160
670
671/* This will allocate and free the translator path as needed so it can be re-used on subsequent calls */
672static void fill_frame_buffer(struct ast_frame *source_frame,
673 struct ast_format *target_format,
674 struct ast_format **last_source_format,
676 short *dest_buf,
677 size_t dest_buf_size,
678 const char *direction)
679{
680 struct ast_frame *converted_frame = NULL;
681
682 if (!source_frame) {
683 memset(dest_buf, 0, dest_buf_size);
684 return;
685 }
686
687 /*
688 * We only need to worry about translating if the frame format does not match
689 * the expected format of the frame we are writing.
690 */
691 if (ast_format_cmp(target_format, source_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL) {
692 /*
693 * If the format changed from the last frame or if this is the first frame
694 * that does not match the native format, we need to set up a new
695 * translator path.
696 */
697 if (ast_format_cmp(*last_source_format, source_frame->subclass.format) == AST_FORMAT_CMP_NOT_EQUAL
698 || !*translator_path) {
699 ast_debug(3, "%s frame format changed from %s to %s, building translator path to %s\n",
700 direction,
701 *last_source_format ? ast_format_get_name(*last_source_format) : "none",
702 ast_format_get_name(source_frame->subclass.format),
703 ast_format_get_name(target_format));
704
705 if (*translator_path) {
707 }
708
709 *translator_path = ast_translator_build_path(target_format, source_frame->subclass.format);
710 }
711
712 if (*translator_path) {
713 converted_frame = ast_translate(*translator_path, source_frame, 0);
714 }
715
716 /*
717 * If creating the translated frame or translator path failed, write silence for this frame. We will
718 * try rebuilding the translator path later if it is still needed.
719 */
720 if (converted_frame) {
721 memcpy(dest_buf, converted_frame->data.ptr, dest_buf_size);
722 ast_frame_free(converted_frame, 1);
723 } else {
724 memset(dest_buf, 0, dest_buf_size);
725 }
726 } else {
727 memcpy(dest_buf, source_frame->data.ptr, dest_buf_size);
728
729 /*
730 * If we are doing native frame copying, we can free the translator path if
731 * it exists. We will create a new one later if needed.
732 */
733 if (*translator_path) {
736 ast_debug(3, "%s frame format changed from %s to %s, translator path no longer needed\n",
737 direction,
738 *last_source_format ? ast_format_get_name(*last_source_format) : "none",
739 ast_format_get_name(source_frame->subclass.format));
740 }
741 }
742
743 *last_source_format = source_frame->subclass.format;
744}
745
770
771/*!
772 * \internal
773 * \brief Copies the mixmonitor to all voicemail recipients
774 * \param mixmonitor The mixmonitor that needs to forward its file to recipients
775 * \param ext Format of the file that was saved
776 * \param filename
777 */
778static void copy_to_voicemail(struct mixmonitor *mixmonitor, const char *ext, const char *filename)
779{
780 struct vm_recipient *recipient = NULL;
783 ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
784 return;
785 }
786
787 /* Copy strings to stringfields that will be used for all recipients */
794 /* and call_priority gets copied too */
795 recording_data.call_priority = mixmonitor->call_priority;
796
797 AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
798 /* context, mailbox, and folder need to be set per recipient */
802
803 ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
804 recording_data.context);
806 }
807
808 /* Free the string fields for recording_data before exiting the function. */
810}
811
812static void mixmonitor_save_prep(struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
813{
814 /* Initialize the file if not already done so */
815 char *last_slash = NULL;
816 if (!ast_strlen_zero(filename)) {
817 if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
818 *oflags = O_CREAT | O_WRONLY;
819 *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
820
821 last_slash = strrchr(filename, '/');
822
823 if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
824 **ext = '\0';
825 *ext = *ext + 1;
826 } else {
827 *ext = "raw";
828 }
829
830 if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
831 ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
832 *errflag = 1;
833 } else {
834 struct ast_filestream *tmp = *fs;
836 }
837 }
838 }
839}
840
842{
843 int is_bridged;
844
846 is_bridged = ast_channel_is_bridged(autochan->chan);
848 return is_bridged;
849}
850
851static void *mixmonitor_thread(void *obj)
852{
853 struct mixmonitor *mixmonitor = obj;
854 char *fs_ext = "";
855 char *fs_read_ext = "";
856 char *fs_write_ext = "";
857
858 struct ast_filestream **fs = NULL;
859 struct ast_filestream **fs_read = NULL;
860 struct ast_filestream **fs_write = NULL;
861
862 struct ast_format *last_format_read = NULL;
863 struct ast_format *last_format_write = NULL;
864 struct ast_trans_pvt *trans_pvt_read = NULL;
865 struct ast_trans_pvt *trans_pvt_write = NULL;
866
867 unsigned int oflags;
868 int errflag = 0;
869 struct ast_format *format_slin;
870
871 struct timeval skip_start = ast_tvnow();
872
873 /* Keep callid association before any log messages */
874 if (mixmonitor->callid) {
876 }
877
878 ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
879
881 fs_read = &mixmonitor->mixmonitor_ds->fs_read;
882 fs_write = &mixmonitor->mixmonitor_ds->fs_write;
883
885 mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
886 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
887 mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);
888
890
892
893 if (mixmonitor->skip_seconds > 0.0) {
894 ast_debug(3, "%s skipping initial %.3f seconds\n",
896 }
897
898 /* The audiohook must enter and exit the loop locked */
901 struct ast_frame *fr = NULL;
902 struct ast_frame *fr_read = NULL;
903 struct ast_frame *fr_write = NULL;
904
906 &fr_read, &fr_write))) {
908
910 break;
911 }
912 continue;
913 }
914
915 /* audiohook lock is not required for the next block.
916 * Unlock it, but remember to lock it before looping or exiting */
918
922
923 /* Skip writing audio for the first N seconds */
924 if (mixmonitor->skip_seconds > 0.0) {
925 struct timeval now = ast_tvnow();
926 double elapsed = ast_tvdiff_ms(now, skip_start) / 1000.0;
927
928 if (elapsed < mixmonitor->skip_seconds) {
930 /* Skip this frame and continue */
931 goto frame_cleanup;
932 } else {
933 ast_debug(3, "%s skip period %.3f seconds elapsed; starting to write audio\n",
936 }
937 }
938
939 /* Write out the frame(s) */
940 if ((*fs_read) && (fr_read)) {
941 struct ast_frame *cur;
942
943 for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
944 ast_writestream(*fs_read, cur);
945 }
946 }
947
948 if ((*fs_write) && (fr_write)) {
949 struct ast_frame *cur;
950
951 for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
952 ast_writestream(*fs_write, cur);
953 }
954 }
955
957 /* The 'D' option is set, so mix the frame as an interleaved dual channel frame */
958 int i;
959 /*
960 * We are fed by a call to ast_audiohook_read_frame_all specifying format_slin. However
961 * we may get frames back in a different format. Either way, we we will translate them
962 * to the format with the correct frame size before writing into these buffers.
963 */
964 short read_buf[SAMPLES_PER_FRAME];
966 short stereo_buf[SAMPLES_PER_FRAME * 2];
967 struct ast_frame stereo_frame = {
969 .datalen = sizeof(stereo_buf),
971 };
972
973 if (fr) {
974 ast_frame_free(fr, 0);
975 fr = NULL;
976 }
977
978 /*
979 * Depending on the input codec's rate (which may change during the call) we may get frames in
980 * a slin format that does not match the native format_slin of the mixmonitor. In that case
981 * we need to translate.
982 */
983 fill_frame_buffer(fr_read, format_slin, &last_format_read, &trans_pvt_read,
984 read_buf, sizeof(read_buf), "Read");
985
986 fill_frame_buffer(fr_write, format_slin, &last_format_write, &trans_pvt_write,
987 write_buf, sizeof(write_buf), "Write");
988
989 for (i = 0; i < SAMPLES_PER_FRAME; i++) {
990 stereo_buf[i * 2] = read_buf[i];
991 stereo_buf[i * 2 + 1] = write_buf[i];
992 }
993
994 stereo_frame.data.ptr = stereo_buf;
995 stereo_frame.subclass.format = format_slin;
996
997 fr = ast_frdup(&stereo_frame);
998 }
999
1000 if ((*fs) && (fr)) {
1001 struct ast_frame *cur;
1002
1003 for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
1004 ast_writestream(*fs, cur);
1005 }
1006 }
1008 }
1009
1010frame_cleanup:
1011 /* All done! free it. */
1012 if (fr) {
1013 ast_frame_free(fr, 0);
1014 }
1015 if (fr_read) {
1016 ast_frame_free(fr_read, 0);
1017 }
1018 if (fr_write) {
1019 ast_frame_free(fr_write, 0);
1020 }
1021
1022 fr = NULL;
1023 fr_write = NULL;
1024 fr_read = NULL;
1025
1027 }
1028
1030
1035 }
1036
1038
1039 /* Datastore cleanup. close the filestream and wait for ds destruction */
1044 }
1046
1047 /* Free the translate paths */
1048 if (trans_pvt_read) {
1049 ast_translator_free_path(trans_pvt_read);
1050 trans_pvt_read = NULL;
1051 }
1052 if (trans_pvt_write) {
1053 ast_translator_free_path(trans_pvt_write);
1054 trans_pvt_write = NULL;
1055 }
1056
1057 /* kill the audiohook */
1059
1060 if (mixmonitor->post_process) {
1061 ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
1063 }
1064
1065 ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
1066 ast_test_suite_event_notify("MIXMONITOR_END", "File: %s\r\n", mixmonitor->filename);
1067
1069 if (ast_strlen_zero(fs_ext)) {
1070 ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
1071 mixmonitor -> name);
1072 } else {
1073 ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
1075 }
1076 if (!ast_strlen_zero(fs_read_ext)) {
1077 ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
1079 }
1080 if (!ast_strlen_zero(fs_write_ext)) {
1081 ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
1083 }
1084 } else {
1085 ast_debug(3, "No recipients to forward monitor to, moving on.\n");
1086 }
1087
1089 ast_debug(3, "Deleting our copies of recording files\n");
1090 if (!ast_strlen_zero(fs_ext)) {
1092 }
1093 if (!ast_strlen_zero(fs_read_ext)) {
1095 }
1096 if (!ast_strlen_zero(fs_write_ext)) {
1098 }
1099 }
1100
1102
1104 return NULL;
1105}
1106
1107static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id, const char *beep_id)
1108{
1109 struct ast_datastore *datastore = NULL;
1111
1112 if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
1113 return -1;
1114 }
1115
1116 if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
1117 ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
1119 return -1;
1120 }
1121
1124
1125 if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
1129 return -1;
1130 }
1131
1136 }
1137
1138 mixmonitor_ds->samp_rate = 8000;
1141 if (!ast_strlen_zero(beep_id)) {
1143 }
1144 datastore->data = mixmonitor_ds;
1145
1146 ast_channel_lock(chan);
1147 ast_channel_datastore_add(chan, datastore);
1148 ast_channel_unlock(chan);
1149
1151 return 0;
1152}
1153
1154static void mixmonitor_ds_remove_and_free(struct ast_channel *chan, const char *datastore_id)
1155{
1156 struct ast_datastore *datastore;
1157
1158 ast_channel_lock(chan);
1159
1160 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, datastore_id);
1161
1162 if (datastore) {
1163 ast_channel_datastore_remove(chan, datastore);
1164 ast_datastore_free(datastore);
1165 }
1166
1167 ast_channel_unlock(chan);
1168}
1169
1170static int launch_monitor_thread(struct ast_channel *chan, const char *filename,
1171 unsigned int flags, int readvol, int writevol,
1172 const char *post_process, const char *filename_write,
1173 char *filename_read, const char *uid_channel_var,
1174 const char *recipients, const char *beep_id, double skip_seconds)
1175{
1176 pthread_t thread;
1177 struct mixmonitor *mixmonitor;
1178 char postprocess2[1024] = "";
1179 char *datastore_id = NULL;
1180
1181 postprocess2[0] = 0;
1182 /* If a post process system command is given attach it to the structure */
1184 char *p1, *p2;
1185
1187 for (p2 = p1; *p2; p2++) {
1188 if (*p2 == '^' && *(p2+1) == '{') {
1189 *p2 = '$';
1190 }
1191 }
1192 ast_channel_lock(chan);
1193 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
1194 ast_channel_unlock(chan);
1195 }
1196
1197 /* Pre-allocate mixmonitor structure and spy */
1198 if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
1199 return -1;
1200 }
1201
1202 /* Now that the struct has been calloced, go ahead and initialize the string fields. */
1205 return -1;
1206 }
1207
1208 /* Setup the actual spy before creating our thread */
1211 return -1;
1212 }
1213
1214 /* Copy over flags and channel name */
1217 if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
1219 return -1;
1220 }
1221
1222 if (!ast_strlen_zero(filename)) {
1224 }
1225
1228 }
1229
1232 }
1233
1234 if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id, beep_id)) {
1237 ast_free(datastore_id);
1238 return -1;
1239 }
1240
1241 if (!ast_strlen_zero(uid_channel_var)) {
1242 if (datastore_id) {
1243 pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
1244 }
1245 }
1246
1248
1249 if (!ast_strlen_zero(postprocess2)) {
1250 mixmonitor->post_process = ast_strdup(postprocess2);
1251 }
1252
1253 if (!ast_strlen_zero(recipients)) {
1254 char callerid[256];
1255
1256 ast_channel_lock(chan);
1257
1258 /* We use the connected line of the invoking channel for caller ID,
1259 * unless we've been told to use the Caller ID.
1260 * The initial use for this relied on Connected Line to get the
1261 * actual number for recording with Digium phones,
1262 * but in generic use the Caller ID is likely what people want.
1263 */
1264
1266 struct ast_party_caller *caller;
1267 caller = ast_channel_caller(chan);
1268 ast_debug(3, "Caller ID = %d - %s : %d - %s\n", caller->id.name.valid,
1269 caller->id.name.str, caller->id.number.valid,
1270 caller->id.number.str);
1271 ast_callerid_merge(callerid, sizeof(callerid),
1272 S_COR(caller->id.name.valid, caller->id.name.str, NULL),
1273 S_COR(caller->id.number.valid, caller->id.number.str, NULL),
1274 "Unknown");
1275 } else {
1278 ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
1279 connected->id.name.str, connected->id.number.valid,
1280 connected->id.number.str);
1281 ast_callerid_merge(callerid, sizeof(callerid),
1282 S_COR(connected->id.name.valid, connected->id.name.str, NULL),
1283 S_COR(connected->id.number.valid, connected->id.number.str, NULL),
1284 "Unknown");
1285 }
1286
1288 ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
1289 ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
1290 ast_string_field_set(mixmonitor, call_callerid, callerid);
1292
1293 ast_channel_unlock(chan);
1294
1296 }
1297
1301 }
1302
1303 if (readvol)
1305 if (writevol)
1307
1308 if (startmon(chan, &mixmonitor->audiohook)) {
1309 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
1311 mixmonitor_ds_remove_and_free(chan, datastore_id);
1312 ast_free(datastore_id);
1316 return -1;
1317 }
1318
1319 ast_free(datastore_id);
1320
1321 /* reference be released at mixmonitor destruction */
1323
1325}
1326
1327/* a note on filename_parse: creates directory structure and assigns absolute path from relative paths for filenames */
1328/* requires immediate copying of string from return to retain data since otherwise it will immediately lose scope */
1329static char *filename_parse(char *filename, char *buffer, size_t len)
1330{
1331 char *slash;
1332 char *ext;
1333
1334 ast_assert(len > 0);
1335
1336 if (ast_strlen_zero(filename)) {
1337 ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
1338 buffer[0] = 0;
1339 return buffer;
1340 }
1341
1342 /* If we don't have an absolute path, make one */
1343 if (*filename != '/') {
1344 char *build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
1345 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
1346 filename = build;
1347 }
1348
1349 ast_copy_string(buffer, filename, len);
1350
1351 /* If the provided filename has a .wav49 extension, we need to convert it to .WAV to
1352 match the behavior of build_filename in main/file.c. Otherwise MIXMONITOR_FILENAME
1353 ends up referring to a file that does not/will not exist */
1354 ext = strrchr(buffer, '.');
1355 if (ext && !strcmp(ext, ".wav49")) {
1356 /* Change to WAV - we know we have at least 6 writeable bytes where 'ext' points,
1357 * so this is safe */
1358 memcpy(ext, ".WAV", sizeof(".WAV"));
1359 }
1360
1361 if ((slash = strrchr(filename, '/'))) {
1362 *slash = '\0';
1363 }
1364 ast_mkdir(filename, 0777);
1365
1366 return buffer;
1367}
1368
1369static int mixmonitor_exec(struct ast_channel *chan, const char *data)
1370{
1371 int x, readvol = 0, writevol = 0;
1372 double skip_seconds = 0.0;
1373 char *filename_read = NULL;
1374 char *filename_write = NULL;
1375 char filename_buffer[1024] = "";
1376 char *uid_channel_var = NULL;
1377 char beep_id[64] = "";
1378
1379 struct ast_flags flags = { 0 };
1380 char *recipients = NULL;
1381 char *parse;
1384 AST_APP_ARG(filename);
1386 AST_APP_ARG(post_process);
1387 );
1388
1389 if (ast_strlen_zero(data)) {
1390 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
1391 return -1;
1392 }
1393
1394 parse = ast_strdupa(data);
1395
1397
1398 if (args.options) {
1399 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1400
1402
1404 ast_log(LOG_NOTICE, "The synchronization behavior enabled by the 'S' option is now the default"
1405 " and does not need to be specified.\n");
1406 }
1407
1410 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
1411 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1412 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
1413 } else {
1414 readvol = get_volfactor(x);
1415 }
1416 }
1417
1420 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
1421 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1422 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
1423 } else {
1424 writevol = get_volfactor(x);
1425 }
1426 }
1427
1429 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
1430 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
1431 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
1432 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
1433 } else {
1434 readvol = writevol = get_volfactor(x);
1435 }
1436 }
1437
1440 ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
1441 } else {
1442 recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
1443 }
1444 }
1445
1447 filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
1448 }
1449
1451 filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
1452 }
1453
1455 uid_channel_var = opts[OPT_ARG_UID];
1456 }
1457
1459 const char *interval_str = S_OR(opts[OPT_ARG_BEEP_INTERVAL], "15");
1460 unsigned int interval = 15;
1461
1462 if (sscanf(interval_str, "%30u", &interval) != 1) {
1463 ast_log(LOG_WARNING, "Invalid interval '%s' for periodic beep. Using default of %u\n",
1464 interval_str, interval);
1465 }
1466
1467 if (ast_beep_start(chan, interval, beep_id, sizeof(beep_id))) {
1468 ast_log(LOG_WARNING, "Unable to enable periodic beep, please ensure func_periodic_hook is loaded.\n");
1469 return -1;
1470 }
1471 }
1472
1474 if (ast_strlen_zero(opts[OPT_ARG_SKIP])) {
1475 ast_log(LOG_WARNING, "No skip value provided for the 's' (skip) option; skipping will be ignored as no default exists.\n");
1476 } else {
1477 char *endptr = NULL;
1478 double val = strtod(opts[OPT_ARG_SKIP], &endptr);
1479 if (endptr == opts[OPT_ARG_SKIP] || *endptr != '\0') {
1480 ast_log(LOG_WARNING, "Skip value '%s' is not a valid number; ignoring skip.\n", opts[OPT_ARG_SKIP]);
1481 } else if (val < (double) MIN_SKIP_SECONDS) {
1482 ast_log(LOG_WARNING, "Skip value %.3f is below minimum %d; ignoring skip.\n", val, MIN_SKIP_SECONDS);
1483 } else {
1484 skip_seconds = val;
1485 }
1486 }
1487 }
1488 }
1489 /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */
1490
1492 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
1493 return -1;
1494 }
1495
1496 /* If filename exists, try to create directories for it */
1497 if (!(ast_strlen_zero(args.filename))) {
1498 args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
1499 }
1500
1501 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
1502
1503 /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
1505 if (launch_monitor_thread(chan,
1506 args.filename,
1507 flags.flags,
1508 readvol,
1509 writevol,
1510 args.post_process,
1511 filename_write,
1512 filename_read,
1513 uid_channel_var,
1514 recipients,
1515 beep_id,
1516 skip_seconds)) {
1518 }
1519
1522 if (message) {
1524 }
1525
1526 return 0;
1527}
1528
1529static int stop_mixmonitor_full(struct ast_channel *chan, const char *data)
1530{
1531 struct ast_datastore *datastore = NULL;
1532 char *parse = "";
1534 const char *beep_id = NULL;
1536
1538 AST_APP_ARG(mixmonid);
1539 );
1540
1541 if (!ast_strlen_zero(data)) {
1542 parse = ast_strdupa(data);
1543 }
1544
1546
1547 ast_channel_lock(chan);
1548
1550 S_OR(args.mixmonid, NULL));
1551 if (!datastore) {
1552 ast_channel_unlock(chan);
1553 return -1;
1554 }
1555 mixmonitor_ds = datastore->data;
1556
1558
1559 /* closing the filestream here guarantees the file is available to the dialplan
1560 * after calling StopMixMonitor */
1562
1563 /* The mixmonitor thread may be waiting on the audiohook trigger.
1564 * In order to exit from the mixmonitor loop before waiting on channel
1565 * destruction, poke the audiohook trigger. */
1566 if (mixmonitor_ds->audiohook) {
1569 }
1574 }
1575
1578 }
1579
1581
1582 /* Remove the datastore so the monitor thread can exit */
1583 if (!ast_channel_datastore_remove(chan, datastore)) {
1584 ast_datastore_free(datastore);
1585 }
1586
1587 ast_channel_unlock(chan);
1588
1589 if (!ast_strlen_zero(beep_id)) {
1590 ast_beep_stop(chan, beep_id);
1591 }
1592
1595 NULL);
1596 if (message) {
1598 }
1599
1600 return 0;
1601}
1602
1603static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
1604{
1605 stop_mixmonitor_full(chan, data);
1606 return 0;
1607}
1608
1609static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
1610{
1611 struct ast_channel *chan;
1612 struct ast_datastore *datastore = NULL;
1614
1615 switch (cmd) {
1616 case CLI_INIT:
1617 e->command = "mixmonitor {start|stop|list}";
1618 e->usage =
1619 "Usage: mixmonitor start <chan_name> [args]\n"
1620 " The optional arguments are passed to the MixMonitor application.\n"
1621 " mixmonitor stop <chan_name> [args]\n"
1622 " The optional arguments are passed to the StopMixMonitor application.\n"
1623 " mixmonitor list <chan_name>\n";
1624 return NULL;
1625 case CLI_GENERATE:
1626 return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
1627 }
1628
1629 if (a->argc < 3) {
1630 return CLI_SHOWUSAGE;
1631 }
1632
1633 if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
1634 ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
1635 /* Technically this is a failure, but we don't want 2 errors printing out */
1636 return CLI_SUCCESS;
1637 }
1638
1639 if (!strcasecmp(a->argv[1], "start")) {
1640 mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1641 } else if (!strcasecmp(a->argv[1], "stop")){
1642 stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
1643 } else if (!strcasecmp(a->argv[1], "list")) {
1644 ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
1645 ast_cli(a->fd, "=========================================================================\n");
1646 ast_channel_lock(chan);
1647 AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
1648 if (datastore->info == &mixmonitor_ds_info) {
1649 char *filename = "";
1650 char *filename_read = "";
1651 char *filename_write = "";
1652
1653 mixmonitor_ds = datastore->data;
1654 if (mixmonitor_ds->fs) {
1656 }
1657 if (mixmonitor_ds->fs_read) {
1658 filename_read = mixmonitor_ds->fs_read->filename;
1659 }
1660 if (mixmonitor_ds->fs_write) {
1661 filename_write = mixmonitor_ds->fs_write->filename;
1662 }
1663 ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
1664 }
1665 }
1666 ast_channel_unlock(chan);
1667 } else {
1668 chan = ast_channel_unref(chan);
1669 return CLI_SHOWUSAGE;
1670 }
1671
1672 chan = ast_channel_unref(chan);
1673
1674 return CLI_SUCCESS;
1675}
1676
1677/*! \brief Mute / unmute an individual MixMonitor by id */
1678static int mute_mixmonitor_instance(struct ast_channel *chan, const char *data,
1679 enum ast_audiohook_flags flag, int clearmute)
1680{
1681 struct ast_datastore *datastore = NULL;
1682 char *parse = "";
1684
1686 AST_APP_ARG(mixmonid);
1687 );
1688
1689 if (!ast_strlen_zero(data)) {
1690 parse = ast_strdupa(data);
1691 }
1692
1694
1695 ast_channel_lock(chan);
1696
1698 S_OR(args.mixmonid, NULL));
1699 if (!datastore) {
1700 ast_channel_unlock(chan);
1701 return -1;
1702 }
1703 mixmonitor_ds = datastore->data;
1704
1706
1707 if (mixmonitor_ds->audiohook) {
1708 if (clearmute) {
1710 } else {
1712 }
1713 }
1714
1716 ast_channel_unlock(chan);
1717
1718 return 0;
1719}
1720
1721/*! \brief Mute / unmute a MixMonitor channel */
1722static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
1723{
1724 struct ast_channel *c;
1725 const char *name = astman_get_header(m, "Channel");
1726 const char *id = astman_get_header(m, "ActionID");
1727 const char *state = astman_get_header(m, "State");
1728 const char *direction = astman_get_header(m,"Direction");
1729 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1730 int clearmute = 1, mutedcount = 0;
1733 RAII_VAR(struct ast_json *, stasis_message_blob, NULL, ast_json_unref);
1734
1736 astman_send_error(s, m, "No direction specified. Must be read, write or both");
1737 return AMI_SUCCESS;
1738 }
1739
1740 if (!strcasecmp(direction, "read")) {
1742 } else if (!strcasecmp(direction, "write")) {
1744 } else if (!strcasecmp(direction, "both")) {
1746 } else {
1747 astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
1748 return AMI_SUCCESS;
1749 }
1750
1751 if (ast_strlen_zero(name)) {
1752 astman_send_error(s, m, "No channel specified");
1753 return AMI_SUCCESS;
1754 }
1755
1756 if (ast_strlen_zero(state)) {
1757 astman_send_error(s, m, "No state specified");
1758 return AMI_SUCCESS;
1759 }
1760
1761 clearmute = ast_false(state);
1762
1764 if (!c) {
1765 astman_send_error(s, m, "No such channel");
1766 return AMI_SUCCESS;
1767 }
1768
1769 if (ast_strlen_zero(mixmonitor_id)) {
1770 mutedcount = ast_audiohook_set_mute_all(c, mixmonitor_spy_type, flag, clearmute);
1771 if (mutedcount < 0) {
1773 astman_send_error(s, m, "Cannot set mute flag");
1774 return AMI_SUCCESS;
1775 }
1776 } else {
1777 if (mute_mixmonitor_instance(c, mixmonitor_id, flag, clearmute)) {
1779 astman_send_error(s, m, "Cannot set mute flag");
1780 return AMI_SUCCESS;
1781 }
1782 mutedcount = 1;
1783 }
1784
1785
1786 stasis_message_blob = ast_json_pack("{s: s, s: b, s: s, s: i}",
1787 "direction", direction,
1788 "state", ast_true(state),
1789 "mixmonitorid", mixmonitor_id,
1790 "count", mutedcount);
1791
1793 ast_channel_mixmonitor_mute_type(), stasis_message_blob);
1794
1795 if (stasis_message) {
1797 }
1798
1799 astman_append(s, "Response: Success\r\n");
1800
1801 if (!ast_strlen_zero(id)) {
1802 astman_append(s, "ActionID: %s\r\n", id);
1803 }
1804
1805 astman_append(s, "\r\n");
1806
1808
1809 return AMI_SUCCESS;
1810}
1811
1812static int start_mixmonitor_callback(struct ast_channel *chan, const char *filename, const char *options)
1813{
1814 char args[PATH_MAX];
1815
1816 if (ast_strlen_zero(options)) {
1817 snprintf(args, sizeof(args), "%s", filename);
1818 } else {
1819 snprintf(args, sizeof(args), "%s,%s", filename, options);
1820 }
1821
1822 return mixmonitor_exec(chan, args);
1823}
1824
1825static int stop_mixmonitor_callback(struct ast_channel *chan, const char *mixmonitor_id)
1826{
1827 return stop_mixmonitor_full(chan, mixmonitor_id);
1828}
1829
1830static int manager_mixmonitor(struct mansession *s, const struct message *m)
1831{
1832 struct ast_channel *c;
1833 const char *name = astman_get_header(m, "Channel");
1834 const char *id = astman_get_header(m, "ActionID");
1835 const char *file = astman_get_header(m, "File");
1836 const char *options = astman_get_header(m, "Options");
1837 const char *command = astman_get_header(m, "Command");
1838 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
1839 struct ast_flags flags = { 0 };
1840 char *uid_channel_var = NULL;
1841 const char *mixmonitor_id = NULL;
1842 int res;
1843 char args[PATH_MAX];
1844
1845 if (ast_strlen_zero(name)) {
1846 astman_send_error(s, m, "No channel specified");
1847 return AMI_SUCCESS;
1848 }
1849
1851 if (!c) {
1852 astman_send_error(s, m, "No such channel");
1853 return AMI_SUCCESS;
1854 }
1855
1856 if (!ast_strlen_zero(options)) {
1858 }
1859
1860 snprintf(args, sizeof(args), "%s,%s,%s", file, options, command);
1861
1862 res = mixmonitor_exec(c, args);
1863
1865 uid_channel_var = opts[OPT_ARG_UID];
1867 mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
1868 mixmonitor_id = ast_strdupa(S_OR(mixmonitor_id, ""));
1870 }
1871
1872 if (res) {
1874 astman_send_error(s, m, "Could not start monitoring channel");
1875 return AMI_SUCCESS;
1876 }
1877
1878 astman_append(s, "Response: Success\r\n");
1879
1880 if (!ast_strlen_zero(id)) {
1881 astman_append(s, "ActionID: %s\r\n", id);
1882 }
1883
1884 if (!ast_strlen_zero(mixmonitor_id)) {
1885 astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
1886 }
1887
1888 astman_append(s, "\r\n");
1889
1891
1892 return AMI_SUCCESS;
1893}
1894
1895static int manager_stop_mixmonitor(struct mansession *s, const struct message *m)
1896{
1897 struct ast_channel *c;
1898 const char *name = astman_get_header(m, "Channel");
1899 const char *id = astman_get_header(m, "ActionID");
1900 const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");
1901 int res;
1902
1903 if (ast_strlen_zero(name)) {
1904 astman_send_error(s, m, "No channel specified");
1905 return AMI_SUCCESS;
1906 }
1907
1909 if (!c) {
1910 astman_send_error(s, m, "No such channel");
1911 return AMI_SUCCESS;
1912 }
1913
1914 res = stop_mixmonitor_full(c, mixmonitor_id);
1915 if (res) {
1917 astman_send_error(s, m, "Could not stop monitoring channel");
1918 return AMI_SUCCESS;
1919 }
1920
1921 astman_append(s, "Response: Success\r\n");
1922
1923 if (!ast_strlen_zero(id)) {
1924 astman_append(s, "ActionID: %s\r\n", id);
1925 }
1926
1927 astman_append(s, "\r\n");
1928
1930
1931 return AMI_SUCCESS;
1932}
1933
1934static int func_mixmonitor_read(struct ast_channel *chan, const char *cmd, char *data,
1935 char *buf, size_t len)
1936{
1937 struct ast_datastore *datastore;
1938 struct mixmonitor_ds *ds_data;
1940 AST_APP_ARG(id);
1941 AST_APP_ARG(key);
1942 );
1943
1945
1946 if (ast_strlen_zero(args.id) || ast_strlen_zero(args.key)) {
1947 ast_log(LOG_WARNING, "Not enough arguments provided to %s. "
1948 "An ID and key must be provided\n", cmd);
1949 return -1;
1950 }
1951
1952 ast_channel_lock(chan);
1953 datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.id);
1954 ast_channel_unlock(chan);
1955
1956 if (!datastore) {
1957 ast_log(LOG_WARNING, "Could not find MixMonitor with ID %s\n", args.id);
1958 return -1;
1959 }
1960
1961 ds_data = datastore->data;
1962
1963 if (!strcasecmp(args.key, "filename")) {
1964 ast_copy_string(buf, ds_data->filename, len);
1965 } else {
1966 ast_log(LOG_WARNING, "Unrecognized %s option %s\n", cmd, args.key);
1967 return -1;
1968 }
1969 return 0;
1970}
1971
1973 .name = "MIXMONITOR",
1974 .read = func_mixmonitor_read,
1975};
1976
1978 AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
1979};
1980
1990
1992{
1994}
1995
1996static int unload_module(void)
1997{
1998 int res;
1999
2003 res |= ast_manager_unregister("MixMonitorMute");
2004 res |= ast_manager_unregister("MixMonitor");
2005 res |= ast_manager_unregister("StopMixMonitor");
2008
2009 return res;
2010}
2011
2027
2028AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_DEFAULT, "Mixed Audio Monitoring Application",
2029 .support_level = AST_MODULE_SUPPORT_CORE,
2030 .load = load_module,
2031 .unload = unload_module,
2032 .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:488
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:100
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
Wait for audiohook trigger to be triggered.
Definition audiohook.c:1131
void ast_audiohook_update_status(struct ast_audiohook *audiohook, enum ast_audiohook_status status)
Update audiohook's status.
Definition audiohook.c:577
#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:1432
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:587
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition audiohook.c:521
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition audiohook.c:124
#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:255
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:1457
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:1912
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition file.c:1130
int ast_filedelete(const char *filename, const char *fmt)
Deletes a file.
Definition file.c:1160
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:2000
const char * astman_get_header(const struct message *m, char *var)
Get header from manager transaction.
Definition manager.c:1661
void astman_append(struct mansession *s, const char *fmt,...)
Definition manager.c:1921
int ast_manager_unregister(const char *action)
Unregister a registered manager command.
Definition manager.c:7716
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:2268
int ast_callid_threadassoc_add(ast_callid callid)
Adds a known callid to thread storage of the calling thread.
Definition logger.c:2290
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 @522 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:323
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