Mon Mar 19 11:30:31 2012

Asterisk developer's documentation


app_confbridge.c File Reference

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 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_bridgejoin_conference_bridge (const char *name, struct conference_bridge_user *conference_bridge_user)
 Join a conference bridge.
static void leave_conference_bridge (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Leave a conference bridge.
static int load_module (void)
 Called when module is being loaded.
static int menu_callback (struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 DTMF Menu Callback.
static int play_prompt_to_channel (struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
 Play back an audio file to a channel.
static int play_sound_file (struct conference_bridge *conference_bridge, const char *filename)
 Play sound file into conference bridge.
static int post_join_marked (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Perform post-joining marked specific actions.
static int post_join_unmarked (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Perform post-joining non-marked specific actions.
static int unload_module (void)
 Called when module is being unloaded.

Variables

static struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Conference Bridge Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, }
static const char app [] = "ConfBridge"
static struct ast_app_option app_opts [128] = { [ 'A' ] = { .flag = OPTION_MARKEDUSER }, [ 'a' ] = { .flag = OPTION_ADMIN }, [ 'c' ] = { .flag = OPTION_ANNOUNCEUSERCOUNT }, [ 'm' ] = { .flag = OPTION_STARTMUTED }, [ 'M' ] = { .flag = OPTION_MUSICONHOLD , .arg_index = OPTION_MUSICONHOLD_CLASS + 1 }, [ '1' ] = { .flag = OPTION_NOONLYPERSON }, [ 's' ] = { .flag = OPTION_MENU }, [ 'w' ] = { .flag = OPTION_WAITMARKED }, [ 'q' ] = { .flag = OPTION_QUIET },}
static struct ast_module_infoast_module_info = &__mod_info
static struct ao2_containerconference_bridges
 Container to hold all conference bridges in progress.


Detailed Description

Conference Bridge application.

Author:
Joshua Colp <jcolp@digium.com> 
This is a conference bridge application utilizing the bridging core.

Definition in file app_confbridge.c.


Define Documentation

#define CONFERENCE_BRIDGE_BUCKETS   53

Definition at line 155 of file app_confbridge.c.

Referenced by load_module().

#define MAX_CONF_NAME   32

Definition at line 152 of file app_confbridge.c.


Enumeration Type Documentation

anonymous enum

Enumerator:
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

Enumerator:
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 };


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 854 of file app_confbridge.c.

static void __unreg_module ( void   )  [static]

Definition at line 854 of file app_confbridge.c.

static int announce_user_count ( struct conference_bridge conference_bridge,
struct conference_bridge_user conference_bridge_user 
) [static]

Announce number of users in the conference bridge to the caller.

Parameters:
conference_bridge Conference bridge to peek at
conference_bridge_user Caller
Returns:
Returns 0 on success, -1 if the user hung up

Definition at line 207 of file app_confbridge.c.

References ast_say_number(), ast_stream_and_wait(), conference_bridge_user::chan, ast_channel::language, and conference_bridge::users.

Referenced by post_join_unmarked().

00208 {
00209    if (conference_bridge->users == 1) {
00210       /* Awww we are the only person in the conference bridge */
00211       return 0;
00212    } else if (conference_bridge->users == 2) {
00213       /* Eep, there is one other person */
00214       if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
00215          return -1;
00216       }
00217    } else {
00218       /* Alas multiple others in here */
00219       if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
00220          return -1;
00221       }
00222       if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
00223          return -1;
00224       }
00225       if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
00226          return -1;
00227       }
00228    }
00229    return 0;
00230 }

static int confbridge_exec ( struct ast_channel chan,
const char *  data 
) [static]

The ConfBridge application.

Definition at line 723 of file app_confbridge.c.

References app_opts, args, AST_APP_ARG, ast_app_parse_options(), AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_volume_get(), ast_audiohook_volume_set(), ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_features_cleanup(), ast_bridge_features_hook(), ast_bridge_features_init(), ast_bridge_join(), ast_channel_lock, ast_channel_unlock, AST_DECLARE_APP_ARGS, ast_log(), AST_STANDARD_APP_ARGS, ast_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridge_user::features, conference_bridge_user::flags, join_conference_bridge(), conference_bridge_user::kicked, leave_conference_bridge(), LOG_WARNING, menu_callback(), ast_bridge_features::mute, conference_bridge_user::opt_args, OPTION_MENU, OPTION_QUIET, OPTION_STARTMUTED, parse(), pbx_builtin_getvar_helper(), play_sound_file(), and conference_bridge::users.

Referenced by load_module().

00724 {
00725    int res = 0, volume_adjustments[2];
00726    char *parse;
00727    struct conference_bridge *conference_bridge = NULL;
00728    struct conference_bridge_user conference_bridge_user = {
00729       .chan = chan,
00730    };
00731    const char *tmp, *join_sound = NULL, *leave_sound = NULL;
00732    AST_DECLARE_APP_ARGS(args,
00733       AST_APP_ARG(conf_name);
00734       AST_APP_ARG(options);
00735    );
00736 
00737    if (ast_strlen_zero(data)) {
00738       ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
00739       return -1;
00740    }
00741 
00742    /* We need to make a copy of the input string if we are going to modify it! */
00743    parse = ast_strdupa(data);
00744 
00745    AST_STANDARD_APP_ARGS(args, parse);
00746 
00747    if (args.argc == 2) {
00748       ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
00749    }
00750 
00751    /* Look for a conference bridge matching the provided name */
00752    if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
00753       return -1;
00754    }
00755 
00756    /* Keep a copy of volume adjustments so we can restore them later if need be */
00757    volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
00758    volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
00759 
00760    /* Always initialize the features structure, we are in most cases always going to need it. */
00761    ast_bridge_features_init(&conference_bridge_user.features);
00762 
00763    /* If the menu option is enabled provide a user or admin menu as a custom feature hook */
00764    if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
00765       ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
00766    }
00767 
00768    /* If the caller should be joined already muted, make it so */
00769    if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
00770       conference_bridge_user.features.mute = 1;
00771    }
00772 
00773    /* Grab join/leave sounds from the channel */
00774    ast_channel_lock(chan);
00775    if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
00776       join_sound = ast_strdupa(tmp);
00777    }
00778    if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
00779       leave_sound = ast_strdupa(tmp);
00780    }
00781    ast_channel_unlock(chan);
00782 
00783    /* If there is 1 or more people already in the conference then play our join sound unless overridden */
00784    if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
00785       ast_autoservice_start(chan);
00786       play_sound_file(conference_bridge, join_sound);
00787       ast_autoservice_stop(chan);
00788    }
00789 
00790    /* Join our conference bridge for real */
00791    ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
00792 
00793    /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */
00794    if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
00795       ast_autoservice_start(chan);
00796       play_sound_file(conference_bridge, leave_sound);
00797       ast_autoservice_stop(chan);
00798    }
00799 
00800    /* Easy as pie, depart this channel from the conference bridge */
00801    leave_conference_bridge(conference_bridge, &conference_bridge_user);
00802    conference_bridge = NULL;
00803 
00804    /* Can't forget to clean up the features structure, or else we risk a memory leak */
00805    ast_bridge_features_cleanup(&conference_bridge_user.features);
00806 
00807    /* If the user was kicked from the conference play back the audio prompt for it */
00808    if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
00809       res = ast_stream_and_wait(chan, "conf-kicked", "");
00810    }
00811 
00812    /* Restore volume adjustments to previous values in case they were changed */
00813    if (volume_adjustments[0]) {
00814       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
00815    }
00816    if (volume_adjustments[1]) {
00817       ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
00818    }
00819 
00820    return res;
00821 }

static int conference_bridge_cmp_cb ( void *  obj,
void *  arg,
int  flags 
) [static]

Comparison function used for conference bridges container.

Definition at line 193 of file app_confbridge.c.

References CMP_MATCH, CMP_STOP, and conference_bridge::name.

Referenced by load_module().

00194 {
00195    const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
00196    return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
00197 }

static int conference_bridge_hash_cb ( const void *  obj,
const int  flags 
) [static]

Hashing function used for conference bridges container.

Definition at line 186 of file app_confbridge.c.

References ast_str_case_hash(), and conference_bridge::name.

Referenced by load_module().

00187 {
00188    const struct conference_bridge *conference_bridge = obj;
00189    return ast_str_case_hash(conference_bridge->name);
00190 }

static void destroy_conference_bridge ( void *  obj  )  [static]

Destroy a conference bridge.

Parameters:
obj The conference bridge object
Returns:
Returns nothing

Definition at line 382 of file app_confbridge.c.

References ast_bridge_destroy(), ast_debug, ast_hangup(), ast_mutex_destroy, conference_bridge::bridge, ast_channel_tech::bridged_channel, conference_bridge::name, conference_bridge::playback_chan, conference_bridge::playback_lock, and ast_channel::tech.

Referenced by join_conference_bridge().

00383 {
00384    struct conference_bridge *conference_bridge = obj;
00385 
00386    ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
00387 
00388    ast_mutex_destroy(&conference_bridge->playback_lock);
00389 
00390    if (conference_bridge->playback_chan) {
00391       struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00392       ast_hangup(underlying_channel);
00393       ast_hangup(conference_bridge->playback_chan);
00394       conference_bridge->playback_chan = NULL;
00395    }
00396 
00397    /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
00398    if (conference_bridge->bridge) {
00399       ast_bridge_destroy(conference_bridge->bridge);
00400       conference_bridge->bridge = NULL;
00401    }
00402 }

static struct conference_bridge* join_conference_bridge ( const char *  name,
struct conference_bridge_user conference_bridge_user 
) [static]

Join a conference bridge.

Parameters:
name The conference name
conference_bridge_user Conference bridge user structure
Returns:
A pointer to the conference bridge struct, or NULL if the conference room wasn't found.

Definition at line 414 of file app_confbridge.c.

References ao2_alloc, ao2_find, ao2_link, ao2_lock, ao2_ref, ao2_unlock, AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART, ast_bridge_new(), ast_copy_string(), ast_debug, AST_DEVICE_INUSE, ast_devstate_changed(), AST_LIST_INSERT_TAIL, ast_log(), ast_mutex_init, ast_stream_and_wait(), ast_test_flag, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridges, destroy_conference_bridge(), conference_bridge_user::flags, leave_conference_bridge(), conference_bridge::locked, LOG_ERROR, conference_bridge::markedusers, conference_bridge::name, OBJ_POINTER, OPTION_ADMIN, OPTION_MARKEDUSER, OPTION_WAITMARKED, post_join_marked(), post_join_unmarked(), conference_bridge::users, and conference_bridge::users_list.

Referenced by confbridge_exec().

00415 {
00416    struct conference_bridge *conference_bridge = NULL;
00417    struct conference_bridge tmp;
00418 
00419    ast_copy_string(tmp.name, name, sizeof(tmp.name));
00420 
00421    /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
00422    ao2_lock(conference_bridges);
00423 
00424    ast_debug(1, "Trying to find conference bridge '%s'\n", name);
00425 
00426    /* Attempt to find an existing conference bridge */
00427    conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
00428 
00429    /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
00430    if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
00431       ao2_unlock(conference_bridges);
00432       ao2_ref(conference_bridge, -1);
00433       ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
00434       ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
00435       return NULL;
00436    }
00437 
00438    /* If no conference bridge was found see if we can create one */
00439    if (!conference_bridge) {
00440       /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
00441       if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
00442          ao2_unlock(conference_bridges);
00443          ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
00444          return NULL;
00445       }
00446 
00447       /* Setup conference bridge parameters */
00448       ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
00449 
00450       /* Create an actual bridge that will do the audio mixing */
00451       if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
00452          ao2_ref(conference_bridge, -1);
00453          conference_bridge = NULL;
00454          ao2_unlock(conference_bridges);
00455          ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
00456          return NULL;
00457       }
00458 
00459       /* Setup lock for playback channel */
00460       ast_mutex_init(&conference_bridge->playback_lock);
00461 
00462       /* Link it into the conference bridges container */
00463       ao2_link(conference_bridges, conference_bridge);
00464 
00465       ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
00466    }
00467 
00468    ao2_unlock(conference_bridges);
00469 
00470    /* Setup conference bridge user parameters */
00471    conference_bridge_user->conference_bridge = conference_bridge;
00472 
00473    ao2_lock(conference_bridge);
00474 
00475    /* All good to go, add them in */
00476    AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
00477 
00478    /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
00479    conference_bridge->users++;
00480 
00481    /* If the caller is a marked user bump up the count */
00482    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00483       conference_bridge->markedusers++;
00484    }
00485 
00486    /* Set the device state for this conference */
00487    if (conference_bridge->users == 1) {
00488       ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
00489    }
00490 
00491    /* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
00492    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
00493       if (post_join_marked(conference_bridge, conference_bridge_user)) {
00494          ao2_unlock(conference_bridge);
00495          leave_conference_bridge(conference_bridge, conference_bridge_user);
00496          return NULL;
00497       }
00498    } else {
00499       if (post_join_unmarked(conference_bridge, conference_bridge_user)) {
00500          ao2_unlock(conference_bridge);
00501          leave_conference_bridge(conference_bridge, conference_bridge_user);
00502          return NULL;
00503       }
00504    }
00505 
00506    ao2_unlock(conference_bridge);
00507 
00508    return conference_bridge;
00509 }

static void leave_conference_bridge ( struct conference_bridge conference_bridge,
struct conference_bridge_user conference_bridge_user 
) [static]

Leave a conference bridge.

Parameters:
conference_bridge The conference bridge to leave
conference_bridge_user The conference bridge user structure

Definition at line 518 of file app_confbridge.c.

References ao2_lock, ao2_ref, ao2_unlink, ao2_unlock, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_suspend(), ast_bridge_unsuspend(), AST_DEVICE_NOT_INUSE, ast_devstate_changed(), AST_LIST_FIRST, AST_LIST_REMOVE, AST_LIST_TRAVERSE, ast_moh_start(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridges, conference_bridge_user::features, conference_bridge_user::flags, conference_bridge_user::list, conference_bridge::markedusers, ast_bridge_features::mute, conference_bridge::name, conference_bridge_user::opt_args, OPTION_MARKEDUSER, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_QUIET, play_sound_file(), conference_bridge::users, and conference_bridge::users_list.

Referenced by confbridge_exec(), and join_conference_bridge().

00519 {
00520    ao2_lock(conference_bridge);
00521 
00522    /* If this caller is a marked user bump down the count */
00523    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00524       conference_bridge->markedusers--;
00525    }
00526 
00527    /* Decrement the users count while keeping the previous participant count */
00528    conference_bridge->users--;
00529 
00530    /* Drop conference bridge user from the list, they be going bye bye */
00531    AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
00532 
00533    /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
00534    if (conference_bridge->users) {
00535       if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
00536          struct conference_bridge_user *other_participant = NULL;
00537 
00538          /* Start out with muting everyone */
00539          AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00540             other_participant->features.mute = 1;
00541          }
00542 
00543          /* Play back the audio prompt saying the leader has left the conference */
00544          if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00545             ao2_unlock(conference_bridge);
00546             ast_autoservice_start(conference_bridge_user->chan);
00547             play_sound_file(conference_bridge, "conf-leaderhasleft");
00548             ast_autoservice_stop(conference_bridge_user->chan);
00549             ao2_lock(conference_bridge);
00550          }
00551 
00552          /* Now on to starting MOH if needed */
00553          AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00554             if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
00555                ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00556                ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
00557             }
00558          }
00559       } else if (conference_bridge->users == 1) {
00560          /* Of course if there is one other person in here we may need to start up MOH on them */
00561          struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00562 
00563          if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00564             ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00565             ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00566          }
00567       }
00568    } else {
00569       /* Set device state to "not in use" */
00570       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
00571 
00572       ao2_unlink(conference_bridges, conference_bridge);
00573    }
00574 
00575    /* Done mucking with the conference bridge, huzzah */
00576    ao2_unlock(conference_bridge);
00577 
00578    ao2_ref(conference_bridge, -1);
00579 }

static int load_module ( void   )  [static]

Called when module is being loaded.

Definition at line 835 of file app_confbridge.c.

References ao2_container_alloc, ao2_ref, AST_MODULE_LOAD_DECLINE, AST_MODULE_LOAD_SUCCESS, ast_register_application_xml, confbridge_exec(), CONFERENCE_BRIDGE_BUCKETS, conference_bridge_cmp_cb(), conference_bridge_hash_cb(), and conference_bridges.

00836 {
00837    /* Create a container to hold the conference bridges */
00838    if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
00839       return AST_MODULE_LOAD_DECLINE;
00840    }
00841 
00842    if (ast_register_application_xml(app, confbridge_exec)) {
00843       ao2_ref(conference_bridges, -1);
00844       return AST_MODULE_LOAD_DECLINE;
00845    }
00846 
00847    return AST_MODULE_LOAD_SUCCESS;
00848 }

static int menu_callback ( struct ast_bridge bridge,
struct ast_bridge_channel bridge_channel,
void *  hook_pvt 
) [static]

DTMF Menu Callback.

Parameters:
bridge Bridge this is involving
bridge_channel Bridged channel this is involving
hook_pvt User's conference bridge structure
Return values:
0 success
-1 failure

Definition at line 643 of file app_confbridge.c.

References ao2_lock, ao2_unlock, AST_AUDIOHOOK_DIRECTION_READ, AST_AUDIOHOOK_DIRECTION_WRITE, ast_audiohook_volume_adjust(), AST_BRIDGE_CHANNEL_STATE_WAIT, ast_bridge_remove(), AST_DIGIT_ANY, AST_LIST_LAST, ast_moh_start(), ast_moh_stop(), ast_stopstream(), ast_stream_and_wait(), ast_streamfile(), ast_test_flag, ast_waitstream(), conference_bridge::bridge, conference_bridge_user::chan, ast_bridge_channel::chan, conference_bridge_user::conference_bridge, conference_bridge_user::features, conference_bridge_user::flags, conference_bridge_user::kicked, ast_channel::language, conference_bridge::locked, conference_bridge::markedusers, ast_bridge_features::mute, conference_bridge_user::opt_args, OPTION_ADMIN, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_WAITMARKED, ast_bridge_channel::state, conference_bridge::users, and conference_bridge::users_list.

Referenced by confbridge_exec().

00644 {
00645    struct conference_bridge_user *conference_bridge_user = hook_pvt;
00646    struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
00647    int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
00648 
00649    /* See if music on hold is playing */
00650    ao2_lock(conference_bridge);
00651    if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00652       /* Just us so MOH is probably indeed going, let's stop it */
00653       ast_moh_stop(bridge_channel->chan);
00654    }
00655    ao2_unlock(conference_bridge);
00656 
00657    /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
00658    if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
00659       res = -1;
00660       goto finished;
00661    }
00662 
00663    /* Wait for them to enter a digit from the user menu options */
00664    digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
00665    ast_stopstream(bridge_channel->chan);
00666 
00667    if (digit == '1') {
00668       /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
00669       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
00670          conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
00671       }
00672       res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
00673    } else if (isadmin && digit == '2') {
00674       /* 2 - Unlock or lock conference */
00675       conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
00676       res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
00677    } else if (isadmin && digit == '3') {
00678       /* 3 - Eject last user */
00679       struct conference_bridge_user *last_participant = NULL;
00680 
00681       ao2_lock(conference_bridge);
00682       if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
00683          ao2_unlock(conference_bridge);
00684          res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00685       } else {
00686          last_participant->kicked = 1;
00687          ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
00688          ao2_unlock(conference_bridge);
00689       }
00690    } else if (digit == '4') {
00691       /* 4 - Decrease listening volume */
00692       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
00693    } else if (digit == '6') {
00694       /* 6 - Increase listening volume */
00695       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
00696    } else if (digit == '7') {
00697       /* 7 - Decrease talking volume */
00698       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
00699    } else if (digit == '8') {
00700       /* 8 - Exit the IVR */
00701    } else if (digit == '9') {
00702       /* 9 - Increase talking volume */
00703       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
00704    } else {
00705       /* No valid option was selected */
00706       res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00707    }
00708 
00709  finished:
00710    /* See if music on hold needs to be started back up again */
00711    ao2_lock(conference_bridge);
00712    if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00713       ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00714    }
00715    ao2_unlock(conference_bridge);
00716 
00717    bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
00718 
00719    return res;
00720 }

static int play_prompt_to_channel ( struct conference_bridge conference_bridge,
struct ast_channel chan,
const char *  file 
) [static]

Play back an audio file to a channel.

Parameters:
conference_bridge Conference bridge they are in
chan Channel to play audio prompt to
file Prompt to play
Returns:
Returns 0 on success, -1 if the user hung up
Note:
This function assumes that conference_bridge is locked

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.

Parameters:
conference_bridge The conference bridge to play sound file into
filename Sound file to play
Return values:
0 success
-1 failure

Definition at line 590 of file app_confbridge.c.

References ast_bridge_depart(), ast_bridge_impart(), ast_call(), ast_debug, AST_FORMAT_SLINEAR, ast_hangup(), ast_mutex_lock, ast_mutex_unlock, ast_request(), ast_stream_and_wait(), conference_bridge::bridge, ast_channel::bridge, ast_channel_tech::bridged_channel, cause, ast_channel::name, conference_bridge::name, conference_bridge::playback_chan, conference_bridge::playback_lock, and ast_channel::tech.

Referenced by confbridge_exec(), leave_conference_bridge(), and post_join_marked().

00591 {
00592    struct ast_channel *underlying_channel;
00593 
00594    ast_mutex_lock(&conference_bridge->playback_lock);
00595 
00596    if (!(conference_bridge->playback_chan)) {
00597       int cause;
00598 
00599       if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) {
00600          ast_mutex_unlock(&conference_bridge->playback_lock);
00601          return -1;
00602       }
00603 
00604       conference_bridge->playback_chan->bridge = conference_bridge->bridge;
00605 
00606       if (ast_call(conference_bridge->playback_chan, "", 0)) {
00607          ast_hangup(conference_bridge->playback_chan);
00608          conference_bridge->playback_chan = NULL;
00609          ast_mutex_unlock(&conference_bridge->playback_lock);
00610          return -1;
00611       }
00612 
00613       ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
00614 
00615       underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00616    } else {
00617       /* Channel was already available so we just need to add it back into the bridge */
00618       underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00619       ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
00620    }
00621 
00622    /* The channel is all under our control, in goes the prompt */
00623    ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
00624 
00625    ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
00626    ast_bridge_depart(conference_bridge->bridge, underlying_channel);
00627 
00628    ast_mutex_unlock(&conference_bridge->playback_lock);
00629 
00630    return 0;
00631 }

static int post_join_marked ( struct conference_bridge conference_bridge,
struct conference_bridge_user conference_bridge_user 
) [static]

Perform post-joining marked specific actions.

Parameters:
conference_bridge Conference bridge being joined
conference_bridge_user Conference bridge user joining
Returns:
Returns 0 on success, -1 if the user hung up

Definition at line 260 of file app_confbridge.c.

References ao2_lock, ao2_unlock, ast_autoservice_start(), ast_autoservice_stop(), ast_bridge_suspend(), ast_bridge_unsuspend(), AST_LIST_TRAVERSE, ast_moh_start(), ast_moh_stop(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridge_user::features, conference_bridge_user::flags, conference_bridge_user::list, conference_bridge::markedusers, ast_bridge_features::mute, conference_bridge_user::opt_args, OPTION_MARKEDUSER, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_QUIET, play_prompt_to_channel(), play_sound_file(), and conference_bridge::users_list.

Referenced by join_conference_bridge().

00261 {
00262    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00263       struct conference_bridge_user *other_conference_bridge_user = NULL;
00264 
00265       /* If we are not the first marked user to join just bail out now */
00266       if (conference_bridge->markedusers >= 2) {
00267          return 0;
00268       }
00269 
00270       /* Iterate through every participant stopping MOH on them if need be */
00271       AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00272          if (other_conference_bridge_user == conference_bridge_user) {
00273             continue;
00274          }
00275          if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
00276             ast_moh_stop(other_conference_bridge_user->chan);
00277             ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
00278          }
00279       }
00280 
00281       /* Next play the audio file stating they are going to be placed into the conference */
00282       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00283          ao2_unlock(conference_bridge);
00284          ast_autoservice_start(conference_bridge_user->chan);
00285          play_sound_file(conference_bridge, "conf-placeintoconf");
00286          ast_autoservice_stop(conference_bridge_user->chan);
00287          ao2_lock(conference_bridge);
00288       }
00289 
00290       /* Finally iterate through and unmute them all */
00291       AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
00292          if (other_conference_bridge_user == conference_bridge_user) {
00293             continue;
00294          }
00295          other_conference_bridge_user->features.mute = 0;
00296       }
00297 
00298    } else {
00299       /* If a marked user already exists in the conference bridge we can just bail out now */
00300       if (conference_bridge->markedusers) {
00301          return 0;
00302       }
00303       /* Be sure we are muted so we can't talk to anybody else waiting */
00304       conference_bridge_user->features.mute = 1;
00305       /* If we have not been quieted play back that they are waiting for the leader */
00306       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00307          if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader")) {
00308             /* user hung up while the sound was playing */
00309             return -1;
00310          }
00311       }
00312       /* Start music on hold if needed */
00313       /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
00314        * allowing a marked user to enter while the prompt was playing
00315        */
00316       if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00317          ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00318       }
00319    }
00320    return 0;
00321 }

static int post_join_unmarked ( struct conference_bridge conference_bridge,
struct conference_bridge_user conference_bridge_user 
) [static]

Perform post-joining non-marked specific actions.

Parameters:
conference_bridge Conference bridge being joined
conference_bridge_user Conference bridge user joining
Returns:
Returns 0 on success, -1 if the user hung up

Definition at line 331 of file app_confbridge.c.

References announce_user_count(), ao2_lock, ao2_unlock, ast_bridge_suspend(), ast_bridge_unsuspend(), AST_LIST_FIRST, ast_moh_start(), ast_moh_stop(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridge_user::flags, conference_bridge_user::opt_args, OPTION_ANNOUNCEUSERCOUNT, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_NOONLYPERSON, OPTION_QUIET, play_prompt_to_channel(), conference_bridge::users, and conference_bridge::users_list.

Referenced by join_conference_bridge().

00332 {
00333    /* Play back audio prompt and start MOH if need be if we are the first participant */
00334    if (conference_bridge->users == 1) {
00335       /* If audio prompts have not been quieted or this prompt quieted play it on out */
00336       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
00337          if (play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson")) {
00338             /* user hung up while the sound was playing */
00339             return -1;
00340          }
00341       }
00342       /* If we need to start music on hold on the channel do so now */
00343       /* We need to re-check the number of users in the conference bridge here because another conference bridge
00344        * participant could have joined while the above prompt was playing for the first user.
00345        */
00346       if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00347          ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00348       }
00349       return 0;
00350    }
00351 
00352    /* Announce number of users if need be */
00353    if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
00354       ao2_unlock(conference_bridge);
00355       if (announce_user_count(conference_bridge, conference_bridge_user)) {
00356          ao2_lock(conference_bridge);
00357          return -1;
00358       }
00359       ao2_lock(conference_bridge);
00360    }
00361 
00362    /* If we are the second participant we may need to stop music on hold on the first */
00363    if (conference_bridge->users == 2) {
00364       struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00365 
00366       /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
00367       if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00368          ast_moh_stop(first_participant->chan);
00369          ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00370       }
00371    }
00372    return 0;
00373 }

static int unload_module ( void   )  [static]

Called when module is being unloaded.

Definition at line 824 of file app_confbridge.c.

References ao2_ref, ast_unregister_application(), and conference_bridges.

00825 {
00826    int res = ast_unregister_application(app);
00827 
00828    /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
00829    ao2_ref(conference_bridges, -1);
00830 
00831    return res;
00832 }


Variable Documentation

struct ast_module_info __mod_info = { .name = AST_MODULE, .flags = AST_MODFLAG_LOAD_ORDER , .description = "Conference Bridge Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "88eaa8f5c1bd988bedd71113385e0886" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, } [static]

Definition at line 854 of file app_confbridge.c.

const char app[] = "ConfBridge" [static]

Playing back a file to a channel in a conference
You might notice in this application that while playing a sound file to a channel the actual conference bridge lock is not held. This is done so that other channels are not blocked from interacting with the conference bridge. Unfortunately because of this it is possible for things to change after the sound file is done being played. Data must therefore be checked after reacquiring the conference bridge lock if it is important.

Definition at line 119 of file app_confbridge.c.

struct ast_app_option app_opts[128] = { [ 'A' ] = { .flag = OPTION_MARKEDUSER }, [ 'a' ] = { .flag = OPTION_ADMIN }, [ 'c' ] = { .flag = OPTION_ANNOUNCEUSERCOUNT }, [ 'm' ] = { .flag = OPTION_STARTMUTED }, [ 'M' ] = { .flag = OPTION_MUSICONHOLD , .arg_index = OPTION_MUSICONHOLD_CLASS + 1 }, [ '1' ] = { .flag = OPTION_NOONLYPERSON }, [ 's' ] = { .flag = OPTION_MENU }, [ 'w' ] = { .flag = OPTION_WAITMARKED }, [ 'q' ] = { .flag = OPTION_QUIET },} [static]

Definition at line 149 of file app_confbridge.c.

Referenced by app_exec(), confbridge_exec(), disa_exec(), record_exec(), sendurl_exec(), and softhangup_exec().

struct ast_module_info* ast_module_info = &__mod_info [static]

Definition at line 854 of file app_confbridge.c.

struct ao2_container* conference_bridges [static]

Container to hold all conference bridges in progress.

Definition at line 181 of file app_confbridge.c.

Referenced by join_conference_bridge(), leave_conference_bridge(), load_module(), and unload_module().


Generated on Mon Mar 19 11:30:31 2012 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7