[asterisk-commits] file: branch file/bridging r106841 - /team/file/bridging/apps/app_confbridge.c
SVN commits to the Asterisk project
asterisk-commits at lists.digium.com
Fri Mar 7 15:48:18 CST 2008
Author: file
Date: Fri Mar 7 15:48:18 2008
New Revision: 106841
URL: http://svn.digium.com/view/asterisk?view=rev&rev=106841
Log:
Make the 'M' option work. Now if you are the only person in the conference bridge and have the 'M' option set you will hear music on hold.
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=106841&r1=106840&r2=106841
==============================================================================
--- team/file/bridging/apps/app_confbridge.c (original)
+++ team/file/bridging/apps/app_confbridge.c Fri Mar 7 15:48:18 2008
@@ -34,6 +34,7 @@
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
+#include <signal.h>
#include "asterisk/file.h"
#include "asterisk/logger.h"
@@ -43,6 +44,7 @@
#include "asterisk/lock.h"
#include "asterisk/app.h"
#include "asterisk/bridging.h"
+#include "asterisk/musiconhold.h"
static char *app = "ConfBridge";
static char *synopsis =
@@ -51,10 +53,10 @@
" It shows you the basic structure to create your own Asterisk applications.\n";
enum {
- OPTION_DYNAMIC = (1 << 0),
- OPTION_ADMIN = (1 << 1),
- OPTION_MENU = (1 << 2),
- OPTION_MUSICONHOLD = (1 << 3),
+ 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_flags;
AST_APP_OPTIONS(app_opts,{
@@ -64,19 +66,33 @@
AST_APP_OPTION('M', OPTION_MUSICONHOLD),
});
+#define MAX_CONF_NAME 32
+
+/*! \brief The structure that represents a conference bridge */
struct conference_bridge {
- const char *name; /*!< Name of the conference bridge */
+ char name[MAX_CONF_NAME]; /*!< Name of the conference bridge */
struct ast_bridge *bridge; /*!< Bridge structure doing the mixing */
int users; /*!< Number of users present */
int dynamic:1; /*!< Is this conference bridge dynamically generated? */
+ pthread_t first_participant; /*!< Thread of the first participant */
AST_LIST_ENTRY(conference_bridge) list; /*!< Linked list information */
};
+/*! \brief List of conference bridges configured or currently in existence */
static AST_LIST_HEAD_STATIC(conference_bridges, conference_bridge);
-static struct conference_bridge *find_conference_bridge(const char *name, int dynamic)
+/*!
+ * \brief Join a conference bridge
+ *
+ * \param name The conference name
+ * \param dynamic Whether this conference can be dynamically created or not
+ *
+ * \return A pointer to the conference struct, or NULL if it wasn't found.
+ */
+static struct conference_bridge *join_conference_bridge(const char *name, int dynamic)
{
struct conference_bridge *conference_bridge = NULL;
+ int previous_participants = 0;
AST_LIST_LOCK(&conference_bridges);
@@ -86,37 +102,121 @@
break;
}
- /* If one was not found and we can dynamically create it, do so */
- if (!conference_bridge && dynamic && (conference_bridge = ast_calloc(1, sizeof(*conference_bridge)))) {
+ /* If one was not found try to create one if the dynamic option is set */
+ 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 (!dynamic || !(conference_bridge = ast_calloc(1, sizeof(*conference_bridge)))) {
+ AST_LIST_UNLOCK(&conference_bridges);
+ return NULL;
+ }
+ /* Set whether this is dynamic and the name, not having those would be so silly */
conference_bridge->dynamic = 1;
- conference_bridge->name = name;
- if (!(conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART))) {
- free(conference_bridge);
- conference_bridge = NULL;
- } else
- AST_LIST_INSERT_TAIL(&conference_bridges, conference_bridge, list);
- }
-
- /* Increment user count if we found a conference bridge */
- if (conference_bridge)
- ast_atomic_fetchadd_int(&conference_bridge->users, +1);
-
+ ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
+
+ /* 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);
+ }
+
+ /* Increment the users count on the bridge, but record it as it is going to need to be known right after this */
+ previous_participants = ast_atomic_fetchadd_int(&conference_bridge->users, +1);
+
+ if (previous_participants == 0) {
+ /* If we are the first person to join the conference bridge record our thread so that the second person can bump us */
+ conference_bridge->first_participant = pthread_self();
+ } else if (previous_participants == 1) {
+ /* If we are the second person to join we are responsible for setting up an actual bridge, and bumping the first person */
+ conference_bridge->bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_SMART);
+ pthread_kill(conference_bridge->first_participant, SIGURG);
+ conference_bridge->first_participant = AST_PTHREADT_NULL;
+ }
+
+ /* That is that... we are done searchin/modifying the list. */
AST_LIST_UNLOCK(&conference_bridges);
return conference_bridge;
}
+/*!
+ * \brief Leave a conference bridge
+ *
+ * \param conference_bridge The conference bridge to leave
+ *
+ * \return Returns 0 on success, -1 on failure
+ */
+static int leave_conference_bridge(struct conference_bridge *conference_bridge)
+{
+ int previous_participants = 0;
+
+ AST_LIST_LOCK(&conference_bridges);
+
+ /* Decrement the users count while keeping the previous participant count */
+ previous_participants = ast_atomic_fetchadd_int(&conference_bridge->users, -1);
+
+ if (previous_participants == 2) {
+ /* If there were 2 participants the other participant needs to go to either music on hold, or dead air */
+ ast_bridge_destroy(conference_bridge->bridge);
+ conference_bridge->bridge = NULL;
+ } else if (previous_participants == 1 && conference_bridge->dynamic) {
+ /* If we were the last participant the conference bridge needs to be destroyed if it was dynamically created */
+ AST_LIST_REMOVE(&conference_bridges, conference_bridge, list);
+ ast_free(conference_bridge);
+ }
+
+ AST_LIST_UNLOCK(&conference_bridges);
+
+ return 0;
+}
+
+/*!
+ * \brief DTMF Menu Callback for Administrators
+ *
+ * \param bridge Bridge this is involving
+ * \param bridge_channel Bridged channel this is involving
+ *
+ * \return Returns 0 on success, -1 on failure
+ */
static int menu_callback_admin(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
return 0;
}
+/*!
+ * \brief DTMF Menu Callback for Users
+ *
+ * \param bridge Bridge this is involving
+ * \param bridge_channel Bridged channel this is involving
+ *
+ * \return Returns 0 on success, -1 on failure
+ */
static int menu_callback_user(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
{
- ast_streamfile(bridge_channel->chan, "demo-congrats", bridge_channel->chan->language);
- ast_waitstream(bridge_channel->chan, "");
- bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
return 0;
+}
+
+/*!
+ * \brief Helper function which basically services a channel and waits to be bumped
+ *
+ * \param chan Channel to service
+ *
+ * \return Nothing
+ */
+static void wait_for_the_bump(struct ast_channel *chan)
+{
+ int ms = -1;
+ struct ast_channel *watcher[1] = {chan, };
+ struct ast_channel *winner = NULL;
+
+ while ((winner = ast_waitfor_n(watcher, 1, &ms))) {
+ struct ast_frame *frame = ast_read(chan);
+ if (!frame || (frame->frametype == AST_FRAME_CONTROL && frame->subclass == AST_CONTROL_HANGUP)) {
+ if (frame)
+ ast_frfree(frame);
+ return;
+ }
+ ast_frfree(frame);
+ }
+
+ return;
}
static int app_exec(struct ast_channel *chan, void *data)
@@ -145,7 +245,7 @@
ast_app_parse_options(app_opts, &flags, NULL, args.options);
/* Look for a conference bridge matching the provided name */
- if (!(conference_bridge = find_conference_bridge(args.conf_name, ast_test_flag(&flags, OPTION_DYNAMIC)))) {
+ if (!(conference_bridge = join_conference_bridge(args.conf_name, ast_test_flag(&flags, OPTION_DYNAMIC)))) {
ast_log(LOG_WARNING, "Conference bridge with name %s does not exist.\n", args.conf_name);
return -1;
}
@@ -161,24 +261,32 @@
ast_streamfile(chan, "conf-onlyperson", chan->language);
ast_waitstream(chan, AST_DIGIT_ANY);
ast_stopstream(chan);
- /* If the music on hold option is enabled start it up and wait for the next user to enter */
- }
-
- /* Actually join the bridge */
- ast_bridge_join(conference_bridge->bridge, chan, NULL, &features);
-
- /* Drop ourselves from the conference bridge and remove it if needed */
- AST_LIST_LOCK(&conference_bridges);
- /* Decrement users count, and if this is dynamic remove it if we are the last one */
- if (ast_atomic_fetchadd_int(&conference_bridge->users, -1) == 1 && conference_bridge->dynamic) {
- /* Remove from list */
- AST_LIST_REMOVE(&conference_bridges, conference_bridge, list);
- /* Drop bridge */
- ast_bridge_destroy(conference_bridge->bridge);
- /* Finally deallocate ourselves */
- ast_free(conference_bridge);
- }
- AST_LIST_UNLOCK(&conference_bridges);
+ }
+
+ /* Enter a loop where we basically either play dead air/music on hold, or participant in the conference bridge */
+ while (!ast_check_hangup(chan)) {
+ /* If we are the only person in the conference bridge we actually want to play dead air or music on hold depending on configuration */
+ if (conference_bridge->users == 1) {
+ /* Record our thread so that the second participant can bump us */
+ AST_LIST_LOCK(&conference_bridges);
+ conference_bridge->first_participant = pthread_self();
+ AST_LIST_UNLOCK(&conference_bridges);
+ /* If we are playing music on hold start it up and wait, otherwise just wait */
+ if (ast_test_flag(&flags, OPTION_MUSICONHOLD)) {
+ ast_moh_start(chan, NULL, NULL);
+ wait_for_the_bump(chan);
+ ast_moh_stop(chan);
+ } else {
+ wait_for_the_bump(chan);
+ }
+ continue;
+ }
+ /* Join our conference bridge for real */
+ ast_bridge_join(conference_bridge->bridge, chan, NULL, &features);
+ }
+
+ /* Easy as pie, depart this channel from the conference bridge */
+ leave_conference_bridge(conference_bridge);
/* Can't forget to clean up the features structure, or else we risk a memory leak */
if (ast_test_flag(&flags, OPTION_MENU))
More information about the asterisk-commits
mailing list