[asterisk-commits] file: branch file/bridging r114778 - /team/file/bridging/apps/app_confbridge.c
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Mon Apr 28 12:26:01 CDT 2008
Author: file
Date: Mon Apr 28 12:26:01 2008
New Revision: 114778
URL: http://svn.digium.com/view/asterisk?view=rev&rev=114778
Log:
Convert app_confbridge over to using astobj2.
Modified:
team/file/bridging/apps/app_confbridge.c
Modified: team/file/bridging/apps/app_confbridge.c
URL: http://svn.digium.com/view/asterisk/team/file/bridging/apps/app_confbridge.c?view=diff&rev=114778&r1=114777&r2=114778
==============================================================================
--- team/file/bridging/apps/app_confbridge.c (original)
+++ team/file/bridging/apps/app_confbridge.c Mon Apr 28 12:26:01 2008
@@ -47,6 +47,7 @@
#include "asterisk/musiconhold.h"
#include "asterisk/say.h"
#include "asterisk/audiohook.h"
+#include "asterisk/astobj2.h"
static char *app = "ConfBridge";
static char *synopsis =
@@ -57,7 +58,6 @@
" 'A' -- Set marked mode\n"
" 'a' -- Set admin mode\n"
" 'c' -- Announce user(s) count on joining a conference\n"
-" 'd' -- Dynamically add conference\n"
" 'm' -- Present menu (user or admin) when '#' is received\n"
" 'M[(<class>)]'\n"
" -- Enable music on hold when the conference has a single caller.\n"
@@ -72,16 +72,15 @@
"can be set using the CONFBRIDGE_LEAVE_SOUND variable. These can be unique to the caller.";
enum {
- OPTION_DYNAMIC = (1 << 0), /*!< Set if the conference is to be dynamically generated */
- OPTION_ADMIN = (1 << 1), /*!< Set if the caller is an administrator */
- OPTION_MENU = (1 << 2), /*!< Set if the caller should have access to the conference bridge IVR menu */
- OPTION_MUSICONHOLD = (1 << 3), /*!< Set if music on hold should be played if nobody else is in the conference bridge */
- OPTION_NOONLYPERSON = (1 << 4), /*!< Set if the "you are currently the only person in this conference" sound file should not be played */
- OPTION_STARTMUTED = (1 << 5), /*!< Set if the caller should be initially set muted */
- OPTION_ANNOUNCEUSERCOUNT = (1 << 6), /*!< Set if the number of users should be announced to the caller */
- OPTION_MARKEDUSER = (1 << 7), /*!< Set if the caller is a marked user */
- OPTION_WAITMARKED = (1 << 8), /*!< Set if the conference must wait for a marked user before starting */
- OPTION_QUIET = (1 << 9), /*!< Set if no audio prompts should be played */
+ 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 */
} option_flags;
enum {
@@ -94,7 +93,6 @@
AST_APP_OPTION('A', OPTION_MARKEDUSER),
AST_APP_OPTION('a', OPTION_ADMIN),
AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT),
- AST_APP_OPTION('d', OPTION_DYNAMIC),
AST_APP_OPTION('m', OPTION_MENU),
AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS),
AST_APP_OPTION('1', OPTION_NOONLYPERSON),
@@ -103,7 +101,11 @@
AST_APP_OPTION('q', OPTION_QUIET),
});
+/* Maximum length of a conference bridge name */
#define MAX_CONF_NAME 32
+
+/* Maximum number of buckets our conference bridges container can have */
+#define MAX_CONFERENCE_BRIDGE_BUCKETS 53
/*! \brief The structure that represents a conference bridge */
struct conference_bridge {
@@ -111,10 +113,8 @@
struct ast_bridge *bridge; /*!< Bridge structure doing the mixing */
int users; /*!< Number of users present */
int markedusers; /*!< Number of marked users present */
- unsigned int dynamic:1; /*!< Is this conference bridge dynamically generated? */
unsigned int locked:1; /*!< Is this conference bridge locked? */
AST_LIST_HEAD_NOLOCK(, conference_bridge_user) users_list; /*!< List of users participating in the conference bridge */
- AST_LIST_ENTRY(conference_bridge) list; /*!< Linked list information */
};
/*! \brief The structure that represents a conference bridge user */
@@ -122,16 +122,30 @@
struct conference_bridge *conference_bridge; /*!< Conference bridge they are participating in */
struct ast_channel *chan; /*!< Asterisk channel participating */
struct ast_flags flags; /*!< Flags passed in when the application was called */
- char *opt_args[OPTION_ARRAY_SIZE]; /*!< Arguments to options passed when application was called */
+ char *opt_args[OPTION_ARRAY_SIZE]; /*!< Arguments to options passed when application was called */
struct ast_bridge_features features; /*!< Bridge features structure */
unsigned int kicked:1; /*!< User has been kicked from the conference */
AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
};
-/*! \brief List of conference bridges configured or currently in existence */
-static AST_LIST_HEAD_STATIC(conference_bridges, conference_bridge);
+/*! \brief Container to hold all conference bridges in progress */
+static struct ao2_container *conference_bridges;
static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename);
+
+/*! \brief Hashing function used for conference bridges container */
+static int conference_bridge_hash_cb(const void *obj, const int flags)
+{
+ const struct conference_bridge *conference_bridge = obj;
+ return ast_str_hash(conference_bridge->name);
+}
+
+/*! \brief Comparison function used for conference bridges container */
+static int conference_bridge_cmp_cb(void *obj, void *arg, int flags)
+{
+ const struct conference_bridge *conference_bridge0 = obj, *conference_bridge1 = arg;
+ return (!strcasecmp(conference_bridge0->name, conference_bridge1->name) ? CMP_MATCH : 0);
+}
/*!
* \brief Announce number of users in the conference bridge to the caller
@@ -184,7 +198,7 @@
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) {
@@ -196,12 +210,12 @@
}
}
- /* Next play the audio file stating they are going to placed into the conference */
- AST_LIST_UNLOCK(&conference_bridges);
+ /* Next play the audio file stating they are going to be placed into the conference */
+ 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);
- AST_LIST_LOCK(&conference_bridges);
+ ao2_lock(conference_bridge);
/* Finally iterate through and unmute them all */
AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
@@ -210,7 +224,7 @@
}
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) {
@@ -220,9 +234,9 @@
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)) {
- AST_LIST_UNLOCK(&conference_bridges);
+ ao2_unlock(conference_bridge);
ast_stream_and_wait(conference_bridge_user->chan, "conf-waitforleader", "");
- AST_LIST_LOCK(&conference_bridges);
+ ao2_lock(conference_bridge);
}
/* Start music on hold if needed */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
@@ -247,9 +261,9 @@
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) && !ast_test_flag(&conference_bridge_user->flags, OPTION_NOONLYPERSON)) {
- AST_LIST_UNLOCK(&conference_bridges);
+ ao2_unlock(conference_bridge);
ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyperson", "");
- AST_LIST_LOCK(&conference_bridges);
+ ao2_lock(conference_bridge);
}
/* If we need to start music on hold on the channel do so now */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
@@ -260,9 +274,9 @@
/* Announce number of users if need be */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
- AST_LIST_UNLOCK(&conference_bridges);
+ ao2_unlock(conference_bridge);
announce_user_count(conference_bridge, conference_bridge_user);
- AST_LIST_LOCK(&conference_bridges);
+ ao2_lock(conference_bridge);
}
/* If we are the second participant we may need to stop music on hold on the first */
@@ -280,6 +294,25 @@
}
/*!
+ * \brief Destroy a conference bridge
+ *
+ * \param obj The conference bridge object
+ *
+ * \return Nothing
+ */
+static void destroy_conference_bridge(void *obj)
+{
+ struct conference_bridge *conference_bridge = obj;
+
+ ast_debug(1, "Destroying conference bridge '%s'\n", conference_bridge->name);
+
+ /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
+ ast_bridge_destroy(conference_bridge->bridge);
+
+ return;
+}
+
+/*!
* \brief Join a conference bridge
*
* \param name The conference name
@@ -290,58 +323,69 @@
static struct conference_bridge *join_conference_bridge(const char *name, struct conference_bridge_user *conference_bridge_user)
{
struct conference_bridge *conference_bridge = NULL;
-
- AST_LIST_LOCK(&conference_bridges);
-
- /* Look for an already existing conference bridge */
- AST_LIST_TRAVERSE(&conference_bridges, conference_bridge, list) {
- if (!strcasecmp(conference_bridge->name, name)) {
- break;
- }
- }
-
- /* When finding a conference bridge that already exists make sure it is locked or that we are an admin */
+ struct conference_bridge tmp;
+
+ ast_copy_string(tmp.name, name, sizeof(tmp.name));
+
+ 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", "");
- AST_LIST_UNLOCK(&conference_bridges);
return NULL;
}
- /* If one was not found try to create one if the dynamic option is set */
+ /* If no conference bridge was found see if we can create one */
if (!conference_bridge) {
- /* Oh dear... the dynamic option was not set or we buggered up and couldn't allocate memory for the darn thing */
- if (!ast_test_flag(&conference_bridge_user->flags, OPTION_DYNAMIC) || !(conference_bridge = ast_calloc(1, sizeof(*conference_bridge)))) {
- AST_LIST_UNLOCK(&conference_bridges);
+ /* 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;
}
- /* Set whether this is dynamic and the name, not having those would be so silly */
- conference_bridge->dynamic = 1;
+
+ /* Setup conference bridge parameters */
ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
- /* Actually create the bridge that will do the audio mixing */
+ /* 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))) {
- ast_free(conference_bridge);
- AST_LIST_UNLOCK(&conference_bridges);
+ ao2_ref(conference_bridge, -1);
+ ao2_unlock(conference_bridges);
+ ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
return NULL;
}
- /* All done... add it to the list so the next person who joins can find it */
- AST_LIST_INSERT_TAIL(&conference_bridges, conference_bridge, list);
- }
+ /* Link it into the conference bridges container */
+ ao2_link(conference_bridges, conference_bridge);
+ ao2_ref(conference_bridge, -1);
+
+ 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 */
- ast_atomic_fetchadd_int(&conference_bridge->users, +1);
+ conference_bridge->users++;
/* If the caller is a marked user bump up the count */
if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
- ast_atomic_fetchadd_int(&conference_bridge->markedusers, +1);
+ 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 */
@@ -351,8 +395,7 @@
post_join_unmarked(conference_bridge, conference_bridge_user);
}
- /* That is that... we are done searchin/modifying the list. */
- AST_LIST_UNLOCK(&conference_bridges);
+ ao2_unlock(conference_bridge);
return conference_bridge;
}
@@ -367,61 +410,58 @@
*/
static int leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
{
- AST_LIST_LOCK(&conference_bridges);
+ 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)) {
- ast_atomic_fetchadd_int(&conference_bridge->markedusers, -1);
+ conference_bridge->markedusers--;
}
/* Decrement the users count while keeping the previous participant count */
- ast_atomic_fetchadd_int(&conference_bridge->users, -1);
+ 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 (!conference_bridge->users) {
- /* If there are no more users in this conference we do not have much to do... */
- if (conference_bridge->dynamic) {
- /* Unless it is dynamic in which case we delete it. */
- ast_bridge_destroy(conference_bridge->bridge);
- AST_LIST_REMOVE(&conference_bridges, conference_bridge, list);
- ast_free(conference_bridge);
- }
- } else if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER) && !conference_bridge->markedusers) {
- /* Now if they were a marked user and no other marked users exist... we have to mute everyone else and start MOH if need be */
- 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 the audio prompt saying the leader has left the conference */
- AST_LIST_UNLOCK(&conference_bridges);
- ast_autoservice_start(conference_bridge_user->chan);
- play_sound_file(conference_bridge, "conf-leaderhasleft");
- ast_autoservice_stop(conference_bridge_user->chan);
- AST_LIST_LOCK(&conference_bridges);
-
- /* Move 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);
+ /* 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;
}
- }
- } 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);
- }
- }
-
- AST_LIST_UNLOCK(&conference_bridges);
+
+ /* Play back the audio prompt saying the leader has left the conference */
+ 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);
+ }
+ }
+ }
+
+ /* Done mucking with the conference bridge, huzzah */
+ ao2_unlock(conference_bridge);
+
+ ao2_ref(conference_bridge, -1);
return 0;
}
@@ -478,12 +518,12 @@
int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
/* See if music on hold is playing */
- AST_LIST_LOCK(&conference_bridges);
+ 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);
}
- AST_LIST_UNLOCK(&conference_bridges);
+ 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)) {
@@ -509,14 +549,14 @@
/* 3 - Eject last user */
struct conference_bridge_user *last_participant = NULL;
- AST_LIST_LOCK(&conference_bridges);
+ 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))) {
- AST_LIST_UNLOCK(&conference_bridges);
+ 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);
- AST_LIST_UNLOCK(&conference_bridges);
+ ao2_unlock(conference_bridge);
}
} else if (digit == '4') {
/* 4 - Decrease listening volume */
@@ -539,11 +579,11 @@
finished:
/* See if music on hold needs to be started back up again */
- AST_LIST_LOCK(&conference_bridges);
+ 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);
}
- AST_LIST_UNLOCK(&conference_bridges);
+ ao2_unlock(conference_bridge);
bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
@@ -653,14 +693,24 @@
/*! \brief Called when module is being unloaded */
static int unload_module(void)
{
+ /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
+ ao2_ref(conference_bridges, -1);
+
return ast_unregister_application(app);
}
/*! \brief Called when module is being loaded */
static int load_module(void)
{
- if (ast_register_application(app, confbridge_exec, synopsis, descrip))
+ /* Create a container to hold the conference bridges */
+ if (!(conference_bridges = ao2_container_alloc(MAX_CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (ast_register_application(app, confbridge_exec, synopsis, descrip)) {
+ ao2_ref(conference_bridges, -1);
+ return AST_MODULE_LOAD_DECLINE;
+ }
return AST_MODULE_LOAD_SUCCESS;
}
More information about the asterisk-commits
mailing list