#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, 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 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 | __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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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. |
Joshua Colp <jcolp@digium.com>
Definition in file app_confbridge.c.
#define CONFERENCE_BRIDGE_BUCKETS 53 |
#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.
00117 { 00118 OPTION_ADMIN = (1 << 0), /*!< Set if the caller is an administrator */ 00119 OPTION_MENU = (1 << 1), /*!< Set if the caller should have access to the conference bridge IVR menu */ 00120 OPTION_MUSICONHOLD = (1 << 2), /*!< Set if music on hold should be played if nobody else is in the conference bridge */ 00121 OPTION_NOONLYPERSON = (1 << 3), /*!< Set if the "you are currently the only person in this conference" sound file should not be played */ 00122 OPTION_STARTMUTED = (1 << 4), /*!< Set if the caller should be initially set muted */ 00123 OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */ 00124 OPTION_MARKEDUSER = (1 << 6), /*!< Set if the caller is a marked user */ 00125 OPTION_WAITMARKED = (1 << 7), /*!< Set if the conference must wait for a marked user before starting */ 00126 OPTION_QUIET = (1 << 8), /*!< Set if no audio prompts should be played */ 00127 };
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.
00129 { 00130 OPTION_MUSICONHOLD_CLASS, /*!< If the 'M' option is set, the music on hold class to play */ 00131 /*This must be the last element */ 00132 OPTION_ARRAY_SIZE, 00133 };
static void __reg_module | ( | void | ) | [static] |
Definition at line 826 of file app_confbridge.c.
static void __unreg_module | ( | void | ) | [static] |
Definition at line 826 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().
00204 { 00205 if (conference_bridge->users == 1) { 00206 /* Awww we are the only person in the conference bridge */ 00207 return; 00208 } else if (conference_bridge->users == 2) { 00209 /* Eep, there is one other person */ 00210 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) { 00211 return; 00212 } 00213 } else { 00214 /* Alas multiple others in here */ 00215 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) { 00216 return; 00217 } 00218 if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) { 00219 return; 00220 } 00221 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) { 00222 return; 00223 } 00224 } 00225 }
static int confbridge_exec | ( | struct ast_channel * | chan, | |
const char * | data | |||
) | [static] |
The ConfBridge application.
Definition at line 695 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::conference_bridge, 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().
00696 { 00697 int res = 0, volume_adjustments[2]; 00698 char *parse; 00699 struct conference_bridge *conference_bridge = NULL; 00700 struct conference_bridge_user conference_bridge_user = { 00701 .chan = chan, 00702 }; 00703 const char *tmp, *join_sound = NULL, *leave_sound = NULL; 00704 AST_DECLARE_APP_ARGS(args, 00705 AST_APP_ARG(conf_name); 00706 AST_APP_ARG(options); 00707 ); 00708 00709 if (ast_strlen_zero(data)) { 00710 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app); 00711 return -1; 00712 } 00713 00714 /* We need to make a copy of the input string if we are going to modify it! */ 00715 parse = ast_strdupa(data); 00716 00717 AST_STANDARD_APP_ARGS(args, parse); 00718 00719 if (args.argc == 2) { 00720 ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options); 00721 } 00722 00723 /* Look for a conference bridge matching the provided name */ 00724 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) { 00725 return -1; 00726 } 00727 00728 /* Keep a copy of volume adjustments so we can restore them later if need be */ 00729 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ); 00730 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE); 00731 00732 /* Always initialize the features structure, we are in most cases always going to need it. */ 00733 ast_bridge_features_init(&conference_bridge_user.features); 00734 00735 /* If the menu option is enabled provide a user or admin menu as a custom feature hook */ 00736 if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) { 00737 ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user); 00738 } 00739 00740 /* If the caller should be joined already muted, make it so */ 00741 if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) { 00742 conference_bridge_user.features.mute = 1; 00743 } 00744 00745 /* Grab join/leave sounds from the channel */ 00746 ast_channel_lock(chan); 00747 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) { 00748 join_sound = ast_strdupa(tmp); 00749 } 00750 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) { 00751 leave_sound = ast_strdupa(tmp); 00752 } 00753 ast_channel_unlock(chan); 00754 00755 /* If there is 1 or more people already in the conference then play our join sound unless overridden */ 00756 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) { 00757 ast_autoservice_start(chan); 00758 play_sound_file(conference_bridge, join_sound); 00759 ast_autoservice_stop(chan); 00760 } 00761 00762 /* Join our conference bridge for real */ 00763 ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features); 00764 00765 /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */ 00766 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) { 00767 ast_autoservice_start(chan); 00768 play_sound_file(conference_bridge, leave_sound); 00769 ast_autoservice_stop(chan); 00770 } 00771 00772 /* Easy as pie, depart this channel from the conference bridge */ 00773 leave_conference_bridge(conference_bridge, &conference_bridge_user); 00774 conference_bridge = NULL; 00775 00776 /* Can't forget to clean up the features structure, or else we risk a memory leak */ 00777 ast_bridge_features_cleanup(&conference_bridge_user.features); 00778 00779 /* If the user was kicked from the conference play back the audio prompt for it */ 00780 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) { 00781 res = ast_stream_and_wait(chan, "conf-kicked", ""); 00782 } 00783 00784 /* Restore volume adjustments to previous values in case they were changed */ 00785 if (volume_adjustments[0]) { 00786 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]); 00787 } 00788 if (volume_adjustments[1]) { 00789 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]); 00790 } 00791 00792 return res; 00793 }
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().
00190 { 00191 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg; 00192 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0); 00193 }
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().
00183 { 00184 const struct conference_bridge *conference_bridge = obj; 00185 return ast_str_case_hash(conference_bridge->name); 00186 }
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().
00365 { 00366 struct conference_bridge *conference_bridge = obj; 00367 00368 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name); 00369 00370 ast_mutex_destroy(&conference_bridge->playback_lock); 00371 00372 if (conference_bridge->playback_chan) { 00373 struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL); 00374 ast_hangup(underlying_channel); 00375 ast_hangup(conference_bridge->playback_chan); 00376 conference_bridge->playback_chan = NULL; 00377 } 00378 00379 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */ 00380 if (conference_bridge->bridge) { 00381 ast_bridge_destroy(conference_bridge->bridge); 00382 conference_bridge->bridge = NULL; 00383 } 00384 }
static struct conference_bridge* join_conference_bridge | ( | const char * | name, | |
struct conference_bridge_user * | conference_bridge_user | |||
) | [static] |
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_DEVICE_INUSE, ast_devstate_changed(), AST_LIST_INSERT_TAIL, ast_log(), ast_mutex_init, ast_stream_and_wait(), ast_test_flag, 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, post_join_marked(), post_join_unmarked(), conference_bridge::users, and conference_bridge::users_list.
Referenced by confbridge_exec().
00395 { 00396 struct conference_bridge *conference_bridge = NULL; 00397 struct conference_bridge tmp; 00398 00399 ast_copy_string(tmp.name, name, sizeof(tmp.name)); 00400 00401 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */ 00402 ao2_lock(conference_bridges); 00403 00404 ast_debug(1, "Trying to find conference bridge '%s'\n", name); 00405 00406 /* Attempt to find an existing conference bridge */ 00407 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); 00408 00409 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */ 00410 if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) { 00411 ao2_unlock(conference_bridges); 00412 ao2_ref(conference_bridge, -1); 00413 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name); 00414 ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", ""); 00415 return NULL; 00416 } 00417 00418 /* If no conference bridge was found see if we can create one */ 00419 if (!conference_bridge) { 00420 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */ 00421 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) { 00422 ao2_unlock(conference_bridges); 00423 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name); 00424 return NULL; 00425 } 00426 00427 /* Setup conference bridge parameters */ 00428 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name)); 00429 00430 /* Create an actual bridge that will do the audio mixing */ 00431 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) { 00432 ao2_ref(conference_bridge, -1); 00433 conference_bridge = NULL; 00434 ao2_unlock(conference_bridges); 00435 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name); 00436 return NULL; 00437 } 00438 00439 /* Setup lock for playback channel */ 00440 ast_mutex_init(&conference_bridge->playback_lock); 00441 00442 /* Link it into the conference bridges container */ 00443 ao2_link(conference_bridges, conference_bridge); 00444 00445 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges); 00446 } 00447 00448 ao2_unlock(conference_bridges); 00449 00450 /* Setup conference bridge user parameters */ 00451 conference_bridge_user->conference_bridge = conference_bridge; 00452 00453 ao2_lock(conference_bridge); 00454 00455 /* All good to go, add them in */ 00456 AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list); 00457 00458 /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */ 00459 conference_bridge->users++; 00460 00461 /* If the caller is a marked user bump up the count */ 00462 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) { 00463 conference_bridge->markedusers++; 00464 } 00465 00466 /* Set the device state for this conference */ 00467 if (conference_bridge->users == 1) { 00468 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name); 00469 } 00470 00471 /* 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 */ 00472 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) { 00473 post_join_marked(conference_bridge, conference_bridge_user); 00474 } else { 00475 post_join_unmarked(conference_bridge, conference_bridge_user); 00476 } 00477 00478 ao2_unlock(conference_bridge); 00479 00480 return conference_bridge; 00481 }
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 490 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_bridge_user::conference_bridge, 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().
00491 { 00492 ao2_lock(conference_bridge); 00493 00494 /* If this caller is a marked user bump down the count */ 00495 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) { 00496 conference_bridge->markedusers--; 00497 } 00498 00499 /* Decrement the users count while keeping the previous participant count */ 00500 conference_bridge->users--; 00501 00502 /* Drop conference bridge user from the list, they be going bye bye */ 00503 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list); 00504 00505 /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */ 00506 if (conference_bridge->users) { 00507 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) { 00508 struct conference_bridge_user *other_participant = NULL; 00509 00510 /* Start out with muting everyone */ 00511 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { 00512 other_participant->features.mute = 1; 00513 } 00514 00515 /* Play back the audio prompt saying the leader has left the conference */ 00516 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) { 00517 ao2_unlock(conference_bridge); 00518 ast_autoservice_start(conference_bridge_user->chan); 00519 play_sound_file(conference_bridge, "conf-leaderhasleft"); 00520 ast_autoservice_stop(conference_bridge_user->chan); 00521 ao2_lock(conference_bridge); 00522 } 00523 00524 /* Now on to starting MOH if needed */ 00525 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { 00526 if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) { 00527 ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00528 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan); 00529 } 00530 } 00531 } else if (conference_bridge->users == 1) { 00532 /* Of course if there is one other person in here we may need to start up MOH on them */ 00533 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list); 00534 00535 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { 00536 ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00537 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); 00538 } 00539 } 00540 } else { 00541 /* Set device state to "not in use" */ 00542 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name); 00543 00544 ao2_unlink(conference_bridges, conference_bridge); 00545 } 00546 00547 /* Done mucking with the conference bridge, huzzah */ 00548 ao2_unlock(conference_bridge); 00549 00550 ao2_ref(conference_bridge, -1); 00551 }
static int load_module | ( | void | ) | [static] |
Called when module is being loaded.
Definition at line 807 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.
00808 { 00809 /* Create a container to hold the conference bridges */ 00810 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) { 00811 return AST_MODULE_LOAD_DECLINE; 00812 } 00813 00814 if (ast_register_application_xml(app, confbridge_exec)) { 00815 ao2_ref(conference_bridges, -1); 00816 return AST_MODULE_LOAD_DECLINE; 00817 } 00818 00819 return AST_MODULE_LOAD_SUCCESS; 00820 }
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 615 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().
00616 { 00617 struct conference_bridge_user *conference_bridge_user = hook_pvt; 00618 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge; 00619 int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN); 00620 00621 /* See if music on hold is playing */ 00622 ao2_lock(conference_bridge); 00623 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00624 /* Just us so MOH is probably indeed going, let's stop it */ 00625 ast_moh_stop(bridge_channel->chan); 00626 } 00627 ao2_unlock(conference_bridge); 00628 00629 /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */ 00630 if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) { 00631 res = -1; 00632 goto finished; 00633 } 00634 00635 /* Wait for them to enter a digit from the user menu options */ 00636 digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY); 00637 ast_stopstream(bridge_channel->chan); 00638 00639 if (digit == '1') { 00640 /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */ 00641 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) { 00642 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0); 00643 } 00644 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), ""); 00645 } else if (isadmin && digit == '2') { 00646 /* 2 - Unlock or lock conference */ 00647 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0); 00648 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), ""); 00649 } else if (isadmin && digit == '3') { 00650 /* 3 - Eject last user */ 00651 struct conference_bridge_user *last_participant = NULL; 00652 00653 ao2_lock(conference_bridge); 00654 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) { 00655 ao2_unlock(conference_bridge); 00656 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", ""); 00657 } else { 00658 last_participant->kicked = 1; 00659 ast_bridge_remove(conference_bridge->bridge, last_participant->chan); 00660 ao2_unlock(conference_bridge); 00661 } 00662 } else if (digit == '4') { 00663 /* 4 - Decrease listening volume */ 00664 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1); 00665 } else if (digit == '6') { 00666 /* 6 - Increase listening volume */ 00667 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1); 00668 } else if (digit == '7') { 00669 /* 7 - Decrease talking volume */ 00670 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1); 00671 } else if (digit == '8') { 00672 /* 8 - Exit the IVR */ 00673 } else if (digit == '9') { 00674 /* 9 - Increase talking volume */ 00675 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1); 00676 } else { 00677 /* No valid option was selected */ 00678 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", ""); 00679 } 00680 00681 finished: 00682 /* See if music on hold needs to be started back up again */ 00683 ao2_lock(conference_bridge); 00684 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00685 ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00686 } 00687 ao2_unlock(conference_bridge); 00688 00689 bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT; 00690 00691 return res; 00692 }
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().
00239 { 00240 ao2_unlock(conference_bridge); 00241 ast_stream_and_wait(chan, file, ""); 00242 ao2_lock(conference_bridge); 00243 }
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 562 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().
00563 { 00564 struct ast_channel *underlying_channel; 00565 00566 ast_mutex_lock(&conference_bridge->playback_lock); 00567 00568 if (!(conference_bridge->playback_chan)) { 00569 int cause; 00570 00571 if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) { 00572 ast_mutex_unlock(&conference_bridge->playback_lock); 00573 return -1; 00574 } 00575 00576 conference_bridge->playback_chan->bridge = conference_bridge->bridge; 00577 00578 if (ast_call(conference_bridge->playback_chan, "", 0)) { 00579 ast_hangup(conference_bridge->playback_chan); 00580 conference_bridge->playback_chan = NULL; 00581 ast_mutex_unlock(&conference_bridge->playback_lock); 00582 return -1; 00583 } 00584 00585 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name); 00586 00587 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL); 00588 } else { 00589 /* Channel was already available so we just need to add it back into the bridge */ 00590 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL); 00591 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL); 00592 } 00593 00594 /* The channel is all under our control, in goes the prompt */ 00595 ast_stream_and_wait(conference_bridge->playback_chan, filename, ""); 00596 00597 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge); 00598 ast_bridge_depart(conference_bridge->bridge, underlying_channel); 00599 00600 ast_mutex_unlock(&conference_bridge->playback_lock); 00601 00602 return 0; 00603 }
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::conference_bridge, 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().
00254 { 00255 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) { 00256 struct conference_bridge_user *other_conference_bridge_user = NULL; 00257 00258 /* If we are not the first marked user to join just bail out now */ 00259 if (conference_bridge->markedusers >= 2) { 00260 return; 00261 } 00262 00263 /* Iterate through every participant stopping MOH on them if need be */ 00264 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) { 00265 if (other_conference_bridge_user == conference_bridge_user) { 00266 continue; 00267 } 00268 if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) { 00269 ast_moh_stop(other_conference_bridge_user->chan); 00270 ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan); 00271 } 00272 } 00273 00274 /* Next play the audio file stating they are going to be placed into the conference */ 00275 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) { 00276 ao2_unlock(conference_bridge); 00277 ast_autoservice_start(conference_bridge_user->chan); 00278 play_sound_file(conference_bridge, "conf-placeintoconf"); 00279 ast_autoservice_stop(conference_bridge_user->chan); 00280 ao2_lock(conference_bridge); 00281 } 00282 00283 /* Finally iterate through and unmute them all */ 00284 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) { 00285 if (other_conference_bridge_user == conference_bridge_user) { 00286 continue; 00287 } 00288 other_conference_bridge_user->features.mute = 0; 00289 } 00290 00291 } else { 00292 /* If a marked user already exists in the conference bridge we can just bail out now */ 00293 if (conference_bridge->markedusers) { 00294 return; 00295 } 00296 /* Be sure we are muted so we can't talk to anybody else waiting */ 00297 conference_bridge_user->features.mute = 1; 00298 /* If we have not been quieted play back that they are waiting for the leader */ 00299 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) { 00300 play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader"); 00301 } 00302 /* Start music on hold if needed */ 00303 /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially 00304 * allowing a marked user to enter while the prompt was playing 00305 */ 00306 if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00307 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00308 } 00309 } 00310 }
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::conference_bridge, 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().
00321 { 00322 /* Play back audio prompt and start MOH if need be if we are the first participant */ 00323 if (conference_bridge->users == 1) { 00324 /* If audio prompts have not been quieted or this prompt quieted play it on out */ 00325 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) { 00326 play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson"); 00327 } 00328 /* If we need to start music on hold on the channel do so now */ 00329 /* We need to re-check the number of users in the conference bridge here because another conference bridge 00330 * participant could have joined while the above prompt was playing for the first user. 00331 */ 00332 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00333 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00334 } 00335 return; 00336 } 00337 00338 /* Announce number of users if need be */ 00339 if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) { 00340 ao2_unlock(conference_bridge); 00341 announce_user_count(conference_bridge, conference_bridge_user); 00342 ao2_lock(conference_bridge); 00343 } 00344 00345 /* If we are the second participant we may need to stop music on hold on the first */ 00346 if (conference_bridge->users == 2) { 00347 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list); 00348 00349 /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */ 00350 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { 00351 ast_moh_stop(first_participant->chan); 00352 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); 00353 } 00354 } 00355 }
static int unload_module | ( | void | ) | [static] |
Called when module is being unloaded.
Definition at line 796 of file app_confbridge.c.
References ao2_ref, ast_unregister_application(), and conference_bridges.
00797 { 00798 int res = ast_unregister_application(app); 00799 00800 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */ 00801 ao2_ref(conference_bridges, -1); 00802 00803 return res; 00804 }
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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, } [static] |
Definition at line 826 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 app_exec(), confbridge_exec(), disa_exec(), record_exec(), sendurl_exec(), and softhangup_exec().
struct ast_module_info* ast_module_info = &__mod_info [static] |
Definition at line 826 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().