Wed Jan 8 2020 09:49:40

Asterisk developer's documentation


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  <support_level>core</support_level>
38  ***/
39 
40 #include "asterisk.h"
41 
42 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 404044 $")
43 
44 #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/test.h"
55 
56 /*** DOCUMENTATION
57  <application name="MixMonitor" language="en_US">
58  <synopsis>
59  Record a call and mix the audio during the recording. Use of StopMixMonitor is required
60  to guarantee the audio file is available for processing during dialplan execution.
61  </synopsis>
62  <syntax>
63  <parameter name="file" required="true" argsep=".">
64  <argument name="filename" required="true">
65  <para>If <replaceable>filename</replaceable> is an absolute path, uses that path, otherwise
66  creates the file in the configured monitoring directory from <filename>asterisk.conf.</filename></para>
67  </argument>
68  <argument name="extension" required="true" />
69  </parameter>
70  <parameter name="options">
71  <optionlist>
72  <option name="a">
73  <para>Append to the file instead of overwriting it.</para>
74  </option>
75  <option name="b">
76  <para>Only save audio to the file while the channel is bridged.</para>
77  <note><para>Does not include conferences or sounds played to each bridged party</para></note>
78  <note><para>If you utilize this option inside a Local channel, you must make sure the Local
79  channel is not optimized away. To do this, be sure to call your Local channel with the
80  <literal>/n</literal> option. For example: Dial(Local/start@mycontext/n)</para></note>
81  </option>
82  <option name="v">
83  <para>Adjust the <emphasis>heard</emphasis> volume by a factor of <replaceable>x</replaceable>
84  (range <literal>-4</literal> to <literal>4</literal>)</para>
85  <argument name="x" required="true" />
86  </option>
87  <option name="V">
88  <para>Adjust the <emphasis>spoken</emphasis> volume by a factor
89  of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
90  <argument name="x" required="true" />
91  </option>
92  <option name="W">
93  <para>Adjust both, <emphasis>heard and spoken</emphasis> volumes by a factor
94  of <replaceable>x</replaceable> (range <literal>-4</literal> to <literal>4</literal>)</para>
95  <argument name="x" required="true" />
96  </option>
97  </optionlist>
98  </parameter>
99  <parameter name="command">
100  <para>Will be executed when the recording is over.</para>
101  <para>Any strings matching <literal>^{X}</literal> will be unescaped to <variable>X</variable>.</para>
102  <para>All variables will be evaluated at the time MixMonitor is called.</para>
103  </parameter>
104  </syntax>
105  <description>
106  <para>Records the audio on the current channel to the specified file.</para>
107  <para>This application does not automatically answer and should be preceeded by
108  an application such as Answer or Progress().</para>
109  <note><para>MixMonitor runs as an audiohook. In order to keep it running through
110  a transfer, AUDIOHOOK_INHERIT must be set for the channel which ran mixmonitor.
111  For more information, including dialplan configuration set for using
112  AUDIOHOOK_INHERIT with MixMonitor, see the function documentation for
113  AUDIOHOOK_INHERIT.</para></note>
114  <variablelist>
115  <variable name="MIXMONITOR_FILENAME">
116  <para>Will contain the filename used to record.</para>
117  </variable>
118  </variablelist>
119  </description>
120  <see-also>
121  <ref type="application">Monitor</ref>
122  <ref type="application">StopMixMonitor</ref>
123  <ref type="application">PauseMonitor</ref>
124  <ref type="application">UnpauseMonitor</ref>
125  <ref type="function">AUDIOHOOK_INHERIT</ref>
126  </see-also>
127  </application>
128  <application name="StopMixMonitor" language="en_US">
129  <synopsis>
130  Stop recording a call through MixMonitor, and free the recording's file handle.
131  </synopsis>
132  <syntax />
133  <description>
134  <para>Stops the audio recording that was started with a call to <literal>MixMonitor()</literal>
135  on the current channel.</para>
136  </description>
137  <see-also>
138  <ref type="application">MixMonitor</ref>
139  </see-also>
140  </application>
141  <manager name="MixMonitorMute" language="en_US">
142  <synopsis>
143  Mute / unMute a Mixmonitor recording.
144  </synopsis>
145  <syntax>
146  <xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
147  <parameter name="Channel" required="true">
148  <para>Used to specify the channel to mute.</para>
149  </parameter>
150  <parameter name="Direction">
151  <para>Which part of the recording to mute: read, write or both (from channel, to channel or both channels).</para>
152  </parameter>
153  <parameter name="State">
154  <para>Turn mute on or off : 1 to turn on, 0 to turn off.</para>
155  </parameter>
156  </syntax>
157  <description>
158  <para>This action may be used to mute a MixMonitor recording.</para>
159  </description>
160  </manager>
161 
162  ***/
163 
164 #define get_volfactor(x) x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
165 
166 static const char * const app = "MixMonitor";
167 
168 static const char * const stop_app = "StopMixMonitor";
169 
170 static const char * const mixmonitor_spy_type = "MixMonitor";
171 
172 struct mixmonitor {
174  char *filename;
176  char *name;
177  unsigned int flags;
180 };
181 
183  MUXFLAG_APPEND = (1 << 1),
184  MUXFLAG_BRIDGED = (1 << 2),
185  MUXFLAG_VOLUME = (1 << 3),
186  MUXFLAG_READVOLUME = (1 << 4),
188 };
189 
195 };
196 
203 });
204 
206  unsigned int destruction_ok;
209 
210  /* The filestream is held in the datastore so it can be stopped
211  * immediately during stop_mixmonitor or channel destruction. */
212  int fs_quit;
215 };
216 
217 /*!
218  * \internal
219  * \pre mixmonitor_ds must be locked before calling this function
220  */
222 {
223  if (mixmonitor_ds->fs) {
224  ast_closestream(mixmonitor_ds->fs);
225  mixmonitor_ds->fs = NULL;
226  mixmonitor_ds->fs_quit = 1;
227  ast_verb(2, "MixMonitor close filestream\n");
228  }
229 }
230 
231 static void mixmonitor_ds_destroy(void *data)
232 {
233  struct mixmonitor_ds *mixmonitor_ds = data;
234 
235  ast_mutex_lock(&mixmonitor_ds->lock);
236  mixmonitor_ds->audiohook = NULL;
237  mixmonitor_ds->destruction_ok = 1;
238  ast_cond_signal(&mixmonitor_ds->destruction_condition);
239  ast_mutex_unlock(&mixmonitor_ds->lock);
240 }
241 
242 static const struct ast_datastore_info mixmonitor_ds_info = {
243  .type = "mixmonitor",
244  .destroy = mixmonitor_ds_destroy,
245 };
246 
248 {
249  if (mixmonitor->mixmonitor_ds) {
250  ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
251  mixmonitor->mixmonitor_ds->audiohook = NULL;
252  ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
253  }
254  /* kill the audiohook.*/
255  ast_audiohook_lock(&mixmonitor->audiohook);
256  ast_audiohook_detach(&mixmonitor->audiohook);
257  ast_audiohook_unlock(&mixmonitor->audiohook);
258  ast_audiohook_destroy(&mixmonitor->audiohook);
259 }
260 
261 static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
262 {
263  struct ast_channel *peer = NULL;
264  int res = 0;
265 
266  if (!chan)
267  return -1;
268 
269  ast_audiohook_attach(chan, audiohook);
270 
271  if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
273 
274  return res;
275 }
276 
277 #define SAMPLES_PER_FRAME 160
278 
280 {
281  if (mixmonitor) {
282  if (mixmonitor->mixmonitor_ds) {
283  ast_mutex_destroy(&mixmonitor->mixmonitor_ds->lock);
285  ast_free(mixmonitor->mixmonitor_ds);
286  }
287  ast_free(mixmonitor);
288  }
289 }
290 static void *mixmonitor_thread(void *obj)
291 {
292  struct mixmonitor *mixmonitor = obj;
293  struct ast_filestream **fs = NULL;
294  unsigned int oflags;
295  char *ext;
296  char *last_slash;
297  int errflag = 0;
298 
299  ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);
300 
301  fs = &mixmonitor->mixmonitor_ds->fs;
302 
303  /* The audiohook must enter and exit the loop locked */
304  ast_audiohook_lock(&mixmonitor->audiohook);
305  while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
306  struct ast_frame *fr = NULL;
307 
310 
311  if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
312  break;
313  }
314  continue;
315  }
316 
317  /* audiohook lock is not required for the next block.
318  * Unlock it, but remember to lock it before looping or exiting */
319  ast_audiohook_unlock(&mixmonitor->audiohook);
320 
321  if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
322  ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
323  /* Initialize the file if not already done so */
324  if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
325  oflags = O_CREAT | O_WRONLY;
326  oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;
327 
328  last_slash = strrchr(mixmonitor->filename, '/');
329  if ((ext = strrchr(mixmonitor->filename, '.')) && (ext > last_slash))
330  *(ext++) = '\0';
331  else
332  ext = "raw";
333 
334  if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
335  ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
336  errflag = 1;
337  }
338  }
339 
340  /* Write out the frame(s) */
341  if (*fs) {
342  struct ast_frame *cur;
343 
344  for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
345  ast_writestream(*fs, cur);
346  }
347  }
348  ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
349  }
350  /* All done! free it. */
351  ast_frame_free(fr, 0);
352 
353  ast_audiohook_lock(&mixmonitor->audiohook);
354  }
355 
356  /* Test Event */
357  ast_test_suite_event_notify("MIXMONITOR_END", "Channel: %s\r\n"
358  "File: %s\r\n",
359  mixmonitor->autochan->chan->name,
360  mixmonitor->filename);
361 
362  ast_audiohook_unlock(&mixmonitor->audiohook);
363 
364  ast_autochan_destroy(mixmonitor->autochan);
365 
366  /* Datastore cleanup. close the filestream and wait for ds destruction */
367  ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
369  if (!mixmonitor->mixmonitor_ds->destruction_ok) {
371  }
372  ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
373 
374  /* kill the audiohook */
375  destroy_monitor_audiohook(mixmonitor);
376 
377  if (mixmonitor->post_process) {
378  ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
379  ast_safe_system(mixmonitor->post_process);
380  }
381 
382  ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);
383  mixmonitor_free(mixmonitor);
384 
386  return NULL;
387 }
388 
389 static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
390 {
391  struct ast_datastore *datastore = NULL;
393 
394  if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
395  return -1;
396  }
397 
398  ast_mutex_init(&mixmonitor_ds->lock);
399  ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);
400 
401  if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, NULL))) {
402  ast_mutex_destroy(&mixmonitor_ds->lock);
403  ast_cond_destroy(&mixmonitor_ds->destruction_condition);
404  ast_free(mixmonitor_ds);
405  return -1;
406  }
407 
408  mixmonitor_ds->audiohook = &mixmonitor->audiohook;
409  datastore->data = mixmonitor_ds;
410 
411  ast_channel_lock(chan);
412  ast_channel_datastore_add(chan, datastore);
413  ast_channel_unlock(chan);
414 
415  mixmonitor->mixmonitor_ds = mixmonitor_ds;
416  return 0;
417 }
418 
419 static int launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags,
420  int readvol, int writevol, const char *post_process)
421 {
422  pthread_t thread;
423  struct mixmonitor *mixmonitor;
424  char postprocess2[1024] = "";
425  size_t len;
426 
427  len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;
428 
429  postprocess2[0] = 0;
430  /* If a post process system command is given attach it to the structure */
431  if (!ast_strlen_zero(post_process)) {
432  char *p1, *p2;
433 
434  p1 = ast_strdupa(post_process);
435  for (p2 = p1; *p2 ; p2++) {
436  if (*p2 == '^' && *(p2+1) == '{') {
437  *p2 = '$';
438  }
439  }
440  pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
441  if (!ast_strlen_zero(postprocess2))
442  len += strlen(postprocess2) + 1;
443  }
444 
445  /* Pre-allocate mixmonitor structure and spy */
446  if (!(mixmonitor = ast_calloc(1, len))) {
447  return -1;
448  }
449 
450  /* Setup the actual spy before creating our thread */
451  if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) {
452  mixmonitor_free(mixmonitor);
453  return -1;
454  }
455 
456  /* Copy over flags and channel name */
457  mixmonitor->flags = flags;
458  if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
459  mixmonitor_free(mixmonitor);
460  return -1;
461  }
462 
463  if (setup_mixmonitor_ds(mixmonitor, chan)) {
464  ast_autochan_destroy(mixmonitor->autochan);
465  mixmonitor_free(mixmonitor);
466  return -1;
467  }
468  mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
469  strcpy(mixmonitor->name, chan->name);
470  if (!ast_strlen_zero(postprocess2)) {
471  mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
472  strcpy(mixmonitor->post_process, postprocess2);
473  }
474 
475  mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
476  strcpy(mixmonitor->filename, filename);
477 
479 
480  if (readvol)
481  mixmonitor->audiohook.options.read_volume = readvol;
482  if (writevol)
483  mixmonitor->audiohook.options.write_volume = writevol;
484 
485  if (startmon(chan, &mixmonitor->audiohook)) {
486  ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
487  mixmonitor_spy_type, chan->name);
488  ast_audiohook_destroy(&mixmonitor->audiohook);
489  mixmonitor_free(mixmonitor);
490  return -1;
491  }
492 
493  return ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
494 }
495 
496 static int mixmonitor_exec(struct ast_channel *chan, const char *data)
497 {
498  int x, readvol = 0, writevol = 0;
499  struct ast_flags flags = {0};
500  char *parse, *tmp, *slash;
502  AST_APP_ARG(filename);
503  AST_APP_ARG(options);
504  AST_APP_ARG(post_process);
505  );
506 
507  if (ast_strlen_zero(data)) {
508  ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
509  return -1;
510  }
511 
512  parse = ast_strdupa(data);
513 
514  AST_STANDARD_APP_ARGS(args, parse);
515 
516  if (ast_strlen_zero(args.filename)) {
517  ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
518  return -1;
519  }
520 
521  if (args.options) {
522  char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
523 
524  ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);
525 
526  if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
527  if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
528  ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
529  } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
530  ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
531  } else {
532  readvol = get_volfactor(x);
533  }
534  }
535 
536  if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
538  ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
539  } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
540  ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
541  } else {
542  writevol = get_volfactor(x);
543  }
544  }
545 
546  if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
547  if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
548  ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
549  } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
550  ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
551  } else {
552  readvol = writevol = get_volfactor(x);
553  }
554  }
555  }
556 
557  /* if not provided an absolute path, use the system-configured monitoring directory */
558  if (args.filename[0] != '/') {
559  char *build;
560 
561  build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
562  sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
563  args.filename = build;
564  }
565 
566  tmp = ast_strdupa(args.filename);
567  if ((slash = strrchr(tmp, '/')))
568  *slash = '\0';
569  ast_mkdir(tmp, 0777);
570 
571  pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
572 
573  /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
575  if (launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process)) {
577  }
578 
579  return 0;
580 }
581 
582 static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
583 {
584  struct ast_datastore *datastore = NULL;
585 
586  ast_channel_lock(chan);
587  ast_audiohook_detach_source(chan, mixmonitor_spy_type);
588  if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
589  struct mixmonitor_ds *mixmonitor_ds = datastore->data;
590 
591  ast_mutex_lock(&mixmonitor_ds->lock);
592 
593  /* closing the filestream here guarantees the file is available to the dialplan
594  * after calling StopMixMonitor */
595  mixmonitor_ds_close_fs(mixmonitor_ds);
596 
597  /* The mixmonitor thread may be waiting on the audiohook trigger.
598  * In order to exit from the mixmonitor loop before waiting on channel
599  * destruction, poke the audiohook trigger. */
600  if (mixmonitor_ds->audiohook) {
601  ast_audiohook_lock(mixmonitor_ds->audiohook);
602  ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
603  ast_audiohook_unlock(mixmonitor_ds->audiohook);
604  mixmonitor_ds->audiohook = NULL;
605  }
606 
607  ast_mutex_unlock(&mixmonitor_ds->lock);
608 
609  /* Remove the datastore so the monitor thread can exit */
610  if (!ast_channel_datastore_remove(chan, datastore)) {
611  ast_datastore_free(datastore);
612  }
613  }
614  ast_channel_unlock(chan);
615 
616  return 0;
617 }
618 
619 static char *handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
620 {
621  struct ast_channel *chan;
622 
623  switch (cmd) {
624  case CLI_INIT:
625  e->command = "mixmonitor {start|stop}";
626  e->usage =
627  "Usage: mixmonitor <start|stop> <chan_name> [args]\n"
628  " The optional arguments are passed to the MixMonitor\n"
629  " application when the 'start' command is used.\n";
630  return NULL;
631  case CLI_GENERATE:
632  return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
633  }
634 
635  if (a->argc < 3)
636  return CLI_SHOWUSAGE;
637 
638  if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
639  ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
640  /* Technically this is a failure, but we don't want 2 errors printing out */
641  return CLI_SUCCESS;
642  }
643 
644  ast_channel_lock(chan);
645 
646  if (!strcasecmp(a->argv[1], "start")) {
647  mixmonitor_exec(chan, a->argv[3]);
648  ast_channel_unlock(chan);
649  } else {
650  ast_channel_unlock(chan);
651  ast_audiohook_detach_source(chan, mixmonitor_spy_type);
652  }
653 
654  chan = ast_channel_unref(chan);
655 
656  return CLI_SUCCESS;
657 }
658 
659 /*! \brief Mute / unmute a MixMonitor channel */
660 static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
661 {
662  struct ast_channel *c = NULL;
663 
664  const char *name = astman_get_header(m, "Channel");
665  const char *id = astman_get_header(m, "ActionID");
666  const char *state = astman_get_header(m, "State");
667  const char *direction = astman_get_header(m,"Direction");
668 
669  int clearmute = 1;
670 
671  enum ast_audiohook_flags flag;
672 
673  if (ast_strlen_zero(direction)) {
674  astman_send_error(s, m, "No direction specified. Must be read, write or both");
675  return AMI_SUCCESS;
676  }
677 
678  if (!strcasecmp(direction, "read")) {
680  } else if (!strcasecmp(direction, "write")) {
682  } else if (!strcasecmp(direction, "both")) {
684  } else {
685  astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
686  return AMI_SUCCESS;
687  }
688 
689  if (ast_strlen_zero(name)) {
690  astman_send_error(s, m, "No channel specified");
691  return AMI_SUCCESS;
692  }
693 
694  if (ast_strlen_zero(state)) {
695  astman_send_error(s, m, "No state specified");
696  return AMI_SUCCESS;
697  }
698 
699  clearmute = ast_false(state);
700  c = ast_channel_get_by_name(name);
701 
702  if (!c) {
703  astman_send_error(s, m, "No such channel");
704  return AMI_SUCCESS;
705  }
706 
707  if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
708  c = ast_channel_unref(c);
709  astman_send_error(s, m, "Cannot set mute flag");
710  return AMI_SUCCESS;
711  }
712 
713  astman_append(s, "Response: Success\r\n");
714 
715  if (!ast_strlen_zero(id)) {
716  astman_append(s, "ActionID: %s\r\n", id);
717  }
718 
719  astman_append(s, "\r\n");
720 
721  c = ast_channel_unref(c);
722 
723  return AMI_SUCCESS;
724 }
725 
726 static struct ast_cli_entry cli_mixmonitor[] = {
727  AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
728 };
729 
730 static int unload_module(void)
731 {
732  int res;
733 
734  ast_cli_unregister_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
735  res = ast_unregister_application(stop_app);
736  res |= ast_unregister_application(app);
737  res |= ast_manager_unregister("MixMonitorMute");
738 
739  return res;
740 }
741 
742 static int load_module(void)
743 {
744  int res;
745 
746  ast_cli_register_multiple(cli_mixmonitor, ARRAY_LEN(cli_mixmonitor));
749  res |= ast_manager_register_xml("MixMonitorMute", 0, manager_mute_mixmonitor);
750 
751  return res;
752 }
753 
754 AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Mixed Audio Monitoring Application");
const char * type
Definition: datastore.h:32
static int stop_mixmonitor_exec(struct ast_channel *chan, const char *data)
void pbx_substitute_variables_helper(struct ast_channel *c, const char *cp1, char *cp2, int count)
Definition: pbx.c:4676
void ast_frame_free(struct ast_frame *fr, int cache)
Requests a frame to be allocated.
Definition: frame.c:375
pthread_t thread
Definition: app_meetme.c:962
#define ast_channel_lock(chan)
Definition: channel.h:2466
Main Channel structure associated with a channel.
Definition: channel.h:742
static int launch_monitor_thread(struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process)
#define AST_CLI_DEFINE(fn, txt,...)
Definition: cli.h:191
struct ast_frame * ast_audiohook_read_frame(struct ast_audiohook *audiohook, size_t samples, enum ast_audiohook_direction direction, format_t format)
Reads a frame in from the audiohook structure.
Definition: audiohook.c:313
#define AST_MODULE_INFO_STANDARD(keystr, desc)
Definition: module.h:396
#define AST_APP_OPTION_ARG(option, flagno, argno)
Declares an application option that accepts an argument.
Definition: app.h:732
ast_audiohook_flags
Definition: audiohook.h:60
int ast_safe_system(const char *s)
Safely spawn an external program while closing file descriptors.
Definition: asterisk.c:1077
void astman_append(struct mansession *s, const char *fmt,...)
Definition: manager.c:2068
Asterisk main include file. File version handling, generic pbx functions.
#define AST_APP_OPTIONS(holder, options...)
Declares an array of options for an application.
Definition: app.h:712
#define ARRAY_LEN(a)
Definition: isdn_lib.c:42
void ast_module_unref(struct ast_module *)
Definition: loader.c:1312
static void destroy_monitor_audiohook(struct mixmonitor *mixmonitor)
#define ast_alloca(size)
call __builtin_alloca to ensure we get gcc builtin semantics
Definition: utils.h:653
int ast_cli_unregister_multiple(struct ast_cli_entry *e, int len)
Unregister multiple commands.
Definition: cli.c:2177
struct ast_filestream * fs
#define ast_channel_unref(c)
Decrease channel reference count.
Definition: channel.h:2502
#define ast_test_flag(p, flag)
Definition: utils.h:63
unsigned int destruction_ok
#define ast_set_flag(p, flag)
Definition: utils.h:70
int ast_audiohook_init(struct ast_audiohook *audiohook, enum ast_audiohook_type type, const char *source)
Initialize an audiohook structure.
Definition: audiohook.c:64
descriptor for a cli entry.
Definition: cli.h:165
const int argc
Definition: cli.h:154
#define LOG_WARNING
Definition: logger.h:144
Audiohooks Architecture.
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: app.c:2101
#define AST_DECLARE_APP_ARGS(name, arglist)
Declare a structure to hold an application&#39;s arguments.
Definition: app.h:572
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook)
unsigned int flags
Definition: utils.h:201
static void mixmonitor_ds_destroy(void *data)
int ast_audiohook_set_mute(struct ast_channel *chan, const char *source, enum ast_audiohook_flags flag, int clear)
Mute frames read from or written to a channel.
Definition: audiohook.c:1057
#define AST_LIST_NEXT(elm, field)
Returns the next entry in the list after the given entry.
Definition: linkedlists.h:438
Test Framework API.
Definition: cli.h:146
Structure for a data store type.
Definition: datastore.h:31
int ast_audiohook_attach(struct ast_channel *chan, struct ast_audiohook *audiohook)
Attach audiohook to channel.
Definition: audiohook.c:348
#define ast_cond_wait(cond, mutex)
Definition: lock.h:171
#define ast_cond_init(cond, attr)
Definition: lock.h:167
struct ast_channel * chan
Definition: autochan.h:33
char * post_process
#define ast_mutex_lock(a)
Definition: lock.h:155
struct mixmonitor_ds * mixmonitor_ds
static int manager_mute_mixmonitor(struct mansession *s, const struct message *m)
Mute / unmute a MixMonitor channel.
Structure for a data store object.
Definition: datastore.h:54
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:2604
Generic File Format Support. Should be included by clients of the file handling routines. File service providers should instead include mod_format.h.
void ast_cli(int fd, const char *fmt,...)
Definition: cli.c:105
const char * ext
Definition: http.c:112
int ast_unregister_application(const char *app)
Unregister an application.
Definition: pbx.c:7705
int ast_audiohook_destroy(struct ast_audiohook *audiohook)
Destroys an audiohook structure.
Definition: audiohook.c:96
#define ast_cond_signal(cond)
Definition: lock.h:169
#define ast_pthread_create_detached_background(a, b, c, d)
Definition: utils.h:431
#define ast_verb(level,...)
Definition: logger.h:243
const char * line
Definition: cli.h:156
int ast_datastore_free(struct ast_datastore *datastore)
Free a data store object.
Definition: datastore.c:65
static int mixmonitor_exec(struct ast_channel *chan, const char *data)
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:1808
const char * astman_get_header(const struct message *m, char *var)
Get header from mananger transaction.
Definition: manager.c:1860
pthread_cond_t ast_cond_t
Definition: lock.h:144
ast_cond_t destruction_condition
mixmonitor_args
#define get_volfactor(x)
#define ast_audiohook_unlock(ah)
Unlock an audiohook.
Definition: audiohook.h:272
static void mixmonitor_free(struct mixmonitor *mixmonitor)
static const char *const stop_app
struct ast_module * self
Definition: module.h:227
General Asterisk PBX channel definitions.
static const char *const mixmonitor_spy_type
const int fd
Definition: cli.h:153
#define ast_manager_register_xml(a, b, c)
Register a manager callback using XML documentation to describe the manager.
Definition: manager.h:172
static force_inline int attribute_pure ast_strlen_zero(const char *s)
Definition: strings.h:63
const int n
Definition: cli.h:159
int ast_softhangup(struct ast_channel *chan, int reason)
Softly hangup up a channel.
Definition: channel.c:2746
mixmonitor_flags
int ast_audiohook_detach_source(struct ast_channel *chan, const char *source)
Detach specified source audiohook from channel.
Definition: audiohook.c:509
Core PBX routines and definitions.
static int setup_mixmonitor_ds(struct mixmonitor *mixmonitor, struct ast_channel *chan)
#define ast_test_suite_event_notify(s, f,...)
Definition: test.h:184
static int unload_module(void)
const char *const * argv
Definition: cli.h:155
The AMI - Asterisk Manager Interface - is a TCP protocol created to manage Asterisk with third-party ...
#define ast_strdupa(s)
duplicate a string in memory from the stack
Definition: utils.h:663
&quot;smart&quot; channels that update automatically if a channel is masqueraded
int ast_audiohook_detach(struct ast_audiohook *audiohook)
Detach audiohook from channel.
Definition: audiohook.c:401
#define LOG_ERROR
Definition: logger.h:155
static void mixmonitor_ds_close_fs(struct mixmonitor_ds *mixmonitor_ds)
static struct @350 args
#define CLI_SHOWUSAGE
Definition: cli.h:44
static int len(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t buflen)
struct ast_channel * ast_bridged_channel(struct ast_channel *chan)
Find bridged channel.
Definition: channel.c:7160
const ast_string_field name
Definition: channel.h:787
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:1049
void ast_log(int level, const char *file, int line, const char *function, const char *fmt,...)
Used for sending a log message This is the standard logger function. Probably the only way you will i...
Definition: logger.c:1207
#define ast_cond_destroy(cond)
Definition: lock.h:168
struct ast_datastore * ast_datastore_alloc(const struct ast_datastore_info *info, const char *uid)
Definition: datastore.c:98
#define LOG_NOTICE
Definition: logger.h:133
#define ast_channel_unlock(chan)
Definition: channel.h:2467
static void parse(struct mgcp_request *req)
Definition: chan_mgcp.c:1858
static const char name[]
ast_mutex_t lock
#define ast_free(a)
Definition: astmm.h:97
char * command
Definition: cli.h:180
const char * ast_config_AST_MONITOR_DIR
Definition: asterisk.c:260
char * filename
int ast_closestream(struct ast_filestream *f)
Closes a stream.
Definition: file.c:904
static char * handle_cli_mixmonitor(struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
#define SAMPLES_PER_FRAME
const char * word
Definition: cli.h:157
static const char *const app
Structure used to handle boolean flags.
Definition: utils.h:200
ast_cond_t trigger
Definition: audiohook.h:105
const char * usage
Definition: cli.h:171
void ast_autochan_destroy(struct ast_autochan *autochan)
destroy an ast_autochan structure
Definition: autochan.c:63
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...
Definition: pbx.c:10546
#define CLI_SUCCESS
Definition: cli.h:43
struct ast_autochan * ast_autochan_setup(struct ast_channel *chan)
set up a new ast_autochan structure
Definition: autochan.c:40
void * data
Definition: datastore.h:56
struct ast_audiohook_options options
Definition: audiohook.h:117
This structure is allocated by file.c in one chunk, together with buf_size and desc_size bytes of mem...
Definition: mod_format.h:100
static void * mixmonitor_thread(void *obj)
#define AST_FORMAT_SLINEAR
Definition: frame.h:254
Standard Command Line Interface.
#define ast_calloc(a, b)
Definition: astmm.h:82
unsigned int flags
int ast_writestream(struct ast_filestream *fs, struct ast_frame *f)
Writes a frame to a stream.
Definition: file.c:150
int attribute_pure ast_false(const char *val)
Make sure something is false. Determine if a string containing a boolean value is &quot;false&quot;...
Definition: utils.c:1550
int ast_cli_register_multiple(struct ast_cli_entry *e, int len)
Register multiple commands.
Definition: cli.c:2167
const int pos
Definition: cli.h:158
Application convenience functions, designed to give consistent look and feel to Asterisk apps...
struct ast_audiohook * audiohook
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: cli.c:1547
Data structure associated with a single frame of data.
Definition: frame.h:142
enum ast_audiohook_status status
Definition: audiohook.h:107
#define AST_APP_ARG(name)
Define an application argument.
Definition: app.h:555
static struct ast_datastore_info mixmonitor_ds_info
static struct ast_cli_entry cli_mixmonitor[]
#define ast_mutex_init(pmutex)
Definition: lock.h:152
#define AST_STANDARD_APP_ARGS(args, parse)
Performs the &#39;standard&#39; argument separation process for an application.
Definition: app.h:604
#define ast_mutex_destroy(a)
Definition: lock.h:154
#define ASTERISK_GPL_KEY
The text the key() function should return.
Definition: module.h:38
static struct ast_app_option mixmonitor_opts[128]
struct ast_audiohook audiohook
struct ast_channel * ast_channel_get_by_name(const char *name)
Find a channel by name.
Definition: channel.c:1803
#define ast_audiohook_lock(ah)
Lock an audiohook.
Definition: audiohook.h:267
void astman_send_error(struct mansession *s, const struct message *m, char *error)
Send error in manager transaction.
Definition: manager.c:2130
Asterisk module definitions.
int ast_channel_datastore_add(struct ast_channel *chan, struct ast_datastore *datastore)
Add a datastore to a channel.
Definition: channel.c:2590
struct ast_autochan * autochan
void ast_audiohook_trigger_wait(struct ast_audiohook *audiohook)
Wait for audiohook trigger to be triggered.
Definition: audiohook.c:776
int ast_channel_datastore_remove(struct ast_channel *chan, struct ast_datastore *datastore)
Remove a datastore from a channel.
Definition: channel.c:2599
#define ast_register_application_xml(app, execute)
Register an application using XML documentation.
Definition: module.h:437
#define AMI_SUCCESS
Definition: manager.h:65
Structure for mutex and tracking information.
Definition: lock.h:121
int ast_manager_unregister(char *action)
Unregister a registered manager command.
Definition: manager.c:5355
#define ASTERISK_FILE_VERSION(file, version)
Register/unregister a source code file with the core.
Definition: asterisk.h:180
#define AST_APP_OPTION(option, flagno)
Declares an application option that does not accept an argument.
Definition: app.h:721
#define ast_mutex_unlock(a)
Definition: lock.h:156
int ast_mkdir(const char *path, int mode)
Recursively create directory path.
Definition: utils.c:2151
struct ast_module * ast_module_ref(struct ast_module *)
Definition: loader.c:1300
static int load_module(void)