[asterisk-commits] file: branch file/bridging r106999 - /team/file/bridging/apps/app_confbridge.c

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sat Mar 8 13:13:29 CST 2008


Author: file
Date: Sat Mar  8 13:13:29 2008
New Revision: 106999

URL: http://svn.digium.com/view/asterisk?view=rev&rev=106999
Log:
Rewrite how MOH starting/stopping is handled. This is now handled by the second participant, thus the first participant no longer needs to leave the bridge. This means that IVR options (such as muting) will still be in effect when the second participant joins.

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=106999&r1=106998&r2=106999
==============================================================================
--- team/file/bridging/apps/app_confbridge.c (original)
+++ team/file/bridging/apps/app_confbridge.c Sat Mar  8 13:13:29 2008
@@ -70,12 +70,21 @@
 
 /*! \brief The structure that represents a conference bridge */
 struct 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 */
+	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 */
+	unsigned int dynamic:1;                                    /*!< Is this conference bridge dynamically generated? */
+	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 */
+struct conference_bridge_user {
+	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 */
+	unsigned int muted:1;                        /*!< Is this user muted or not */
+	AST_LIST_ENTRY(conference_bridge_user) list; /*!< Linked list information */
 };
 
 /*! \brief List of conference bridges configured or currently in existence */
@@ -85,11 +94,12 @@
  * \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)
+ * \param dynamic Can the conference bridge be dynamically created
+ * \param conference_bridge_user Conference bridge user structure
+ *
+ * \return A pointer to the conference bridge struct, or NULL if the conference room wasn't found.
+ */
+static struct conference_bridge *join_conference_bridge(const char *name, int dynamic, struct conference_bridge_user *conference_bridge_user)
 {
 	struct conference_bridge *conference_bridge = NULL;
 	int previous_participants = 0;
@@ -113,21 +123,46 @@
 		conference_bridge->dynamic = 1;
 		ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
 
+		/* Actually create the 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);
+			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);
 	}
 
+	/* Setup conference bridge user parameters */
+	conference_bridge_user->conference_bridge = 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 */
 	previous_participants = ast_atomic_fetchadd_int(&conference_bridge->users, +1);
 
+	/* If we are the first participant we may need to start MOH, if we are the second participant we may need to stop MOH on the first */
 	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();
+		/* Play back the "you are currently the only person in this conference" recording while not blocking, as that would be silly. */
+		AST_LIST_UNLOCK(&conference_bridges);
+		ast_streamfile(conference_bridge_user->chan, "conf-onlyperson", conference_bridge_user->chan->language);
+		ast_waitstream(conference_bridge_user->chan, AST_DIGIT_ANY);
+		ast_stopstream(conference_bridge_user->chan);
+		AST_LIST_LOCK(&conference_bridges);
+		/* We have to check again as the value may have changed while playing back the recording */
+		if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
+			ast_moh_start(conference_bridge_user->chan, NULL, NULL);
+		}
 	} 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;
+		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);
+		}
 	}
 
 	/* That is that... we are done searchin/modifying the list. */
@@ -140,10 +175,11 @@
  * \brief Leave a conference bridge
  *
  * \param conference_bridge The conference bridge to leave
+ * \param conference_bridge_user The conference bridge user structure
  *
  * \return Returns 0 on success, -1 on failure
  */
-static int leave_conference_bridge(struct conference_bridge *conference_bridge)
+static int leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
 {
 	int previous_participants = 0;
 
@@ -152,12 +188,20 @@
 	/* Decrement the users count while keeping the previous participant count */
 	previous_participants = ast_atomic_fetchadd_int(&conference_bridge->users, -1);
 
+	/* 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 is still one participant in the conference bridge then start MOH on them if need be, otherwise if we are the last and this is a dynamic conference kill it off. */
 	if (previous_participants == 2) {
-		/* If there were 2 participants the other participant needs to go to either music on hold, or dead air */
+		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 start MOH if needed */
+		if (ast_test_flag(&first_participant->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, first_participant->chan)) {
+			ast_moh_start(first_participant->chan, NULL, NULL);
+			ast_bridge_unsuspend(conference_bridge->bridge, first_participant->chan);
+		}
+	} else if (previous_participants == 1 && conference_bridge->dynamic) {
 		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);
 	}
@@ -177,7 +221,20 @@
  */
 static int menu_callback_admin(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	return 0;
+	int digit, res = 0;
+
+	/* 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, "conf-adminmenu", bridge_channel->chan->language)) {
+		return -1;
+	}
+
+	/* 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);
+
+	bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
+
+	return res;
 }
 
 /*!
@@ -213,32 +270,6 @@
 	return res;
 }
 
-/*!
- * \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)
 {
 	int res = 0;
@@ -246,6 +277,9 @@
 	char *parse;
 	struct conference_bridge *conference_bridge = NULL;
 	struct ast_bridge_features features;
+	struct conference_bridge_user conference_bridge_user = {
+		.chan = chan,
+	};
 	AST_DECLARE_APP_ARGS(args,
 		AST_APP_ARG(conf_name);
 		AST_APP_ARG(options);
@@ -261,11 +295,15 @@
 
 	AST_STANDARD_APP_ARGS(args, parse);
 
-	if (args.argc == 2)
+	if (args.argc == 2) {
 		ast_app_parse_options(app_opts, &flags, NULL, args.options);
+	}
+
+	/* Copy flags over to the conference bridge user structure, they may need to be used */
+	ast_copy_flags(&conference_bridge_user.flags, &flags, AST_FLAGS_ALL);
 
 	/* Look for a conference bridge matching the provided name */
-	if (!(conference_bridge = join_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), &conference_bridge_user))) {
 		ast_log(LOG_WARNING, "Conference bridge with name %s does not exist.\n", args.conf_name);
 		return -1;
 	}
@@ -273,44 +311,22 @@
 	/* If the menu option is enabled provide a user or admin menu as a custom feature hook */
 	if (ast_test_flag(&flags, OPTION_MENU)) {
 		ast_bridge_features_init(&features);
-		ast_bridge_features_hook(&features, "#", (ast_test_flag(&flags, OPTION_ADMIN) ? menu_callback_admin : menu_callback_user), NULL);
-	}
-
-	/* If this is the first user in the conference announce it */
-	if (conference_bridge->users == 1) {
-		ast_streamfile(chan, "conf-onlyperson", chan->language);
-		ast_waitstream(chan, AST_DIGIT_ANY);
-		ast_stopstream(chan);
+		ast_bridge_features_hook(&features, "#", (ast_test_flag(&flags, OPTION_ADMIN) ? menu_callback_admin : menu_callback_user), &conference_bridge_user);
 	}
 
 	/* 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);
+	leave_conference_bridge(conference_bridge, &conference_bridge_user);
 
 	/* Can't forget to clean up the features structure, or else we risk a memory leak */
-	if (ast_test_flag(&flags, OPTION_MENU))
+	if (ast_test_flag(&flags, OPTION_MENU)) {
 		ast_bridge_features_cleanup(&features);
+	}
 
 	return res;
 }




More information about the asterisk-commits mailing list