Conference Bridge application. More...
#include "asterisk.h"
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <signal.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
#include "asterisk/channel.h"
#include "asterisk/pbx.h"
#include "asterisk/module.h"
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/bridging.h"
#include "asterisk/musiconhold.h"
#include "asterisk/say.h"
#include "asterisk/audiohook.h"
#include "asterisk/astobj2.h"
Go to the source code of this file.
Data Structures | |
struct | conference_bridge |
The structure that represents a conference bridge. More... | |
struct | conference_bridge_user |
The structure that represents a conference bridge user. More... | |
Defines | |
#define | CONFERENCE_BRIDGE_BUCKETS 53 |
#define | MAX_CONF_NAME AST_MAX_EXTENSION |
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 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. | |
AST_APP_OPTIONS (app_opts,{AST_APP_OPTION('A', OPTION_MARKEDUSER), AST_APP_OPTION('a', OPTION_ADMIN), AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT), AST_APP_OPTION('m', OPTION_STARTMUTED), AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS), AST_APP_OPTION('1', OPTION_NOONLYPERSON), AST_APP_OPTION('s', OPTION_MENU), AST_APP_OPTION('w', OPTION_WAITMARKED), AST_APP_OPTION('q', OPTION_QUIET),}) | |
AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"Conference Bridge Application",.load=load_module,.unload=unload_module,.load_pri=AST_MODPRI_DEVSTATE_PROVIDER,) | |
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 const char | app [] = "ConfBridge" |
static struct ao2_container * | conference_bridges |
Container to hold all conference bridges in progress. |
Conference Bridge application.
This is a conference bridge application utilizing the bridging core.
Definition in file app_confbridge.c.
#define CONFERENCE_BRIDGE_BUCKETS 53 |
Definition at line 155 of file app_confbridge.c.
Referenced by load_module().
#define MAX_CONF_NAME AST_MAX_EXTENSION |
Definition at line 152 of file app_confbridge.c.
Referenced by confbridge_exec().
anonymous enum |
OPTION_ADMIN |
Set if the caller is an administrator |
OPTION_MENU |
Set if the caller should have access to the conference bridge IVR menu |
OPTION_MUSICONHOLD |
Set if music on hold should be played if nobody else is in the conference bridge |
OPTION_NOONLYPERSON |
Set if the "you are currently the only person in this conference" sound file should not be played |
OPTION_STARTMUTED |
Set if the caller should be initially set muted |
OPTION_ANNOUNCEUSERCOUNT |
Set if the number of users should be announced to the caller |
OPTION_MARKEDUSER |
Set if the caller is a marked user |
OPTION_WAITMARKED |
Set if the conference must wait for a marked user before starting |
OPTION_QUIET |
Set if no audio prompts should be played |
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 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, 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 }
AST_APP_OPTIONS | ( | app_opts | ) |
AST_MODULE_INFO | ( | ASTERISK_GPL_KEY | , | |
AST_MODFLAG_LOAD_ORDER | , | |||
"Conference Bridge Application" | , | |||
. | load = load_module , |
|||
. | unload = unload_module , |
|||
. | load_pri = AST_MODPRI_DEVSTATE_PROVIDER | |||
) |
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 args, AST_APP_ARG, ast_app_parse_options(), AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_volume_get(), ast_audiohook_volume_set(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_features_cleanup(), ast_bridge_features_hook(), ast_bridge_features_init(), ast_bridge_join(), ast_channel_lock, ast_channel_unlock, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::features, conference_bridge_user::flags, join_conference_bridge(), conference_bridge_user::kicked, leave_conference_bridge(), LOG_WARNING, MAX_CONF_NAME, 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 /* We need to make a copy of the input string if we are going to modify it! */ 00739 parse = ast_strdupa(data); 00740 00741 AST_STANDARD_APP_ARGS(args, parse); 00742 00743 if (ast_strlen_zero(args.conf_name)) { 00744 ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app); 00745 return -1; 00746 } 00747 00748 if (strlen(args.conf_name) >= MAX_CONF_NAME) { 00749 ast_log(LOG_WARNING, "%s does not accept conference names longer than %d\n", app, MAX_CONF_NAME - 1); 00750 return -1; 00751 } 00752 00753 if (args.argc == 2) { 00754 ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options); 00755 } 00756 00757 /* Look for a conference bridge matching the provided name */ 00758 if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) { 00759 return -1; 00760 } 00761 00762 /* Keep a copy of volume adjustments so we can restore them later if need be */ 00763 volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ); 00764 volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE); 00765 00766 /* Always initialize the features structure, we are in most cases always going to need it. */ 00767 ast_bridge_features_init(&conference_bridge_user.features); 00768 00769 /* If the menu option is enabled provide a user or admin menu as a custom feature hook */ 00770 if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) { 00771 ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user); 00772 } 00773 00774 /* If the caller should be joined already muted, make it so */ 00775 if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) { 00776 conference_bridge_user.features.mute = 1; 00777 } 00778 00779 /* Grab join/leave sounds from the channel */ 00780 ast_channel_lock(chan); 00781 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) { 00782 join_sound = ast_strdupa(tmp); 00783 } 00784 if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) { 00785 leave_sound = ast_strdupa(tmp); 00786 } 00787 ast_channel_unlock(chan); 00788 00789 /* If there is 1 or more people already in the conference then play our join sound unless overridden */ 00790 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) { 00791 ast_autoservice_start(chan); 00792 play_sound_file(conference_bridge, join_sound); 00793 ast_autoservice_stop(chan); 00794 } 00795 00796 /* Join our conference bridge for real */ 00797 ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features); 00798 00799 /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */ 00800 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) { 00801 ast_autoservice_start(chan); 00802 play_sound_file(conference_bridge, leave_sound); 00803 ast_autoservice_stop(chan); 00804 } 00805 00806 /* Easy as pie, depart this channel from the conference bridge */ 00807 leave_conference_bridge(conference_bridge, &conference_bridge_user); 00808 conference_bridge = NULL; 00809 00810 /* Can't forget to clean up the features structure, or else we risk a memory leak */ 00811 ast_bridge_features_cleanup(&conference_bridge_user.features); 00812 00813 /* If the user was kicked from the conference play back the audio prompt for it */ 00814 if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) { 00815 res = ast_stream_and_wait(chan, "conf-kicked", ""); 00816 } 00817 00818 /* Restore volume adjustments to previous values in case they were changed */ 00819 if (volume_adjustments[0]) { 00820 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]); 00821 } 00822 if (volume_adjustments[1]) { 00823 ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]); 00824 } 00825 00826 return res; 00827 }
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, and conference_bridge::name.
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, read] |
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_CACHABLE, ast_devstate_changed(), AST_LIST_INSERT_TAIL, ast_log(), ast_mutex_init, ast_stream_and_wait(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridges, destroy_conference_bridge(), conference_bridge_user::flags, leave_conference_bridge(), conference_bridge::locked, LOG_ERROR, conference_bridge::markedusers, conference_bridge::name, OBJ_POINTER, OPTION_ADMIN, OPTION_MARKEDUSER, OPTION_WAITMARKED, post_join_marked(), post_join_unmarked(), and conference_bridge::users.
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, AST_DEVSTATE_CACHABLE, "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_CACHABLE, ast_devstate_changed(), AST_LIST_FIRST, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_moh_start(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridges, conference_bridge_user::features, conference_bridge_user::flags, conference_bridge::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(), and conference_bridge::users.
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, AST_DEVSTATE_CACHABLE, "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 841 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.
00842 { 00843 /* Create a container to hold the conference bridges */ 00844 if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) { 00845 return AST_MODULE_LOAD_DECLINE; 00846 } 00847 00848 if (ast_register_application_xml(app, confbridge_exec)) { 00849 ao2_ref(conference_bridges, -1); 00850 return AST_MODULE_LOAD_DECLINE; 00851 } 00852 00853 return AST_MODULE_LOAD_SUCCESS; 00854 }
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, 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, and conference_bridge::users.
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, and conference_bridge::name.
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::features, conference_bridge_user::flags, conference_bridge::markedusers, ast_bridge_features::mute, conference_bridge_user::opt_args, OPTION_MARKEDUSER, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_QUIET, and play_prompt_to_channel().
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::flags, conference_bridge_user::opt_args, OPTION_ANNOUNCEUSERCOUNT, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_NOONLYPERSON, OPTION_QUIET, play_prompt_to_channel(), and conference_bridge::users.
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 830 of file app_confbridge.c.
References ao2_ref, ast_unregister_application(), and conference_bridges.
00831 { 00832 int res = ast_unregister_application(app); 00833 00834 /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */ 00835 ao2_ref(conference_bridges, -1); 00836 00837 return res; 00838 }
const char app[] = "ConfBridge" [static] |
Definition at line 119 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().