#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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 152 of file app_confbridge.c.
anonymous enum |
Definition at line 121 of file app_confbridge.c.
00121 { 00122 OPTION_ADMIN = (1 << 0), /*!< Set if the caller is an administrator */ 00123 OPTION_MENU = (1 << 1), /*!< Set if the caller should have access to the conference bridge IVR menu */ 00124 OPTION_MUSICONHOLD = (1 << 2), /*!< Set if music on hold should be played if nobody else is in the conference bridge */ 00125 OPTION_NOONLYPERSON = (1 << 3), /*!< Set if the "you are currently the only person in this conference" sound file should not be played */ 00126 OPTION_STARTMUTED = (1 << 4), /*!< Set if the caller should be initially set muted */ 00127 OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */ 00128 OPTION_MARKEDUSER = (1 << 6), /*!< Set if the caller is a marked user */ 00129 OPTION_WAITMARKED = (1 << 7), /*!< Set if the conference must wait for a marked user before starting */ 00130 OPTION_QUIET = (1 << 8), /*!< Set if no audio prompts should be played */ 00131 };
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.
00133 { 00134 OPTION_MUSICONHOLD_CLASS, /*!< If the 'M' option is set, the music on hold class to play */ 00135 /*This must be the last element */ 00136 OPTION_ARRAY_SIZE, 00137 };
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().
00208 { 00209 if (conference_bridge->users == 1) { 00210 /* Awww we are the only person in the conference bridge */ 00211 return 0; 00212 } else if (conference_bridge->users == 2) { 00213 /* Eep, there is one other person */ 00214 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) { 00215 return -1; 00216 } 00217 } else { 00218 /* Alas multiple others in here */ 00219 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) { 00220 return -1; 00221 } 00222 if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) { 00223 return -1; 00224 } 00225 if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) { 00226 return -1; 00227 } 00228 } 00229 return 0; 00230 }
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::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().
00724 { 00725 int res = 0, volume_adjustments[2]; 00726 char *parse; 00727 struct conference_bridge *conference_bridge = NULL; 00728 struct conference_bridge_user conference_bridge_user = { 00729 .chan = chan, 00730 }; 00731 const char *tmp, *join_sound = NULL, *leave_sound = NULL; 00732 AST_DECLARE_APP_ARGS(args, 00733 AST_APP_ARG(conf_name); 00734 AST_APP_ARG(options); 00735 ); 00736 00737 if (ast_strlen_zero(data)) { 00738 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app); 00739 return -1; 00740 } 00741 00742 /* We need to make a copy of the input string if we are going to modify it! */ 00743 parse = ast_strdupa(data); 00744 00745 AST_STANDARD_APP_ARGS(args, parse); 00746 00747 if (args.argc == 2) { 00748 ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options); 00749 } 00750 00751 /* Look for a conference bridge matching the provided name */ 00752 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) { 00753 return -1; 00754 } 00755 00756 /* Keep a copy of volume adjustments so we can restore them later if need be */ 00757 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ); 00758 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE); 00759 00760 /* Always initialize the features structure, we are in most cases always going to need it. */ 00761 ast_bridge_features_init(&conference_bridge_user.features); 00762 00763 /* If the menu option is enabled provide a user or admin menu as a custom feature hook */ 00764 if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) { 00765 ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user); 00766 } 00767 00768 /* If the caller should be joined already muted, make it so */ 00769 if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) { 00770 conference_bridge_user.features.mute = 1; 00771 } 00772 00773 /* Grab join/leave sounds from the channel */ 00774 ast_channel_lock(chan); 00775 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) { 00776 join_sound = ast_strdupa(tmp); 00777 } 00778 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) { 00779 leave_sound = ast_strdupa(tmp); 00780 } 00781 ast_channel_unlock(chan); 00782 00783 /* If there is 1 or more people already in the conference then play our join sound unless overridden */ 00784 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) { 00785 ast_autoservice_start(chan); 00786 play_sound_file(conference_bridge, join_sound); 00787 ast_autoservice_stop(chan); 00788 } 00789 00790 /* Join our conference bridge for real */ 00791 ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features); 00792 00793 /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */ 00794 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) { 00795 ast_autoservice_start(chan); 00796 play_sound_file(conference_bridge, leave_sound); 00797 ast_autoservice_stop(chan); 00798 } 00799 00800 /* Easy as pie, depart this channel from the conference bridge */ 00801 leave_conference_bridge(conference_bridge, &conference_bridge_user); 00802 conference_bridge = NULL; 00803 00804 /* Can't forget to clean up the features structure, or else we risk a memory leak */ 00805 ast_bridge_features_cleanup(&conference_bridge_user.features); 00806 00807 /* If the user was kicked from the conference play back the audio prompt for it */ 00808 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) { 00809 res = ast_stream_and_wait(chan, "conf-kicked", ""); 00810 } 00811 00812 /* Restore volume adjustments to previous values in case they were changed */ 00813 if (volume_adjustments[0]) { 00814 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]); 00815 } 00816 if (volume_adjustments[1]) { 00817 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]); 00818 } 00819 00820 return res; 00821 }
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().
00194 { 00195 const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg; 00196 return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0); 00197 }
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().
00187 { 00188 const struct conference_bridge *conference_bridge = obj; 00189 return ast_str_case_hash(conference_bridge->name); 00190 }
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().
00383 { 00384 struct conference_bridge *conference_bridge = obj; 00385 00386 ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name); 00387 00388 ast_mutex_destroy(&conference_bridge->playback_lock); 00389 00390 if (conference_bridge->playback_chan) { 00391 struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL); 00392 ast_hangup(underlying_channel); 00393 ast_hangup(conference_bridge->playback_chan); 00394 conference_bridge->playback_chan = NULL; 00395 } 00396 00397 /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */ 00398 if (conference_bridge->bridge) { 00399 ast_bridge_destroy(conference_bridge->bridge); 00400 conference_bridge->bridge = NULL; 00401 } 00402 }
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 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_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, post_join_marked(), post_join_unmarked(), conference_bridge::users, and conference_bridge::users_list.
Referenced by confbridge_exec().
00415 { 00416 struct conference_bridge *conference_bridge = NULL; 00417 struct conference_bridge tmp; 00418 00419 ast_copy_string(tmp.name, name, sizeof(tmp.name)); 00420 00421 /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */ 00422 ao2_lock(conference_bridges); 00423 00424 ast_debug(1, "Trying to find conference bridge '%s'\n", name); 00425 00426 /* Attempt to find an existing conference bridge */ 00427 conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER); 00428 00429 /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */ 00430 if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) { 00431 ao2_unlock(conference_bridges); 00432 ao2_ref(conference_bridge, -1); 00433 ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name); 00434 ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", ""); 00435 return NULL; 00436 } 00437 00438 /* If no conference bridge was found see if we can create one */ 00439 if (!conference_bridge) { 00440 /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */ 00441 if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) { 00442 ao2_unlock(conference_bridges); 00443 ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name); 00444 return NULL; 00445 } 00446 00447 /* Setup conference bridge parameters */ 00448 ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name)); 00449 00450 /* Create an actual bridge that will do the audio mixing */ 00451 if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) { 00452 ao2_ref(conference_bridge, -1); 00453 conference_bridge = NULL; 00454 ao2_unlock(conference_bridges); 00455 ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name); 00456 return NULL; 00457 } 00458 00459 /* Setup lock for playback channel */ 00460 ast_mutex_init(&conference_bridge->playback_lock); 00461 00462 /* Link it into the conference bridges container */ 00463 ao2_link(conference_bridges, conference_bridge); 00464 00465 ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges); 00466 } 00467 00468 ao2_unlock(conference_bridges); 00469 00470 /* Setup conference bridge user parameters */ 00471 conference_bridge_user->conference_bridge = conference_bridge; 00472 00473 ao2_lock(conference_bridge); 00474 00475 /* All good to go, add them in */ 00476 AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list); 00477 00478 /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */ 00479 conference_bridge->users++; 00480 00481 /* If the caller is a marked user bump up the count */ 00482 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) { 00483 conference_bridge->markedusers++; 00484 } 00485 00486 /* Set the device state for this conference */ 00487 if (conference_bridge->users == 1) { 00488 ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name); 00489 } 00490 00491 /* 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 */ 00492 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) { 00493 if (post_join_marked(conference_bridge, conference_bridge_user)) { 00494 ao2_unlock(conference_bridge); 00495 leave_conference_bridge(conference_bridge, conference_bridge_user); 00496 return NULL; 00497 } 00498 } else { 00499 if (post_join_unmarked(conference_bridge, conference_bridge_user)) { 00500 ao2_unlock(conference_bridge); 00501 leave_conference_bridge(conference_bridge, conference_bridge_user); 00502 return NULL; 00503 } 00504 } 00505 00506 ao2_unlock(conference_bridge); 00507 00508 return conference_bridge; 00509 }
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_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(), and join_conference_bridge().
00519 { 00520 ao2_lock(conference_bridge); 00521 00522 /* If this caller is a marked user bump down the count */ 00523 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) { 00524 conference_bridge->markedusers--; 00525 } 00526 00527 /* Decrement the users count while keeping the previous participant count */ 00528 conference_bridge->users--; 00529 00530 /* Drop conference bridge user from the list, they be going bye bye */ 00531 AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list); 00532 00533 /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */ 00534 if (conference_bridge->users) { 00535 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) { 00536 struct conference_bridge_user *other_participant = NULL; 00537 00538 /* Start out with muting everyone */ 00539 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { 00540 other_participant->features.mute = 1; 00541 } 00542 00543 /* Play back the audio prompt saying the leader has left the conference */ 00544 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) { 00545 ao2_unlock(conference_bridge); 00546 ast_autoservice_start(conference_bridge_user->chan); 00547 play_sound_file(conference_bridge, "conf-leaderhasleft"); 00548 ast_autoservice_stop(conference_bridge_user->chan); 00549 ao2_lock(conference_bridge); 00550 } 00551 00552 /* Now on to starting MOH if needed */ 00553 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) { 00554 if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) { 00555 ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00556 ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan); 00557 } 00558 } 00559 } else if (conference_bridge->users == 1) { 00560 /* Of course if there is one other person in here we may need to start up MOH on them */ 00561 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list); 00562 00563 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { 00564 ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00565 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); 00566 } 00567 } 00568 } else { 00569 /* Set device state to "not in use" */ 00570 ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name); 00571 00572 ao2_unlink(conference_bridges, conference_bridge); 00573 } 00574 00575 /* Done mucking with the conference bridge, huzzah */ 00576 ao2_unlock(conference_bridge); 00577 00578 ao2_ref(conference_bridge, -1); 00579 }
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.
00836 { 00837 /* Create a container to hold the conference bridges */ 00838 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) { 00839 return AST_MODULE_LOAD_DECLINE; 00840 } 00841 00842 if (ast_register_application_xml(app, confbridge_exec)) { 00843 ao2_ref(conference_bridges, -1); 00844 return AST_MODULE_LOAD_DECLINE; 00845 } 00846 00847 return AST_MODULE_LOAD_SUCCESS; 00848 }
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, 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().
00644 { 00645 struct conference_bridge_user *conference_bridge_user = hook_pvt; 00646 struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge; 00647 int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN); 00648 00649 /* See if music on hold is playing */ 00650 ao2_lock(conference_bridge); 00651 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00652 /* Just us so MOH is probably indeed going, let's stop it */ 00653 ast_moh_stop(bridge_channel->chan); 00654 } 00655 ao2_unlock(conference_bridge); 00656 00657 /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */ 00658 if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) { 00659 res = -1; 00660 goto finished; 00661 } 00662 00663 /* Wait for them to enter a digit from the user menu options */ 00664 digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY); 00665 ast_stopstream(bridge_channel->chan); 00666 00667 if (digit == '1') { 00668 /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */ 00669 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) { 00670 conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0); 00671 } 00672 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), ""); 00673 } else if (isadmin && digit == '2') { 00674 /* 2 - Unlock or lock conference */ 00675 conference_bridge->locked = (!conference_bridge->locked ? 1 : 0); 00676 res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), ""); 00677 } else if (isadmin && digit == '3') { 00678 /* 3 - Eject last user */ 00679 struct conference_bridge_user *last_participant = NULL; 00680 00681 ao2_lock(conference_bridge); 00682 if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) { 00683 ao2_unlock(conference_bridge); 00684 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", ""); 00685 } else { 00686 last_participant->kicked = 1; 00687 ast_bridge_remove(conference_bridge->bridge, last_participant->chan); 00688 ao2_unlock(conference_bridge); 00689 } 00690 } else if (digit == '4') { 00691 /* 4 - Decrease listening volume */ 00692 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1); 00693 } else if (digit == '6') { 00694 /* 6 - Increase listening volume */ 00695 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1); 00696 } else if (digit == '7') { 00697 /* 7 - Decrease talking volume */ 00698 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1); 00699 } else if (digit == '8') { 00700 /* 8 - Exit the IVR */ 00701 } else if (digit == '9') { 00702 /* 9 - Increase talking volume */ 00703 ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1); 00704 } else { 00705 /* No valid option was selected */ 00706 res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", ""); 00707 } 00708 00709 finished: 00710 /* See if music on hold needs to be started back up again */ 00711 ao2_lock(conference_bridge); 00712 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00713 ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00714 } 00715 ao2_unlock(conference_bridge); 00716 00717 bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT; 00718 00719 return res; 00720 }
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().
00244 { 00245 int res; 00246 ao2_unlock(conference_bridge); 00247 res = ast_stream_and_wait(chan, file, ""); 00248 ao2_lock(conference_bridge); 00249 return res; 00250 }
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, 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().
00591 { 00592 struct ast_channel *underlying_channel; 00593 00594 ast_mutex_lock(&conference_bridge->playback_lock); 00595 00596 if (!(conference_bridge->playback_chan)) { 00597 int cause; 00598 00599 if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) { 00600 ast_mutex_unlock(&conference_bridge->playback_lock); 00601 return -1; 00602 } 00603 00604 conference_bridge->playback_chan->bridge = conference_bridge->bridge; 00605 00606 if (ast_call(conference_bridge->playback_chan, "", 0)) { 00607 ast_hangup(conference_bridge->playback_chan); 00608 conference_bridge->playback_chan = NULL; 00609 ast_mutex_unlock(&conference_bridge->playback_lock); 00610 return -1; 00611 } 00612 00613 ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name); 00614 00615 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL); 00616 } else { 00617 /* Channel was already available so we just need to add it back into the bridge */ 00618 underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL); 00619 ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL); 00620 } 00621 00622 /* The channel is all under our control, in goes the prompt */ 00623 ast_stream_and_wait(conference_bridge->playback_chan, filename, ""); 00624 00625 ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge); 00626 ast_bridge_depart(conference_bridge->bridge, underlying_channel); 00627 00628 ast_mutex_unlock(&conference_bridge->playback_lock); 00629 00630 return 0; 00631 }
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::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().
00261 { 00262 if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) { 00263 struct conference_bridge_user *other_conference_bridge_user = NULL; 00264 00265 /* If we are not the first marked user to join just bail out now */ 00266 if (conference_bridge->markedusers >= 2) { 00267 return 0; 00268 } 00269 00270 /* Iterate through every participant stopping MOH on them if need be */ 00271 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) { 00272 if (other_conference_bridge_user == conference_bridge_user) { 00273 continue; 00274 } 00275 if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) { 00276 ast_moh_stop(other_conference_bridge_user->chan); 00277 ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan); 00278 } 00279 } 00280 00281 /* Next play the audio file stating they are going to be placed into the conference */ 00282 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) { 00283 ao2_unlock(conference_bridge); 00284 ast_autoservice_start(conference_bridge_user->chan); 00285 play_sound_file(conference_bridge, "conf-placeintoconf"); 00286 ast_autoservice_stop(conference_bridge_user->chan); 00287 ao2_lock(conference_bridge); 00288 } 00289 00290 /* Finally iterate through and unmute them all */ 00291 AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) { 00292 if (other_conference_bridge_user == conference_bridge_user) { 00293 continue; 00294 } 00295 other_conference_bridge_user->features.mute = 0; 00296 } 00297 00298 } else { 00299 /* If a marked user already exists in the conference bridge we can just bail out now */ 00300 if (conference_bridge->markedusers) { 00301 return 0; 00302 } 00303 /* Be sure we are muted so we can't talk to anybody else waiting */ 00304 conference_bridge_user->features.mute = 1; 00305 /* If we have not been quieted play back that they are waiting for the leader */ 00306 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) { 00307 if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader")) { 00308 /* user hung up while the sound was playing */ 00309 return -1; 00310 } 00311 } 00312 /* Start music on hold if needed */ 00313 /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially 00314 * allowing a marked user to enter while the prompt was playing 00315 */ 00316 if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00317 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00318 } 00319 } 00320 return 0; 00321 }
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::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().
00332 { 00333 /* Play back audio prompt and start MOH if need be if we are the first participant */ 00334 if (conference_bridge->users == 1) { 00335 /* If audio prompts have not been quieted or this prompt quieted play it on out */ 00336 if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) { 00337 if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson")) { 00338 /* user hung up while the sound was playing */ 00339 return -1; 00340 } 00341 } 00342 /* If we need to start music on hold on the channel do so now */ 00343 /* We need to re-check the number of users in the conference bridge here because another conference bridge 00344 * participant could have joined while the above prompt was playing for the first user. 00345 */ 00346 if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) { 00347 ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL); 00348 } 00349 return 0; 00350 } 00351 00352 /* Announce number of users if need be */ 00353 if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) { 00354 ao2_unlock(conference_bridge); 00355 if (announce_user_count(conference_bridge, conference_bridge_user)) { 00356 ao2_lock(conference_bridge); 00357 return -1; 00358 } 00359 ao2_lock(conference_bridge); 00360 } 00361 00362 /* If we are the second participant we may need to stop music on hold on the first */ 00363 if (conference_bridge->users == 2) { 00364 struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list); 00365 00366 /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */ 00367 if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) { 00368 ast_moh_stop(first_participant->chan); 00369 ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan); 00370 } 00371 } 00372 return 0; 00373 }
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.
00825 { 00826 int res = ast_unregister_application(app); 00827 00828 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */ 00829 ao2_ref(conference_bridges, -1); 00830 00831 return res; 00832 }
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 = "88eaa8f5c1bd988bedd71113385e0886" , .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 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 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().