#include "asterisk.h"
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <unistd.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/audiohook.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/cli.h"
#include "asterisk/options.h"
#include "asterisk/app.h"
#include "asterisk/linkedlists.h"
#include "asterisk/utils.h"
Go to the source code of this file.
Data Structures | |
struct | mixmonitor |
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 char * | complete_mixmonitor_cli (const char *line, const char *word, int pos, int state) |
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 int | mixmonitor_cli (int fd, int argc, char **argv) |
static int | mixmonitor_exec (struct ast_channel *chan, void *data) |
static void * | mixmonitor_thread (void *obj) |
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 | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, } |
static const char * | app = "MixMonitor" |
static const struct ast_module_info * | ast_module_info = &__mod_info |
static struct ast_cli_entry | cli_mixmonitor [] |
static const char * | desc |
module_symbols * | me |
enum { ... } | mixmonitor_args |
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" |
static const char * | stop_desc |
static const char * | stop_synopsis = "Stop recording a call through MixMonitor" |
static const char * | synopsis = "Record a call and mix the audio during the recording" |
Kevin P. Fleming <kpfleming@digium.com>
Definition in file app_mixmonitor.c.
#define get_volfactor | ( | x | ) | x ? ((x > 0) ? (1 << x) : ((1 << abs(x)) * -1)) : 0 |
#define SAMPLES_PER_FRAME 160 |
anonymous enum |
Definition at line 104 of file app_mixmonitor.c.
00104 { 00105 MUXFLAG_APPEND = (1 << 1), 00106 MUXFLAG_BRIDGED = (1 << 2), 00107 MUXFLAG_VOLUME = (1 << 3), 00108 MUXFLAG_READVOLUME = (1 << 4), 00109 MUXFLAG_WRITEVOLUME = (1 << 5), 00110 } mixmonitor_flags;
anonymous enum |
Definition at line 112 of file app_mixmonitor.c.
00112 { 00113 OPT_ARG_READVOLUME = 0, 00114 OPT_ARG_WRITEVOLUME, 00115 OPT_ARG_VOLUME, 00116 OPT_ARG_ARRAY_SIZE, 00117 } mixmonitor_args;
static void __reg_module | ( | void | ) | [static] |
Definition at line 446 of file app_mixmonitor.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 446 of file app_mixmonitor.c.
static char* complete_mixmonitor_cli | ( | const char * | line, | |
const char * | word, | |||
int | pos, | |||
int | state | |||
) | [static] |
Definition at line 408 of file app_mixmonitor.c.
References ast_complete_channels().
00409 { 00410 return ast_complete_channels(line, word, pos, state, 2); 00411 }
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 217 of file app_mixmonitor.c.
References ast_audiohook_destroy(), ast_audiohook_init(), AST_AUDIOHOOK_TRIGGER_SYNC, AST_AUDIOHOOK_TYPE_SPY, ast_log(), ast_pthread_create_background, ast_set_flag, ast_strdupa, ast_strlen_zero(), calloc, mixmonitor::chan, mixmonitor::flags, free, len(), LOG_WARNING, mixmonitor_thread(), mixmonitor::name, ast_channel::name, pbx_substitute_variables_helper(), startmon(), and thread.
Referenced by mixmonitor_exec().
00219 { 00220 pthread_attr_t attr; 00221 pthread_t thread; 00222 struct mixmonitor *mixmonitor; 00223 char postprocess2[1024] = ""; 00224 size_t len; 00225 00226 len = sizeof(*mixmonitor) + strlen(chan->name) + strlen(filename) + 2; 00227 00228 /* If a post process system command is given attach it to the structure */ 00229 if (!ast_strlen_zero(post_process)) { 00230 char *p1, *p2; 00231 00232 p1 = ast_strdupa(post_process); 00233 for (p2 = p1; *p2 ; p2++) { 00234 if (*p2 == '^' && *(p2+1) == '{') { 00235 *p2 = '$'; 00236 } 00237 } 00238 00239 pbx_substitute_variables_helper(chan, p1, postprocess2, sizeof(postprocess2) - 1); 00240 if (!ast_strlen_zero(postprocess2)) 00241 len += strlen(postprocess2) + 1; 00242 } 00243 00244 /* Pre-allocate mixmonitor structure and spy */ 00245 if (!(mixmonitor = calloc(1, len))) { 00246 return; 00247 } 00248 00249 /* Copy over flags and channel name */ 00250 mixmonitor->flags = flags; 00251 mixmonitor->chan = chan; 00252 mixmonitor->name = (char *) mixmonitor + sizeof(*mixmonitor); 00253 strcpy(mixmonitor->name, chan->name); 00254 if (!ast_strlen_zero(postprocess2)) { 00255 mixmonitor->post_process = mixmonitor->name + strlen(mixmonitor->name) + strlen(filename) + 2; 00256 strcpy(mixmonitor->post_process, postprocess2); 00257 } 00258 00259 mixmonitor->filename = (char *) mixmonitor + sizeof(*mixmonitor) + strlen(chan->name) + 1; 00260 strcpy(mixmonitor->filename, filename); 00261 00262 /* Setup the actual spy before creating our thread */ 00263 if (ast_audiohook_init(&mixmonitor->audiohook, AST_AUDIOHOOK_TYPE_SPY, mixmonitor_spy_type)) { 00264 free(mixmonitor); 00265 return; 00266 } 00267 00268 ast_set_flag(&mixmonitor->audiohook, AST_AUDIOHOOK_TRIGGER_SYNC); 00269 00270 if (readvol) 00271 mixmonitor->audiohook.options.read_volume = readvol; 00272 if (writevol) 00273 mixmonitor->audiohook.options.write_volume = writevol; 00274 00275 if (startmon(chan, &mixmonitor->audiohook)) { 00276 ast_log(LOG_WARNING, "Unable to add '%s' spy to channel '%s'\n", 00277 mixmonitor_spy_type, chan->name); 00278 /* Since we couldn't add ourselves - bail out! */ 00279 ast_audiohook_destroy(&mixmonitor->audiohook); 00280 free(mixmonitor); 00281 return; 00282 } 00283 00284 pthread_attr_init(&attr); 00285 pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); 00286 ast_pthread_create_background(&thread, &attr, mixmonitor_thread, mixmonitor); 00287 pthread_attr_destroy(&attr); 00288 00289 }
static int load_module | ( | void | ) | [static] |
Definition at line 435 of file app_mixmonitor.c.
References ast_cli_register_multiple(), ast_register_application(), cli_mixmonitor, mixmonitor_exec(), and stop_mixmonitor_exec().
00436 { 00437 int res; 00438 00439 ast_cli_register_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry)); 00440 res = ast_register_application(app, mixmonitor_exec, synopsis, desc); 00441 res |= ast_register_application(stop_app, stop_mixmonitor_exec, stop_synopsis, stop_desc); 00442 00443 return res; 00444 }
static int mixmonitor_cli | ( | int | fd, | |
int | argc, | |||
char ** | argv | |||
) | [static] |
Definition at line 386 of file app_mixmonitor.c.
References ast_audiohook_detach_source(), ast_channel_unlock, ast_cli(), ast_get_channel_by_name_prefix_locked(), mixmonitor_exec(), RESULT_SHOWUSAGE, and RESULT_SUCCESS.
00387 { 00388 struct ast_channel *chan; 00389 00390 if (argc < 3) 00391 return RESULT_SHOWUSAGE; 00392 00393 if (!(chan = ast_get_channel_by_name_prefix_locked(argv[2], strlen(argv[2])))) { 00394 ast_cli(fd, "No channel matching '%s' found.\n", argv[2]); 00395 return RESULT_SUCCESS; 00396 } 00397 00398 if (!strcasecmp(argv[1], "start")) 00399 mixmonitor_exec(chan, argv[3]); 00400 else if (!strcasecmp(argv[1], "stop")) 00401 ast_audiohook_detach_source(chan, mixmonitor_spy_type); 00402 00403 ast_channel_unlock(chan); 00404 00405 return RESULT_SUCCESS; 00406 }
static int mixmonitor_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 291 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_module_user_add, ast_module_user_remove, 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 load_module(), and mixmonitor_cli().
00292 { 00293 int x, readvol = 0, writevol = 0; 00294 struct ast_module_user *u; 00295 struct ast_flags flags = {0}; 00296 char *parse; 00297 AST_DECLARE_APP_ARGS(args, 00298 AST_APP_ARG(filename); 00299 AST_APP_ARG(options); 00300 AST_APP_ARG(post_process); 00301 ); 00302 00303 if (ast_strlen_zero(data)) { 00304 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00305 return -1; 00306 } 00307 00308 u = ast_module_user_add(chan); 00309 00310 parse = ast_strdupa(data); 00311 00312 AST_STANDARD_APP_ARGS(args, parse); 00313 00314 if (ast_strlen_zero(args.filename)) { 00315 ast_log(LOG_WARNING, "MixMonitor requires an argument (filename)\n"); 00316 ast_module_user_remove(u); 00317 return -1; 00318 } 00319 00320 if (args.options) { 00321 char *opts[OPT_ARG_ARRAY_SIZE] = { NULL, }; 00322 00323 ast_app_parse_options(mixmonitor_opts, &flags, opts, args.options); 00324 00325 if (ast_test_flag(&flags, MUXFLAG_READVOLUME)) { 00326 if (ast_strlen_zero(opts[OPT_ARG_READVOLUME])) { 00327 ast_log(LOG_WARNING, "No volume level was provided for the heard volume ('v') option.\n"); 00328 } else if ((sscanf(opts[OPT_ARG_READVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00329 ast_log(LOG_NOTICE, "Heard volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_READVOLUME]); 00330 } else { 00331 readvol = get_volfactor(x); 00332 } 00333 } 00334 00335 if (ast_test_flag(&flags, MUXFLAG_WRITEVOLUME)) { 00336 if (ast_strlen_zero(opts[OPT_ARG_WRITEVOLUME])) { 00337 ast_log(LOG_WARNING, "No volume level was provided for the spoken volume ('V') option.\n"); 00338 } else if ((sscanf(opts[OPT_ARG_WRITEVOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00339 ast_log(LOG_NOTICE, "Spoken volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_WRITEVOLUME]); 00340 } else { 00341 writevol = get_volfactor(x); 00342 } 00343 } 00344 00345 if (ast_test_flag(&flags, MUXFLAG_VOLUME)) { 00346 if (ast_strlen_zero(opts[OPT_ARG_VOLUME])) { 00347 ast_log(LOG_WARNING, "No volume level was provided for the combined volume ('W') option.\n"); 00348 } else if ((sscanf(opts[OPT_ARG_VOLUME], "%d", &x) != 1) || (x < -4) || (x > 4)) { 00349 ast_log(LOG_NOTICE, "Combined volume must be a number between -4 and 4, not '%s'\n", opts[OPT_ARG_VOLUME]); 00350 } else { 00351 readvol = writevol = get_volfactor(x); 00352 } 00353 } 00354 } 00355 00356 /* if not provided an absolute path, use the system-configured monitoring directory */ 00357 if (args.filename[0] != '/') { 00358 char *build; 00359 00360 build = alloca(strlen(ast_config_AST_MONITOR_DIR) + strlen(args.filename) + 3); 00361 sprintf(build, "%s/%s", ast_config_AST_MONITOR_DIR, args.filename); 00362 args.filename = build; 00363 } 00364 00365 pbx_builtin_setvar_helper(chan, "MIXMONITOR_FILENAME", args.filename); 00366 launch_monitor_thread(chan, args.filename, flags.flags, readvol, writevol, args.post_process); 00367 00368 ast_module_user_remove(u); 00369 00370 return 0; 00371 }
static void* mixmonitor_thread | ( | void * | obj | ) | [static] |
Definition at line 145 of file app_mixmonitor.c.
References ast_audiohook_destroy(), ast_audiohook_detach(), 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_closestream(), AST_FORMAT_SLINEAR, ast_frame_free(), ast_log(), ast_safe_system(), ast_test_flag, ast_verbose(), ast_writefile(), ast_writestream(), mixmonitor::audiohook, mixmonitor::chan, ext, mixmonitor::filename, free, LOG_ERROR, MUXFLAG_APPEND, MUXFLAG_BRIDGED, mixmonitor::name, option_verbose, mixmonitor::post_process, SAMPLES_PER_FRAME, ast_audiohook::status, and VERBOSE_PREFIX_2.
Referenced by launch_monitor_thread().
00146 { 00147 struct mixmonitor *mixmonitor = obj; 00148 struct ast_filestream *fs = NULL; 00149 unsigned int oflags; 00150 char *ext; 00151 int errflag = 0; 00152 00153 if (option_verbose > 1) 00154 ast_verbose(VERBOSE_PREFIX_2 "Begin MixMonitor Recording %s\n", mixmonitor->name); 00155 00156 ast_audiohook_lock(&mixmonitor->audiohook); 00157 00158 while (mixmonitor->audiohook.status == AST_AUDIOHOOK_STATUS_RUNNING) { 00159 struct ast_frame *fr = NULL; 00160 00161 ast_audiohook_trigger_wait(&mixmonitor->audiohook); 00162 00163 if (mixmonitor->audiohook.status != AST_AUDIOHOOK_STATUS_RUNNING) 00164 break; 00165 00166 if (!(fr = ast_audiohook_read_frame(&mixmonitor->audiohook, SAMPLES_PER_FRAME, AST_AUDIOHOOK_DIRECTION_BOTH, AST_FORMAT_SLINEAR))) 00167 continue; 00168 00169 if (!ast_test_flag(mixmonitor, MUXFLAG_BRIDGED) || ast_bridged_channel(mixmonitor->chan)) { 00170 /* Initialize the file if not already done so */ 00171 if (!fs && !errflag) { 00172 oflags = O_CREAT | O_WRONLY; 00173 oflags |= ast_test_flag(mixmonitor, MUXFLAG_APPEND) ? O_APPEND : O_TRUNC; 00174 00175 if ((ext = strrchr(mixmonitor->filename, '.'))) 00176 *(ext++) = '\0'; 00177 else 00178 ext = "raw"; 00179 00180 if (!(fs = ast_writefile(mixmonitor->filename, ext, NULL, oflags, 0, 0644))) { 00181 ast_log(LOG_ERROR, "Cannot open %s.%s\n", mixmonitor->filename, ext); 00182 errflag = 1; 00183 } 00184 } 00185 00186 /* Write out the frame */ 00187 if (fs) 00188 ast_writestream(fs, fr); 00189 } 00190 00191 /* All done! free it. */ 00192 ast_frame_free(fr, 0); 00193 } 00194 00195 ast_audiohook_detach(&mixmonitor->audiohook); 00196 ast_audiohook_unlock(&mixmonitor->audiohook); 00197 ast_audiohook_destroy(&mixmonitor->audiohook); 00198 00199 if (option_verbose > 1) 00200 ast_verbose(VERBOSE_PREFIX_2 "End MixMonitor Recording %s\n", mixmonitor->name); 00201 00202 if (fs) 00203 ast_closestream(fs); 00204 00205 if (mixmonitor->post_process) { 00206 if (option_verbose > 2) 00207 ast_verbose(VERBOSE_PREFIX_2 "Executing [%s]\n", mixmonitor->post_process); 00208 ast_safe_system(mixmonitor->post_process); 00209 } 00210 00211 free(mixmonitor); 00212 00213 00214 return NULL; 00215 }
static int startmon | ( | struct ast_channel * | chan, | |
struct ast_audiohook * | audiohook | |||
) | [static] |
Definition at line 127 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().
00128 { 00129 struct ast_channel *peer; 00130 int res; 00131 00132 if (!chan) 00133 return -1; 00134 00135 res = ast_audiohook_attach(chan, audiohook); 00136 00137 if (!res && ast_test_flag(chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(chan))) 00138 ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); 00139 00140 return res; 00141 }
static int stop_mixmonitor_exec | ( | struct ast_channel * | chan, | |
void * | data | |||
) | [static] |
Definition at line 373 of file app_mixmonitor.c.
References ast_audiohook_detach_source(), ast_module_user_add, ast_module_user_remove, and ast_module_user::chan.
Referenced by load_module().
00374 { 00375 struct ast_module_user *u; 00376 00377 u = ast_module_user_add(chan); 00378 00379 ast_audiohook_detach_source(chan, mixmonitor_spy_type); 00380 00381 ast_module_user_remove(u); 00382 00383 return 0; 00384 }
static int unload_module | ( | void | ) | [static] |
Definition at line 422 of file app_mixmonitor.c.
References ast_cli_unregister_multiple(), ast_module_user_hangup_all, ast_unregister_application(), and cli_mixmonitor.
00423 { 00424 int res; 00425 00426 ast_cli_unregister_multiple(cli_mixmonitor, sizeof(cli_mixmonitor) / sizeof(struct ast_cli_entry)); 00427 res = ast_unregister_application(stop_app); 00428 res |= ast_unregister_application(app); 00429 00430 ast_module_user_hangup_all(); 00431 00432 return res; 00433 }
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT | AST_MODFLAG_BUILDSUM, .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 = "f450f61f60e761b3aa089ebed76ca8a5" , .load = load_module, .unload = unload_module, } [static] |
Definition at line 446 of file app_mixmonitor.c.
const char* app = "MixMonitor" [static] |
Definition at line 60 of file app_mixmonitor.c.
const struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 446 of file app_mixmonitor.c.
struct ast_cli_entry cli_mixmonitor[] [static] |
const char* desc [static] |
Definition at line 62 of file app_mixmonitor.c.
struct module_symbols* me |
Definition at line 91 of file app_mixmonitor.c.
enum { ... } mixmonitor_args |
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] |
const char* mixmonitor_spy_type = "MixMonitor" [static] |
Definition at line 93 of file app_mixmonitor.c.
const char* stop_app = "StopMixMonitor" [static] |
Definition at line 83 of file app_mixmonitor.c.
const char* stop_desc [static] |
Initial value:
"" " StopMixMonitor()\n\n" "Stops the audio recording that was started with a call to MixMonitor()\n" "on the current channel.\n" ""
Definition at line 85 of file app_mixmonitor.c.
const char* stop_synopsis = "Stop recording a call through MixMonitor" [static] |
Definition at line 84 of file app_mixmonitor.c.
const char* synopsis = "Record a call and mix the audio during the recording" [static] |
Definition at line 61 of file app_mixmonitor.c.