Wed Jan 27 20:02:17 2016

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   AST_MAX_EXTENSION

Enumerations

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

Functions

static int announce_user_count (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Announce number of users in the conference bridge to the caller.
 AST_APP_OPTIONS (app_opts,{AST_APP_OPTION('A', OPTION_MARKEDUSER), AST_APP_OPTION('a', OPTION_ADMIN), AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT), AST_APP_OPTION('m', OPTION_STARTMUTED), AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS), AST_APP_OPTION('1', OPTION_NOONLYPERSON), AST_APP_OPTION('s', OPTION_MENU), AST_APP_OPTION('w', OPTION_WAITMARKED), AST_APP_OPTION('q', OPTION_QUIET),})
 AST_MODULE_INFO (ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER,"Conference Bridge Application",.load=load_module,.unload=unload_module,.load_pri=AST_MODPRI_DEVSTATE_PROVIDER,)
static int confbridge_exec (struct ast_channel *chan, const char *data)
 The ConfBridge application.
static int conference_bridge_cmp_cb (void *obj, void *arg, int flags)
 Comparison function used for conference bridges container.
static int conference_bridge_hash_cb (const void *obj, const int flags)
 Hashing function used for conference bridges container.
static void destroy_conference_bridge (void *obj)
 Destroy a conference bridge.
static struct conference_bridgejoin_conference_bridge (const char *name, struct conference_bridge_user *conference_bridge_user)
 Join a conference bridge.
static void leave_conference_bridge (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Leave a conference bridge.
static int load_module (void)
 Called when module is being loaded.
static int menu_callback (struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
 DTMF Menu Callback.
static int play_prompt_to_channel (struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
 Play back an audio file to a channel.
static int play_sound_file (struct conference_bridge *conference_bridge, const char *filename)
 Play sound file into conference bridge.
static int post_join_marked (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Perform post-joining marked specific actions.
static int post_join_unmarked (struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 Perform post-joining non-marked specific actions.
static int unload_module (void)
 Called when module is being unloaded.

Variables

static const char app [] = "ConfBridge"
static struct ao2_containerconference_bridges
 Container to hold all conference bridges in progress.

Detailed Description

Conference Bridge application.

Author:
Joshua Colp <jcolp@digium.com> 

This is a conference bridge application utilizing the bridging core.

Definition in file app_confbridge.c.


Define Documentation

#define CONFERENCE_BRIDGE_BUCKETS   53

Definition at line 155 of file app_confbridge.c.

Referenced by load_module().

#define MAX_CONF_NAME   AST_MAX_EXTENSION

Definition at line 152 of file app_confbridge.c.

Referenced by confbridge_exec().


Enumeration Type Documentation

anonymous enum
Enumerator:
OPTION_ADMIN 

Set if the caller is an administrator

OPTION_MENU 

Set if the caller should have access to the conference bridge IVR menu

OPTION_MUSICONHOLD 

Set if music on hold should be played if nobody else is in the conference bridge

OPTION_NOONLYPERSON 

Set if the "you are currently the only person in this conference" sound file should not be played

OPTION_STARTMUTED 

Set if the caller should be initially set muted

OPTION_ANNOUNCEUSERCOUNT 

Set if the number of users should be announced to the caller

OPTION_MARKEDUSER 

Set if the caller is a marked user

OPTION_WAITMARKED 

Set if the conference must wait for a marked user before starting

OPTION_QUIET 

Set if no audio prompts should be played

Definition at line 121 of file app_confbridge.c.

00121      {
00122    OPTION_ADMIN = (1 << 0),             /*!< Set if the caller is an administrator */
00123    OPTION_MENU = (1 << 1),              /*!< Set if the caller should have access to the conference bridge IVR menu */
00124    OPTION_MUSICONHOLD = (1 << 2),       /*!< Set if music on hold should be played if nobody else is in the conference bridge */
00125    OPTION_NOONLYPERSON = (1 << 3),      /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
00126    OPTION_STARTMUTED = (1 << 4),        /*!< Set if the caller should be initially set muted */
00127    OPTION_ANNOUNCEUSERCOUNT = (1 << 5), /*!< Set if the number of users should be announced to the caller */
00128    OPTION_MARKEDUSER = (1 << 6),        /*!< Set if the caller is a marked user */
00129    OPTION_WAITMARKED = (1 << 7),        /*!< Set if the conference must wait for a marked user before starting */
00130    OPTION_QUIET = (1 << 8),             /*!< Set if no audio prompts should be played */
00131 };

anonymous enum
Enumerator:
OPTION_MUSICONHOLD_CLASS 

If the 'M' option is set, the music on hold class to play

OPTION_ARRAY_SIZE 

Definition at line 133 of file app_confbridge.c.

00133      {
00134    OPTION_MUSICONHOLD_CLASS,            /*!< If the 'M' option is set, the music on hold class to play */
00135    /*This must be the last element */
00136    OPTION_ARRAY_SIZE,
00137 };


Function Documentation

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

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

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

Definition at line 207 of file app_confbridge.c.

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

Referenced by post_join_unmarked().

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

AST_APP_OPTIONS ( app_opts   ) 
AST_MODULE_INFO ( ASTERISK_GPL_KEY  ,
AST_MODFLAG_LOAD_ORDER  ,
"Conference Bridge Application"  ,
load = load_module,
unload = unload_module,
load_pri = AST_MODPRI_DEVSTATE_PROVIDER 
)
static int confbridge_exec ( struct ast_channel chan,
const char *  data 
) [static]

The ConfBridge application.

Definition at line 724 of file app_confbridge.c.

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

Referenced by load_module().

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

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

Comparison function used for conference bridges container.

Definition at line 193 of file app_confbridge.c.

References CMP_MATCH, CMP_STOP, and conference_bridge::name.

Referenced by load_module().

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

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

Hashing function used for conference bridges container.

Definition at line 186 of file app_confbridge.c.

References ast_str_case_hash(), and conference_bridge::name.

Referenced by load_module().

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

static void destroy_conference_bridge ( void *  obj  )  [static]

Destroy a conference bridge.

Parameters:
obj The conference bridge object
Returns:
Returns nothing

Definition at line 383 of file app_confbridge.c.

References ast_bridge_destroy(), ast_debug, ast_hangup(), ast_mutex_destroy, conference_bridge::bridge, and conference_bridge::name.

Referenced by join_conference_bridge().

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

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

Join a conference bridge.

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

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

Referenced by confbridge_exec().

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

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

Leave a conference bridge.

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

Definition at line 519 of file app_confbridge.c.

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

Referenced by confbridge_exec(), and join_conference_bridge().

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

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

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

Referenced by confbridge_exec().

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

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

Play back an audio file to a channel.

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

Definition at line 243 of file app_confbridge.c.

References ao2_lock, ao2_unlock, and ast_stream_and_wait().

Referenced by post_join_marked(), and post_join_unmarked().

00244 {
00245    int res;
00246    ao2_unlock(conference_bridge);
00247    res = ast_stream_and_wait(chan, file, "");
00248    ao2_lock(conference_bridge);
00249    return res;
00250 }

static int play_sound_file ( struct conference_bridge conference_bridge,
const char *  filename 
) [static]

Play sound file into conference bridge.

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

Definition at line 591 of file app_confbridge.c.

References ast_bridge_depart(), ast_bridge_impart(), ast_call(), ast_debug, AST_FORMAT_SLINEAR, ast_hangup(), ast_mutex_lock, ast_mutex_unlock, ast_request(), ast_stream_and_wait(), conference_bridge::bridge, and conference_bridge::name.

Referenced by confbridge_exec(), and leave_conference_bridge().

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

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

Perform post-joining marked specific actions.

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

Definition at line 260 of file app_confbridge.c.

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

Referenced by join_conference_bridge().

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

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

Perform post-joining non-marked specific actions.

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

Definition at line 332 of file app_confbridge.c.

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

Referenced by join_conference_bridge().

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

static int unload_module ( void   )  [static]

Called when module is being unloaded.

Definition at line 830 of file app_confbridge.c.

References ao2_ref, ast_unregister_application(), and conference_bridges.

00831 {
00832    int res = ast_unregister_application(app);
00833 
00834    /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
00835    ao2_ref(conference_bridges, -1);
00836 
00837    return res;
00838 }


Variable Documentation

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

Definition at line 119 of file app_confbridge.c.

Container to hold all conference bridges in progress.

Definition at line 181 of file app_confbridge.c.

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


Generated on 27 Jan 2016 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1