Conference Bridge application. More...
#include "asterisk.h"#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <string.h>#include <signal.h>#include "asterisk/file.h"#include "asterisk/logger.h"#include "asterisk/channel.h"#include "asterisk/pbx.h"#include "asterisk/module.h"#include "asterisk/lock.h"#include "asterisk/app.h"#include "asterisk/bridging.h"#include "asterisk/musiconhold.h"#include "asterisk/say.h"#include "asterisk/audiohook.h"#include "asterisk/astobj2.h"
Go to the source code of this file.
Data Structures | |
| struct | conference_bridge |
| The structure that represents a conference bridge. More... | |
| struct | conference_bridge_user |
| The structure that represents a conference bridge user. More... | |
Defines | |
| #define | CONFERENCE_BRIDGE_BUCKETS 53 |
| #define | MAX_CONF_NAME 32 |
Enumerations | |
| enum | { OPTION_ADMIN = (1 << 0), OPTION_MENU = (1 << 1), OPTION_MUSICONHOLD = (1 << 2), OPTION_NOONLYPERSON = (1 << 3), OPTION_STARTMUTED = (1 << 4), OPTION_ANNOUNCEUSERCOUNT = (1 << 5), OPTION_MARKEDUSER = (1 << 6), OPTION_WAITMARKED = (1 << 7), OPTION_QUIET = (1 << 8) } |
| enum | { OPTION_MUSICONHOLD_CLASS, OPTION_ARRAY_SIZE } |
Functions | |
| static void | __reg_module (void) |
| static void | __unreg_module (void) |
| static int | announce_user_count (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) |
| Announce number of users in the conference bridge to the caller. | |
| static int | confbridge_exec (struct ast_channel *chan, const char *data) |
| The ConfBridge application. | |
| static int | conference_bridge_cmp_cb (void *obj, void *arg, int flags) |
| Comparison function used for conference bridges container. | |
| static int | conference_bridge_hash_cb (const void *obj, const int flags) |
| Hashing function used for conference bridges container. | |
| static void | destroy_conference_bridge (void *obj) |
| Destroy a conference bridge. | |
| static struct conference_bridge * | join_conference_bridge (const char *name, struct conference_bridge_user *conference_bridge_user) |
| Join a conference bridge. | |
| static void | leave_conference_bridge (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) |
| Leave a conference bridge. | |
| static int | load_module (void) |
| Called when module is being loaded. | |
| static int | menu_callback (struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt) |
| DTMF Menu Callback. | |
| static int | play_prompt_to_channel (struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file) |
| Play back an audio file to a channel. | |
| static int | play_sound_file (struct conference_bridge *conference_bridge, const char *filename) |
| Play sound file into conference bridge. | |
| static int | post_join_marked (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) |
| Perform post-joining marked specific actions. | |
| static int | post_join_unmarked (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) |
| Perform post-joining non-marked specific actions. | |
| static int | unload_module (void) |
| Called when module is being unloaded. | |
Variables | |
| static struct ast_module_info | __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Conference Bridge 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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, } |
| static const char | app [] = "ConfBridge" |
| static struct ast_app_option | app_opts [128] = { [ 'A' ] = { .flag = OPTION_MARKEDUSER }, [ 'a' ] = { .flag = OPTION_ADMIN }, [ 'c' ] = { .flag = OPTION_ANNOUNCEUSERCOUNT }, [ 'm' ] = { .flag = OPTION_STARTMUTED }, [ 'M' ] = { .flag = OPTION_MUSICONHOLD , .arg_index = OPTION_MUSICONHOLD_CLASS + 1 }, [ '1' ] = { .flag = OPTION_NOONLYPERSON }, [ 's' ] = { .flag = OPTION_MENU }, [ 'w' ] = { .flag = OPTION_WAITMARKED }, [ 'q' ] = { .flag = OPTION_QUIET },} |
| static struct ast_module_info * | ast_module_info = &__mod_info |
| static struct ao2_container * | conference_bridges |
| Container to hold all conference bridges in progress. | |
Conference Bridge application.
This is a conference bridge application utilizing the bridging core.
Definition in file app_confbridge.c.
| #define CONFERENCE_BRIDGE_BUCKETS 53 |
Definition at line 155 of file app_confbridge.c.
Referenced by load_module().
| #define MAX_CONF_NAME 32 |
Definition at line 152 of file app_confbridge.c.
| anonymous enum |
Definition at line 121 of file app_confbridge.c.
{
OPTION_ADMIN = (1 << 0), /*!< Set if the caller is an administrator */
OPTION_MENU = (1 << 1), /*!< Set if the caller should have access to the conference bridge IVR menu */
OPTION_MUSICONHOLD = (1 << 2), /*!< Set if music on hold should be played if nobody else is in the conference bridge */
OPTION_NOONLYPERSON = (1 << 3), /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
OPTION_STARTMUTED = (1 << 4), /*!< Set if the caller should be initially set muted */
OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */
OPTION_MARKEDUSER = (1 << 6), /*!< Set if the caller is a marked user */
OPTION_WAITMARKED = (1 << 7), /*!< Set if the conference must wait for a marked user before starting */
OPTION_QUIET = (1 << 8), /*!< Set if no audio prompts should be played */
};
| anonymous enum |
| OPTION_MUSICONHOLD_CLASS |
If the 'M' option is set, the music on hold class to play |
| OPTION_ARRAY_SIZE |
Definition at line 133 of file app_confbridge.c.
{
OPTION_MUSICONHOLD_CLASS, /*!< If the 'M' option is set, the music on hold class to play */
/*This must be the last element */
OPTION_ARRAY_SIZE,
};
| static void __reg_module | ( | void | ) | [static] |
Definition at line 854 of file app_confbridge.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 854 of file app_confbridge.c.
| static int announce_user_count | ( | struct conference_bridge * | conference_bridge, |
| struct conference_bridge_user * | conference_bridge_user | ||
| ) | [static] |
Announce number of users in the conference bridge to the caller.
| conference_bridge | Conference bridge to peek at |
| conference_bridge_user | Caller |
Definition at line 207 of file app_confbridge.c.
References ast_say_number(), ast_stream_and_wait(), conference_bridge_user::chan, ast_channel::language, and conference_bridge::users.
Referenced by post_join_unmarked().
{
if (conference_bridge->users == 1) {
/* Awww we are the only person in the conference bridge */
return 0;
} else if (conference_bridge->users == 2) {
/* Eep, there is one other person */
if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
return -1;
}
} else {
/* Alas multiple others in here */
if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
return -1;
}
if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
return -1;
}
if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
return -1;
}
}
return 0;
}
| static int confbridge_exec | ( | struct ast_channel * | chan, |
| const char * | data | ||
| ) | [static] |
The ConfBridge application.
Definition at line 723 of file app_confbridge.c.
References app_opts, args, AST_APP_ARG, ast_app_parse_options(), AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_volume_get(), ast_audiohook_volume_set(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_features_cleanup(), ast_bridge_features_hook(), ast_bridge_features_init(), ast_bridge_join(), ast_channel_lock, ast_channel_unlock, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::features, conference_bridge_user::flags, join_conference_bridge(), conference_bridge_user::kicked, leave_conference_bridge(), LOG_WARNING, menu_callback(), ast_bridge_features::mute, conference_bridge_user::opt_args, OPTION_MENU, OPTION_QUIET, OPTION_STARTMUTED, parse(), pbx_builtin_getvar_helper(), play_sound_file(), and conference_bridge::users.
Referenced by load_module().
{
int res = 0, volume_adjustments[2];
char *parse;
struct conference_bridge *conference_bridge = NULL;
struct conference_bridge_user conference_bridge_user = {
.chan = chan,
};
const char *tmp, *join_sound = NULL, *leave_sound = NULL;
AST_DECLARE_APP_ARGS(args,
AST_APP_ARG(conf_name);
AST_APP_ARG(options);
);
if (ast_strlen_zero(data)) {
ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
return -1;
}
/* We need to make a copy of the input string if we are going to modify it! */
parse = ast_strdupa(data);
AST_STANDARD_APP_ARGS(args, parse);
if (args.argc == 2) {
ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
}
/* Look for a conference bridge matching the provided name */
if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
return -1;
}
/* Keep a copy of volume adjustments so we can restore them later if need be */
volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
/* Always initialize the features structure, we are in most cases always going to need it. */
ast_bridge_features_init(&conference_bridge_user.features);
/* If the menu option is enabled provide a user or admin menu as a custom feature hook */
if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
}
/* If the caller should be joined already muted, make it so */
if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
conference_bridge_user.features.mute = 1;
}
/* Grab join/leave sounds from the channel */
ast_channel_lock(chan);
if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
join_sound = ast_strdupa(tmp);
}
if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
leave_sound = ast_strdupa(tmp);
}
ast_channel_unlock(chan);
/* If there is 1 or more people already in the conference then play our join sound unless overridden */
if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
ast_autoservice_start(chan);
play_sound_file(conference_bridge, join_sound);
ast_autoservice_stop(chan);
}
/* Join our conference bridge for real */
ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
/* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */
if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
ast_autoservice_start(chan);
play_sound_file(conference_bridge, leave_sound);
ast_autoservice_stop(chan);
}
/* Easy as pie, depart this channel from the conference bridge */
leave_conference_bridge(conference_bridge, &conference_bridge_user);
conference_bridge = NULL;
/* Can't forget to clean up the features structure, or else we risk a memory leak */
ast_bridge_features_cleanup(&conference_bridge_user.features);
/* If the user was kicked from the conference play back the audio prompt for it */
if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
res = ast_stream_and_wait(chan, "conf-kicked", "");
}
/* Restore volume adjustments to previous values in case they were changed */
if (volume_adjustments[0]) {
ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
}
if (volume_adjustments[1]) {
ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
}
return res;
}
| static int conference_bridge_cmp_cb | ( | void * | obj, |
| void * | arg, | ||
| int | flags | ||
| ) | [static] |
Comparison function used for conference bridges container.
Definition at line 193 of file app_confbridge.c.
References CMP_MATCH, CMP_STOP, and conference_bridge::name.
Referenced by load_module().
{
const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
}
| static int conference_bridge_hash_cb | ( | const void * | obj, |
| const int | flags | ||
| ) | [static] |
Hashing function used for conference bridges container.
Definition at line 186 of file app_confbridge.c.
References ast_str_case_hash(), and conference_bridge::name.
Referenced by load_module().
{
const struct conference_bridge *conference_bridge = obj;
return ast_str_case_hash(conference_bridge->name);
}
| static void destroy_conference_bridge | ( | void * | obj | ) | [static] |
Destroy a conference bridge.
| obj | The conference bridge object |
Definition at line 382 of file app_confbridge.c.
References ast_bridge_destroy(), ast_debug, ast_hangup(), ast_mutex_destroy, conference_bridge::bridge, ast_channel_tech::bridged_channel, conference_bridge::name, conference_bridge::playback_chan, conference_bridge::playback_lock, and ast_channel::tech.
Referenced by join_conference_bridge().
{
struct conference_bridge *conference_bridge = obj;
ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
ast_mutex_destroy(&conference_bridge->playback_lock);
if (conference_bridge->playback_chan) {
struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
ast_hangup(underlying_channel);
ast_hangup(conference_bridge->playback_chan);
conference_bridge->playback_chan = NULL;
}
/* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
if (conference_bridge->bridge) {
ast_bridge_destroy(conference_bridge->bridge);
conference_bridge->bridge = NULL;
}
}
| static struct conference_bridge* join_conference_bridge | ( | const char * | name, |
| struct conference_bridge_user * | conference_bridge_user | ||
| ) | [static, read] |
Join a conference bridge.
| name | The conference name |
| conference_bridge_user | Conference bridge user structure |
Definition at line 414 of file app_confbridge.c.
References ao2_alloc, ao2_find, ao2_link, ao2_lock, ao2_ref, ao2_unlock, AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART, ast_bridge_new(), ast_copy_string(), ast_debug, AST_DEVICE_INUSE, ast_devstate_changed(), AST_LIST_INSERT_TAIL, ast_log(), ast_mutex_init, ast_stream_and_wait(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridges, destroy_conference_bridge(), conference_bridge_user::flags, leave_conference_bridge(), conference_bridge::locked, LOG_ERROR, conference_bridge::markedusers, conference_bridge::name, OBJ_POINTER, OPTION_ADMIN, OPTION_MARKEDUSER, OPTION_WAITMARKED, conference_bridge::playback_lock, post_join_marked(), post_join_unmarked(), conference_bridge::users, and conference_bridge::users_list.
Referenced by confbridge_exec().
{
struct conference_bridge *conference_bridge = NULL;
struct conference_bridge tmp;
ast_copy_string(tmp.name, name, sizeof(tmp.name));
/* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
ao2_lock(conference_bridges);
ast_debug(1, "Trying to find conference bridge '%s'\n", name);
/* Attempt to find an existing conference bridge */
conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
/* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
ao2_unlock(conference_bridges);
ao2_ref(conference_bridge, -1);
ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
return NULL;
}
/* If no conference bridge was found see if we can create one */
if (!conference_bridge) {
/* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
ao2_unlock(conference_bridges);
ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
return NULL;
}
/* Setup conference bridge parameters */
ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
/* Create an actual bridge that will do the audio mixing */
if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
ao2_ref(conference_bridge, -1);
conference_bridge = NULL;
ao2_unlock(conference_bridges);
ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
return NULL;
}
/* Setup lock for playback channel */
ast_mutex_init(&conference_bridge->playback_lock);
/* Link it into the conference bridges container */
ao2_link(conference_bridges, conference_bridge);
ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
}
ao2_unlock(conference_bridges);
/* Setup conference bridge user parameters */
conference_bridge_user->conference_bridge = conference_bridge;
ao2_lock(conference_bridge);
/* All good to go, add them in */
AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
/* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
conference_bridge->users++;
/* If the caller is a marked user bump up the count */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
conference_bridge->markedusers++;
}
/* Set the device state for this conference */
if (conference_bridge->users == 1) {
ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
}
/* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
if (post_join_marked(conference_bridge, conference_bridge_user)) {
ao2_unlock(conference_bridge);
leave_conference_bridge(conference_bridge, conference_bridge_user);
return NULL;
}
} else {
if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
ao2_unlock(conference_bridge);
leave_conference_bridge(conference_bridge, conference_bridge_user);
return NULL;
}
}
ao2_unlock(conference_bridge);
return conference_bridge;
}
| static void leave_conference_bridge | ( | struct conference_bridge * | conference_bridge, |
| struct conference_bridge_user * | conference_bridge_user | ||
| ) | [static] |
Leave a conference bridge.
| conference_bridge | The conference bridge to leave |
| conference_bridge_user | The conference bridge user structure |
Definition at line 518 of file app_confbridge.c.
References ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_suspend(), ast_bridge_unsuspend(), AST_DEVICE_NOT_INUSE, ast_devstate_changed(), AST_LIST_FIRST, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_moh_start(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridges, conference_bridge_user::features, conference_bridge_user::flags, conference_bridge_user::list, conference_bridge::markedusers, ast_bridge_features::mute, conference_bridge::name, conference_bridge_user::opt_args, OPTION_MARKEDUSER, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_QUIET, play_sound_file(), conference_bridge::users, and conference_bridge::users_list.
Referenced by confbridge_exec(), and join_conference_bridge().
{
ao2_lock(conference_bridge);
/* If this caller is a marked user bump down the count */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
conference_bridge->markedusers--;
}
/* Decrement the users count while keeping the previous participant count */
conference_bridge->users--;
/* Drop conference bridge user from the list, they be going bye bye */
AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
/* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
if (conference_bridge->users) {
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
struct conference_bridge_user *other_participant = NULL;
/* Start out with muting everyone */
AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
other_participant->features.mute = 1;
}
/* Play back the audio prompt saying the leader has left the conference */
if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
ao2_unlock(conference_bridge);
ast_autoservice_start(conference_bridge_user->chan);
play_sound_file(conference_bridge, "conf-leaderhasleft");
ast_autoservice_stop(conference_bridge_user->chan);
ao2_lock(conference_bridge);
}
/* Now on to starting MOH if needed */
AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
}
}
} else if (conference_bridge->users == 1) {
/* Of course if there is one other person in here we may need to start up MOH on them */
struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
}
}
} else {
/* Set device state to "not in use" */
ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
ao2_unlink(conference_bridges, conference_bridge);
}
/* Done mucking with the conference bridge, huzzah */
ao2_unlock(conference_bridge);
ao2_ref(conference_bridge, -1);
}
| static int load_module | ( | void | ) | [static] |
Called when module is being loaded.
Definition at line 835 of file app_confbridge.c.
References ao2_container_alloc, ao2_ref, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_register_application_xml, confbridge_exec(), CONFERENCE_BRIDGE_BUCKETS, conference_bridge_cmp_cb(), conference_bridge_hash_cb(), and conference_bridges.
{
/* Create a container to hold the conference bridges */
if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
return AST_MODULE_LOAD_DECLINE;
}
if (ast_register_application_xml(app, confbridge_exec)) {
ao2_ref(conference_bridges, -1);
return AST_MODULE_LOAD_DECLINE;
}
return AST_MODULE_LOAD_SUCCESS;
}
| static int menu_callback | ( | struct ast_bridge * | bridge, |
| struct ast_bridge_channel * | bridge_channel, | ||
| void * | hook_pvt | ||
| ) | [static] |
DTMF Menu Callback.
| bridge | Bridge this is involving |
| bridge_channel | Bridged channel this is involving |
| hook_pvt | User's conference bridge structure |
| 0 | success |
| -1 | failure |
Definition at line 643 of file app_confbridge.c.
References ao2_lock, ao2_unlock, AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_volume_adjust(), AST_BRIDGE_CHANNEL_STATE_WAIT, ast_bridge_remove(), AST_DIGIT_ANY, AST_LIST_LAST, ast_moh_start(), ast_moh_stop(), ast_stopstream(), ast_stream_and_wait(), ast_streamfile(), ast_test_flag, ast_waitstream(), conference_bridge::bridge, ast_bridge_channel::chan, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridge_user::features, conference_bridge_user::flags, conference_bridge_user::kicked, ast_channel::language, conference_bridge::locked, conference_bridge::markedusers, ast_bridge_features::mute, conference_bridge_user::opt_args, OPTION_ADMIN, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_WAITMARKED, ast_bridge_channel::state, conference_bridge::users, and conference_bridge::users_list.
Referenced by confbridge_exec().
{
struct conference_bridge_user *conference_bridge_user = hook_pvt;
struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
/* See if music on hold is playing */
ao2_lock(conference_bridge);
if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
/* Just us so MOH is probably indeed going, let's stop it */
ast_moh_stop(bridge_channel->chan);
}
ao2_unlock(conference_bridge);
/* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
res = -1;
goto finished;
}
/* Wait for them to enter a digit from the user menu options */
digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
ast_stopstream(bridge_channel->chan);
if (digit == '1') {
/* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
}
res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
} else if (isadmin && digit == '2') {
/* 2 - Unlock or lock conference */
conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
} else if (isadmin && digit == '3') {
/* 3 - Eject last user */
struct conference_bridge_user *last_participant = NULL;
ao2_lock(conference_bridge);
if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
ao2_unlock(conference_bridge);
res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
} else {
last_participant->kicked = 1;
ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
ao2_unlock(conference_bridge);
}
} else if (digit == '4') {
/* 4 - Decrease listening volume */
ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
} else if (digit == '6') {
/* 6 - Increase listening volume */
ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
} else if (digit == '7') {
/* 7 - Decrease talking volume */
ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
} else if (digit == '8') {
/* 8 - Exit the IVR */
} else if (digit == '9') {
/* 9 - Increase talking volume */
ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
} else {
/* No valid option was selected */
res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
}
finished:
/* See if music on hold needs to be started back up again */
ao2_lock(conference_bridge);
if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
}
ao2_unlock(conference_bridge);
bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
return res;
}
| static int play_prompt_to_channel | ( | struct conference_bridge * | conference_bridge, |
| struct ast_channel * | chan, | ||
| const char * | file | ||
| ) | [static] |
Play back an audio file to a channel.
| conference_bridge | Conference bridge they are in |
| chan | Channel to play audio prompt to |
| file | Prompt to play |
Definition at line 243 of file app_confbridge.c.
References ao2_lock, ao2_unlock, and ast_stream_and_wait().
Referenced by post_join_marked(), and post_join_unmarked().
{
int res;
ao2_unlock(conference_bridge);
res = ast_stream_and_wait(chan, file, "");
ao2_lock(conference_bridge);
return res;
}
| static int play_sound_file | ( | struct conference_bridge * | conference_bridge, |
| const char * | filename | ||
| ) | [static] |
Play sound file into conference bridge.
| conference_bridge | The conference bridge to play sound file into |
| filename | Sound file to play |
| 0 | success |
| -1 | failure |
Definition at line 590 of file app_confbridge.c.
References ast_bridge_depart(), ast_bridge_impart(), ast_call(), ast_debug, AST_FORMAT_SLINEAR, ast_hangup(), ast_mutex_lock, ast_mutex_unlock, ast_request(), ast_stream_and_wait(), conference_bridge::bridge, ast_channel::bridge, ast_channel_tech::bridged_channel, cause, conference_bridge::name, ast_channel::name, conference_bridge::playback_chan, conference_bridge::playback_lock, and ast_channel::tech.
Referenced by confbridge_exec(), leave_conference_bridge(), and post_join_marked().
{
struct ast_channel *underlying_channel;
ast_mutex_lock(&conference_bridge->playback_lock);
if (!(conference_bridge->playback_chan)) {
int cause;
if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) {
ast_mutex_unlock(&conference_bridge->playback_lock);
return -1;
}
conference_bridge->playback_chan->bridge = conference_bridge->bridge;
if (ast_call(conference_bridge->playback_chan, "", 0)) {
ast_hangup(conference_bridge->playback_chan);
conference_bridge->playback_chan = NULL;
ast_mutex_unlock(&conference_bridge->playback_lock);
return -1;
}
ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
} else {
/* Channel was already available so we just need to add it back into the bridge */
underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
}
/* The channel is all under our control, in goes the prompt */
ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
ast_bridge_depart(conference_bridge->bridge, underlying_channel);
ast_mutex_unlock(&conference_bridge->playback_lock);
return 0;
}
| static int post_join_marked | ( | struct conference_bridge * | conference_bridge, |
| struct conference_bridge_user * | conference_bridge_user | ||
| ) | [static] |
Perform post-joining marked specific actions.
| conference_bridge | Conference bridge being joined |
| conference_bridge_user | Conference bridge user joining |
Definition at line 260 of file app_confbridge.c.
References ao2_lock, ao2_unlock, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_suspend(), ast_bridge_unsuspend(), AST_LIST_TRAVERSE, ast_moh_start(), ast_moh_stop(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::features, conference_bridge_user::flags, conference_bridge_user::list, conference_bridge::markedusers, ast_bridge_features::mute, conference_bridge_user::opt_args, OPTION_MARKEDUSER, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_QUIET, play_prompt_to_channel(), play_sound_file(), and conference_bridge::users_list.
Referenced by join_conference_bridge().
{
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
struct conference_bridge_user *other_conference_bridge_user = NULL;
/* If we are not the first marked user to join just bail out now */
if (conference_bridge->markedusers >= 2) {
return 0;
}
/* Iterate through every participant stopping MOH on them if need be */
AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
if (other_conference_bridge_user == conference_bridge_user) {
continue;
}
if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
ast_moh_stop(other_conference_bridge_user->chan);
ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
}
}
/* Next play the audio file stating they are going to be placed into the conference */
if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
ao2_unlock(conference_bridge);
ast_autoservice_start(conference_bridge_user->chan);
play_sound_file(conference_bridge, "conf-placeintoconf");
ast_autoservice_stop(conference_bridge_user->chan);
ao2_lock(conference_bridge);
}
/* Finally iterate through and unmute them all */
AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
if (other_conference_bridge_user == conference_bridge_user) {
continue;
}
other_conference_bridge_user->features.mute = 0;
}
} else {
/* If a marked user already exists in the conference bridge we can just bail out now */
if (conference_bridge->markedusers) {
return 0;
}
/* Be sure we are muted so we can't talk to anybody else waiting */
conference_bridge_user->features.mute = 1;
/* If we have not been quieted play back that they are waiting for the leader */
if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader")) {
/* user hung up while the sound was playing */
return -1;
}
}
/* Start music on hold if needed */
/* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
* allowing a marked user to enter while the prompt was playing
*/
if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
}
}
return 0;
}
| static int post_join_unmarked | ( | struct conference_bridge * | conference_bridge, |
| struct conference_bridge_user * | conference_bridge_user | ||
| ) | [static] |
Perform post-joining non-marked specific actions.
| conference_bridge | Conference bridge being joined |
| conference_bridge_user | Conference bridge user joining |
Definition at line 331 of file app_confbridge.c.
References announce_user_count(), ao2_lock, ao2_unlock, ast_bridge_suspend(), ast_bridge_unsuspend(), AST_LIST_FIRST, ast_moh_start(), ast_moh_stop(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::flags, conference_bridge_user::opt_args, OPTION_ANNOUNCEUSERCOUNT, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_NOONLYPERSON, OPTION_QUIET, play_prompt_to_channel(), conference_bridge::users, and conference_bridge::users_list.
Referenced by join_conference_bridge().
{
/* Play back audio prompt and start MOH if need be if we are the first participant */
if (conference_bridge->users == 1) {
/* If audio prompts have not been quieted or this prompt quieted play it on out */
if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson")) {
/* user hung up while the sound was playing */
return -1;
}
}
/* If we need to start music on hold on the channel do so now */
/* We need to re-check the number of users in the conference bridge here because another conference bridge
* participant could have joined while the above prompt was playing for the first user.
*/
if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
}
return 0;
}
/* Announce number of users if need be */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
ao2_unlock(conference_bridge);
if (announce_user_count(conference_bridge, conference_bridge_user)) {
ao2_lock(conference_bridge);
return -1;
}
ao2_lock(conference_bridge);
}
/* If we are the second participant we may need to stop music on hold on the first */
if (conference_bridge->users == 2) {
struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
/* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
ast_moh_stop(first_participant->chan);
ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
}
}
return 0;
}
| static int unload_module | ( | void | ) | [static] |
Called when module is being unloaded.
Definition at line 824 of file app_confbridge.c.
References ao2_ref, ast_unregister_application(), and conference_bridges.
{
int res = ast_unregister_application(app);
/* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
ao2_ref(conference_bridges, -1);
return res;
}
struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Conference Bridge 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 = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, } [static] |
Definition at line 854 of file app_confbridge.c.
const char app[] = "ConfBridge" [static] |
Definition at line 119 of file app_confbridge.c.
struct ast_app_option app_opts[128] = { [ 'A' ] = { .flag = OPTION_MARKEDUSER }, [ 'a' ] = { .flag = OPTION_ADMIN }, [ 'c' ] = { .flag = OPTION_ANNOUNCEUSERCOUNT }, [ 'm' ] = { .flag = OPTION_STARTMUTED }, [ 'M' ] = { .flag = OPTION_MUSICONHOLD , .arg_index = OPTION_MUSICONHOLD_CLASS + 1 }, [ '1' ] = { .flag = OPTION_NOONLYPERSON }, [ 's' ] = { .flag = OPTION_MENU }, [ 'w' ] = { .flag = OPTION_WAITMARKED }, [ 'q' ] = { .flag = OPTION_QUIET },} [static] |
Definition at line 149 of file app_confbridge.c.
Referenced by confbridge_exec().
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 854 of file app_confbridge.c.
struct ao2_container* conference_bridges [static] |
Container to hold all conference bridges in progress.
Definition at line 181 of file app_confbridge.c.
Referenced by join_conference_bridge(), leave_conference_bridge(), load_module(), and unload_module().