/*! \brief SpeechDestroy() Dialplan Application */ static int speech_destroy(struct ast_channel *chan, void *data) { int res = 0; struct ast_module_user *u = NULL; struct ast_speech *speech = find_speech(chan); struct ast_datastore *datastore = NULL; u = ast_module_user_add(chan); if (speech == NULL) { ast_module_user_remove(u); return -1; } /* Destroy speech structure */ ast_speech_destroy(speech); datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL); if (datastore != NULL) { ast_channel_datastore_remove(chan, datastore); } ast_module_user_remove(u); return res; }
static int srv_query_read(struct ast_channel *chan, const char *cmd, char *data, char *buf, size_t len) { struct ast_datastore *datastore; if (!chan) { ast_log(LOG_WARNING, "%s cannot be used without a channel\n", cmd); return -1; } if (ast_strlen_zero(data)) { ast_log(LOG_WARNING, "%s requires a service as an argument\n", cmd); return -1; } /* If they already called SRVQUERY for this service once, * we need to kill the old datastore. */ ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &srv_result_datastore_info, data); ast_channel_unlock(chan); if (datastore) { ast_channel_datastore_remove(chan, datastore); ast_datastore_free(datastore); } if (!srv_datastore_setup(data, chan)) { return -1; } ast_copy_string(buf, data, len); return 0; }
/*! * \brief Dial timeout * * This is a bridge interval hook callback. The interval hook triggering * means that the dial timeout has been reached. If the channel has not * been answered by the time this callback is called, then the channel * is hung up * * \param bridge_channel Bridge channel on which interval hook has been called * \param ignore Ignored * \return -1 (i.e. remove the interval hook) */ static int bridge_timeout(struct ast_bridge_channel *bridge_channel, void *ignore) { struct ast_datastore *datastore; RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup); control = stasis_app_control_find_by_channel(bridge_channel->chan); ast_channel_lock(bridge_channel->chan); if (ast_channel_state(bridge_channel->chan) != AST_STATE_UP) { /* Don't bother removing the datastore because it will happen when the channel is hung up */ ast_channel_unlock(bridge_channel->chan); stasis_app_send_command_async(control, hangup_channel, NULL, NULL); return -1; } datastore = ast_channel_datastore_find(bridge_channel->chan, &timeout_datastore, NULL); if (!datastore) { ast_channel_unlock(bridge_channel->chan); return -1; } ast_channel_datastore_remove(bridge_channel->chan, datastore); ast_channel_unlock(bridge_channel->chan); ast_datastore_free(datastore); return -1; }
static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds) { if (!chanspy_ds) return NULL; ast_mutex_lock(&chanspy_ds->lock); if (chanspy_ds->chan) { struct ast_datastore *datastore; struct ast_channel *chan; chan = chanspy_ds->chan; ast_channel_lock(chan); if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { ast_channel_datastore_remove(chan, datastore); /* chanspy_ds->chan is NULL after this call */ chanspy_ds_destroy(datastore->data); datastore->data = NULL; ast_channel_datastore_free(datastore); } ast_channel_unlock(chan); } ast_mutex_unlock(&chanspy_ds->lock); return NULL; }
static struct chanspy_ds *chanspy_ds_free(struct chanspy_ds *chanspy_ds) { struct ast_channel *chan; if (!chanspy_ds) { return NULL; } ast_mutex_lock(&chanspy_ds->lock); while ((chan = chanspy_ds->chan)) { struct ast_datastore *datastore; if (ast_channel_trylock(chan)) { DEADLOCK_AVOIDANCE(&chanspy_ds->lock); continue; } if ((datastore = ast_channel_datastore_find(chan, &chanspy_ds_info, chanspy_ds->unique_id))) { ast_channel_datastore_remove(chan, datastore); /* chanspy_ds->chan is NULL after this call */ chanspy_ds_destroy(datastore->data); datastore->data = NULL; ast_channel_datastore_free(datastore); } ast_channel_unlock(chan); break; } ast_mutex_unlock(&chanspy_ds->lock); return NULL; }
/*! \internal \brief Disable talk detection on the channel */ static int remove_talk_detect(struct ast_channel *chan) { 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) { ast_log(AST_LOG_WARNING, "Cannot remove TALK_DETECT from %s: TALK_DETECT not currently enabled\n", ast_channel_name(chan)); return -1; } td_params = datastore->data; if (ast_audiohook_remove(chan, &td_params->audiohook)) { ast_log(AST_LOG_WARNING, "Failed to remove TALK_DETECT audiohook from channel %s\n", ast_channel_name(chan)); return -1; } if (ast_channel_datastore_remove(chan, datastore)) { ast_log(AST_LOG_WARNING, "Failed to remove TALK_DETECT datastore from channel %s\n", ast_channel_name(chan)); return -1; } ast_datastore_free(datastore); return 0; }
static int frame_trace_helper(struct ast_channel *chan, const char *cmd, char *data, const char *value) { struct frame_trace_data *framedata; struct ast_datastore *datastore = NULL; struct ast_framehook_interface interface = { .version = AST_FRAMEHOOK_INTERFACE_VERSION, .event_cb = hook_event_cb, .destroy_cb = hook_destroy_cb, }; int i = 0; if (!(framedata = ast_calloc(1, sizeof(*framedata)))) { return 0; } interface.data = framedata; if (!strcasecmp(data, "black")) { framedata->list_type = 1; } for (i = 0; i < ARRAY_LEN(frametype2str); i++) { if (strcasestr(value, frametype2str[i].str)) { framedata->values[i] = 1; } } ast_channel_lock(chan); i = ast_framehook_attach(chan, &interface); if (i >= 0) { int *id; if ((datastore = ast_channel_datastore_find(chan, &frame_trace_datastore, NULL))) { id = datastore->data; ast_framehook_detach(chan, *id); ast_channel_datastore_remove(chan, datastore); } if (!(datastore = ast_datastore_alloc(&frame_trace_datastore, NULL))) { ast_framehook_detach(chan, i); ast_channel_unlock(chan); return 0; } if (!(id = ast_calloc(1, sizeof(int)))) { ast_datastore_free(datastore); ast_framehook_detach(chan, i); ast_channel_unlock(chan); return 0; } *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */ datastore->data = id; ast_channel_datastore_add(chan, datastore); } ast_channel_unlock(chan); return 0; }
static void wipe_subscription_datastore(struct ast_channel *chan) { struct ast_datastore *datastore; ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &parked_subscription_info, NULL); if (datastore) { ast_channel_datastore_remove(chan, datastore); ast_datastore_free(datastore); } ast_channel_unlock(chan); }
/*! * \internal * \brief Destroy the speech datastore on the given channel. * * \param chan Channel to destroy speech datastore. * * \retval 0 on success. * \retval -1 not found. */ static int speech_datastore_destroy(struct ast_channel *chan) { struct ast_datastore *datastore; int res; ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL); if (datastore) { ast_channel_datastore_remove(chan, datastore); } ast_channel_unlock(chan); if (datastore) { ast_datastore_free(datastore); res = 0; } else { res = -1; } return res; }
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; }
/*! \brief SpeechBackground(Sound File|Timeout) Dialplan Application */ static int speech_background(struct ast_channel *chan, void *data) { unsigned int timeout = 0; int res = 0, done = 0, argc = 0, started = 0, quieted = 0, max_dtmf_len = 0; struct ast_module_user *u = NULL; struct ast_speech *speech = find_speech(chan); struct ast_frame *f = NULL; int oldreadformat = AST_FORMAT_SLINEAR; char dtmf[AST_MAX_EXTENSION] = ""; time_t start, current; struct ast_datastore *datastore = NULL; char *argv[2], *args = NULL, *filename_tmp = NULL, *filename = NULL, tmp[2] = "", dtmf_terminator = '#'; const char *tmp2 = NULL; args = ast_strdupa(data); u = ast_module_user_add(chan); if (speech == NULL) { ast_module_user_remove(u); return -1; } /* If channel is not already answered, then answer it */ if (chan->_state != AST_STATE_UP && ast_answer(chan)) { ast_module_user_remove(u); return -1; } /* Record old read format */ oldreadformat = chan->readformat; /* Change read format to be signed linear */ if (ast_set_read_format(chan, AST_FORMAT_SLINEAR)) { ast_module_user_remove(u); return -1; } /* Parse out options */ argc = ast_app_separate_args(args, '|', argv, sizeof(argv) / sizeof(argv[0])); if (argc > 0) { /* Yay sound file */ filename_tmp = ast_strdupa(argv[0]); if (!ast_strlen_zero(argv[1])) { if ((timeout = atoi(argv[1])) == 0) timeout = -1; } else timeout = 0; } /* See if the maximum DTMF length variable is set... we use a variable in case they want to carry it through their entire dialplan */ if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_MAXLEN")) && !ast_strlen_zero(tmp2)) max_dtmf_len = atoi(tmp2); /* See if a terminator is specified */ if ((tmp2 = pbx_builtin_getvar_helper(chan, "SPEECH_DTMF_TERMINATOR"))) { if (ast_strlen_zero(tmp2)) dtmf_terminator = '\0'; else dtmf_terminator = tmp2[0]; } /* Before we go into waiting for stuff... make sure the structure is ready, if not - start it again */ if (speech->state == AST_SPEECH_STATE_NOT_READY || speech->state == AST_SPEECH_STATE_DONE) { ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY); ast_speech_start(speech); } /* Ensure no streams are currently running */ ast_stopstream(chan); /* Okay it's streaming so go into a loop grabbing frames! */ while (done == 0) { /* If the filename is null and stream is not running, start up a new sound file */ if (!quieted && (chan->streamid == -1 && chan->timingfunc == NULL) && (filename = strsep(&filename_tmp, "&"))) { /* Discard old stream information */ ast_stopstream(chan); /* Start new stream */ speech_streamfile(chan, filename, chan->language); } /* Run scheduled stuff */ ast_sched_runq(chan->sched); /* Yay scheduling */ res = ast_sched_wait(chan->sched); if (res < 0) { res = 1000; } /* If there is a frame waiting, get it - if not - oh well */ if (ast_waitfor(chan, res) > 0) { f = ast_read(chan); if (f == NULL) { /* The channel has hung up most likely */ done = 3; break; } } /* Do timeout check (shared between audio/dtmf) */ if ((!quieted || strlen(dtmf)) && started == 1) { time(¤t); if ((current-start) >= timeout) { done = 1; if (f) ast_frfree(f); break; } } /* Do checks on speech structure to see if it's changed */ ast_mutex_lock(&speech->lock); if (ast_test_flag(speech, AST_SPEECH_QUIET)) { if (chan->stream) ast_stopstream(chan); ast_clear_flag(speech, AST_SPEECH_QUIET); quieted = 1; } /* Check state so we can see what to do */ switch (speech->state) { case AST_SPEECH_STATE_READY: /* If audio playback has stopped do a check for timeout purposes */ if (chan->streamid == -1 && chan->timingfunc == NULL) ast_stopstream(chan); if (!quieted && chan->stream == NULL && timeout && started == 0 && !filename_tmp) { if (timeout == -1) { done = 1; if (f) ast_frfree(f); break; } time(&start); started = 1; } /* Write audio frame out to speech engine if no DTMF has been received */ if (!strlen(dtmf) && f != NULL && f->frametype == AST_FRAME_VOICE) { ast_speech_write(speech, f->data, f->datalen); } break; case AST_SPEECH_STATE_WAIT: /* Cue up waiting sound if not already playing */ if (!strlen(dtmf)) { if (chan->stream == NULL) { if (speech->processing_sound != NULL) { if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) { speech_streamfile(chan, speech->processing_sound, chan->language); } } } else if (chan->streamid == -1 && chan->timingfunc == NULL) { ast_stopstream(chan); if (speech->processing_sound != NULL) { if (strlen(speech->processing_sound) > 0 && strcasecmp(speech->processing_sound,"none")) { speech_streamfile(chan, speech->processing_sound, chan->language); } } } } break; case AST_SPEECH_STATE_DONE: /* Now that we are done... let's switch back to not ready state */ ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY); if (!strlen(dtmf)) { /* Copy to speech structure the results, if available */ speech->results = ast_speech_results_get(speech); /* Break out of our background too */ done = 1; /* Stop audio playback */ if (chan->stream != NULL) { ast_stopstream(chan); } } break; default: break; } ast_mutex_unlock(&speech->lock); /* Deal with other frame types */ if (f != NULL) { /* Free the frame we received */ switch (f->frametype) { case AST_FRAME_DTMF: if (dtmf_terminator != '\0' && f->subclass == dtmf_terminator) { done = 1; } else { if (chan->stream != NULL) { ast_stopstream(chan); } if (!started) { /* Change timeout to be 5 seconds for DTMF input */ timeout = (chan->pbx && chan->pbx->dtimeout) ? chan->pbx->dtimeout : 5; started = 1; } time(&start); snprintf(tmp, sizeof(tmp), "%c", f->subclass); strncat(dtmf, tmp, sizeof(dtmf) - strlen(dtmf) - 1); /* If the maximum length of the DTMF has been reached, stop now */ if (max_dtmf_len && strlen(dtmf) == max_dtmf_len) done = 1; } break; case AST_FRAME_CONTROL: switch (f->subclass) { case AST_CONTROL_HANGUP: /* Since they hung up we should destroy the speech structure */ done = 3; default: break; } default: break; } ast_frfree(f); f = NULL; } } if (strlen(dtmf)) { /* We sort of make a results entry */ speech->results = ast_calloc(1, sizeof(*speech->results)); if (speech->results != NULL) { ast_speech_dtmf(speech, dtmf); speech->results->score = 1000; speech->results->text = strdup(dtmf); speech->results->grammar = strdup("dtmf"); } ast_speech_change_state(speech, AST_SPEECH_STATE_NOT_READY); } /* See if it was because they hung up */ if (done == 3) { /* Destroy speech structure */ ast_speech_destroy(speech); datastore = ast_channel_datastore_find(chan, &speech_datastore, NULL); if (datastore != NULL) { ast_channel_datastore_remove(chan, datastore); } } else { /* Channel is okay so restore read format */ ast_set_read_format(chan, oldreadformat); } ast_module_user_remove(u); return 0; }
/*! * \brief Get the lua_State for this channel * * If no channel is passed then a new state is allocated. States with no * channel assocatied with them should only be used for matching extensions. * If the channel does not yet have a lua state associated with it, one will be * created. * * \note If no channel was passed then the caller is expected to free the state * using lua_close(). * * \return a lua_State */ static lua_State *lua_get_state(struct ast_channel *chan) { struct ast_datastore *datastore = NULL; lua_State *L; if (!chan) { L = luaL_newstate(); if (!L) { ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n"); return NULL; } if (lua_load_extensions(L, NULL)) { const char *error = lua_tostring(L, -1); ast_log(LOG_ERROR, "Error loading extensions.lua: %s\n", error); lua_close(L); return NULL; } return L; } else { ast_channel_lock(chan); datastore = ast_channel_datastore_find(chan, &lua_datastore, NULL); ast_channel_unlock(chan); if (!datastore) { /* nothing found, allocate a new lua state */ datastore = ast_datastore_alloc(&lua_datastore, NULL); if (!datastore) { ast_log(LOG_ERROR, "Error allocation channel datastore for lua_State\n"); return NULL; } datastore->data = luaL_newstate(); if (!datastore->data) { ast_datastore_free(datastore); ast_log(LOG_ERROR, "Error allocating lua_State, no memory\n"); return NULL; } ast_channel_lock(chan); ast_channel_datastore_add(chan, datastore); ast_channel_unlock(chan); L = datastore->data; if (lua_load_extensions(L, chan)) { const char *error = lua_tostring(L, -1); ast_log(LOG_ERROR, "Error loading extensions.lua for %s: %s\n", ast_channel_name(chan), error); ast_channel_lock(chan); ast_channel_datastore_remove(chan, datastore); ast_channel_unlock(chan); ast_datastore_free(datastore); return NULL; } } return datastore->data; } }
int ast_do_pickup(struct ast_channel *chan, struct ast_channel *target) { struct ast_party_connected_line connected_caller; struct ast_datastore *ds_pickup; const char *chan_name;/*!< A masquerade changes channel names. */ const char *target_name;/*!< A masquerade changes channel names. */ int res = -1; RAII_VAR(struct ast_channel_snapshot *, chan_snapshot, NULL, ao2_cleanup); RAII_VAR(struct ast_channel_snapshot *, target_snapshot, NULL, ao2_cleanup); target_name = ast_strdupa(ast_channel_name(target)); ast_debug(1, "Call pickup on '%s' by '%s'\n", target_name, ast_channel_name(chan)); /* Mark the target to block any call pickup race. */ ds_pickup = ast_datastore_alloc(&pickup_active, NULL); if (!ds_pickup) { ast_log(LOG_WARNING, "Unable to create channel datastore on '%s' for call pickup\n", target_name); return -1; } ast_channel_datastore_add(target, ds_pickup); ast_party_connected_line_init(&connected_caller); ast_party_connected_line_copy(&connected_caller, ast_channel_connected(target)); ast_channel_unlock(target);/* The pickup race is avoided so we do not need the lock anymore. */ /* Reset any earlier private connected id representation */ ast_party_id_reset(&connected_caller.priv); connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; if (ast_channel_connected_line_sub(NULL, chan, &connected_caller, 0) && ast_channel_connected_line_macro(NULL, chan, &connected_caller, 0, 0)) { ast_channel_update_connected_line(chan, &connected_caller, NULL); } ast_party_connected_line_free(&connected_caller); ast_channel_lock(chan); chan_name = ast_strdupa(ast_channel_name(chan)); ast_connected_line_copy_from_caller(&connected_caller, ast_channel_caller(chan)); ast_channel_unlock(chan); connected_caller.source = AST_CONNECTED_LINE_UPDATE_SOURCE_ANSWER; if (ast_answer(chan)) { ast_log(LOG_WARNING, "Unable to answer '%s'\n", chan_name); goto pickup_failed; } if (ast_queue_control(chan, AST_CONTROL_ANSWER)) { ast_log(LOG_WARNING, "Unable to queue answer on '%s'\n", chan_name); goto pickup_failed; } ast_channel_queue_connected_line_update(chan, &connected_caller, NULL); /* setting the HANGUPCAUSE so the ringing channel knows this call was not a missed call */ ast_channel_hangupcause_set(chan, AST_CAUSE_ANSWERED_ELSEWHERE); ast_channel_lock(chan); chan_snapshot = ast_channel_snapshot_create(chan); ast_channel_unlock(chan); if (!chan_snapshot) { goto pickup_failed; } target_snapshot = ast_channel_snapshot_get_latest(ast_channel_uniqueid(target)); if (!target_snapshot) { goto pickup_failed; } if (ast_channel_move(target, chan)) { ast_log(LOG_WARNING, "Unable to complete call pickup of '%s' with '%s'\n", chan_name, target_name); goto pickup_failed; } /* target points to the channel that did the pickup at this point, so use that channel's topic instead of chan */ send_call_pickup_stasis_message(target, chan_snapshot, target_snapshot); res = 0; pickup_failed: ast_channel_lock(target); if (!ast_channel_datastore_remove(target, ds_pickup)) { ast_datastore_free(ds_pickup); } ast_party_connected_line_free(&connected_caller); return res; }
void ast_jb_create_framehook(struct ast_channel *chan, struct ast_jb_conf *jb_conf, int prefer_existing) { struct jb_framedata *framedata; struct ast_datastore *datastore = NULL; struct ast_framehook_interface interface = { .version = AST_FRAMEHOOK_INTERFACE_VERSION, .event_cb = hook_event_cb, .destroy_cb = hook_destroy_cb, }; int i = 0; /* If disabled, strip any existing jitterbuffer and don't replace it. */ if (!strcasecmp(jb_conf->impl, "disabled")) { int *id; ast_channel_lock(chan); if ((datastore = ast_channel_datastore_find(chan, &jb_datastore, NULL))) { id = datastore->data; ast_framehook_detach(chan, *id); ast_channel_datastore_remove(chan, datastore); ast_datastore_free(datastore); } ast_channel_unlock(chan); return; } if (!(framedata = ast_calloc(1, sizeof(*framedata)))) { return; } if (jb_framedata_init(framedata, jb_conf)) { jb_framedata_destroy(framedata); return; } interface.data = framedata; ast_channel_lock(chan); i = ast_framehook_attach(chan, &interface); if (i >= 0) { int *id; if ((datastore = ast_channel_datastore_find(chan, &jb_datastore, NULL))) { /* There is already a jitterbuffer on the channel. */ if (prefer_existing) { /* We prefer the existing jitterbuffer, so remove the new one and keep the old one. */ ast_framehook_detach(chan, i); ast_channel_unlock(chan); return; } /* We prefer the new jitterbuffer, so strip the old one. */ id = datastore->data; ast_framehook_detach(chan, *id); ast_channel_datastore_remove(chan, datastore); ast_datastore_free(datastore); } if (!(datastore = ast_datastore_alloc(&jb_datastore, NULL))) { ast_framehook_detach(chan, i); ast_channel_unlock(chan); return; } if (!(id = ast_calloc(1, sizeof(int)))) { ast_datastore_free(datastore); ast_framehook_detach(chan, i); ast_channel_unlock(chan); return; } *id = i; /* Store off the id. The channel is still locked so it is safe to access this ptr. */ datastore->data = id; ast_channel_datastore_add(chan, datastore); ast_channel_set_fd(chan, AST_JITTERBUFFER_FD, framedata->timer_fd); } else { jb_framedata_destroy(framedata); framedata = NULL; } ast_channel_unlock(chan); }