aboutsummaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorfile <file@f38db490-d61c-443f-a65b-d21fe96a405b>2009-03-05 18:18:27 +0000
committerfile <file@f38db490-d61c-443f-a65b-d21fe96a405b>2009-03-05 18:18:27 +0000
commit78d18e52a495a2b1f964a2dceeb7fcc705a79d73 (patch)
treecc242551fdaa17b1537490b8ec0621d2c3fa2c66
parent62297fe334460eaa96bc7a2a2c39d9714c77c868 (diff)
Merge phase 1 support for the new bridging architecture.
This commit brings in the bridging core, bridging technologies, and the ConfBridge application. For usage information on the ConfBridge application please see the output of "core show application ConfBridge" from the CLI. For API documentation please see the doxygen page describing the architecture and the documentation for each API call. Review: http://reviewboard.digium.com/r/93/ git-svn-id: http://svn.digium.com/svn/asterisk/trunk@180369 f38db490-d61c-443f-a65b-d21fe96a405b
-rw-r--r--CHANGES3
-rw-r--r--Makefile2
-rw-r--r--apps/app_confbridge.c809
-rw-r--r--bridges/Makefile20
-rw-r--r--bridges/bridge_builtin_features.c257
-rw-r--r--bridges/bridge_multiplexed.c404
-rw-r--r--bridges/bridge_simple.c103
-rw-r--r--bridges/bridge_softmix.c303
-rw-r--r--channels/chan_bridge.c246
-rw-r--r--include/asterisk/bridging.h426
-rw-r--r--include/asterisk/bridging_features.h298
-rw-r--r--include/asterisk/bridging_technology.h180
-rw-r--r--include/asterisk/channel.h8
-rw-r--r--main/Makefile2
-rw-r--r--main/bridging.c1337
15 files changed, 4395 insertions, 3 deletions
diff --git a/CHANGES b/CHANGES
index 8da30aa77..73556b928 100644
--- a/CHANGES
+++ b/CHANGES
@@ -117,6 +117,9 @@ Applications
call origination from the dialplan.
* Voicemail now permits setting the emailsubject and emailbody per mailbox,
in addition to the setting in the "general" context.
+ * Added ConfBridge dialplan application which does conference bridges without
+ DAHDI. For information on its use, please see the output of
+ "core show application ConfBridge" from the CLI.
Miscellaneous
-------------
diff --git a/Makefile b/Makefile
index 3e2950e86..7eb70fd3c 100644
--- a/Makefile
+++ b/Makefile
@@ -293,7 +293,7 @@ endif
# value directly to ASTCFLAGS
ASTCFLAGS+=$(MALLOC_DEBUG)$(OPTIONS)
-MOD_SUBDIRS:=channels pbx apps codecs formats cdr funcs tests main res $(LOCAL_MOD_SUBDIRS)
+MOD_SUBDIRS:=channels pbx apps codecs formats cdr bridges funcs tests main res $(LOCAL_MOD_SUBDIRS)
OTHER_SUBDIRS:=utils agi
SUBDIRS:=$(OTHER_SUBDIRS) $(MOD_SUBDIRS)
SUBDIRS_INSTALL:=$(SUBDIRS:%=%-install)
diff --git a/apps/app_confbridge.c b/apps/app_confbridge.c
new file mode 100644
index 000000000..058f581ec
--- /dev/null
+++ b/apps/app_confbridge.c
@@ -0,0 +1,809 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007-2008, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Conference Bridge application
+ *
+ * \author\verbatim Joshua Colp <jcolp@digium.com> \endverbatim
+ *
+ * This is a conference bridge application utilizing the bridging core.
+ * \ingroup applications
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <string.h>
+#include <signal.h>
+
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/pbx.h"
+#include "asterisk/module.h"
+#include "asterisk/lock.h"
+#include "asterisk/app.h"
+#include "asterisk/bridging.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/say.h"
+#include "asterisk/audiohook.h"
+#include "asterisk/astobj2.h"
+
+/*** DOCUMENTATION
+ <application name="ConfBridge" language="en_US">
+ <synopsis>
+ Conference bridge application.
+ </synopsis>
+ <syntax>
+ <parameter name="confno">
+ <para>The conference number</para>
+ </parameter>
+ <parameter name="options">
+ <optionlist>
+ <option name="a">
+ <para>Set admin mode.</para>
+ </option>
+ <option name="A">
+ <para>Set marked mode.</para>
+ </option>
+ <option name="c">
+ <para>Announce user(s) count on joining a conference.</para>
+ </option>
+ <option name="m">
+ <para>Set initially muted.</para>
+ </option>
+ <option name="M" hasparams="optional">
+ <para>Enable music on hold when the conference has a single caller. Optionally,
+ specify a musiconhold class to use. If one is not provided, it will use the
+ channel's currently set music class, or <literal>default</literal>.</para>
+ <argument name="class" required="true" />
+ </option>
+ <option name="1">
+ <para>Do not play message when first person enters</para>
+ </option>
+ <option name="s">
+ <para>Present menu (user or admin) when <literal>*</literal> is received
+ (send to menu).</para>
+ </option>
+ <option name="w">
+ <para>Wait until the marked user enters the conference.</para>
+ </option>
+ <option name="q">
+ <para>Quiet mode (don't play enter/leave sounds).</para>
+ </option>
+ </optionlist>
+ </parameter>
+ </syntax>
+ <description>
+ <para>Enters the user into a specified conference bridge. The user can exit the conference by hangup only.</para>
+ <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>
+ </description>
+ </application>
+***/
+
+/*!
+ * \par Playing back a file to a channel in a conference
+ * You might notice in this application that while playing a sound file
+ * to a channel the actual conference bridge lock is not held. This is done so
+ * that other channels are not blocked from interacting with the conference bridge.
+ * Unfortunately because of this it is possible for things to change after the sound file
+ * is done being played. Data must therefore be checked after reacquiring the conference
+ * bridge lock if it is important.
+ */
+
+static const char *app = "ConfBridge";
+
+enum {
+ 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 */
+};
+
+enum {
+ OPTION_MUSICONHOLD_CLASS, /*!< If the 'M' option is set, the music on hold class to play */
+ /*This must be the last element */
+ OPTION_ARRAY_SIZE,
+};
+
+AST_APP_OPTIONS(app_opts,{
+ AST_APP_OPTION('A', OPTION_MARKEDUSER),
+ AST_APP_OPTION('a', OPTION_ADMIN),
+ AST_APP_OPTION('c', OPTION_ANNOUNCEUSERCOUNT),
+ AST_APP_OPTION('m', OPTION_MENU),
+ AST_APP_OPTION_ARG('M', OPTION_MUSICONHOLD, OPTION_MUSICONHOLD_CLASS),
+ AST_APP_OPTION('1', OPTION_NOONLYPERSON),
+ AST_APP_OPTION('s', OPTION_STARTMUTED),
+ AST_APP_OPTION('w', OPTION_WAITMARKED),
+ AST_APP_OPTION('q', OPTION_QUIET),
+});
+
+/* Maximum length of a conference bridge name */
+#define MAX_CONF_NAME 32
+
+/* Number of buckets our conference bridges container can have */
+#define CONFERENCE_BRIDGE_BUCKETS 53
+
+/*! \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 */
+ unsigned int users; /*!< Number of users present */
+ unsigned int markedusers; /*!< Number of marked users present */
+ 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 */
+ struct ast_channel *playback_chan; /*!< Channel used for playback into the conference bridge */
+ ast_mutex_t playback_lock; /*!< Lock used for playback channel */
+};
+
+/*! \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 */
+ 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 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_case_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 | CMP_STOP : 0);
+}
+
+/*!
+ * \brief Announce number of users in the conference bridge to the caller
+ *
+ * \param conference_bridge Conference bridge to peek at
+ * \param conference_bridge_user Caller
+ *
+ * \return Returns nothing
+ */
+static void announce_user_count(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ if (conference_bridge->users == 1) {
+ /* Awww we are the only person in the conference bridge */
+ return;
+ } else if (conference_bridge->users == 2) {
+ /* Eep, there is one other person */
+ if (ast_stream_and_wait(conference_bridge_user->chan, "conf-onlyone", "")) {
+ return;
+ }
+ } else {
+ /* Alas multiple others in here */
+ if (ast_stream_and_wait(conference_bridge_user->chan, "conf-thereare", "")) {
+ return;
+ }
+ if (ast_say_number(conference_bridge_user->chan, conference_bridge->users - 1, "", conference_bridge_user->chan->language, NULL)) {
+ return;
+ }
+ if (ast_stream_and_wait(conference_bridge_user->chan, "conf-otherinparty", "")) {
+ return;
+ }
+ }
+}
+
+/*!
+ * \brief Play back an audio file to a channel
+ *
+ * \param conference_bridge Conference bridge they are in
+ * \param chan Channel to play audio prompt to
+ * \param file Prompt to play
+ *
+ * \return Returns nothing
+ *
+ * \note This function assumes that conference_bridge is locked
+ */
+static void play_prompt_to_channel(struct conference_bridge *conference_bridge, struct ast_channel *chan, const char *file)
+{
+ ao2_unlock(conference_bridge);
+ ast_stream_and_wait(chan, file, "");
+ ao2_lock(conference_bridge);
+}
+
+/*!
+ * \brief Perform post-joining marked specific actions
+ *
+ * \param conference_bridge Conference bridge being joined
+ * \param conference_bridge_user Conference bridge user joining
+ *
+ * \return Returns nothing
+ */
+static void post_join_marked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
+ struct conference_bridge_user *other_conference_bridge_user = NULL;
+
+ /* If we are not the first marked user to join just bail out now */
+ 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) {
+ continue;
+ }
+ if (ast_test_flag(&other_conference_bridge_user->flags, OPTION_MUSICONHOLD) && !ast_bridge_suspend(conference_bridge->bridge, other_conference_bridge_user->chan)) {
+ ast_moh_stop(other_conference_bridge_user->chan);
+ ast_bridge_unsuspend(conference_bridge->bridge, other_conference_bridge_user->chan);
+ }
+ }
+
+ /* 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);
+ ao2_lock(conference_bridge);
+
+ /* Finally iterate through and unmute them all */
+ AST_LIST_TRAVERSE(&conference_bridge->users_list, other_conference_bridge_user, list) {
+ if (other_conference_bridge_user == conference_bridge_user) {
+ continue;
+ }
+ 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) {
+ return;
+ }
+ /* Be sure we are muted so we can't talk to anybody else waiting */
+ 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)) {
+ play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-waitforleader");
+ }
+ /* Start music on hold if needed */
+ /* We need to recheck the markedusers value here. play_prompt_to_channel unlocks the conference bridge, potentially
+ * allowing a marked user to enter while the prompt was playing
+ */
+ if (!conference_bridge->markedusers && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
+ ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
+ }
+ }
+}
+
+/*!
+ * \brief Perform post-joining non-marked specific actions
+ *
+ * \param conference_bridge Conference bridge being joined
+ * \param conference_bridge_user Conference bridge user joining
+ *
+ * \return Returns nothing
+ */
+static void post_join_unmarked(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ /* Play back audio prompt and start MOH if need be if we are the first participant */
+ 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 | OPTION_NOONLYPERSON)) {
+ play_prompt_to_channel(conference_bridge, conference_bridge_user->chan, "conf-onlyperson");
+ }
+ /* If we need to start music on hold on the channel do so now */
+ /* We need to re-check the number of users in the conference bridge here because another conference bridge
+ * participant could have joined while the above prompt was playing for the first user.
+ */
+ if (conference_bridge->users == 1 && ast_test_flag(&conference_bridge_user->flags, OPTION_MUSICONHOLD)) {
+ ast_moh_start(conference_bridge_user->chan, conference_bridge_user->opt_args[OPTION_MUSICONHOLD_CLASS], NULL);
+ }
+ return;
+ }
+
+ /* Announce number of users if need be */
+ if (ast_test_flag(&conference_bridge_user->flags, OPTION_ANNOUNCEUSERCOUNT)) {
+ ao2_unlock(conference_bridge);
+ announce_user_count(conference_bridge, conference_bridge_user);
+ ao2_lock(conference_bridge);
+ }
+
+ /* If we are the second participant we may need to stop music on hold on the first */
+ if (conference_bridge->users == 2) {
+ 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);
+ }
+ }
+}
+
+/*!
+ * \brief Destroy a conference bridge
+ *
+ * \param obj The conference bridge object
+ *
+ * \return Returns 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);
+
+ ast_mutex_destroy(&conference_bridge->playback_lock);
+
+ if (conference_bridge->playback_chan) {
+ struct ast_channel *underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
+ ast_hangup(underlying_channel);
+ ast_hangup(conference_bridge->playback_chan);
+ conference_bridge->playback_chan = NULL;
+ }
+
+ /* Destroying a conference bridge is simple, all we have to do is destroy the bridging object */
+ if (conference_bridge->bridge) {
+ ast_bridge_destroy(conference_bridge->bridge);
+ conference_bridge->bridge = NULL;
+ }
+}
+
+/*!
+ * \brief Join a conference bridge
+ *
+ * \param name The conference name
+ * \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, struct conference_bridge_user *conference_bridge_user)
+{
+ struct conference_bridge *conference_bridge = NULL;
+ struct conference_bridge tmp;
+
+ ast_copy_string(tmp.name, name, sizeof(tmp.name));
+
+ /* We explictly lock the conference bridges container ourselves so that other callers can not create duplicate conferences at the same */
+ 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", "");
+ return NULL;
+ }
+
+ /* If no conference bridge was found see if we can create one */
+ if (!conference_bridge) {
+ /* 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;
+ }
+
+ /* Setup conference bridge parameters */
+ ast_copy_string(conference_bridge->name, name, sizeof(conference_bridge->name));
+
+ /* 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))) {
+ ao2_ref(conference_bridge, -1);
+ conference_bridge = NULL;
+ ao2_unlock(conference_bridges);
+ ast_log(LOG_ERROR, "Conference bridge '%s' could not be created.\n", name);
+ return NULL;
+ }
+
+ /* Setup lock for playback channel */
+ ast_mutex_init(&conference_bridge->playback_lock);
+
+ /* Link it into the conference bridges container */
+ ao2_link(conference_bridges, conference_bridge);
+
+ 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 */
+ conference_bridge->users++;
+
+ /* If the caller is a marked user bump up the count */
+ if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER)) {
+ 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 */
+ if (ast_test_flag(&conference_bridge_user->flags, OPTION_MARKEDUSER | OPTION_WAITMARKED)) {
+ post_join_marked(conference_bridge, conference_bridge_user);
+ } else {
+ post_join_unmarked(conference_bridge, conference_bridge_user);
+ }
+
+ ao2_unlock(conference_bridge);
+
+ return conference_bridge;
+}
+
+/*!
+ * \brief Leave a conference bridge
+ *
+ * \param conference_bridge The conference bridge to leave
+ * \param conference_bridge_user The conference bridge user structure
+ *
+ */
+static void leave_conference_bridge(struct conference_bridge *conference_bridge, struct conference_bridge_user *conference_bridge_user)
+{
+ 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)) {
+ conference_bridge->markedusers--;
+ }
+
+ /* Decrement the users count while keeping the previous participant count */
+ 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 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;
+ }
+
+ /* 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);
+ }
+ }
+ } else {
+ ao2_unlink(conference_bridges, conference_bridge);
+ }
+
+ /* Done mucking with the conference bridge, huzzah */
+ ao2_unlock(conference_bridge);
+
+ ao2_ref(conference_bridge, -1);
+}
+
+/*!
+ * \brief Play sound file into conference bridge
+ *
+ * \param conference_bridge The conference bridge to play sound file into
+ * \param filename Sound file to play
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int play_sound_file(struct conference_bridge *conference_bridge, const char *filename)
+{
+ struct ast_channel *underlying_channel;
+
+ ast_mutex_lock(&conference_bridge->playback_lock);
+
+ if (!(conference_bridge->playback_chan)) {
+ int cause;
+
+ if (!(conference_bridge->playback_chan = ast_request("Bridge", AST_FORMAT_SLINEAR, "", &cause))) {
+ ast_mutex_unlock(&conference_bridge->playback_lock);
+ return -1;
+ }
+
+ conference_bridge->playback_chan->bridge = conference_bridge->bridge;
+
+ if (ast_call(conference_bridge->playback_chan, "", 0)) {
+ ast_hangup(conference_bridge->playback_chan);
+ conference_bridge->playback_chan = NULL;
+ ast_mutex_unlock(&conference_bridge->playback_lock);
+ return -1;
+ }
+
+ ast_debug(1, "Created a playback channel to conference bridge '%s'\n", conference_bridge->name);
+
+ underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
+ } else {
+ /* Channel was already available so we just need to add it back into the bridge */
+ underlying_channel = conference_bridge->playback_chan->tech->bridged_channel(conference_bridge->playback_chan, NULL);
+ ast_bridge_impart(conference_bridge->bridge, underlying_channel, NULL, NULL);
+ }
+
+ /* The channel is all under our control, in goes the prompt */
+ ast_stream_and_wait(conference_bridge->playback_chan, filename, "");
+
+ ast_debug(1, "Departing underlying channel '%s' from bridge '%p'\n", underlying_channel->name, conference_bridge->bridge);
+ ast_bridge_depart(conference_bridge->bridge, underlying_channel);
+
+ ast_mutex_unlock(&conference_bridge->playback_lock);
+
+ return 0;
+}
+
+/*!
+ * \brief DTMF Menu Callback
+ *
+ * \param bridge Bridge this is involving
+ * \param bridge_channel Bridged channel this is involving
+ * \param hook_pvt User's conference bridge structure
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+static int menu_callback(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ struct conference_bridge_user *conference_bridge_user = hook_pvt;
+ struct conference_bridge *conference_bridge = conference_bridge_user->conference_bridge;
+ int digit, res = 0, isadmin = ast_test_flag(&conference_bridge_user->flags, OPTION_ADMIN);
+
+ /* See if music on hold is playing */
+ 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);
+ }
+ 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)) {
+ res = -1;
+ goto finished;
+ }
+
+ /* 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);
+
+ if (digit == '1') {
+ /* 1 - Mute or unmute yourself, note we only allow manipulation if they aren't waiting for a marked user or if marked users exist */
+ if (!ast_test_flag(&conference_bridge_user->flags, OPTION_WAITMARKED) || conference_bridge->markedusers) {
+ conference_bridge_user->features.mute = (!conference_bridge_user->features.mute ? 1 : 0);
+ }
+ res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge_user->features.mute ? "conf-muted" : "conf-unmuted"), "");
+ } else if (isadmin && digit == '2') {
+ /* 2 - Unlock or lock conference */
+ conference_bridge->locked = (!conference_bridge->locked ? 1 : 0);
+ res = ast_stream_and_wait(bridge_channel->chan, (conference_bridge->locked ? "conf-lockednow" : "conf-unlockednow"), "");
+ } else if (isadmin && digit == '3') {
+ /* 3 - Eject last user */
+ struct conference_bridge_user *last_participant = NULL;
+
+ 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))) {
+ 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);
+ ao2_unlock(conference_bridge);
+ }
+ } else if (digit == '4') {
+ /* 4 - Decrease listening volume */
+ ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, -1);
+ } else if (digit == '6') {
+ /* 6 - Increase listening volume */
+ ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_WRITE, 1);
+ } else if (digit == '7') {
+ /* 7 - Decrease talking volume */
+ ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, -1);
+ } else if (digit == '8') {
+ /* 8 - Exit the IVR */
+ } else if (digit == '9') {
+ /* 9 - Increase talking volume */
+ ast_audiohook_volume_adjust(conference_bridge_user->chan, AST_AUDIOHOOK_DIRECTION_READ, 1);
+ } else {
+ /* No valid option was selected */
+ res = ast_stream_and_wait(bridge_channel->chan, "conf-errormenu", "");
+ }
+
+ finished:
+ /* See if music on hold needs to be started back up again */
+ 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);
+ }
+ ao2_unlock(conference_bridge);
+
+ bridge_channel->state = AST_BRIDGE_CHANNEL_STATE_WAIT;
+
+ return res;
+}
+
+/*! \brief The ConfBridge application */
+static int confbridge_exec(struct ast_channel *chan, void *data)
+{
+ int res = 0, volume_adjustments[2];
+ char *parse;
+ struct conference_bridge *conference_bridge = NULL;
+ struct conference_bridge_user conference_bridge_user = {
+ .chan = chan,
+ };
+ const char *tmp, *join_sound = NULL, *leave_sound = NULL;
+ AST_DECLARE_APP_ARGS(args,
+ AST_APP_ARG(conf_name);
+ AST_APP_ARG(options);
+ );
+
+ if (ast_strlen_zero(data)) {
+ ast_log(LOG_WARNING, "%s requires an argument (conference name[,options])\n", app);
+ return -1;
+ }
+
+ /* We need to make a copy of the input string if we are going to modify it! */
+ parse = ast_strdupa(data);
+
+ AST_STANDARD_APP_ARGS(args, parse);
+
+ if (args.argc == 2) {
+ ast_app_parse_options(app_opts, &conference_bridge_user.flags, conference_bridge_user.opt_args, args.options);
+ }
+
+ /* Look for a conference bridge matching the provided name */
+ if (!(conference_bridge = join_conference_bridge(args.conf_name, &conference_bridge_user))) {
+ return -1;
+ }
+
+ /* Keep a copy of volume adjustments so we can restore them later if need be */
+ volume_adjustments[0] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_READ);
+ volume_adjustments[1] = ast_audiohook_volume_get(chan, AST_AUDIOHOOK_DIRECTION_WRITE);
+
+ /* Always initialize the features structure, we are in most cases always going to need it. */
+ ast_bridge_features_init(&conference_bridge_user.features);
+
+ /* If the menu option is enabled provide a user or admin menu as a custom feature hook */
+ if (ast_test_flag(&conference_bridge_user.flags, OPTION_MENU)) {
+ ast_bridge_features_hook(&conference_bridge_user.features, "#", menu_callback, &conference_bridge_user);
+ }
+
+ /* If the caller should be joined already muted, make it so */
+ if (ast_test_flag(&conference_bridge_user.flags, OPTION_STARTMUTED)) {
+ conference_bridge_user.features.mute = 1;
+ }
+
+ /* Grab join/leave sounds from the channel */
+ ast_channel_lock(chan);
+ if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_JOIN_SOUND"))) {
+ join_sound = ast_strdupa(tmp);
+ }
+ if ((tmp = pbx_builtin_getvar_helper(chan, "CONFBRIDGE_LEAVE_SOUND"))) {
+ leave_sound = ast_strdupa(tmp);
+ }
+ ast_channel_unlock(chan);
+
+ /* If there is 1 or more people already in the conference then play our join sound unless overridden */
+ if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(join_sound) && conference_bridge->users >= 2) {
+ ast_autoservice_start(chan);
+ play_sound_file(conference_bridge, join_sound);
+ ast_autoservice_stop(chan);
+ }
+
+ /* Join our conference bridge for real */
+ ast_bridge_join(conference_bridge->bridge, chan, NULL, &conference_bridge_user.features);
+
+ /* If there is 1 or more people (not including us) already in the conference then play our leave sound unless overridden */
+ if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && !ast_strlen_zero(leave_sound) && conference_bridge->users >= 2) {
+ ast_autoservice_start(chan);
+ play_sound_file(conference_bridge, leave_sound);
+ ast_autoservice_stop(chan);
+ }
+
+ /* Easy as pie, depart this channel from the conference bridge */
+ leave_conference_bridge(conference_bridge, &conference_bridge_user);
+ conference_bridge = NULL;
+
+ /* Can't forget to clean up the features structure, or else we risk a memory leak */
+ ast_bridge_features_cleanup(&conference_bridge_user.features);
+
+ /* If the user was kicked from the conference play back the audio prompt for it */
+ if (!ast_test_flag(&conference_bridge_user.flags, OPTION_QUIET) && conference_bridge_user.kicked) {
+ res = ast_stream_and_wait(chan, "conf-kicked", "");
+ }
+
+ /* Restore volume adjustments to previous values in case they were changed */
+ if (volume_adjustments[0]) {
+ ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_READ, volume_adjustments[0]);
+ }
+ if (volume_adjustments[1]) {
+ ast_audiohook_volume_set(chan, AST_AUDIOHOOK_DIRECTION_WRITE, volume_adjustments[1]);
+ }
+
+ return res;
+}
+
+/*! \brief Called when module is being unloaded */
+static int unload_module(void)
+{
+ int res = ast_unregister_application(app);
+
+ /* Get rid of the conference bridges container. Since we only allow dynamic ones none will be active. */
+ ao2_ref(conference_bridges, -1);
+
+ return res;
+}
+
+/*! \brief Called when module is being loaded */
+static int load_module(void)
+{
+ /* Create a container to hold the conference bridges */
+ if (!(conference_bridges = ao2_container_alloc(CONFERENCE_BRIDGE_BUCKETS, conference_bridge_hash_cb, conference_bridge_cmp_cb))) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ if (ast_register_application_xml(app, confbridge_exec)) {
+ ao2_ref(conference_bridges, -1);
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Conference Bridge Application");
diff --git a/bridges/Makefile b/bridges/Makefile
new file mode 100644
index 000000000..90ed6c204
--- /dev/null
+++ b/bridges/Makefile
@@ -0,0 +1,20 @@
+#
+# Asterisk -- A telephony toolkit for Linux.
+#
+# Makefile for bridging modules
+#
+# Copyright (C) 2005-2007, Digium, Inc.
+#
+# This program is free software, distributed under the terms of
+# the GNU General Public License
+#
+
+-include $(ASTTOPDIR)/menuselect.makeopts $(ASTTOPDIR)/menuselect.makedeps
+
+MODULE_PREFIX=bridge
+MENUSELECT_CATEGORY=BRIDGES
+MENUSELECT_DESCRIPTION=Bridging Modules
+
+all: _all
+
+include $(ASTTOPDIR)/Makefile.moddir_rules
diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c
new file mode 100644
index 000000000..a61421c79
--- /dev/null
+++ b/bridges/bridge_builtin_features.c
@@ -0,0 +1,257 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Built in bridging features
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup bridges
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/frame.h"
+#include "asterisk/file.h"
+#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
+
+/*! \brief Helper function that presents dialtone and grabs extension */
+static int grab_transfer(struct ast_channel *chan, char *exten, size_t exten_len, const char *context)
+{
+ int res;
+
+ /* Play the simple "transfer" prompt out and wait */
+ res = ast_stream_and_wait(chan, "pbx-transfer", AST_DIGIT_ANY);
+ ast_stopstream(chan);
+
+ /* If the person hit a DTMF digit while the above played back stick it into the buffer */
+ if (res) {
+ exten[0] = (char)res;
+ }
+
+ /* Drop to dialtone so they can enter the extension they want to transfer to */
+ res = ast_app_dtget(chan, context, exten, exten_len, 100, 1000);
+
+ return res;
+}
+
+/*! \brief Helper function that creates an outgoing channel and returns it immediately */
+static struct ast_channel *dial_transfer(const struct ast_channel *caller, const char *exten, const char *context)
+{
+ char destination[AST_MAX_EXTENSION+AST_MAX_CONTEXT+1] = "";
+ struct ast_channel *chan = NULL;
+ int cause;
+
+ /* Fill the variable with the extension and context we want to call */
+ snprintf(destination, sizeof(destination), "%s@%s", exten, context);
+
+ /* Now we request that chan_local prepare to call the destination */
+ if (!(chan = ast_request("Local", caller->nativeformats, destination, &cause))) {
+ return NULL;
+ }
+
+ /* Before we actually dial out let's inherit the appropriate dialplan variables */
+ ast_channel_inherit_variables(caller, chan);
+
+ /* Since the above worked fine now we actually call it and return the channel */
+ if (ast_call(chan, destination, 0)) {
+ ast_hangup(chan);
+ return NULL;
+ }
+
+ return chan;
+}
+
+/*! \brief Internal built in feature for blind transfers */
+static int feature_blind_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ char exten[AST_MAX_EXTENSION] = "";
+ struct ast_channel *chan = NULL;
+ struct ast_bridge_features_blind_transfer *blind_transfer = hook_pvt;
+ const char *context = (blind_transfer && !ast_strlen_zero(blind_transfer->context) ? blind_transfer->context : bridge_channel->chan->context);
+
+ /* Grab the extension to transfer to */
+ if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+ ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ return 0;
+ }
+
+ /* Get a channel that is the destination we wish to call */
+ if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ return 0;
+ }
+
+ /* This is sort of the fun part. We impart the above channel onto the bridge, and have it take our place. */
+ ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
+
+ return 0;
+}
+
+/*! \brief Attended transfer feature to turn it into a threeway call */
+static int attended_threeway_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ /* This is sort of abusing the depart state but in this instance it is only going to be handled in the below function so it is okay */
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
+ return 0;
+}
+
+/*! \brief Attended transfer abort feature */
+static int attended_abort_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ struct ast_bridge_channel *called_bridge_channel = NULL;
+
+ /* It is possible (albeit unlikely) that the bridge channels list may change, so we have to ensure we do all of our magic while locked */
+ ao2_lock(bridge);
+
+ if (AST_LIST_FIRST(&bridge->channels) != bridge_channel) {
+ called_bridge_channel = AST_LIST_FIRST(&bridge->channels);
+ } else {
+ called_bridge_channel = AST_LIST_LAST(&bridge->channels);
+ }
+
+ /* Now we basically eject the other channel from the bridge. This will cause their thread to hang them up, and our own code to consider the transfer failed. */
+ if (called_bridge_channel) {
+ ast_bridge_change_state(called_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ }
+
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+
+ ao2_unlock(bridge);
+
+ return 0;
+}
+
+/*! \brief Internal built in feature for attended transfers */
+static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ char exten[AST_MAX_EXTENSION] = "";
+ struct ast_channel *chan = NULL;
+ struct ast_bridge *attended_bridge = NULL;
+ struct ast_bridge_features caller_features, called_features;
+ enum ast_bridge_channel_state attended_bridge_result;
+ struct ast_bridge_features_attended_transfer *attended_transfer = hook_pvt;
+ const char *context = (attended_transfer && !ast_strlen_zero(attended_transfer->context) ? attended_transfer->context : bridge_channel->chan->context);
+
+ /* Grab the extension to transfer to */
+ if (!grab_transfer(bridge_channel->chan, exten, sizeof(exten), context)) {
+ ast_stream_and_wait(bridge_channel->chan, "pbx-invalid", AST_DIGIT_ANY);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ return 0;
+ }
+
+ /* Get a channel that is the destination we wish to call */
+ if (!(chan = dial_transfer(bridge_channel->chan, exten, context))) {
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ return 0;
+ }
+
+ /* Create a bridge to use to talk to the person we are calling */
+ if (!(attended_bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
+ ast_hangup(chan);
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ return 0;
+ }
+
+ /* Setup our called features structure so that if they hang up we immediately get thrown out of the bridge */
+ ast_bridge_features_init(&called_features);
+ ast_bridge_features_set_flag(&called_features, AST_BRIDGE_FLAG_DISSOLVE);
+
+ /* This is how this is going down, we are imparting the channel we called above into this bridge first */
+ ast_bridge_impart(attended_bridge, chan, NULL, &called_features);
+
+ /* Before we join setup a features structure with the hangup option, just in case they want to use DTMF */
+ ast_bridge_features_init(&caller_features);
+ ast_bridge_features_enable(&caller_features, AST_BRIDGE_BUILTIN_HANGUP,
+ (attended_transfer && !ast_strlen_zero(attended_transfer->complete) ? attended_transfer->complete : "*1"), NULL);
+ ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->threeway) ? attended_transfer->threeway : "*2"),
+ attended_threeway_transfer, NULL);
+ ast_bridge_features_hook(&caller_features, (attended_transfer && !ast_strlen_zero(attended_transfer->abort) ? attended_transfer->abort : "*3"),
+ attended_abort_transfer, NULL);
+
+ /* But for the caller we want to join the bridge in a blocking fashion so we don't spin around in this function doing nothing while waiting */
+ attended_bridge_result = ast_bridge_join(attended_bridge, bridge_channel->chan, NULL, &caller_features);
+
+ /* Since the above returned the caller features structure is of no more use */
+ ast_bridge_features_cleanup(&caller_features);
+
+ /* Drop the channel we are transferring to out of the above bridge since it has ended */
+ if ((attended_bridge_result != AST_BRIDGE_CHANNEL_STATE_HANGUP) && !ast_bridge_depart(attended_bridge, chan)) {
+ /* If the user wants to turn this into a threeway transfer then do so, otherwise they take our place */
+ if (attended_bridge_result == AST_BRIDGE_CHANNEL_STATE_DEPART) {
+ /* We want to impart them upon the bridge and just have us return to it as normal */
+ ast_bridge_impart(bridge, chan, NULL, NULL);
+ } else {
+ ast_bridge_impart(bridge, chan, bridge_channel->chan, NULL);
+ }
+ } else {
+ ast_stream_and_wait(bridge_channel->chan, "beeperr", AST_DIGIT_ANY);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ }
+
+ /* Now that all channels are out of it we can destroy the bridge and the called features structure */
+ ast_bridge_features_cleanup(&called_features);
+ ast_bridge_destroy(attended_bridge);
+
+ return 0;
+}
+
+/*! \brief Internal built in feature for hangup */
+static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
+{
+ /* This is very simple, we basically change the state on the bridge channel to end and the core takes care of the rest */
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ return 0;
+}
+
+static int unload_module(void)
+{
+ return 0;
+}
+
+static int load_module(void)
+{
+ ast_bridge_features_register(AST_BRIDGE_BUILTIN_BLINDTRANSFER, feature_blind_transfer, NULL);
+ ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, feature_attended_transfer, NULL);
+ ast_bridge_features_register(AST_BRIDGE_BUILTIN_HANGUP, feature_hangup, NULL);
+
+ /* Bump up our reference count so we can't be unloaded */
+ ast_module_ref(ast_module_info->self);
+
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Built in bridging features");
diff --git a/bridges/bridge_multiplexed.c b/bridges/bridge_multiplexed.c
new file mode 100644
index 000000000..520673134
--- /dev/null
+++ b/bridges/bridge_multiplexed.c
@@ -0,0 +1,404 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2008, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Two channel bridging module which groups bridges into batches of threads
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup bridges
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/frame.h"
+#include "asterisk/astobj2.h"
+
+/*! \brief Number of buckets our multiplexed thread container can have */
+#define MULTIPLEXED_BUCKETS 53
+
+/*! \brief Number of channels we handle in a single thread */
+#define MULTIPLEXED_MAX_CHANNELS 8
+
+/*! \brief Structure which represents a single thread handling multiple 2 channel bridges */
+struct multiplexed_thread {
+ /*! Thread itself */
+ pthread_t thread;
+ /*! Pipe used to wake up the multiplexed thread */
+ int pipe[2];
+ /*! Channels in this thread */
+ struct ast_channel *chans[MULTIPLEXED_MAX_CHANNELS];
+ /*! Number of channels in this thread */
+ unsigned int count;
+ /*! Bit used to indicate that the thread is waiting on channels */
+ unsigned int waiting:1;
+ /*! Number of channels actually being serviced by this thread */
+ unsigned int service_count;
+};
+
+/*! \brief Container of all operating multiplexed threads */
+static struct ao2_container *multiplexed_threads;
+
+/*! \brief Callback function for finding a free multiplexed thread */
+static int find_multiplexed_thread(void *obj, void *arg, int flags)
+{
+ struct multiplexed_thread *multiplexed_thread = obj;
+ return (multiplexed_thread->count <= (MULTIPLEXED_MAX_CHANNELS - 2)) ? CMP_MATCH | CMP_STOP : 0;
+}
+
+/*! \brief Destroy callback for a multiplexed thread structure */
+static void destroy_multiplexed_thread(void *obj)
+{
+ struct multiplexed_thread *multiplexed_thread = obj;
+
+ if (multiplexed_thread->pipe[0] > -1) {
+ close(multiplexed_thread->pipe[0]);
+ }
+ if (multiplexed_thread->pipe[1] > -1) {
+ close(multiplexed_thread->pipe[1]);
+ }
+
+ return;
+}
+
+/*! \brief Create function which finds/reserves/references a multiplexed thread structure */
+static int multiplexed_bridge_create(struct ast_bridge *bridge)
+{
+ struct multiplexed_thread *multiplexed_thread;
+
+ ao2_lock(multiplexed_threads);
+
+ /* Try to find an existing thread to handle our additional channels */
+ if (!(multiplexed_thread = ao2_callback(multiplexed_threads, 0, find_multiplexed_thread, NULL))) {
+ int flags;
+
+ /* If we failed we will have to create a new one from scratch */
+ if (!(multiplexed_thread = ao2_alloc(sizeof(*multiplexed_thread), destroy_multiplexed_thread))) {
+ ast_debug(1, "Failed to find or create a new multiplexed thread for bridge '%p'\n", bridge);
+ ao2_unlock(multiplexed_threads);
+ return -1;
+ }
+
+ multiplexed_thread->pipe[0] = multiplexed_thread->pipe[1] = -1;
+ /* Setup a pipe so we can poke the thread itself when needed */
+ if (pipe(multiplexed_thread->pipe)) {
+ ast_debug(1, "Failed to create a pipe for poking a multiplexed thread for bridge '%p'\n", bridge);
+ ao2_ref(multiplexed_thread, -1);
+ ao2_unlock(multiplexed_threads);
+ return -1;
+ }
+
+ /* Setup each pipe for non-blocking operation */
+ flags = fcntl(multiplexed_thread->pipe[0], F_GETFL);
+ if (fcntl(multiplexed_thread->pipe[0], F_SETFL, flags | O_NONBLOCK) < 0) {
+ ast_log(LOG_WARNING, "Failed to setup first nudge pipe for non-blocking operation on %p (%d: %s)\n", bridge, errno, strerror(errno));
+ ao2_ref(multiplexed_thread, -1);
+ ao2_unlock(multiplexed_threads);
+ return -1;
+ }
+ flags = fcntl(multiplexed_thread->pipe[1], F_GETFL);
+ if (fcntl(multiplexed_thread->pipe[1], F_SETFL, flags | O_NONBLOCK) < 0) {
+ ast_log(LOG_WARNING, "Failed to setup second nudge pipe for non-blocking operation on %p (%d: %s)\n", bridge, errno, strerror(errno));
+ ao2_ref(multiplexed_thread, -1);
+ ao2_unlock(multiplexed_threads);
+ return -1;
+ }
+
+ /* Set up default parameters */
+ multiplexed_thread->thread = AST_PTHREADT_NULL;
+
+ /* Finally link us into the container so others may find us */
+ ao2_link(multiplexed_threads, multiplexed_thread);
+ ast_debug(1, "Created multiplexed thread '%p' for bridge '%p'\n", multiplexed_thread, bridge);
+ } else {
+ ast_debug(1, "Found multiplexed thread '%p' for bridge '%p'\n", multiplexed_thread, bridge);
+ }
+
+ /* Bump the count of the thread structure up by two since the channels for this bridge will be joining shortly */
+ multiplexed_thread->count += 2;
+
+ ao2_unlock(multiplexed_threads);
+
+ bridge->bridge_pvt = multiplexed_thread;
+
+ return 0;
+}
+
+/*! \brief Internal function which nudges the thread */
+static void multiplexed_nudge(struct multiplexed_thread *multiplexed_thread)
+{
+ int nudge = 0;
+
+ if (multiplexed_thread->thread == AST_PTHREADT_NULL) {
+ return;
+ }
+
+ if (write(multiplexed_thread->pipe[1], &nudge, sizeof(nudge)) != sizeof(nudge)) {
+ ast_log(LOG_ERROR, "We couldn't poke multiplexed thread '%p'... something is VERY wrong\n", multiplexed_thread);
+ }
+
+ while (multiplexed_thread->waiting) {
+ sched_yield();
+ }
+
+ return;
+}
+
+/*! \brief Destroy function which unreserves/unreferences/removes a multiplexed thread structure */
+static int multiplexed_bridge_destroy(struct ast_bridge *bridge)
+{
+ struct multiplexed_thread *multiplexed_thread = bridge->bridge_pvt;
+
+ ao2_lock(multiplexed_threads);
+
+ multiplexed_thread->count -= 2;
+
+ if (!multiplexed_thread->count) {
+ ast_debug(1, "Unlinking multiplexed thread '%p' since nobody is using it anymore\n", multiplexed_thread);
+ ao2_unlink(multiplexed_threads, multiplexed_thread);
+ }
+
+ multiplexed_nudge(multiplexed_thread);
+
+ ao2_unlock(multiplexed_threads);
+
+ ao2_ref(multiplexed_thread, -1);
+
+ return 0;
+}
+
+/*! \brief Thread function that executes for multiplexed threads */
+static void *multiplexed_thread_function(void *data)
+{
+ struct multiplexed_thread *multiplexed_thread = data;
+ int fds = multiplexed_thread->pipe[0];
+
+ ao2_lock(multiplexed_thread);
+
+ ast_debug(1, "Starting actual thread for multiplexed thread '%p'\n", multiplexed_thread);
+
+ while (multiplexed_thread->thread != AST_PTHREADT_STOP) {
+ struct ast_channel *winner = NULL, *first = multiplexed_thread->chans[0];
+ int to = -1, outfd = -1;
+
+ /* Move channels around so not just the first one gets priority */
+ memmove(multiplexed_thread->chans, multiplexed_thread->chans + 1, sizeof(struct ast_channel *) * (multiplexed_thread->service_count - 1));
+ multiplexed_thread->chans[multiplexed_thread->service_count - 1] = first;
+
+ multiplexed_thread->waiting = 1;
+ ao2_unlock(multiplexed_thread);
+ winner = ast_waitfor_nandfds(multiplexed_thread->chans, multiplexed_thread->service_count, &fds, 1, NULL, &outfd, &to);
+ multiplexed_thread->waiting = 0;
+ ao2_lock(multiplexed_thread);
+
+ if (outfd > -1) {
+ int nudge;
+
+ if (read(multiplexed_thread->pipe[0], &nudge, sizeof(nudge)) < 0) {
+ if (errno != EINTR && errno != EAGAIN) {
+ ast_log(LOG_WARNING, "read() failed for pipe on multiplexed thread '%p': %s\n", multiplexed_thread, strerror(errno));
+ }
+ }
+ }
+ if (winner && winner->bridge) {
+ ast_bridge_handle_trip(winner->bridge, NULL, winner, -1);
+ }
+ }
+
+ multiplexed_thread->thread = AST_PTHREADT_NULL;
+
+ ast_debug(1, "Stopping actual thread for multiplexed thread '%p'\n", multiplexed_thread);
+
+ ao2_unlock(multiplexed_thread);
+ ao2_ref(multiplexed_thread, -1);
+
+ return NULL;
+}
+
+/*! \brief Helper function which adds or removes a channel and nudges the thread */
+static void multiplexed_add_or_remove(struct multiplexed_thread *multiplexed_thread, struct ast_channel *chan, int add)
+{
+ int i, removed = 0;
+ pthread_t thread = AST_PTHREADT_NULL;
+
+ ao2_lock(multiplexed_thread);
+
+ multiplexed_nudge(multiplexed_thread);
+
+ for (i = 0; i < MULTIPLEXED_MAX_CHANNELS; i++) {
+ if (multiplexed_thread->chans[i] == chan) {
+ if (!add) {
+ multiplexed_thread->chans[i] = NULL;
+ multiplexed_thread->service_count--;
+ removed = 1;
+ }
+ break;
+ } else if (!multiplexed_thread->chans[i] && add) {
+ multiplexed_thread->chans[i] = chan;
+ multiplexed_thread->service_count++;
+ break;
+ }
+ }
+
+ if (multiplexed_thread->service_count && multiplexed_thread->thread == AST_PTHREADT_NULL) {
+ ao2_ref(multiplexed_thread, +1);
+ if (ast_pthread_create(&multiplexed_thread->thread, NULL, multiplexed_thread_function, multiplexed_thread)) {
+ ao2_ref(multiplexed_thread, -1);
+ ast_debug(1, "Failed to create an actual thread for multiplexed thread '%p', trying next time\n", multiplexed_thread);
+ }
+ } else if (!multiplexed_thread->service_count && multiplexed_thread->thread != AST_PTHREADT_NULL) {
+ thread = multiplexed_thread->thread;
+ multiplexed_thread->thread = AST_PTHREADT_STOP;
+ } else if (!add && removed) {
+ memmove(multiplexed_thread->chans + i, multiplexed_thread->chans + i + 1, sizeof(struct ast_channel *) * (MULTIPLEXED_MAX_CHANNELS - (i + 1)));
+ }
+
+ ao2_unlock(multiplexed_thread);
+
+ if (thread != AST_PTHREADT_NULL) {
+ pthread_join(thread, NULL);
+ }
+
+ return;
+}
+
+/*! \brief Join function which actually adds the channel into the array to be monitored */
+static int multiplexed_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan, *c1 = AST_LIST_LAST(&bridge->channels)->chan;
+ struct multiplexed_thread *multiplexed_thread = bridge->bridge_pvt;
+
+ ast_debug(1, "Adding channel '%s' to multiplexed thread '%p' for monitoring\n", bridge_channel->chan->name, multiplexed_thread);
+
+ multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 1);
+
+ /* If the second channel has not yet joined do not make things compatible */
+ if (c0 == c1) {
+ return 0;
+ }
+
+ if (((c0->writeformat == c1->readformat) && (c0->readformat == c1->writeformat) && (c0->nativeformats == c1->nativeformats))) {
+ return 0;
+ }
+
+ return ast_channel_make_compatible(c0, c1);
+}
+
+/*! \brief Leave function which actually removes the channel from the array */
+static int multiplexed_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct multiplexed_thread *multiplexed_thread = bridge->bridge_pvt;
+
+ ast_debug(1, "Removing channel '%s' from multiplexed thread '%p'\n", bridge_channel->chan->name, multiplexed_thread);
+
+ multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 0);
+
+ return 0;
+}
+
+/*! \brief Suspend function which means control of the channel is going elsewhere */
+static void multiplexed_bridge_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct multiplexed_thread *multiplexed_thread = bridge->bridge_pvt;
+
+ ast_debug(1, "Suspending channel '%s' from multiplexed thread '%p'\n", bridge_channel->chan->name, multiplexed_thread);
+
+ multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 0);
+
+ return;
+}
+
+/*! \brief Unsuspend function which means control of the channel is coming back to us */
+static void multiplexed_bridge_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct multiplexed_thread *multiplexed_thread = bridge->bridge_pvt;
+
+ ast_debug(1, "Unsuspending channel '%s' from multiplexed thread '%p'\n", bridge_channel->chan->name, multiplexed_thread);
+
+ multiplexed_add_or_remove(multiplexed_thread, bridge_channel->chan, 1);
+
+ return;
+}
+
+/*! \brief Write function for writing frames into the bridge */
+static enum ast_bridge_write_result multiplexed_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct ast_bridge_channel *other;
+
+ if (AST_LIST_FIRST(&bridge->channels) == AST_LIST_LAST(&bridge->channels)) {
+ return AST_BRIDGE_WRITE_FAILED;
+ }
+
+ if (!(other = (AST_LIST_FIRST(&bridge->channels) == bridge_channel ? AST_LIST_LAST(&bridge->channels) : AST_LIST_FIRST(&bridge->channels)))) {
+ return AST_BRIDGE_WRITE_FAILED;
+ }
+
+ if (other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+ ast_write(other->chan, frame);
+ }
+
+ return AST_BRIDGE_WRITE_SUCCESS;
+}
+
+static struct ast_bridge_technology multiplexed_bridge = {
+ .name = "multiplexed_bridge",
+ .capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX,
+ .preference = AST_BRIDGE_PREFERENCE_HIGH,
+ .formats = AST_FORMAT_AUDIO_MASK | AST_FORMAT_VIDEO_MASK | AST_FORMAT_TEXT_MASK,
+ .create = multiplexed_bridge_create,
+ .destroy = multiplexed_bridge_destroy,
+ .join = multiplexed_bridge_join,
+ .leave = multiplexed_bridge_leave,
+ .suspend = multiplexed_bridge_suspend,
+ .unsuspend = multiplexed_bridge_unsuspend,
+ .write = multiplexed_bridge_write,
+};
+
+static int unload_module(void)
+{
+ int res = ast_bridge_technology_unregister(&multiplexed_bridge);
+
+ ao2_ref(multiplexed_threads, -1);
+
+ return res;
+}
+
+static int load_module(void)
+{
+ if (!(multiplexed_threads = ao2_container_alloc(MULTIPLEXED_BUCKETS, NULL, NULL))) {
+ return AST_MODULE_LOAD_DECLINE;
+ }
+
+ return ast_bridge_technology_register(&multiplexed_bridge);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Multiplexed two channel bridging module");
diff --git a/bridges/bridge_simple.c b/bridges/bridge_simple.c
new file mode 100644
index 000000000..0dbcd25d2
--- /dev/null
+++ b/bridges/bridge_simple.c
@@ -0,0 +1,103 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Simple two channel bridging module
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup bridges
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/frame.h"
+
+static int simple_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct ast_channel *c0 = AST_LIST_FIRST(&bridge->channels)->chan, *c1 = AST_LIST_LAST(&bridge->channels)->chan;
+
+ /* If this is the first channel we can't make it compatible... unless we make it compatible with itself O.o */
+ if (AST_LIST_FIRST(&bridge->channels) == AST_LIST_LAST(&bridge->channels)) {
+ return 0;
+ }
+
+ /* See if we need to make these compatible */
+ if (((c0->writeformat == c1->readformat) && (c0->readformat == c1->writeformat) && (c0->nativeformats == c1->nativeformats))) {
+ return 0;
+ }
+
+ /* BOOM! We do. */
+ return ast_channel_make_compatible(c0, c1);
+}
+
+static enum ast_bridge_write_result simple_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct ast_bridge_channel *other = NULL;
+
+ /* If this is the only channel in this bridge then immediately exit */
+ if (AST_LIST_FIRST(&bridge->channels) == AST_LIST_LAST(&bridge->channels)) {
+ return AST_BRIDGE_WRITE_FAILED;
+ }
+
+ /* Find the channel we actually want to write to */
+ if (!(other = (AST_LIST_FIRST(&bridge->channels) == bridge_channel ? AST_LIST_LAST(&bridge->channels) : AST_LIST_FIRST(&bridge->channels)))) {
+ return AST_BRIDGE_WRITE_FAILED;
+ }
+
+ /* Write the frame out if they are in the waiting state... don't worry about freeing it, the bridging core will take care of it */
+ if (other->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+ ast_write(other->chan, frame);
+ }
+
+ return AST_BRIDGE_WRITE_SUCCESS;
+}
+
+static struct ast_bridge_technology simple_bridge = {
+ .name = "simple_bridge",
+ .capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_THREAD,
+ .preference = AST_BRIDGE_PREFERENCE_MEDIUM,
+ .formats = AST_FORMAT_AUDIO_MASK | AST_FORMAT_VIDEO_MASK | AST_FORMAT_TEXT_MASK,
+ .join = simple_bridge_join,
+ .write = simple_bridge_write,
+};
+
+static int unload_module(void)
+{
+ return ast_bridge_technology_unregister(&simple_bridge);
+}
+
+static int load_module(void)
+{
+ return ast_bridge_technology_register(&simple_bridge);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Simple two channel bridging module");
diff --git a/bridges/bridge_softmix.c b/bridges/bridge_softmix.c
new file mode 100644
index 000000000..4f1e4d76f
--- /dev/null
+++ b/bridges/bridge_softmix.c
@@ -0,0 +1,303 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Multi-party software based channel mixing
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \ingroup bridges
+ *
+ * \todo This bridge operates in 8 kHz mode unless a define is uncommented.
+ * This needs to be improved so the bridge moves between the dominant codec as needed depending
+ * on channels present in the bridge and transcoding capabilities.
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <sys/time.h>
+#include <signal.h>
+#include <errno.h>
+#include <unistd.h>
+
+#include "asterisk/module.h"
+#include "asterisk/channel.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/frame.h"
+#include "asterisk/options.h"
+#include "asterisk/logger.h"
+#include "asterisk/slinfactory.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/timing.h"
+
+/*! \brief Interval at which mixing will take place. Valid options are 10, 20, and 40. */
+#define SOFTMIX_INTERVAL 20
+
+/*! \brief Size of the buffer used for sample manipulation */
+#define SOFTMIX_DATALEN (160 * (SOFTMIX_INTERVAL / 10))
+
+/*! \brief Number of samples we are dealing with */
+#define SOFTMIX_SAMPLES (SOFTMIX_DATALEN / 2)
+
+/*! \brief Define used to turn on 16 kHz audio support */
+/* #define SOFTMIX_16_SUPPORT */
+
+/*! \brief Structure which contains per-channel mixing information */
+struct softmix_channel {
+ /*! Lock to protect this structure */
+ ast_mutex_t lock;
+ /*! Factory which contains audio read in from the channel */
+ struct ast_slinfactory factory;
+ /*! Frame that contains mixed audio to be written out to the channel */
+ struct ast_frame frame;
+ /*! Bit used to indicate that the channel provided audio for this mixing interval */
+ int have_audio:1;
+ /*! Bit used to indicate that a frame is available to be written out to the channel */
+ int have_frame:1;
+ /*! Buffer containing final mixed audio from all sources */
+ short final_buf[SOFTMIX_DATALEN];
+ /*! Buffer containing only the audio from the channel */
+ short our_buf[SOFTMIX_DATALEN];
+};
+
+/*! \brief Function called when a bridge is created */
+static int softmix_bridge_create(struct ast_bridge *bridge)
+{
+ int timingfd;
+
+ if ((timingfd = ast_timer_open()) < 0) {
+ return -1;
+ }
+
+ ast_timer_close(timingfd);
+
+ return 0;
+}
+
+/*! \brief Function called when a channel is joined into the bridge */
+static int softmix_bridge_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct softmix_channel *sc = NULL;
+
+ /* Create a new softmix_channel structure and allocate various things on it */
+ if (!(sc = ast_calloc(1, sizeof(*sc)))) {
+ return -1;
+ }
+
+ /* Can't forget the lock */
+ ast_mutex_init(&sc->lock);
+
+ /* Setup smoother */
+ ast_slinfactory_init(&sc->factory);
+
+ /* Setup frame parameters */
+ sc->frame.frametype = AST_FRAME_VOICE;
+#ifdef SOFTMIX_16_SUPPORT
+ sc->frame.subclass = AST_FORMAT_SLINEAR16;
+#else
+ sc->frame.subclass = AST_FORMAT_SLINEAR;
+#endif
+ sc->frame.data.ptr = sc->final_buf;
+ sc->frame.datalen = SOFTMIX_DATALEN;
+ sc->frame.samples = SOFTMIX_SAMPLES;
+
+ /* Can't forget to record our pvt structure within the bridged channel structure */
+ bridge_channel->bridge_pvt = sc;
+
+ return 0;
+}
+
+/*! \brief Function called when a channel leaves the bridge */
+static int softmix_bridge_leave(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct softmix_channel *sc = bridge_channel->bridge_pvt;
+
+ /* Drop mutex lock */
+ ast_mutex_destroy(&sc->lock);
+
+ /* Drop the factory */
+ ast_slinfactory_destroy(&sc->factory);
+
+ /* Eep! drop ourselves */
+ ast_free(sc);
+
+ return 0;
+}
+
+/*! \brief Function called when a channel writes a frame into the bridge */
+static enum ast_bridge_write_result softmix_bridge_write(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct softmix_channel *sc = bridge_channel->bridge_pvt;
+
+ /* Only accept audio frames, all others are unsupported */
+ if (frame->frametype != AST_FRAME_VOICE) {
+ return AST_BRIDGE_WRITE_UNSUPPORTED;
+ }
+
+ ast_mutex_lock(&sc->lock);
+
+ /* If a frame was provided add it to the smoother */
+#ifdef SOFTMIX_16_SUPPORT
+ if (frame->frametype == AST_FRAME_VOICE && frame->subclass == AST_FORMAT_SLINEAR16) {
+#else
+ if (frame->frametype == AST_FRAME_VOICE && frame->subclass == AST_FORMAT_SLINEAR) {
+#endif
+ ast_slinfactory_feed(&sc->factory, frame);
+ }
+
+ /* If a frame is ready to be written out, do so */
+ if (sc->have_frame) {
+ ast_write(bridge_channel->chan, &sc->frame);
+ sc->have_frame = 0;
+ }
+
+ /* Alllll done */
+ ast_mutex_unlock(&sc->lock);
+
+ return AST_BRIDGE_WRITE_SUCCESS;
+}
+
+/*! \brief Function called when the channel's thread is poked */
+static int softmix_bridge_poke(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct softmix_channel *sc = bridge_channel->bridge_pvt;
+
+ ast_mutex_lock(&sc->lock);
+
+ if (sc->have_frame) {
+ ast_write(bridge_channel->chan, &sc->frame);
+ sc->have_frame = 0;
+ }
+
+ ast_mutex_unlock(&sc->lock);
+
+ return 0;
+}
+
+/*! \brief Function which acts as the mixing thread */
+static int softmix_bridge_thread(struct ast_bridge *bridge)
+{
+ int timingfd;
+
+ if ((timingfd = ast_timer_open()) < 0) {
+ return -1;
+ }
+
+ ast_timer_set_rate(timingfd, (1000 / SOFTMIX_INTERVAL));
+
+ while (!bridge->stop && !bridge->refresh && bridge->array_num) {
+ struct ast_bridge_channel *bridge_channel = NULL;
+ short buf[SOFTMIX_DATALEN] = {0, };
+ int timeout = -1;
+
+ /* Go through pulling audio from each factory that has it available */
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ struct softmix_channel *sc = bridge_channel->bridge_pvt;
+
+ ast_mutex_lock(&sc->lock);
+
+ /* Try to get audio from the factory if available */
+ if (ast_slinfactory_available(&sc->factory) >= SOFTMIX_SAMPLES && ast_slinfactory_read(&sc->factory, sc->our_buf, SOFTMIX_SAMPLES)) {
+ short *data1, *data2;
+ int i;
+
+ /* Put into the local final buffer */
+ for (i = 0, data1 = buf, data2 = sc->our_buf; i < SOFTMIX_DATALEN; i++, data1++, data2++)
+ ast_slinear_saturated_add(data1, data2);
+ /* Yay we have our own audio */
+ sc->have_audio = 1;
+ } else {
+ /* Awww we don't have audio ;( */
+ sc->have_audio = 0;
+ }
+ ast_mutex_unlock(&sc->lock);
+ }
+
+ /* Next step go through removing the channel's own audio and creating a good frame... */
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ struct softmix_channel *sc = bridge_channel->bridge_pvt;
+ int i = 0;
+
+ /* Copy from local final buffer to our final buffer */
+ memcpy(sc->final_buf, buf, sizeof(sc->final_buf));
+
+ /* If we provided audio then take it out */
+ if (sc->have_audio) {
+ for (i = 0; i < SOFTMIX_DATALEN; i++) {
+ ast_slinear_saturated_subtract(&sc->final_buf[i], &sc->our_buf[i]);
+ }
+ }
+
+ /* The frame is now ready for use... */
+ sc->have_frame = 1;
+
+ /* Poke bridged channel thread just in case */
+ pthread_kill(bridge_channel->thread, SIGURG);
+ }
+
+ ao2_unlock(bridge);
+
+ /* Wait for the timing source to tell us to wake up and get things done */
+ ast_waitfor_n_fd(&timingfd, 1, &timeout, NULL);
+
+ ast_timer_ack(timingfd, 1);
+
+ ao2_lock(bridge);
+ }
+
+ ast_timer_set_rate(timingfd, 0);
+ ast_timer_close(timingfd);
+
+ return 0;
+}
+
+static struct ast_bridge_technology softmix_bridge = {
+ .name = "softmix",
+ .capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX | AST_BRIDGE_CAPABILITY_THREAD | AST_BRIDGE_CAPABILITY_MULTITHREADED,
+ .preference = AST_BRIDGE_PREFERENCE_LOW,
+#ifdef SOFTMIX_16_SUPPORT
+ .formats = AST_FORMAT_SLINEAR16,
+#else
+ .formats = AST_FORMAT_SLINEAR,
+#endif
+ .create = softmix_bridge_create,
+ .join = softmix_bridge_join,
+ .leave = softmix_bridge_leave,
+ .write = softmix_bridge_write,
+ .thread = softmix_bridge_thread,
+ .poke = softmix_bridge_poke,
+};
+
+static int unload_module(void)
+{
+ return ast_bridge_technology_unregister(&softmix_bridge);
+}
+
+static int load_module(void)
+{
+ return ast_bridge_technology_register(&softmix_bridge);
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Multi-party software based channel mixing");
diff --git a/channels/chan_bridge.c b/channels/chan_bridge.c
new file mode 100644
index 000000000..84909e795
--- /dev/null
+++ b/channels/chan_bridge.c
@@ -0,0 +1,246 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 1999 - 2008, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ *
+ * \brief Bridge Interaction Channel
+ *
+ * \ingroup channel_drivers
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <fcntl.h>
+#include <sys/signal.h>
+
+#include "asterisk/lock.h"
+#include "asterisk/channel.h"
+#include "asterisk/config.h"
+#include "asterisk/module.h"
+#include "asterisk/pbx.h"
+#include "asterisk/sched.h"
+#include "asterisk/io.h"
+#include "asterisk/rtp.h"
+#include "asterisk/acl.h"
+#include "asterisk/callerid.h"
+#include "asterisk/file.h"
+#include "asterisk/cli.h"
+#include "asterisk/app.h"
+#include "asterisk/bridging.h"
+
+static struct ast_channel *bridge_request(const char *type, int format, void *data, int *cause);
+static int bridge_call(struct ast_channel *ast, char *dest, int timeout);
+static int bridge_hangup(struct ast_channel *ast);
+static struct ast_frame *bridge_read(struct ast_channel *ast);
+static int bridge_write(struct ast_channel *ast, struct ast_frame *f);
+static struct ast_channel *bridge_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge);
+
+static const struct ast_channel_tech bridge_tech = {
+ .type = "Bridge",
+ .description = "Bridge Interaction Channel",
+ .capabilities = -1,
+ .requester = bridge_request,
+ .call = bridge_call,
+ .hangup = bridge_hangup,
+ .read = bridge_read,
+ .write = bridge_write,
+ .write_video = bridge_write,
+ .exception = bridge_read,
+ .bridged_channel = bridge_bridgedchannel,
+};
+
+struct bridge_pvt {
+ ast_mutex_t lock; /*!< Lock that protects this structure */
+ struct ast_channel *input; /*!< Input channel - talking to source */
+ struct ast_channel *output; /*!< Output channel - talking to bridge */
+};
+
+/*! \brief Called when the user of this channel wants to get the actual channel in the bridge */
+static struct ast_channel *bridge_bridgedchannel(struct ast_channel *chan, struct ast_channel *bridge)
+{
+ struct bridge_pvt *p = chan->tech_pvt;
+ return (chan == p->input) ? p->output : bridge;
+}
+
+/*! \brief Called when a frame should be read from the channel */
+static struct ast_frame *bridge_read(struct ast_channel *ast)
+{
+ return &ast_null_frame;
+}
+
+/*! \brief Called when a frame should be written out to a channel */
+static int bridge_write(struct ast_channel *ast, struct ast_frame *f)
+{
+ struct bridge_pvt *p = ast->tech_pvt;
+ struct ast_channel *other;
+
+ ast_mutex_lock(&p->lock);
+
+ other = (p->input == ast ? p->output : p->input);
+
+ while (other && ast_channel_trylock(other)) {
+ ast_mutex_unlock(&p->lock);
+ do {
+ CHANNEL_DEADLOCK_AVOIDANCE(ast);
+ } while (ast_mutex_trylock(&p->lock));
+ other = (p->input == ast ? p->output : p->input);
+ }
+
+ /* We basically queue the frame up on the other channel if present */
+ if (other) {
+ ast_queue_frame(other, f);
+ ast_channel_unlock(other);
+ }
+
+ ast_mutex_unlock(&p->lock);
+
+ return 0;
+}
+
+/*! \brief Called when the channel should actually be dialed */
+static int bridge_call(struct ast_channel *ast, char *dest, int timeout)
+{
+ struct bridge_pvt *p = ast->tech_pvt;
+
+ /* If no bridge has been provided on the input channel, bail out */
+ if (!ast->bridge) {
+ return -1;
+ }
+
+ /* Impart the output channel upon the given bridge of the input channel */
+ ast_bridge_impart(p->input->bridge, p->output, NULL, NULL);
+
+ return 0;
+}
+
+/*! \brief Helper function to not deadlock when queueing the hangup frame */
+static void bridge_queue_hangup(struct bridge_pvt *p, struct ast_channel *us)
+{
+ struct ast_channel *other = (p->input == us ? p->output : p->input);
+
+ while (other && ast_channel_trylock(other)) {
+ ast_mutex_unlock(&p->lock);
+ do {
+ CHANNEL_DEADLOCK_AVOIDANCE(us);
+ } while (ast_mutex_trylock(&p->lock));
+ other = (p->input == us ? p->output : p->input);
+ }
+
+ /* We basically queue the frame up on the other channel if present */
+ if (other) {
+ ast_queue_hangup(other);
+ ast_channel_unlock(other);
+ }
+
+ return;
+}
+
+/*! \brief Called when a channel should be hung up */
+static int bridge_hangup(struct ast_channel *ast)
+{
+ struct bridge_pvt *p = ast->tech_pvt;
+
+ ast_mutex_lock(&p->lock);
+
+ /* Figure out which channel this is... and set it to NULL as it has gone, but also queue up a hangup frame. */
+ if (p->input == ast) {
+ if (p->output) {
+ bridge_queue_hangup(p, ast);
+ }
+ p->input = NULL;
+ } else if (p->output == ast) {
+ if (p->input) {
+ bridge_queue_hangup(p, ast);
+ }
+ p->output = NULL;
+ }
+
+ /* Deal with the Asterisk portion of it */
+ ast->tech_pvt = NULL;
+
+ /* If both sides have been terminated free the structure and be done with things */
+ if (!p->input && !p->output) {
+ ast_mutex_unlock(&p->lock);
+ ast_mutex_destroy(&p->lock);
+ ast_free(p);
+ } else {
+ ast_mutex_unlock(&p->lock);
+ }
+
+ return 0;
+}
+
+/*! \brief Called when we want to place a call somewhere, but not actually call it... yet */
+static struct ast_channel *bridge_request(const char *type, int format, void *data, int *cause)
+{
+ struct bridge_pvt *p = NULL;
+
+ /* Try to allocate memory for our very minimal pvt structure */
+ if (!(p = ast_calloc(1, sizeof(*p)))) {
+ return NULL;
+ }
+
+ /* Try to grab two Asterisk channels to use as input and output channels */
+ if (!(p->input = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-input", p))) {
+ ast_free(p);
+ return NULL;
+ }
+ if (!(p->output = ast_channel_alloc(1, AST_STATE_UP, 0, 0, "", "", "", 0, "Bridge/%p-output", p))) {
+ ast_channel_free(p->input);
+ ast_free(p);
+ return NULL;
+ }
+
+ /* Setup the lock on the pvt structure, we will need that */
+ ast_mutex_init(&p->lock);
+
+ /* Setup parameters on both new channels */
+ p->input->tech = p->output->tech = &bridge_tech;
+ p->input->tech_pvt = p->output->tech_pvt = p;
+ p->input->nativeformats = p->output->nativeformats = AST_FORMAT_SLINEAR;
+ p->input->readformat = p->output->readformat = AST_FORMAT_SLINEAR;
+ p->input->rawreadformat = p->output->rawreadformat = AST_FORMAT_SLINEAR;
+ p->input->writeformat = p->output->writeformat = AST_FORMAT_SLINEAR;
+ p->input->rawwriteformat = p->output->rawwriteformat = AST_FORMAT_SLINEAR;
+
+ return p->input;
+}
+
+/*! \brief Load module into PBX, register channel */
+static int load_module(void)
+{
+ /* Make sure we can register our channel type */
+ if (ast_channel_register(&bridge_tech)) {
+ ast_log(LOG_ERROR, "Unable to register channel class 'Bridge'\n");
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+/*! \brief Unload the bridge interaction channel from Asterisk */
+static int unload_module(void)
+{
+ ast_channel_unregister(&bridge_tech);
+ return 0;
+}
+
+AST_MODULE_INFO_STANDARD(ASTERISK_GPL_KEY, "Bridge Interaction Channel");
diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h
new file mode 100644
index 000000000..33fe041ca
--- /dev/null
+++ b/include/asterisk/bridging.h
@@ -0,0 +1,426 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 - 2009, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Channel Bridging API
+ * \author Joshua Colp <jcolp@digium.com>
+ * \ref AstBridging
+ */
+
+/*!
+ * \page AstBridging Channel Bridging API
+ *
+ * The purpose of this API is to provide an easy and flexible way to bridge
+ * channels of different technologies with different features.
+ *
+ * Bridging technologies provide the mechanism that do the actual handling
+ * of frames between channels. They provide capability information, codec information,
+ * and preference value to assist the bridging core in choosing a bridging technology when
+ * creating a bridge. Different bridges may use different bridging technologies based on needs
+ * but once chosen they all operate under the same premise; they receive frames and send frames.
+ *
+ * Bridges are a combination of bridging technology, channels, and features. A
+ * developer creates a new bridge based on what they are currently expecting to do
+ * with it or what they will do with it in the future. The bridging core determines what
+ * available bridging technology will best fit the requirements and creates a new bridge.
+ * Once created, channels can be added to the bridge in a blocking or non-blocking fashion.
+ *
+ * Features are such things as channel muting or DTMF based features such as attended transfer,
+ * blind transfer, and hangup. Feature information must be set at the most granular level, on
+ * the channel. While you can use features on a global scope the presence of a feature structure
+ * on the channel will override the global scope. An example would be having the bridge muted
+ * at global scope and attended transfer enabled on a channel. Since the channel itself is not muted
+ * it would be able to speak.
+ *
+ * Feature hooks allow a developer to tell the bridging core that when a DTMF string
+ * is received from a channel a callback should be called in their application. For
+ * example, a conference bridge application may want to provide an IVR to control various
+ * settings on the conference bridge. This can be accomplished by attaching a feature hook
+ * that calls an IVR function when a DTMF string is entered.
+ *
+ */
+
+#ifndef _ASTERISK_BRIDGING_H
+#define _ASTERISK_BRIDGING_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/bridging_features.h"
+
+/*! \brief Capabilities for a bridge technology */
+enum ast_bridge_capability {
+ /*! Bridge is only capable of mixing 2 channels */
+ AST_BRIDGE_CAPABILITY_1TO1MIX = (1 << 1),
+ /*! Bridge is capable of mixing 2 or more channels */
+ AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 2),
+ /*! Bridge should natively bridge two channels if possible */
+ AST_BRIDGE_CAPABILITY_NATIVE = (1 << 3),
+ /*! Bridge should run using the multithreaded model */
+ AST_BRIDGE_CAPABILITY_MULTITHREADED = (1 << 4),
+ /*! Bridge should run a central bridge thread */
+ AST_BRIDGE_CAPABILITY_THREAD = (1 << 5),
+ /*! Bridge technology can do video mixing (or something along those lines) */
+ AST_BRIDGE_CAPABILITY_VIDEO = (1 << 6),
+ /*! Bridge technology can optimize things based on who is talking */
+ AST_BRIDGE_CAPABILITY_OPTIMIZE = (1 << 7),
+};
+
+/*! \brief State information about a bridged channel */
+enum ast_bridge_channel_state {
+ /*! Waiting for a signal */
+ AST_BRIDGE_CHANNEL_STATE_WAIT = 0,
+ /*! Bridged channel has ended itself (it has hung up) */
+ AST_BRIDGE_CHANNEL_STATE_END,
+ /*! Bridged channel should be hung up */
+ AST_BRIDGE_CHANNEL_STATE_HANGUP,
+ /*! Bridged channel should be removed from the bridge without being hung up */
+ AST_BRIDGE_CHANNEL_STATE_DEPART,
+ /*! Bridged channel is executing a feature hook */
+ AST_BRIDGE_CHANNEL_STATE_FEATURE,
+ /*! Bridged channel is sending a DTMF stream out */
+ AST_BRIDGE_CHANNEL_STATE_DTMF,
+};
+
+/*! \brief Return values for bridge technology write function */
+enum ast_bridge_write_result {
+ /*! Bridge technology wrote out frame fine */
+ AST_BRIDGE_WRITE_SUCCESS = 0,
+ /*! Bridge technology attempted to write out the frame but failed */
+ AST_BRIDGE_WRITE_FAILED,
+ /*! Bridge technology does not support writing out a frame of this type */
+ AST_BRIDGE_WRITE_UNSUPPORTED,
+};
+
+struct ast_bridge_technology;
+struct ast_bridge;
+
+/*!
+ * \brief Structure that contains information regarding a channel in a bridge
+ */
+struct ast_bridge_channel {
+ /*! Lock to protect this data structure */
+ ast_mutex_t lock;
+ /*! Condition, used if we want to wake up a thread waiting on the bridged channel */
+ ast_cond_t cond;
+ /*! Current bridged channel state */
+ enum ast_bridge_channel_state state;
+ /*! Asterisk channel participating in the bridge */
+ struct ast_channel *chan;
+ /*! Asterisk channel we are swapping with (if swapping) */
+ struct ast_channel *swap;
+ /*! Bridge this channel is participating in */
+ struct ast_bridge *bridge;
+ /*! Private information unique to the bridge technology */
+ void *bridge_pvt;
+ /*! Thread handling the bridged channel */
+ pthread_t thread;
+ /*! Additional file descriptors to look at */
+ int fds[4];
+ /*! Bit to indicate whether the channel is suspended from the bridge or not */
+ unsigned int suspended:1;
+ /*! Features structure for features that are specific to this channel */
+ struct ast_bridge_features *features;
+ /*! Queue of DTMF digits used for DTMF streaming */
+ char dtmf_stream_q[8];
+ /*! Linked list information */
+ AST_LIST_ENTRY(ast_bridge_channel) entry;
+};
+
+/*!
+ * \brief Structure that contains information about a bridge
+ */
+struct ast_bridge {
+ /*! Number of channels participating in the bridge */
+ int num;
+ /*! Bit to indicate that the bridge thread is waiting on channels in the bridge array */
+ unsigned int waiting:1;
+ /*! Bit to indicate the bridge thread should stop */
+ unsigned int stop:1;
+ /*! Bit to indicate the bridge thread should refresh itself */
+ unsigned int refresh:1;
+ /*! Bridge flags to tweak behavior */
+ struct ast_flags feature_flags;
+ /*! Bridge technology that is handling the bridge */
+ struct ast_bridge_technology *technology;
+ /*! Private information unique to the bridge technology */
+ void *bridge_pvt;
+ /*! Thread running the bridge */
+ pthread_t thread;
+ /*! Enabled features information */
+ struct ast_bridge_features features;
+ /*! Array of channels that the bridge thread is currently handling */
+ struct ast_channel **array;
+ /*! Number of channels in the above array */
+ size_t array_num;
+ /*! Number of channels the array can handle */
+ size_t array_size;
+ /*! Linked list of channels participating in the bridge */
+ AST_LIST_HEAD_NOLOCK(, ast_bridge_channel) channels;
+};
+
+/*! \brief Create a new bridge
+ *
+ * \param capabilities The capabilities that we require to be used on the bridge
+ * \param flags Flags that will alter the behavior of the bridge
+ *
+ * \retval a pointer to a new bridge on success
+ * \retval NULL on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge *bridge;
+ * bridge = ast_bridge_new(AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE);
+ * \endcode
+ *
+ * This creates a simple two party bridge that will be destroyed once one of
+ * the channels hangs up.
+ */
+struct ast_bridge *ast_bridge_new(int capabilities, int flags);
+
+/*! \brief See if it is possible to create a bridge
+ *
+ * \param capabilities The capabilities that the bridge will use
+ *
+ * \retval 1 if possible
+ * \retval 0 if not possible
+ *
+ * Example usage:
+ *
+ * \code
+ * int possible = ast_bridge_check(AST_BRIDGE_CAPABILITY_1TO1MIX);
+ * \endcode
+ *
+ * This sees if it is possible to create a bridge capable of bridging two channels
+ * together.
+ */
+int ast_bridge_check(int capabilities);
+
+/*! \brief Destroy a bridge
+ *
+ * \param bridge Bridge to destroy
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_destroy(bridge);
+ * \endcode
+ *
+ * This destroys a bridge that was previously created using ast_bridge_new.
+ */
+int ast_bridge_destroy(struct ast_bridge *bridge);
+
+/*! \brief Join (blocking) a channel to a bridge
+ *
+ * \param bridge Bridge to join
+ * \param chan Channel to join
+ * \param swap Channel to swap out if swapping
+ * \param features Bridge features structure
+ *
+ * \retval state that channel exited the bridge with
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_join(bridge, chan, NULL, NULL);
+ * \endcode
+ *
+ * This adds a channel pointed to by the chan pointer to the bridge pointed to by
+ * the bridge pointer. This function will not return until the channel has been
+ * removed from the bridge, swapped out for another channel, or has hung up.
+ *
+ * If this channel will be replacing another channel the other channel can be specified
+ * in the swap parameter. The other channel will be thrown out of the bridge in an
+ * atomic fashion.
+ *
+ * If channel specific features are enabled a pointer to the features structure
+ * can be specified in the features parameter.
+ */
+enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features);
+
+/*! \brief Impart (non-blocking) a channel on a bridge
+ *
+ * \param bridge Bridge to impart on
+ * \param chan Channel to impart
+ * \param swap Channel to swap out if swapping
+ * \param features Bridge features structure
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_impart(bridge, chan, NULL, NULL);
+ * \endcode
+ *
+ * This adds a channel pointed to by the chan pointer to the bridge pointed to by
+ * the bridge pointer. This function will return immediately and will not wait
+ * until the channel is no longer part of the bridge.
+ *
+ * If this channel will be replacing another channel the other channel can be specified
+ * in the swap parameter. The other channel will be thrown out of the bridge in an
+ * atomic fashion.
+ *
+ * If channel specific features are enabled a pointer to the features structure
+ * can be specified in the features parameter.
+ */
+int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features);
+
+/*! \brief Depart a channel from a bridge
+ *
+ * \param bridge Bridge to depart from
+ * \param chan Channel to depart
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_depart(bridge, chan);
+ * \endcode
+ *
+ * This removes the channel pointed to by the chan pointer from the bridge
+ * pointed to by the bridge pointer and gives control to the calling thread.
+ * This does not hang up the channel.
+ *
+ * \note This API call can only be used on channels that were added to the bridge
+ * using the ast_bridge_impart API call.
+ */
+int ast_bridge_depart(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*! \brief Remove a channel from a bridge
+ *
+ * \param bridge Bridge that the channel is to be removed from
+ * \param chan Channel to remove
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_remove(bridge, chan);
+ * \endcode
+ *
+ * This removes the channel pointed to by the chan pointer from the bridge
+ * pointed to by the bridge pointer and requests that it be hung up. Control
+ * over the channel will NOT be given to the calling thread.
+ *
+ * \note This API call can be used on channels that were added to the bridge
+ * using both ast_bridge_join and ast_bridge_impart.
+ */
+int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*! \brief Merge two bridges together
+ *
+ * \param bridge0 First bridge
+ * \param bridge1 Second bridge
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_merge(bridge0, bridge1);
+ * \endcode
+ *
+ * This merges the bridge pointed to by bridge1 with the bridge pointed to by bridge0.
+ * In reality all of the channels in bridge1 are simply moved to bridge0.
+ *
+ * \note The second bridge specified is not destroyed when this operation is
+ * completed.
+ */
+int ast_bridge_merge(struct ast_bridge *bridge0, struct ast_bridge *bridge1);
+
+/*! \brief Suspend a channel temporarily from a bridge
+ *
+ * \param bridge Bridge to suspend the channel from
+ * \param chan Channel to suspend
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_suspend(bridge, chan);
+ * \endcode
+ *
+ * This suspends the channel pointed to by chan from the bridge pointed to by bridge temporarily.
+ * Control of the channel is given to the calling thread. This differs from ast_bridge_depart as
+ * the channel will not be removed from the bridge.
+ *
+ * \note This API call can be used on channels that were added to the bridge
+ * using both ast_bridge_join and ast_bridge_impart.
+ */
+int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*! \brief Unsuspend a channel from a bridge
+ *
+ * \param bridge Bridge to unsuspend the channel from
+ * \param chan Channel to unsuspend
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_unsuspend(bridge, chan);
+ * \endcode
+ *
+ * This unsuspends the channel pointed to by chan from the bridge pointed to by bridge.
+ * The bridge will go back to handling the channel once this function returns.
+ *
+ * \note You must not mess with the channel once this function returns.
+ * Doing so may result in bad things happening.
+ */
+int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*! \brief Change the state of a bridged channel
+ *
+ * \param bridge_channel Channel to change the state on
+ * \param new_state The new state to place the channel into
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ * \endcode
+ *
+ * This places the channel pointed to by bridge_channel into the state
+ * AST_BRIDGE_CHANNEL_STATE_WAIT.
+ *
+ * \note This API call is only meant to be used in feature hook callbacks to
+ * make sure the channel either hangs up or returns to the bridge.
+ */
+void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_BRIDGING_H */
diff --git a/include/asterisk/bridging_features.h b/include/asterisk/bridging_features.h
new file mode 100644
index 000000000..ee36a561c
--- /dev/null
+++ b/include/asterisk/bridging_features.h
@@ -0,0 +1,298 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Channel Bridging API
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#ifndef _ASTERISK_BRIDGING_FEATURES_H
+#define _ASTERISK_BRIDGING_FEATURES_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief Flags used for bridge features */
+enum ast_bridge_feature_flags {
+ /*! Upon hangup the bridge should be discontinued */
+ AST_BRIDGE_FLAG_DISSOLVE = (1 << 0),
+ /*! Move between bridging technologies as needed. */
+ AST_BRIDGE_FLAG_SMART = (1 << 1),
+};
+
+/*! \brief Built in features */
+enum ast_bridge_builtin_feature {
+ /*! DTMF Based Blind Transfer */
+ AST_BRIDGE_BUILTIN_BLINDTRANSFER = 0,
+ /*! DTMF Based Attended Transfer */
+ AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER,
+ /*! DTMF Based Hangup Feature */
+ AST_BRIDGE_BUILTIN_HANGUP,
+ /*! End terminator for list of built in features. Must remain last. */
+ AST_BRIDGE_BUILTIN_END,
+};
+
+struct ast_bridge;
+struct ast_bridge_channel;
+
+/*!
+ * \brief Features hook callback type
+ *
+ * \param bridge The bridge that the channel is part of
+ * \param bridge_channel Channel executing the feature
+ * \param hook_pvt Private data passed in when the hook was created
+ *
+ * \retval 0 success
+ * \retval -1 failure
+ */
+typedef int (*ast_bridge_features_hook_callback)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, void *hook_pvt);
+
+/*!
+ * \brief Maximum length of a DTMF feature string
+ */
+#define MAXIMUM_DTMF_FEATURE_STRING 8
+
+/*!
+ * \brief Structure that is the essence of a features hook
+ */
+struct ast_bridge_features_hook {
+ /*! DTMF String that is examined during a feature hook lookup */
+ char dtmf[MAXIMUM_DTMF_FEATURE_STRING];
+ /*! Callback that is called when DTMF string is matched */
+ ast_bridge_features_hook_callback callback;
+ /*! Unique data that was passed into us */
+ void *hook_pvt;
+ /*! Linked list information */
+ AST_LIST_ENTRY(ast_bridge_features_hook) entry;
+};
+
+/*!
+ * \brief Structure that contains features information
+ */
+struct ast_bridge_features {
+ /*! Attached DTMF based feature hooks */
+ AST_LIST_HEAD_NOLOCK(, ast_bridge_features_hook) hooks;
+ /*! Feature flags that are enabled */
+ struct ast_flags feature_flags;
+ /*! Bit to indicate that this structure is useful and should be considered when looking for features */
+ unsigned int usable:1;
+ /*! Bit to indicate whether the channel/bridge is muted or not */
+ unsigned int mute:1;
+};
+
+/*!
+ * \brief Structure that contains configuration information for the blind transfer built in feature
+ */
+struct ast_bridge_features_blind_transfer {
+ /*! Context to use for transfers */
+ char context[AST_MAX_CONTEXT];
+};
+
+/*!
+ * \brief Structure that contains configuration information for the attended transfer built in feature
+ */
+struct ast_bridge_features_attended_transfer {
+ /*! DTMF string used to abort the transfer */
+ char abort[MAXIMUM_DTMF_FEATURE_STRING];
+ /*! DTMF string used to turn the transfer into a three way conference */
+ char threeway[MAXIMUM_DTMF_FEATURE_STRING];
+ /*! DTMF string used to complete the transfer */
+ char complete[MAXIMUM_DTMF_FEATURE_STRING];
+ /*! Context to use for transfers */
+ char context[AST_MAX_CONTEXT];
+};
+
+/*! \brief Register a handler for a built in feature
+ *
+ * \param feature The feature that the handler will be responsible for
+ * \param callback The callback function that will handle it
+ * \param dtmf Default DTMF string used to activate the feature
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_features_register(AST_BRIDGE_BUILTIN_ATTENDED_TRANSFER, bridge_builtin_attended_transfer, "*1");
+ * \endcode
+ *
+ * This registers the function bridge_builtin_attended_transfer as the function responsible for the built in
+ * attended transfer feature.
+ */
+int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_features_hook_callback callback, const char *dtmf);
+
+/*! \brief Unregister a handler for a built in feature
+ *
+ * \param feature The feature to unregister
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_features_unregister(AST_BRIDGE_BUILTIN_ATTENDED_TRANSFER);
+ * \endcode
+ *
+ * This unregisters the function that is handling the built in attended transfer feature.
+ */
+int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature);
+
+/*! \brief Attach a custom hook to a bridge features structure
+ *
+ * \param features Bridge features structure
+ * \param dtmf DTMF string to be activated upon
+ * \param callback Function to execute upon activation
+ * \param hook_pvt Unique data
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_hook(&features, "#", pound_callback, NULL);
+ * \endcode
+ *
+ * This makes the bridging core call pound_callback if a channel that has this
+ * feature structure inputs the DTMF string '#'. A pointer to useful data may be
+ * provided to the hook_pvt parameter.
+ *
+ * \note It is important that the callback set the bridge channel state back to
+ * AST_BRIDGE_CHANNEL_STATE_WAIT or the bridge thread will not service the channel.
+ */
+int ast_bridge_features_hook(struct ast_bridge_features *features, const char *dtmf, ast_bridge_features_hook_callback callback, void *hook_pvt);
+
+/*! \brief Enable a built in feature on a bridge features structure
+ *
+ * \param features Bridge features structure
+ * \param feature Feature to enable
+ * \param dtmf Optionally the DTMF stream to trigger the feature, if not specified it will be the default
+ * \param config Configuration structure unique to the built in type
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_enable(&features, AST_BRIDGE_BUILTIN_ATTENDEDTRANSFER, NULL);
+ * \endcode
+ *
+ * This enables the attended transfer DTMF option using the default DTMF string. An alternate
+ * string may be provided using the dtmf parameter. Internally this is simply setting up a hook
+ * to a built in feature callback function.
+ */
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config);
+
+/*! \brief Set a flag on a bridge features structure
+ *
+ * \param features Bridge features structure
+ * \param flag Flag to enable
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_set_flag(&features, AST_BRIDGE_FLAG_DISSOLVE);
+ * \endcode
+ *
+ * This sets the AST_BRIDGE_FLAG_DISSOLVE feature to be enabled on the features structure
+ * 'features'.
+ */
+int ast_bridge_features_set_flag(struct ast_bridge_features *features, enum ast_bridge_feature_flags flag);
+
+/*! \brief Initialize bridge features structure
+ *
+ * \param features Bridge featues structure
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * \endcode
+ *
+ * This initializes the feature structure 'features' to have nothing enabled.
+ *
+ * \note This MUST be called before enabling features or flags. Failure to do so
+ * may result in a crash.
+ */
+int ast_bridge_features_init(struct ast_bridge_features *features);
+
+/*! \brief Clean up the contents of a bridge features structure
+ *
+ * \param features Bridge features structure
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge_features features;
+ * ast_bridge_features_init(&features);
+ * ast_bridge_features_cleanup(&features);
+ * \endcode
+ *
+ * This cleans up the feature structure 'features'.
+ *
+ * \note This MUST be called after the features structure is done being used
+ * or a memory leak may occur.
+ */
+int ast_bridge_features_cleanup(struct ast_bridge_features *features);
+
+/*! \brief Play a DTMF stream into a bridge, optionally not to a given channel
+ *
+ * \param bridge Bridge to play stream into
+ * \param dtmf DTMF to play
+ * \param chan Channel to optionally not play to
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_dtmf_stream(bridge, "0123456789", NULL);
+ * \endcode
+ *
+ * This sends the DTMF digits '0123456789' to all channels in the bridge pointed to
+ * by the bridge pointer. Optionally a channel may be excluded by passing it's channel pointer
+ * using the chan parameter.
+ */
+int ast_bridge_dtmf_stream(struct ast_bridge *bridge, const char *dtmf, struct ast_channel *chan);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_BRIDGING_FEATURES_H */
diff --git a/include/asterisk/bridging_technology.h b/include/asterisk/bridging_technology.h
new file mode 100644
index 000000000..58b27a4b4
--- /dev/null
+++ b/include/asterisk/bridging_technology.h
@@ -0,0 +1,180 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2009, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ * \brief Channel Bridging API
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#ifndef _ASTERISK_BRIDGING_TECHNOLOGY_H
+#define _ASTERISK_BRIDGING_TECHNOLOGY_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+/*! \brief Preference for choosing the bridge technology */
+enum ast_bridge_preference {
+ /*! Bridge technology should have high precedence over other bridge technologies */
+ AST_BRIDGE_PREFERENCE_HIGH = 0,
+ /*! Bridge technology is decent, not the best but should still be considered over low */
+ AST_BRIDGE_PREFERENCE_MEDIUM,
+ /*! Bridge technology is low, it should not be considered unless it is absolutely needed */
+ AST_BRIDGE_PREFERENCE_LOW,
+};
+
+/*!
+ * \brief Structure that is the essence of a bridge technology
+ */
+struct ast_bridge_technology {
+ /*! Unique name to this bridge technology */
+ const char *name;
+ /*! The capabilities that this bridge technology is capable of */
+ int capabilities;
+ /*! Preference level that should be used when determining whether to use this bridge technology or not */
+ enum ast_bridge_preference preference;
+ /*! Callback for when a bridge is being created */
+ int (*create)(struct ast_bridge *bridge);
+ /*! Callback for when a bridge is being destroyed */
+ int (*destroy)(struct ast_bridge *bridge);
+ /*! Callback for when a channel is being added to a bridge */
+ int (*join)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+ /*! Callback for when a channel is leaving a bridge */
+ int (*leave)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+ /*! Callback for when a channel is suspended from the bridge */
+ void (*suspend)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+ /*! Callback for when a channel is unsuspended from the bridge */
+ void (*unsuspend)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+ /*! Callback to see if a channel is compatible with the bridging technology */
+ int (*compatible)(struct ast_bridge_channel *bridge_channel);
+ /*! Callback for writing a frame into the bridging technology */
+ enum ast_bridge_write_result (*write)(struct ast_bridge *bridge, struct ast_bridge_channel *bridged_channel, struct ast_frame *frame);
+ /*! Callback for when a file descriptor trips */
+ int (*fd)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, int fd);
+ /*! Callback for replacement thread function */
+ int (*thread)(struct ast_bridge *bridge);
+ /*! Callback for poking a bridge thread */
+ int (*poke)(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel);
+ /*! Formats that the bridge technology supports */
+ int formats;
+ /*! Bit to indicate whether the bridge technology is currently suspended or not */
+ unsigned int suspended:1;
+ /*! Module this bridge technology belongs to. Is used for reference counting when creating/destroying a bridge. */
+ struct ast_module *mod;
+ /*! Linked list information */
+ AST_RWLIST_ENTRY(ast_bridge_technology) entry;
+};
+
+/*! \brief Register a bridge technology for use
+ *
+ * \param technology The bridge technology to register
+ * \param module The module that is registering the bridge technology
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_technology_register(&simple_bridge_tech);
+ * \endcode
+ *
+ * This registers a bridge technology declared as the structure
+ * simple_bridge_tech with the bridging core and makes it available for
+ * use when creating bridges.
+ */
+int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *mod);
+
+/*! \brief See \ref __ast_bridge_technology_register() */
+#define ast_bridge_technology_register(technology) __ast_bridge_technology_register(technology, ast_module_info->self)
+
+/*! \brief Unregister a bridge technology from use
+ *
+ * \param technology The bridge technology to unregister
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_technology_unregister(&simple_bridge_tech);
+ * \endcode
+ *
+ * This unregisters a bridge technlogy declared as the structure
+ * simple_bridge_tech with the bridging core. It will no longer be
+ * considered when creating a new bridge.
+ */
+int ast_bridge_technology_unregister(struct ast_bridge_technology *technology);
+
+/*! \brief Feed notification that a frame is waiting on a channel into the bridging core
+ *
+ * \param bridge The bridge that the notification should influence
+ * \param bridge_channel Bridge channel the notification was received on (if known)
+ * \param chan Channel the notification was received on (if known)
+ * \param outfd File descriptor that the notification was received on (if known)
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_handle_trip(bridge, NULL, chan, -1);
+ * \endcode
+ *
+ * This tells the bridging core that a frame has been received on
+ * the channel pointed to by chan and that it should be read and handled.
+ *
+ * \note This should only be used by bridging technologies.
+ */
+void ast_bridge_handle_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_channel *chan, int outfd);
+
+/*! \brief Suspend a bridge technology from consideration
+ *
+ * \param technology The bridge technology to suspend
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_technology_suspend(&simple_bridge_tech);
+ * \endcode
+ *
+ * This suspends the bridge technology simple_bridge_tech from being considered
+ * when creating a new bridge. Existing bridges using the bridge technology
+ * are not affected.
+ */
+void ast_bridge_technology_suspend(struct ast_bridge_technology *technology);
+
+/*! \brief Unsuspend a bridge technology
+ *
+ * \param technology The bridge technology to unsuspend
+ *
+ * Example usage:
+ *
+ * \code
+ * ast_bridge_technology_unsuspend(&simple_bridge_tech);
+ * \endcode
+ *
+ * This makes the bridge technology simple_bridge_tech considered when
+ * creating a new bridge again.
+ */
+void ast_bridge_technology_unsuspend(struct ast_bridge_technology *technology);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif /* _ASTERISK_BRIDGING_TECHNOLOGY_H */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 39b302fc8..2d0643a0f 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -496,7 +496,13 @@ struct ast_channel {
unsigned short transfercapability; /*!< ISDN Transfer Capbility - AST_FLAG_DIGITAL is not enough */
- char unused_old_dtmfq[AST_MAX_EXTENSION]; /*!< (deprecated, use readq instead) Any/all queued DTMF characters */
+ union {
+ char unused_old_dtmfq[AST_MAX_EXTENSION]; /*!< (deprecated, use readq instead) Any/all queued DTMF characters */
+ struct {
+ struct ast_bridge *bridge; /*!< Bridge this channel is participating in */
+ };
+ };
+
char context[AST_MAX_CONTEXT]; /*!< Dialplan: Current extension context */
char exten[AST_MAX_EXTENSION]; /*!< Dialplan: Current extension number */
char macrocontext[AST_MAX_CONTEXT]; /*!< Macro: Current non-macro context. See app_macro.c */
diff --git a/main/Makefile b/main/Makefile
index 51ab58f5f..aec3d7ed6 100644
--- a/main/Makefile
+++ b/main/Makefile
@@ -29,7 +29,7 @@ OBJS= tcptls.o io.o sched.o logger.o frame.o loader.o config.o channel.o \
strcompat.o threadstorage.o dial.o event.o adsistub.o audiohook.o \
astobj2.o hashtab.o global_datastores.o version.o \
features.o taskprocessor.o timing.o datastore.o xml.o xmldoc.o \
- strings.o
+ strings.o bridging.o
# we need to link in the objects statically, not as a library, because
# otherwise modules will not have them available if none of the static
diff --git a/main/bridging.c b/main/bridging.c
new file mode 100644
index 000000000..4546c8353
--- /dev/null
+++ b/main/bridging.c
@@ -0,0 +1,1337 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2007 - 2009, Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief Channel Bridging API
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ */
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/logger.h"
+#include "asterisk/channel.h"
+#include "asterisk/options.h"
+#include "asterisk/utils.h"
+#include "asterisk/lock.h"
+#include "asterisk/linkedlists.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_technology.h"
+#include "asterisk/app.h"
+#include "asterisk/file.h"
+#include "asterisk/module.h"
+#include "asterisk/astobj2.h"
+
+static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
+
+/* Initial starting point for the bridge array of channels */
+#define BRIDGE_ARRAY_START 128
+
+/* Grow rate of bridge array of channels */
+#define BRIDGE_ARRAY_GROW 32
+
+/*! Default DTMF keys for built in features */
+static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
+
+/*! Function handlers for the built in features */
+static void *builtin_features_handlers[AST_BRIDGE_BUILTIN_END];
+
+int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *module)
+{
+ struct ast_bridge_technology *current = NULL;
+
+ /* Perform a sanity check to make sure the bridge technology conforms to our needed requirements */
+ if (ast_strlen_zero(technology->name) || !technology->capabilities || !technology->write) {
+ ast_log(LOG_WARNING, "Bridge technology %s failed registration sanity check.\n", technology->name);
+ return -1;
+ }
+
+ AST_RWLIST_WRLOCK(&bridge_technologies);
+
+ /* Look for duplicate bridge technology already using this name, or already registered */
+ AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
+ if ((!strcasecmp(current->name, technology->name)) || (current == technology)) {
+ ast_log(LOG_WARNING, "A bridge technology of %s already claims to exist in our world.\n", technology->name);
+ AST_RWLIST_UNLOCK(&bridge_technologies);
+ return -1;
+ }
+ }
+
+ /* Copy module pointer so reference counting can keep the module from unloading */
+ technology->mod = module;
+
+ /* Insert our new bridge technology into the list and print out a pretty message */
+ AST_RWLIST_INSERT_TAIL(&bridge_technologies, technology, entry);
+
+ AST_RWLIST_UNLOCK(&bridge_technologies);
+
+ if (option_verbose > 1) {
+ ast_verbose(VERBOSE_PREFIX_2 "Registered bridge technology %s\n", technology->name);
+ }
+
+ return 0;
+}
+
+int ast_bridge_technology_unregister(struct ast_bridge_technology *technology)
+{
+ struct ast_bridge_technology *current = NULL;
+
+ AST_RWLIST_WRLOCK(&bridge_technologies);
+
+ /* Ensure the bridge technology is registered before removing it */
+ AST_RWLIST_TRAVERSE_SAFE_BEGIN(&bridge_technologies, current, entry) {
+ if (current == technology) {
+ AST_RWLIST_REMOVE_CURRENT(entry);
+ if (option_verbose > 1) {
+ ast_verbose(VERBOSE_PREFIX_2 "Unregistered bridge technology %s\n", technology->name);
+ }
+ break;
+ }
+ }
+ AST_RWLIST_TRAVERSE_SAFE_END
+
+ AST_RWLIST_UNLOCK(&bridge_technologies);
+
+ return current ? 0 : -1;
+}
+
+void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
+{
+ /* Change the state on the bridge channel */
+ bridge_channel->state = new_state;
+
+ /* Only poke the channel's thread if it is not us */
+ if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
+ pthread_kill(bridge_channel->thread, SIGURG);
+ ast_mutex_lock(&bridge_channel->lock);
+ ast_cond_signal(&bridge_channel->cond);
+ ast_mutex_unlock(&bridge_channel->lock);
+ }
+
+ return;
+}
+
+/*! \brief Helper function to poke the bridge thread */
+static void bridge_poke(struct ast_bridge *bridge)
+{
+ /* Poke the thread just in case */
+ if (bridge->thread != AST_PTHREADT_NULL && bridge->thread != AST_PTHREADT_STOP) {
+ pthread_kill(bridge->thread, SIGURG);
+ }
+
+ return;
+}
+
+/*! \brief Helper function to add a channel to the bridge array
+ *
+ * \note This function assumes the bridge is locked.
+ */
+static void bridge_array_add(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ /* We have to make sure the bridge thread is not using the bridge array before messing with it */
+ while (bridge->waiting) {
+ bridge_poke(bridge);
+ sched_yield();
+ }
+
+ bridge->array[bridge->array_num++] = chan;
+
+ ast_debug(1, "Added channel %s(%p) to bridge array on %p, new count is %d\n", chan->name, chan, bridge, (int)bridge->array_num);
+
+ /* If the next addition of a channel will exceed our array size grow it out */
+ if (bridge->array_num == bridge->array_size) {
+ struct ast_channel **tmp;
+ ast_debug(1, "Growing bridge array on %p from %d to %d\n", bridge, (int)bridge->array_size, (int)bridge->array_size + BRIDGE_ARRAY_GROW);
+ if (!(tmp = ast_realloc(bridge->array, (bridge->array_size + BRIDGE_ARRAY_GROW) * sizeof(struct ast_channel *)))) {
+ ast_log(LOG_ERROR, "Failed to allocate more space for another channel on bridge '%p', this is not going to end well\n", bridge);
+ return;
+ }
+ bridge->array = tmp;
+ bridge->array_size += BRIDGE_ARRAY_GROW;
+ }
+
+ return;
+}
+
+/*! \brief Helper function to remove a channel from the bridge array
+ *
+ * \note This function assumes the bridge is locked.
+ */
+static void bridge_array_remove(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ int i;
+
+ /* We have to make sure the bridge thread is not using the bridge array before messing with it */
+ while (bridge->waiting) {
+ bridge_poke(bridge);
+ sched_yield();
+ }
+
+ for (i = 0; i < bridge->array_num; i++) {
+ if (bridge->array[i] == chan) {
+ bridge->array[i] = (bridge->array[(bridge->array_num - 1)] != chan ? bridge->array[(bridge->array_num - 1)] : NULL);
+ bridge->array[(bridge->array_num - 1)] = NULL;
+ bridge->array_num--;
+ ast_debug(1, "Removed channel %p from bridge array on %p, new count is %d\n", chan, bridge, (int)bridge->array_num);
+ break;
+ }
+ }
+
+ return;
+}
+
+/*! \brief Helper function to find a bridge channel given a channel */
+static struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ struct ast_bridge_channel *bridge_channel = NULL;
+
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ if (bridge_channel->chan == chan) {
+ break;
+ }
+ }
+
+ return bridge_channel;
+}
+
+/*! \brief Internal function to see whether a bridge should dissolve, and if so do it */
+static void bridge_check_dissolve(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct ast_bridge_channel *bridge_channel2 = NULL;
+
+ if (!ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE) && (!bridge_channel->features || !bridge_channel->features->usable || !ast_test_flag(&bridge_channel->features->feature_flags, AST_BRIDGE_FLAG_DISSOLVE))) {
+ return;
+ }
+
+ ast_debug(1, "Dissolving bridge %p\n", bridge);
+
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel2, entry) {
+ if (bridge_channel2->state != AST_BRIDGE_CHANNEL_STATE_END && bridge_channel2->state != AST_BRIDGE_CHANNEL_STATE_DEPART) {
+ ast_bridge_change_state(bridge_channel2, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ }
+ }
+
+ /* Since all the channels are going away let's go ahead and stop our on thread */
+ bridge->stop = 1;
+
+ return;
+}
+
+/*! \brief Internal function to handle DTMF from a channel */
+static struct ast_frame *bridge_handle_dtmf(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+ struct ast_bridge_features *features = (bridge_channel->features ? bridge_channel->features : &bridge->features);
+ struct ast_bridge_features_hook *hook = NULL;
+
+ /* If the features structure we grabbed is not usable immediately return the frame */
+ if (!features->usable) {
+ return frame;
+ }
+
+ /* See if this DTMF matches the beginnings of any feature hooks, if so we switch to the feature state to either execute the feature or collect more DTMF */
+ AST_LIST_TRAVERSE(&features->hooks, hook, entry) {
+ if (hook->dtmf[0] == frame->subclass) {
+ ast_frfree(frame);
+ frame = NULL;
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_FEATURE);
+ break;
+ }
+ }
+
+ return frame;
+}
+
+void ast_bridge_handle_trip(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_channel *chan, int outfd)
+{
+ /* If no bridge channel has been provided and the actual channel has been provided find it */
+ if (chan && !bridge_channel) {
+ bridge_channel = find_bridge_channel(bridge, chan);
+ }
+
+ /* If a bridge channel with actual channel is present read a frame and handle it */
+ if (chan && bridge_channel) {
+ struct ast_frame *frame = (((bridge->features.mute) || (bridge_channel->features && bridge_channel->features->mute)) ? ast_read_noaudio(chan) : ast_read(chan));
+
+ /* This is pretty simple... see if they hung up */
+ if (!frame || (frame->frametype == AST_FRAME_CONTROL && frame->subclass == AST_CONTROL_HANGUP)) {
+ /* Signal the thread that is handling the bridged channel that it should be ended */
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ } else if (frame->frametype == AST_FRAME_CONTROL && frame->subclass == AST_CONTROL_ANSWER) {
+ ast_debug(1, "Dropping answer frame from bridge channel %p\n", bridge_channel);
+ } else {
+ if (frame->frametype == AST_FRAME_DTMF_BEGIN) {
+ frame = bridge_handle_dtmf(bridge, bridge_channel, frame);
+ }
+ /* Simply write the frame out to the bridge technology if it still exists */
+ if (frame) {
+ bridge->technology->write(bridge, bridge_channel, frame);
+ }
+ }
+
+ if (frame) {
+ ast_frfree(frame);
+ }
+ return;
+ }
+
+ /* If a file descriptor actually tripped pass it off to the bridge technology */
+ if (outfd > -1 && bridge->technology->fd) {
+ bridge->technology->fd(bridge, bridge_channel, outfd);
+ return;
+ }
+
+ /* If all else fails just poke the bridge */
+ if (bridge->technology->poke && bridge_channel) {
+ bridge->technology->poke(bridge, bridge_channel);
+ return;
+ }
+
+ return;
+}
+
+/*! \brief Generic thread loop, TODO: Rethink this/improve it */
+static int generic_thread_loop(struct ast_bridge *bridge)
+{
+ while (!bridge->stop && !bridge->refresh && bridge->array_num) {
+ struct ast_channel *winner = NULL;
+ int to = -1;
+
+ /* Move channels around for priority reasons if we have more than one channel in our array */
+ if (bridge->array_num > 1) {
+ struct ast_channel *first = bridge->array[0];
+ memmove(bridge->array, bridge->array + 1, sizeof(bridge->array) - 1);
+ bridge->array[(bridge->array_num - 1)] = first;
+ }
+
+ /* Wait on the channels */
+ bridge->waiting = 1;
+ ao2_unlock(bridge);
+ winner = ast_waitfor_n(bridge->array, (int)bridge->array_num, &to);
+ bridge->waiting = 0;
+ ao2_lock(bridge);
+
+ /* Process whatever they did */
+ ast_bridge_handle_trip(bridge, NULL, winner, -1);
+ }
+
+ return 0;
+}
+
+/*! \brief Bridge thread function */
+static void *bridge_thread(void *data)
+{
+ struct ast_bridge *bridge = data;
+ int res = 0;
+
+ ao2_lock(bridge);
+
+ ast_debug(1, "Started bridge thread for %p\n", bridge);
+
+ /* Loop around until we are told to stop */
+ while (!bridge->stop && bridge->array_num && !res) {
+ /* In case the refresh bit was set simply set it back to off */
+ bridge->refresh = 0;
+
+ ast_debug(1, "Launching bridge thread function %p for bridge %p\n", (bridge->technology->thread ? bridge->technology->thread : &generic_thread_loop), bridge);
+
+ /* Execute the appropriate thread function. If the technology does not provide one we use the generic one */
+ res = (bridge->technology->thread ? bridge->technology->thread(bridge) : generic_thread_loop(bridge));
+ }
+
+ ast_debug(1, "Ending bridge thread for %p\n", bridge);
+
+ /* Indicate the bridge thread is no longer active */
+ bridge->thread = AST_PTHREADT_NULL;
+ ao2_unlock(bridge);
+
+ ao2_ref(bridge, -1);
+
+ return NULL;
+}
+
+/*! \brief Helper function used to find the "best" bridge technology given a specified capabilities */
+static struct ast_bridge_technology *find_best_technology(int capabilities)
+{
+ struct ast_bridge_technology *current = NULL, *best = NULL;
+
+ AST_RWLIST_RDLOCK(&bridge_technologies);
+ AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
+ ast_debug(1, "Bridge technology %s has capabilities %d and we want %d\n", current->name, current->capabilities, capabilities);
+ if (current->suspended) {
+ ast_debug(1, "Bridge technology %s is suspended. Skipping.\n", current->name);
+ continue;
+ }
+ if (!(current->capabilities & capabilities)) {
+ ast_debug(1, "Bridge technology %s does not have the capabilities we need.\n", current->name);
+ continue;
+ }
+ if (best && best->preference < current->preference) {
+ ast_debug(1, "Bridge technology %s has preference %d while %s has preference %d. Skipping.\n", current->name, current->preference, best->name, best->preference);
+ continue;
+ }
+ best = current;
+ }
+
+ if (best) {
+ /* Increment it's module reference count if present so it does not get unloaded while in use */
+ if (best->mod) {
+ ast_module_ref(best->mod);
+ }
+ ast_debug(1, "Chose bridge technology %s\n", best->name);
+ }
+
+ AST_RWLIST_UNLOCK(&bridge_technologies);
+
+ return best;
+}
+
+static void destroy_bridge(void *obj)
+{
+ struct ast_bridge *bridge = obj;
+
+ ast_debug(1, "Actually destroying bridge %p, nobody wants it anymore\n", bridge);
+
+ /* Pass off the bridge to the technology to destroy if needed */
+ if (bridge->technology->destroy) {
+ ast_debug(1, "Giving bridge technology %s the bridge structure %p to destroy\n", bridge->technology->name, bridge);
+ if (bridge->technology->destroy(bridge)) {
+ ast_debug(1, "Bridge technology %s failed to destroy bridge structure %p... trying our best\n", bridge->technology->name, bridge);
+ }
+ }
+
+ /* We are no longer using the bridge technology so decrement the module reference count on it */
+ if (bridge->technology->mod) {
+ ast_module_unref(bridge->technology->mod);
+ }
+
+ /* Last but not least clean up the features configuration */
+ ast_bridge_features_cleanup(&bridge->features);
+
+ /* Drop the array of channels */
+ ast_free(bridge->array);
+
+ return;
+}
+
+struct ast_bridge *ast_bridge_new(int capabilities, int flags)
+{
+ struct ast_bridge *bridge = NULL;
+ struct ast_bridge_technology *bridge_technology = NULL;
+
+ /* If we need to be a smart bridge see if we can move between 1to1 and multimix bridges */
+ if (flags & AST_BRIDGE_FLAG_SMART) {
+ struct ast_bridge *other_bridge;
+
+ if (!(other_bridge = ast_bridge_new((capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) ? AST_BRIDGE_CAPABILITY_MULTIMIX : AST_BRIDGE_CAPABILITY_1TO1MIX, 0))) {
+ return NULL;
+ }
+
+ ast_bridge_destroy(other_bridge);
+ }
+
+ /* If capabilities were provided use our helper function to find the "best" bridge technology, otherwise we can
+ * just look for the most basic capability needed, single 1to1 mixing. */
+ bridge_technology = (capabilities ? find_best_technology(capabilities) : find_best_technology(AST_BRIDGE_CAPABILITY_1TO1MIX));
+
+ /* If no bridge technology was found we can't possibly do bridging so fail creation of the bridge */
+ if (!bridge_technology) {
+ ast_debug(1, "Failed to find a bridge technology to satisfy capabilities %d\n", capabilities);
+ return NULL;
+ }
+
+ /* We have everything we need to create this bridge... so allocate the memory, link things together, and fire her up! */
+ if (!(bridge = ao2_alloc(sizeof(*bridge), destroy_bridge))) {
+ return NULL;
+ }
+
+ bridge->technology = bridge_technology;
+ bridge->thread = AST_PTHREADT_NULL;
+
+ /* Create an array of pointers for the channels that will be joining us */
+ bridge->array = ast_calloc(BRIDGE_ARRAY_START, sizeof(struct ast_channel*));
+ bridge->array_size = BRIDGE_ARRAY_START;
+
+ ast_set_flag(&bridge->feature_flags, flags);
+
+ /* Pass off the bridge to the technology to manipulate if needed */
+ if (bridge->technology->create) {
+ ast_debug(1, "Giving bridge technology %s the bridge structure %p to setup\n", bridge->technology->name, bridge);
+ if (bridge->technology->create(bridge)) {
+ ast_debug(1, "Bridge technology %s failed to setup bridge structure %p\n", bridge->technology->name, bridge);
+ ao2_ref(bridge, -1);
+ bridge = NULL;
+ }
+ }
+
+ return bridge;
+}
+
+int ast_bridge_check(int capabilities)
+{
+ struct ast_bridge_technology *bridge_technology = NULL;
+
+ if (!(bridge_technology = find_best_technology(capabilities))) {
+ return 0;
+ }
+
+ ast_module_unref(bridge_technology->mod);
+
+ return 1;
+}
+
+int ast_bridge_destroy(struct ast_bridge *bridge)
+{
+ struct ast_bridge_channel *bridge_channel = NULL;
+
+ ao2_lock(bridge);
+
+ bridge->stop = 1;
+
+ bridge_poke(bridge);
+
+ ast_debug(1, "Telling all channels in bridge %p to end and leave the party\n", bridge);
+
+ /* Drop every bridged channel, the last one will cause the bridge thread (if it exists) to exit */
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+ }
+
+ ao2_unlock(bridge);
+
+ ao2_ref(bridge, -1);
+
+ return 0;
+}
+
+static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ int formats[2] = {bridge_channel->chan->readformat, bridge_channel->chan->writeformat};
+
+ /* Are the formats currently in use something ths bridge can handle? */
+ if (!(bridge->technology->formats & bridge_channel->chan->readformat)) {
+ int best_format = ast_best_codec(bridge->technology->formats);
+
+ /* Read format is a no go... */
+ if (option_debug) {
+ char codec_buf[512];
+ ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats);
+ ast_debug(1, "Bridge technology %s wants to read any of formats %s(%d) but channel has %s(%d)\n", bridge->technology->name, codec_buf, bridge->technology->formats, ast_getformatname(formats[0]), formats[0]);
+ }
+ /* Switch read format to the best one chosen */
+ if (ast_set_read_format(bridge_channel->chan, best_format)) {
+ ast_log(LOG_WARNING, "Failed to set channel %s to read format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+ return -1;
+ }
+ ast_debug(1, "Bridge %p put channel %s into read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+ } else {
+ ast_debug(1, "Bridge %p is happy that channel %s already has read format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[0]), formats[0]);
+ }
+
+ if (!(bridge->technology->formats & formats[1])) {
+ int best_format = ast_best_codec(bridge->technology->formats);
+
+ /* Write format is a no go... */
+ if (option_debug) {
+ char codec_buf[512];
+ ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->formats);
+ ast_debug(1, "Bridge technology %s wants to write any of formats %s(%d) but channel has %s(%d)\n", bridge->technology->name, codec_buf, bridge->technology->formats, ast_getformatname(formats[1]), formats[1]);
+ }
+ /* Switch write format to the best one chosen */
+ if (ast_set_write_format(bridge_channel->chan, best_format)) {
+ ast_log(LOG_WARNING, "Failed to set channel %s to write format %s(%d)\n", bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+ return -1;
+ }
+ ast_debug(1, "Bridge %p put channel %s into write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(best_format), best_format);
+ } else {
+ ast_debug(1, "Bridge %p is happy that channel %s already has write format %s(%d)\n", bridge, bridge_channel->chan->name, ast_getformatname(formats[1]), formats[1]);
+ }
+
+ return 0;
+}
+
+/*! \brief Perform the smart bridge operation. Basically sees if a new bridge technology should be used instead of the current one. */
+static int smart_bridge_operation(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, int count)
+{
+ int new_capabilities = 0;
+ struct ast_bridge_technology *new_technology = NULL, *old_technology = bridge->technology;
+ struct ast_bridge temp_bridge = {
+ .technology = bridge->technology,
+ .bridge_pvt = bridge->bridge_pvt,
+ };
+ struct ast_bridge_channel *bridge_channel2 = NULL;
+
+ /* Based on current feature determine whether we want to change bridge technologies or not */
+ if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX) {
+ if (count <= 2) {
+ ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, count, bridge->technology->name);
+ return 0;
+ }
+ new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
+ } else if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
+ if (count > 2) {
+ ast_debug(1, "Bridge %p channel count (%d) is within limits for bridge technology %s, not performing smart bridge operation.\n", bridge, count, bridge->technology->name);
+ return 0;
+ }
+ new_capabilities = AST_BRIDGE_CAPABILITY_1TO1MIX;
+ }
+
+ if (!new_capabilities) {
+ ast_debug(1, "Bridge '%p' has no new capabilities, not performing smart bridge operation.\n", bridge);
+ return 0;
+ }
+
+ /* Attempt to find a new bridge technology to satisfy the capabilities */
+ if (!(new_technology = find_best_technology(new_capabilities))) {
+ ast_debug(1, "Smart bridge operation was unable to find new bridge technology with capabilities %d to satisfy bridge %p\n", new_capabilities, bridge);
+ return -1;
+ }
+
+ ast_debug(1, "Performing smart bridge operation on bridge %p, moving from bridge technology %s to %s\n", bridge, old_technology->name, new_technology->name);
+
+ /* If a thread is currently executing for the current technology tell it to stop */
+ if (bridge->thread != AST_PTHREADT_NULL) {
+ /* If the new bridge technology also needs a thread simply tell the bridge thread to refresh itself. This has the benefit of not incurring the cost/time of tearing down and bringing up a new thread. */
+ if (new_technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD) {
+ ast_debug(1, "Telling current bridge thread for bridge %p to refresh\n", bridge);
+ bridge->refresh = 1;
+ } else {
+ ast_debug(1, "Telling current bridge thread for bridge %p to stop\n", bridge);
+ bridge->stop = 1;
+ }
+ bridge_poke(bridge);
+ }
+
+ /* Since we are soon going to pass this bridge to a new technology we need to NULL out the bridge_pvt pointer but don't worry as it still exists in temp_bridge, ditto for the old technology */
+ bridge->bridge_pvt = NULL;
+ bridge->technology = new_technology;
+
+ /* Pass the bridge to the new bridge technology so it can set it up */
+ if (new_technology->create) {
+ ast_debug(1, "Giving bridge technology %s the bridge structure %p to setup\n", new_technology->name, bridge);
+ if (new_technology->create(bridge)) {
+ ast_debug(1, "Bridge technology %s failed to setup bridge structure %p\n", new_technology->name, bridge);
+ }
+ }
+
+ /* Move existing channels over to the new technology, while taking them away from the old one */
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel2, entry) {
+ /* Skip over channel that initiated the smart bridge operation */
+ if (bridge_channel == bridge_channel2) {
+ continue;
+ }
+
+ /* First we part them from the old technology */
+ if (old_technology->leave) {
+ ast_debug(1, "Giving bridge technology %s notification that %p is leaving bridge %p (really %p)\n", old_technology->name, bridge_channel2, &temp_bridge, bridge);
+ if (old_technology->leave(&temp_bridge, bridge_channel2)) {
+ ast_debug(1, "Bridge technology %s failed to allow %p (really %p) to leave bridge %p\n", old_technology->name, bridge_channel2, &temp_bridge, bridge);
+ }
+ }
+
+ /* Second we make them compatible again with the bridge */
+ bridge_make_compatible(bridge, bridge_channel2);
+
+ /* Third we join them to the new technology */
+ if (new_technology->join) {
+ ast_debug(1, "Giving bridge technology %s notification that %p is joining bridge %p\n", new_technology->name, bridge_channel2, bridge);
+ if (new_technology->join(bridge, bridge_channel2)) {
+ ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n", new_technology->name, bridge_channel2, bridge);
+ }
+ }
+
+ /* Fourth we tell them to wake up so they become aware that they above has happened */
+ pthread_kill(bridge_channel2->thread, SIGURG);
+ ast_mutex_lock(&bridge_channel2->lock);
+ ast_cond_signal(&bridge_channel2->cond);
+ ast_mutex_unlock(&bridge_channel2->lock);
+ }
+
+ /* Now that all the channels have been moved over we need to get rid of all the information the old technology may have left around */
+ if (old_technology->destroy) {
+ ast_debug(1, "Giving bridge technology %s the bridge structure %p (really %p) to destroy\n", old_technology->name, &temp_bridge, bridge);
+ if (old_technology->destroy(&temp_bridge)) {
+ ast_debug(1, "Bridge technology %s failed to destroy bridge structure %p (really %p)... some memory may have leaked\n", old_technology->name, &temp_bridge, bridge);
+ }
+ }
+
+ /* Finally if the old technology has module referencing remove our reference, we are no longer going to use it */
+ if (old_technology->mod) {
+ ast_module_unref(old_technology->mod);
+ }
+
+ return 0;
+}
+
+/*! \brief Run in a multithreaded model. Each joined channel does writing/reading in their own thread. TODO: Improve */
+static enum ast_bridge_channel_state bridge_channel_join_multithreaded(struct ast_bridge_channel *bridge_channel)
+{
+ int fds[4] = { -1, }, nfds = 0, i = 0, outfd = -1, ms = -1;
+ struct ast_channel *chan = NULL;
+
+ /* Add any file descriptors we may want to monitor */
+ if (bridge_channel->bridge->technology->fd) {
+ for (i = 0; i < 4; i ++) {
+ if (bridge_channel->fds[i] >= 0) {
+ fds[nfds++] = bridge_channel->fds[i];
+ }
+ }
+ }
+
+ ao2_unlock(bridge_channel->bridge);
+
+ /* Wait for data to either come from the channel or us to be signalled */
+ if (!bridge_channel->suspended) {
+ ast_debug(1, "Going into a multithreaded waitfor for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge);
+ chan = ast_waitfor_nandfds(&bridge_channel->chan, 1, fds, nfds, NULL, &outfd, &ms);
+ } else {
+ ast_mutex_lock(&bridge_channel->lock);
+ ast_debug(1, "Going into a multithreaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge);
+ ast_cond_wait(&bridge_channel->cond, &bridge_channel->lock);
+ ast_mutex_unlock(&bridge_channel->lock);
+ }
+
+ ao2_lock(bridge_channel->bridge);
+
+ if (!bridge_channel->suspended) {
+ ast_bridge_handle_trip(bridge_channel->bridge, bridge_channel, chan, outfd);
+ }
+
+ return bridge_channel->state;
+}
+
+/*! \brief Run in a singlethreaded model. Each joined channel yields itself to the main bridge thread. TODO: Improve */
+static enum ast_bridge_channel_state bridge_channel_join_singlethreaded(struct ast_bridge_channel *bridge_channel)
+{
+ ao2_unlock(bridge_channel->bridge);
+ ast_mutex_lock(&bridge_channel->lock);
+ if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+ ast_debug(1, "Going into a single threaded signal wait for bridge channel %p of bridge %p\n", bridge_channel, bridge_channel->bridge);
+ ast_cond_wait(&bridge_channel->cond, &bridge_channel->lock);
+ }
+ ast_mutex_unlock(&bridge_channel->lock);
+ ao2_lock(bridge_channel->bridge);
+
+ return bridge_channel->state;
+}
+
+/*! \brief Internal function that suspends a channel from a bridge */
+static void bridge_channel_suspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ bridge_channel->suspended = 1;
+
+ bridge_array_remove(bridge, bridge_channel->chan);
+
+ if (bridge->technology->suspend) {
+ bridge->technology->suspend(bridge, bridge_channel);
+ }
+
+ return;
+}
+
+/*! \brief Internal function that unsuspends a channel from a bridge */
+static void bridge_channel_unsuspend(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ bridge_channel->suspended =0;
+
+ bridge_array_add(bridge, bridge_channel->chan);
+
+ if (bridge->technology->unsuspend) {
+ bridge->technology->unsuspend(bridge, bridge_channel);
+ }
+
+ return;
+}
+
+/*! \brief Internal function that executes a feature on a bridge channel */
+static void bridge_channel_feature(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ struct ast_bridge_features *features = (bridge_channel->features ? bridge_channel->features : &bridge->features);
+ struct ast_bridge_features_hook *hook = NULL;
+ char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
+ int look_for_dtmf = 1, dtmf_len = 0;
+
+ /* The channel is now under our control and we don't really want any begin frames to do our DTMF matching so disable 'em at the core level */
+ ast_set_flag(bridge_channel->chan, AST_FLAG_END_DTMF_ONLY);
+
+ /* Wait for DTMF on the channel and put it into a buffer. If the buffer matches any feature hook execute the hook. */
+ while (look_for_dtmf) {
+ int res = ast_waitfordigit(bridge_channel->chan, 3000);
+
+ /* If the above timed out simply exit */
+ if (!res) {
+ ast_debug(1, "DTMF feature string collection on bridge channel %p timed out\n", bridge_channel);
+ break;
+ } else if (res < 0) {
+ ast_debug(1, "DTMF feature string collection failed on bridge channel %p for some reason\n", bridge_channel);
+ break;
+ }
+
+ /* Add the above DTMF into the DTMF string so we can do our matching */
+ dtmf[dtmf_len++] = res;
+
+ ast_debug(1, "DTMF feature string on bridge channel %p is now '%s'\n", bridge_channel, dtmf);
+
+ /* Assume that we do not want to look for DTMF any longer */
+ look_for_dtmf = 0;
+
+ /* See if a DTMF feature hook matches or can match */
+ AST_LIST_TRAVERSE(&features->hooks, hook, entry) {
+ /* If this hook matches just break out now */
+ if (!strcmp(hook->dtmf, dtmf)) {
+ ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on bridge channel %p\n", hook, dtmf, bridge_channel);
+ break;
+ } else if (!strncmp(hook->dtmf, dtmf, dtmf_len)) {
+ ast_debug(1, "DTMF feature hook %p can match DTMF string '%s', it wants '%s', on bridge channel %p\n", hook, dtmf, hook->dtmf, bridge_channel);
+ look_for_dtmf = 1;
+ } else {
+ ast_debug(1, "DTMF feature hook %p does not match DTMF string '%s', it wants '%s', on bridge channel %p\n", hook, dtmf, hook->dtmf, bridge_channel);
+ }
+ }
+
+ /* If we have reached the maximum length of a DTMF feature string bail out */
+ if (dtmf_len == MAXIMUM_DTMF_FEATURE_STRING) {
+ break;
+ }
+ }
+
+ /* Since we are done bringing DTMF in return to using both begin and end frames */
+ ast_clear_flag(bridge_channel->chan, AST_FLAG_END_DTMF_ONLY);
+
+ /* If a hook was actually matched execute it on this channel, otherwise stream up the DTMF to the other channels */
+ if (hook) {
+ hook->callback(bridge, bridge_channel, hook->hook_pvt);
+ } else {
+ ast_bridge_dtmf_stream(bridge, dtmf, bridge_channel->chan);
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+ }
+
+ return;
+}
+
+/*! \brief Internal function that plays back DTMF on a bridge channel */
+static void bridge_channel_dtmf_stream(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
+{
+ char dtmf_q[8] = "";
+
+ ast_copy_string(dtmf_q, bridge_channel->dtmf_stream_q, sizeof(dtmf_q));
+ bridge_channel->dtmf_stream_q[0] = '\0';
+
+ ast_debug(1, "Playing DTMF stream '%s' out to bridge channel %p\n", dtmf_q, bridge_channel);
+ ast_dtmf_stream(bridge_channel->chan, NULL, dtmf_q, 250, 0);
+
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_WAIT);
+
+ return;
+}
+
+/*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */
+static enum ast_bridge_channel_state bridge_channel_join(struct ast_bridge_channel *bridge_channel)
+{
+ int formats[2] = { bridge_channel->chan->readformat, bridge_channel->chan->writeformat };
+ enum ast_bridge_channel_state state;
+
+ /* Record the thread that will be the owner of us */
+ bridge_channel->thread = pthread_self();
+
+ ast_debug(1, "Joining bridge channel %p to bridge %p\n", bridge_channel, bridge_channel->bridge);
+
+ ao2_lock(bridge_channel->bridge);
+
+ state = bridge_channel->state;
+
+ /* Add channel into the bridge */
+ AST_LIST_INSERT_TAIL(&bridge_channel->bridge->channels, bridge_channel, entry);
+ bridge_channel->bridge->num++;
+
+ bridge_array_add(bridge_channel->bridge, bridge_channel->chan);
+
+ if (bridge_channel->swap) {
+ struct ast_bridge_channel *bridge_channel2 = NULL;
+
+ /* If we are performing a swap operation we do not need to execute the smart bridge operation as the actual number of channels involved will not have changed, we just need to tell the other channel to leave */
+ if ((bridge_channel2 = find_bridge_channel(bridge_channel->bridge, bridge_channel->swap))) {
+ ast_debug(1, "Swapping bridge channel %p out from bridge %p so bridge channel %p can slip in\n", bridge_channel2, bridge_channel->bridge, bridge_channel);
+ ast_bridge_change_state(bridge_channel2, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+ }
+
+ bridge_channel->swap = NULL;
+ } else if (ast_test_flag(&bridge_channel->bridge->feature_flags, AST_BRIDGE_FLAG_SMART)) {
+ /* Perform the smart bridge operation, basically see if we need to move around between technologies */
+ smart_bridge_operation(bridge_channel->bridge, bridge_channel, bridge_channel->bridge->num);
+ }
+
+ /* Make the channel compatible with the bridge */
+ bridge_make_compatible(bridge_channel->bridge, bridge_channel);
+
+ /* Tell the bridge technology we are joining so they set us up */
+ if (bridge_channel->bridge->technology->join) {
+ ast_debug(1, "Giving bridge technology %s notification that %p is joining bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+ if (bridge_channel->bridge->technology->join(bridge_channel->bridge, bridge_channel)) {
+ ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+ }
+ }
+
+ /* Actually execute the respective threading model, and keep our bridge thread alive */
+ while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+ /* Update bridge pointer on channel */
+ bridge_channel->chan->bridge = bridge_channel->bridge;
+ /* If the technology requires a thread and one is not running, start it up */
+ if (bridge_channel->bridge->thread == AST_PTHREADT_NULL && (bridge_channel->bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_THREAD)) {
+ bridge_channel->bridge->stop = 0;
+ ast_debug(1, "Starting a bridge thread for bridge %p\n", bridge_channel->bridge);
+ ao2_ref(bridge_channel->bridge, +1);
+ if (ast_pthread_create(&bridge_channel->bridge->thread, NULL, bridge_thread, bridge_channel->bridge)) {
+ ast_debug(1, "Failed to create a bridge thread for bridge %p, giving it another go.\n", bridge_channel->bridge);
+ ao2_ref(bridge_channel->bridge, -1);
+ continue;
+ }
+ }
+ /* Execute the threading model */
+ state = (bridge_channel->bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTITHREADED ? bridge_channel_join_multithreaded(bridge_channel) : bridge_channel_join_singlethreaded(bridge_channel));
+ /* Depending on the above state see what we need to do */
+ if (state == AST_BRIDGE_CHANNEL_STATE_FEATURE) {
+ bridge_channel_suspend(bridge_channel->bridge, bridge_channel);
+ bridge_channel_feature(bridge_channel->bridge, bridge_channel);
+ bridge_channel_unsuspend(bridge_channel->bridge, bridge_channel);
+ } else if (state == AST_BRIDGE_CHANNEL_STATE_DTMF) {
+ bridge_channel_suspend(bridge_channel->bridge, bridge_channel);
+ bridge_channel_dtmf_stream(bridge_channel->bridge, bridge_channel);
+ bridge_channel_unsuspend(bridge_channel->bridge, bridge_channel);
+ }
+ }
+
+ bridge_channel->chan->bridge = NULL;
+
+ /* See if we need to dissolve the bridge itself if they hung up */
+ if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_END) {
+ bridge_check_dissolve(bridge_channel->bridge, bridge_channel);
+ }
+
+ /* Tell the bridge technology we are leaving so they tear us down */
+ if (bridge_channel->bridge->technology->leave) {
+ ast_debug(1, "Giving bridge technology %s notification that %p is leaving bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+ if (bridge_channel->bridge->technology->leave(bridge_channel->bridge, bridge_channel)) {
+ ast_debug(1, "Bridge technology %s failed to leave %p from bridge %p\n", bridge_channel->bridge->technology->name, bridge_channel, bridge_channel->bridge);
+ }
+ }
+
+ /* Remove channel from the bridge */
+ bridge_channel->bridge->num--;
+ AST_LIST_REMOVE(&bridge_channel->bridge->channels, bridge_channel, entry);
+
+ bridge_array_remove(bridge_channel->bridge, bridge_channel->chan);
+
+ /* Perform the smart bridge operation if needed since a channel has left */
+ if (ast_test_flag(&bridge_channel->bridge->feature_flags, AST_BRIDGE_FLAG_SMART)) {
+ smart_bridge_operation(bridge_channel->bridge, NULL, bridge_channel->bridge->num);
+ }
+
+ ao2_unlock(bridge_channel->bridge);
+
+ /* Restore original formats of the channel as they came in */
+ if (bridge_channel->chan->readformat != formats[0]) {
+ ast_debug(1, "Bridge is returning %p to read format %s(%d)\n", bridge_channel, ast_getformatname(formats[0]), formats[0]);
+ if (ast_set_read_format(bridge_channel->chan, formats[0])) {
+ ast_debug(1, "Bridge failed to return channel %p to read format %s(%d)\n", bridge_channel, ast_getformatname(formats[0]), formats[0]);
+ }
+ }
+ if (bridge_channel->chan->writeformat != formats[1]) {
+ ast_debug(1, "Bridge is returning %p to write format %s(%d)\n", bridge_channel, ast_getformatname(formats[1]), formats[1]);
+ if (ast_set_write_format(bridge_channel->chan, formats[1])) {
+ ast_debug(1, "Bridge failed to return channel %p to write format %s(%d)\n", bridge_channel, ast_getformatname(formats[1]), formats[1]);
+ }
+ }
+
+ return bridge_channel->state;
+}
+
+enum ast_bridge_channel_state ast_bridge_join(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features)
+{
+ struct ast_bridge_channel bridge_channel = {
+ .chan = chan,
+ .swap = swap,
+ .bridge = bridge,
+ .features = features,
+ };
+ enum ast_bridge_channel_state state;
+
+ /* Initialize various other elements of the bridge channel structure that we can't do above */
+ ast_mutex_init(&bridge_channel.lock);
+ ast_cond_init(&bridge_channel.cond, NULL);
+
+ ao2_ref(bridge_channel.bridge, +1);
+
+ state = bridge_channel_join(&bridge_channel);
+
+ ao2_ref(bridge_channel.bridge, -1);
+
+ /* Destroy some elements of the bridge channel structure above */
+ ast_mutex_destroy(&bridge_channel.lock);
+ ast_cond_destroy(&bridge_channel.cond);
+
+ return state;
+}
+
+/*! \brief Thread responsible for imparted bridged channels */
+static void *bridge_channel_thread(void *data)
+{
+ struct ast_bridge_channel *bridge_channel = data;
+ enum ast_bridge_channel_state state;
+
+ state = bridge_channel_join(bridge_channel);
+
+ ao2_ref(bridge_channel->bridge, -1);
+
+ /* If no other thread is going to take the channel then hang it up, or else we would have to service it until something else came along */
+ if (state == AST_BRIDGE_CHANNEL_STATE_END || state == AST_BRIDGE_CHANNEL_STATE_HANGUP) {
+ ast_hangup(bridge_channel->chan);
+ }
+
+ /* Destroy elements of the bridge channel structure and the bridge channel structure itself */
+ ast_mutex_destroy(&bridge_channel->lock);
+ ast_cond_destroy(&bridge_channel->cond);
+ ast_free(bridge_channel);
+
+ return NULL;
+}
+
+int ast_bridge_impart(struct ast_bridge *bridge, struct ast_channel *chan, struct ast_channel *swap, struct ast_bridge_features *features)
+{
+ struct ast_bridge_channel *bridge_channel = NULL;
+
+ /* Try to allocate a structure for the bridge channel */
+ if (!(bridge_channel = ast_calloc(1, sizeof(*bridge_channel)))) {
+ return -1;
+ }
+
+ /* Setup various parameters */
+ bridge_channel->chan = chan;
+ bridge_channel->swap = swap;
+ bridge_channel->bridge = bridge;
+ bridge_channel->features = features;
+
+ /* Initialize our mutex lock and condition */
+ ast_mutex_init(&bridge_channel->lock);
+ ast_cond_init(&bridge_channel->cond, NULL);
+
+ /* Bump up the reference count on the bridge, it'll get decremented later */
+ ao2_ref(bridge, +1);
+
+ /* Actually create the thread that will handle the channel */
+ if (ast_pthread_create(&bridge_channel->thread, NULL, bridge_channel_thread, bridge_channel)) {
+ ao2_ref(bridge, -1);
+ ast_cond_destroy(&bridge_channel->cond);
+ ast_mutex_destroy(&bridge_channel->lock);
+ ast_free(bridge_channel);
+ return -1;
+ }
+
+ return 0;
+}
+
+int ast_bridge_depart(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ struct ast_bridge_channel *bridge_channel = NULL;
+ pthread_t thread;
+
+ ao2_lock(bridge);
+
+ /* Try to find the channel that we want to depart */
+ if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
+ ao2_unlock(bridge);
+ return -1;
+ }
+
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DEPART);
+ thread = bridge_channel->thread;
+
+ ao2_unlock(bridge);
+
+ pthread_join(thread, NULL);
+
+ return 0;
+}
+
+int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ struct ast_bridge_channel *bridge_channel = NULL;
+
+ ao2_lock(bridge);
+
+ /* Try to find the channel that we want to remove */
+ if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
+ ao2_unlock(bridge);
+ return -1;
+ }
+
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+
+ ao2_unlock(bridge);
+
+ return 0;
+}
+
+int ast_bridge_merge(struct ast_bridge *bridge0, struct ast_bridge *bridge1)
+{
+ struct ast_bridge_channel *bridge_channel = NULL;
+
+ ao2_lock(bridge0);
+ ao2_lock(bridge1);
+
+ /* If the first bridge currently has 2 channels and is not capable of becoming a multimixing bridge we can not merge */
+ if ((bridge0->num + bridge1->num) > 2 && (!(bridge0->technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) && !ast_test_flag(&bridge0->feature_flags, AST_BRIDGE_FLAG_SMART))) {
+ ao2_unlock(bridge1);
+ ao2_unlock(bridge0);
+ ast_debug(1, "Can't merge bridge %p into bridge %p, multimix is needed and it could not be acquired.\n", bridge1, bridge0);
+ return -1;
+ }
+
+ ast_debug(1, "Merging channels from bridge %p into bridge %p\n", bridge1, bridge0);
+
+ /* Perform smart bridge operation on bridge we are merging into so it can change bridge technology if needed */
+ if (smart_bridge_operation(bridge0, NULL, bridge0->num + bridge1->num)) {
+ ao2_unlock(bridge1);
+ ao2_unlock(bridge0);
+ ast_debug(1, "Can't merge bridge %p into bridge %p, tried to perform smart bridge operation and failed.\n", bridge1, bridge0);
+ return -1;
+ }
+
+ /* If a thread is currently executing on bridge1 tell it to stop */
+ if (bridge1->thread) {
+ ast_debug(1, "Telling bridge thread on bridge %p to stop as it is being merged into %p\n", bridge1, bridge0);
+ bridge1->thread = AST_PTHREADT_STOP;
+ }
+
+ /* Move channels from bridge1 over to bridge0 */
+ while ((bridge_channel = AST_LIST_REMOVE_HEAD(&bridge1->channels, entry))) {
+ /* Tell the technology handling bridge1 that the bridge channel is leaving */
+ if (bridge1->technology->leave) {
+ ast_debug(1, "Giving bridge technology %s notification that %p is leaving bridge %p\n", bridge1->technology->name, bridge_channel, bridge1);
+ if (bridge1->technology->leave(bridge1, bridge_channel)) {
+ ast_debug(1, "Bridge technology %s failed to allow %p to leave bridge %p\n", bridge1->technology->name, bridge_channel, bridge1);
+ }
+ }
+
+ /* Drop channel count and reference count on the bridge they are leaving */
+ bridge1->num--;
+ ao2_ref(bridge1, -1);
+
+ bridge_array_remove(bridge1, bridge_channel->chan);
+
+ /* Now add them into the bridge they are joining, increase channel count, and bump up reference count */
+ bridge_channel->bridge = bridge0;
+ AST_LIST_INSERT_TAIL(&bridge0->channels, bridge_channel, entry);
+ bridge0->num++;
+ ao2_ref(bridge0, +1);
+
+ bridge_array_add(bridge0, bridge_channel->chan);
+
+ /* Make the channel compatible with the new bridge it is joining or else formats would go amuck */
+ bridge_make_compatible(bridge0, bridge_channel);
+
+ /* Tell the technology handling bridge0 that the bridge channel is joining */
+ if (bridge0->technology->join) {
+ ast_debug(1, "Giving bridge technology %s notification that %p is joining bridge %p\n", bridge0->technology->name, bridge_channel, bridge0);
+ if (bridge0->technology->join(bridge0, bridge_channel)) {
+ ast_debug(1, "Bridge technology %s failed to join %p to bridge %p\n", bridge0->technology->name, bridge_channel, bridge0);
+ }
+ }
+
+ /* Poke the bridge channel, this will cause it to wake up and execute the proper threading model for the new bridge it is in */
+ pthread_kill(bridge_channel->thread, SIGURG);
+ ast_mutex_lock(&bridge_channel->lock);
+ ast_cond_signal(&bridge_channel->cond);
+ ast_mutex_unlock(&bridge_channel->lock);
+ }
+
+ ast_debug(1, "Merged channels from bridge %p into bridge %p\n", bridge1, bridge0);
+
+ ao2_unlock(bridge1);
+ ao2_unlock(bridge0);
+
+ return 0;
+}
+
+int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ struct ast_bridge_channel *bridge_channel;
+
+ ao2_lock(bridge);
+
+ if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
+ ao2_unlock(bridge);
+ return -1;
+ }
+
+ bridge_channel_suspend(bridge, bridge_channel);
+
+ ao2_unlock(bridge);
+
+ return 0;
+}
+
+int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+ struct ast_bridge_channel *bridge_channel;
+
+ ao2_lock(bridge);
+
+ if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
+ ao2_unlock(bridge);
+ return -1;
+ }
+
+ bridge_channel_unsuspend(bridge, bridge_channel);
+
+ ao2_unlock(bridge);
+
+ return 0;
+}
+
+void ast_bridge_technology_suspend(struct ast_bridge_technology *technology)
+{
+ technology->suspended = 1;
+ return;
+}
+
+void ast_bridge_technology_unsuspend(struct ast_bridge_technology *technology)
+{
+ technology->suspended = 0;
+ return;
+}
+
+int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_features_hook_callback callback, const char *dtmf)
+{
+ if (builtin_features_handlers[feature]) {
+ return -1;
+ }
+
+ if (!ast_strlen_zero(dtmf)) {
+ ast_copy_string(builtin_features_dtmf[feature], dtmf, sizeof(builtin_features_dtmf[feature]));
+ }
+
+ builtin_features_handlers[feature] = callback;
+
+ return 0;
+}
+
+int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature)
+{
+ if (!builtin_features_handlers[feature]) {
+ return -1;
+ }
+
+ builtin_features_handlers[feature] = NULL;
+
+ return 0;
+}
+
+int ast_bridge_features_hook(struct ast_bridge_features *features, const char *dtmf, ast_bridge_features_hook_callback callback, void *hook_pvt)
+{
+ struct ast_bridge_features_hook *hook = NULL;
+
+ /* Allocate new memory and setup it's various variables */
+ if (!(hook = ast_calloc(1, sizeof(*hook)))) {
+ return -1;
+ }
+
+ ast_copy_string(hook->dtmf, dtmf, sizeof(hook->dtmf));
+ hook->callback = callback;
+ hook->hook_pvt = hook_pvt;
+
+ /* Once done we add it onto the list. Now it will be picked up when DTMF is used */
+ AST_LIST_INSERT_TAIL(&features->hooks, hook, entry);
+
+ features->usable = 1;
+
+ return 0;
+}
+
+int ast_bridge_features_enable(struct ast_bridge_features *features, enum ast_bridge_builtin_feature feature, const char *dtmf, void *config)
+{
+ /* If no alternate DTMF stream was provided use the default one */
+ if (ast_strlen_zero(dtmf)) {
+ dtmf = builtin_features_dtmf[feature];
+ /* If no DTMF is still available (ie: it has been disabled) then error out now */
+ if (ast_strlen_zero(dtmf)) {
+ ast_debug(1, "Failed to enable built in feature %d on %p, no DTMF string is available for it.\n", feature, features);
+ return -1;
+ }
+ }
+
+ if (!builtin_features_handlers[feature]) {
+ return -1;
+ }
+
+ /* The rest is basically pretty easy. We create another hook using the built in feature's callback and DTMF, easy as pie. */
+ return ast_bridge_features_hook(features, dtmf, builtin_features_handlers[feature], config);
+}
+
+int ast_bridge_features_set_flag(struct ast_bridge_features *features, enum ast_bridge_feature_flags flag)
+{
+ ast_set_flag(&features->feature_flags, flag);
+ features->usable = 1;
+ return 0;
+}
+
+int ast_bridge_features_init(struct ast_bridge_features *features)
+{
+ /* Zero out the structure */
+ memset(features, 0, sizeof(*features));
+
+ /* Initialize the hooks list, just in case */
+ AST_LIST_HEAD_INIT_NOLOCK(&features->hooks);
+
+ return 0;
+}
+
+int ast_bridge_features_cleanup(struct ast_bridge_features *features)
+{
+ struct ast_bridge_features_hook *hook = NULL;
+
+ /* This is relatively simple, hooks are kept as a list on the features structure so we just pop them off and free them */
+ while ((hook = AST_LIST_REMOVE_HEAD(&features->hooks, entry))) {
+ ast_free(hook);
+ }
+
+ return 0;
+}
+
+int ast_bridge_dtmf_stream(struct ast_bridge *bridge, const char *dtmf, struct ast_channel *chan)
+{
+ struct ast_bridge_channel *bridge_channel = NULL;
+
+ ao2_lock(bridge);
+
+ AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+ if (bridge_channel->chan == chan) {
+ continue;
+ }
+ ast_copy_string(bridge_channel->dtmf_stream_q, dtmf, sizeof(bridge_channel->dtmf_stream_q));
+ ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_DTMF);
+ }
+
+ ao2_unlock(bridge);
+
+ return 0;
+}