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