static int app_control_remove_channel_from_bridge( struct stasis_app_control *control, struct ast_channel *chan, void *data) { struct ast_bridge *bridge = data; if (!control) { return -1; } /* We should only depart from our own bridge */ ast_debug(3, "%s: Departing bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); if (bridge != stasis_app_get_bridge(control)) { ast_log(LOG_WARNING, "%s: Not in bridge %s; not removing\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); return -1; } ast_bridge_depart(chan); return 0; }
void ast_ari_bridges_set_video_source(struct ast_variable *headers, struct ast_ari_bridges_set_video_source_args *args, struct ast_ari_response *response) { struct ast_bridge *bridge; struct stasis_app_control *control; bridge = find_bridge(response, args->bridge_id); if (!bridge) { return; } control = find_channel_control(response, args->channel_id); if (!control) { ao2_ref(bridge, -1); return; } if (stasis_app_get_bridge(control) != bridge) { ast_ari_response_error(response, 422, "Unprocessable Entity", "Channel not in this bridge"); ao2_ref(bridge, -1); ao2_ref(control, -1); return; } stasis_app_send_command(control, bridge_set_video_source_cb, ao2_bump(bridge), __ao2_cleanup); ao2_ref(bridge, -1); ao2_ref(control, -1); ast_ari_response_no_content(response); }
static int app_control_continue(struct stasis_app_control *control, struct ast_channel *chan, void *data) { RAII_VAR(struct stasis_app_control_continue_data *, continue_data, data, ast_free); ast_assert(control->channel != NULL); /* If we're in a Stasis bridge, depart it before going back to the * dialplan */ if (stasis_app_get_bridge(control)) { ast_bridge_depart(control->channel); } /* Called from stasis_app_exec thread; no lock needed */ ast_explicit_goto(control->channel, continue_data->context, continue_data->extension, continue_data->priority); control->is_done = 1; return 0; }
void ast_ari_bridges_remove_channel(struct ast_variable *headers, struct ast_ari_bridges_remove_channel_args *args, struct ast_ari_response *response) { RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup); RAII_VAR(struct control_list *, list, NULL, ao2_cleanup); size_t i; if (!bridge) { /* Response filled in by find_bridge() */ return; } list = control_list_create(response, args->channel_count, args->channel); if (!list) { /* Response filled in by control_list_create() */ return; } /* Make sure all of the channels are in this bridge */ for (i = 0; i < list->count; ++i) { if (stasis_app_get_bridge(list->controls[i]) != bridge) { ast_log(LOG_WARNING, "Channel %s not in bridge %s\n", args->channel[i], args->bridge_id); ast_ari_response_error(response, 422, "Unprocessable Entity", "Channel not in this bridge"); return; } } /* Now actually remove it */ for (i = 0; i < list->count; ++i) { stasis_app_control_remove_channel_from_bridge(list->controls[i], bridge); } ast_ari_response_no_content(response); }
static int record_file(struct stasis_app_control *control, struct ast_channel *chan, void *data) { struct stasis_app_recording *recording = data; char *acceptdtmf; int res; ast_assert(recording != NULL); if (stasis_app_get_bridge(control)) { ast_log(LOG_ERROR, "Cannot record channel while in bridge\n"); recording_fail(control, recording, "Cannot record channel while in bridge"); return -1; } switch (recording->options->terminate_on) { case STASIS_APP_RECORDING_TERMINATE_NONE: case STASIS_APP_RECORDING_TERMINATE_INVALID: acceptdtmf = ""; break; case STASIS_APP_RECORDING_TERMINATE_ANY: acceptdtmf = "#*0123456789abcd"; break; default: acceptdtmf = ast_alloca(2); acceptdtmf[0] = recording->options->terminate_on; acceptdtmf[1] = '\0'; } res = ast_auto_answer(chan); if (res != 0) { ast_debug(3, "%s: Failed to answer\n", ast_channel_uniqueid(chan)); recording_fail(control, recording, "Failed to answer channel"); return -1; } recording_set_state( recording, STASIS_APP_RECORDING_STATE_RECORDING, NULL); ast_play_and_record_full(chan, NULL, /* playfile */ recording->absolute_name, recording->options->max_duration_seconds, recording->options->format, &recording->duration.total, recording->options->max_silence_seconds ? &recording->duration.energy_only : NULL, recording->options->beep, -1, /* silencethreshold */ recording->options->max_silence_seconds * 1000, NULL, /* path */ acceptdtmf, NULL, /* canceldtmf */ 1, /* skip_confirmation_sound */ recording->options->if_exists); ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan)); recording_set_state( recording, STASIS_APP_RECORDING_STATE_COMPLETE, NULL); stasis_app_control_unregister_add_rule(control, &rule_recording); return 0; }
static void *record_file(struct stasis_app_control *control, struct ast_channel *chan, void *data) { RAII_VAR(struct stasis_app_recording *, recording, NULL, recording_cleanup); char *acceptdtmf; int res; int duration = 0; recording = data; ast_assert(recording != NULL); if (stasis_app_get_bridge(control)) { ast_log(LOG_ERROR, "Cannot record channel while in bridge\n"); recording_fail(recording); return NULL; } switch (recording->options->terminate_on) { case STASIS_APP_RECORDING_TERMINATE_NONE: case STASIS_APP_RECORDING_TERMINATE_INVALID: acceptdtmf = ""; break; case STASIS_APP_RECORDING_TERMINATE_ANY: acceptdtmf = "#*0123456789abcd"; break; default: acceptdtmf = ast_alloca(2); acceptdtmf[0] = recording->options->terminate_on; acceptdtmf[1] = '\0'; } res = ast_auto_answer(chan); if (res != 0) { ast_debug(3, "%s: Failed to answer\n", ast_channel_uniqueid(chan)); recording_fail(recording); return NULL; } ao2_lock(recording); recording->state = STASIS_APP_RECORDING_STATE_RECORDING; recording_publish(recording); ao2_unlock(recording); ast_play_and_record_full(chan, NULL, /* playfile */ recording->absolute_name, recording->options->max_duration_seconds, recording->options->format, &duration, NULL, /* sound_duration */ recording->options->beep, -1, /* silencethreshold */ recording->options->max_silence_seconds * 1000, NULL, /* path */ acceptdtmf, NULL, /* canceldtmf */ 1, /* skip_confirmation_sound */ recording->options->if_exists); ast_debug(3, "%s: Recording complete\n", ast_channel_uniqueid(chan)); ao2_lock(recording); recording->state = STASIS_APP_RECORDING_STATE_COMPLETE; recording_publish(recording); ao2_unlock(recording); return NULL; }
static int app_control_add_channel_to_bridge( struct stasis_app_control *control, struct ast_channel *chan, void *data) { struct ast_bridge *bridge = data; int res; if (!control || !bridge) { return -1; } ast_debug(3, "%s: Adding to bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); ast_assert(chan != NULL); /* Depart whatever Stasis bridge we're currently in. */ if (stasis_app_get_bridge(control)) { /* Note that it looks like there's a race condition here, since * we don't have control locked. But this happens from the * control callback thread, so there won't be any other * concurrent attempts to bridge. */ ast_bridge_depart(chan); } res = ast_bridge_set_after_callback(chan, bridge_after_cb, bridge_after_cb_failed, control); if (res != 0) { ast_log(LOG_ERROR, "Error setting after-bridge callback\n"); return -1; } { /* pbx and bridge are modified by the bridging impart thread. * It shouldn't happen concurrently, but we still need to lock * for the memory fence. */ SCOPED_AO2LOCK(lock, control); /* Save off the channel's PBX */ ast_assert(control->pbx == NULL); if (!control->pbx) { control->pbx = ast_channel_pbx(chan); ast_channel_pbx_set(chan, NULL); } res = ast_bridge_impart(bridge, chan, NULL, /* swap channel */ NULL, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; return -1; } ast_assert(stasis_app_get_bridge(control) == NULL); control->bridge = bridge; } return 0; }
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap) { int res; if (!control || !bridge) { return -1; } ast_debug(3, "%s: Adding to bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); ast_assert(chan != NULL); /* Depart whatever Stasis bridge we're currently in. */ if (stasis_app_get_bridge(control)) { /* Note that it looks like there's a race condition here, since * we don't have control locked. But this happens from the * control callback thread, so there won't be any other * concurrent attempts to bridge. */ ast_bridge_depart(chan); } res = ast_bridge_set_after_callback(chan, bridge_after_cb, bridge_after_cb_failed, control); if (res != 0) { ast_log(LOG_ERROR, "Error setting after-bridge callback\n"); return -1; } { /* pbx and bridge are modified by the bridging impart thread. * It shouldn't happen concurrently, but we still need to lock * for the memory fence. */ SCOPED_AO2LOCK(lock, control); /* Ensure the controlling application is subscribed early enough * to receive the ChannelEnteredBridge message. This works in concert * with the subscription handled in the Stasis application execution * loop */ app_subscribe_bridge(control->app, bridge); /* Save off the channel's PBX */ ast_assert(control->pbx == NULL); if (!control->pbx) { control->pbx = ast_channel_pbx(chan); ast_channel_pbx_set(chan, NULL); } res = ast_bridge_impart(bridge, chan, swap, NULL, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; return -1; } ast_assert(stasis_app_get_bridge(control) == NULL); control->bridge = bridge; ast_channel_lock(chan); set_interval_hook(chan); ast_channel_unlock(chan); } return 0; }
int control_swap_channel_in_bridge(struct stasis_app_control *control, struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap) { int res; struct ast_bridge_features *features; if (!control || !bridge) { return -1; } ast_debug(3, "%s: Adding to bridge %s\n", stasis_app_control_get_channel_id(control), bridge->uniqueid); ast_assert(chan != NULL); /* Depart whatever Stasis bridge we're currently in. */ if (stasis_app_get_bridge(control)) { /* Note that it looks like there's a race condition here, since * we don't have control locked. But this happens from the * control callback thread, so there won't be any other * concurrent attempts to bridge. */ ast_bridge_depart(chan); } res = ast_bridge_set_after_callback(chan, bridge_after_cb, bridge_after_cb_failed, control); if (res != 0) { ast_log(LOG_ERROR, "Error setting after-bridge callback\n"); return -1; } ao2_lock(control); /* Ensure the controlling application is subscribed early enough * to receive the ChannelEnteredBridge message. This works in concert * with the subscription handled in the Stasis application execution * loop */ app_subscribe_bridge(control->app, bridge); /* Save off the channel's PBX */ ast_assert(control->pbx == NULL); if (!control->pbx) { control->pbx = ast_channel_pbx(chan); ast_channel_pbx_set(chan, NULL); } /* Pull bridge features from the control */ features = control->bridge_features; control->bridge_features = NULL; ast_assert(stasis_app_get_bridge(control) == NULL); /* We need to set control->bridge here since bridge_after_cb may be run * before ast_bridge_impart returns. bridge_after_cb gets a reason * code so it can tell if the bridge is actually valid or not. */ control->bridge = bridge; /* We can't be holding the control lock while impart is running * or we could create a deadlock with bridge_after_cb which also * tries to lock control. */ ao2_unlock(control); res = ast_bridge_impart(bridge, chan, swap, features, /* features */ AST_BRIDGE_IMPART_CHAN_DEPARTABLE); if (res != 0) { /* ast_bridge_impart failed before it could spawn the depart * thread. The callbacks aren't called in this case. * The impart could still fail even if ast_bridge_impart returned * ok but that's handled by bridge_after_cb. */ ast_log(LOG_ERROR, "Error adding channel to bridge\n"); ao2_lock(control); ast_channel_pbx_set(chan, control->pbx); control->pbx = NULL; control->bridge = NULL; ao2_unlock(control); } else { ast_channel_lock(chan); set_interval_hook(chan); ast_channel_unlock(chan); } return res; }