/*! \brief Add or activate mute audiohook on channel Assumes channel is locked */ static int mute_add_audiohook(struct ast_channel *chan, struct mute_information *mute, struct ast_datastore *datastore) { /* Activate the settings */ ast_channel_datastore_add(chan, datastore); if (ast_audiohook_attach(chan, &mute->audiohook)) { ast_log(LOG_ERROR, "Failed to attach audiohook for muting channel %s\n", ast_channel_name(chan)); return -1; } ast_module_ref(ast_module_info->self); ast_debug(2, "Initialized audiohook on channel %s\n", ast_channel_name(chan)); return 0; }
static int start_spying(struct ast_channel *chan, const char *spychan_name, struct ast_audiohook *audiohook) { int res = 0; struct ast_channel *peer = NULL; ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, chan->name); res = 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 start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook) { int res = 0; struct ast_channel *peer = NULL; ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, autochan->chan->name); ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); res = ast_audiohook_attach(autochan->chan, audiohook); if (!res && ast_test_flag(autochan->chan, AST_FLAG_NBRIDGE) && (peer = ast_bridged_channel(autochan->chan))) { ast_softhangup(peer, AST_SOFTHANGUP_UNBRIDGE); } return res; }
static int startmon(struct ast_channel *chan, struct ast_audiohook *audiohook) { 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 start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook, struct ast_flags *flags) { ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan)); if(ast_test_flag(flags, OPTION_READONLY)) { ast_set_flag(audiohook, AST_AUDIOHOOK_MUTE_WRITE); } else { ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC); } if(ast_test_flag(flags, OPTION_LONG_QUEUE)) { ast_debug(9, "Using a long queue to store audio frames in spy audiohook\n"); } else { ast_set_flag(audiohook, AST_AUDIOHOOK_SMALL_QUEUE); } return ast_audiohook_attach(autochan->chan, audiohook); }
/*! \internal \brief Enable talk detection on the channel */ static int set_talk_detect(struct ast_channel *chan, int dsp_silence_threshold, int dsp_talking_threshold) { struct ast_datastore *datastore = NULL; struct talk_detect_params *td_params; SCOPED_CHANNELLOCK(chan_lock, chan); datastore = ast_channel_datastore_find(chan, &talk_detect_datastore, NULL); if (!datastore) { datastore = ast_datastore_alloc(&talk_detect_datastore, NULL); if (!datastore) { return -1; } td_params = ast_calloc(1, sizeof(*td_params)); if (!td_params) { ast_datastore_free(datastore); return -1; } ast_audiohook_init(&td_params->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "TALK_DETECT", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); td_params->audiohook.manipulate_callback = talk_detect_audiohook_cb; ast_set_flag(&td_params->audiohook, AST_AUDIOHOOK_TRIGGER_READ); td_params->dsp = ast_dsp_new_with_rate(ast_format_get_sample_rate(ast_channel_rawreadformat(chan))); if (!td_params->dsp) { ast_datastore_free(datastore); ast_free(td_params); return -1; } datastore->data = td_params; ast_channel_datastore_add(chan, datastore); ast_audiohook_attach(chan, &td_params->audiohook); } else { /* Talk detection already enabled; update existing settings */ td_params = datastore->data; } td_params->dsp_talking_threshold = dsp_talking_threshold; td_params->dsp_silence_threshold = dsp_silence_threshold; ast_dsp_set_threshold(td_params->dsp, td_params->dsp_talking_threshold); return 0; }
/*! \brief Internal helper function which sets up and attaches a snoop audiohook */ static int snoop_setup_audiohook(struct ast_channel *chan, enum ast_audiohook_type type, enum stasis_app_snoop_direction requested_direction, enum ast_audiohook_direction *direction, struct ast_audiohook *audiohook) { ast_audiohook_init(audiohook, type, "Snoop", 0); if (requested_direction == STASIS_SNOOP_DIRECTION_OUT) { *direction = AST_AUDIOHOOK_DIRECTION_WRITE; } else if (requested_direction == STASIS_SNOOP_DIRECTION_IN) { *direction = AST_AUDIOHOOK_DIRECTION_READ; } else if (requested_direction == STASIS_SNOOP_DIRECTION_BOTH) { *direction = AST_AUDIOHOOK_DIRECTION_BOTH; } else { return -1; } return ast_audiohook_attach(chan, audiohook); }
static int start_spying(struct ast_autochan *autochan, const char *spychan_name, struct ast_audiohook *audiohook) { int res = 0; ast_log(LOG_NOTICE, "Attaching %s to %s\n", spychan_name, ast_channel_name(autochan->chan)); ast_set_flag(audiohook, AST_AUDIOHOOK_TRIGGER_SYNC | AST_AUDIOHOOK_SMALL_QUEUE); res = ast_audiohook_attach(autochan->chan, audiohook); if (!res) { ast_channel_lock(autochan->chan); if (ast_channel_is_bridged(autochan->chan)) { ast_softhangup_nolock(autochan->chan, AST_SOFTHANGUP_UNBRIDGE); } ast_channel_unlock(autochan->chan); } return res; }
static int volume_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) { struct ast_datastore *datastore = NULL; struct volume_information *vi = NULL; int is_new = 0; if (!(datastore = ast_channel_datastore_find(chan, &volume_datastore, NULL))) { /* Allocate a new datastore to hold the reference to this volume and audiohook information */ if (!(datastore = ast_datastore_alloc(&volume_datastore, NULL))) return 0; if (!(vi = ast_calloc(1, sizeof(*vi)))) { ast_datastore_free(datastore); return 0; } ast_audiohook_init(&vi->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "Volume"); vi->audiohook.manipulate_callback = volume_callback; ast_set_flag(&vi->audiohook, AST_AUDIOHOOK_WANTS_DTMF); is_new = 1; } else { vi = datastore->data; } /* Adjust gain on volume information structure */ if (!strcasecmp(data, "tx")) vi->tx_gain = atoi(value); else if (!strcasecmp(data, "rx")) vi->rx_gain = atoi(value); if (is_new) { datastore->data = vi; ast_channel_datastore_add(chan, datastore); ast_audiohook_attach(chan, &vi->audiohook); } return 0; }
static void *hook_launch_thread(void *data) { struct hook_thread_arg *arg = data; struct ast_variable hook_id = { .name = "HOOK_ID", .value = arg->hook_id, }; struct ast_variable chan_name_var = { .name = "HOOK_CHANNEL", .value = arg->chan_name, .next = &hook_id, }; ast_pbx_outgoing_exten("Local", NULL, full_exten_name, 60, arg->context, arg->exten, 1, NULL, AST_OUTGOING_NO_WAIT, NULL, NULL, &chan_name_var, NULL, NULL, 1, NULL); hook_thread_arg_destroy(arg); return NULL; } static struct hook_thread_arg *hook_thread_arg_alloc(struct ast_channel *chan, struct hook_state *state) { struct hook_thread_arg *arg; if (!(arg = ast_calloc(1, sizeof(*arg)))) { return NULL; } ast_channel_lock(chan); arg->chan_name = ast_strdup(ast_channel_name(chan)); ast_channel_unlock(chan); if (!arg->chan_name) { hook_thread_arg_destroy(arg); return NULL; } if (ast_asprintf(&arg->hook_id, "%u", state->hook_id) == -1) { hook_thread_arg_destroy(arg); return NULL; } if (!(arg->context = ast_strdup(state->context))) { hook_thread_arg_destroy(arg); return NULL; } if (!(arg->exten = ast_strdup(state->exten))) { hook_thread_arg_destroy(arg); return NULL; } return arg; } static int do_hook(struct ast_channel *chan, struct hook_state *state) { pthread_t t; struct hook_thread_arg *arg; int res; if (!(arg = hook_thread_arg_alloc(chan, state))) { return -1; } /* * We don't want to block normal frame processing *at all* while we kick * this off, so do it in a new thread. */ res = ast_pthread_create_detached_background(&t, NULL, hook_launch_thread, arg); if (res != 0) { hook_thread_arg_destroy(arg); } return res; } static int hook_callback(struct ast_audiohook *audiohook, struct ast_channel *chan, struct ast_frame *frame, enum ast_audiohook_direction direction) { struct hook_state *state = (struct hook_state *) audiohook; /* trust me. */ struct timeval now; int res = 0; if (audiohook->status == AST_AUDIOHOOK_STATUS_DONE || state->disabled) { return 0; } now = ast_tvnow(); if (ast_tvdiff_ms(now, state->last_hook) > state->interval * 1000) { if ((res = do_hook(chan, state))) { const char *name; ast_channel_lock(chan); name = ast_strdupa(ast_channel_name(chan)); ast_channel_unlock(chan); ast_log(LOG_WARNING, "Failed to run hook on '%s'\n", name); } state->last_hook = now; } return res; } static struct hook_state *hook_state_alloc(const char *context, const char *exten, unsigned int interval, unsigned int hook_id) { struct hook_state *state; if (!(state = ast_calloc(1, sizeof(*state)))) { return NULL; } state->context = ast_strdup(context); state->exten = ast_strdup(exten); state->interval = interval; state->hook_id = hook_id; ast_audiohook_init(&state->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, AST_MODULE, AST_AUDIOHOOK_MANIPULATE_ALL_RATES); state->audiohook.manipulate_callback = hook_callback; return state; } static int init_hook(struct ast_channel *chan, const char *context, const char *exten, unsigned int interval, unsigned int hook_id) { struct hook_state *state; struct ast_datastore *datastore; char uid[32]; snprintf(uid, sizeof(uid), "%u", hook_id); if (!(datastore = ast_datastore_alloc(&hook_datastore, uid))) { return -1; } ast_module_ref(ast_module_info->self); if (!(state = hook_state_alloc(context, exten, interval, hook_id))) { ast_datastore_free(datastore); return -1; } datastore->data = state; ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_audiohook_attach(chan, &state->audiohook); ast_channel_unlock(chan); return 0; } static int hook_on(struct ast_channel *chan, const char *data, unsigned int hook_id) { char *parse = ast_strdupa(S_OR(data, "")); AST_DECLARE_APP_ARGS(args, AST_APP_ARG(context); AST_APP_ARG(exten); AST_APP_ARG(interval); );
static int speex_write(struct ast_channel *chan, const char *cmd, char *data, const char *value) { struct ast_datastore *datastore = NULL; struct speex_info *si = NULL; struct speex_direction_info **sdi = NULL; int is_new = 0; ast_channel_lock(chan); if (!(datastore = ast_channel_datastore_find(chan, &speex_datastore, NULL))) { ast_channel_unlock(chan); if (!(datastore = ast_datastore_alloc(&speex_datastore, NULL))) { return 0; } if (!(si = ast_calloc(1, sizeof(*si)))) { ast_datastore_free(datastore); return 0; } ast_audiohook_init(&si->audiohook, AST_AUDIOHOOK_TYPE_MANIPULATE, "speex", AST_AUDIOHOOK_MANIPULATE_ALL_RATES); si->audiohook.manipulate_callback = speex_callback; si->lastrate = 8000; is_new = 1; } else { ast_channel_unlock(chan); si = datastore->data; } if (!strcasecmp(data, "rx")) { sdi = &si->rx; } else if (!strcasecmp(data, "tx")) { sdi = &si->tx; } else { ast_log(LOG_ERROR, "Invalid argument provided to the %s function\n", cmd); if (is_new) { ast_datastore_free(datastore); return -1; } } if (!*sdi) { if (!(*sdi = ast_calloc(1, sizeof(**sdi)))) { return 0; } /* Right now, the audiohooks API will _only_ provide us 8 kHz slinear * audio. When it supports 16 kHz (or any other sample rates, we will * have to take that into account here. */ (*sdi)->samples = -1; } if (!strcasecmp(cmd, "agc")) { if (!sscanf(value, "%30f", &(*sdi)->agclevel)) (*sdi)->agclevel = ast_true(value) ? DEFAULT_AGC_LEVEL : 0.0; if ((*sdi)->agclevel > 32768.0) { ast_log(LOG_WARNING, "AGC(%s)=%.01f is greater than 32768... setting to 32768 instead\n", ((*sdi == si->rx) ? "rx" : "tx"), (*sdi)->agclevel); (*sdi)->agclevel = 32768.0; } (*sdi)->agc = !!((*sdi)->agclevel); if ((*sdi)->state) { speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC, &(*sdi)->agc); if ((*sdi)->agc) { speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_AGC_LEVEL, &(*sdi)->agclevel); } } } else if (!strcasecmp(cmd, "denoise")) { (*sdi)->denoise = (ast_true(value) != 0); if ((*sdi)->state) { speex_preprocess_ctl((*sdi)->state, SPEEX_PREPROCESS_SET_DENOISE, &(*sdi)->denoise); } } if (!(*sdi)->agc && !(*sdi)->denoise) { if ((*sdi)->state) speex_preprocess_state_destroy((*sdi)->state); ast_free(*sdi); *sdi = NULL; } if (!si->rx && !si->tx) { if (is_new) { is_new = 0; } else { ast_channel_lock(chan); ast_channel_datastore_remove(chan, datastore); ast_channel_unlock(chan); ast_audiohook_remove(chan, &si->audiohook); ast_audiohook_detach(&si->audiohook); } ast_datastore_free(datastore); } if (is_new) { datastore->data = si; ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_channel_unlock(chan); ast_audiohook_attach(chan, &si->audiohook); } return 0; }