Sat Apr 26 2014 22:01:48

Asterisk developer's documentation


app_mixmonitor.c File Reference

MixMonitor() - Record a call and mix the audio during the recording. More...

#include "asterisk.h"
#include "asterisk/paths.h"
#include "asterisk/stringfields.h"
#include "asterisk/file.h"
#include "asterisk/audiohook.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/cli.h"
#include "asterisk/app.h"
#include "asterisk/channel.h"
#include "asterisk/autochan.h"
#include "asterisk/manager.h"
#include "asterisk/callerid.h"
#include "asterisk/mod_format.h"
#include "asterisk/linkedlists.h"
#include "asterisk/test.h"
Include dependency graph for app_mixmonitor.c:

Go to the source code of this file.

Data Structures

struct  mixmonitor
struct  mixmonitor_ds
struct  vm_recipient

Defines

#define get_volfactor(x)   x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0
#define SAMPLES_PER_FRAME   160

Enumerations

enum  mixmonitor_args {
  OPT_ARG_READVOLUME = 0, OPT_ARG_WRITEVOLUME, OPT_ARG_VOLUME, OPT_ARG_WRITENAME,
  OPT_ARG_READNAME, OPT_ARG_UID, OPT_ARG_VMRECIPIENTS, OPT_ARG_ARRAY_SIZE
}
enum  mixmonitor_flags {
  MUXFLAG_APPEND = (1 << 1), MUXFLAG_BRIDGED = (1 << 2), MUXFLAG_VOLUME = (1 << 3), MUXFLAG_READVOLUME = (1 << 4),
  MUXFLAG_WRITEVOLUME = (1 << 5), MUXFLAG_READ = (1 << 6), MUXFLAG_WRITE = (1 << 7), MUXFLAG_COMBINED = (1 << 8),
  MUXFLAG_UID = (1 << 9), MUXFLAG_VMRECIPIENTS = (1 << 10)
}

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void add_vm_recipients_from_string (struct mixmonitor *mixmonitor, const char *vm_recipients)
static void clear_mixmonitor_recipient_list (struct mixmonitor *mixmonitor)
static void copy_to_voicemail (struct mixmonitor *mixmonitor, const char *ext, const char *filename)
static void destroy_monitor_audiohook (struct mixmonitor *mixmonitor)
static char * filename_parse (char *filename, char *buffer, size_t len)
static char * handle_cli_mixmonitor (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
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)
static int load_module (void)
static int manager_mixmonitor (struct mansession *s, const struct message *m)
static int manager_mute_mixmonitor (struct mansession *s, const struct message *m)
 Mute / unmute a MixMonitor channel.
static int manager_stop_mixmonitor (struct mansession *s, const struct message *m)
static void mixmonitor_ds_close_fs (struct mixmonitor_ds *mixmonitor_ds)
static void mixmonitor_ds_destroy (void *data)
static int mixmonitor_exec (struct ast_channel *chan, const char *data)
static void mixmonitor_free (struct mixmonitor *mixmonitor)
static void mixmonitor_save_prep (struct mixmonitor *mixmonitor, char *filename, struct ast_filestream **fs, unsigned int *oflags, int *errflag, char **ext)
static void * mixmonitor_thread (void *obj)
static int setup_mixmonitor_ds (struct mixmonitor *mixmonitor, struct ast_channel *chan, char **datastore_id)
static int startmon (struct ast_channel *chan, struct ast_audiohook *audiohook)
static int stop_mixmonitor_exec (struct ast_channel *chan, const char *data)
static int stop_mixmonitor_full (struct ast_channel *chan, const char *data)
static int unload_module (void)

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Mixed Audio Monitoring Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, }
static const char *const app = "MixMonitor"
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_mixmonitor []
static struct ast_datastore_info mixmonitor_ds_info
static struct ast_app_option mixmonitor_opts [128] = { [ 'a' ] = { .flag = MUXFLAG_APPEND }, [ 'b' ] = { .flag = MUXFLAG_BRIDGED }, [ 'v' ] = { .flag = MUXFLAG_READVOLUME , .arg_index = OPT_ARG_READVOLUME + 1 }, [ 'V' ] = { .flag = MUXFLAG_WRITEVOLUME , .arg_index = OPT_ARG_WRITEVOLUME + 1 }, [ 'W' ] = { .flag = MUXFLAG_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'r' ] = { .flag = MUXFLAG_READ , .arg_index = OPT_ARG_READNAME + 1 }, [ 't' ] = { .flag = MUXFLAG_WRITE , .arg_index = OPT_ARG_WRITENAME + 1 }, [ 'i' ] = { .flag = MUXFLAG_UID , .arg_index = OPT_ARG_UID + 1 }, [ 'm' ] = { .flag = MUXFLAG_VMRECIPIENTS , .arg_index = OPT_ARG_VMRECIPIENTS + 1 },}
static const char *const mixmonitor_spy_type = "MixMonitor"
static const char *const stop_app = "StopMixMonitor"

Detailed Description

MixMonitor() - Record a call and mix the audio during the recording.

Author:
Mark Spencer <markster@digium.com>
Kevin P. Fleming <kpfleming@digium.com>
Note:
Based on app_muxmon.c provided by Anthony Minessale II <anthmct@yahoo.com>

Definition in file app_mixmonitor.c.


Define Documentation

#define get_volfactor (   x)    x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0

Definition at line 248 of file app_mixmonitor.c.

Referenced by mixmonitor_exec().

#define SAMPLES_PER_FRAME   160

Definition at line 492 of file app_mixmonitor.c.

Referenced by mixmonitor_thread().


Enumeration Type Documentation

Enumerator:
OPT_ARG_READVOLUME 
OPT_ARG_WRITEVOLUME 
OPT_ARG_VOLUME 
OPT_ARG_WRITENAME 
OPT_ARG_READNAME 
OPT_ARG_UID 
OPT_ARG_VMRECIPIENTS 
OPT_ARG_ARRAY_SIZE 

Definition at line 307 of file app_mixmonitor.c.

Enumerator:
MUXFLAG_APPEND 
MUXFLAG_BRIDGED 
MUXFLAG_VOLUME 
MUXFLAG_READVOLUME 
MUXFLAG_WRITEVOLUME 
MUXFLAG_READ 
MUXFLAG_WRITE 
MUXFLAG_COMBINED 
MUXFLAG_UID 
MUXFLAG_VMRECIPIENTS 

Definition at line 294 of file app_mixmonitor.c.

                      {
   MUXFLAG_APPEND = (1 << 1),
   MUXFLAG_BRIDGED = (1 << 2),
   MUXFLAG_VOLUME = (1 << 3),
   MUXFLAG_READVOLUME = (1 << 4),
   MUXFLAG_WRITEVOLUME = (1 << 5),
   MUXFLAG_READ = (1 << 6),
   MUXFLAG_WRITE = (1 << 7),
   MUXFLAG_COMBINED = (1 << 8),
   MUXFLAG_UID = (1 << 9),
   MUXFLAG_VMRECIPIENTS = (1 << 10),
};

Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 1386 of file app_mixmonitor.c.

static void __unreg_module ( void  ) [static]

Definition at line 1386 of file app_mixmonitor.c.

static void add_vm_recipients_from_string ( struct mixmonitor mixmonitor,
const char *  vm_recipients 
) [static]

Definition at line 434 of file app_mixmonitor.c.

References ast_copy_string(), ast_debug, AST_LIST_INSERT_HEAD, ast_log(), ast_malloc, ast_strlen_zero(), ast_verb, vm_recipient::context, vm_recipient::folder, LOG_ERROR, vm_recipient::mailbox, vm_recipient::next, and mixmonitor::recipient_list.

Referenced by launch_monitor_thread().

{
   /* recipients are in a single string with a format format resembling "mailbox@context/INBOX,mailbox2@context2,mailbox3@context3/Work" */
   char *cur_mailbox = ast_strdupa(vm_recipients);
   char *cur_context;
   char *cur_folder;
   char *next;
   int elements_processed = 0;

   while (!ast_strlen_zero(cur_mailbox)) {
      ast_debug(3, "attempting to add next element %d from %s\n", elements_processed, cur_mailbox);
      if ((next = strchr(cur_mailbox, ',')) || (next = strchr(cur_mailbox, '&'))) {
         *(next++) = '\0';
      }

      if ((cur_folder = strchr(cur_mailbox, '/'))) {
         *(cur_folder++) = '\0';
      } else {
         cur_folder = "INBOX";
      }

      if ((cur_context = strchr(cur_mailbox, '@'))) {
         *(cur_context++) = '\0';
      } else {
         cur_context = "default";
      }

      if (!ast_strlen_zero(cur_mailbox) && !ast_strlen_zero(cur_context)) {
         struct vm_recipient *recipient;
         if (!(recipient = ast_malloc(sizeof(*recipient)))) {
            ast_log(LOG_ERROR, "Failed to allocate recipient. Aborting function.\n");
            return;
         }
         ast_copy_string(recipient->context, cur_context, sizeof(recipient->context));
         ast_copy_string(recipient->mailbox, cur_mailbox, sizeof(recipient->mailbox));
         ast_copy_string(recipient->folder, cur_folder, sizeof(recipient->folder));

         /* Add to list */
         ast_verb(5, "Adding %s@%s to recipient list\n", recipient->mailbox, recipient->context);
         AST_LIST_INSERT_HEAD(&mixmonitor->recipient_list, recipient, list);
      } else {
         ast_log(LOG_ERROR, "Failed to properly parse extension and/or context from element %d of recipient string: %s\n", elements_processed, vm_recipients);
      }

      cur_mailbox = next;
      elements_processed++;
   }
}
static void clear_mixmonitor_recipient_list ( struct mixmonitor mixmonitor) [static]

Definition at line 483 of file app_mixmonitor.c.

References ast_free, AST_LIST_REMOVE_HEAD, and mixmonitor::recipient_list.

Referenced by mixmonitor_free().

{
   struct vm_recipient *current;
   while ((current = AST_LIST_REMOVE_HEAD(&mixmonitor->recipient_list, list))) {
      /* Clear list element data */
      ast_free(current);
   }
}
static void copy_to_voicemail ( struct mixmonitor mixmonitor,
const char *  ext,
const char *  filename 
) [static]

Definition at line 529 of file app_mixmonitor.c.

References ast_app_copy_recording_to_vm(), AST_LIST_TRAVERSE, ast_log(), ast_string_field_free_memory, ast_string_field_init, ast_string_field_set, ast_verb, ast_vm_recording_data::call_callerchan, mixmonitor::call_callerchan, ast_vm_recording_data::call_callerid, mixmonitor::call_callerid, ast_vm_recording_data::call_context, mixmonitor::call_context, ast_vm_recording_data::call_extension, mixmonitor::call_extension, ast_vm_recording_data::call_macrocontext, mixmonitor::call_macrocontext, ast_vm_recording_data::call_priority, mixmonitor::call_priority, ast_vm_recording_data::context, vm_recipient::context, vm_recipient::folder, LOG_ERROR, ast_vm_recording_data::mailbox, vm_recipient::mailbox, mixmonitor::recipient_list, ast_vm_recording_data::recording_ext, and ast_vm_recording_data::recording_file.

Referenced by mixmonitor_thread().

{
   struct vm_recipient *recipient = NULL;
   struct ast_vm_recording_data recording_data;
   if (ast_string_field_init(&recording_data, 512)) {
      ast_log(LOG_ERROR, "Failed to string_field_init, skipping copy_to_voicemail\n");
      return;
   }

   /* Copy strings to stringfields that will be used for all recipients */
   ast_string_field_set(&recording_data, recording_file, filename);
   ast_string_field_set(&recording_data, recording_ext, ext);
   ast_string_field_set(&recording_data, call_context, mixmonitor->call_context);
   ast_string_field_set(&recording_data, call_macrocontext, mixmonitor->call_macrocontext);
   ast_string_field_set(&recording_data, call_extension, mixmonitor->call_extension);
   ast_string_field_set(&recording_data, call_callerchan, mixmonitor->call_callerchan);
   ast_string_field_set(&recording_data, call_callerid, mixmonitor->call_callerid);
   /* and call_priority gets copied too */
   recording_data.call_priority = mixmonitor->call_priority;

   AST_LIST_TRAVERSE(&mixmonitor->recipient_list, recipient, list) {
      /* context, mailbox, and folder need to be set per recipient */
      ast_string_field_set(&recording_data, context, recipient->context);
      ast_string_field_set(&recording_data, mailbox, recipient->mailbox);
      ast_string_field_set(&recording_data, folder, recipient->folder);

      ast_verb(4, "MixMonitor attempting to send voicemail copy to %s@%s\n", recording_data.mailbox,
         recording_data.context);
      ast_app_copy_recording_to_vm(&recording_data);
   }

   /* Free the string fields for recording_data before exiting the function. */
   ast_string_field_free_memory(&recording_data);
}
static char* filename_parse ( char *  filename,
char *  buffer,
size_t  len 
) [static]

Definition at line 920 of file app_mixmonitor.c.

References ast_alloca, ast_config_AST_MONITOR_DIR, ast_copy_string(), ast_log(), ast_mkdir(), ast_strlen_zero(), and LOG_WARNING.

Referenced by mixmonitor_exec().

{
   char *slash;
   if (ast_strlen_zero(filename)) {
      ast_log(LOG_WARNING, "No file name was provided for a file save option.\n");
   } else if (filename[0] != '/') {
      char *build;
      build = ast_alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(filename) + 3);
      sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, filename);
      filename = build;
   }

   ast_copy_string(buffer, filename, len);

   if ((slash = strrchr(filename, '/'))) {
      *slash = '\0';
   }
   ast_mkdir(filename, 0777);

   return buffer;
}
static char* handle_cli_mixmonitor ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 1115 of file app_mixmonitor.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_channel_datastores(), ast_channel_get_by_name_prefix(), ast_channel_lock, ast_channel_unlock, ast_channel_unref, ast_cli(), ast_complete_channels(), AST_LIST_TRAVERSE, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_datastore::data, ast_cli_args::fd, ast_filestream::filename, mixmonitor_ds::fs, mixmonitor_ds::fs_read, mixmonitor_ds::fs_write, ast_datastore::info, ast_cli_args::line, mixmonitor_exec(), ast_cli_args::n, ast_cli_args::pos, stop_mixmonitor_exec(), ast_cli_entry::usage, and ast_cli_args::word.

{
   struct ast_channel *chan;
   struct ast_datastore *datastore = NULL;
   struct mixmonitor_ds *mixmonitor_ds = NULL;

   switch (cmd) {
   case CLI_INIT:
      e->command = "mixmonitor {start|stop|list}";
      e->usage =
         "Usage: mixmonitor <start|stop|list> <chan_name> [args]\n"
         "       The optional arguments are passed to the MixMonitor\n"
         "       application when the 'start' command is used.\n";
      return NULL;
   case CLI_GENERATE:
      return ast_complete_channels(a->line, a->word, a->pos, a->n, 2);
   }

   if (a->argc < 3) {
      return CLI_SHOWUSAGE;
   }

   if (!(chan = ast_channel_get_by_name_prefix(a->argv[2], strlen(a->argv[2])))) {
      ast_cli(a->fd, "No channel matching '%s' found.\n", a->argv[2]);
      /* Technically this is a failure, but we don't want 2 errors printing out */
      return CLI_SUCCESS;
   }

   ast_channel_lock(chan);

   if (!strcasecmp(a->argv[1], "start")) {
      mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
      ast_channel_unlock(chan);
   } else if (!strcasecmp(a->argv[1], "stop")){
      ast_channel_unlock(chan);
      stop_mixmonitor_exec(chan, (a->argc >= 4) ? a->argv[3] : "");
   } else if (!strcasecmp(a->argv[1], "list")) {
      ast_cli(a->fd, "MixMonitor ID\tFile\tReceive File\tTransmit File\n");
      ast_cli(a->fd, "=========================================================================\n");
      AST_LIST_TRAVERSE(ast_channel_datastores(chan), datastore, entry) {
         if (datastore->info == &mixmonitor_ds_info) {
            char *filename = "";
            char *filename_read = "";
            char *filename_write = "";
            mixmonitor_ds = datastore->data;
            if (mixmonitor_ds->fs)
               filename = ast_strdupa(mixmonitor_ds->fs->filename);
            if (mixmonitor_ds->fs_read)
               filename_read = ast_strdupa(mixmonitor_ds->fs_read->filename);
            if (mixmonitor_ds->fs_write)
               filename_write = ast_strdupa(mixmonitor_ds->fs_write->filename);
            ast_cli(a->fd, "%p\t%s\t%s\t%s\n", mixmonitor_ds, filename, filename_read, filename_write);
         }
      }
      ast_channel_unlock(chan);
   } else {
      ast_channel_unlock(chan);
      chan = ast_channel_unref(chan);
      return CLI_SHOWUSAGE;
   }

   chan = ast_channel_unref(chan);

   return CLI_SUCCESS;
}
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 
) [static]

Definition at line 787 of file app_mixmonitor.c.

References add_vm_recipients_from_string(), ast_audiohook_destroy(), ast_audiohook_init(), AST_AUDIOHOOK_TRIGGER_SYNC, AST_AUDIOHOOK_TYPE_SPY, ast_autochan_destroy(), ast_autochan_setup(), ast_callerid_merge(), ast_calloc, ast_channel_connected(), ast_channel_context(), ast_channel_exten(), ast_channel_lock, ast_channel_macrocontext(), ast_channel_name(), ast_channel_priority(), ast_channel_unlock, ast_debug, ast_free, ast_log(), ast_pthread_create_detached_background, ast_read_threadstorage_callid(), ast_set_flag, ast_strdup, ast_string_field_init, ast_string_field_set, ast_strlen_zero(), mixmonitor::audiohook, mixmonitor::autochan, mixmonitor::call_priority, mixmonitor::callid, connected, mixmonitor::filename, mixmonitor::filename_read, mixmonitor::filename_write, mixmonitor::flags, ast_party_connected_line::id, LOG_WARNING, mixmonitor_free(), mixmonitor_thread(), mixmonitor::name, ast_party_id::name, ast_party_id::number, ast_audiohook::options, pbx_builtin_setvar_helper(), pbx_substitute_variables_helper(), mixmonitor::post_process, ast_audiohook_options::read_volume, S_COR, setup_mixmonitor_ds(), startmon(), ast_party_name::str, ast_party_number::str, thread, ast_party_name::valid, ast_party_number::valid, and ast_audiohook_options::write_volume.

Referenced by mixmonitor_exec().

{
   pthread_t thread;
   struct mixmonitor *mixmonitor;
   char postprocess2[1024] = "";
   char *datastore_id = NULL;

   postprocess2[0] = 0;
   /* If a post process system command is given attach it to the structure */
   if (!ast_strlen_zero(post_process)) {
      char *p1, *p2;

      p1 = ast_strdupa(post_process);
      for (p2 = p1; *p2; p2++) {
         if (*p2 == '^' && *(p2+1) == '{') {
            *p2 = '$';
         }
      }
      pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1);
   }

   /* Pre-allocate mixmonitor structure and spy */
   if (!(mixmonitor = ast_calloc(1, sizeof(*mixmonitor)))) {
      return -1;
   }

   /* Now that the struct has been calloced, go ahead and initialize the string fields. */
   if (ast_string_field_init(mixmonitor, 512)) {
      mixmonitor_free(mixmonitor);
      return -1;
   }

   /* Setup the actual spy before creating our thread */
   if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type, 0)) {
      mixmonitor_free(mixmonitor);
      return -1;
   }

   /* Copy over flags and channel name */
   mixmonitor->flags = flags;
   if (!(mixmonitor->autochan = ast_autochan_setup(chan))) {
      mixmonitor_free(mixmonitor);
      return -1;
   }

   if (setup_mixmonitor_ds(mixmonitor, chan, &datastore_id)) {
      ast_autochan_destroy(mixmonitor->autochan);
      mixmonitor_free(mixmonitor);
      ast_free(datastore_id);
      return -1;
   }

   if (!ast_strlen_zero(uid_channel_var)) {
      if (datastore_id) {
         pbx_builtin_setvar_helper(chan, uid_channel_var, datastore_id);
      }
   }
   ast_free(datastore_id);

   mixmonitor->name = ast_strdup(ast_channel_name(chan));

   if (!ast_strlen_zero(postprocess2)) {
      mixmonitor->post_process = ast_strdup(postprocess2);
   }

   if (!ast_strlen_zero(filename)) {
      mixmonitor->filename = ast_strdup(filename);
   }

   if (!ast_strlen_zero(filename_write)) {
      mixmonitor->filename_write = ast_strdup(filename_write);
   }

   if (!ast_strlen_zero(filename_read)) {
      mixmonitor->filename_read = ast_strdup(filename_read);
   }

   if (!ast_strlen_zero(recipients)) {
      char callerid[256];
      struct ast_party_connected_line *connected;

      ast_channel_lock(chan);

      /* We use the connected line of the invoking channel for caller ID. */

      connected = ast_channel_connected(chan);
      ast_debug(3, "Connected Line CID = %d - %s : %d - %s\n", connected->id.name.valid,
         connected->id.name.str, connected->id.number.valid,
         connected->id.number.str);
      ast_callerid_merge(callerid, sizeof(callerid),
         S_COR(connected->id.name.valid, connected->id.name.str, NULL),
         S_COR(connected->id.number.valid, connected->id.number.str, NULL),
         "Unknown");

      ast_string_field_set(mixmonitor, call_context, ast_channel_context(chan));
      ast_string_field_set(mixmonitor, call_macrocontext, ast_channel_macrocontext(chan));
      ast_string_field_set(mixmonitor, call_extension, ast_channel_exten(chan));
      ast_string_field_set(mixmonitor, call_callerchan, ast_channel_name(chan));
      ast_string_field_set(mixmonitor, call_callerid, callerid);
      mixmonitor->call_priority = ast_channel_priority(chan);

      ast_channel_unlock(chan);

      add_vm_recipients_from_string(mixmonitor, recipients);
   }

   ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC);

   if (readvol)
      mixmonitor->audiohook.options.read_volume = readvol;
   if (writevol)
      mixmonitor->audiohook.options.write_volume = writevol;

   if (startmon(chan, &mixmonitor->audiohook)) {
      ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n",
         mixmonitor_spy_type, ast_channel_name(chan));
      ast_audiohook_destroy(&mixmonitor->audiohook);
      mixmonitor_free(mixmonitor);
      return -1;
   }

   /* reference be released at mixmonitor destruction */
   mixmonitor->callid = ast_read_threadstorage_callid();

   return ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
}
static int manager_mixmonitor ( struct mansession s,
const struct message m 
) [static]

Definition at line 1248 of file app_mixmonitor.c.

References AMI_SUCCESS, args, ast_app_parse_options(), ast_channel_get_by_name(), ast_channel_lock, ast_channel_unlock, ast_channel_unref, ast_strlen_zero(), ast_test_flag, astman_append(), astman_get_header(), astman_send_error(), mixmonitor_exec(), mixmonitor_opts, MUXFLAG_UID, OPT_ARG_ARRAY_SIZE, OPT_ARG_UID, and pbx_builtin_getvar_helper().

Referenced by load_module().

{
   struct ast_channel *c = NULL;

   const char *name = astman_get_header(m, "Channel");
   const char *id = astman_get_header(m, "ActionID");
   const char *file = astman_get_header(m, "File");
   const char *options = astman_get_header(m, "Options");
   char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };
   struct ast_flags flags = { 0 };
   char *uid_channel_var = NULL;
   const char *mixmonitor_id = NULL;

   int res;
   char args[PATH_MAX] = "";
   if (ast_strlen_zero(name)) {
      astman_send_error(s, m, "No channel specified");
      return AMI_SUCCESS;
   }

   c = ast_channel_get_by_name(name);

   if (!c) {
      astman_send_error(s, m, "No such channel");
      return AMI_SUCCESS;
   }

   if (!ast_strlen_zero(options)) {
      ast_app_parse_options(mixmonitor_opts, &flags, opts, ast_strdupa(options));
   }

   snprintf(args, sizeof(args), "%s,%s", file, options);

   ast_channel_lock(c);
   res = mixmonitor_exec(c, args);

   if (ast_test_flag(&flags, MUXFLAG_UID)) {
      uid_channel_var = opts[OPT_ARG_UID];
      mixmonitor_id = pbx_builtin_getvar_helper(c, uid_channel_var);
   }
   ast_channel_unlock(c);

   if (res) {
      c = ast_channel_unref(c);
      astman_send_error(s, m, "Could not start monitoring channel");
      return AMI_SUCCESS;
   }

   astman_append(s, "Response: Success\r\n");

   if (!ast_strlen_zero(id)) {
      astman_append(s, "ActionID: %s\r\n", id);
   }

   if (!ast_strlen_zero(mixmonitor_id)) {
      astman_append(s, "MixMonitorID: %s\r\n", mixmonitor_id);
   }

   astman_append(s, "\r\n");

   c = ast_channel_unref(c);

   return AMI_SUCCESS;
}
static int manager_mute_mixmonitor ( struct mansession s,
const struct message m 
) [static]

Mute / unmute a MixMonitor channel.

Definition at line 1182 of file app_mixmonitor.c.

References AMI_SUCCESS, AST_AUDIOHOOK_MUTE_READ, AST_AUDIOHOOK_MUTE_WRITE, ast_audiohook_set_mute(), ast_channel_get_by_name(), ast_channel_unref, ast_false(), ast_strlen_zero(), astman_append(), astman_get_header(), and astman_send_error().

Referenced by load_module().

{
   struct ast_channel *c = NULL;

   const char *name = astman_get_header(m, "Channel");
   const char *id = astman_get_header(m, "ActionID");
   const char *state = astman_get_header(m, "State");
   const char *direction = astman_get_header(m,"Direction");

   int clearmute = 1;

   enum ast_audiohook_flags flag;

   if (ast_strlen_zero(direction)) {
      astman_send_error(s, m, "No direction specified. Must be read, write or both");
      return AMI_SUCCESS;
   }

   if (!strcasecmp(direction, "read")) {
      flag = AST_AUDIOHOOK_MUTE_READ;
   } else  if (!strcasecmp(direction, "write")) {
      flag = AST_AUDIOHOOK_MUTE_WRITE;
   } else  if (!strcasecmp(direction, "both")) {
      flag = AST_AUDIOHOOK_MUTE_READ | AST_AUDIOHOOK_MUTE_WRITE;
   } else {
      astman_send_error(s, m, "Invalid direction specified. Must be read, write or both");
      return AMI_SUCCESS;
   }

   if (ast_strlen_zero(name)) {
      astman_send_error(s, m, "No channel specified");
      return AMI_SUCCESS;
   }

   if (ast_strlen_zero(state)) {
      astman_send_error(s, m, "No state specified");
      return AMI_SUCCESS;
   }

   clearmute = ast_false(state);
   c = ast_channel_get_by_name(name);

   if (!c) {
      astman_send_error(s, m, "No such channel");
      return AMI_SUCCESS;
   }

   if (ast_audiohook_set_mute(c, mixmonitor_spy_type, flag, clearmute)) {
      c = ast_channel_unref(c);
      astman_send_error(s, m, "Cannot set mute flag");
      return AMI_SUCCESS;
   }

   astman_append(s, "Response: Success\r\n");

   if (!ast_strlen_zero(id)) {
      astman_append(s, "ActionID: %s\r\n", id);
   }

   astman_append(s, "\r\n");

   c = ast_channel_unref(c);

   return AMI_SUCCESS;
}
static int manager_stop_mixmonitor ( struct mansession s,
const struct message m 
) [static]

Definition at line 1313 of file app_mixmonitor.c.

References AMI_SUCCESS, ast_channel_get_by_name(), ast_channel_unref, ast_strlen_zero(), astman_append(), astman_get_header(), astman_send_error(), and stop_mixmonitor_full().

Referenced by load_module().

{
   struct ast_channel *c = NULL;

   const char *name = astman_get_header(m, "Channel");
   const char *id = astman_get_header(m, "ActionID");
   const char *mixmonitor_id = astman_get_header(m, "MixMonitorID");

   int res;
   if (ast_strlen_zero(name)) {
      astman_send_error(s, m, "No channel specified");
      return AMI_SUCCESS;
   }

   c = ast_channel_get_by_name(name);

   if (!c) {
      astman_send_error(s, m, "No such channel");
      return AMI_SUCCESS;
   }

   res = stop_mixmonitor_full(c, mixmonitor_id);

   if (res) {
      ast_channel_unref(c);
      astman_send_error(s, m, "Could not stop monitoring channel");
      return AMI_SUCCESS;
   }

   astman_append(s, "Response: Success\r\n");

   if (!ast_strlen_zero(id)) {
      astman_append(s, "ActionID: %s\r\n", id);
   }

   astman_append(s, "\r\n");

   c = ast_channel_unref(c);

   return AMI_SUCCESS;
}
static void mixmonitor_ds_close_fs ( struct mixmonitor_ds mixmonitor_ds) [static]

Definition at line 352 of file app_mixmonitor.c.

References ast_closestream(), ast_verb, mixmonitor_ds::fs, mixmonitor_ds::fs_quit, mixmonitor_ds::fs_read, and mixmonitor_ds::fs_write.

Referenced by mixmonitor_thread(), and stop_mixmonitor_full().

{
   unsigned char quitting = 0;

   if (mixmonitor_ds->fs) {
      quitting = 1;
      ast_closestream(mixmonitor_ds->fs);
      mixmonitor_ds->fs = NULL;
      ast_verb(2, "MixMonitor close filestream (mixed)\n");
   }

   if (mixmonitor_ds->fs_read) {
      quitting = 1;
      ast_closestream(mixmonitor_ds->fs_read);
      mixmonitor_ds->fs_read = NULL;
      ast_verb(2, "MixMonitor close filestream (read)\n");
   }

   if (mixmonitor_ds->fs_write) {
      quitting = 1;
      ast_closestream(mixmonitor_ds->fs_write);
      mixmonitor_ds->fs_write = NULL;
      ast_verb(2, "MixMonitor close filestream (write)\n");
   }

   if (quitting) {
      mixmonitor_ds->fs_quit = 1;
   }
}
static void mixmonitor_ds_destroy ( void *  data) [static]
static int mixmonitor_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 942 of file app_mixmonitor.c.

References args, AST_APP_ARG, ast_app_parse_options(), AST_DECLARE_APP_ARGS, ast_log(), ast_module_ref(), ast_module_unref(), AST_STANDARD_APP_ARGS, ast_strlen_zero(), ast_test_flag, filename_parse(), ast_flags::flags, get_volfactor, launch_monitor_thread(), LOG_NOTICE, LOG_WARNING, mixmonitor_opts, MUXFLAG_READ, MUXFLAG_READVOLUME, MUXFLAG_UID, MUXFLAG_VMRECIPIENTS, MUXFLAG_VOLUME, MUXFLAG_WRITE, MUXFLAG_WRITEVOLUME, OPT_ARG_ARRAY_SIZE, OPT_ARG_READNAME, OPT_ARG_READVOLUME, OPT_ARG_UID, OPT_ARG_VMRECIPIENTS, OPT_ARG_VOLUME, OPT_ARG_WRITENAME, OPT_ARG_WRITEVOLUME, parse(), pbx_builtin_setvar_helper(), and ast_module_info::self.

Referenced by handle_cli_mixmonitor(), load_module(), and manager_mixmonitor().

{
   int x, readvol = 0, writevol = 0;
   char *filename_read = NULL;
   char *filename_write = NULL;
   char filename_buffer[1024] = "";
        char *uid_channel_var = NULL;

   struct ast_flags flags = { 0 };
   char *recipients = NULL;
   char *parse;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(filename);
      AST_APP_ARG(options);
      AST_APP_ARG(post_process);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "MixMonitor requires an argument (filename or ,t(filename) and/or r(filename)\n");
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (args.options) {
      char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, };

      ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options);

      if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) {
         if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) {
            ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n");
         } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
            ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]);
         } else {
            readvol = get_volfactor(x);
         }
      }

      if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) {
         if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) {
            ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n");
         } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
            ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]);
         } else {
            writevol = get_volfactor(x);
         }
      }

      if (ast_test_flag(&flags, MUXFLAG_VOLUME)) {
         if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) {
            ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n");
         } else if ((sscanf(opts[OPT_ARG_VOLUME], "%2d", &x) != 1) || (x < -4) || (x > 4)) {
            ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]);
         } else {
            readvol = writevol = get_volfactor(x);
         }
      }

      if (ast_test_flag(&flags, MUXFLAG_VMRECIPIENTS)) {
         if (ast_strlen_zero(opts[OPT_ARG_VMRECIPIENTS])) {
            ast_log(LOG_WARNING, "No voicemail recipients were specified for the vm copy ('m') option.\n");
         } else {
            recipients = ast_strdupa(opts[OPT_ARG_VMRECIPIENTS]);
         }
      }

      if (ast_test_flag(&flags, MUXFLAG_WRITE)) {
         filename_write = ast_strdupa(filename_parse(opts[OPT_ARG_WRITENAME], filename_buffer, sizeof(filename_buffer)));
      }

      if (ast_test_flag(&flags, MUXFLAG_READ)) {
         filename_read = ast_strdupa(filename_parse(opts[OPT_ARG_READNAME], filename_buffer, sizeof(filename_buffer)));
      }

      if (ast_test_flag(&flags, MUXFLAG_UID)) {
         uid_channel_var = opts[OPT_ARG_UID];
      }
   }
   /* If there are no file writing arguments/options for the mix monitor, send a warning message and return -1 */

   if (!ast_test_flag(&flags, MUXFLAG_WRITE) && !ast_test_flag(&flags, MUXFLAG_READ) && ast_strlen_zero(args.filename)) {
      ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
      return -1;
   }

   /* If filename exists, try to create directories for it */
   if (!(ast_strlen_zero(args.filename))) {
      args.filename = ast_strdupa(filename_parse(args.filename, filename_buffer, sizeof(filename_buffer)));
   }

   pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);

   /* If launch_monitor_thread works, the module reference must not be released until it is finished. */
   ast_module_ref(ast_module_info->self);
   if (launch_monitor_thread(chan,
         args.filename,
         flags.flags,
         readvol,
         writevol,
         args.post_process,
         filename_write,
         filename_read,
         uid_channel_var,
         recipients)) {
      ast_module_unref(ast_module_info->self);
   }

   return 0;
}
static void mixmonitor_save_prep ( struct mixmonitor mixmonitor,
char *  filename,
struct ast_filestream **  fs,
unsigned int *  oflags,
int *  errflag,
char **  ext 
) [static]

Definition at line 564 of file app_mixmonitor.c.

References ast_format_rate(), ast_log(), ast_strlen_zero(), ast_test_flag, ast_writefile(), ast_filestream::fmt, ast_format_def::format, mixmonitor_ds::fs_quit, LOG_ERROR, MAX, mixmonitor::mixmonitor_ds, MUXFLAG_APPEND, and mixmonitor_ds::samp_rate.

Referenced by mixmonitor_thread().

{
   /* Initialize the file if not already done so */
   char *last_slash = NULL;
   if (!ast_strlen_zero(filename)) {
      if (!*fs && !*errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
         *oflags = O_CREAT | O_WRONLY;
         *oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;

         last_slash = strrchr(filename, '/');

         if ((*ext = strrchr(filename, '.')) && (*ext > last_slash)) {
            **ext = '\0';
            *ext = *ext + 1;
         } else {
            *ext = "raw";
         }

         if (!(*fs = ast_writefile(filename, *ext, NULL, *oflags, 0, 0666))) {
            ast_log(LOG_ERROR, "Cannot open %s.%s\n", filename, *ext);
            *errflag = 1;
         } else {
            struct ast_filestream *tmp = *fs;
            mixmonitor->mixmonitor_ds->samp_rate = MAX(mixmonitor->mixmonitor_ds->samp_rate, ast_format_rate(&tmp->fmt->format));
         }
      }
   }
}
static void* mixmonitor_thread ( void *  obj) [static]

Definition at line 593 of file app_mixmonitor.c.

References ast_audiohook_lock, ast_audiohook_read_frame_all(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_trigger_wait(), ast_audiohook_unlock, ast_autochan_destroy(), ast_bridged_channel(), ast_callid_threadassoc_add(), ast_channel_name(), ast_cond_wait, ast_debug, ast_format_set(), ast_format_slin_by_rate(), ast_frame_free(), AST_LIST_EMPTY, AST_LIST_NEXT, ast_log(), ast_module_unref(), ast_mutex_lock, ast_mutex_unlock, ast_safe_system(), ast_strlen_zero(), ast_test_flag, ast_test_suite_event_notify, ast_verb, ast_writestream(), mixmonitor::audiohook, mixmonitor::autochan, mixmonitor::callid, ast_autochan::chan, copy_to_voicemail(), destroy_monitor_audiohook(), mixmonitor_ds::destruction_condition, mixmonitor_ds::destruction_ok, mixmonitor::filename, mixmonitor::filename_read, mixmonitor::filename_write, mixmonitor_ds::fs, mixmonitor_ds::fs_quit, mixmonitor_ds::fs_read, mixmonitor_ds::fs_write, mixmonitor_ds::lock, LOG_ERROR, mixmonitor::mixmonitor_ds, mixmonitor_ds_close_fs(), mixmonitor_free(), mixmonitor_save_prep(), MUXFLAG_BRIDGED, mixmonitor::name, mixmonitor::post_process, mixmonitor::recipient_list, mixmonitor_ds::samp_rate, SAMPLES_PER_FRAME, ast_module_info::self, and ast_audiohook::status.

Referenced by launch_monitor_thread().

{
   struct mixmonitor *mixmonitor = obj;
   char *fs_ext = "";
   char *fs_read_ext = "";
   char *fs_write_ext = "";

   struct ast_filestream **fs = NULL;
   struct ast_filestream **fs_read = NULL;
   struct ast_filestream **fs_write = NULL;

   unsigned int oflags;
   int errflag = 0;
   struct ast_format format_slin;

   /* Keep callid association before any log messages */
   if (mixmonitor->callid) {
      ast_callid_threadassoc_add(mixmonitor->callid);
   }

   ast_verb(2, "Begin MixMonitor Recording %s\n", mixmonitor->name);

   fs = &mixmonitor->mixmonitor_ds->fs;
   fs_read = &mixmonitor->mixmonitor_ds->fs_read;
   fs_write = &mixmonitor->mixmonitor_ds->fs_write;

   ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
   mixmonitor_save_prep(mixmonitor, mixmonitor->filename, fs, &oflags, &errflag, &fs_ext);
   mixmonitor_save_prep(mixmonitor, mixmonitor->filename_read, fs_read, &oflags, &errflag, &fs_read_ext);
   mixmonitor_save_prep(mixmonitor, mixmonitor->filename_write, fs_write, &oflags, &errflag, &fs_write_ext);

   ast_format_set(&format_slin, ast_format_slin_by_rate(mixmonitor->mixmonitor_ds->samp_rate), 0);

   ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);


   /* The audiohook must enter and exit the loop locked */
   ast_audiohook_lock(&mixmonitor->audiohook);
   while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING && !mixmonitor->mixmonitor_ds->fs_quit) {
      struct ast_frame *fr = NULL;
      struct ast_frame *fr_read = NULL;
      struct ast_frame *fr_write = NULL;

      if (!(fr = ast_audiohook_read_frame_all(&mixmonitor->audiohook, SAMPLES_PER_FRAME, &format_slin,
                  &fr_read, &fr_write))) {
         ast_audiohook_trigger_wait(&mixmonitor->audiohook);

         if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) {
            break;
         }
         continue;
      }

      /* audiohook lock is not required for the next block.
       * Unlock it, but remember to lock it before looping or exiting */
      ast_audiohook_unlock(&mixmonitor->audiohook);

      if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->autochan->chan && ast_bridged_channel(mixmonitor->autochan->chan))) {
         ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);

         /* Write out the frame(s) */
         if ((*fs_read) && (fr_read)) {
            struct ast_frame *cur;

            for (cur = fr_read; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
               ast_writestream(*fs_read, cur);
            }
         }

         if ((*fs_write) && (fr_write)) {
            struct ast_frame *cur;

            for (cur = fr_write; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
               ast_writestream(*fs_write, cur);
            }
         }

         if ((*fs) && (fr)) {
            struct ast_frame *cur;

            for (cur = fr; cur; cur = AST_LIST_NEXT(cur, frame_list)) {
               ast_writestream(*fs, cur);
            }
         }
         ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);
      }
      /* All done! free it. */
      if (fr) {
         ast_frame_free(fr, 0);
      }
      if (fr_read) {
         ast_frame_free(fr_read, 0);
      }
      if (fr_write) {
         ast_frame_free(fr_write, 0);
      }

      fr = NULL;
      fr_write = NULL;
      fr_read = NULL;

      ast_audiohook_lock(&mixmonitor->audiohook);
   }

   /* Test Event */
   ast_test_suite_event_notify("MIXMONITOR_END", "Channel: %s\r\n"
                           "File: %s\r\n",
                           ast_channel_name(mixmonitor->autochan->chan),
                           mixmonitor->filename);

   ast_audiohook_unlock(&mixmonitor->audiohook);

   ast_autochan_destroy(mixmonitor->autochan);

   /* Datastore cleanup.  close the filestream and wait for ds destruction */
   ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
   mixmonitor_ds_close_fs(mixmonitor->mixmonitor_ds);
   if (!mixmonitor->mixmonitor_ds->destruction_ok) {
      ast_cond_wait(&mixmonitor->mixmonitor_ds->destruction_condition, &mixmonitor->mixmonitor_ds->lock);
   }
   ast_mutex_unlock(&mixmonitor->mixmonitor_ds->lock);

   /* kill the audiohook */
   destroy_monitor_audiohook(mixmonitor);

   if (mixmonitor->post_process) {
      ast_verb(2, "Executing [%s]\n", mixmonitor->post_process);
      ast_safe_system(mixmonitor->post_process);
   }

   ast_verb(2, "End MixMonitor Recording %s\n", mixmonitor->name);

   if (!AST_LIST_EMPTY(&mixmonitor->recipient_list)) {
      if (ast_strlen_zero(fs_ext)) {
         ast_log(LOG_ERROR, "No file extension set for Mixmonitor %s. Skipping copy to voicemail.\n",
            mixmonitor -> name);
      } else {
         ast_verb(3, "Copying recordings for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
         copy_to_voicemail(mixmonitor, fs_ext, mixmonitor->filename);
      }
      if (!ast_strlen_zero(fs_read_ext)) {
         ast_verb(3, "Copying read recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
         copy_to_voicemail(mixmonitor, fs_read_ext, mixmonitor->filename_read);
      }
      if (!ast_strlen_zero(fs_write_ext)) {
         ast_verb(3, "Copying write recording for Mixmonitor %s to voicemail recipients\n", mixmonitor->name);
         copy_to_voicemail(mixmonitor, fs_write_ext, mixmonitor->filename_write);
      }
   } else {
      ast_debug(3, "No recipients to forward monitor to, moving on.\n");
   }

   mixmonitor_free(mixmonitor);

   ast_module_unref(ast_module_info->self);
   return NULL;
}
static int setup_mixmonitor_ds ( struct mixmonitor mixmonitor,
struct ast_channel chan,
char **  datastore_id 
) [static]

Definition at line 751 of file app_mixmonitor.c.

References ast_asprintf, ast_calloc, ast_channel_datastore_add(), ast_channel_lock, ast_channel_unlock, ast_cond_destroy, ast_cond_init, ast_datastore_alloc(), ast_free, ast_log(), ast_mutex_destroy, ast_mutex_init, mixmonitor::audiohook, mixmonitor_ds::audiohook, ast_datastore::data, mixmonitor_ds::destruction_condition, mixmonitor_ds::lock, LOG_ERROR, mixmonitor::mixmonitor_ds, and mixmonitor_ds::samp_rate.

Referenced by launch_monitor_thread().

{
   struct ast_datastore *datastore = NULL;
   struct mixmonitor_ds *mixmonitor_ds;

   if (!(mixmonitor_ds = ast_calloc(1, sizeof(*mixmonitor_ds)))) {
      return -1;
   }

   if (ast_asprintf(datastore_id, "%p", mixmonitor_ds) == -1) {
      ast_log(LOG_ERROR, "Failed to allocate memory for MixMonitor ID.\n");
   }

   ast_mutex_init(&mixmonitor_ds->lock);
   ast_cond_init(&mixmonitor_ds->destruction_condition, NULL);

   if (!(datastore = ast_datastore_alloc(&mixmonitor_ds_info, *datastore_id))) {
      ast_mutex_destroy(&mixmonitor_ds->lock);
      ast_cond_destroy(&mixmonitor_ds->destruction_condition);
      ast_free(mixmonitor_ds);
      return -1;
   }


   mixmonitor_ds->samp_rate = 8000;
   mixmonitor_ds->audiohook = &mixmonitor->audiohook;
   datastore->data = mixmonitor_ds;

   ast_channel_lock(chan);
   ast_channel_datastore_add(chan, datastore);
   ast_channel_unlock(chan);

   mixmonitor->mixmonitor_ds = mixmonitor_ds;
   return 0;
}
static int startmon ( struct ast_channel chan,
struct ast_audiohook audiohook 
) [static]

Definition at line 412 of file app_mixmonitor.c.

References ast_audiohook_attach(), ast_bridged_channel(), ast_channel_flags(), AST_FLAG_NBRIDGE, ast_softhangup(), AST_SOFTHANGUP_UNBRIDGE, and ast_test_flag.

Referenced by launch_monitor_thread().

{
   struct ast_channel *peer = NULL;
   int res = 0;

   if (!chan)
      return -1;

   ast_audiohook_attach(chan, audiohook);

   if (!res && ast_test_flag(ast_channel_flags(chan), AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan)))
      ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE);

   return res;
}
static int stop_mixmonitor_exec ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 1109 of file app_mixmonitor.c.

References stop_mixmonitor_full().

Referenced by handle_cli_mixmonitor(), and load_module().

{
   stop_mixmonitor_full(chan, data);
   return 0;
}
static int stop_mixmonitor_full ( struct ast_channel chan,
const char *  data 
) [static]

Definition at line 1055 of file app_mixmonitor.c.

References args, AST_APP_ARG, ast_audiohook_lock, AST_AUDIOHOOK_STATUS_DONE, AST_AUDIOHOOK_STATUS_SHUTDOWN, ast_audiohook_unlock, ast_audiohook_update_status(), ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_cond_signal, ast_datastore_free(), AST_DECLARE_APP_ARGS, ast_mutex_lock, ast_mutex_unlock, AST_STANDARD_APP_ARGS, ast_strlen_zero(), mixmonitor_ds::audiohook, ast_datastore::data, mixmonitor_ds::lock, mixmonitor_ds_close_fs(), parse(), ast_audiohook::status, and ast_audiohook::trigger.

Referenced by manager_stop_mixmonitor(), and stop_mixmonitor_exec().

{
   struct ast_datastore *datastore = NULL;
   char *parse = "";
   struct mixmonitor_ds *mixmonitor_ds;

   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(mixmonid);
   );

   if (!ast_strlen_zero(data)) {
      parse = ast_strdupa(data);
   }

   AST_STANDARD_APP_ARGS(args, parse);

   ast_channel_lock(chan);

   if (!(datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, args.mixmonid))) {
      ast_channel_unlock(chan);
                return -1;
   }
   mixmonitor_ds = datastore->data;

   ast_mutex_lock(&mixmonitor_ds->lock);

   /* closing the filestream here guarantees the file is available to the dialplan
    * after calling StopMixMonitor */
   mixmonitor_ds_close_fs(mixmonitor_ds);

   /* The mixmonitor thread may be waiting on the audiohook trigger.
    * In order to exit from the mixmonitor loop before waiting on channel
    * destruction, poke the audiohook trigger. */
   if (mixmonitor_ds->audiohook) {
      if (mixmonitor_ds->audiohook->status != AST_AUDIOHOOK_STATUS_DONE) {
         ast_audiohook_update_status(mixmonitor_ds->audiohook, AST_AUDIOHOOK_STATUS_SHUTDOWN);
      }
      ast_audiohook_lock(mixmonitor_ds->audiohook);
      ast_cond_signal(&mixmonitor_ds->audiohook->trigger);
      ast_audiohook_unlock(mixmonitor_ds->audiohook);
      mixmonitor_ds->audiohook = NULL;
   }

   ast_mutex_unlock(&mixmonitor_ds->lock);

   /* Remove the datastore so the monitor thread can exit */
   if (!ast_channel_datastore_remove(chan, datastore)) {
      ast_datastore_free(datastore);
   }
   ast_channel_unlock(chan);

   return 0;
}
static int unload_module ( void  ) [static]

Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Mixed Audio Monitoring Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEFAULT, } [static]

Definition at line 1386 of file app_mixmonitor.c.

const char* const app = "MixMonitor" [static]

Definition at line 250 of file app_mixmonitor.c.

Definition at line 1386 of file app_mixmonitor.c.

struct ast_cli_entry cli_mixmonitor[] [static]
Initial value:
 {
   AST_CLI_DEFINE(handle_cli_mixmonitor, "Execute a MixMonitor command")
}

Definition at line 1355 of file app_mixmonitor.c.

Initial value:
 {
   .type = "mixmonitor",
   .destroy = mixmonitor_ds_destroy,
}

Definition at line 393 of file app_mixmonitor.c.

struct ast_app_option mixmonitor_opts[128] = { [ 'a' ] = { .flag = MUXFLAG_APPEND }, [ 'b' ] = { .flag = MUXFLAG_BRIDGED }, [ 'v' ] = { .flag = MUXFLAG_READVOLUME , .arg_index = OPT_ARG_READVOLUME + 1 }, [ 'V' ] = { .flag = MUXFLAG_WRITEVOLUME , .arg_index = OPT_ARG_WRITEVOLUME + 1 }, [ 'W' ] = { .flag = MUXFLAG_VOLUME , .arg_index = OPT_ARG_VOLUME + 1 }, [ 'r' ] = { .flag = MUXFLAG_READ , .arg_index = OPT_ARG_READNAME + 1 }, [ 't' ] = { .flag = MUXFLAG_WRITE , .arg_index = OPT_ARG_WRITENAME + 1 }, [ 'i' ] = { .flag = MUXFLAG_UID , .arg_index = OPT_ARG_UID + 1 }, [ 'm' ] = { .flag = MUXFLAG_VMRECIPIENTS , .arg_index = OPT_ARG_VMRECIPIENTS + 1 },} [static]

Definition at line 328 of file app_mixmonitor.c.

Referenced by manager_mixmonitor(), and mixmonitor_exec().

const char* const mixmonitor_spy_type = "MixMonitor" [static]

Definition at line 254 of file app_mixmonitor.c.

Referenced by builtin_automixmonitor().

const char* const stop_app = "StopMixMonitor" [static]

Definition at line 252 of file app_mixmonitor.c.