[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