Mon Jun 27 16:50:58 2011

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 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, 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 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_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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .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 151 of file app_confbridge.c.

Referenced by load_module().

#define MAX_CONF_NAME   32

Definition at line 148 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 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

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


Function Documentation

static void __reg_module ( void   )  [static]

Definition at line 826 of file app_confbridge.c.

static void __unreg_module ( void   )  [static]

Definition at line 826 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.

Parameters:
conference_bridge Conference bridge to peek at
conference_bridge_user Caller
Returns:
Returns nothing

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,
const char *  data 
) [static]

The ConfBridge application.

Definition at line 695 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().

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

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.

Parameters:
obj The conference bridge object
Returns:
Returns nothing

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]

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 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_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, 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().

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    /* Set the device state for this conference */
00467    if (conference_bridge->users == 1) {
00468       ast_devstate_changed(AST_DEVICE_INUSE, "confbridge:%s", conference_bridge->name);
00469    }
00470 
00471    /* 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 */
00472    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
00473       post_join_marked(conference_bridge, conference_bridge_user);
00474    } else {
00475       post_join_unmarked(conference_bridge, conference_bridge_user);
00476    }
00477 
00478    ao2_unlock(conference_bridge);
00479 
00480    return conference_bridge;
00481 }

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 490 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().

00491 {
00492    ao2_lock(conference_bridge);
00493 
00494    /* If this caller is a marked user bump down the count */
00495    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00496       conference_bridge->markedusers--;
00497    }
00498 
00499    /* Decrement the users count while keeping the previous participant count */
00500    conference_bridge->users--;
00501 
00502    /* Drop conference bridge user from the list, they be going bye bye */
00503    AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);
00504 
00505    /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
00506    if (conference_bridge->users) {
00507       if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
00508          struct conference_bridge_user *other_participant = NULL;
00509 
00510          /* Start out with muting everyone */
00511          AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00512             other_participant->features.mute = 1;
00513          }
00514 
00515          /* Play back the audio prompt saying the leader has left the conference */
00516          if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
00517             ao2_unlock(conference_bridge);
00518             ast_autoservice_start(conference_bridge_user->chan);
00519             play_sound_file(conference_bridge, "conf-leaderhasleft");
00520             ast_autoservice_stop(conference_bridge_user->chan);
00521             ao2_lock(conference_bridge);
00522          }
00523 
00524          /* Now on to starting MOH if needed */
00525          AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
00526             if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
00527                ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00528                ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
00529             }
00530          }
00531       } else if (conference_bridge->users == 1) {
00532          /* Of course if there is one other person in here we may need to start up MOH on them */
00533          struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);
00534 
00535          if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
00536             ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00537             ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
00538          }
00539       }
00540    } else {
00541       /* Set device state to "not in use" */
00542       ast_devstate_changed(AST_DEVICE_NOT_INUSE, "confbridge:%s", conference_bridge->name);
00543 
00544       ao2_unlink(conference_bridges, conference_bridge);
00545    }
00546 
00547    /* Done mucking with the conference bridge, huzzah */
00548    ao2_unlock(conference_bridge);
00549 
00550    ao2_ref(conference_bridge, -1);
00551 }

static int load_module ( void   )  [static]

Called when module is being loaded.

Definition at line 807 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.

00808 {
00809    /* Create a container to hold the conference bridges */
00810    if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
00811       return AST_MODULE_LOAD_DECLINE;
00812    }
00813 
00814    if (ast_register_application_xml(app, confbridge_exec)) {
00815       ao2_ref(conference_bridges, -1);
00816       return AST_MODULE_LOAD_DECLINE;
00817    }
00818 
00819    return AST_MODULE_LOAD_SUCCESS;
00820 }

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 615 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().

00616 {
00617    struct conference_bridge_user *conference_bridge_user = hook_pvt;
00618    struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
00619    int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
00620 
00621    /* See if music on hold is playing */
00622    ao2_lock(conference_bridge);
00623    if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00624       /* Just us so MOH is probably indeed going, let's stop it */
00625       ast_moh_stop(bridge_channel->chan);
00626    }
00627    ao2_unlock(conference_bridge);
00628 
00629    /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
00630    if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
00631       res = -1;
00632       goto finished;
00633    }
00634 
00635    /* Wait for them to enter a digit from the user menu options */
00636    digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
00637    ast_stopstream(bridge_channel->chan);
00638 
00639    if (digit == '1') {
00640       /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
00641       if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
00642          conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
00643       }
00644       res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
00645    } else if (isadmin && digit == '2') {
00646       /* 2 - Unlock or lock conference */
00647       conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
00648       res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
00649    } else if (isadmin && digit == '3') {
00650       /* 3 - Eject last user */
00651       struct conference_bridge_user *last_participant = NULL;
00652 
00653       ao2_lock(conference_bridge);
00654       if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
00655          ao2_unlock(conference_bridge);
00656          res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00657       } else {
00658          last_participant->kicked = 1;
00659          ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
00660          ao2_unlock(conference_bridge);
00661       }
00662    } else if (digit == '4') {
00663       /* 4 - Decrease listening volume */
00664       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
00665    } else if (digit == '6') {
00666       /* 6 - Increase listening volume */
00667       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
00668    } else if (digit == '7') {
00669       /* 7 - Decrease talking volume */
00670       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
00671    } else if (digit == '8') {
00672       /* 8 - Exit the IVR */
00673    } else if (digit == '9') {
00674       /* 9 - Increase talking volume */
00675       ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
00676    } else {
00677       /* No valid option was selected */
00678       res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
00679    }
00680 
00681  finished:
00682    /* See if music on hold needs to be started back up again */
00683    ao2_lock(conference_bridge);
00684    if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
00685       ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
00686    }
00687    ao2_unlock(conference_bridge);
00688 
00689    bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
00690 
00691    return res;
00692 }

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.

Parameters:
conference_bridge Conference bridge they are in
chan Channel to play audio prompt to
file Prompt to play
Returns:
Returns nothing
Note:
This function assumes that conference_bridge is locked

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.

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 562 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().

00563 {
00564    struct ast_channel *underlying_channel;
00565 
00566    ast_mutex_lock(&conference_bridge->playback_lock);
00567 
00568    if (!(conference_bridge->playback_chan)) {
00569       int cause;
00570 
00571       if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, NULL, "", &cause))) {
00572          ast_mutex_unlock(&conference_bridge->playback_lock);
00573          return -1;
00574       }
00575 
00576       conference_bridge->playback_chan->bridge = conference_bridge->bridge;
00577 
00578       if (ast_call(conference_bridge->playback_chan, "", 0)) {
00579          ast_hangup(conference_bridge->playback_chan);
00580          conference_bridge->playback_chan = NULL;
00581          ast_mutex_unlock(&conference_bridge->playback_lock);
00582          return -1;
00583       }
00584 
00585       ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
00586 
00587       underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00588    } else {
00589       /* Channel was already available so we just need to add it back into the bridge */
00590       underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
00591       ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
00592    }
00593 
00594    /* The channel is all under our control, in goes the prompt */
00595    ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
00596 
00597    ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
00598    ast_bridge_depart(conference_bridge->bridge, underlying_channel);
00599 
00600    ast_mutex_unlock(&conference_bridge->playback_lock);
00601 
00602    return 0;
00603 }

static void 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 nothing

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::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().

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.

Parameters:
conference_bridge Conference bridge being joined
conference_bridge_user Conference bridge user joining
Returns:
Returns nothing

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::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().

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 796 of file app_confbridge.c.

References ao2_ref, ast_unregister_application(), and conference_bridges.

00797 {
00798    int res = ast_unregister_application(app);
00799 
00800    /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
00801    ao2_ref(conference_bridges, -1);
00802 
00803    return res;
00804 }


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 = "8586c2a7d357cb591cc3a6607a8f62d1" , .load = load_module, .unload = unload_module, .load_pri = AST_MODPRI_DEVSTATE_PROVIDER, } [static]

Definition at line 826 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 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 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 826 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().


Generated on Mon Jun 27 16:50:58 2011 for Asterisk - The Open Source Telephony Project by  doxygen 1.4.7