Fri Nov 12 11:48:32 2010

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"
Include dependency graph for app_confbridge.c:

Go to the source code of this file.

Data Structures

struct  conference_bridge
 The structure that represents a conference bridge. More...
struct  conference_bridge_user
 The structure that represents a conference bridge user. More...

Defines

#define CONFERENCE_BRIDGE_BUCKETS   53
#define MAX_CONF_NAME   32

Enumerations

enum  {
  OPTION_ADMIN = (1 << 0), OPTION_MENU = (1 << 1), OPTION_MUSICONHOLD = (1 << 2), OPTION_NOONLYPERSON = (1 << 3),
  OPTION_STARTMUTED = (1 << 4), OPTION_ANNOUNCEUSERCOUNT = (1 << 5), OPTION_MARKEDUSER = (1 << 6), OPTION_WAITMARKED = (1 << 7),
  OPTION_QUIET = (1 << 8)
}
enum  { OPTION_MUSICONHOLD_CLASS, OPTION_ARRAY_SIZE }

Functions

static void __reg_module (void)
static void __unreg_module (void)
static void announce_user_count (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Announce number of users in the conference bridge to the caller.
static int confbridge_exec (struct ast_channel *chan, void *data)
 The ConfBridge application.
static int conference_bridge_cmp_cb (void *obj, void *arg, int flags)
 Comparison function used for conference bridges container.
static int conference_bridge_hash_cb (const void *obj, const int flags)
 Hashing function used for conference bridges container.
static void destroy_conference_bridge (void *obj)
 Destroy a conference bridge.
static struct conference_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_DEFAULT , .description = "Conference Bridge Application" , .key = "This paragraph is copyright (c) 2006 by Digium, Inc. \In order for your module to load, it must return this \key via a function called \"key\". Any code which \includes this paragraph must be licensed under the GNU \General Public License version 2 or later (at your \option). In addition to Digium's general reservations \of rights, Digium expressly reserves the right to \allow other parties to license this paragraph under \different terms. Any use of Digium, Inc. trademarks or \logos (including \"Asterisk\" or \"Digium\") without \express written permission of Digium, Inc. is prohibited.\n" , .buildopt_sum = "0901e4e500243c855563a2d78b0c03e4" , .load = load_module, .unload = unload_module, }
static const char * app = "ConfBridge"
static struct ast_app_option app_opts [128] = { [ 'A' ] = { .flag = OPTION_MARKEDUSER }, [ 'a' ] = { .flag = OPTION_ADMIN }, [ 'c' ] = { .flag = OPTION_ANNOUNCEUSERCOUNT }, [ 'm' ] = { .flag = OPTION_STARTMUTED }, [ 'M' ] = { .flag = OPTION_MUSICONHOLD , .arg_index = OPTION_MUSICONHOLD_CLASS + 1 }, [ '1' ] = { .flag = OPTION_NOONLYPERSON }, [ 's' ] = { .flag = OPTION_MENU }, [ 'w' ] = { .flag = OPTION_WAITMARKED }, [ 'q' ] = { .flag = OPTION_QUIET },}
static struct ast_module_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 814 of file app_confbridge.c.

static void __unreg_module ( void   )  [static]

Definition at line 814 of file app_confbridge.c.

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

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

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

The ConfBridge application.

Definition at line 687 of file app_confbridge.c.

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

Referenced by load_module().

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

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

Comparison function used for conference bridges container.

Definition at line 189 of file app_confbridge.c.

References CMP_MATCH, CMP_STOP, and conference_bridge::name.

Referenced by load_module().

00190 {
00191    const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
00192    return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
00193 }

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

Hashing function used for conference bridges container.

Definition at line 182 of file app_confbridge.c.

References ast_str_case_hash(), and conference_bridge::name.

Referenced by load_module().

00183 {
00184    const struct conference_bridge *conference_bridge = obj;
00185    return ast_str_case_hash(conference_bridge->name);
00186 }

static void destroy_conference_bridge ( void *  obj  )  [static]

Destroy a conference bridge.

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, read]

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_LIST_INSERT_TAIL, ast_log(), ast_mutex_init(), ast_stream_and_wait(), ast_test_flag, conference_bridge::bridge, conference_bridge_user::chan, conference_bridge_user::conference_bridge, conference_bridges, destroy_conference_bridge(), conference_bridge_user::flags, conference_bridge::locked, LOG_ERROR, conference_bridge::markedusers, conference_bridge::name, OBJ_POINTER, OPTION_ADMIN, OPTION_MARKEDUSER, OPTION_WAITMARKED, conference_bridge::playback_lock, post_join_marked(), post_join_unmarked(), conference_bridge::users, and conference_bridge::users_list.

Referenced by confbridge_exec().

00395 {
00396    struct conference_bridge *conference_bridge = NULL;
00397    struct conference_bridge tmp;
00398 
00399    ast_copy_string(tmp.name, name, sizeof(tmp.name));
00400 
00401    /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
00402    ao2_lock(conference_bridges);
00403 
00404    ast_debug(1, "Trying to find conference bridge '%s'\n", name);
00405 
00406    /* Attempt to find an existing conference bridge */
00407    conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);
00408 
00409    /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
00410    if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
00411       ao2_unlock(conference_bridges);
00412       ao2_ref(conference_bridge, -1);
00413       ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
00414       ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
00415       return NULL;
00416    }
00417 
00418    /* If no conference bridge was found see if we can create one */
00419    if (!conference_bridge) {
00420       /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
00421       if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
00422          ao2_unlock(conference_bridges);
00423          ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
00424          return NULL;
00425       }
00426 
00427       /* Setup conference bridge parameters */
00428       ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
00429 
00430       /* Create an actual bridge that will do the audio mixing */
00431       if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
00432          ao2_ref(conference_bridge, -1);
00433          conference_bridge = NULL;
00434          ao2_unlock(conference_bridges);
00435          ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
00436          return NULL;
00437       }
00438 
00439       /* Setup lock for playback channel */
00440       ast_mutex_init(&conference_bridge->playback_lock);
00441 
00442       /* Link it into the conference bridges container */
00443       ao2_link(conference_bridges, conference_bridge);
00444 
00445       ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
00446    }
00447 
00448    ao2_unlock(conference_bridges);
00449 
00450    /* Setup conference bridge user parameters */
00451    conference_bridge_user->conference_bridge = conference_bridge;
00452 
00453    ao2_lock(conference_bridge);
00454 
00455    /* All good to go, add them in */
00456    AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);
00457 
00458    /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
00459    conference_bridge->users++;
00460 
00461    /* If the caller is a marked user bump up the count */
00462    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
00463       conference_bridge->markedusers++;
00464    }
00465 
00466    /* If the caller is a marked user or is waiting for a marked user to enter pass 'em off, otherwise pass them off to do regular joining stuff */
00467    if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
00468       post_join_marked(conference_bridge, conference_bridge_user);
00469    } else {
00470       post_join_unmarked(conference_bridge, conference_bridge_user);
00471    }
00472 
00473    ao2_unlock(conference_bridge);
00474 
00475    return conference_bridge;
00476 }

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

Leave a conference bridge.

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

Definition at line 485 of file app_confbridge.c.

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

Referenced by confbridge_exec().

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

static int load_module ( void   )  [static]
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 607 of file app_confbridge.c.

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

Referenced by confbridge_exec().

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

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

Play back an audio file to a channel.

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

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

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

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

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

Perform post-joining marked specific actions.

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::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::flags, conference_bridge_user::opt_args, OPTION_ANNOUNCEUSERCOUNT, OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS, OPTION_NOONLYPERSON, OPTION_QUIET, play_prompt_to_channel(), conference_bridge::users, and conference_bridge::users_list.

Referenced by join_conference_bridge().

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

static int unload_module ( void   )  [static]

Called when module is being unloaded.

Definition at line 788 of file app_confbridge.c.

References ao2_ref, ast_unregister_application(), and conference_bridges.

00789 {
00790    int res = ast_unregister_application(app);
00791 
00792    /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
00793    ao2_ref(conference_bridges, -1);
00794 
00795    return res;
00796 }


Variable Documentation

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

Definition at line 814 of file app_confbridge.c.

const char* app = "ConfBridge" [static]
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 confbridge_exec().

Definition at line 814 of file app_confbridge.c.

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 by  doxygen 1.6.2