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 void | 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, void *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 void | 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 void | post_join_marked (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user) |
| Perform post-joining marked specific actions. | |
| static void | 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 __MODULE_INFO_SECTION | __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Conference Bridge Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } |
| 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 151 of file app_confbridge.c.
Referenced by load_module().
| #define MAX_CONF_NAME 32 |
Definition at line 148 of file app_confbridge.c.
| anonymous enum |
Definition at line 117 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 129 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 814 of file app_confbridge.c.
| static void __unreg_module | ( | void | ) | [static] |
Definition at line 814 of file app_confbridge.c.
| static void 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 203 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;
} else if (conference_bridge->users == 2) {
/* Eep, there is one other person */
if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
return;
}
} else {
/* Alas multiple others in here */
if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
return;
}
if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
return;
}
if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
return;
}
}
}
| static int confbridge_exec | ( | struct ast_channel * | chan, |
| void * | data | ||
| ) | [static] |
The ConfBridge application.
Definition at line 687 of file app_confbridge.c.
References app_opts, 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, chan, 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 189 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 182 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 364 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 394 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_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, 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++;
}
/* 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)) {
post_join_marked(conference_bridge, conference_bridge_user);
} else {
post_join_unmarked(conference_bridge, conference_bridge_user);
}
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 485 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_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_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().
{
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 {
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 799 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 607 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, conference_bridge_user::chan, ast_bridge_channel::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 void 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 238 of file app_confbridge.c.
References ao2_lock(), ao2_unlock(), and ast_stream_and_wait().
Referenced by post_join_marked(), and post_join_unmarked().
{
ao2_unlock(conference_bridge);
ast_stream_and_wait(chan, file, "");
ao2_lock(conference_bridge);
}
| 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 554 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, ast_channel::name, conference_bridge::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, "", &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 void 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 253 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;
}
/* 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;
}
/* 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)) {
play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader");
}
/* 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);
}
}
}
| static void 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 320 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)) {
play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson");
}
/* 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;
}
/* Announce number of users if need be */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
ao2_unlock(conference_bridge);
announce_user_count(conference_bridge, conference_bridge_user);
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);
}
}
}
| static int unload_module | ( | void | ) | [static] |
Called when module is being unloaded.
Definition at line 788 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 __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Conference Bridge Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .load = load_module, .unload = unload_module, } [static] |
Definition at line 814 of file app_confbridge.c.
const char* app = "ConfBridge" [static] |
Definition at line 115 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 145 of file app_confbridge.c.
Referenced by confbridge_exec().
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 814 of file app_confbridge.c.
struct ao2_container* conference_bridges [static] |
Container to hold all conference bridges in progress.
Definition at line 177 of file app_confbridge.c.
Referenced by join_conference_bridge(), leave_conference_bridge(), load_module(), and unload_module().