Thu Apr 28 2011 16:56:51

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/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 dependency graph for app_mixmonitor.c:

Go to the source code of this file.

Data Structures

struct  mixmonitor
struct  mixmonitor_ds

Defines

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

Enumerations

enum  {
  MUXFLAG_APPEND = (1 << 1), MUXFLAG_BRIDGED = (1 << 2), MUXFLAG_VOLUME = (1 << 3), MUXFLAG_READVOLUME = (1 << 4),
  MUXFLAG_WRITEVOLUME = (1 << 5)
}
enum  { OPT_ARG_READVOLUME = 0, OPT_ARG_WRITEVOLUME, OPT_ARG_VOLUME, OPT_ARG_ARRAY_SIZE }

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void destroy_monitor_audiohook (struct mixmonitor *mixmonitor)
static char * handle_cli_mixmonitor (struct ast_cli_entry *e, int cmd, struct ast_cli_args *a)
static void launch_monitor_thread (struct ast_channel *chan, const char *filename, unsigned int flags, int readvol, int writevol, const char *post_process)
static int load_module (void)
static void mixmonitor_ds_chan_fixup (void *data, struct ast_channel *old_chan, struct ast_channel *new_chan)
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, void *data)
static void mixmonitor_free (struct mixmonitor *mixmonitor)
static void * mixmonitor_thread (void *obj)
static int setup_mixmonitor_ds (struct mixmonitor *mixmonitor, struct ast_channel *chan)
static int startmon (struct ast_channel *chan, struct ast_audiohook *audiohook)
static int stop_mixmonitor_exec (struct ast_channel *chan, void *data)
static int unload_module (void)

Variables

static struct ast_module_info
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Mixed Audio Monitoring Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, }
static const char * app = "MixMonitor"
static struct ast_module_infoast_module_info = &__mod_info
static struct ast_cli_entry cli_mixmonitor []
struct module_symbols * me
enum { ... }  mixmonitor_args
static struct ast_datastore_info mixmonitor_ds_info
enum { ... }  mixmonitor_flags
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 },}
static const char * mixmonitor_spy_type = "MixMonitor"
static const char * 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 129 of file app_mixmonitor.c.

Referenced by mixmonitor_exec().

#define SAMPLES_PER_FRAME   160

Definition at line 266 of file app_mixmonitor.c.

Referenced by mixmonitor_thread().


Enumeration Type Documentation

anonymous enum
Enumerator:
MUXFLAG_APPEND 
MUXFLAG_BRIDGED 
MUXFLAG_VOLUME 
MUXFLAG_READVOLUME 
MUXFLAG_WRITEVOLUME 

Definition at line 148 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),
} mixmonitor_flags;
anonymous enum
Enumerator:
OPT_ARG_READVOLUME 
OPT_ARG_WRITEVOLUME 
OPT_ARG_VOLUME 
OPT_ARG_ARRAY_SIZE 

Definition at line 156 of file app_mixmonitor.c.


Function Documentation

static void __reg_module ( void  ) [static]

Definition at line 650 of file app_mixmonitor.c.

static void __unreg_module ( void  ) [static]

Definition at line 650 of file app_mixmonitor.c.

static void destroy_monitor_audiohook ( struct mixmonitor mixmonitor) [static]
static char* handle_cli_mixmonitor ( struct ast_cli_entry e,
int  cmd,
struct ast_cli_args a 
) [static]

Definition at line 588 of file app_mixmonitor.c.

References ast_cli_args::argc, ast_cli_args::argv, ast_audiohook_detach_source(), ast_channel_unlock, ast_cli(), ast_complete_channels(), ast_get_channel_by_name_prefix_locked(), chan, CLI_GENERATE, CLI_INIT, CLI_SHOWUSAGE, CLI_SUCCESS, ast_cli_entry::command, ast_cli_args::fd, ast_cli_args::line, mixmonitor_exec(), ast_cli_args::n, ast_cli_args::pos, ast_cli_entry::usage, and ast_cli_args::word.

{
   struct ast_channel *chan;

   switch (cmd) {
   case CLI_INIT:
      e->command = "mixmonitor {start|stop}";
      e->usage =
         "Usage: mixmonitor <start|stop> <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_get_channel_by_name_prefix_locked(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;
   }

   if (!strcasecmp(a->argv[1], "start")) {
      mixmonitor_exec(chan, a->argv[3]);
      ast_channel_unlock(chan);
   } else {
      ast_channel_unlock(chan);
      ast_audiohook_detach_source(chan, mixmonitor_spy_type);
   }

   return CLI_SUCCESS;
}
static void launch_monitor_thread ( struct ast_channel chan,
const char *  filename,
unsigned int  flags,
int  readvol,
int  writevol,
const char *  post_process 
) [static]

Definition at line 399 of file app_mixmonitor.c.

References ast_audiohook_destroy(), ast_audiohook_init(), AST_AUDIOHOOK_TRIGGER_SYNC, AST_AUDIOHOOK_TYPE_SPY, ast_calloc, ast_log(), ast_pthread_create_detached_background, ast_set_flag, ast_strdupa, ast_strlen_zero(), mixmonitor::audiohook, mixmonitor::filename, mixmonitor::flags, len(), LOG_WARNING, mixmonitor_free(), mixmonitor_thread(), mixmonitor::name, ast_channel::name, ast_audiohook::options, pbx_substitute_variables_helper(), mixmonitor::post_process, ast_audiohook_options::read_volume, setup_mixmonitor_ds(), startmon(), thread, and ast_audiohook_options::write_volume.

Referenced by mixmonitor_exec().

{
   pthread_t thread;
   struct mixmonitor *mixmonitor;
   char postprocess2[1024] = "";
   size_t len;

   len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2;

   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);
      if (!ast_strlen_zero(postprocess2))
         len += strlen(postprocess2) + 1;
   }

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

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

   /* Copy over flags and channel name */
   mixmonitor->flags = flags;
   if (setup_mixmonitor_ds(mixmonitor, chan)) {
      mixmonitor_free(mixmonitor);
      return;
   }
   mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor);
   strcpy(mixmonitor->name, chan->name);
   if (!ast_strlen_zero(postprocess2)) {
      mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2;
      strcpy(mixmonitor->post_process, postprocess2);
   }

   mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1;
   strcpy(mixmonitor->filename, filename);

   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, chan->name);
      ast_audiohook_destroy(&mixmonitor->audiohook);
      mixmonitor_free(mixmonitor);
      return;
   }

   ast_pthread_create_detached_background(&thread, NULL, mixmonitor_thread, mixmonitor);
}
static void mixmonitor_ds_chan_fixup ( void *  data,
struct ast_channel old_chan,
struct ast_channel new_chan 
) [static]

Definition at line 221 of file app_mixmonitor.c.

References ast_mutex_lock(), ast_mutex_unlock(), mixmonitor_ds::chan, and mixmonitor_ds::lock.

{
   struct mixmonitor_ds *mixmonitor_ds = data;

   ast_mutex_lock(&mixmonitor_ds->lock);
   mixmonitor_ds->chan = new_chan;
   ast_mutex_unlock(&mixmonitor_ds->lock);
}
static void mixmonitor_ds_close_fs ( struct mixmonitor_ds mixmonitor_ds) [static]

Definition at line 199 of file app_mixmonitor.c.

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

Referenced by mixmonitor_thread(), and stop_mixmonitor_exec().

{
   if (mixmonitor_ds->fs) {
      ast_closestream(mixmonitor_ds->fs);
      mixmonitor_ds->fs = NULL;
      mixmonitor_ds->fs_quit = 1;
      ast_verb(2, "MixMonitor close filestream\n");
   }
}
static void mixmonitor_ds_destroy ( void *  data) [static]
static int mixmonitor_exec ( struct ast_channel chan,
void *  data 
) [static]

Definition at line 470 of file app_mixmonitor.c.

References AST_APP_ARG, ast_app_parse_options(), ast_config_AST_MONITOR_DIR, AST_DECLARE_APP_ARGS, ast_log(), ast_mkdir(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_strlen_zero(), ast_test_flag, ast_flags::flags, get_volfactor, launch_monitor_thread(), LOG_NOTICE, LOG_WARNING, mixmonitor_opts, MUXFLAG_READVOLUME, MUXFLAG_VOLUME, MUXFLAG_WRITEVOLUME, OPT_ARG_ARRAY_SIZE, OPT_ARG_READVOLUME, OPT_ARG_VOLUME, OPT_ARG_WRITEVOLUME, parse(), and pbx_builtin_setvar_helper().

Referenced by handle_cli_mixmonitor(), and load_module().

{
   int x, readvol = 0, writevol = 0;
   struct ast_flags flags = {0};
   char *parse, *tmp, *slash;
   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)\n");
      return -1;
   }

   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);
   
   if (ast_strlen_zero(args.filename)) {
      ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n");
      return -1;
   }

   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 not provided an absolute path, use the system-configured monitoring directory */
   if (args.filename[0] != '/') {
      char *build;

      build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3);
      sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename);
      args.filename = build;
   }

   tmp = ast_strdupa(args.filename);
   if ((slash = strrchr(tmp, '/')))
      *slash = '\0';
   ast_mkdir(tmp, 0777);

   pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename);
   launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process);

   return 0;
}
static void mixmonitor_free ( struct mixmonitor mixmonitor) [static]
static void* mixmonitor_thread ( void *  obj) [static]

Definition at line 280 of file app_mixmonitor.c.

References AST_AUDIOHOOK_DIRECTION_BOTH, ast_audiohook_lock, ast_audiohook_read_frame(), AST_AUDIOHOOK_STATUS_RUNNING, ast_audiohook_trigger_wait(), ast_audiohook_unlock, ast_bridged_channel(), ast_cond_wait(), AST_FORMAT_SLINEAR, ast_frame_free(), AST_LIST_NEXT, ast_log(), ast_mutex_lock(), ast_mutex_unlock(), ast_safe_system(), ast_test_flag, ast_verb, ast_writefile(), ast_writestream(), mixmonitor::audiohook, mixmonitor_ds::chan, destroy_monitor_audiohook(), mixmonitor_ds::destruction_condition, mixmonitor_ds::destruction_ok, ext, mixmonitor::filename, mixmonitor_ds::fs, mixmonitor_ds::fs_quit, mixmonitor_ds::lock, LOG_ERROR, mixmonitor::mixmonitor_ds, mixmonitor_ds_close_fs(), mixmonitor_free(), MUXFLAG_APPEND, MUXFLAG_BRIDGED, mixmonitor::name, mixmonitor::post_process, SAMPLES_PER_FRAME, and ast_audiohook::status.

Referenced by launch_monitor_thread().

{
   struct mixmonitor *mixmonitor = obj;
   struct ast_filestream **fs = NULL;
   unsigned int oflags;
   char *ext;
   int errflag = 0;

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

   fs = &mixmonitor->mixmonitor_ds->fs;

   /* 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;

      if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) {
         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);

      ast_mutex_lock(&mixmonitor->mixmonitor_ds->lock);
      if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || (mixmonitor->mixmonitor_ds->chan && ast_bridged_channel(mixmonitor->mixmonitor_ds->chan))) {
         /* Initialize the file if not already done so */
         if (!*fs && !errflag && !mixmonitor->mixmonitor_ds->fs_quit) {
            oflags = O_CREAT | O_WRONLY;
            oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC;

            if ((ext = strrchr(mixmonitor->filename, '.')))
               *(ext++) = '\0';
            else
               ext = "raw";

            if (!(*fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0666))) {
               ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext);
               errflag = 1;
            }
         }

         /* Write out the frame(s) */
         if (*fs) {
            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. */
      ast_frame_free(fr, 0);
      ast_audiohook_lock(&mixmonitor->audiohook);
   }

   ast_audiohook_unlock(&mixmonitor->audiohook);

   /* 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);
   mixmonitor_free(mixmonitor);
   return NULL;
}
static int setup_mixmonitor_ds ( struct mixmonitor mixmonitor,
struct ast_channel chan 
) [static]

Definition at line 367 of file app_mixmonitor.c.

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

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;
   }

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

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

   /* No need to lock mixmonitor_ds since this is still operating in the channel's thread */
   mixmonitor_ds->chan = chan;
   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 250 of file app_mixmonitor.c.

References ast_audiohook_attach(), ast_bridged_channel(), 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(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,
void *  data 
) [static]

Definition at line 551 of file app_mixmonitor.c.

References ast_audiohook_detach_source(), ast_audiohook_lock, ast_audiohook_unlock, ast_channel_datastore_find(), ast_channel_datastore_remove(), ast_channel_lock, ast_channel_unlock, ast_cond_signal(), ast_datastore_free(), ast_mutex_lock(), ast_mutex_unlock(), mixmonitor_ds::audiohook, ast_datastore::data, mixmonitor_ds::lock, mixmonitor_ds_close_fs(), and ast_audiohook::trigger.

Referenced by load_module().

{
   struct ast_datastore *datastore = NULL;

   ast_channel_lock(chan);
   ast_audiohook_detach_source(chan, mixmonitor_spy_type);
   if ((datastore = ast_channel_datastore_find(chan, &mixmonitor_ds_info, NULL))) {
      struct mixmonitor_ds *mixmonitor_ds = datastore->data;

      ast_mutex_lock(&mixmonitor_ds->lock);

      /* closing the filestream here guarantees the file is avaliable 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) {
         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 __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Mixed Audio Monitoring Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static]

Definition at line 650 of file app_mixmonitor.c.

const char* app = "MixMonitor" [static]

Definition at line 131 of file app_mixmonitor.c.

Definition at line 650 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 624 of file app_mixmonitor.c.

struct module_symbols* me
enum { ... } mixmonitor_args
Initial value:
 {
   .type = "mixmonitor",
   .destroy = mixmonitor_ds_destroy,
   .chan_fixup = mixmonitor_ds_chan_fixup,
}

Definition at line 230 of file app_mixmonitor.c.

enum { ... } mixmonitor_flags
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 },} [static]

Definition at line 169 of file app_mixmonitor.c.

Referenced by mixmonitor_exec().

const char* mixmonitor_spy_type = "MixMonitor" [static]

Definition at line 137 of file app_mixmonitor.c.

Referenced by builtin_automixmonitor().

const char* stop_app = "StopMixMonitor" [static]

Definition at line 133 of file app_mixmonitor.c.