Mon Aug 31 12:29:57 2015

Asterisk developer's documentation


app_confbridge.c

Go to the documentation of this file.
00001 /*
00002  * Asterisk -- An open source telephony toolkit.
00003  *
00004  * Copyright (C) 2007-2008, Digium, Inc.
00005  *
00006  * Joshua Colp <jcolp@digium.com>
00007  *
00008  * See http://www.asterisk.org for more information about
00009  * the Asterisk project. Please do not directly contact
00010  * any of the maintainers of this project for assistance;
00011  * the project provides a web site, mailing lists and IRC
00012  * channels for your use.
00013  *
00014  * This program is free software, distributed under the terms of
00015  * the GNU General Public License Version 2. See the LICENSE file
00016  * at the top of the source tree.
00017  */
00018 
00019 /*! \file
00020  *
00021  * \brief Conference Bridge application
00022  *
00023  * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
00024  *
00025  * This is a conference bridge application utilizing the bridging core.
00026  * \ingroup applications
00027  */
00028 
00029 /*** MODULEINFO
00030    <support_level>extended</support_level>
00031  ***/
00032 
00033 #include "asterisk.h"
00034 
00035 ASTERISK_FILE_VERSION(__FILE__, "$Revision: 415060 $")
00036 
00037 #include <stdio.h>
00038 #include <stdlib.h>
00039 #include <unistd.h>
00040 #include <string.h>
00041 #include <signal.h>
00042 
00043 #include "asterisk/file.h"
00044 #include "asterisk/logger.h"
00045 #include "asterisk/channel.h"
00046 #include "asterisk/pbx.h"
00047 #include "asterisk/module.h"
00048 #include "asterisk/lock.h"
00049 #include "asterisk/app.h"
00050 #include "asterisk/bridging.h"
00051 #include "asterisk/musiconhold.h"
00052 #include "asterisk/say.h"
00053 #include "asterisk/audiohook.h"
00054 #include "asterisk/astobj2.h"
00055 
00056 /*** DOCUMENTATION
00057         <application name="ConfBridge" language="en_US">
00058                 <synopsis>
00059                         Conference bridge application.
00060                 </synopsis>
00061                 <syntax>
00062                         <parameter name="confno">
00063                                 <para>The conference number</para>
00064                         </parameter>
00065                         <parameter name="options">
00066                                 <optionlist>
00067                                         <option name="a">
00068                                                 <para>Set admin mode.</para>
00069                                         </option>
00070                                         <option name="A">
00071                                                 <para>Set marked mode.</para>
00072                                         </option>
00073                                         <option name="c">
00074                                                 <para>Announce user(s) count on joining a conference.</para>
00075                                         </option>
00076                                         <option name="m">
00077                                                 <para>Set initially muted.</para>
00078                                         </option>
00079                                         <option name="M" hasparams="optional">
00080                                                 <para>Enable music on hold when the conference has a single caller. Optionally,
00081                                                 specify a musiconhold class to use. If one is not provided, it will use the
00082                                                 channel's currently set music class, or <literal>default</literal>.</para>
00083                                                 <argument name="class" required="true" />
00084                                         </option>
00085                                         <option name="1">
00086                                                 <para>Do not play message when first person enters</para>
00087                                         </option>
00088                                         <option name="s">
00089                                                 <para>Present menu (user or admin) when <literal>#</literal> is received
00090                                                 (send to menu).</para>
00091                                         </option>
00092                                         <option name="w">
00093                                                 <para>Wait until the marked user enters the conference.</para>
00094                                         </option>
00095                                         <option name="q">
00096                                                 <para>Quiet mode (don't play enter/leave sounds).</para>
00097                                         </option>
00098             </optionlist>
00099             </parameter>
00100                 </syntax>
00101                 <description>
00102                         <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup only.</para>
00103                         <para>The join sound can be set using the <literal>CONFBRIDGE_JOIN_SOUND</literal> variable and the leave sound can be set using the <literal>CONFBRIDGE_LEAVE_SOUND</literal> variable. These can be unique to the caller.</para>
00104          <note><para>This application will not automatically answer the channel.</para></note>
00105                 </description>
00106         </application>
00107 ***/
00108 
00109 /*!
00110  * \par Playing back a file to a channel in a conference
00111  * You might notice in this application that while playing a sound file
00112  * to a channel the actual conference bridge lock is not held. This is done so
00113  * that other channels are not blocked from interacting with the conference bridge.
00114  * Unfortunately because of this it is possible for things to change after the sound file
00115  * is done being played. Data must therefore be checked after reacquiring the conference
00116  * bridge lock if it is important.
00117  */
00118 
00119 static const char app[] = "ConfBridge";
00120 
00121 enum {
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 };
00132 
00133 enum {
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 };
00138 
00139 AST_APP_OPTIONS(app_opts,{
00140    AST_APP_OPTION('A', OPTION_MARKEDUSER),
00141    AST_APP_OPTION('a', OPTION_ADMIN),
00142    AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT),
00143    AST_APP_OPTION('m', OPTION_STARTMUTED),
00144    AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS),
00145    AST_APP_OPTION('1', OPTION_NOONLYPERSON),
00146    AST_APP_OPTION('s', OPTION_MENU),
00147    AST_APP_OPTION('w', OPTION_WAITMARKED),
00148    AST_APP_OPTION('q', OPTION_QUIET),
00149 });
00150 
00151 /* Maximum length of a conference bridge name */
00152 #define MAX_CONF_NAME AST_MAX_EXTENSION
00153 
00154 /* Number of buckets our conference bridges container can have */
00155 #define CONFERENCE_BRIDGE_BUCKETS 53
00156 
00157 /*! \brief The structure that represents a conference bridge */
00158 struct conference_bridge {
00159    char name[MAX_CONF_NAME];                                         /*!< Name of the conference bridge */
00160    struct ast_bridge *bridge;                                        /*!< Bridge structure doing the mixing */
00161    unsigned int users;                                               /*!< Number of users present */
00162    unsigned int markedusers;                                         /*!< Number of marked users present */
00163    unsigned int locked:1;                                            /*!< Is this conference bridge locked? */
00164    AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list;        /*!< List of users participating in the conference bridge */
00165    struct ast_channel *playback_chan;                                /*!< Channel used for playback into the conference bridge */
00166    ast_mutex_t playback_lock;                                        /*!< Lock used for playback channel */
00167 };
00168 
00169 /*! \brief The structure that represents a conference bridge user */
00170 struct conference_bridge_user {
00171    struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
00172    struct ast_channel *chan;                    /*!< Asterisk channel participating */
00173    struct ast_flags flags;                      /*!< Flags passed in when the application was called */
00174    char *opt_args[OPTION_ARRAY_SIZE];           /*!< Arguments to options passed when application was called */
00175    struct ast_bridge_features features;         /*!< Bridge features structure */
00176    unsigned int kicked:1;                       /*!< User has been kicked from the conference */
00177    AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
00178 };
00179 
00180 /*! \brief Container to hold all conference bridges in progress */
00181 static struct ao2_container *conference_bridges;
00182 
00183 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
00184 
00185 /*! \brief Hashing function used for conference bridges container */
00186 static int conference_bridge_hash_cb(const void *obj, const int flags)
00187 {
00188    const struct conference_bridge *conference_bridge = obj;
00189    return ast_str_case_hash(conference_bridge->name);
00190 }
00191 
00192 /*! \brief Comparison function used for conference bridges container */
00193 static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
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 }
00198 
00199 /*!
00200  * \brief Announce number of users in the conference bridge to the caller
00201  *
00202  * \param conference_bridge Conference bridge to peek at
00203  * \param conference_bridge_user Caller
00204  *
00205  * \return Returns 0 on success, -1 if the user hung up
00206  */
00207 static int announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
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 }
00231 
00232 /*!
00233  * \brief Play back an audio file to a channel
00234  *
00235  * \param conference_bridge Conference bridge they are in
00236  * \param chan Channel to play audio prompt to
00237  * \param file Prompt to play
00238  *
00239  * \return Returns 0 on success, -1 if the user hung up
00240  *
00241  * \note This function assumes that conference_bridge is locked
00242  */
00243 static int play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
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 }
00251 
00252 /*!
00253  * \brief Perform post-joining marked specific actions
00254  *
00255  * \param conference_bridge Conference bridge being joined
00256  * \param conference_bridge_user Conference bridge user joining
00257  *
00258  * \return Returns 0 on success, -1 if the user hung up
00259  */
00260 static int post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
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 }
00323 
00324 /*!
00325  * \brief Perform post-joining non-marked specific actions
00326  *
00327  * \param conference_bridge Conference bridge being joined
00328  * \param conference_bridge_user Conference bridge user joining
00329  *
00330  * \return Returns 0 on success, -1 if the user hung up
00331  */
00332 static int post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
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 }
00375 
00376 /*!
00377  * \brief Destroy a conference bridge
00378  *
00379  * \param obj The conference bridge object
00380  *
00381  * \return Returns nothing
00382  */
00383 static void destroy_conference_bridge(void *obj)
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 }
00404 
00405 static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user);
00406 
00407 /*!
00408  * \brief Join a conference bridge
00409  *
00410  * \param name The conference name
00411  * \param conference_bridge_user Conference bridge user structure
00412  *
00413  * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
00414  */
00415 static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
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 }
00511 
00512 /*!
00513  * \brief Leave a conference bridge
00514  *
00515  * \param conference_bridge The conference bridge to leave
00516  * \param conference_bridge_user The conference bridge user structure
00517  *
00518  */
00519 static void  leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
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 }
00581 
00582 /*!
00583  * \brief Play sound file into conference bridge
00584  *
00585  * \param conference_bridge The conference bridge to play sound file into
00586  * \param filename Sound file to play
00587  *
00588  * \retval 0 success
00589  * \retval -1 failure
00590  */
00591 static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
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 }
00633 
00634 /*!
00635  * \brief DTMF Menu Callback
00636  *
00637  * \param bridge Bridge this is involving
00638  * \param bridge_channel Bridged channel this is involving
00639  * \param hook_pvt User's conference bridge structure
00640  *
00641  * \retval 0 success
00642  * \retval -1 failure
00643  */
00644 static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
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 }
00722 
00723 /*! \brief The ConfBridge application */
00724 static int confbridge_exec(struct ast_channel *chan, const char *data)
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 }
00828 
00829 /*! \brief Called when module is being unloaded */
00830 static int unload_module(void)
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 }
00839 
00840 /*! \brief Called when module is being loaded */
00841 static int load_module(void)
00842 {
00843    /* Create a container to hold the conference bridges */
00844    if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
00845       return AST_MODULE_LOAD_DECLINE;
00846    }
00847 
00848    if (ast_register_application_xml(app, confbridge_exec)) {
00849       ao2_ref(conference_bridges, -1);
00850       return AST_MODULE_LOAD_DECLINE;
00851    }
00852 
00853    return AST_MODULE_LOAD_SUCCESS;
00854 }
00855 
00856 AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_LOAD_ORDER, "Conference Bridge Application",
00857    .load = load_module,
00858    .unload = unload_module,
00859    .load_pri = AST_MODPRI_DEVSTATE_PROVIDER,
00860 );

Generated on 31 Aug 2015 for Asterisk - The Open Source Telephony Project by  doxygen 1.6.1