Thu Apr 28 2011 17:13:36

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
__MODULE_INFO_SECTION 
__mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Conference Bridge Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .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.

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

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

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_bridgeConference bridge to peek at
conference_bridge_userCaller
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().

{
   if (conference_bridge->users == 1) {
      /* Awww we are the only person in the conference bridge */
      return;
   } else if (conference_bridge->users == 2) {
      /* Eep, there is one other person */
      if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
         return;
      }
   } else {
      /* Alas multiple others in here */
      if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
         return;
      }
      if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
         return;
      }
      if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
         return;
      }
   }
}
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_strdupa, ast_stream_and_wait(), ast_strlen_zero(), ast_test_flag, conference_bridge::bridge, chan, 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().

{
   int res = 0, volume_adjustments[2];
   char *parse;
   struct conference_bridge *conference_bridge = NULL;
   struct conference_bridge_user conference_bridge_user = {
      .chan = chan,
   };
   const char *tmp, *join_sound = NULL, *leave_sound = NULL;
   AST_DECLARE_APP_ARGS(args,
      AST_APP_ARG(conf_name);
      AST_APP_ARG(options);
   );

   if (ast_strlen_zero(data)) {
      ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
      return -1;
   }

   /* We need to make a copy of the input string if we are going to modify it! */
   parse = ast_strdupa(data);

   AST_STANDARD_APP_ARGS(args, parse);

   if (args.argc == 2) {
      ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
   }

   /* Look for a conference bridge matching the provided name */
   if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
      return -1;
   }

   /* Keep a copy of volume adjustments so we can restore them later if need be */
   volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
   volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);

   /* Always initialize the features structure, we are in most cases always going to need it. */
   ast_bridge_features_init(&conference_bridge_user.features);

   /* If the menu option is enabled provide a user or admin menu as a custom feature hook */
   if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
      ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
   }

   /* If the caller should be joined already muted, make it so */
   if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
      conference_bridge_user.features.mute = 1;
   }

   /* Grab join/leave sounds from the channel */
   ast_channel_lock(chan);
   if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
      join_sound = ast_strdupa(tmp);
   }
   if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
      leave_sound = ast_strdupa(tmp);
   }
   ast_channel_unlock(chan);

   /* If there is 1 or more people already in the conference then play our join sound unless overridden */
   if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
      ast_autoservice_start(chan);
      play_sound_file(conference_bridge, join_sound);
      ast_autoservice_stop(chan);
   }

   /* Join our conference bridge for real */
   ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);

   /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */
   if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
      ast_autoservice_start(chan);
      play_sound_file(conference_bridge, leave_sound);
      ast_autoservice_stop(chan);
   }

   /* Easy as pie, depart this channel from the conference bridge */
   leave_conference_bridge(conference_bridge, &conference_bridge_user);
   conference_bridge = NULL;

   /* Can't forget to clean up the features structure, or else we risk a memory leak */
   ast_bridge_features_cleanup(&conference_bridge_user.features);

   /* If the user was kicked from the conference play back the audio prompt for it */
   if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
      res = ast_stream_and_wait(chan, "conf-kicked", "");
   }

   /* Restore volume adjustments to previous values in case they were changed */
   if (volume_adjustments[0]) {
      ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
   }
   if (volume_adjustments[1]) {
      ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
   }

   return res;
}
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().

{
   const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
   return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH | CMP_STOP : 0);
}
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().

{
   const struct conference_bridge *conference_bridge = obj;
   return ast_str_case_hash(conference_bridge->name);
}
static void destroy_conference_bridge ( void *  obj) [static]

Destroy a conference bridge.

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

{
   struct conference_bridge *conference_bridge = obj;

   ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);

   ast_mutex_destroy(&conference_bridge->playback_lock);

   if (conference_bridge->playback_chan) {
      struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
      ast_hangup(underlying_channel);
      ast_hangup(conference_bridge->playback_chan);
      conference_bridge->playback_chan = NULL;
   }

   /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
   if (conference_bridge->bridge) {
      ast_bridge_destroy(conference_bridge->bridge);
      conference_bridge->bridge = NULL;
   }
}
static struct conference_bridge* join_conference_bridge ( const char *  name,
struct conference_bridge_user conference_bridge_user 
) [static, read]

Join a conference bridge.

Parameters:
nameThe conference name
conference_bridge_userConference 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().

{
   struct conference_bridge *conference_bridge = NULL;
   struct conference_bridge tmp;

   ast_copy_string(tmp.name, name, sizeof(tmp.name));

   /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
   ao2_lock(conference_bridges);

   ast_debug(1, "Trying to find conference bridge '%s'\n", name);

   /* Attempt to find an existing conference bridge */
   conference_bridge = ao2_find(conference_bridges, &tmp, OBJ_POINTER);

   /* When finding a conference bridge that already exists make sure that it is not locked, and if so that we are not an admin */
   if (conference_bridge && conference_bridge->locked && !ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN)) {
      ao2_unlock(conference_bridges);
      ao2_ref(conference_bridge, -1);
      ast_debug(1, "Conference bridge '%s' is locked and caller is not an admin\n", name);
      ast_stream_and_wait(conference_bridge_user->chan, "conf-locked", "");
      return NULL;
   }

   /* If no conference bridge was found see if we can create one */
   if (!conference_bridge) {
      /* Try to allocate memory for a new conference bridge, if we fail... this won't end well. */
      if (!(conference_bridge = ao2_alloc(sizeof(*conference_bridge), destroy_conference_bridge))) {
         ao2_unlock(conference_bridges);
         ast_log(LOG_ERROR, "Conference bridge '%s' does not exist.\n", name);
         return NULL;
      }

      /* Setup conference bridge parameters */
      ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));

      /* Create an actual bridge that will do the audio mixing */
      if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
         ao2_ref(conference_bridge, -1);
         conference_bridge = NULL;
         ao2_unlock(conference_bridges);
         ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
         return NULL;
      }

      /* Setup lock for playback channel */
      ast_mutex_init(&conference_bridge->playback_lock);

      /* Link it into the conference bridges container */
      ao2_link(conference_bridges, conference_bridge);

      ast_debug(1, "Created conference bridge '%s' and linked to container '%p'\n", name, conference_bridges);
   }

   ao2_unlock(conference_bridges);

   /* Setup conference bridge user parameters */
   conference_bridge_user->conference_bridge = conference_bridge;

   ao2_lock(conference_bridge);

   /* All good to go, add them in */
   AST_LIST_INSERT_TAIL(&conference_bridge->users_list, conference_bridge_user, list);

   /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
   conference_bridge->users++;

   /* If the caller is a marked user bump up the count */
   if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
      conference_bridge->markedusers++;
   }

   /* 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 */
   if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
      post_join_marked(conference_bridge, conference_bridge_user);
   } else {
      post_join_unmarked(conference_bridge, conference_bridge_user);
   }

   ao2_unlock(conference_bridge);

   return conference_bridge;
}
static void leave_conference_bridge ( struct conference_bridge conference_bridge,
struct conference_bridge_user conference_bridge_user 
) [static]

Leave a conference bridge.

Parameters:
conference_bridgeThe conference bridge to leave
conference_bridge_userThe 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().

{
   ao2_lock(conference_bridge);

   /* If this caller is a marked user bump down the count */
   if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
      conference_bridge->markedusers--;
   }

   /* Decrement the users count while keeping the previous participant count */
   conference_bridge->users--;

   /* Drop conference bridge user from the list, they be going bye bye */
   AST_LIST_REMOVE(&conference_bridge->users_list, conference_bridge_user, list);

   /* If there are still users in the conference bridge we may need to do things (such as start MOH on them) */
   if (conference_bridge->users) {
      if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
         struct conference_bridge_user *other_participant = NULL;

         /* Start out with muting everyone */
         AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
            other_participant->features.mute = 1;
         }

         /* Play back the audio prompt saying the leader has left the conference */
         if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
            ao2_unlock(conference_bridge);
            ast_autoservice_start(conference_bridge_user->chan);
            play_sound_file(conference_bridge, "conf-leaderhasleft");
            ast_autoservice_stop(conference_bridge_user->chan);
            ao2_lock(conference_bridge);
         }

         /* Now on to starting MOH if needed */
         AST_LIST_TRAVERSE(&conference_bridge->users_list, other_participant, list) {
            if (ast_test_flag(&other_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_participant->chan)) {
               ast_moh_start(other_participant->chan, other_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
               ast_bridge_unsuspend(conference_bridge->bridge, other_participant->chan);
            }
         }
      } else if (conference_bridge->users == 1) {
         /* Of course if there is one other person in here we may need to start up MOH on them */
         struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);

         if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
            ast_moh_start(first_participant->chan, first_participant->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
            ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
         }
      }
   } else {
      ao2_unlink(conference_bridges, conference_bridge);
   }

   /* Done mucking with the conference bridge, huzzah */
   ao2_unlock(conference_bridge);

   ao2_ref(conference_bridge, -1);
}
static int menu_callback ( struct ast_bridge bridge,
struct ast_bridge_channel bridge_channel,
void *  hook_pvt 
) [static]

DTMF Menu Callback.

Parameters:
bridgeBridge this is involving
bridge_channelBridged channel this is involving
hook_pvtUser's conference bridge structure
Return values:
0success
-1failure

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

{
   struct conference_bridge_user *conference_bridge_user = hook_pvt;
   struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
   int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);

   /* See if music on hold is playing */
   ao2_lock(conference_bridge);
   if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
      /* Just us so MOH is probably indeed going, let's stop it */
      ast_moh_stop(bridge_channel->chan);
   }
   ao2_unlock(conference_bridge);

   /* Try to play back the user menu, if it fails pass this back up so the bridging core will act on it */
   if (ast_streamfile(bridge_channel->chan, (isadmin ? "conf-adminmenu" : "conf-usermenu"), bridge_channel->chan->language)) {
      res = -1;
      goto finished;
   }

   /* Wait for them to enter a digit from the user menu options */
   digit = ast_waitstream(bridge_channel->chan, AST_DIGIT_ANY);
   ast_stopstream(bridge_channel->chan);

   if (digit == '1') {
      /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
      if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
         conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
      }
      res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
   } else if (isadmin && digit == '2') {
      /* 2 - Unlock or lock conference */
      conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
      res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
   } else if (isadmin && digit == '3') {
      /* 3 - Eject last user */
      struct conference_bridge_user *last_participant = NULL;

      ao2_lock(conference_bridge);
      if (((last_participant = AST_LIST_LAST(&conference_bridge->users_list)) == conference_bridge_user) || (ast_test_flag(&last_participant->flags, OPTION_ADMIN))) {
         ao2_unlock(conference_bridge);
         res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
      } else {
         last_participant->kicked = 1;
         ast_bridge_remove(conference_bridge->bridge, last_participant->chan);
         ao2_unlock(conference_bridge);
      }
   } else if (digit == '4') {
      /* 4 - Decrease listening volume */
      ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
   } else if (digit == '6') {
      /* 6 - Increase listening volume */
      ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
   } else if (digit == '7') {
      /* 7 - Decrease talking volume */
      ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
   } else if (digit == '8') {
      /* 8 - Exit the IVR */
   } else if (digit == '9') {
      /* 9 - Increase talking volume */
      ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
   } else {
      /* No valid option was selected */
      res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
   }

 finished:
   /* See if music on hold needs to be started back up again */
   ao2_lock(conference_bridge);
   if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
      ast_moh_start(bridge_channel->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
   }
   ao2_unlock(conference_bridge);

   bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;

   return res;
}
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_bridgeConference bridge they are in
chanChannel to play audio prompt to
filePrompt 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().

{
   ao2_unlock(conference_bridge);
   ast_stream_and_wait(chan, file, "");
   ao2_lock(conference_bridge);
}
static int play_sound_file ( struct conference_bridge conference_bridge,
const char *  filename 
) [static]

Play sound file into conference bridge.

Parameters:
conference_bridgeThe conference bridge to play sound file into
filenameSound file to play
Return values:
0success
-1failure

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

{
   struct ast_channel *underlying_channel;

   ast_mutex_lock(&conference_bridge->playback_lock);

   if (!(conference_bridge->playback_chan)) {
      int cause;

      if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {
         ast_mutex_unlock(&conference_bridge->playback_lock);
         return -1;
      }

      conference_bridge->playback_chan->bridge = conference_bridge->bridge;

      if (ast_call(conference_bridge->playback_chan, "", 0)) {
         ast_hangup(conference_bridge->playback_chan);
         conference_bridge->playback_chan = NULL;
         ast_mutex_unlock(&conference_bridge->playback_lock);
         return -1;
      }

      ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);

      underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
   } else {
      /* Channel was already available so we just need to add it back into the bridge */
      underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
      ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
   }

   /* The channel is all under our control, in goes the prompt */
   ast_stream_and_wait(conference_bridge->playback_chan, filename, "");

   ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
   ast_bridge_depart(conference_bridge->bridge, underlying_channel);

   ast_mutex_unlock(&conference_bridge->playback_lock);

   return 0;
}
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_bridgeConference bridge being joined
conference_bridge_userConference 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().

{
   if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
      struct conference_bridge_user *other_conference_bridge_user = NULL;

      /* If we are not the first marked user to join just bail out now */
      if (conference_bridge->markedusers >= 2) {
         return;
      }

      /* Iterate through every participant stopping MOH on them if need be */
      AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
         if (other_conference_bridge_user == conference_bridge_user) {
            continue;
         }
         if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
            ast_moh_stop(other_conference_bridge_user->chan);
            ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
         }
      }

      /* Next play the audio file stating they are going to be placed into the conference */
      if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
         ao2_unlock(conference_bridge);
         ast_autoservice_start(conference_bridge_user->chan);
         play_sound_file(conference_bridge, "conf-placeintoconf");
         ast_autoservice_stop(conference_bridge_user->chan);
         ao2_lock(conference_bridge);
      }

      /* Finally iterate through and unmute them all */
      AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
         if (other_conference_bridge_user == conference_bridge_user) {
            continue;
         }
         other_conference_bridge_user->features.mute = 0;
      }

   } else {
      /* If a marked user already exists in the conference bridge we can just bail out now */
      if (conference_bridge->markedusers) {
         return;
      }
      /* Be sure we are muted so we can't talk to anybody else waiting */
      conference_bridge_user->features.mute = 1;
      /* If we have not been quieted play back that they are waiting for the leader */
      if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET)) {
         play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader");
      }
      /* Start music on hold if needed */
      /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
       * allowing a marked user to enter while the prompt was playing
       */
      if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
         ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
      }
   }
}
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_bridgeConference bridge being joined
conference_bridge_userConference 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().

{
   /* Play back audio prompt and start MOH if need be if we are the first participant */
   if (conference_bridge->users == 1) {
      /* If audio prompts have not been quieted or this prompt quieted play it on out */
      if (!ast_test_flag(&conference_bridge_user->flags, OPTION_QUIET | OPTION_NOONLYPERSON)) {
         play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson");
      }
      /* If we need to start music on hold on the channel do so now */
      /* We need to re-check the number of users in the conference bridge here because another conference bridge
       * participant could have joined while the above prompt was playing for the first user.
       */
      if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
         ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
      }
      return;
   }

   /* Announce number of users if need be */
   if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
      ao2_unlock(conference_bridge);
      announce_user_count(conference_bridge, conference_bridge_user);
      ao2_lock(conference_bridge);
   }

   /* If we are the second participant we may need to stop music on hold on the first */
   if (conference_bridge->users == 2) {
      struct conference_bridge_user *first_participant = AST_LIST_FIRST(&conference_bridge->users_list);

      /* Temporarily suspend the above participant from the bridge so we have control to stop MOH if needed */
      if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
         ast_moh_stop(first_participant->chan);
         ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
      }
   }
}
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.

{
   int res = ast_unregister_application(app);

   /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
   ao2_ref(conference_bridges, -1);

   return res;
}

Variable Documentation

struct ast_module_info __MODULE_INFO_SECTION __mod_info = { __MODULE_INFO_GLOBALS .name = AST_MODULE, .flags = AST_MODFLAG_DEFAULT , .description = "Conference Bridge Application" , .key = ASTERISK_GPL_KEY , .buildopt_sum = AST_BUILDOPT_SUM, .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().