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