From d91dc6d1a8a6e364d9edd6738c20ac5b8545db97 Mon Sep 17 00:00:00 2001
From: Matthew Jordan <mjordan@digium.com>
Date: Wed, 24 Jul 2013 15:38:18 +0000
Subject: [PATCH] Perform the initial renaming of the Bridging API

This patch does the following:
 * It pulls out bridge_channel and puts it into its own translation unit
 * It adds public and protected headers for bridging_channel. Protected
   functions are appropriate only for the Bridging API and sub-classes of a
   bridge.

(issue ASTERISK-22130)


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395253 65c4cc65-6c06-0410-ace0-fbb531ad65f3
---
 apps/app_agent_pool.c                        |   21 +-
 bridges/bridge_builtin_features.c            |    2 +-
 bridges/bridge_builtin_interval_features.c   |    2 +-
 include/asterisk/bridging.h                  |  693 +---
 include/asterisk/bridging_channel.h          |  576 +++
 include/asterisk/bridging_channel_internal.h |  156 +
 include/asterisk/bridging_internal.h         |  129 +-
 include/asterisk/bridging_technology.h       |   16 +
 include/asterisk/channel.h                   |   12 +
 include/asterisk/features.h                  |   14 +-
 main/bridging.c                              | 3311 ++++--------------
 main/bridging_basic.c                        |   25 +-
 main/bridging_channel.c                      | 1838 ++++++++++
 main/channel.c                               |   23 +-
 main/features.c                              |   21 -
 res/parking/parking_bridge.c                 |   11 +-
 res/parking/parking_bridge_features.c        |    3 +
 17 files changed, 3565 insertions(+), 3288 deletions(-)
 create mode 100644 include/asterisk/bridging_channel.h
 create mode 100644 include/asterisk/bridging_channel_internal.h
 create mode 100644 main/bridging_channel.c

diff --git a/apps/app_agent_pool.c b/apps/app_agent_pool.c
index 92209e1eb2..fb73d869fb 100644
--- a/apps/app_agent_pool.c
+++ b/apps/app_agent_pool.c
@@ -41,6 +41,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/module.h"
 #include "asterisk/channel.h"
 #include "asterisk/bridging.h"
+#include "asterisk/bridging_internal.h"
 #include "asterisk/bridging_basic.h"
 #include "asterisk/config_options.h"
 #include "asterisk/features_config.h"
@@ -1054,7 +1055,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
 
 	if (!caller_bridge) {
 		/* Reset agent. */
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel);
 		return;
 	}
 	res = ast_bridge_move(caller_bridge, bridge_channel->bridge, bridge_channel->chan,
@@ -1062,7 +1063,7 @@ static void agent_connect_caller(struct ast_bridge_channel *bridge_channel, stru
 	if (res) {
 		/* Reset agent. */
 		ast_bridge_destroy(caller_bridge);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel);
 		return;
 	}
 	ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_ANSWER, NULL, 0);
@@ -1158,13 +1159,13 @@ static int bridge_agent_hold_heartbeat(struct ast_bridge *bridge, struct ast_bri
 
 	if (deferred_logoff) {
 		ast_debug(1, "Agent %s: Deferred logoff.\n", agent->username);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel);
 	} else if (probation_timedout) {
 		ast_debug(1, "Agent %s: Login complete.\n", agent->username);
 		agent_devstate_changed(agent->username);
 	} else if (ack_timedout) {
 		ast_debug(1, "Agent %s: Ack call timeout.\n", agent->username);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel);
 	} else if (wrapup_timedout) {
 		ast_debug(1, "Agent %s: Wrapup timeout. Ready for new call.\n", agent->username);
 		agent_devstate_changed(agent->username);
@@ -1269,7 +1270,7 @@ static int bridge_agent_hold_push(struct ast_bridge *self, struct ast_bridge_cha
 		 * agent will have some slightly different behavior in corner
 		 * cases.
 		 */
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel);
 		return 0;
 	}
 
@@ -1393,11 +1394,11 @@ static struct ast_bridge *bridge_agent_hold_new(void)
 {
 	struct ast_bridge *bridge;
 
-	bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
-	bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
+	bridge = bridge_alloc(sizeof(struct ast_bridge), &bridge_agent_hold_v_table);
+	bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
 		AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
 			| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM | AST_BRIDGE_FLAG_TRANSFER_PROHIBITED);
-	bridge = ast_bridge_register(bridge);
+	bridge = bridge_register(bridge);
 	return bridge;
 }
 
@@ -1703,7 +1704,7 @@ static void caller_abort_agent(struct agent_pvt *agent)
 	}
 
 	/* Kick the agent out of the holding bridge to reset it. */
-	ast_bridge_change_state_nolock(logged, AST_BRIDGE_CHANNEL_STATE_END);
+	ast_bridge_channel_leave_bridge_nolock(logged);
 	ast_bridge_channel_unlock(logged);
 }
 
@@ -1713,7 +1714,7 @@ static int caller_safety_timeout(struct ast_bridge *bridge, struct ast_bridge_ch
 
 	if (agent->state == AGENT_STATE_CALL_PRESENT) {
 		ast_verb(3, "Agent '%s' did not respond.  Safety timeout.\n", agent->username);
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+		ast_bridge_channel_leave_bridge(bridge_channel);
 		caller_abort_agent(agent);
 	}
 
diff --git a/bridges/bridge_builtin_features.c b/bridges/bridge_builtin_features.c
index 87b74d4e21..8554495eb3 100644
--- a/bridges/bridge_builtin_features.c
+++ b/bridges/bridge_builtin_features.c
@@ -483,7 +483,7 @@ static int feature_hangup(struct ast_bridge *bridge, struct ast_bridge_channel *
 	 * bridge_channel to force the channel out of the bridge and the
 	 * core takes care of the rest.
 	 */
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+	ast_bridge_channel_leave_bridge(bridge_channel);
 	return 0;
 }
 
diff --git a/bridges/bridge_builtin_interval_features.c b/bridges/bridge_builtin_interval_features.c
index a4fd97ba09..2ca3f7ddb5 100644
--- a/bridges/bridge_builtin_interval_features.c
+++ b/bridges/bridge_builtin_interval_features.c
@@ -58,7 +58,7 @@ static int bridge_features_duration_callback(struct ast_bridge *bridge, struct a
 		ast_stream_and_wait(bridge_channel->chan, limits->duration_sound, AST_DIGIT_NONE);
 	}
 
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+	ast_bridge_channel_leave_bridge(bridge_channel);
 
 	ast_test_suite_event_notify("BRIDGE_TIMELIMIT", "Channel1: %s", ast_channel_name(bridge_channel->chan));
 	return -1;
diff --git a/include/asterisk/bridging.h b/include/asterisk/bridging.h
index 0bba614344..bc16ae0bc1 100644
--- a/include/asterisk/bridging.h
+++ b/include/asterisk/bridging.h
@@ -19,7 +19,7 @@
 
 /*!
  * \file
- * \brief Channel Bridging API
+ * \brief Bridging API
  *
  * \author Richard Mudgett <rmudgett@digium.com>
  * \author Joshua Colp <jcolp@digium.com>
@@ -30,7 +30,7 @@
  */
 
 /*!
- * \page AstBridging Channel Bridging API
+ * \page AstBridging Bridging API
  *
  * The purpose of this API is to provide an easy and flexible way to bridge
  * channels of different technologies with different features.
@@ -70,10 +70,15 @@ extern "C" {
 #endif
 
 #include "asterisk/bridging_features.h"
+#include "asterisk/bridging_channel.h"
 #include "asterisk/bridging_roles.h"
 #include "asterisk/dsp.h"
 #include "asterisk/uuid.h"
 
+struct ast_bridge_technology;
+struct ast_bridge;
+struct ast_bridge_tech_optimizations;
+
 /*! \brief Capabilities for a bridge technology */
 enum ast_bridge_capability {
 	/*! Bridge technology can service calls on hold. */
@@ -88,160 +93,7 @@ enum ast_bridge_capability {
 	AST_BRIDGE_CAPABILITY_MULTIMIX = (1 << 4),
 };
 
-/*! \brief State information about a bridged channel */
-enum ast_bridge_channel_state {
-	/*! Waiting for a signal (Channel in the bridge) */
-	AST_BRIDGE_CHANNEL_STATE_WAIT = 0,
-	/*! Bridged channel was forced out and should be hung up (Bridge may dissolve.) */
-	AST_BRIDGE_CHANNEL_STATE_END,
-	/*! Bridged channel was forced out and should be hung up */
-	AST_BRIDGE_CHANNEL_STATE_HANGUP,
-};
-
-enum ast_bridge_channel_thread_state {
-	/*! Bridge channel thread is idle/waiting. */
-	AST_BRIDGE_CHANNEL_THREAD_IDLE,
-	/*! Bridge channel thread is writing a normal/simple frame. */
-	AST_BRIDGE_CHANNEL_THREAD_SIMPLE,
-	/*! Bridge channel thread is processing a frame. */
-	AST_BRIDGE_CHANNEL_THREAD_FRAME,
-};
-
-struct ast_bridge_technology;
-struct ast_bridge;
-
-/*!
- * \brief Structure specific to bridge technologies capable of
- * performing talking optimizations.
- */
-struct ast_bridge_tech_optimizations {
-	/*! The amount of time in ms that talking must be detected before
-	 *  the dsp determines that talking has occurred */
-	unsigned int talking_threshold;
-	/*! The amount of time in ms that silence must be detected before
-	 *  the dsp determines that talking has stopped */
-	unsigned int silence_threshold;
-	/*! Whether or not the bridging technology should drop audio
-	 *  detected as silence from the mix. */
-	unsigned int drop_silence:1;
-};
-
-/*!
- * \brief Structure that contains information regarding a channel in a bridge
- */
-struct ast_bridge_channel {
-/* BUGBUG cond is only here because of external party suspend/unsuspend support. */
-	/*! 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;
-	/*!
-	 * \brief Bridge this channel is participating in
-	 *
-	 * \note The bridge pointer cannot change while the bridge or
-	 * bridge_channel is locked.
-	 */
-	struct ast_bridge *bridge;
-	/*!
-	 * \brief Bridge class private channel data.
-	 *
-	 * \note This information is added when the channel is pushed
-	 * into the bridge and removed when it is pulled from the
-	 * bridge.
-	 */
-	void *bridge_pvt;
-	/*!
-	 * \brief Private information unique to the bridge technology.
-	 *
-	 * \note This information is added when the channel joins the
-	 * bridge's technology and removed when it leaves the bridge's
-	 * technology.
-	 */
-	void *tech_pvt;
-	/*! Thread handling the bridged channel (Needed by ast_bridge_depart) */
-	pthread_t thread;
-	/* v-- These flags change while the bridge is locked or before the channel is in the bridge. */
-	/*! TRUE if the channel is in a bridge. */
-	unsigned int in_bridge:1;
-	/*! TRUE if the channel just joined the bridge. */
-	unsigned int just_joined:1;
-	/*! TRUE if the channel is suspended from the bridge. */
-	unsigned int suspended:1;
-	/*! TRUE if the channel must wait for an ast_bridge_depart to reclaim the channel. */
-	unsigned int depart_wait:1;
-	/* ^-- These flags change while the bridge is locked or before the channel is in the bridge. */
-	/*! Features structure for features that are specific to this channel */
-	struct ast_bridge_features *features;
-	/*! Technology optimization parameters used by bridging technologies capable of
-	 *  optimizing based upon talk detection. */
-	struct ast_bridge_tech_optimizations tech_args;
-	/*! Copy of read format used by chan before join */
-	struct ast_format read_format;
-	/*! Copy of write format used by chan before join */
-	struct ast_format write_format;
-	/*! Call ID associated with bridge channel */
-	struct ast_callid *callid;
-	/*! A clone of the roles living on chan when the bridge channel joins the bridge. This may require some opacification */
-	struct bridge_roles_datastore *bridge_roles;
-	/*! Linked list information */
-	AST_LIST_ENTRY(ast_bridge_channel) entry;
-	/*! Queue of outgoing frames to the channel. */
-	AST_LIST_HEAD_NOLOCK(, ast_frame) wr_queue;
-	/*! Pipe to alert thread when frames are put into the wr_queue. */
-	int alert_pipe[2];
-	/*! TRUE if the bridge channel thread is waiting on channels (needs to be atomically settable) */
-	int waiting;
-	/*!
-	 * \brief The bridge channel thread activity.
-	 *
-	 * \details Used by local channel optimization to determine if
-	 * the thread is in an acceptable state to optimize.
-	 *
-	 * \note Needs to be atomically settable.
-	 */
-	enum ast_bridge_channel_thread_state activity;
-};
-
-enum ast_bridge_action_type {
-	/*! Bridged channel is to detect a feature hook */
-	AST_BRIDGE_ACTION_FEATURE,
-	/*! Bridged channel is to act on an interval hook */
-	AST_BRIDGE_ACTION_INTERVAL,
-	/*! Bridged channel is to send a DTMF stream out */
-	AST_BRIDGE_ACTION_DTMF_STREAM,
-	/*! Bridged channel is to indicate talking start */
-	AST_BRIDGE_ACTION_TALKING_START,
-	/*! Bridged channel is to indicate talking stop */
-	AST_BRIDGE_ACTION_TALKING_STOP,
-	/*! Bridge channel is to play the indicated sound file. */
-	AST_BRIDGE_ACTION_PLAY_FILE,
-	/*! Bridge channel is to run the indicated application. */
-	AST_BRIDGE_ACTION_RUN_APP,
-	/*! Bridge channel is to run the custom callback routine. */
-	AST_BRIDGE_ACTION_CALLBACK,
-	/*! Bridge channel is to get parked. */
-	AST_BRIDGE_ACTION_PARK,
-	/*! Bridge channel is to execute a blind transfer. */
-	AST_BRIDGE_ACTION_BLIND_TRANSFER,
-	/*! Bridge channel is to execute an attended transfer */
-	AST_BRIDGE_ACTION_ATTENDED_TRANSFER,
-
-	/*
-	 * Bridge actions put after this comment must never be put onto
-	 * the bridge_channel wr_queue because they have other resources
-	 * that must be freed.
-	 */
-
-	/*! Bridge reconfiguration deferred technology destruction. */
-	AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY = 1000,
-	/*! Bridge deferred dissolving. */
-	AST_BRIDGE_ACTION_DEFERRED_DISSOLVING,
-};
-
+/*! \brief Video source modes */
 enum ast_bridge_video_mode_type {
 	/*! Video is not allowed in the bridge */
 	AST_BRIDGE_VIDEO_MODE_NONE = 0,
@@ -252,14 +104,14 @@ enum ast_bridge_video_mode_type {
 	AST_BRIDGE_VIDEO_MODE_TALKER_SRC,
 };
 
-/*! This is used for both SINGLE_SRC mode to set what channel
+/*! \brief This is used for both SINGLE_SRC mode to set what channel
  *  should be the current single video feed */
 struct ast_bridge_video_single_src_data {
 	/*! Only accept video coming from this channel */
 	struct ast_channel *chan_vsrc;
 };
 
-/*! This is used for both SINGLE_SRC_TALKER mode to set what channel
+/*! \brief This is used for both SINGLE_SRC_TALKER mode to set what channel
  *  should be the current single video feed */
 struct ast_bridge_video_talker_src_data {
 	/*! Only accept video coming from this channel */
@@ -270,6 +122,7 @@ struct ast_bridge_video_talker_src_data {
 	struct ast_channel *chan_old_vsrc;
 };
 
+/*! \brief Data structure that defines a video source mode */
 struct ast_bridge_video_mode {
 	enum ast_bridge_video_mode_type mode;
 	/* Add data for all the video modes here. */
@@ -371,7 +224,7 @@ typedef int (*ast_bridge_merge_priority_fn)(struct ast_bridge *self);
  * \brief Bridge virtual methods table definition.
  *
  * \note Any changes to this struct must be reflected in
- * ast_bridge_alloc() validity checking.
+ * bridge_alloc() validity checking.
  */
 struct ast_bridge_methods {
 	/*! Bridge class name for log messages. */
@@ -390,6 +243,8 @@ struct ast_bridge_methods {
 	ast_bridge_merge_priority_fn get_merge_priority;
 };
 
+struct ast_bridge_channel;
+
 /*! Softmix technology parameters. */
 struct ast_bridge_softmix {
 	/*! The video mode softmix is using */
@@ -455,75 +310,9 @@ struct ast_bridge {
 	char uniqueid[AST_UUID_STR_LEN];
 };
 
-/*!
- * \brief Register the new bridge with the system.
- * \since 12.0.0
- *
- * \param bridge What to register. (Tolerates a NULL pointer)
- *
- * \code
- * struct ast_bridge *ast_bridge_basic_new(uint32_t capabilities, int flags, uint32 dtmf_features)
- * {
- *     void *bridge;
- *
- *     bridge = ast_bridge_alloc(sizeof(struct ast_bridge_basic), &ast_bridge_basic_v_table);
- *     bridge = ast_bridge_base_init(bridge, capabilities, flags);
- *     bridge = ast_bridge_basic_init(bridge, dtmf_features);
- *     bridge = ast_bridge_register(bridge);
- *     return bridge;
- * }
- * \endcode
- *
- * \note This must be done after a bridge constructor has
- * completed setting up the new bridge but before it returns.
- *
- * \note After a bridge is registered, ast_bridge_destroy() must
- * eventually be called to get rid of the bridge.
- *
- * \retval bridge on success.
- * \retval NULL on error.
- */
-struct ast_bridge *ast_bridge_register(struct ast_bridge *bridge);
-
-/*!
- * \internal
- * \brief Allocate the bridge class object memory.
- * \since 12.0.0
- *
- * \param size Size of the bridge class structure to allocate.
- * \param v_table Bridge class virtual method table.
- *
- * \retval bridge on success.
- * \retval NULL on error.
- */
-struct ast_bridge *ast_bridge_alloc(size_t size, const struct ast_bridge_methods *v_table);
-
 /*! \brief Bridge base class virtual method table. */
 extern struct ast_bridge_methods ast_bridge_base_v_table;
 
-/*!
- * \brief Initialize the base class of the bridge.
- *
- * \param self Bridge to operate upon. (Tolerates a NULL pointer)
- * \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 self on success
- * \retval NULL on failure, self is already destroyed
- *
- * Example usage:
- *
- * \code
- * struct ast_bridge *bridge;
- * bridge = ast_bridge_alloc(sizeof(*bridge), &ast_bridge_base_v_table);
- * bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
- * \endcode
- *
- * This creates a no frills two party bridge that will be
- * destroyed once one of the channels hangs up.
- */
-struct ast_bridge *ast_bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags);
-
 /*!
  * \brief Create a new base class bridge
  *
@@ -824,20 +613,6 @@ int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge
  */
 void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request);
 
-/*!
- * \brief Adjust the bridge_channel's bridge merge inhibit request count.
- * \since 12.0.0
- *
- * \param bridge_channel What to operate on.
- * \param request Inhibit request increment.
- *     (Positive to add requests.  Negative to remove requests.)
- *
- * \note This API call is meant for internal bridging operations.
- *
- * \retval bridge adjusted merge inhibit with reference count.
- */
-struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request);
-
 /*!
  * \brief Suspend a channel temporarily from a bridge
  *
@@ -942,101 +717,6 @@ enum ast_bridge_optimization {
 enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *chan_bridge,
 		struct ast_bridge *peer_bridge);
 
-/*!
- * \brief Try locking the bridge_channel.
- *
- * \param bridge_channel What to try locking
- *
- * \retval 0 on success.
- * \retval non-zero on error.
- */
-#define ast_bridge_channel_trylock(bridge_channel)	_ast_bridge_channel_trylock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel)
-static inline int _ast_bridge_channel_trylock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var)
-{
-	return __ao2_trylock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var);
-}
-
-/*!
- * \brief Lock the bridge_channel.
- *
- * \param bridge_channel What to lock
- *
- * \return Nothing
- */
-#define ast_bridge_channel_lock(bridge_channel)	_ast_bridge_channel_lock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel)
-static inline void _ast_bridge_channel_lock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var)
-{
-	__ao2_lock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var);
-}
-
-/*!
- * \brief Unlock the bridge_channel.
- *
- * \param bridge_channel What to unlock
- *
- * \return Nothing
- */
-#define ast_bridge_channel_unlock(bridge_channel)	_ast_bridge_channel_unlock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel)
-static inline void _ast_bridge_channel_unlock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var)
-{
-	__ao2_unlock(bridge_channel, file, function, line, var);
-}
-
-/*!
- * \brief Lock the bridge associated with the bridge channel.
- * \since 12.0.0
- *
- * \param bridge_channel Channel that wants to lock the bridge.
- *
- * \details
- * This is an upstream lock operation.  The defined locking
- * order is bridge then bridge_channel.
- *
- * \note On entry, neither the bridge nor bridge_channel is locked.
- *
- * \note The bridge_channel->bridge pointer changes because of a
- * bridge-merge/channel-move operation between bridges.
- *
- * \return Nothing
- */
-void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel);
-
-/*!
- * \brief Set bridge channel state to leave bridge (if not leaving already) with no lock.
- *
- * \param bridge_channel Channel to change the state on
- * \param new_state The new state to place the channel into
- *
- * \note This API call is only meant to be used within the
- * bridging module and hook callbacks to request the channel
- * exit the bridge.
- *
- * \note This function assumes the bridge_channel is locked.
- */
-void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
-
-/*!
- * \brief Set bridge channel state to leave bridge (if not leaving already).
- *
- * \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_HANGUP);
- * \endcode
- *
- * This places the channel pointed to by bridge_channel into the
- * state AST_BRIDGE_CHANNEL_STATE_HANGUP if it was
- * AST_BRIDGE_CHANNEL_STATE_WAIT before.
- *
- * \note This API call is only meant to be used within the
- * bridging module and hook callbacks to request the channel
- * exit the bridge.
- */
-void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
-
 /*!
  * \brief Put an action onto the specified bridge.
  * \since 12.0.0
@@ -1052,37 +732,6 @@ void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast
  */
 int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action);
 
-/*!
- * \brief Update the linkedid for all channels in a bridge
- * \since 12.0.0
- *
- * \param bridge The bridge to update the linkedids on
- * \param bridge_channel The channel joining the bridge
- * \param swap The channel being swapped out of the bridge. May be NULL.
- *
- * \note The bridge must be locked prior to calling this function.
- * \note This API call is meant for internal bridging operations.
- */
-void ast_bridge_update_linkedids(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
-
-/*!
- * \brief Update the accountcodes for a channel entering a bridge
- * \since 12.0.0
- *
- * This function updates the accountcode and peeraccount on channels in two-party
- * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense -
- * however accountcode propagation will still occur if the channel joining has an
- * accountcode.
- *
- * \param bridge The bridge to update the accountcodes in
- * \param bridge_channel The channel joining the bridge
- * \param swap The channel being swapped out of the bridge. May be NULL.
- *
- * \note The bridge must be locked prior to calling this function.
- * \note This API call is meant for internal bridging operations.
- */
-void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
-
 /*!
  * \brief Queue the given frame to everyone else.
  * \since 12.0.0
@@ -1099,319 +748,6 @@ void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge
  */
 int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame);
 
-/*!
- * \brief Write a frame to the specified bridge_channel.
- * \since 12.0.0
- *
- * \param bridge_channel Channel to queue the frame.
- * \param fr Frame to write.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- *
- * \note This API call is meant for internal bridging operations.
- * \note BUGBUG This may get moved.
- */
-int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr);
-
-/*!
- * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel work with.
- * \param action Type of bridge action frame.
- * \param data Frame payload data to pass.
- * \param datalen Frame payload data length to pass.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
-
-/*!
- * \brief Queue an action frame onto the bridge channel with data.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to queue the frame onto.
- * \param action Type of bridge action frame.
- * \param data Frame payload data to pass.
- * \param datalen Frame payload data length to pass.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
-
-/*!
- * \brief Write an action frame into the bridge with data.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel is putting the frame into the bridge.
- * \param action Type of bridge action frame.
- * \param data Frame payload data to pass.
- * \param datalen Frame payload data length to pass.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
-
-/*!
- * \brief Queue a control frame onto the bridge channel with data.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to queue the frame onto.
- * \param control Type of control frame.
- * \param data Frame payload data to pass.
- * \param datalen Frame payload data length to pass.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen);
-
-/*!
- * \brief Write a control frame into the bridge with data.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel is putting the frame into the bridge.
- * \param control Type of control frame.
- * \param data Frame payload data to pass.
- * \param datalen Frame payload data length to pass.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen);
-
-/*!
- * \brief Write a hold frame into the bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel is putting the hold into the bridge.
- * \param moh_class The suggested music class for the other end to use.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class);
-
-/*!
- * \brief Write an unhold frame into the bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel is putting the hold into the bridge.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel);
-
-/*!
- * \brief Run an application on the bridge channel.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to run the application on.
- * \param app_name Dialplan application name.
- * \param app_args Arguments for the application. (NULL tolerant)
- * \param moh_class MOH class to request bridge peers to hear while application is running.
- *     NULL if no MOH.
- *     Empty if default MOH class.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \return Nothing
- */
-void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
-
-/*!
- * \brief Write a bridge action run application frame into the bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel is putting the frame into the bridge
- * \param app_name Dialplan application name.
- * \param app_args Arguments for the application. (NULL or empty for no arguments)
- * \param moh_class MOH class to request bridge peers to hear while application is running.
- *     NULL if no MOH.
- *     Empty if default MOH class.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
-
-/*!
- * \brief Queue a bridge action run application frame onto the bridge channel.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to put the frame onto
- * \param app_name Dialplan application name.
- * \param app_args Arguments for the application. (NULL or empty for no arguments)
- * \param moh_class MOH class to request bridge peers to hear while application is running.
- *     NULL if no MOH.
- *     Empty if default MOH class.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
-
-/*!
- * \brief Custom interpretation of the playfile name.
- *
- * \param bridge_channel Which channel to play the file on
- * \param playfile Sound filename to play.
- *
- * \return Nothing
- */
-typedef void (*ast_bridge_custom_play_fn)(struct ast_bridge_channel *bridge_channel, const char *playfile);
-
-/*!
- * \brief Play a file on the bridge channel.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to play the file on
- * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
- * \param playfile Sound filename to play.
- * \param moh_class MOH class to request bridge peers to hear while file is played.
- *     NULL if no MOH.
- *     Empty if default MOH class.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \return Nothing
- */
-void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
-
-/*!
- * \brief Write a bridge action play file frame into the bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel is putting the frame into the bridge
- * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
- * \param playfile Sound filename to play.
- * \param moh_class MOH class to request bridge peers to hear while file is played.
- *     NULL if no MOH.
- *     Empty if default MOH class.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
-
-/*!
- * \brief Queue a bridge action play file frame onto the bridge channel.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to put the frame onto.
- * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
- * \param playfile Sound filename to play.
- * \param moh_class MOH class to request bridge peers to hear while file is played.
- *     NULL if no MOH.
- *     Empty if default MOH class.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
-
-/*!
- * \brief Custom callback run on a bridge channel.
- *
- * \param bridge_channel Which channel to operate on.
- * \param payload Data to pass to the callback. (NULL if none).
- * \param payload_size Size of the payload if payload is non-NULL.  A number otherwise.
- *
- * \note The payload MUST NOT have any resources that need to be freed.
- *
- * \return Nothing
- */
-typedef void (*ast_bridge_custom_callback_fn)(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size);
-
-/*!
- * \brief Write a bridge action custom callback frame into the bridge.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel is putting the frame into the bridge
- * \param callback Custom callback run on a bridge channel.
- * \param payload Data to pass to the callback. (NULL if none).
- * \param payload_size Size of the payload if payload is non-NULL.  A number otherwise.
- *
- * \note The payload MUST NOT have any resources that need to be freed.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size);
-
-/*!
- * \brief Queue a bridge action custom callback frame onto the bridge channel.
- * \since 12.0.0
- *
- * \param bridge_channel Which channel to put the frame onto.
- * \param callback Custom callback run on a bridge channel.
- * \param payload Data to pass to the callback. (NULL if none).
- * \param payload_size Size of the payload if payload is non-NULL.  A number otherwise.
- *
- * \note The payload MUST NOT have any resources that need to be freed.
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size);
-
-/*!
- * \brief Have a bridge channel park a channel in the bridge
- * \since 12.0.0
- *
- * \param bridge_channel Bridge channel performing the parking
- * \param parkee_uuid Unique id of the channel we want to park
- * \param parker_uuid Unique id of the channel parking the call
- * \param app_data string indicating data used for park application (NULL allowed)
- *
- * \note This is intended to be called by bridge hooks.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid,
-	const char *parker_uuid, const char *app_data);
-
-/*!
- * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join
- * \since 12.0.0
- *
- * \param bridge_channel Channel to restore
- */
-void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel);
-
-/*!
- * \brief Get the peer bridge channel of a two party bridge.
- * \since 12.0.0
- *
- * \param bridge_channel What to get the peer of.
- *
- * \note On entry, bridge_channel->bridge is already locked.
- *
- * \note This is an internal bridge function.
- *
- * \retval peer on success.
- * \retval NULL no peer channel.
- */
-struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel);
-
 /*!
  * \brief Adjust the internal mixing sample rate of a bridge
  * used during multimix mode.
@@ -1810,6 +1146,7 @@ struct ast_channel *ast_bridge_peer(struct ast_bridge *bridge, struct ast_channe
  * \return Nothing
  */
 void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags flags);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif
diff --git a/include/asterisk/bridging_channel.h b/include/asterisk/bridging_channel.h
new file mode 100644
index 0000000000..dc559030c2
--- /dev/null
+++ b/include/asterisk/bridging_channel.h
@@ -0,0 +1,576 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Joshua Colp <jcolp@digium.com>
+ * Richard Mudgett <rmudgett@digium.com>
+ * Matt Jordan <mjordan@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 Bridging Channel API
+ *
+ * An API that act on a channel in a bridge
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Richard Mudgett <rmudgett@digium.com>
+ * \author Matt Jordan <mjordan@digium.com>
+ *
+ * See Also:
+ * \ref bridging.h
+ * \arg \ref AstCREDITS
+ */
+
+#ifndef _ASTERISK_BRIDGING_CHANNEL_H
+#define _ASTERISK_BRIDGING_CHANNEL_H
+
+#if defined(__cplusplus) || defined(c_plusplus)
+extern "C" {
+#endif
+
+#include "asterisk/bridging_technology.h"
+
+/*! \brief State information about a bridged channel */
+enum ast_bridge_channel_state {
+	/*! Waiting for a signal (Channel in the bridge) */
+	AST_BRIDGE_CHANNEL_STATE_WAIT = 0,
+	/*! Bridged channel was forced out and should be hung up (Bridge may dissolve.) */
+	AST_BRIDGE_CHANNEL_STATE_END,
+	/*! Bridged channel was forced out and should be hung up */
+	AST_BRIDGE_CHANNEL_STATE_HANGUP,
+};
+
+enum ast_bridge_channel_thread_state {
+	/*! Bridge channel thread is idle/waiting. */
+	AST_BRIDGE_CHANNEL_THREAD_IDLE,
+	/*! Bridge channel thread is writing a normal/simple frame. */
+	AST_BRIDGE_CHANNEL_THREAD_SIMPLE,
+	/*! Bridge channel thread is processing a frame. */
+	AST_BRIDGE_CHANNEL_THREAD_FRAME,
+};
+
+/*! \brief Actions that can be taken on a channel in a bridge */
+enum ast_bridge_action_type {
+	/*! Bridged channel is to detect a feature hook */
+	AST_BRIDGE_ACTION_FEATURE,
+	/*! Bridged channel is to act on an interval hook */
+	AST_BRIDGE_ACTION_INTERVAL,
+	/*! Bridged channel is to send a DTMF stream out */
+	AST_BRIDGE_ACTION_DTMF_STREAM,
+	/*! Bridged channel is to indicate talking start */
+	AST_BRIDGE_ACTION_TALKING_START,
+	/*! Bridged channel is to indicate talking stop */
+	AST_BRIDGE_ACTION_TALKING_STOP,
+	/*! Bridge channel is to play the indicated sound file. */
+	AST_BRIDGE_ACTION_PLAY_FILE,
+	/*! Bridge channel is to run the indicated application. */
+	AST_BRIDGE_ACTION_RUN_APP,
+	/*! Bridge channel is to run the custom callback routine. */
+	AST_BRIDGE_ACTION_CALLBACK,
+	/*! Bridge channel is to get parked. */
+	AST_BRIDGE_ACTION_PARK,
+	/*! Bridge channel is to execute a blind transfer. */
+	AST_BRIDGE_ACTION_BLIND_TRANSFER,
+	/*! Bridge channel is to execute an attended transfer */
+	AST_BRIDGE_ACTION_ATTENDED_TRANSFER,
+
+	/*
+	 * Bridge actions put after this comment must never be put onto
+	 * the bridge_channel wr_queue because they have other resources
+	 * that must be freed.
+	 */
+
+	/*! Bridge reconfiguration deferred technology destruction. */
+	AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY = 1000,
+	/*! Bridge deferred dissolving. */
+	AST_BRIDGE_ACTION_DEFERRED_DISSOLVING,
+};
+
+struct ast_bridge;
+struct ast_bridge_tech_optimizations;
+
+ /*!
+ * \brief Structure that contains information regarding a channel in a bridge
+ */
+struct ast_bridge_channel {
+/* BUGBUG cond is only here because of external party suspend/unsuspend support. */
+	/*! 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;
+	/*!
+	 * \brief Bridge this channel is participating in
+	 *
+	 * \note The bridge pointer cannot change while the bridge or
+	 * bridge_channel is locked.
+	 */
+	struct ast_bridge *bridge;
+	/*!
+	 * \brief Bridge class private channel data.
+	 *
+	 * \note This information is added when the channel is pushed
+	 * into the bridge and removed when it is pulled from the
+	 * bridge.
+	 */
+	void *bridge_pvt;
+	/*!
+	 * \brief Private information unique to the bridge technology.
+	 *
+	 * \note This information is added when the channel joins the
+	 * bridge's technology and removed when it leaves the bridge's
+	 * technology.
+	 */
+	void *tech_pvt;
+	/*! Thread handling the bridged channel (Needed by ast_bridge_depart) */
+	pthread_t thread;
+	/* v-- These flags change while the bridge is locked or before the channel is in the bridge. */
+	/*! TRUE if the channel is in a bridge. */
+	unsigned int in_bridge:1;
+	/*! TRUE if the channel just joined the bridge. */
+	unsigned int just_joined:1;
+	/*! TRUE if the channel is suspended from the bridge. */
+	unsigned int suspended:1;
+	/*! TRUE if the channel must wait for an ast_bridge_depart to reclaim the channel. */
+	unsigned int depart_wait:1;
+	/* ^-- These flags change while the bridge is locked or before the channel is in the bridge. */
+	/*! Features structure for features that are specific to this channel */
+	struct ast_bridge_features *features;
+	/*! Technology optimization parameters used by bridging technologies capable of
+	 *  optimizing based upon talk detection. */
+	struct ast_bridge_tech_optimizations tech_args;
+	/*! Copy of read format used by chan before join */
+	struct ast_format read_format;
+	/*! Copy of write format used by chan before join */
+	struct ast_format write_format;
+	/*! Call ID associated with bridge channel */
+	struct ast_callid *callid;
+	/*! A clone of the roles living on chan when the bridge channel joins the bridge. This may require some opacification */
+	struct bridge_roles_datastore *bridge_roles;
+	/*! Linked list information */
+	AST_LIST_ENTRY(ast_bridge_channel) entry;
+	/*! Queue of outgoing frames to the channel. */
+	AST_LIST_HEAD_NOLOCK(, ast_frame) wr_queue;
+	/*! Pipe to alert thread when frames are put into the wr_queue. */
+	int alert_pipe[2];
+	/*! TRUE if the bridge channel thread is waiting on channels (needs to be atomically settable) */
+	int waiting;
+	/*!
+	 * \brief The bridge channel thread activity.
+	 *
+	 * \details Used by local channel optimization to determine if
+	 * the thread is in an acceptable state to optimize.
+	 *
+	 * \note Needs to be atomically settable.
+	 */
+	enum ast_bridge_channel_thread_state activity;
+};
+
+/*!
+ * \brief Try locking the bridge_channel.
+ *
+ * \param bridge_channel What to try locking
+ *
+ * \retval 0 on success.
+ * \retval non-zero on error.
+ */
+#define ast_bridge_channel_trylock(bridge_channel)	_ast_bridge_channel_trylock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel)
+static inline int _ast_bridge_channel_trylock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var)
+{
+	return __ao2_trylock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var);
+}
+
+/*!
+ * \brief Lock the bridge_channel.
+ *
+ * \param bridge_channel What to lock
+ *
+ * \return Nothing
+ */
+#define ast_bridge_channel_lock(bridge_channel)	_ast_bridge_channel_lock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel)
+static inline void _ast_bridge_channel_lock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var)
+{
+	__ao2_lock(bridge_channel, AO2_LOCK_REQ_MUTEX, file, function, line, var);
+}
+
+/*!
+ * \brief Unlock the bridge_channel.
+ *
+ * \param bridge_channel What to unlock
+ *
+ * \return Nothing
+ */
+#define ast_bridge_channel_unlock(bridge_channel)	_ast_bridge_channel_unlock(bridge_channel, __FILE__, __PRETTY_FUNCTION__, __LINE__, #bridge_channel)
+static inline void _ast_bridge_channel_unlock(struct ast_bridge_channel *bridge_channel, const char *file, const char *function, int line, const char *var)
+{
+	__ao2_unlock(bridge_channel, file, function, line, var);
+}
+
+/*!
+ * \brief Lock the bridge associated with the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel that wants to lock the bridge.
+ *
+ * \details
+ * This is an upstream lock operation.  The defined locking
+ * order is bridge then bridge_channel.
+ *
+ * \note On entry, neither the bridge nor bridge_channel is locked.
+ *
+ * \note The bridge_channel->bridge pointer changes because of a
+ * bridge-merge/channel-move operation between bridges.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Ask the bridged channel to leave the bridge it is currently in
+ *
+ * \param bridge_channel Channel to leave the bridge
+ */
+void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Ask the bridged channel to leave the bridge it is currently in
+ *
+ * \param bridge_channel Channel to leave the bridge
+ *
+ * \note This function assumes the bridge_channel is locked.
+ */
+void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Write a frame to the specified bridge_channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to queue the frame.
+ * \param fr Frame to write.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ *
+ * \note This API call is meant for internal bridging operations.
+ * \note BUGBUG This may get moved.
+ */
+int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr);
+
+/*!
+ * \brief Used to queue an action frame onto a bridge channel and write an action frame into a bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel work with.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+typedef int (*ast_bridge_channel_post_action_data)(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
+
+/*!
+ * \brief Queue an action frame onto the bridge channel with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
+
+/*!
+ * \brief Write an action frame into the bridge with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge.
+ * \param action Type of bridge action frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen);
+
+/*!
+ * \brief Queue a control frame onto the bridge channel with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to queue the frame onto.
+ * \param control Type of control frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen);
+
+/*!
+ * \brief Write a control frame into the bridge with data.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge.
+ * \param control Type of control frame.
+ * \param data Frame payload data to pass.
+ * \param datalen Frame payload data length to pass.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen);
+
+/*!
+ * \brief Write a hold frame into the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the hold into the bridge.
+ * \param moh_class The suggested music class for the other end to use.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class);
+
+/*!
+ * \brief Write an unhold frame into the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the hold into the bridge.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Run an application on the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to run the application on.
+ * \param app_name Dialplan application name.
+ * \param app_args Arguments for the application. (NULL tolerant)
+ * \param moh_class MOH class to request bridge peers to hear while application is running.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
+
+/*!
+ * \brief Write a bridge action run application frame into the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge
+ * \param app_name Dialplan application name.
+ * \param app_args Arguments for the application. (NULL or empty for no arguments)
+ * \param moh_class MOH class to request bridge peers to hear while application is running.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
+
+/*!
+ * \brief Queue a bridge action run application frame onto the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to put the frame onto
+ * \param app_name Dialplan application name.
+ * \param app_args Arguments for the application. (NULL or empty for no arguments)
+ * \param moh_class MOH class to request bridge peers to hear while application is running.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class);
+
+/*!
+ * \brief Custom interpretation of the playfile name.
+ *
+ * \param bridge_channel Which channel to play the file on
+ * \param playfile Sound filename to play.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_custom_play_fn)(struct ast_bridge_channel *bridge_channel, const char *playfile);
+
+/*!
+ * \brief Play a file on the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to play the file on
+ * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
+ * \param playfile Sound filename to play.
+ * \param moh_class MOH class to request bridge peers to hear while file is played.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \return Nothing
+ */
+void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
+
+/*!
+ * \brief Write a bridge action play file frame into the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge
+ * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
+ * \param playfile Sound filename to play.
+ * \param moh_class MOH class to request bridge peers to hear while file is played.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
+
+/*!
+ * \brief Queue a bridge action play file frame onto the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to put the frame onto.
+ * \param custom_play Call this function to play the playfile. (NULL if normal sound file to play)
+ * \param playfile Sound filename to play.
+ * \param moh_class MOH class to request bridge peers to hear while file is played.
+ *     NULL if no MOH.
+ *     Empty if default MOH class.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class);
+
+/*!
+ * \brief Custom callback run on a bridge channel.
+ *
+ * \param bridge_channel Which channel to operate on.
+ * \param payload Data to pass to the callback. (NULL if none).
+ * \param payload_size Size of the payload if payload is non-NULL.  A number otherwise.
+ *
+ * \note The payload MUST NOT have any resources that need to be freed.
+ *
+ * \return Nothing
+ */
+typedef void (*ast_bridge_custom_callback_fn)(struct ast_bridge_channel *bridge_channel, const void *payload, size_t payload_size);
+
+/*!
+ * \brief Write a bridge action custom callback frame into the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is putting the frame into the bridge
+ * \param callback Custom callback run on a bridge channel.
+ * \param payload Data to pass to the callback. (NULL if none).
+ * \param payload_size Size of the payload if payload is non-NULL.  A number otherwise.
+ *
+ * \note The payload MUST NOT have any resources that need to be freed.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size);
+
+/*!
+ * \brief Queue a bridge action custom callback frame onto the bridge channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to put the frame onto.
+ * \param callback Custom callback run on a bridge channel.
+ * \param payload Data to pass to the callback. (NULL if none).
+ * \param payload_size Size of the payload if payload is non-NULL.  A number otherwise.
+ *
+ * \note The payload MUST NOT have any resources that need to be freed.
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size);
+
+/*!
+ * \brief Have a bridge channel park a channel in the bridge
+ * \since 12.0.0
+ *
+ * \param bridge_channel Bridge channel performing the parking
+ * \param parkee_uuid Unique id of the channel we want to park
+ * \param parker_uuid Unique id of the channel parking the call
+ * \param app_data string indicating data used for park application (NULL allowed)
+ *
+ * \note This is intended to be called by bridge hooks.
+ *
+ * \retval 0 on success.
+ * \retval -1 on error.
+ */
+int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid,
+	const char *parker_uuid, const char *app_data);
+
+/*!
+ * \brief Restore the formats of a bridge channel's channel to how they were before bridge_channel_join
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to restore
+ */
+void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \brief Get the peer bridge channel of a two party bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to get the peer of.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \note This is an internal bridge function.
+ *
+ * \retval peer on success.
+ * \retval NULL no peer channel.
+ */
+struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel);
+
+#if defined(__cplusplus) || defined(c_plusplus)
+}
+#endif
+
+#endif	/* _ASTERISK_BRIDGING_CHANNEL_H */
\ No newline at end of file
diff --git a/include/asterisk/bridging_channel_internal.h b/include/asterisk/bridging_channel_internal.h
new file mode 100644
index 0000000000..8f5eb4dd8f
--- /dev/null
+++ b/include/asterisk/bridging_channel_internal.h
@@ -0,0 +1,156 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013 Digium, Inc.
+ *
+ * Matt Jordan <mjordan@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 Private Bridging Channel API
+ *
+ * \author Matt Jordan <mjordan@digium.com>
+ *
+ * A private API to manipulate channels in a bridge. These can be called on a channel in
+ * a bridge by the bridging API, but should not be called by external consumers of the
+ * Bridging API.
+ *
+ * See Also:
+ * \arg \ref AstCREDITS
+ */
+
+#ifndef _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H
+#define _ASTERISK_PRIVATE_BRIDGING_CHANNEL_H
+
+struct blind_transfer_data {
+	char exten[AST_MAX_EXTENSION];
+	char context[AST_MAX_CONTEXT];
+};
+
+/*!
+ * \brief Adjust the bridge_channel's bridge merge inhibit request count.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to operate on.
+ * \param request Inhibit request increment.
+ *     (Positive to add requests.  Negative to remove requests.)
+ *
+ * \note This API call is meant for internal bridging operations.
+ *
+ * \retval bridge adjusted merge inhibit with reference count.
+ */
+struct ast_bridge *bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request);
+
+/*!
+ * \internal
+ * \brief Pull the bridge channel out of its current bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to pull.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \return Nothing
+ */
+void bridge_channel_pull(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \internal
+ * \brief Push the bridge channel into its specified bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to push.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.  The channel did not get pushed.
+ */
+int bridge_channel_push(struct ast_bridge_channel *bridge_channel);
+
+void bridge_channel_join(struct ast_bridge_channel *bridge_channel);
+
+void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel);
+
+void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel);
+
+/*!
+ * \internal
+ * \brief Update the linkedids for all channels in a bridge
+ * \since 12.0.0
+ *
+ * \param bridge_channel The channel joining the bridge
+ * \param swap The channel being swapped out of the bridge. May be NULL.
+ *
+ * \note The bridge must be locked prior to calling this function. This should be called
+ * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge
+ */
+void bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
+
+/*!
+ * \internal
+ * \brief Update the accountcodes for a channel entering a bridge
+ * \since 12.0.0
+ *
+ * This function updates the accountcode and peeraccount on channels in two-party
+ * bridges. In multi-party bridges, peeraccount is not set - it doesn't make much sense -
+ * however accountcode propagation will still occur if the channel joining has an
+ * accountcode.
+ *
+ * \param bridge_channel The channel joining the bridge
+ * \param swap The channel being swapped out of the bridge. May be NULL.
+ *
+ * \note The bridge must be locked prior to calling this function. This should be called
+ * during a \ref bridge_channel_push operation, typically by a sub-class of a bridge
+ */
+void bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap);
+
+
+/*!
+ * \brief Set bridge channel state to leave bridge (if not leaving already) with no lock.
+ *
+ * \param bridge_channel Channel to change the state on
+ * \param new_state The new state to place the channel into
+ *
+ * \note This API call is only meant to be used within the
+ * bridging module and hook callbacks to request the channel
+ * exit the bridge.
+ *
+ * \note This function assumes the bridge_channel is locked.
+ */
+void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
+
+/*!
+ * \brief Set bridge channel state to leave bridge (if not leaving already).
+ *
+ * \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_HANGUP);
+ * \endcode
+ *
+ * This places the channel pointed to by bridge_channel into the
+ * state AST_BRIDGE_CHANNEL_STATE_HANGUP if it was
+ * AST_BRIDGE_CHANNEL_STATE_WAIT before.
+ *
+ * \note This API call is only meant to be used within the
+ * bridging module and hook callbacks to request the channel
+ * exit the bridge.
+ */
+void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state);
+
+#endif /* _ASTERISK_PRIVATE_BRIDGING_H */
\ No newline at end of file
diff --git a/include/asterisk/bridging_internal.h b/include/asterisk/bridging_internal.h
index 132c42b76a..cafa693f59 100644
--- a/include/asterisk/bridging_internal.h
+++ b/include/asterisk/bridging_internal.h
@@ -20,6 +20,11 @@
  * \file
  * \brief Private Bridging API
  *
+ * Functions in this file are intended to be used by the Bridging API,
+ * bridge mixing technologies, and bridge sub-classes. Users of bridges
+ * that do not fit those three categories should *not* use the API
+ * defined in this file.
+ *
  * \author Mark Michelson <mmichelson@digium.com>
  *
  * See Also:
@@ -32,6 +37,72 @@
 struct ast_bridge;
 struct ast_bridge_channel;
 
+/*!
+ * \brief Register the new bridge with the system.
+ * \since 12.0.0
+ *
+ * \param bridge What to register. (Tolerates a NULL pointer)
+ *
+ * \code
+ * struct ast_bridge *ast_bridge_basic_new(uint32_t capabilities, int flags, uint32 dtmf_features)
+ * {
+ *     void *bridge;
+ *
+ *     bridge = bridge_alloc(sizeof(struct ast_bridge_basic), &ast_bridge_basic_v_table);
+ *     bridge = bridge_base_init(bridge, capabilities, flags);
+ *     bridge = ast_bridge_basic_init(bridge, dtmf_features);
+ *     bridge = bridge_register(bridge);
+ *     return bridge;
+ * }
+ * \endcode
+ *
+ * \note This must be done after a bridge constructor has
+ * completed setting up the new bridge but before it returns.
+ *
+ * \note After a bridge is registered, ast_bridge_destroy() must
+ * eventually be called to get rid of the bridge.
+ *
+ * \retval bridge on success.
+ * \retval NULL on error.
+ */
+struct ast_bridge *bridge_register(struct ast_bridge *bridge);
+
+/*!
+ * \internal
+ * \brief Allocate the bridge class object memory.
+ * \since 12.0.0
+ *
+ * \param size Size of the bridge class structure to allocate.
+ * \param v_table Bridge class virtual method table.
+ *
+ * \retval bridge on success.
+ * \retval NULL on error.
+ */
+struct ast_bridge *bridge_alloc(size_t size, const struct ast_bridge_methods *v_table);
+
+/*!
+ * \brief Initialize the base class of the bridge.
+ *
+ * \param self Bridge to operate upon. (Tolerates a NULL pointer)
+ * \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 self on success
+ * \retval NULL on failure, self is already destroyed
+ *
+ * Example usage:
+ *
+ * \code
+ * struct ast_bridge *bridge;
+ * bridge = bridge_alloc(sizeof(*bridge), &ast_bridge_base_v_table);
+ * bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_1TO1MIX, AST_BRIDGE_FLAG_DISSOLVE_HANGUP);
+ * \endcode
+ *
+ * This creates a no frills two party bridge that will be
+ * destroyed once one of the channels hangs up.
+ */
+struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags);
+
 /*!
  * \internal
  * \brief Move a bridge channel from one bridge to another.
@@ -47,7 +118,7 @@ struct ast_bridge_channel;
  * \retval 0 on success.
  * \retval -1 on failure.
  */
-int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel,
+int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel,
 		int attempt_recovery, unsigned int optimized);
 
 /*!
@@ -68,7 +139,7 @@ int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bri
  * This moves the channels in src_bridge into the bridge pointed
  * to by dst_bridge.
  */
-void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge,
+void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge,
 		struct ast_bridge_channel **kick_me, unsigned int num_kick, unsigned int optimized);
 
 /*!
@@ -83,6 +154,58 @@ void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
  * \retval bridge_channel if channel is in the bridge.
  * \retval NULL if not in bridge.
  */
-struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan);
+struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan);
+
+/*!
+ * \internal
+ * \brief Adjust the bridge merge inhibit request count.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ * \param request Inhibit request increment.
+ *     (Positive to add requests.  Negative to remove requests.)
+ *
+ * \note This function assumes bridge is locked.
+ *
+ * \return Nothing
+ */
+void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request);
+
+/*!
+ * \internal
+ * \brief Notify the bridge that it has been reconfigured.
+ * \since 12.0.0
+ *
+ * \param bridge Reconfigured bridge.
+ * \param colp_update Whether to perform COLP updates.
+ *
+ * \details
+ * After a series of bridge_channel_push and
+ * bridge_channel_pull calls, you need to call this function
+ * to cause the bridge to complete restructuring for the change
+ * in the channel makeup of the bridge.
+ *
+ * \note On entry, the bridge is already locked.
+ *
+ * \return Nothing
+ */
+void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update);
+
+/*!
+ * \internal
+ * \brief Dissolve the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge Bridge to eject all channels
+ *
+ * \details
+ * Force out all channels that are not already going out of the
+ * bridge.  Any new channels joining will leave immediately.
+ *
+ * \note On entry, bridge is already locked.
+ *
+ * \return Nothing
+ */
+void bridge_dissolve(struct ast_bridge *bridge);
 
 #endif /* _ASTERISK_PRIVATE_BRIDGING_H */
diff --git a/include/asterisk/bridging_technology.h b/include/asterisk/bridging_technology.h
index 4e680679e4..e037b7490e 100644
--- a/include/asterisk/bridging_technology.h
+++ b/include/asterisk/bridging_technology.h
@@ -41,6 +41,22 @@ enum ast_bridge_preference {
 	AST_BRIDGE_PREFERENCE_BASE_MULTIMIX = 10,
 };
 
+/*!
+ * \brief Structure specific to bridge technologies capable of
+ * performing talking optimizations.
+ */
+struct ast_bridge_tech_optimizations {
+	/*! The amount of time in ms that talking must be detected before
+	 *  the dsp determines that talking has occurred */
+	unsigned int talking_threshold;
+	/*! The amount of time in ms that silence must be detected before
+	 *  the dsp determines that talking has stopped */
+	unsigned int silence_threshold;
+	/*! Whether or not the bridging technology should drop audio
+	 *  detected as silence from the mix. */
+	unsigned int drop_silence:1;
+};
+
 /*!
  * \brief Structure that is the essence of a bridge technology
  */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index d8db4705d0..f6cdd4e628 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -4385,4 +4385,16 @@ int ast_channel_suppress(struct ast_channel *chan, unsigned int direction, enum
  */
 int ast_channel_unsuppress(struct ast_channel *chan, unsigned int direction, enum ast_frame_type frametype);
 
+/*!
+ * \brief Simulate a DTMF end on a broken bridge channel.
+ *
+ * \param chan Channel sending DTMF that has not ended.
+ * \param digit DTMF digit to stop.
+ * \param start DTMF digit start time.
+ * \param why Reason bridge broken.
+ *
+ * \return Nothing
+ */
+void ast_channel_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why);
+
 #endif /* _ASTERISK_CHANNEL_H */
diff --git a/include/asterisk/features.h b/include/asterisk/features.h
index 162a7fc372..96eec98c86 100644
--- a/include/asterisk/features.h
+++ b/include/asterisk/features.h
@@ -146,25 +146,13 @@ int ast_masq_park_call(struct ast_channel *park_me, struct ast_channel *parker,
  */
 int ast_masq_park_call_exten(struct ast_channel *park_me, struct ast_channel *parker, const char *park_exten, const char *park_context, int timeout, int *extout);
 
-/*! 
+/*!
  * \brief Determine if parking extension exists in a given context
  * \retval 0 if extension does not exist
  * \retval 1 if extension does exist
 */
 int ast_parking_ext_valid(const char *exten_str, struct ast_channel *chan, const char *context);
 
-/*!
- * \brief Simulate a DTMF end on a broken bridge channel.
- *
- * \param chan Channel sending DTMF that has not ended.
- * \param digit DTMF digit to stop.
- * \param start DTMF digit start time.
- * \param why Reason bridge broken.
- *
- * \return Nothing
- */
-void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why);
-
 /*! \brief Bridge a call, optionally allowing redirection */
 int ast_bridge_call(struct ast_channel *chan, struct ast_channel *peer,struct ast_bridge_config *config);
 
diff --git a/main/bridging.c b/main/bridging.c
index fe38d4cac6..5f001d1f5e 100644
--- a/main/bridging.c
+++ b/main/bridging.c
@@ -18,7 +18,7 @@
 
 /*! \file
  *
- * \brief Channel Bridging API
+ * \brief Bridging API
  *
  * \author Joshua Colp <jcolp@digium.com>
  */
@@ -31,8 +31,6 @@
 
 ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 
-#include <signal.h>
-
 #include "asterisk/logger.h"
 #include "asterisk/channel.h"
 #include "asterisk/options.h"
@@ -42,6 +40,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/bridging.h"
 #include "asterisk/bridging_basic.h"
 #include "asterisk/bridging_technology.h"
+#include "asterisk/bridging_channel.h"
 #include "asterisk/stasis_bridging.h"
 #include "asterisk/stasis_channels.h"
 #include "asterisk/app.h"
@@ -62,8 +61,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/parking.h"
 #include "asterisk/core_local.h"
 #include "asterisk/core_unreal.h"
-#include "asterisk/features_config.h"
 #include "asterisk/bridging_internal.h"
+#include "asterisk/bridging_channel_internal.h"
 
 /*! All bridges container. */
 static struct ao2_container *bridges;
@@ -204,62 +203,6 @@ int ast_bridge_technology_unregister(struct ast_bridge_technology *technology)
 	return current ? 0 : -1;
 }
 
-void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge *bridge;
-
-	for (;;) {
-		/* Safely get the bridge pointer */
-		ast_bridge_channel_lock(bridge_channel);
-		bridge = bridge_channel->bridge;
-		ao2_ref(bridge, +1);
-		ast_bridge_channel_unlock(bridge_channel);
-
-		/* Lock the bridge and see if it is still the bridge we need to lock. */
-		ast_bridge_lock(bridge);
-		if (bridge == bridge_channel->bridge) {
-			ao2_ref(bridge, -1);
-			return;
-		}
-		ast_bridge_unlock(bridge);
-		ao2_ref(bridge, -1);
-	}
-}
-
-static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
-{
-	if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
-		while (bridge_channel->waiting) {
-			pthread_kill(bridge_channel->thread, SIGURG);
-			sched_yield();
-		}
-	}
-}
-
-void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
-{
-/* BUGBUG need cause code for the bridge_channel leaving the bridge. */
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
-		return;
-	}
-
-	ast_debug(1, "Setting %p(%s) state from:%d to:%d\n",
-		bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state,
-		new_state);
-
-	/* Change the state on the bridge channel */
-	bridge_channel->state = new_state;
-
-	bridge_channel_poke(bridge_channel);
-}
-
-void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
-{
-	ast_bridge_channel_lock(bridge_channel);
-	ast_bridge_change_state_nolock(bridge_channel, new_state);
-	ast_bridge_channel_unlock(bridge_channel);
-}
-
 /*!
  * \internal
  * \brief Put an action onto the specified bridge. Don't dup the action frame.
@@ -293,198 +236,6 @@ int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action)
 	return 0;
 }
 
-void ast_bridge_update_accountcodes(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
-{
-	struct ast_bridge_channel *other = NULL;
-
-	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
-		if (other == swap) {
-			continue;
-		}
-
-		if (!ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan)) && ast_strlen_zero(ast_channel_peeraccount(other->chan))) {
-			ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n",
-					ast_channel_accountcode(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
-			ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan));
-		}
-		if (!ast_strlen_zero(ast_channel_accountcode(other->chan)) && ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan))) {
-			ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n",
-					ast_channel_accountcode(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
-			ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan));
-		}
-		if (!ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan)) && ast_strlen_zero(ast_channel_accountcode(other->chan))) {
-			ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n",
-					ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
-			ast_channel_accountcode_set(other->chan, ast_channel_peeraccount(bridge_channel->chan));
-		}
-		if (!ast_strlen_zero(ast_channel_peeraccount(other->chan)) && ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan))) {
-			ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n",
-					ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
-			ast_channel_accountcode_set(bridge_channel->chan, ast_channel_peeraccount(other->chan));
-		}
-		if (bridge->num_channels == 2) {
-			if (strcmp(ast_channel_accountcode(bridge_channel->chan), ast_channel_peeraccount(other->chan))) {
-				ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n",
-						ast_channel_peeraccount(other->chan), ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
-				ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan));
-			}
-			if (strcmp(ast_channel_accountcode(other->chan), ast_channel_peeraccount(bridge_channel->chan))) {
-				ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n",
-						ast_channel_peeraccount(bridge_channel->chan), ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
-				ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan));
-			}
-		}
-	}
-}
-
-void ast_bridge_update_linkedids(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
-{
-	struct ast_bridge_channel *other = NULL;
-	const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan);
-
-	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
-		if (other == swap) {
-			continue;
-		}
-		oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan));
-	}
-
-	if (ast_strlen_zero(oldest_linkedid)) {
-		return;
-	}
-
-	ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid);
-	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
-		if (other == swap) {
-			continue;
-		}
-		ast_channel_linkedid_set(other->chan, oldest_linkedid);
-	}
-}
-
-int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
-{
-	struct ast_frame *dup;
-	char nudge = 0;
-
-	if (bridge_channel->suspended
-		/* Also defer DTMF frames. */
-		&& fr->frametype != AST_FRAME_DTMF_BEGIN
-		&& fr->frametype != AST_FRAME_DTMF_END
-		&& !ast_is_deferrable_frame(fr)) {
-		/* Drop non-deferable frames when suspended. */
-		return 0;
-	}
-	if (fr->frametype == AST_FRAME_NULL) {
-		/* "Accept" the frame and discard it. */
-		return 0;
-	}
-
-	dup = ast_frdup(fr);
-	if (!dup) {
-		return -1;
-	}
-
-	ast_bridge_channel_lock(bridge_channel);
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
-		/* Drop frames on channels leaving the bridge. */
-		ast_bridge_channel_unlock(bridge_channel);
-		ast_frfree(dup);
-		return 0;
-	}
-
-	AST_LIST_INSERT_TAIL(&bridge_channel->wr_queue, dup, frame_list);
-	if (write(bridge_channel->alert_pipe[1], &nudge, sizeof(nudge)) != sizeof(nudge)) {
-		ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan));
-	}
-	ast_bridge_channel_unlock(bridge_channel);
-	return 0;
-}
-
-int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen)
-{
-	struct ast_frame frame = {
-		.frametype = AST_FRAME_BRIDGE_ACTION,
-		.subclass.integer = action,
-		.datalen = datalen,
-		.data.ptr = (void *) data,
-	};
-
-	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
-}
-
-int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
-{
-	struct ast_frame frame = {
-		.frametype = AST_FRAME_CONTROL,
-		.subclass.integer = control,
-		.datalen = datalen,
-		.data.ptr = (void *) data,
-	};
-
-	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
-}
-
-int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
-{
-	struct ast_bridge_channel *cur;
-	int not_written = -1;
-
-	if (frame->frametype == AST_FRAME_NULL) {
-		/* "Accept" the frame and discard it. */
-		return 0;
-	}
-
-	AST_LIST_TRAVERSE(&bridge->channels, cur, entry) {
-		if (cur == bridge_channel) {
-			continue;
-		}
-		if (!ast_bridge_channel_queue_frame(cur, frame)) {
-			not_written = 0;
-		}
-	}
-	return not_written;
-}
-
-void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel)
-{
-	/* Restore original formats of the channel as they came in */
-	if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Bridge is returning %p(%s) to read format %s\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&bridge_channel->read_format));
-		if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) {
-			ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan),
-				ast_getformatname(&bridge_channel->read_format));
-		}
-	}
-	if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) {
-		ast_debug(1, "Bridge is returning %p(%s) to write format %s\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&bridge_channel->write_format));
-		if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) {
-			ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan),
-				ast_getformatname(&bridge_channel->write_format));
-		}
-	}
-}
-
-struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct ast_channel *chan)
-{
-	struct ast_bridge_channel *bridge_channel;
-
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		if (bridge_channel->chan == chan) {
-			break;
-		}
-	}
-
-	return bridge_channel;
-}
-
 /*!
  * \internal
  * \brief Dissolve the bridge.
@@ -500,7 +251,7 @@ struct ast_bridge_channel *find_bridge_channel(struct ast_bridge *bridge, struct
  *
  * \return Nothing
  */
-static void bridge_dissolve(struct ast_bridge *bridge)
+void bridge_dissolve(struct ast_bridge *bridge)
 {
 	struct ast_bridge_channel *bridge_channel;
 	struct ast_frame action = {
@@ -524,49 +275,6 @@ static void bridge_dissolve(struct ast_bridge *bridge)
 	ast_bridge_queue_action(bridge, &action);
 }
 
-/*!
- * \internal
- * \brief Check if a bridge should dissolve and do it.
- * \since 12.0.0
- *
- * \param bridge_channel Channel causing the check.
- *
- * \note On entry, bridge_channel->bridge is already locked.
- *
- * \return Nothing
- */
-static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge *bridge = bridge_channel->bridge;
-
-	if (bridge->dissolved) {
-		return;
-	}
-
-	if (!bridge->num_channels
-		&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) {
-		/* Last channel leaving the bridge turns off the lights. */
-		bridge_dissolve(bridge);
-		return;
-	}
-
-	switch (bridge_channel->state) {
-	case AST_BRIDGE_CHANNEL_STATE_END:
-		/* Do we need to dissolve the bridge because this channel hung up? */
-		if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
-			|| (bridge_channel->features->usable
-				&& ast_test_flag(&bridge_channel->features->feature_flags,
-					AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) {
-			bridge_dissolve(bridge);
-			return;
-		}
-		break;
-	default:
-		break;
-	}
-/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
-}
-
 /*!
  * \internal
  * \brief Check if a bridge should dissolve because of a stolen channel and do it.
@@ -651,2487 +359,1032 @@ static void bridge_reconfigured_connected_line_update(struct ast_bridge *bridge)
 
 /*!
  * \internal
- * \brief Pull the bridge channel out of its current bridge.
+ * \brief Complete joining a channel to the bridge.
  * \since 12.0.0
  *
- * \param bridge_channel Channel to pull.
+ * \param bridge What to operate upon.
+ * \param bridge_channel What is joining the bridge technology.
  *
- * \note On entry, bridge_channel->bridge is already locked.
+ * \note On entry, bridge is already locked.
  *
  * \return Nothing
  */
-static void bridge_channel_pull(struct ast_bridge_channel *bridge_channel)
+static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	struct ast_bridge *bridge = bridge_channel->bridge;
-
-	if (!bridge_channel->in_bridge) {
-		return;
-	}
-	bridge_channel->in_bridge = 0;
-
-	ast_debug(1, "Bridge %s: pulling %p(%s)\n",
-		bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
-
-	ast_verb(3, "Channel %s left '%s' %s-bridge <%s>\n",
-		ast_channel_name(bridge_channel->chan),
-		bridge->technology->name,
-		bridge->v_table->name,
-		bridge->uniqueid);
+	/* Make the channel compatible with the bridge */
+	bridge_make_compatible(bridge, bridge_channel);
 
-/* BUGBUG This is where incoming HOLD/UNHOLD memory should write UNHOLD into bridge. (if not local optimizing) */
-/* BUGBUG This is where incoming DTMF begin/end memory should write DTMF end into bridge. (if not local optimizing) */
-	if (!bridge_channel->just_joined) {
-		/* Tell the bridge technology we are leaving so they tear us down */
-		ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology\n",
+	/* Tell the bridge technology we are joining so they set us up */
+	ast_debug(1, "Bridge %s: %p(%s) is joining %s technology\n",
+		bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
+		bridge->technology->name);
+	if (bridge->technology->join
+		&& bridge->technology->join(bridge, bridge_channel)) {
+		ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology\n",
 			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
 			bridge->technology->name);
-		if (bridge->technology->leave) {
-			bridge->technology->leave(bridge, bridge_channel);
-		}
-	}
-
-	/* Remove channel from the bridge */
-	if (!bridge_channel->suspended) {
-		--bridge->num_active;
-	}
-	--bridge->num_channels;
-	AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry);
-	bridge->v_table->pull(bridge, bridge_channel);
-
-	ast_bridge_channel_clear_roles(bridge_channel);
-
-	/* If we are not going to be hung up after leaving a bridge, and we were an
-	 * outgoing channel, clear the outgoing flag.
-	 */
-	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING)
-			&& (ast_channel_softhangup_internal_flag(bridge_channel->chan) &
-				(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))) {
-		ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING);
+		bridge_channel->just_joined = 1;
+		return;
 	}
 
-	bridge_dissolve_check(bridge_channel);
-
-	bridge->reconfigured = 1;
-	ast_bridge_publish_leave(bridge, bridge_channel->chan);
+	bridge_channel->just_joined = 0;
 }
 
 /*!
  * \internal
- * \brief Push the bridge channel into its specified bridge.
+ * \brief Complete joining new channels to the bridge.
  * \since 12.0.0
  *
- * \param bridge_channel Channel to push.
+ * \param bridge Check for new channels on this bridge.
  *
- * \note On entry, bridge_channel->bridge is already locked.
+ * \note On entry, bridge is already locked.
  *
- * \retval 0 on success.
- * \retval -1 on failure.  The channel did not get pushed.
+ * \return Nothing
  */
-static int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
+static void bridge_complete_join(struct ast_bridge *bridge)
 {
-	struct ast_bridge *bridge = bridge_channel->bridge;
-	struct ast_bridge_channel *swap;
-
-	ast_assert(!bridge_channel->in_bridge);
-
-	swap = find_bridge_channel(bridge, bridge_channel->swap);
-	bridge_channel->swap = NULL;
+	struct ast_bridge_channel *bridge_channel;
 
-	if (swap) {
-		ast_debug(1, "Bridge %s: pushing %p(%s) by swapping with %p(%s)\n",
-			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
-			swap, ast_channel_name(swap->chan));
-	} else {
-		ast_debug(1, "Bridge %s: pushing %p(%s)\n",
-			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
+	if (bridge->dissolved) {
+		/*
+		 * No sense in completing the join on channels for a dissolved
+		 * bridge.  They are just going to be removed soon anyway.
+		 * However, we do have reason to abort here because the bridge
+		 * technology may not be able to handle the number of channels
+		 * still in the bridge.
+		 */
+		return;
 	}
 
-	/* Add channel to the bridge */
-	if (bridge->dissolved
-		|| bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT
-		|| (swap && swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT)
-		|| bridge->v_table->push(bridge, bridge_channel, swap)
-		|| ast_bridge_channel_establish_roles(bridge_channel)) {
-		ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n",
-			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
-		ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
-		return -1;
-	}
-	bridge_channel->in_bridge = 1;
-	bridge_channel->just_joined = 1;
-	AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry);
-	++bridge->num_channels;
-	if (!bridge_channel->suspended) {
-		++bridge->num_active;
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (!bridge_channel->just_joined) {
+			continue;
+		}
+		bridge_channel_complete_join(bridge, bridge_channel);
 	}
+}
 
-	ast_verb(3, "Channel %s %s%s%s '%s' %s-bridge <%s>\n",
-		ast_channel_name(bridge_channel->chan),
-		swap ? "swapped with " : "joined",
-		swap ? ast_channel_name(swap->chan) : "",
-		swap ? " into" : "",
-		bridge->technology->name,
-		bridge->v_table->name,
-		bridge->uniqueid);
+/*! \brief Helper function used to find the "best" bridge technology given specified capabilities */
+static struct ast_bridge_technology *find_best_technology(uint32_t capabilities, struct ast_bridge *bridge)
+{
+	struct ast_bridge_technology *current;
+	struct ast_bridge_technology *best = NULL;
 
-	ast_bridge_publish_enter(bridge, bridge_channel->chan);
-	if (swap) {
-		ast_bridge_change_state(swap, AST_BRIDGE_CHANNEL_STATE_HANGUP);
-		bridge_channel_pull(swap);
+	AST_RWLIST_RDLOCK(&bridge_technologies);
+	AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
+		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 any capabilities we want.\n",
+				current->name);
+			continue;
+		}
+		if (best && current->preference <= best->preference) {
+			ast_debug(1, "Bridge technology %s has less preference than %s (%d <= %d). Skipping.\n",
+				current->name, best->name, current->preference, best->preference);
+			continue;
+		}
+		if (current->compatible && !current->compatible(bridge)) {
+			ast_debug(1, "Bridge technology %s is not compatible with properties of existing bridge.\n",
+				current->name);
+			continue;
+		}
+		best = current;
 	}
 
-	/* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */
-	pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL);
-	pbx_builtin_setvar_helper(bridge_channel->chan, "ATTENDEDTRANSFER", NULL);
+	if (best) {
+		/* Increment it's module reference count if present so it does not get unloaded while in use */
+		ast_module_ref(best->mod);
+		ast_debug(1, "Chose bridge technology %s\n", best->name);
+	}
 
-	bridge->reconfigured = 1;
-	return 0;
+	AST_RWLIST_UNLOCK(&bridge_technologies);
+
+	return best;
 }
 
-/*! \brief Internal function to handle DTMF from a channel */
-static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
-{
-	struct ast_bridge_features *features = bridge_channel->features;
-	struct ast_bridge_hook *hook;
-	char dtmf[2];
-
-/* BUGBUG the feature hook matching needs to be done here.  Any matching feature hook needs to be queued onto the bridge_channel.  Also the feature hook digit timeout needs to be handled. */
-/* BUGBUG the AMI atxfer action just sends DTMF end events to initiate DTMF atxfer and dial the extension.  Another reason the DTMF hook matching needs rework. */
-	/* 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 */
-	dtmf[0] = frame->subclass.integer;
-	dtmf[1] = '\0';
-	hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY);
-	if (hook) {
-		struct ast_frame action = {
-			.frametype = AST_FRAME_BRIDGE_ACTION,
-			.subclass.integer = AST_BRIDGE_ACTION_FEATURE,
-		};
+struct tech_deferred_destroy {
+	struct ast_bridge_technology *tech;
+	void *tech_pvt;
+};
 
-		ast_frfree(frame);
-		frame = NULL;
-		ast_bridge_channel_queue_frame(bridge_channel, &action);
-		ao2_ref(hook, -1);
-	}
+/*!
+ * \internal
+ * \brief Deferred destruction of bridge tech private structure.
+ * \since 12.0.0
+ *
+ * \param bridge What to execute the action on.
+ * \param action Deferred bridge tech destruction.
+ *
+ * \note On entry, bridge must not be locked.
+ *
+ * \return Nothing
+ */
+static void bridge_tech_deferred_destroy(struct ast_bridge *bridge, struct ast_frame *action)
+{
+	struct tech_deferred_destroy *deferred = action->data.ptr;
+	struct ast_bridge dummy_bridge = {
+		.technology = deferred->tech,
+		.tech_pvt = deferred->tech_pvt,
+		};
 
-	return frame;
+	ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid));
+	ast_debug(1, "Bridge %s: calling %s technology destructor (deferred, dummy)\n",
+		dummy_bridge.uniqueid, dummy_bridge.technology->name);
+	dummy_bridge.technology->destroy(&dummy_bridge);
+	ast_module_unref(dummy_bridge.technology->mod);
 }
 
 /*!
  * \internal
- * \brief Handle bridge hangup event.
+ * \brief Handle bridge action frame.
  * \since 12.0.0
  *
- * \param bridge_channel Which channel is hanging up.
+ * \param bridge What to execute the action on.
+ * \param action What to do.
+ *
+ * \note On entry, bridge is already locked.
+ * \note Can be called by the bridge destructor.
  *
  * \return Nothing
  */
-static void bridge_handle_hangup(struct ast_bridge_channel *bridge_channel)
+static void bridge_action_bridge(struct ast_bridge *bridge, struct ast_frame *action)
 {
-	struct ast_bridge_features *features = bridge_channel->features;
-	struct ast_bridge_hook *hook;
-	struct ao2_iterator iter;
-
-	/* Run any hangup hooks. */
-	iter = ao2_iterator_init(features->hangup_hooks, 0);
-	for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
-		int remove_me;
+#if 0	/* In case we need to know when the destructor is calling us. */
+	int in_destructor = !ao2_ref(bridge, 0);
+#endif
 
-		remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
-		if (remove_me) {
-			ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n",
-				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
-			ao2_unlink(features->hangup_hooks, hook);
-		}
+	switch (action->subclass.integer) {
+	case AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY:
+		ast_bridge_unlock(bridge);
+		bridge_tech_deferred_destroy(bridge, action);
+		ast_bridge_lock(bridge);
+		break;
+	case AST_BRIDGE_ACTION_DEFERRED_DISSOLVING:
+		ast_bridge_unlock(bridge);
+		bridge->v_table->dissolving(bridge);
+		ast_bridge_lock(bridge);
+		break;
+	default:
+		/* Unexpected deferred action type.  Should never happen. */
+		ast_assert(0);
+		break;
 	}
-	ao2_iterator_destroy(&iter);
-
-	/* Default hangup action. */
-	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
 }
 
-static int bridge_channel_interval_ready(struct ast_bridge_channel *bridge_channel)
+/*!
+ * \internal
+ * \brief Do any pending bridge actions.
+ * \since 12.0.0
+ *
+ * \param bridge What to do actions on.
+ *
+ * \note On entry, bridge is already locked.
+ * \note Can be called by the bridge destructor.
+ *
+ * \return Nothing
+ */
+static void bridge_handle_actions(struct ast_bridge *bridge)
 {
-	struct ast_bridge_features *features = bridge_channel->features;
-	struct ast_bridge_hook *hook;
-	int ready;
-
-	ast_heap_wrlock(features->interval_hooks);
-	hook = ast_heap_peek(features->interval_hooks, 1);
-	ready = hook && ast_tvdiff_ms(hook->parms.timer.trip_time, ast_tvnow()) <= 0;
-	ast_heap_unlock(features->interval_hooks);
+	struct ast_frame *action;
 
-	return ready;
+	while ((action = AST_LIST_REMOVE_HEAD(&bridge->action_queue, frame_list))) {
+		switch (action->frametype) {
+		case AST_FRAME_BRIDGE_ACTION:
+			bridge_action_bridge(bridge, action);
+			break;
+		default:
+			/* Unexpected deferred frame type.  Should never happen. */
+			ast_assert(0);
+			break;
+		}
+		ast_frfree(action);
+	}
 }
 
-int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking)
+static struct stasis_message *create_bridge_snapshot_message(struct ast_bridge *bridge)
 {
-	struct ast_frame action = {
-		.frametype = AST_FRAME_BRIDGE_ACTION,
-		.subclass.integer = started_talking
-			? AST_BRIDGE_ACTION_TALKING_START : AST_BRIDGE_ACTION_TALKING_STOP,
-	};
+	RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
 
-	return ast_bridge_channel_queue_frame(bridge_channel, &action);
+	snapshot = ast_bridge_snapshot_create(bridge);
+	if (!snapshot) {
+		return NULL;
+	}
+
+	return stasis_message_create(ast_bridge_snapshot_type(), snapshot);
 }
 
-static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+static void destroy_bridge(void *obj)
 {
-	ast_bridge_channel_lock_bridge(bridge_channel);
-/*
- * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked).
- *
- * The tech decides if a frame needs to be pushed back for deferral.
- * simple_bridge/native_bridge are likely the only techs that will do this.
- */
-	bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame);
-	ast_bridge_unlock(bridge_channel->bridge);
+	struct ast_bridge *bridge = obj;
 
-	/*
-	 * Claim successful write to bridge.  If deferred frame
-	 * support is added, claim successfully deferred.
-	 */
-	return 0;
-}
+	ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n",
+		bridge->uniqueid, bridge->v_table->name);
 
-int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen)
-{
-	struct ast_frame frame = {
-		.frametype = AST_FRAME_BRIDGE_ACTION,
-		.subclass.integer = action,
-		.datalen = datalen,
-		.data.ptr = (void *) data,
-	};
+	if (bridge->construction_completed) {
+		RAII_VAR(struct stasis_message *, clear_msg, NULL, ao2_cleanup);
 
-	return bridge_channel_write_frame(bridge_channel, &frame);
-}
+		clear_msg = create_bridge_snapshot_message(bridge);
+		if (clear_msg) {
+			RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
 
-int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
-{
-	struct ast_frame frame = {
-		.frametype = AST_FRAME_CONTROL,
-		.subclass.integer = control,
-		.datalen = datalen,
-		.data.ptr = (void *) data,
-	};
+			msg = stasis_cache_clear_create(clear_msg);
+			if (msg) {
+				stasis_publish(ast_bridge_topic(bridge), msg);
+			}
+		}
+	}
 
-	return bridge_channel_write_frame(bridge_channel, &frame);
-}
+	/* Do any pending actions in the context of destruction. */
+	ast_bridge_lock(bridge);
+	bridge_handle_actions(bridge);
+	ast_bridge_unlock(bridge);
 
-int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class)
-{
-	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
-	size_t datalen;
+	/* There should not be any channels left in the bridge. */
+	ast_assert(AST_LIST_EMPTY(&bridge->channels));
 
-	if (!ast_strlen_zero(moh_class)) {
-		datalen = strlen(moh_class) + 1;
+	ast_debug(1, "Bridge %s: calling %s bridge destructor\n",
+		bridge->uniqueid, bridge->v_table->name);
+	bridge->v_table->destroy(bridge);
 
-		blob = ast_json_pack("{s: s}",
-			"musicclass", moh_class);
-	} else {
-		moh_class = NULL;
-		datalen = 0;
+	/* Pass off the bridge to the technology to destroy if needed */
+	if (bridge->technology) {
+		ast_debug(1, "Bridge %s: calling %s technology stop\n",
+			bridge->uniqueid, bridge->technology->name);
+		if (bridge->technology->stop) {
+			ast_bridge_lock(bridge);
+			bridge->technology->stop(bridge);
+			ast_bridge_unlock(bridge);
+		}
+		ast_debug(1, "Bridge %s: calling %s technology destructor\n",
+			bridge->uniqueid, bridge->technology->name);
+		if (bridge->technology->destroy) {
+			bridge->technology->destroy(bridge);
+		}
+		ast_module_unref(bridge->technology->mod);
+		bridge->technology = NULL;
 	}
 
-	ast_channel_publish_blob(bridge_channel->chan, ast_channel_hold_type(), blob);
-	return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
-		moh_class, datalen);
+	if (bridge->callid) {
+		bridge->callid = ast_callid_unref(bridge->callid);
+	}
+
+	cleanup_video_mode(bridge);
 }
 
-int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel)
+struct ast_bridge *bridge_register(struct ast_bridge *bridge)
 {
-	ast_channel_publish_blob(bridge_channel->chan, ast_channel_unhold_type(), NULL);
-	return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0);
+	if (bridge) {
+		bridge->construction_completed = 1;
+		ast_bridge_publish_state(bridge);
+		if (!ao2_link(bridges, bridge)) {
+			ast_bridge_destroy(bridge);
+			bridge = NULL;
+		}
+	}
+	return bridge;
 }
 
-static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args)
+struct ast_bridge *bridge_alloc(size_t size, const struct ast_bridge_methods *v_table)
 {
-	int res = 0;
+	struct ast_bridge *bridge;
 
-	if (!strcasecmp("Gosub", app_name)) {
-		ast_app_exec_sub(NULL, chan, app_args, 0);
-	} else if (!strcasecmp("Macro", app_name)) {
-		ast_app_exec_macro(NULL, chan, app_args);
-	} else {
-		struct ast_app *app;
+	/* Check v_table that all methods are present. */
+	if (!v_table
+		|| !v_table->name
+		|| !v_table->destroy
+		|| !v_table->dissolving
+		|| !v_table->push
+		|| !v_table->pull
+		|| !v_table->notify_masquerade
+		|| !v_table->get_merge_priority) {
+		ast_log(LOG_ERROR, "Virtual method table for bridge class %s not complete.\n",
+			v_table && v_table->name ? v_table->name : "<unknown>");
+		ast_assert(0);
+		return NULL;
+	}
 
-		app = pbx_findapp(app_name);
-		if (!app) {
-			ast_log(LOG_WARNING, "Could not find application (%s)\n", app_name);
-		} else {
-			res = pbx_exec(chan, app, app_args);
-		}
+	bridge = ao2_alloc(size, destroy_bridge);
+	if (bridge) {
+		bridge->v_table = v_table;
 	}
-	return res;
+	return bridge;
 }
 
-void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags)
 {
-	if (moh_class) {
-		ast_bridge_channel_write_hold(bridge_channel, moh_class);
+	if (!self) {
+		return NULL;
 	}
-	if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) {
-		/* Break the bridge if the app returns non-zero. */
-		bridge_handle_hangup(bridge_channel);
+
+	ast_uuid_generate_str(self->uniqueid, sizeof(self->uniqueid));
+	ast_set_flag(&self->feature_flags, flags);
+	self->allowed_capabilities = capabilities;
+
+	/* Use our helper function to find the "best" bridge technology. */
+	self->technology = find_best_technology(capabilities, self);
+	if (!self->technology) {
+		ast_log(LOG_WARNING, "Bridge %s: Could not create class %s.  No technology to support it.\n",
+			self->uniqueid, self->v_table->name);
+		ao2_ref(self, -1);
+		return NULL;
 	}
-	if (moh_class) {
-		ast_bridge_channel_write_unhold(bridge_channel);
+
+	/* Pass off the bridge to the technology to manipulate if needed */
+	ast_debug(1, "Bridge %s: calling %s technology constructor\n",
+		self->uniqueid, self->technology->name);
+	if (self->technology->create && self->technology->create(self)) {
+		ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n",
+			self->uniqueid, self->technology->name);
+		ao2_ref(self, -1);
+		return NULL;
+	}
+	ast_debug(1, "Bridge %s: calling %s technology start\n",
+		self->uniqueid, self->technology->name);
+	if (self->technology->start && self->technology->start(self)) {
+		ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n",
+			self->uniqueid, self->technology->name);
+		ao2_ref(self, -1);
+		return NULL;
 	}
-}
 
-struct bridge_run_app {
-	/*! Offset into app_name[] where the MOH class name starts.  (zero if no MOH) */
-	int moh_offset;
-	/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
-	int app_args_offset;
-	/*! Application name to run. */
-	char app_name[0];
-};
+	if (!ast_bridge_topic(self)) {
+		ao2_ref(self, -1);
+		return NULL;
+	}
+
+	return self;
+}
 
 /*!
  * \internal
- * \brief Handle the run application bridge action.
+ * \brief ast_bridge base class destructor.
  * \since 12.0.0
  *
- * \param bridge_channel Which channel to run the application on.
- * \param data Action frame data to run the application.
+ * \param self Bridge to operate upon.
+ *
+ * \note Stub because of nothing to do.
  *
  * \return Nothing
  */
-static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, struct bridge_run_app *data)
+static void bridge_base_destroy(struct ast_bridge *self)
 {
-	ast_bridge_channel_run_app(bridge_channel, data->app_name,
-		data->app_args_offset ? &data->app_name[data->app_args_offset] : NULL,
-		data->moh_offset ? &data->app_name[data->moh_offset] : NULL);
 }
 
-static int payload_helper_app(ast_bridge_channel_post_action_data post_it,
-	struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+/*!
+ * \internal
+ * \brief The bridge is being dissolved.
+ * \since 12.0.0
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \return Nothing
+ */
+static void bridge_base_dissolving(struct ast_bridge *self)
 {
-	struct bridge_run_app *app_data;
-	size_t len_name = strlen(app_name) + 1;
-	size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1;
-	size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1;
-	size_t len_data = sizeof(*app_data) + len_name + len_args + len_moh;
+	ao2_unlink(bridges, self);
+}
 
-	/* Fill in application run frame data. */
-	app_data = alloca(len_data);
-	app_data->app_args_offset = len_args ? len_name : 0;
-	app_data->moh_offset = len_moh ? len_name + len_args : 0;
-	strcpy(app_data->app_name, app_name);/* Safe */
-	if (len_args) {
-		strcpy(&app_data->app_name[app_data->app_args_offset], app_args);/* Safe */
-	}
-	if (moh_class) {
-		strcpy(&app_data->app_name[app_data->moh_offset], moh_class);/* Safe */
-	}
-
-	return post_it(bridge_channel, AST_BRIDGE_ACTION_RUN_APP, app_data, len_data);
-}
-
-int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
-{
-	return payload_helper_app(ast_bridge_channel_write_action_data,
-		bridge_channel, app_name, app_args, moh_class);
-}
-
-int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
-{
-	return payload_helper_app(ast_bridge_channel_queue_action_data,
-		bridge_channel, app_name, app_args, moh_class);
-}
-
-void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
+/*!
+ * \internal
+ * \brief ast_bridge base push method.
+ * \since 12.0.0
+ *
+ * \param self Bridge to operate upon.
+ * \param bridge_channel Bridge channel to push.
+ * \param swap Bridge channel to swap places with if not NULL.
+ *
+ * \note On entry, self is already locked.
+ * \note Stub because of nothing to do.
+ *
+ * \retval 0 on success
+ * \retval -1 on failure
+ */
+static int bridge_base_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
 {
-	if (moh_class) {
-		ast_bridge_channel_write_hold(bridge_channel, moh_class);
-	}
-	if (custom_play) {
-		custom_play(bridge_channel, playfile);
-	} else {
-		ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
-	}
-	if (moh_class) {
-		ast_bridge_channel_write_unhold(bridge_channel);
-	}
-
-	/*
-	 * It may be necessary to resume music on hold after we finish
-	 * playing the announcment.
-	 *
-	 * XXX We have no idea what MOH class was in use before playing
-	 * the file. This method also fails to restore ringing indications.
-	 * the proposed solution is to create a resume_entertainment callback
-	 * for the bridge technology and execute it here.
-	 */
-	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
-		ast_moh_start(bridge_channel->chan, NULL, NULL);
-	}
+	return 0;
 }
 
-struct bridge_playfile {
-	/*! Call this function to play the playfile. (NULL if normal sound file to play) */
-	ast_bridge_custom_play_fn custom_play;
-	/*! Offset into playfile[] where the MOH class name starts.  (zero if no MOH)*/
-	int moh_offset;
-	/*! Filename to play. */
-	char playfile[0];
-};
-
 /*!
  * \internal
- * \brief Handle the playfile bridge action.
+ * \brief ast_bridge base pull method.
  * \since 12.0.0
  *
- * \param bridge_channel Which channel to play a file on.
- * \param payload Action frame payload to play a file.
+ * \param self Bridge to operate upon.
+ * \param bridge_channel Bridge channel to pull.
+ *
+ * \note On entry, self is already locked.
  *
  * \return Nothing
  */
-static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, struct bridge_playfile *payload)
-{
-	ast_bridge_channel_playfile(bridge_channel, payload->custom_play, payload->playfile,
-		payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL);
-}
-
-static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it,
-	struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
-{
-	struct bridge_playfile *payload;
-	size_t len_name = strlen(playfile) + 1;
-	size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1;
-	size_t len_payload = sizeof(*payload) + len_name + len_moh;
-
-	/* Fill in play file frame data. */
-	payload = alloca(len_payload);
-	payload->custom_play = custom_play;
-	payload->moh_offset = len_moh ? len_name : 0;
-	strcpy(payload->playfile, playfile);/* Safe */
-	if (moh_class) {
-		strcpy(&payload->playfile[payload->moh_offset], moh_class);/* Safe */
-	}
-
-	return post_it(bridge_channel, AST_BRIDGE_ACTION_PLAY_FILE, payload, len_payload);
-}
-
-int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
-{
-	return payload_helper_playfile(ast_bridge_channel_write_action_data,
-		bridge_channel, custom_play, playfile, moh_class);
-}
-
-int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
+static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
 {
-	return payload_helper_playfile(ast_bridge_channel_queue_action_data,
-		bridge_channel, custom_play, playfile, moh_class);
+	ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
 }
 
-struct bridge_custom_callback {
-	/*! Call this function on the bridge channel thread. */
-	ast_bridge_custom_callback_fn callback;
-	/*! Size of the payload if it exists.  A number otherwise. */
-	size_t payload_size;
-	/*! Nonzero if the payload exists. */
-	char payload_exists;
-	/*! Payload to give to callback. */
-	char payload[0];
-};
-
 /*!
  * \internal
- * \brief Handle the do custom callback bridge action.
+ * \brief ast_bridge base notify_masquerade method.
  * \since 12.0.0
  *
- * \param bridge_channel Which channel to run the application on.
- * \param data Action frame data to run the application.
+ * \param self Bridge to operate upon.
+ * \param bridge_channel Bridge channel that was masqueraded.
+ *
+ * \note On entry, self is already locked.
  *
  * \return Nothing
  */
-static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data)
-{
-	data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size);
-}
-
-static int payload_helper_cb(ast_bridge_channel_post_action_data post_it,
-	struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
-{
-	struct bridge_custom_callback *cb_data;
-	size_t len_data = sizeof(*cb_data) + (payload ? payload_size : 0);
-
-	/* Sanity check. */
-	if (!callback) {
-		ast_assert(0);
-		return -1;
-	}
-
-	/* Fill in custom callback frame data. */
-	cb_data = alloca(len_data);
-	cb_data->callback = callback;
-	cb_data->payload_size = payload_size;
-	cb_data->payload_exists = payload && payload_size;
-	if (cb_data->payload_exists) {
-		memcpy(cb_data->payload, payload, payload_size);/* Safe */
-	}
-
-	return post_it(bridge_channel, AST_BRIDGE_ACTION_CALLBACK, cb_data, len_data);
-}
-
-int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+static void bridge_base_notify_masquerade(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
 {
-	return payload_helper_cb(ast_bridge_channel_write_action_data,
-		bridge_channel, callback, payload, payload_size);
+	self->reconfigured = 1;
 }
 
-int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+/*!
+ * \internal
+ * \brief Get the merge priority of this bridge.
+ * \since 12.0.0
+ *
+ * \param self Bridge to operate upon.
+ *
+ * \note On entry, self is already locked.
+ *
+ * \return Merge priority
+ */
+static int bridge_base_get_merge_priority(struct ast_bridge *self)
 {
-	return payload_helper_cb(ast_bridge_channel_queue_action_data,
-		bridge_channel, callback, payload, payload_size);
+	return 0;
 }
 
-struct bridge_park {
-	int parker_uuid_offset;
-	int app_data_offset;
-	/* buffer used for holding those strings */
-	char parkee_uuid[0];
+struct ast_bridge_methods ast_bridge_base_v_table = {
+	.name = "base",
+	.destroy = bridge_base_destroy,
+	.dissolving = bridge_base_dissolving,
+	.push = bridge_base_push,
+	.pull = bridge_base_pull,
+	.notify_masquerade = bridge_base_notify_masquerade,
+	.get_merge_priority = bridge_base_get_merge_priority,
 };
 
-static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
+struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags)
 {
-	ast_bridge_channel_park(bridge_channel, payload->parkee_uuid,
-		&payload->parkee_uuid[payload->parker_uuid_offset],
-		payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL);
+	void *bridge;
+
+	bridge = bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table);
+	bridge = bridge_base_init(bridge, capabilities, flags);
+	bridge = bridge_register(bridge);
+	return bridge;
 }
 
-static int payload_helper_park(ast_bridge_channel_post_action_data post_it,
-	struct ast_bridge_channel *bridge_channel,
-	const char *parkee_uuid,
-	const char *parker_uuid,
-	const char *app_data)
+int ast_bridge_destroy(struct ast_bridge *bridge)
 {
-	struct bridge_park *payload;
-	size_t len_parkee_uuid = strlen(parkee_uuid) + 1;
-	size_t len_parker_uuid = strlen(parker_uuid) + 1;
-	size_t len_app_data = !app_data ? 0 : strlen(app_data) + 1;
-	size_t len_payload = sizeof(*payload) + len_parker_uuid + len_parkee_uuid + len_app_data;
+	ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid);
+	ast_bridge_lock(bridge);
+	bridge_dissolve(bridge);
+	ast_bridge_unlock(bridge);
 
-	payload = alloca(len_payload);
-	payload->app_data_offset = len_app_data ? len_parkee_uuid + len_parker_uuid : 0;
-	payload->parker_uuid_offset = len_parkee_uuid;
-	strcpy(payload->parkee_uuid, parkee_uuid);
-	strcpy(&payload->parkee_uuid[payload->parker_uuid_offset], parker_uuid);
-	if (app_data) {
-		strcpy(&payload->parkee_uuid[payload->app_data_offset], app_data);
-	}
+	ao2_ref(bridge, -1);
 
-	return post_it(bridge_channel, AST_BRIDGE_ACTION_PARK, payload, len_payload);
+	return 0;
 }
 
-int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
+static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
 {
-	return payload_helper_park(ast_bridge_channel_write_action_data,
-		bridge_channel, parkee_uuid, parker_uuid, app_data);
-}
+	struct ast_format read_format;
+	struct ast_format write_format;
+	struct ast_format best_format;
+	char codec_buf[512];
 
-/*!
- * \internal
- * \brief Feed notification that a frame is waiting on a channel into the bridging core
- *
- * \param bridge_channel Bridge channel the notification was received on
- */
-static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_frame *frame;
+	ast_format_copy(&read_format, ast_channel_readformat(bridge_channel->chan));
+	ast_format_copy(&write_format, ast_channel_writeformat(bridge_channel->chan));
 
-	if (bridge_channel->features->mute) {
-		frame = ast_read_noaudio(bridge_channel->chan);
-	} else {
-		frame = ast_read(bridge_channel->chan);
-	}
+	/* Are the formats currently in use something this bridge can handle? */
+	if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, ast_channel_readformat(bridge_channel->chan))) {
+		ast_best_codec(bridge->technology->format_capabilities, &best_format);
 
-	if (!frame) {
-		bridge_handle_hangup(bridge_channel);
-		return;
-	}
-	switch (frame->frametype) {
-	case AST_FRAME_CONTROL:
-		switch (frame->subclass.integer) {
-		case AST_CONTROL_HANGUP:
-			bridge_handle_hangup(bridge_channel);
-			ast_frfree(frame);
-			return;
-/* BUGBUG This is where incoming HOLD/UNHOLD memory should register.  Write UNHOLD into bridge when this channel is pulled. */
-		default:
-			break;
-		}
-		break;
-	case AST_FRAME_DTMF_BEGIN:
-		frame = bridge_handle_dtmf(bridge_channel, frame);
-		if (!frame) {
-			return;
-		}
-		/* Fall through */
-	case AST_FRAME_DTMF_END:
-		if (!bridge_channel->features->dtmf_passthrough) {
-			ast_frfree(frame);
-			return;
+		/* Read format is a no go... */
+		ast_debug(1, "Bridge technology %s wants to read any of formats %s but channel has %s\n",
+			bridge->technology->name,
+			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities),
+			ast_getformatname(&read_format));
+
+		/* 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\n",
+				ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format));
+			return -1;
 		}
-/* BUGBUG This is where incoming DTMF begin/end memory should register.  Write DTMF end into bridge when this channel is pulled. */
-		break;
-	default:
-		break;
+		ast_debug(1, "Bridge %s put channel %s into read format %s\n",
+			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&best_format));
+	} else {
+		ast_debug(1, "Bridge %s is happy that channel %s already has read format %s\n",
+			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&read_format));
 	}
 
-	/* Simply write the frame out to the bridge technology. */
-/* BUGBUG The tech is where AST_CONTROL_ANSWER hook should go. (early bridge) */
-/* BUGBUG The tech is where incoming BUSY/CONGESTION hangup should happen? (early bridge) */
-	bridge_channel_write_frame(bridge_channel, frame);
-	ast_frfree(frame);
-}
+	if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, &write_format)) {
+		ast_best_codec(bridge->technology->format_capabilities, &best_format);
 
-/*!
- * \internal
- * \brief Complete joining a channel to the bridge.
- * \since 12.0.0
- *
- * \param bridge What to operate upon.
- * \param bridge_channel What is joining the bridge technology.
- *
- * \note On entry, bridge is already locked.
- *
- * \return Nothing
- */
-static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
-{
-	/* Make the channel compatible with the bridge */
-	bridge_make_compatible(bridge, bridge_channel);
+		/* Write format is a no go... */
+		ast_debug(1, "Bridge technology %s wants to write any of formats %s but channel has %s\n",
+			bridge->technology->name,
+			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities),
+			ast_getformatname(&write_format));
 
-	/* Tell the bridge technology we are joining so they set us up */
-	ast_debug(1, "Bridge %s: %p(%s) is joining %s technology\n",
-		bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
-		bridge->technology->name);
-	if (bridge->technology->join
-		&& bridge->technology->join(bridge, bridge_channel)) {
-		ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology\n",
-			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
-			bridge->technology->name);
-		bridge_channel->just_joined = 1;
-		return;
+		/* 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\n",
+				ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format));
+			return -1;
+		}
+		ast_debug(1, "Bridge %s put channel %s into write format %s\n",
+			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&best_format));
+	} else {
+		ast_debug(1, "Bridge %s is happy that channel %s already has write format %s\n",
+			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&write_format));
 	}
 
-	bridge_channel->just_joined = 0;
+	return 0;
 }
 
 /*!
  * \internal
- * \brief Complete joining new channels to the bridge.
+ * \brief Perform the smart bridge operation.
  * \since 12.0.0
  *
- * \param bridge Check for new channels on this bridge.
+ * \param bridge Work on this bridge.
+ *
+ * \details
+ * Basically see if a new bridge technology should be used instead
+ * of the current one.
  *
  * \note On entry, bridge is already locked.
  *
- * \return Nothing
+ * \retval 0 on success.
+ * \retval -1 on error.
  */
-static void bridge_complete_join(struct ast_bridge *bridge)
+static int smart_bridge_operation(struct ast_bridge *bridge)
 {
+	uint32_t new_capabilities;
+	struct ast_bridge_technology *new_technology;
+	struct ast_bridge_technology *old_technology = bridge->technology;
 	struct ast_bridge_channel *bridge_channel;
+	struct ast_frame *deferred_action;
+	struct ast_bridge dummy_bridge = {
+		.technology = bridge->technology,
+		.tech_pvt = bridge->tech_pvt,
+	};
 
 	if (bridge->dissolved) {
-		/*
-		 * No sense in completing the join on channels for a dissolved
-		 * bridge.  They are just going to be removed soon anyway.
-		 * However, we do have reason to abort here because the bridge
-		 * technology may not be able to handle the number of channels
-		 * still in the bridge.
-		 */
-		return;
-	}
-
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		if (!bridge_channel->just_joined) {
-			continue;
-		}
-		bridge_channel_complete_join(bridge, bridge_channel);
+		ast_debug(1, "Bridge %s is dissolved, not performing smart bridge operation.\n",
+			bridge->uniqueid);
+		return 0;
 	}
-}
 
-/*! \brief Helper function used to find the "best" bridge technology given specified capabilities */
-static struct ast_bridge_technology *find_best_technology(uint32_t capabilities, struct ast_bridge *bridge)
-{
-	struct ast_bridge_technology *current;
-	struct ast_bridge_technology *best = NULL;
-
-	AST_RWLIST_RDLOCK(&bridge_technologies);
-	AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
-		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 any capabilities we want.\n",
-				current->name);
-			continue;
-		}
-		if (best && current->preference <= best->preference) {
-			ast_debug(1, "Bridge technology %s has less preference than %s (%d <= %d). Skipping.\n",
-				current->name, best->name, current->preference, best->preference);
-			continue;
-		}
-		if (current->compatible && !current->compatible(bridge)) {
-			ast_debug(1, "Bridge technology %s is not compatible with properties of existing bridge.\n",
-				current->name);
-			continue;
-		}
-		best = current;
-	}
-
-	if (best) {
-		/* Increment it's module reference count if present so it does not get unloaded while in use */
-		ast_module_ref(best->mod);
-		ast_debug(1, "Chose bridge technology %s\n", best->name);
-	}
-
-	AST_RWLIST_UNLOCK(&bridge_technologies);
-
-	return best;
-}
-
-struct tech_deferred_destroy {
-	struct ast_bridge_technology *tech;
-	void *tech_pvt;
-};
-
-/*!
- * \internal
- * \brief Deferred destruction of bridge tech private structure.
- * \since 12.0.0
- *
- * \param bridge What to execute the action on.
- * \param action Deferred bridge tech destruction.
- *
- * \note On entry, bridge must not be locked.
- *
- * \return Nothing
- */
-static void bridge_tech_deferred_destroy(struct ast_bridge *bridge, struct ast_frame *action)
-{
-	struct tech_deferred_destroy *deferred = action->data.ptr;
-	struct ast_bridge dummy_bridge = {
-		.technology = deferred->tech,
-		.tech_pvt = deferred->tech_pvt,
-		};
-
-	ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid));
-	ast_debug(1, "Bridge %s: calling %s technology destructor (deferred, dummy)\n",
-		dummy_bridge.uniqueid, dummy_bridge.technology->name);
-	dummy_bridge.technology->destroy(&dummy_bridge);
-	ast_module_unref(dummy_bridge.technology->mod);
-}
-
-/*!
- * \internal
- * \brief Handle bridge action frame.
- * \since 12.0.0
- *
- * \param bridge What to execute the action on.
- * \param action What to do.
- *
- * \note On entry, bridge is already locked.
- * \note Can be called by the bridge destructor.
- *
- * \return Nothing
- */
-static void bridge_action_bridge(struct ast_bridge *bridge, struct ast_frame *action)
-{
-#if 0	/* In case we need to know when the destructor is calling us. */
-	int in_destructor = !ao2_ref(bridge, 0);
-#endif
-
-	switch (action->subclass.integer) {
-	case AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY:
-		ast_bridge_unlock(bridge);
-		bridge_tech_deferred_destroy(bridge, action);
-		ast_bridge_lock(bridge);
-		break;
-	case AST_BRIDGE_ACTION_DEFERRED_DISSOLVING:
-		ast_bridge_unlock(bridge);
-		bridge->v_table->dissolving(bridge);
-		ast_bridge_lock(bridge);
-		break;
-	default:
-		/* Unexpected deferred action type.  Should never happen. */
-		ast_assert(0);
-		break;
-	}
-}
-
-/*!
- * \internal
- * \brief Do any pending bridge actions.
- * \since 12.0.0
- *
- * \param bridge What to do actions on.
- *
- * \note On entry, bridge is already locked.
- * \note Can be called by the bridge destructor.
- *
- * \return Nothing
- */
-static void bridge_handle_actions(struct ast_bridge *bridge)
-{
-	struct ast_frame *action;
-
-	while ((action = AST_LIST_REMOVE_HEAD(&bridge->action_queue, frame_list))) {
-		switch (action->frametype) {
-		case AST_FRAME_BRIDGE_ACTION:
-			bridge_action_bridge(bridge, action);
-			break;
-		default:
-			/* Unexpected deferred frame type.  Should never happen. */
-			ast_assert(0);
-			break;
-		}
-		ast_frfree(action);
-	}
-}
-
-static struct stasis_message *create_bridge_snapshot_message(struct ast_bridge *bridge)
-{
-	RAII_VAR(struct ast_bridge_snapshot *, snapshot, NULL, ao2_cleanup);
-
-	snapshot = ast_bridge_snapshot_create(bridge);
-	if (!snapshot) {
-		return NULL;
-	}
-
-	return stasis_message_create(ast_bridge_snapshot_type(), snapshot);
-}
-
-static void destroy_bridge(void *obj)
-{
-	struct ast_bridge *bridge = obj;
-
-	ast_debug(1, "Bridge %s: actually destroying %s bridge, nobody wants it anymore\n",
-		bridge->uniqueid, bridge->v_table->name);
-
-	if (bridge->construction_completed) {
-		RAII_VAR(struct stasis_message *, clear_msg, NULL, ao2_cleanup);
-
-		clear_msg = create_bridge_snapshot_message(bridge);
-		if (clear_msg) {
-			RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
-
-			msg = stasis_cache_clear_create(clear_msg);
-			if (msg) {
-				stasis_publish(ast_bridge_topic(bridge), msg);
-			}
-		}
-	}
-
-	/* Do any pending actions in the context of destruction. */
-	ast_bridge_lock(bridge);
-	bridge_handle_actions(bridge);
-	ast_bridge_unlock(bridge);
-
-	/* There should not be any channels left in the bridge. */
-	ast_assert(AST_LIST_EMPTY(&bridge->channels));
-
-	ast_debug(1, "Bridge %s: calling %s bridge destructor\n",
-		bridge->uniqueid, bridge->v_table->name);
-	bridge->v_table->destroy(bridge);
-
-	/* Pass off the bridge to the technology to destroy if needed */
-	if (bridge->technology) {
-		ast_debug(1, "Bridge %s: calling %s technology stop\n",
-			bridge->uniqueid, bridge->technology->name);
-		if (bridge->technology->stop) {
-			ast_bridge_lock(bridge);
-			bridge->technology->stop(bridge);
-			ast_bridge_unlock(bridge);
-		}
-		ast_debug(1, "Bridge %s: calling %s technology destructor\n",
-			bridge->uniqueid, bridge->technology->name);
-		if (bridge->technology->destroy) {
-			bridge->technology->destroy(bridge);
-		}
-		ast_module_unref(bridge->technology->mod);
-		bridge->technology = NULL;
-	}
-
-	if (bridge->callid) {
-		bridge->callid = ast_callid_unref(bridge->callid);
-	}
-
-	cleanup_video_mode(bridge);
-}
-
-struct ast_bridge *ast_bridge_register(struct ast_bridge *bridge)
-{
-	if (bridge) {
-		bridge->construction_completed = 1;
-		ast_bridge_publish_state(bridge);
-		if (!ao2_link(bridges, bridge)) {
-			ast_bridge_destroy(bridge);
-			bridge = NULL;
-		}
-	}
-	return bridge;
-}
-
-struct ast_bridge *ast_bridge_alloc(size_t size, const struct ast_bridge_methods *v_table)
-{
-	struct ast_bridge *bridge;
-
-	/* Check v_table that all methods are present. */
-	if (!v_table
-		|| !v_table->name
-		|| !v_table->destroy
-		|| !v_table->dissolving
-		|| !v_table->push
-		|| !v_table->pull
-		|| !v_table->notify_masquerade
-		|| !v_table->get_merge_priority) {
-		ast_log(LOG_ERROR, "Virtual method table for bridge class %s not complete.\n",
-			v_table && v_table->name ? v_table->name : "<unknown>");
-		ast_assert(0);
-		return NULL;
-	}
-
-	bridge = ao2_alloc(size, destroy_bridge);
-	if (bridge) {
-		bridge->v_table = v_table;
-	}
-	return bridge;
-}
-
-struct ast_bridge *ast_bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags)
-{
-	if (!self) {
-		return NULL;
-	}
-
-	ast_uuid_generate_str(self->uniqueid, sizeof(self->uniqueid));
-	ast_set_flag(&self->feature_flags, flags);
-	self->allowed_capabilities = capabilities;
-
-	/* Use our helper function to find the "best" bridge technology. */
-	self->technology = find_best_technology(capabilities, self);
-	if (!self->technology) {
-		ast_log(LOG_WARNING, "Bridge %s: Could not create class %s.  No technology to support it.\n",
-			self->uniqueid, self->v_table->name);
-		ao2_ref(self, -1);
-		return NULL;
-	}
-
-	/* Pass off the bridge to the technology to manipulate if needed */
-	ast_debug(1, "Bridge %s: calling %s technology constructor\n",
-		self->uniqueid, self->technology->name);
-	if (self->technology->create && self->technology->create(self)) {
-		ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n",
-			self->uniqueid, self->technology->name);
-		ao2_ref(self, -1);
-		return NULL;
-	}
-	ast_debug(1, "Bridge %s: calling %s technology start\n",
-		self->uniqueid, self->technology->name);
-	if (self->technology->start && self->technology->start(self)) {
-		ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n",
-			self->uniqueid, self->technology->name);
-		ao2_ref(self, -1);
-		return NULL;
-	}
-
-	if (!ast_bridge_topic(self)) {
-		ao2_ref(self, -1);
-		return NULL;
-	}
-
-	return self;
-}
-
-/*!
- * \internal
- * \brief ast_bridge base class destructor.
- * \since 12.0.0
- *
- * \param self Bridge to operate upon.
- *
- * \note Stub because of nothing to do.
- *
- * \return Nothing
- */
-static void bridge_base_destroy(struct ast_bridge *self)
-{
-}
-
-/*!
- * \internal
- * \brief The bridge is being dissolved.
- * \since 12.0.0
- *
- * \param self Bridge to operate upon.
- *
- * \return Nothing
- */
-static void bridge_base_dissolving(struct ast_bridge *self)
-{
-	ao2_unlink(bridges, self);
-}
-
-/*!
- * \internal
- * \brief ast_bridge base push method.
- * \since 12.0.0
- *
- * \param self Bridge to operate upon.
- * \param bridge_channel Bridge channel to push.
- * \param swap Bridge channel to swap places with if not NULL.
- *
- * \note On entry, self is already locked.
- * \note Stub because of nothing to do.
- *
- * \retval 0 on success
- * \retval -1 on failure
- */
-static int bridge_base_push(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
-{
-	return 0;
-}
-
-/*!
- * \internal
- * \brief ast_bridge base pull method.
- * \since 12.0.0
- *
- * \param self Bridge to operate upon.
- * \param bridge_channel Bridge channel to pull.
- *
- * \note On entry, self is already locked.
- *
- * \return Nothing
- */
-static void bridge_base_pull(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
-{
-	ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
-}
-
-/*!
- * \internal
- * \brief ast_bridge base notify_masquerade method.
- * \since 12.0.0
- *
- * \param self Bridge to operate upon.
- * \param bridge_channel Bridge channel that was masqueraded.
- *
- * \note On entry, self is already locked.
- *
- * \return Nothing
- */
-static void bridge_base_notify_masquerade(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel)
-{
-	self->reconfigured = 1;
-}
-
-/*!
- * \internal
- * \brief Get the merge priority of this bridge.
- * \since 12.0.0
- *
- * \param self Bridge to operate upon.
- *
- * \note On entry, self is already locked.
- *
- * \return Merge priority
- */
-static int bridge_base_get_merge_priority(struct ast_bridge *self)
-{
-	return 0;
-}
-
-struct ast_bridge_methods ast_bridge_base_v_table = {
-	.name = "base",
-	.destroy = bridge_base_destroy,
-	.dissolving = bridge_base_dissolving,
-	.push = bridge_base_push,
-	.pull = bridge_base_pull,
-	.notify_masquerade = bridge_base_notify_masquerade,
-	.get_merge_priority = bridge_base_get_merge_priority,
-};
-
-struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags)
-{
-	void *bridge;
-
-	bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table);
-	bridge = ast_bridge_base_init(bridge, capabilities, flags);
-	bridge = ast_bridge_register(bridge);
-	return bridge;
-}
-
-int ast_bridge_destroy(struct ast_bridge *bridge)
-{
-	ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid);
-	ast_bridge_lock(bridge);
-	bridge_dissolve(bridge);
-	ast_bridge_unlock(bridge);
-
-	ao2_ref(bridge, -1);
-
-	return 0;
-}
-
-static int bridge_make_compatible(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_format read_format;
-	struct ast_format write_format;
-	struct ast_format best_format;
-	char codec_buf[512];
-
-	ast_format_copy(&read_format, ast_channel_readformat(bridge_channel->chan));
-	ast_format_copy(&write_format, ast_channel_writeformat(bridge_channel->chan));
-
-	/* Are the formats currently in use something this bridge can handle? */
-	if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, ast_channel_readformat(bridge_channel->chan))) {
-		ast_best_codec(bridge->technology->format_capabilities, &best_format);
-
-		/* Read format is a no go... */
-		ast_debug(1, "Bridge technology %s wants to read any of formats %s but channel has %s\n",
-			bridge->technology->name,
-			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities),
-			ast_getformatname(&read_format));
-
-		/* 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\n",
-				ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format));
-			return -1;
-		}
-		ast_debug(1, "Bridge %s put channel %s into read format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&best_format));
-	} else {
-		ast_debug(1, "Bridge %s is happy that channel %s already has read format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&read_format));
-	}
-
-	if (!ast_format_cap_iscompatible(bridge->technology->format_capabilities, &write_format)) {
-		ast_best_codec(bridge->technology->format_capabilities, &best_format);
-
-		/* Write format is a no go... */
-		ast_debug(1, "Bridge technology %s wants to write any of formats %s but channel has %s\n",
-			bridge->technology->name,
-			ast_getformatname_multiple(codec_buf, sizeof(codec_buf), bridge->technology->format_capabilities),
-			ast_getformatname(&write_format));
-
-		/* 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\n",
-				ast_channel_name(bridge_channel->chan), ast_getformatname(&best_format));
-			return -1;
-		}
-		ast_debug(1, "Bridge %s put channel %s into write format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&best_format));
-	} else {
-		ast_debug(1, "Bridge %s is happy that channel %s already has write format %s\n",
-			bridge->uniqueid, ast_channel_name(bridge_channel->chan),
-			ast_getformatname(&write_format));
-	}
-
-	return 0;
-}
-
-/*!
- * \internal
- * \brief Perform the smart bridge operation.
- * \since 12.0.0
- *
- * \param bridge Work on this bridge.
- *
- * \details
- * Basically see if a new bridge technology should be used instead
- * of the current one.
- *
- * \note On entry, bridge is already locked.
- *
- * \retval 0 on success.
- * \retval -1 on error.
- */
-static int smart_bridge_operation(struct ast_bridge *bridge)
-{
-	uint32_t new_capabilities;
-	struct ast_bridge_technology *new_technology;
-	struct ast_bridge_technology *old_technology = bridge->technology;
-	struct ast_bridge_channel *bridge_channel;
-	struct ast_frame *deferred_action;
-	struct ast_bridge dummy_bridge = {
-		.technology = bridge->technology,
-		.tech_pvt = bridge->tech_pvt,
-	};
-
-	if (bridge->dissolved) {
-		ast_debug(1, "Bridge %s is dissolved, not performing smart bridge operation.\n",
-			bridge->uniqueid);
-		return 0;
-	}
-
-	/* Determine new bridge technology capabilities needed. */
-	if (2 < bridge->num_channels) {
-		new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
-		new_capabilities &= bridge->allowed_capabilities;
-	} else {
-		new_capabilities = AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX;
-		new_capabilities &= bridge->allowed_capabilities;
-		if (!new_capabilities
-			&& (bridge->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)) {
-			/* Allow switching between different multimix bridge technologies. */
-			new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
-		}
-	}
-
-	/* Find a bridge technology to satisfy the new capabilities. */
-	new_technology = find_best_technology(new_capabilities, bridge);
-	if (!new_technology) {
-		int is_compatible = 0;
-
-		if (old_technology->compatible) {
-			is_compatible = old_technology->compatible(bridge);
-		} else if (old_technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
-			is_compatible = 1;
-		} else if (bridge->num_channels <= 2
-			&& (old_technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX)) {
-			is_compatible = 1;
-		}
-
-		if (is_compatible) {
-			ast_debug(1, "Bridge %s could not get a new technology, staying with old technology.\n",
-				bridge->uniqueid);
-			return 0;
-		}
-		ast_log(LOG_WARNING, "Bridge %s has no technology available to support it.\n",
-			bridge->uniqueid);
-		return -1;
-	}
-	if (new_technology == old_technology) {
-		ast_debug(1, "Bridge %s is already using the new technology.\n",
-			bridge->uniqueid);
-		ast_module_unref(old_technology->mod);
-		return 0;
-	}
-
-	ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid));
-
-	if (old_technology->destroy) {
-		struct tech_deferred_destroy deferred_tech_destroy = {
-			.tech = dummy_bridge.technology,
-			.tech_pvt = dummy_bridge.tech_pvt,
-		};
-		struct ast_frame action = {
-			.frametype = AST_FRAME_BRIDGE_ACTION,
-			.subclass.integer = AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY,
-			.data.ptr = &deferred_tech_destroy,
-			.datalen = sizeof(deferred_tech_destroy),
-		};
-
-		/*
-		 * We need to defer the bridge technology destroy callback
-		 * because we have the bridge locked.
-		 */
-		deferred_action = ast_frdup(&action);
-		if (!deferred_action) {
-			ast_module_unref(new_technology->mod);
-			return -1;
-		}
-	} else {
-		deferred_action = NULL;
-	}
-
-	/*
-	 * We are now committed to changing the bridge technology.  We
-	 * must not release the bridge lock until we have installed the
-	 * new bridge technology.
-	 */
-	ast_verb(4, "Bridge %s: switching from %s technology to %s\n",
-		bridge->uniqueid, old_technology->name, new_technology->name);
-
-	/*
-	 * Since we are soon going to pass this bridge to a new
-	 * technology we need to NULL out the tech_pvt pointer but
-	 * don't worry as it still exists in dummy_bridge, ditto for the
-	 * old technology.
-	 */
-	bridge->tech_pvt = NULL;
-	bridge->technology = new_technology;
-
-	/* Setup the new bridge technology. */
-	ast_debug(1, "Bridge %s: calling %s technology constructor\n",
-		bridge->uniqueid, new_technology->name);
-	if (new_technology->create && new_technology->create(bridge)) {
-		ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n",
-			bridge->uniqueid, new_technology->name);
-		bridge->tech_pvt = dummy_bridge.tech_pvt;
-		bridge->technology = dummy_bridge.technology;
-		ast_module_unref(new_technology->mod);
-		return -1;
-	}
-
-	ast_debug(1, "Bridge %s: calling %s technology stop\n",
-		dummy_bridge.uniqueid, old_technology->name);
-	if (old_technology->stop) {
-		old_technology->stop(&dummy_bridge);
-	}
-
-	/*
-	 * Move existing channels over to the new technology and
-	 * complete joining any new channels to the bridge.
-	 */
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		if (!bridge_channel->just_joined) {
-			/* Take existing channel from the old technology. */
-			ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology (dummy)\n",
-				dummy_bridge.uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
-				old_technology->name);
-			if (old_technology->leave) {
-				old_technology->leave(&dummy_bridge, bridge_channel);
-			}
-		}
-
-		/* Add any new channels or re-add an existing channel to the bridge. */
-		bridge_channel_complete_join(bridge, bridge_channel);
-	}
-
-	ast_debug(1, "Bridge %s: calling %s technology start\n",
-		bridge->uniqueid, new_technology->name);
-	if (new_technology->start && new_technology->start(bridge)) {
-		ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n",
-			bridge->uniqueid, new_technology->name);
-	}
-
-	/*
-	 * 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, "Bridge %s: deferring %s technology destructor\n",
-			dummy_bridge.uniqueid, old_technology->name);
-		bridge_queue_action_nodup(bridge, deferred_action);
-	} else {
-		ast_debug(1, "Bridge %s: calling %s technology destructor\n",
-			dummy_bridge.uniqueid, old_technology->name);
-		ast_module_unref(old_technology->mod);
-	}
-
-	return 0;
-}
-
-/*!
- * \internal
- * \brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played.
- * \since 12.0.0
- *
- * \param bridge_channel What to check.
- *
- * \return Nothing
- */
-static void check_bridge_play_sound(struct ast_bridge_channel *bridge_channel)
-{
-	const char *play_file;
-
-	ast_channel_lock(bridge_channel->chan);
-	play_file = pbx_builtin_getvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND");
-	if (!ast_strlen_zero(play_file)) {
-		play_file = ast_strdupa(play_file);
-		pbx_builtin_setvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND", NULL);
-	} else {
-		play_file = NULL;
-	}
-	ast_channel_unlock(bridge_channel->chan);
-
-	if (play_file) {
-		ast_bridge_channel_queue_playfile(bridge_channel, NULL, play_file, NULL);
-	}
-}
-
-/*!
- * \internal
- * \brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge.
- * \since 12.0.0
- *
- * \param bridge What to operate on.
- *
- * \note On entry, the bridge is already locked.
- *
- * \return Nothing
- */
-static void check_bridge_play_sounds(struct ast_bridge *bridge)
-{
-	struct ast_bridge_channel *bridge_channel;
-
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		check_bridge_play_sound(bridge_channel);
-	}
-}
-
-static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
-{
-	pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name);
-	pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid);
-}
-
-/*!
- * \internal
- * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge.
- * \since 12.0.0
- *
- * \param c0 Party of the first part.
- * \param c1 Party of the second part.
- *
- * \note On entry, the bridge is already locked.
- * \note The bridge is expected to have exactly two parties.
- *
- * \return Nothing
- */
-static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_channel *c1)
-{
-	const char *c0_name;
-	const char *c1_name;
-	const char *c0_pvtid = NULL;
-	const char *c1_pvtid = NULL;
-#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid)									\
-	do {																			\
-		name = ast_strdupa(ast_channel_name(chan));									\
-		if (ast_channel_tech(chan)->get_pvt_uniqueid) {								\
-			pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan));	\
-		}																			\
-	} while (0)
-
-	ast_channel_lock(c1);
-	UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid);
-	ast_channel_unlock(c1);
-
-	ast_channel_lock(c0);
-	update_bridge_vars_set(c0, c1_name, c1_pvtid);
-	UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid);
-	ast_channel_unlock(c0);
-
-	ast_channel_lock(c1);
-	update_bridge_vars_set(c1, c0_name, c0_pvtid);
-	ast_channel_unlock(c1);
-}
-
-/*!
- * \internal
- * \brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names.
- * \since 12.0.0
- *
- * \param buf Buffer to fill.  The caller must guarantee the buffer is large enough.
- * \param cur_idx Which index into names[] to skip.
- * \param names Channel names to put in the buffer.
- * \param num_names Number of names in the array.
- *
- * \return Nothing
- */
-static void fill_bridgepeer_buf(char *buf, unsigned int cur_idx, const char *names[], unsigned int num_names)
-{
-	int need_separator = 0;
-	unsigned int idx;
-	const char *src;
-	char *pos;
-
-	pos = buf;
-	for (idx = 0; idx < num_names; ++idx) {
-		if (idx == cur_idx) {
-			continue;
-		}
-
-		if (need_separator) {
-			*pos++ = ',';
-		}
-		need_separator = 1;
-
-		/* Copy name into buffer. */
-		src = names[idx];
-		while (*src) {
-			*pos++ = *src++;
-		}
-	}
-	*pos = '\0';
-}
-
-/*!
- * \internal
- * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi-party bridge.
- * \since 12.0.0
- *
- * \param bridge What to operate on.
- *
- * \note On entry, the bridge is already locked.
- * \note The bridge is expected to have more than two parties.
- *
- * \return Nothing
- */
-static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge)
-{
-/*
- * Set a maximum number of channel names for the BRIDGEPEER
- * list.  The plus one is for the current channel which is not
- * put in the list.
- */
-#define MAX_BRIDGEPEER_CHANS	(10 + 1)
-
-	unsigned int idx;
-	unsigned int num_names;
-	unsigned int len;
-	const char **names;
-	char *buf;
-	struct ast_bridge_channel *bridge_channel;
-
-	/* Get first MAX_BRIDGEPEER_CHANS channel names. */
-	num_names = MIN(bridge->num_channels, MAX_BRIDGEPEER_CHANS);
-	names = ast_alloca(num_names * sizeof(*names));
-	idx = 0;
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		if (num_names <= idx) {
-			break;
-		}
-		ast_channel_lock(bridge_channel->chan);
-		names[idx++] = ast_strdupa(ast_channel_name(bridge_channel->chan));
-		ast_channel_unlock(bridge_channel->chan);
-	}
-
-	/* Determine maximum buf size needed. */
-	len = num_names;
-	for (idx = 0; idx < num_names; ++idx) {
-		len += strlen(names[idx]);
-	}
-	buf = ast_alloca(len);
-
-	/* Set the bridge channel variables. */
-	idx = 0;
-	buf[0] = '\0';
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		if (idx < num_names) {
-			fill_bridgepeer_buf(buf, idx, names, num_names);
-		}
-		++idx;
-
-		ast_channel_lock(bridge_channel->chan);
-		update_bridge_vars_set(bridge_channel->chan, buf, NULL);
-		ast_channel_unlock(bridge_channel->chan);
-	}
-}
-
-/*!
- * \internal
- * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge.
- * \since 12.0.0
- *
- * \param bridge What to operate on.
- *
- * \note On entry, the bridge is already locked.
- *
- * \return Nothing
- */
-static void set_bridge_peer_vars_holding(struct ast_bridge *bridge)
-{
-	struct ast_bridge_channel *bridge_channel;
-
-	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
-		ast_channel_lock(bridge_channel->chan);
-		update_bridge_vars_set(bridge_channel->chan, NULL, NULL);
-		ast_channel_unlock(bridge_channel->chan);
-	}
-}
-
-/*!
- * \internal
- * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge.
- * \since 12.0.0
- *
- * \param bridge What to operate on.
- *
- * \note On entry, the bridge is already locked.
- *
- * \return Nothing
- */
-static void set_bridge_peer_vars(struct ast_bridge *bridge)
-{
-	if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) {
-		set_bridge_peer_vars_holding(bridge);
-		return;
-	}
-	if (bridge->num_channels < 2) {
-		return;
-	}
-	if (bridge->num_channels == 2) {
-		set_bridge_peer_vars_2party(AST_LIST_FIRST(&bridge->channels)->chan,
-			AST_LIST_LAST(&bridge->channels)->chan);
-	} else {
-		set_bridge_peer_vars_multiparty(bridge);
-	}
-}
-
-/*!
- * \internal
- * \brief Notify the bridge that it has been reconfigured.
- * \since 12.0.0
- *
- * \param bridge Reconfigured bridge.
- * \param colp_update Whether to perform COLP updates.
- *
- * \details
- * After a series of bridge_channel_push and
- * bridge_channel_pull calls, you need to call this function
- * to cause the bridge to complete restructuring for the change
- * in the channel makeup of the bridge.
- *
- * \note On entry, the bridge is already locked.
- *
- * \return Nothing
- */
-static void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update)
-{
-	if (!bridge->reconfigured) {
-		return;
-	}
-	bridge->reconfigured = 0;
-	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART)
-		&& smart_bridge_operation(bridge)) {
-		/* Smart bridge failed. */
-		bridge_dissolve(bridge);
-		return;
-	}
-	bridge_complete_join(bridge);
-
-	if (bridge->dissolved) {
-		return;
-	}
-	check_bridge_play_sounds(bridge);
-	set_bridge_peer_vars(bridge);
-	ast_bridge_publish_state(bridge);
-
-	if (colp_update) {
-		bridge_reconfigured_connected_line_update(bridge);
-	}
-}
-
-/*!
- * \internal
- * \brief Suspend a channel from a bridge.
- *
- * \param bridge_channel Channel to suspend.
- *
- * \note This function assumes bridge_channel->bridge is locked.
- *
- * \return Nothing
- */
-static void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel)
-{
-	bridge_channel->suspended = 1;
-	if (bridge_channel->in_bridge) {
-		--bridge_channel->bridge->num_active;
-	}
-
-	/* Get technology bridge threads off of the channel. */
-	if (bridge_channel->bridge->technology->suspend) {
-		bridge_channel->bridge->technology->suspend(bridge_channel->bridge, bridge_channel);
-	}
-}
-
-/*!
- * \internal
- * \brief Suspend a channel from a bridge.
- *
- * \param bridge_channel Channel to suspend.
- *
- * \return Nothing
- */
-static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel)
-{
-	ast_bridge_channel_lock_bridge(bridge_channel);
-	bridge_channel_suspend_nolock(bridge_channel);
-	ast_bridge_unlock(bridge_channel->bridge);
-}
-
-/*!
- * \internal
- * \brief Unsuspend a channel from a bridge.
- *
- * \param bridge_channel Channel to unsuspend.
- *
- * \note This function assumes bridge_channel->bridge is locked.
- *
- * \return Nothing
- */
-static void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel)
-{
-	bridge_channel->suspended = 0;
-	if (bridge_channel->in_bridge) {
-		++bridge_channel->bridge->num_active;
-	}
-
-	/* Wake technology bridge threads to take care of channel again. */
-	if (bridge_channel->bridge->technology->unsuspend) {
-		bridge_channel->bridge->technology->unsuspend(bridge_channel->bridge, bridge_channel);
-	}
-
-	/* Wake suspended channel. */
-	ast_bridge_channel_lock(bridge_channel);
-	ast_cond_signal(&bridge_channel->cond);
-	ast_bridge_channel_unlock(bridge_channel);
-}
-
-/*!
- * \internal
- * \brief Unsuspend a channel from a bridge.
- *
- * \param bridge_channel Channel to unsuspend.
- *
- * \return Nothing
- */
-static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
-{
-	ast_bridge_channel_lock_bridge(bridge_channel);
-	bridge_channel_unsuspend_nolock(bridge_channel);
-	ast_bridge_unlock(bridge_channel->bridge);
-}
-
-/*! \brief Internal function that activates interval hooks on a bridge channel */
-static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge_hook *hook;
-	struct timeval start;
+	/* Determine new bridge technology capabilities needed. */
+	if (2 < bridge->num_channels) {
+		new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
+		new_capabilities &= bridge->allowed_capabilities;
+	} else {
+		new_capabilities = AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX;
+		new_capabilities &= bridge->allowed_capabilities;
+		if (!new_capabilities
+			&& (bridge->allowed_capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX)) {
+			/* Allow switching between different multimix bridge technologies. */
+			new_capabilities = AST_BRIDGE_CAPABILITY_MULTIMIX;
+		}
+	}
 
-	ast_heap_wrlock(bridge_channel->features->interval_hooks);
-	start = ast_tvnow();
-	while ((hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1))) {
-		int interval;
-		unsigned int execution_time;
+	/* Find a bridge technology to satisfy the new capabilities. */
+	new_technology = find_best_technology(new_capabilities, bridge);
+	if (!new_technology) {
+		int is_compatible = 0;
 
-		if (ast_tvdiff_ms(hook->parms.timer.trip_time, start) > 0) {
-			ast_debug(1, "Hook %p on %p(%s) wants to happen in the future, stopping our traversal\n",
-				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
-			break;
-		}
-		ao2_ref(hook, +1);
-		ast_heap_unlock(bridge_channel->features->interval_hooks);
-
-		ast_debug(1, "Executing hook %p on %p(%s)\n",
-			hook, bridge_channel, ast_channel_name(bridge_channel->chan));
-		interval = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
-
-		ast_heap_wrlock(bridge_channel->features->interval_hooks);
-		if (ast_heap_peek(bridge_channel->features->interval_hooks,
-			hook->parms.timer.heap_index) != hook
-			|| !ast_heap_remove(bridge_channel->features->interval_hooks, hook)) {
-			/* Interval hook is already removed from the bridge_channel. */
-			ao2_ref(hook, -1);
-			continue;
+		if (old_technology->compatible) {
+			is_compatible = old_technology->compatible(bridge);
+		} else if (old_technology->capabilities & AST_BRIDGE_CAPABILITY_MULTIMIX) {
+			is_compatible = 1;
+		} else if (bridge->num_channels <= 2
+			&& (old_technology->capabilities & AST_BRIDGE_CAPABILITY_1TO1MIX)) {
+			is_compatible = 1;
 		}
-		ao2_ref(hook, -1);
 
-		if (interval < 0) {
-			ast_debug(1, "Removed interval hook %p from %p(%s)\n",
-				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
-			ao2_ref(hook, -1);
-			continue;
-		}
-		if (interval) {
-			/* Set new interval for the hook. */
-			hook->parms.timer.interval = interval;
+		if (is_compatible) {
+			ast_debug(1, "Bridge %s could not get a new technology, staying with old technology.\n",
+				bridge->uniqueid);
+			return 0;
 		}
+		ast_log(LOG_WARNING, "Bridge %s has no technology available to support it.\n",
+			bridge->uniqueid);
+		return -1;
+	}
+	if (new_technology == old_technology) {
+		ast_debug(1, "Bridge %s is already using the new technology.\n",
+			bridge->uniqueid);
+		ast_module_unref(old_technology->mod);
+		return 0;
+	}
 
-		ast_debug(1, "Updating interval hook %p with interval %u on %p(%s)\n",
-			hook, hook->parms.timer.interval, bridge_channel,
-			ast_channel_name(bridge_channel->chan));
+	ast_copy_string(dummy_bridge.uniqueid, bridge->uniqueid, sizeof(dummy_bridge.uniqueid));
 
-		/* resetting start */
-		start = ast_tvnow();
+	if (old_technology->destroy) {
+		struct tech_deferred_destroy deferred_tech_destroy = {
+			.tech = dummy_bridge.technology,
+			.tech_pvt = dummy_bridge.tech_pvt,
+		};
+		struct ast_frame action = {
+			.frametype = AST_FRAME_BRIDGE_ACTION,
+			.subclass.integer = AST_BRIDGE_ACTION_DEFERRED_TECH_DESTROY,
+			.data.ptr = &deferred_tech_destroy,
+			.datalen = sizeof(deferred_tech_destroy),
+		};
 
 		/*
-		 * Resetup the interval hook for the next interval.  We may need
-		 * to skip over any missed intervals because the hook was
-		 * delayed or took too long.
+		 * We need to defer the bridge technology destroy callback
+		 * because we have the bridge locked.
 		 */
-		execution_time = ast_tvdiff_ms(start, hook->parms.timer.trip_time);
-		while (hook->parms.timer.interval < execution_time) {
-			execution_time -= hook->parms.timer.interval;
-		}
-		hook->parms.timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->parms.timer.interval - execution_time, 1000));
-		hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1);
-
-		if (ast_heap_push(bridge_channel->features->interval_hooks, hook)) {
-			/* Could not push the hook back onto the heap. */
-			ao2_ref(hook, -1);
+		deferred_action = ast_frdup(&action);
+		if (!deferred_action) {
+			ast_module_unref(new_technology->mod);
+			return -1;
 		}
+	} else {
+		deferred_action = NULL;
 	}
-	ast_heap_unlock(bridge_channel->features->interval_hooks);
-}
 
-static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf)
-{
-	return ast_bridge_channel_write_action_data(bridge_channel,
-		AST_BRIDGE_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1);
-}
+	/*
+	 * We are now committed to changing the bridge technology.  We
+	 * must not release the bridge lock until we have installed the
+	 * new bridge technology.
+	 */
+	ast_verb(4, "Bridge %s: switching from %s technology to %s\n",
+		bridge->uniqueid, old_technology->name, new_technology->name);
 
-/*!
- * \brief Internal function that executes a feature on a bridge channel
- * \note Neither the bridge nor the bridge_channel locks should be held when entering
- * this function.
- */
-static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge_features *features = bridge_channel->features;
-	struct ast_bridge_hook *hook = NULL;
-	char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
-	size_t dtmf_len = 0;
-	unsigned int digit_timeout;
-	RAII_VAR(struct ast_features_general_config *, gen_cfg, NULL, ao2_cleanup);
+	/*
+	 * Since we are soon going to pass this bridge to a new
+	 * technology we need to NULL out the tech_pvt pointer but
+	 * don't worry as it still exists in dummy_bridge, ditto for the
+	 * old technology.
+	 */
+	bridge->tech_pvt = NULL;
+	bridge->technology = new_technology;
 
-	ast_channel_lock(bridge_channel->chan);
-	gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
-	if (!gen_cfg) {
-		ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n");
-		ast_channel_unlock(bridge_channel->chan);
-		return;
+	/* Setup the new bridge technology. */
+	ast_debug(1, "Bridge %s: calling %s technology constructor\n",
+		bridge->uniqueid, new_technology->name);
+	if (new_technology->create && new_technology->create(bridge)) {
+		ast_log(LOG_WARNING, "Bridge %s: failed to setup bridge technology %s\n",
+			bridge->uniqueid, new_technology->name);
+		bridge->tech_pvt = dummy_bridge.tech_pvt;
+		bridge->technology = dummy_bridge.technology;
+		ast_module_unref(new_technology->mod);
+		return -1;
 	}
-	digit_timeout = gen_cfg->featuredigittimeout;
-	ast_channel_unlock(bridge_channel->chan);
-
-	/* 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(ast_channel_flags(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. */
-	do {
-		int res;
-
-		/* If the above timed out simply exit */
-		res = ast_waitfordigit(bridge_channel->chan, digit_timeout);
-		if (!res) {
-			ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan));
-			break;
-		}
-		if (res < 0) {
-			ast_debug(1, "DTMF feature string collection failed on %p(%s) for some reason\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan));
-			break;
-		}
-
-/* BUGBUG need to record the duration of DTMF digits so when the string is played back, they are reproduced. */
-		/* 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 %p(%s) is now '%s'\n",
-			bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
-
-		/* See if a DTMF feature hook matches or can match */
-		hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY);
-		if (!hook) {
-			ast_debug(1, "No DTMF feature hooks on %p(%s) match '%s'\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
-			break;
-		}
-		if (strlen(hook->parms.dtmf.code) == dtmf_len) {
-			ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n",
-				hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
-			break;
-		}
-		ao2_ref(hook, -1);
-		hook = NULL;
-
-		/* Stop if we have reached the maximum length of a DTMF feature string. */
-	} while (dtmf_len < ARRAY_LEN(dtmf) - 1);
 
-	/* Since we are done bringing DTMF in return to using both begin and end frames */
-	ast_clear_flag(ast_channel_flags(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) {
-		int remove_me;
+	ast_debug(1, "Bridge %s: calling %s technology stop\n",
+		dummy_bridge.uniqueid, old_technology->name);
+	if (old_technology->stop) {
+		old_technology->stop(&dummy_bridge);
+	}
 
-		remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
-		if (remove_me) {
-			ast_debug(1, "DTMF hook %p is being removed from %p(%s)\n",
-				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
-			ao2_unlink(features->dtmf_hooks, hook);
+	/*
+	 * Move existing channels over to the new technology and
+	 * complete joining any new channels to the bridge.
+	 */
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (!bridge_channel->just_joined) {
+			/* Take existing channel from the old technology. */
+			ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology (dummy)\n",
+				dummy_bridge.uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
+				old_technology->name);
+			if (old_technology->leave) {
+				old_technology->leave(&dummy_bridge, bridge_channel);
+			}
 		}
-		ao2_ref(hook, -1);
 
-		/*
-		 * If we are handing the channel off to an external hook for
-		 * ownership, we are not guaranteed what kind of state it will
-		 * come back in.  If the channel hungup, we need to detect that
-		 * here if the hook did not already change the state.
-		 */
-		if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
-			bridge_handle_hangup(bridge_channel);
-		}
-	} else if (features->dtmf_passthrough) {
-		bridge_channel_write_dtmf_stream(bridge_channel, dtmf);
+		/* Add any new channels or re-add an existing channel to the bridge. */
+		bridge_channel_complete_join(bridge, bridge_channel);
 	}
-}
-
-static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking)
-{
-	struct ast_bridge_features *features = bridge_channel->features;
 
-	if (features->talker_cb) {
-		features->talker_cb(bridge_channel, features->talker_pvt_data, talking);
+	ast_debug(1, "Bridge %s: calling %s technology start\n",
+		bridge->uniqueid, new_technology->name);
+	if (new_technology->start && new_technology->start(bridge)) {
+		ast_log(LOG_WARNING, "Bridge %s: failed to start bridge technology %s\n",
+			bridge->uniqueid, new_technology->name);
 	}
-}
-
-/*! \brief Internal function that plays back DTMF on a bridge channel */
-static void bridge_channel_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf)
-{
-	ast_debug(1, "Playing DTMF stream '%s' out to %p(%s)\n",
-		dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
-	ast_dtmf_stream(bridge_channel->chan, NULL, dtmf, 0, 0);
-}
 
-struct blind_transfer_data {
-	char exten[AST_MAX_EXTENSION];
-	char context[AST_MAX_CONTEXT];
-};
+	/*
+	 * 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, "Bridge %s: deferring %s technology destructor\n",
+			dummy_bridge.uniqueid, old_technology->name);
+		bridge_queue_action_nodup(bridge, deferred_action);
+	} else {
+		ast_debug(1, "Bridge %s: calling %s technology destructor\n",
+			dummy_bridge.uniqueid, old_technology->name);
+		ast_module_unref(old_technology->mod);
+	}
 
-static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel,
-		struct blind_transfer_data *blind_data)
-{
-	ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1);
-	bridge_handle_hangup(bridge_channel);
+	return 0;
 }
 
-static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data)
+/*!
+ * \internal
+ * \brief Bridge channel to check if a BRIDGE_PLAY_SOUND needs to be played.
+ * \since 12.0.0
+ *
+ * \param bridge_channel What to check.
+ *
+ * \return Nothing
+ */
+static void check_bridge_play_sound(struct ast_bridge_channel *bridge_channel)
 {
-	RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
-	struct ast_party_connected_line connected_target;
-	unsigned char connected_line_data[1024];
-	int payload_size;
-
-	ast_party_connected_line_init(&connected_target);
-
-	ast_channel_lock(chan_target);
-	ast_party_connected_line_copy(&connected_target, ast_channel_connected(chan_target));
-	ast_channel_unlock(chan_target);
-	ast_party_id_reset(&connected_target.priv);
+	const char *play_file;
 
-	if (ast_channel_move(chan_target, chan_bridged)) {
-		ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
-		ast_party_connected_line_free(&connected_target);
-		return;
+	ast_channel_lock(bridge_channel->chan);
+	play_file = pbx_builtin_getvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND");
+	if (!ast_strlen_zero(play_file)) {
+		play_file = ast_strdupa(play_file);
+		pbx_builtin_setvar_helper(bridge_channel->chan, "BRIDGE_PLAY_SOUND", NULL);
+	} else {
+		play_file = NULL;
 	}
+	ast_channel_unlock(bridge_channel->chan);
 
-	if ((payload_size = ast_connected_line_build_data(connected_line_data,
-		sizeof(connected_line_data), &connected_target, NULL)) != -1) {
-		struct ast_control_read_action_payload *frame_payload;
-		int frame_size;
-
-		frame_size = payload_size + sizeof(*frame_payload);
-		frame_payload = ast_alloca(frame_size);
-		frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
-		frame_payload->payload_size = payload_size;
-		memcpy(frame_payload->payload, connected_line_data, payload_size);
-		ast_queue_control_data(chan_target, AST_CONTROL_READ_ACTION, frame_payload, frame_size);
+	if (play_file) {
+		ast_bridge_channel_queue_playfile(bridge_channel, NULL, play_file, NULL);
 	}
-
-	ast_party_connected_line_free(&connected_target);
 }
 
-static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data)
+/*!
+ * \internal
+ * \brief Check for any BRIDGE_PLAY_SOUND channel variables in the bridge.
+ * \since 12.0.0
+ *
+ * \param bridge What to operate on.
+ *
+ * \note On entry, the bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void check_bridge_play_sounds(struct ast_bridge *bridge)
 {
-	RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
+	struct ast_bridge_channel *bridge_channel;
 
-	ast_log(LOG_WARNING, "Unable to complete transfer: %s\n",
-		ast_after_bridge_cb_reason_string(reason));
-	ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		check_bridge_play_sound(bridge_channel);
+	}
 }
 
-static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel,
-		const char *target_chan_name)
+static void update_bridge_vars_set(struct ast_channel *chan, const char *name, const char *pvtid)
 {
-	RAII_VAR(struct ast_channel *, chan_target, NULL, ao2_cleanup);
-	RAII_VAR(struct ast_channel *, chan_bridged, NULL, ao2_cleanup);
-
-	chan_target = ast_channel_get_by_name(target_chan_name);
-	if (!chan_target) {
-		/* Dang, it disappeared somehow */
-		bridge_handle_hangup(bridge_channel);
-		return;
-	}
-
-	ast_bridge_channel_lock(bridge_channel);
-	chan_bridged = bridge_channel->chan;
-	ast_assert(chan_bridged != NULL);
-	ao2_ref(chan_bridged, +1);
-	ast_bridge_channel_unlock(bridge_channel);
-
-	if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel,
-		after_bridge_move_channel_fail, ast_channel_ref(chan_target))) {
-		ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
-
-		/* Release the ref we tried to pass to ast_after_bridge_callback_set(). */
-		ast_channel_unref(chan_target);
-	}
-	bridge_handle_hangup(bridge_channel);
+	pbx_builtin_setvar_helper(chan, "BRIDGEPEER", name);
+	pbx_builtin_setvar_helper(chan, "BRIDGEPVTCALLID", pvtid);
 }
 
 /*!
  * \internal
- * \brief Handle bridge channel bridge action frame.
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a 2 party bridge.
  * \since 12.0.0
  *
- * \param bridge_channel Channel to execute the action on.
- * \param action What to do.
+ * \param c0 Party of the first part.
+ * \param c1 Party of the second part.
+ *
+ * \note On entry, the bridge is already locked.
+ * \note The bridge is expected to have exactly two parties.
  *
  * \return Nothing
  */
-static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action)
+static void set_bridge_peer_vars_2party(struct ast_channel *c0, struct ast_channel *c1)
 {
-	switch (action->subclass.integer) {
-	case AST_BRIDGE_ACTION_INTERVAL:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_interval(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-		break;
-	case AST_BRIDGE_ACTION_FEATURE:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_feature(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-		break;
-	case AST_BRIDGE_ACTION_DTMF_STREAM:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_dtmf_stream(bridge_channel, action->data.ptr);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-		break;
-	case AST_BRIDGE_ACTION_TALKING_START:
-	case AST_BRIDGE_ACTION_TALKING_STOP:
-		bridge_channel_talking(bridge_channel,
-			action->subclass.integer == AST_BRIDGE_ACTION_TALKING_START);
-		break;
-	case AST_BRIDGE_ACTION_PLAY_FILE:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_playfile(bridge_channel, action->data.ptr);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-		break;
-	case AST_BRIDGE_ACTION_RUN_APP:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_run_app(bridge_channel, action->data.ptr);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-		break;
-	case AST_BRIDGE_ACTION_CALLBACK:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_do_callback(bridge_channel, action->data.ptr);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-		break;
-	case AST_BRIDGE_ACTION_PARK:
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_park(bridge_channel, action->data.ptr);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-		break;
-	case AST_BRIDGE_ACTION_BLIND_TRANSFER:
-		bridge_channel_blind_transfer(bridge_channel, action->data.ptr);
-		break;
-	case AST_BRIDGE_ACTION_ATTENDED_TRANSFER:
-		bridge_channel_attended_transfer(bridge_channel, action->data.ptr);
-		break;
-	default:
-		break;
-	}
+	const char *c0_name;
+	const char *c1_name;
+	const char *c0_pvtid = NULL;
+	const char *c1_pvtid = NULL;
+#define UPDATE_BRIDGE_VARS_GET(chan, name, pvtid)									\
+	do {																			\
+		name = ast_strdupa(ast_channel_name(chan));									\
+		if (ast_channel_tech(chan)->get_pvt_uniqueid) {								\
+			pvtid = ast_strdupa(ast_channel_tech(chan)->get_pvt_uniqueid(chan));	\
+		}																			\
+	} while (0)
+
+	ast_channel_lock(c1);
+	UPDATE_BRIDGE_VARS_GET(c1, c1_name, c1_pvtid);
+	ast_channel_unlock(c1);
+
+	ast_channel_lock(c0);
+	update_bridge_vars_set(c0, c1_name, c1_pvtid);
+	UPDATE_BRIDGE_VARS_GET(c0, c0_name, c0_pvtid);
+	ast_channel_unlock(c0);
+
+	ast_channel_lock(c1);
+	update_bridge_vars_set(c1, c0_name, c0_pvtid);
+	ast_channel_unlock(c1);
 }
 
 /*!
  * \internal
- * \brief Handle bridge channel control frame action.
+ * \brief Fill the BRIDGEPEER value buffer with a comma separated list of channel names.
  * \since 12.0.0
  *
- * \param bridge_channel Channel to execute the control frame action on.
- * \param fr Control frame to handle.
+ * \param buf Buffer to fill.  The caller must guarantee the buffer is large enough.
+ * \param cur_idx Which index into names[] to skip.
+ * \param names Channel names to put in the buffer.
+ * \param num_names Number of names in the array.
  *
  * \return Nothing
  */
-static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
+static void fill_bridgepeer_buf(char *buf, unsigned int cur_idx, const char *names[], unsigned int num_names)
 {
-	struct ast_channel *chan;
-	struct ast_option_header *aoh;
-	int is_caller;
-	int intercept_failed;
+	int need_separator = 0;
+	unsigned int idx;
+	const char *src;
+	char *pos;
 
-	chan = bridge_channel->chan;
-	switch (fr->subclass.integer) {
-	case AST_CONTROL_REDIRECTING:
-		is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
-		bridge_channel_suspend(bridge_channel);
-		intercept_failed = ast_channel_redirecting_sub(NULL, chan, fr, 1)
-			&& ast_channel_redirecting_macro(NULL, chan, fr, is_caller, 1);
-		bridge_channel_unsuspend(bridge_channel);
-		if (intercept_failed) {
-			ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
-		}
-		break;
-	case AST_CONTROL_CONNECTED_LINE:
-		is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
-		bridge_channel_suspend(bridge_channel);
-		intercept_failed = ast_channel_connected_line_sub(NULL, chan, fr, 1)
-			&& ast_channel_connected_line_macro(NULL, chan, fr, is_caller, 1);
-		bridge_channel_unsuspend(bridge_channel);
-		if (intercept_failed) {
-			ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+	pos = buf;
+	for (idx = 0; idx < num_names; ++idx) {
+		if (idx == cur_idx) {
+			continue;
 		}
-		break;
-	case AST_CONTROL_HOLD:
-	case AST_CONTROL_UNHOLD:
-/*
- * BUGBUG bridge_channels should remember sending/receiving an outstanding HOLD to/from the bridge
- *
- * When the sending channel is pulled from the bridge it needs to write into the bridge an UNHOLD before being pulled.
- * When the receiving channel is pulled from the bridge it needs to generate its own UNHOLD.
- * Something similar needs to be done for DTMF begin/end.
- */
-	case AST_CONTROL_VIDUPDATE:
-	case AST_CONTROL_SRCUPDATE:
-	case AST_CONTROL_SRCCHANGE:
-	case AST_CONTROL_T38_PARAMETERS:
-/* BUGBUG may have to do something with a jitter buffer for these. */
-		ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
-		break;
-	case AST_CONTROL_OPTION:
-		/*
-		 * Forward option Requests, but only ones we know are safe These
-		 * are ONLY sent by chan_iax2 and I'm not convinced that they
-		 * are useful. I haven't deleted them entirely because I just am
-		 * not sure of the ramifications of removing them.
-		 */
-		aoh = fr->data.ptr;
-		if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
-			switch (ntohs(aoh->option)) {
-			case AST_OPTION_TONE_VERIFY:
-			case AST_OPTION_TDD:
-			case AST_OPTION_RELAXDTMF:
-			case AST_OPTION_AUDIO_MODE:
-			case AST_OPTION_DIGIT_DETECT:
-			case AST_OPTION_FAX_DETECT:
-				ast_channel_setoption(chan, ntohs(aoh->option), aoh->data,
-					fr->datalen - sizeof(*aoh), 0);
-				break;
-			default:
-				break;
-			}
+
+		if (need_separator) {
+			*pos++ = ',';
 		}
-		break;
-	case AST_CONTROL_ANSWER:
-		if (ast_channel_state(chan) != AST_STATE_UP) {
-			ast_answer(chan);
-		} else {
-			ast_indicate(chan, -1);
+		need_separator = 1;
+
+		/* Copy name into buffer. */
+		src = names[idx];
+		while (*src) {
+			*pos++ = *src++;
 		}
-		break;
-	default:
-		ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
-		break;
 	}
+	*pos = '\0';
 }
 
 /*!
  * \internal
- * \brief Handle bridge channel write frame to channel.
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a multi-party bridge.
  * \since 12.0.0
  *
- * \param bridge_channel Channel to write outgoing frame.
+ * \param bridge What to operate on.
+ *
+ * \note On entry, the bridge is already locked.
+ * \note The bridge is expected to have more than two parties.
  *
  * \return Nothing
  */
-static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channel)
+static void set_bridge_peer_vars_multiparty(struct ast_bridge *bridge)
 {
-	struct ast_frame *fr;
-	char nudge;
+/*
+ * Set a maximum number of channel names for the BRIDGEPEER
+ * list.  The plus one is for the current channel which is not
+ * put in the list.
+ */
+#define MAX_BRIDGEPEER_CHANS	(10 + 1)
 
-	ast_bridge_channel_lock(bridge_channel);
-	if (read(bridge_channel->alert_pipe[0], &nudge, sizeof(nudge)) < 0) {
-		if (errno != EINTR && errno != EAGAIN) {
-			ast_log(LOG_WARNING, "read() failed for alert pipe on %p(%s): %s\n",
-				bridge_channel, ast_channel_name(bridge_channel->chan), strerror(errno));
+	unsigned int idx;
+	unsigned int num_names;
+	unsigned int len;
+	const char **names;
+	char *buf;
+	struct ast_bridge_channel *bridge_channel;
+
+	/* Get first MAX_BRIDGEPEER_CHANS channel names. */
+	num_names = MIN(bridge->num_channels, MAX_BRIDGEPEER_CHANS);
+	names = ast_alloca(num_names * sizeof(*names));
+	idx = 0;
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (num_names <= idx) {
+			break;
 		}
+		ast_channel_lock(bridge_channel->chan);
+		names[idx++] = ast_strdupa(ast_channel_name(bridge_channel->chan));
+		ast_channel_unlock(bridge_channel->chan);
 	}
-	fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list);
-	ast_bridge_channel_unlock(bridge_channel);
-	if (!fr) {
-		return;
-	}
-	switch (fr->frametype) {
-	case AST_FRAME_BRIDGE_ACTION:
-		bridge_channel_handle_action(bridge_channel, fr);
-		break;
-	case AST_FRAME_CONTROL:
-		bridge_channel_handle_control(bridge_channel, fr);
-		break;
-	case AST_FRAME_NULL:
-		break;
-	default:
-		/* Write the frame to the channel. */
-		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE;
-		ast_write(bridge_channel->chan, fr);
-		break;
+
+	/* Determine maximum buf size needed. */
+	len = num_names;
+	for (idx = 0; idx < num_names; ++idx) {
+		len += strlen(names[idx]);
 	}
-	ast_frfree(fr);
-}
+	buf = ast_alloca(len);
 
-/*!
- * \internal
- * \brief Handle bridge channel interval expiration.
- * \since 12.0.0
- *
- * \param bridge_channel Channel to check interval on.
- *
- * \return Nothing
- */
-static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_timer *interval_timer;
-
-	interval_timer = bridge_channel->features->interval_timer;
-	if (interval_timer) {
-		if (ast_wait_for_input(ast_timer_fd(interval_timer), 0) == 1) {
-			ast_timer_ack(interval_timer, 1);
-			if (bridge_channel_interval_ready(bridge_channel)) {
-/* BUGBUG since this is now only run by the channel thread, there is no need to queue the action once this intervals become a first class wait item in bridge_channel_wait(). */
-				struct ast_frame interval_action = {
-					.frametype = AST_FRAME_BRIDGE_ACTION,
-					.subclass.integer = AST_BRIDGE_ACTION_INTERVAL,
-				};
-
-				ast_bridge_channel_queue_frame(bridge_channel, &interval_action);
-			}
+	/* Set the bridge channel variables. */
+	idx = 0;
+	buf[0] = '\0';
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (idx < num_names) {
+			fill_bridgepeer_buf(buf, idx, names, num_names);
 		}
+		++idx;
+
+		ast_channel_lock(bridge_channel->chan);
+		update_bridge_vars_set(bridge_channel->chan, buf, NULL);
+		ast_channel_unlock(bridge_channel->chan);
 	}
 }
 
 /*!
  * \internal
- * \brief Wait for something to happen on the bridge channel and handle it.
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in a holding bridge.
  * \since 12.0.0
  *
- * \param bridge_channel Channel to wait.
+ * \param bridge What to operate on.
  *
- * \note Each channel does writing/reading in their own thread.
+ * \note On entry, the bridge is already locked.
  *
  * \return Nothing
  */
-static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel)
+static void set_bridge_peer_vars_holding(struct ast_bridge *bridge)
 {
-	int ms = -1;
-	int outfd;
-	struct ast_channel *chan;
+	struct ast_bridge_channel *bridge_channel;
 
-	/* Wait for data to either come from the channel or us to be signaled */
-	ast_bridge_channel_lock(bridge_channel);
-	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
-	} else if (bridge_channel->suspended) {
-/* BUGBUG the external party use of suspended will go away as will these references because this is the bridge channel thread */
-		ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n",
-			bridge_channel->bridge->uniqueid, bridge_channel,
-			ast_channel_name(bridge_channel->chan));
-		ast_cond_wait(&bridge_channel->cond, ao2_object_get_lockaddr(bridge_channel));
-	} else {
-		ast_debug(10, "Bridge %s: %p(%s) is going into a waitfor\n",
-			bridge_channel->bridge->uniqueid, bridge_channel,
-			ast_channel_name(bridge_channel->chan));
-		bridge_channel->waiting = 1;
-		ast_bridge_channel_unlock(bridge_channel);
-		outfd = -1;
-/* BUGBUG need to make the next expiring active interval setup ms timeout rather than holding up the chan reads. */
-		chan = ast_waitfor_nandfds(&bridge_channel->chan, 1,
-			&bridge_channel->alert_pipe[0], 1, NULL, &outfd, &ms);
-		bridge_channel->waiting = 0;
-		if (ast_channel_softhangup_internal_flag(bridge_channel->chan) & AST_SOFTHANGUP_UNBRIDGE) {
-			ast_channel_clear_softhangup(bridge_channel->chan, AST_SOFTHANGUP_UNBRIDGE);
-			ast_bridge_channel_lock_bridge(bridge_channel);
-			bridge_channel->bridge->reconfigured = 1;
-			bridge_reconfigured(bridge_channel->bridge, 0);
-			ast_bridge_unlock(bridge_channel->bridge);
-		}
-		ast_bridge_channel_lock(bridge_channel);
-		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME;
-		ast_bridge_channel_unlock(bridge_channel);
-		if (!bridge_channel->suspended
-			&& bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-			if (chan) {
-				bridge_channel_handle_interval(bridge_channel);
-				bridge_handle_trip(bridge_channel);
-			} else if (-1 < outfd) {
-				bridge_channel_handle_write(bridge_channel);
-			}
-		}
-		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE;
-		return;
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		ast_channel_lock(bridge_channel->chan);
+		update_bridge_vars_set(bridge_channel->chan, NULL, NULL);
+		ast_channel_unlock(bridge_channel->chan);
 	}
-	ast_bridge_channel_unlock(bridge_channel);
 }
 
 /*!
  * \internal
- * \brief Handle bridge channel join event.
+ * \brief Set BRIDGEPEER and BRIDGEPVTCALLID channel variables in the bridge.
  * \since 12.0.0
  *
- * \param bridge_channel Which channel is joining.
+ * \param bridge What to operate on.
+ *
+ * \note On entry, the bridge is already locked.
  *
  * \return Nothing
  */
-static void bridge_channel_handle_join(struct ast_bridge_channel *bridge_channel)
+static void set_bridge_peer_vars(struct ast_bridge *bridge)
 {
-	struct ast_bridge_features *features = bridge_channel->features;
-	struct ast_bridge_hook *hook;
-	struct ao2_iterator iter;
-
-	/* Run any join hooks. */
-	iter = ao2_iterator_init(features->join_hooks, AO2_ITERATOR_UNLINK);
-	hook = ao2_iterator_next(&iter);
-	if (hook) {
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		do {
-			hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
-			ao2_ref(hook, -1);
-		} while ((hook = ao2_iterator_next(&iter)));
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
+	if (bridge->technology->capabilities & AST_BRIDGE_CAPABILITY_HOLDING) {
+		set_bridge_peer_vars_holding(bridge);
+		return;
+	}
+	if (bridge->num_channels < 2) {
+		return;
+	}
+	if (bridge->num_channels == 2) {
+		set_bridge_peer_vars_2party(AST_LIST_FIRST(&bridge->channels)->chan,
+			AST_LIST_LAST(&bridge->channels)->chan);
+	} else {
+		set_bridge_peer_vars_multiparty(bridge);
 	}
-	ao2_iterator_destroy(&iter);
 }
 
 /*!
  * \internal
- * \brief Handle bridge channel leave event.
+ * \brief Notify the bridge that it has been reconfigured.
  * \since 12.0.0
  *
- * \param bridge_channel Which channel is leaving.
+ * \param bridge Reconfigured bridge.
+ * \param colp_update Whether to perform COLP updates.
+ *
+ * \details
+ * After a series of bridge_channel_push and
+ * bridge_channel_pull calls, you need to call this function
+ * to cause the bridge to complete restructuring for the change
+ * in the channel makeup of the bridge.
+ *
+ * \note On entry, the bridge is already locked.
  *
  * \return Nothing
  */
-static void bridge_channel_handle_leave(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge_features *features = bridge_channel->features;
-	struct ast_bridge_hook *hook;
-	struct ao2_iterator iter;
-
-	/* Run any leave hooks. */
-	iter = ao2_iterator_init(features->leave_hooks, AO2_ITERATOR_UNLINK);
-	hook = ao2_iterator_next(&iter);
-	if (hook) {
-		bridge_channel_suspend(bridge_channel);
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		do {
-			hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
-			ao2_ref(hook, -1);
-		} while ((hook = ao2_iterator_next(&iter)));
-		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
-		bridge_channel_unsuspend(bridge_channel);
-	}
-	ao2_iterator_destroy(&iter);
-}
-
-/*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */
-static void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
+void bridge_reconfigured(struct ast_bridge *bridge, unsigned int colp_update)
 {
-	ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan));
-	ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan));
-
-	ast_debug(1, "Bridge %s: %p(%s) is joining\n",
-		bridge_channel->bridge->uniqueid,
-		bridge_channel, ast_channel_name(bridge_channel->chan));
-
-	/*
-	 * Get "in the bridge" before pushing the channel for any
-	 * masquerades on the channel to happen before bridging.
-	 */
-	ast_channel_lock(bridge_channel->chan);
-	ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
-	ast_channel_unlock(bridge_channel->chan);
-
-	/* Add the jitterbuffer if the channel requires it */
-	ast_jb_enable_for_channel(bridge_channel->chan);
-
-	/*
-	 * Directly locking the bridge is safe here because nobody else
-	 * knows about this bridge_channel yet.
-	 */
-	ast_bridge_lock(bridge_channel->bridge);
-
-	if (!bridge_channel->bridge->callid) {
-		bridge_channel->bridge->callid = ast_read_threadstorage_callid();
-	}
-
-	if (bridge_channel_push(bridge_channel)) {
-		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+	if (!bridge->reconfigured) {
+		return;
 	}
-	bridge_reconfigured(bridge_channel->bridge, 1);
-
-	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-		/*
-		 * Indicate a source change since this channel is entering the
-		 * bridge system only if the bridge technology is not MULTIMIX
-		 * capable.  The MULTIMIX technology has already done it.
-		 */
-		if (!(bridge_channel->bridge->technology->capabilities
-			& AST_BRIDGE_CAPABILITY_MULTIMIX)) {
-			ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE);
-		}
-
-		ast_bridge_unlock(bridge_channel->bridge);
-		bridge_channel_handle_join(bridge_channel);
-		while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
-			/* Wait for something to do. */
-			bridge_channel_wait(bridge_channel);
-		}
-		bridge_channel_handle_leave(bridge_channel);
-		ast_bridge_channel_lock_bridge(bridge_channel);
+	bridge->reconfigured = 0;
+	if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_SMART)
+		&& smart_bridge_operation(bridge)) {
+		/* Smart bridge failed. */
+		bridge_dissolve(bridge);
+		return;
 	}
+	bridge_complete_join(bridge);
 
-	bridge_channel_pull(bridge_channel);
-	bridge_reconfigured(bridge_channel->bridge, 1);
-
-	ast_bridge_unlock(bridge_channel->bridge);
-
-	/* Indicate a source change since this channel is leaving the bridge system. */
-	ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE);
-
-/* BUGBUG Revisit in regards to moving channels between bridges and local channel optimization. */
-/* BUGBUG This is where outgoing HOLD/UNHOLD memory should write UNHOLD to channel. */
-	/* Complete any partial DTMF digit before exiting the bridge. */
-	if (ast_channel_sending_dtmf_digit(bridge_channel->chan)) {
-		ast_bridge_end_dtmf(bridge_channel->chan,
-			ast_channel_sending_dtmf_digit(bridge_channel->chan),
-			ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end");
+	if (bridge->dissolved) {
+		return;
 	}
+	check_bridge_play_sounds(bridge);
+	set_bridge_peer_vars(bridge);
+	ast_bridge_publish_state(bridge);
 
-	/*
-	 * Wait for any dual redirect to complete.
-	 *
-	 * Must be done while "still in the bridge" for ast_async_goto()
-	 * to work right.
-	 */
-	while (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) {
-		sched_yield();
+	if (colp_update) {
+		bridge_reconfigured_connected_line_update(bridge);
 	}
-	ast_channel_lock(bridge_channel->chan);
-	ast_channel_internal_bridge_set(bridge_channel->chan, NULL);
-	ast_channel_unlock(bridge_channel->chan);
-
-	ast_bridge_channel_restore_formats(bridge_channel);
 }
 
 /*!
@@ -3872,7 +2125,7 @@ void ast_bridge_notify_masquerade(struct ast_channel *chan)
 
 	ast_bridge_channel_lock_bridge(bridge_channel);
 	bridge = bridge_channel->bridge;
-	if (bridge_channel == find_bridge_channel(bridge, chan)) {
+	if (bridge_channel == bridge_find_channel(bridge, chan)) {
 /* BUGBUG this needs more work.  The channels need to be made compatible again if the formats change. The bridge_channel thread needs to monitor for this case. */
 		/* The channel we want to notify is still in a bridge. */
 		bridge->v_table->notify_masquerade(bridge, bridge_channel);
@@ -4120,7 +2373,7 @@ int ast_bridge_remove(struct ast_bridge *bridge, struct ast_channel *chan)
 	ast_bridge_lock(bridge);
 
 	/* Try to find the channel that we want to remove */
-	if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
+	if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
 		ast_bridge_unlock(bridge);
 		return -1;
 	}
@@ -4157,7 +2410,7 @@ static void bridge_channel_change_bridge(struct ast_bridge_channel *bridge_chann
 	ao2_ref(old_bridge, -1);
 }
 
-void bridge_merge_do(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
+void bridge_do_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_bridge_channel **kick_me, unsigned int num_kick,
 	unsigned int optimized)
 {
 	struct ast_bridge_channel *bridge_channel;
@@ -4382,9 +2635,9 @@ static int bridge_merge_locked(struct ast_bridge *dst_bridge, struct ast_bridge
 
 		kick_them = ast_alloca(num_kick * sizeof(*kick_them));
 		for (idx = 0; idx < num_kick; ++idx) {
-			kick_them[num_to_kick] = find_bridge_channel(merge.src, kick_me[idx]);
+			kick_them[num_to_kick] = bridge_find_channel(merge.src, kick_me[idx]);
 			if (!kick_them[num_to_kick]) {
-				kick_them[num_to_kick] = find_bridge_channel(merge.dest, kick_me[idx]);
+				kick_them[num_to_kick] = bridge_find_channel(merge.dest, kick_me[idx]);
 			}
 			if (kick_them[num_to_kick]) {
 				++num_to_kick;
@@ -4398,7 +2651,7 @@ static int bridge_merge_locked(struct ast_bridge *dst_bridge, struct ast_bridge
 		}
 	}
 
-	bridge_merge_do(merge.dest, merge.src, kick_them, num_kick, 0);
+	bridge_do_merge(merge.dest, merge.src, kick_them, num_kick, 0);
 	return 0;
 }
 
@@ -4416,7 +2669,7 @@ int ast_bridge_merge(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridg
 	return res;
 }
 
-int bridge_move_do(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
+int bridge_do_move(struct ast_bridge *dst_bridge, struct ast_bridge_channel *bridge_channel, int attempt_recovery,
 	unsigned int optimized)
 {
 	struct ast_bridge *orig_bridge;
@@ -4508,7 +2761,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge *
 		return -1;
 	}
 
-	bridge_channel = find_bridge_channel(src_bridge, chan);
+	bridge_channel = bridge_find_channel(src_bridge, chan);
 	if (!bridge_channel) {
 		ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, channel not in bridge.\n",
 			ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid);
@@ -4529,7 +2782,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge *
 	if (swap) {
 		struct ast_bridge_channel *bridge_channel_swap;
 
-		bridge_channel_swap = find_bridge_channel(dst_bridge, swap);
+		bridge_channel_swap = bridge_find_channel(dst_bridge, swap);
 		if (!bridge_channel_swap) {
 			ast_debug(1, "Can't move channel %s from bridge %s into bridge %s, swap channel %s not in bridge.\n",
 				ast_channel_name(chan), src_bridge->uniqueid, dst_bridge->uniqueid,
@@ -4545,7 +2798,7 @@ static int bridge_move_locked(struct ast_bridge *dst_bridge, struct ast_bridge *
 	}
 
 	bridge_channel->swap = swap;
-	return bridge_move_do(dst_bridge, bridge_channel, attempt_recovery, 0);
+	return bridge_do_move(dst_bridge, bridge_channel, attempt_recovery, 0);
 }
 
 int ast_bridge_move(struct ast_bridge *dst_bridge, struct ast_bridge *src_bridge, struct ast_channel *chan, struct ast_channel *swap, int attempt_recovery)
@@ -4573,7 +2826,7 @@ int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan,
 		RAII_VAR(struct ast_bridge_channel *, bridge_channel, NULL, ao2_cleanup);
 
 		ast_bridge_lock_both(bridge, chan_bridge);
-		bridge_channel = find_bridge_channel(chan_bridge, chan);
+		bridge_channel = bridge_find_channel(chan_bridge, chan);
 		if (bridge_move_locked(bridge, chan_bridge, chan, NULL, 1)) {
 			ast_bridge_unlock(chan_bridge);
 			ast_bridge_unlock(bridge);
@@ -4634,22 +2887,6 @@ int ast_bridge_add_channel(struct ast_bridge *bridge, struct ast_channel *chan,
 	return 0;
 }
 
-struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel)
-{
-	struct ast_bridge *bridge = bridge_channel->bridge;
-	struct ast_bridge_channel *other = NULL;
-
-	if (bridge_channel->in_bridge && bridge->num_channels == 2) {
-		AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
-			if (other != bridge_channel) {
-				break;
-			}
-		}
-	}
-
-	return other;
-}
-
 static int bridge_allows_optimization(struct ast_bridge *bridge)
 {
 	return !(bridge->inhibit_merge
@@ -4873,7 +3110,7 @@ static int try_swap_optimize_out(struct ast_bridge *chan_bridge,
 			ast_set_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN);
 		}
 		other->swap = dst_bridge_channel->chan;
-		if (!bridge_move_do(dst_bridge, other, 1, 1)) {
+		if (!bridge_do_move(dst_bridge, other, 1, 1)) {
 			ast_bridge_change_state(src_bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
 			res = -1;
 			if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) {
@@ -4984,7 +3221,7 @@ static int try_merge_optimize_out(struct ast_bridge *chan_bridge,
 		pvt->callbacks->optimization_started(pvt);
 		ast_set_flag(pvt, AST_UNREAL_OPTIMIZE_BEGUN);
 	}
-	bridge_merge_do(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me), 1);
+	bridge_do_merge(merge.dest, merge.src, kick_me, ARRAY_LEN(kick_me), 1);
 	if (pvt && pvt->callbacks && pvt->callbacks->optimization_finished) {
 		pvt->callbacks->optimization_finished(pvt);
 	}
@@ -5076,7 +3313,7 @@ enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *c
  *
  * \return Nothing
  */
-static void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request)
+void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request)
 {
 	int new_request;
 
@@ -5092,18 +3329,6 @@ void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request)
 	ast_bridge_unlock(bridge);
 }
 
-struct ast_bridge *ast_bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request)
-{
-	struct ast_bridge *bridge;
-
-	ast_bridge_channel_lock_bridge(bridge_channel);
-	bridge = bridge_channel->bridge;
-	ao2_ref(bridge, +1);
-	bridge_merge_inhibit_nolock(bridge, request);
-	ast_bridge_unlock(bridge);
-	return bridge;
-}
-
 int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
 {
 	struct ast_bridge_channel *bridge_channel;
@@ -5113,7 +3338,7 @@ int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
 
 	ast_bridge_lock(bridge);
 
-	if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
+	if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
 		ast_bridge_unlock(bridge);
 		return -1;
 	}
@@ -5132,7 +3357,7 @@ int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
 
 	ast_bridge_lock(bridge);
 
-	if (!(bridge_channel = find_bridge_channel(bridge, chan))) {
+	if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
 		ast_bridge_unlock(bridge);
 		return -1;
 	}
@@ -6622,7 +4847,7 @@ static enum ast_transfer_result bridge_swap_attended_transfer(struct ast_bridge
 		&& !ast_test_flag(&bridged_to_source->features->feature_flags,
 			AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE)) {
 		bridged_to_source->swap = swap_channel;
-		if (bridge_move_do(dest_bridge, bridged_to_source, 1, 0)) {
+		if (bridge_do_move(dest_bridge, bridged_to_source, 1, 0)) {
 			return AST_BRIDGE_TRANSFER_FAIL;
 		}
 		/* Must kick the source channel out of its bridge. */
@@ -6678,12 +4903,12 @@ static enum ast_transfer_result two_bridge_attended_transfer(struct ast_channel
 		goto end;
 	case AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE:
 		final_bridge = to_transferee_bridge;
-		bridge_merge_do(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me), 0);
+		bridge_do_merge(to_transferee_bridge, to_target_bridge, kick_me, ARRAY_LEN(kick_me), 0);
 		res = AST_BRIDGE_TRANSFER_SUCCESS;
 		goto end;
 	case AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE:
 		final_bridge = to_target_bridge;
-		bridge_merge_do(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me), 0);
+		bridge_do_merge(to_target_bridge, to_transferee_bridge, kick_me, ARRAY_LEN(kick_me), 0);
 		res = AST_BRIDGE_TRANSFER_SUCCESS;
 		goto end;
 	case AST_BRIDGE_OPTIMIZE_PROHIBITED:
diff --git a/main/bridging_basic.c b/main/bridging_basic.c
index 3353066853..27cbce35ce 100644
--- a/main/bridging_basic.c
+++ b/main/bridging_basic.c
@@ -42,6 +42,7 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/file.h"
 #include "asterisk/app.h"
 #include "asterisk/bridging_internal.h"
+#include "asterisk/bridging_channel_internal.h"
 #include "asterisk/dial.h"
 #include "asterisk/stasis_bridging.h"
 
@@ -341,8 +342,8 @@ static int bridge_personality_normal_push(struct ast_bridge *self, struct ast_br
 		return -1;
 	}
 
-	ast_bridge_update_accountcodes(self, bridge_channel, swap);
-	ast_bridge_update_linkedids(self, bridge_channel, swap);
+	bridge_channel_update_accountcodes(bridge_channel, swap);
+	bridge_channel_update_linkedids(bridge_channel, swap);
 	return 0;
 }
 
@@ -1367,7 +1368,7 @@ static void bridge_unhold(struct ast_bridge *bridge)
 }
 
 /*!
- * \brief Wrapper for \ref bridge_move_do
+ * \brief Wrapper for \ref bridge_do_move
  */
 static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel *channel, struct ast_channel *swap)
 {
@@ -1386,7 +1387,7 @@ static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct a
 	bridge_channel->swap = swap;
 	ao2_unlock(bridge_channel);
 
-	res = bridge_move_do(dest, bridge_channel, 1, 0);
+	res = bridge_do_move(dest, bridge_channel, 1, 0);
 
 	ast_bridge_unlock(dest);
 	ast_bridge_unlock(src);
@@ -1395,7 +1396,7 @@ static int bridge_move(struct ast_bridge *dest, struct ast_bridge *src, struct a
 }
 
 /*!
- * \brief Wrapper for \ref bridge_merge_do
+ * \brief Wrapper for \ref bridge_do_merge
  */
 static void bridge_merge(struct ast_bridge *dest, struct ast_bridge *src, struct ast_channel **kick_channels, unsigned int num_channels)
 {
@@ -1409,9 +1410,9 @@ static void bridge_merge(struct ast_bridge *dest, struct ast_bridge *src, struct
 	for (i = 0; i < num_channels; ++i) {
 		struct ast_bridge_channel *kick_bridge_channel;
 
-		kick_bridge_channel = find_bridge_channel(src, kick_channels[i]);
+		kick_bridge_channel = bridge_find_channel(src, kick_channels[i]);
 		if (!kick_bridge_channel) {
-			kick_bridge_channel = find_bridge_channel(dest, kick_channels[i]);
+			kick_bridge_channel = bridge_find_channel(dest, kick_channels[i]);
 		}
 
 		/* It's possible (and fine) for the bridge channel to be NULL at this point if the
@@ -1425,7 +1426,7 @@ static void bridge_merge(struct ast_bridge *dest, struct ast_bridge *src, struct
 		kick_bridge_channels[num_bridge_channels++] = kick_bridge_channel;
 	}
 
-	bridge_merge_do(dest, src, kick_bridge_channels, num_bridge_channels, 0);
+	bridge_do_merge(dest, src, kick_bridge_channels, num_bridge_channels, 0);
 	ast_bridge_unlock(dest);
 	ast_bridge_unlock(src);
 }
@@ -2658,7 +2659,7 @@ static int feature_attended_transfer(struct ast_bridge *bridge, struct ast_bridg
 	}
 
 	ast_bridge_channel_write_hold(bridge_channel, NULL);
-	props->transferee_bridge = ast_bridge_channel_merge_inhibit(bridge_channel, +1);
+	props->transferee_bridge = bridge_channel_merge_inhibit(bridge_channel, +1);
 
 	/* Grab the extension to transfer to */
 	if (grab_transfer(bridge_channel->chan, exten, sizeof(exten), props->context)) {
@@ -2900,12 +2901,12 @@ struct ast_bridge *ast_bridge_basic_new(void)
 {
 	struct ast_bridge *bridge;
 
-	bridge = ast_bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_basic_v_table);
-	bridge = ast_bridge_base_init(bridge,
+	bridge = bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_basic_v_table);
+	bridge = bridge_base_init(bridge,
 		AST_BRIDGE_CAPABILITY_NATIVE | AST_BRIDGE_CAPABILITY_1TO1MIX
 			| AST_BRIDGE_CAPABILITY_MULTIMIX, NORMAL_FLAGS);
 	bridge = bridge_basic_personality_alloc(bridge);
-	bridge = ast_bridge_register(bridge);
+	bridge = bridge_register(bridge);
 	return bridge;
 }
 
diff --git a/main/bridging_channel.c b/main/bridging_channel.c
new file mode 100644
index 0000000000..4500dfb79d
--- /dev/null
+++ b/main/bridging_channel.c
@@ -0,0 +1,1838 @@
+/*
+ * 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 Bridging Channel API
+ *
+ * \author Joshua Colp <jcolp@digium.com>
+ * \author Richard Mudgett <rmudgett@digium.com>
+ * \author Matt Jordan <mjordan@digium.com>
+ *
+ */
+
+/*** MODULEINFO
+	<support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include <signal.h>
+
+#include "asterisk/heap.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/app.h"
+#include "asterisk/pbx.h"
+#include "asterisk/channel.h"
+#include "asterisk/timing.h"
+#include "asterisk/bridging.h"
+#include "asterisk/bridging_channel.h"
+#include "asterisk/bridging_channel_internal.h"
+#include "asterisk/bridging_internal.h"
+#include "asterisk/stasis_bridging.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/features_config.h"
+#include "asterisk/parking.h"
+
+
+struct ast_bridge *bridge_channel_merge_inhibit(struct ast_bridge_channel *bridge_channel, int request)
+{
+	struct ast_bridge *bridge;
+
+	ast_bridge_channel_lock_bridge(bridge_channel);
+	bridge = bridge_channel->bridge;
+	ao2_ref(bridge, +1);
+	bridge_merge_inhibit_nolock(bridge, request);
+	ast_bridge_unlock(bridge);
+	return bridge;
+}
+
+void ast_bridge_channel_lock_bridge(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge;
+
+	for (;;) {
+		/* Safely get the bridge pointer */
+		ast_bridge_channel_lock(bridge_channel);
+		bridge = bridge_channel->bridge;
+		ao2_ref(bridge, +1);
+		ast_bridge_channel_unlock(bridge_channel);
+
+		/* Lock the bridge and see if it is still the bridge we need to lock. */
+		ast_bridge_lock(bridge);
+		if (bridge == bridge_channel->bridge) {
+			ao2_ref(bridge, -1);
+			return;
+		}
+		ast_bridge_unlock(bridge);
+		ao2_ref(bridge, -1);
+	}
+}
+
+static void bridge_channel_poke(struct ast_bridge_channel *bridge_channel)
+{
+	if (!pthread_equal(pthread_self(), bridge_channel->thread)) {
+		while (bridge_channel->waiting) {
+			pthread_kill(bridge_channel->thread, SIGURG);
+			sched_yield();
+		}
+	}
+}
+
+void ast_bridge_change_state_nolock(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
+{
+/* BUGBUG need cause code for the bridge_channel leaving the bridge. */
+	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		return;
+	}
+
+	ast_debug(1, "Setting %p(%s) state from:%d to:%d\n",
+		bridge_channel, ast_channel_name(bridge_channel->chan), bridge_channel->state,
+		new_state);
+
+	/* Change the state on the bridge channel */
+	bridge_channel->state = new_state;
+
+	bridge_channel_poke(bridge_channel);
+}
+
+void ast_bridge_change_state(struct ast_bridge_channel *bridge_channel, enum ast_bridge_channel_state new_state)
+{
+	ast_bridge_channel_lock(bridge_channel);
+	ast_bridge_change_state_nolock(bridge_channel, new_state);
+	ast_bridge_channel_unlock(bridge_channel);
+}
+
+void bridge_channel_update_accountcodes(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	struct ast_bridge_channel *other = NULL;
+
+	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+		if (other == swap) {
+			continue;
+		}
+
+		if (!ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan)) && ast_strlen_zero(ast_channel_peeraccount(other->chan))) {
+			ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n",
+					ast_channel_accountcode(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
+			ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan));
+		}
+		if (!ast_strlen_zero(ast_channel_accountcode(other->chan)) && ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan))) {
+			ast_debug(1, "Setting peeraccount to %s for %s from data on channel %s\n",
+					ast_channel_accountcode(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
+			ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan));
+		}
+		if (!ast_strlen_zero(ast_channel_peeraccount(bridge_channel->chan)) && ast_strlen_zero(ast_channel_accountcode(other->chan))) {
+			ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n",
+					ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
+			ast_channel_accountcode_set(other->chan, ast_channel_peeraccount(bridge_channel->chan));
+		}
+		if (!ast_strlen_zero(ast_channel_peeraccount(other->chan)) && ast_strlen_zero(ast_channel_accountcode(bridge_channel->chan))) {
+			ast_debug(1, "Setting accountcode to %s for %s from data on channel %s\n",
+					ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
+			ast_channel_accountcode_set(bridge_channel->chan, ast_channel_peeraccount(other->chan));
+		}
+		if (bridge->num_channels == 2) {
+			if (strcmp(ast_channel_accountcode(bridge_channel->chan), ast_channel_peeraccount(other->chan))) {
+				ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n",
+						ast_channel_peeraccount(other->chan), ast_channel_peeraccount(bridge_channel->chan), ast_channel_name(other->chan), ast_channel_name(bridge_channel->chan));
+				ast_channel_peeraccount_set(other->chan, ast_channel_accountcode(bridge_channel->chan));
+			}
+			if (strcmp(ast_channel_accountcode(other->chan), ast_channel_peeraccount(bridge_channel->chan))) {
+				ast_debug(1, "Changing peeraccount from %s to %s on %s to match channel %s\n",
+						ast_channel_peeraccount(bridge_channel->chan), ast_channel_peeraccount(other->chan), ast_channel_name(bridge_channel->chan), ast_channel_name(other->chan));
+				ast_channel_peeraccount_set(bridge_channel->chan, ast_channel_accountcode(other->chan));
+			}
+		}
+	}
+}
+
+void bridge_channel_update_linkedids(struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
+{
+	struct ast_bridge_channel *other = NULL;
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	const char *oldest_linkedid = ast_channel_linkedid(bridge_channel->chan);
+
+	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+		if (other == swap) {
+			continue;
+		}
+		oldest_linkedid = ast_channel_oldest_linkedid(oldest_linkedid, ast_channel_linkedid(other->chan));
+	}
+
+	if (ast_strlen_zero(oldest_linkedid)) {
+		return;
+	}
+
+	ast_channel_linkedid_set(bridge_channel->chan, oldest_linkedid);
+	AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+		if (other == swap) {
+			continue;
+		}
+		ast_channel_linkedid_set(other->chan, oldest_linkedid);
+	}
+}
+
+int ast_bridge_channel_queue_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
+{
+	struct ast_frame *dup;
+	char nudge = 0;
+
+	if (bridge_channel->suspended
+		/* Also defer DTMF frames. */
+		&& fr->frametype != AST_FRAME_DTMF_BEGIN
+		&& fr->frametype != AST_FRAME_DTMF_END
+		&& !ast_is_deferrable_frame(fr)) {
+		/* Drop non-deferable frames when suspended. */
+		return 0;
+	}
+	if (fr->frametype == AST_FRAME_NULL) {
+		/* "Accept" the frame and discard it. */
+		return 0;
+	}
+
+	dup = ast_frdup(fr);
+	if (!dup) {
+		return -1;
+	}
+
+	ast_bridge_channel_lock(bridge_channel);
+	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		/* Drop frames on channels leaving the bridge. */
+		ast_bridge_channel_unlock(bridge_channel);
+		ast_frfree(dup);
+		return 0;
+	}
+
+	AST_LIST_INSERT_TAIL(&bridge_channel->wr_queue, dup, frame_list);
+	if (write(bridge_channel->alert_pipe[1], &nudge, sizeof(nudge)) != sizeof(nudge)) {
+		ast_log(LOG_ERROR, "We couldn't write alert pipe for %p(%s)... something is VERY wrong\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan));
+	}
+	ast_bridge_channel_unlock(bridge_channel);
+	return 0;
+}
+
+int ast_bridge_channel_queue_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = action,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
+}
+
+int ast_bridge_channel_queue_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_CONTROL,
+		.subclass.integer = control,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	return ast_bridge_channel_queue_frame(bridge_channel, &frame);
+}
+
+int ast_bridge_queue_everyone_else(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+	struct ast_bridge_channel *cur;
+	int not_written = -1;
+
+	if (frame->frametype == AST_FRAME_NULL) {
+		/* "Accept" the frame and discard it. */
+		return 0;
+	}
+
+	AST_LIST_TRAVERSE(&bridge->channels, cur, entry) {
+		if (cur == bridge_channel) {
+			continue;
+		}
+		if (!ast_bridge_channel_queue_frame(cur, frame)) {
+			not_written = 0;
+		}
+	}
+	return not_written;
+}
+
+void ast_bridge_channel_restore_formats(struct ast_bridge_channel *bridge_channel)
+{
+	/* Restore original formats of the channel as they came in */
+	if (ast_format_cmp(ast_channel_readformat(bridge_channel->chan), &bridge_channel->read_format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Bridge is returning %p(%s) to read format %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&bridge_channel->read_format));
+		if (ast_set_read_format(bridge_channel->chan, &bridge_channel->read_format)) {
+			ast_debug(1, "Bridge failed to return %p(%s) to read format %s\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan),
+				ast_getformatname(&bridge_channel->read_format));
+		}
+	}
+	if (ast_format_cmp(ast_channel_writeformat(bridge_channel->chan), &bridge_channel->write_format) == AST_FORMAT_CMP_NOT_EQUAL) {
+		ast_debug(1, "Bridge is returning %p(%s) to write format %s\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan),
+			ast_getformatname(&bridge_channel->write_format));
+		if (ast_set_write_format(bridge_channel->chan, &bridge_channel->write_format)) {
+			ast_debug(1, "Bridge failed to return %p(%s) to write format %s\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan),
+				ast_getformatname(&bridge_channel->write_format));
+		}
+	}
+}
+
+static int bridge_channel_write_frame(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+	ast_bridge_channel_lock_bridge(bridge_channel);
+/*
+ * BUGBUG need to implement a deferred write queue for when there is no peer channel in the bridge (yet or it was kicked).
+ *
+ * The tech decides if a frame needs to be pushed back for deferral.
+ * simple_bridge/native_bridge are likely the only techs that will do this.
+ */
+	bridge_channel->bridge->technology->write(bridge_channel->bridge, bridge_channel, frame);
+	ast_bridge_unlock(bridge_channel->bridge);
+
+	/*
+	 * Claim successful write to bridge.  If deferred frame
+	 * support is added, claim successfully deferred.
+	 */
+	return 0;
+}
+
+int ast_bridge_channel_write_action_data(struct ast_bridge_channel *bridge_channel, enum ast_bridge_action_type action, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = action,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	return bridge_channel_write_frame(bridge_channel, &frame);
+}
+
+int ast_bridge_channel_write_control_data(struct ast_bridge_channel *bridge_channel, enum ast_control_frame_type control, const void *data, size_t datalen)
+{
+	struct ast_frame frame = {
+		.frametype = AST_FRAME_CONTROL,
+		.subclass.integer = control,
+		.datalen = datalen,
+		.data.ptr = (void *) data,
+	};
+
+	return bridge_channel_write_frame(bridge_channel, &frame);
+}
+
+int ast_bridge_channel_write_hold(struct ast_bridge_channel *bridge_channel, const char *moh_class)
+{
+	RAII_VAR(struct ast_json *, blob, NULL, ast_json_unref);
+	size_t datalen;
+
+	if (!ast_strlen_zero(moh_class)) {
+		datalen = strlen(moh_class) + 1;
+
+		blob = ast_json_pack("{s: s}",
+			"musicclass", moh_class);
+	} else {
+		moh_class = NULL;
+		datalen = 0;
+	}
+
+	ast_channel_publish_blob(bridge_channel->chan, ast_channel_hold_type(), blob);
+	return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_HOLD,
+		moh_class, datalen);
+}
+
+int ast_bridge_channel_write_unhold(struct ast_bridge_channel *bridge_channel)
+{
+	ast_channel_publish_blob(bridge_channel->chan, ast_channel_unhold_type(), NULL);
+	return ast_bridge_channel_write_control_data(bridge_channel, AST_CONTROL_UNHOLD, NULL, 0);
+}
+
+static int run_app_helper(struct ast_channel *chan, const char *app_name, const char *app_args)
+{
+	int res = 0;
+
+	if (!strcasecmp("Gosub", app_name)) {
+		ast_app_exec_sub(NULL, chan, app_args, 0);
+	} else if (!strcasecmp("Macro", app_name)) {
+		ast_app_exec_macro(NULL, chan, app_args);
+	} else {
+		struct ast_app *app;
+
+		app = pbx_findapp(app_name);
+		if (!app) {
+			ast_log(LOG_WARNING, "Could not find application (%s)\n", app_name);
+		} else {
+			res = pbx_exec(chan, app, app_args);
+		}
+	}
+	return res;
+}
+
+/*!
+* \internal
+* \brief Handle bridge hangup event.
+* \since 12.0.0
+*
+* \param bridge_channel Which channel is hanging up.
+*
+* \return Nothing
+*/
+static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook;
+	struct ao2_iterator iter;
+
+	/* Run any hangup hooks. */
+	iter = ao2_iterator_init(features->hangup_hooks, 0);
+	for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
+		int remove_me;
+
+		remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
+		if (remove_me) {
+			ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n",
+				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+			ao2_unlink(features->hangup_hooks, hook);
+		}
+	}
+	ao2_iterator_destroy(&iter);
+
+	/* Default hangup action. */
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+}
+
+void ast_bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	if (moh_class) {
+		ast_bridge_channel_write_hold(bridge_channel, moh_class);
+	}
+	if (run_app_helper(bridge_channel->chan, app_name, S_OR(app_args, ""))) {
+		/* Break the bridge if the app returns non-zero. */
+		bridge_channel_handle_hangup(bridge_channel);
+	}
+	if (moh_class) {
+		ast_bridge_channel_write_unhold(bridge_channel);
+	}
+}
+
+
+struct bridge_run_app {
+	/*! Offset into app_name[] where the MOH class name starts.  (zero if no MOH) */
+	int moh_offset;
+	/*! Offset into app_name[] where the application argument string starts. (zero if no arguments) */
+	int app_args_offset;
+	/*! Application name to run. */
+	char app_name[0];
+};
+
+/*!
+ * \internal
+ * \brief Handle the run application bridge action.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to run the application on.
+ * \param data Action frame data to run the application.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_run_app(struct ast_bridge_channel *bridge_channel, struct bridge_run_app *data)
+{
+	ast_bridge_channel_run_app(bridge_channel, data->app_name,
+		data->app_args_offset ? &data->app_name[data->app_args_offset] : NULL,
+		data->moh_offset ? &data->app_name[data->moh_offset] : NULL);
+}
+
+static int payload_helper_app(ast_bridge_channel_post_action_data post_it,
+	struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	struct bridge_run_app *app_data;
+	size_t len_name = strlen(app_name) + 1;
+	size_t len_args = ast_strlen_zero(app_args) ? 0 : strlen(app_args) + 1;
+	size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1;
+	size_t len_data = sizeof(*app_data) + len_name + len_args + len_moh;
+
+	/* Fill in application run frame data. */
+	app_data = alloca(len_data);
+	app_data->app_args_offset = len_args ? len_name : 0;
+	app_data->moh_offset = len_moh ? len_name + len_args : 0;
+	strcpy(app_data->app_name, app_name);/* Safe */
+	if (len_args) {
+		strcpy(&app_data->app_name[app_data->app_args_offset], app_args);/* Safe */
+	}
+	if (moh_class) {
+		strcpy(&app_data->app_name[app_data->moh_offset], moh_class);/* Safe */
+	}
+
+	return post_it(bridge_channel, AST_BRIDGE_ACTION_RUN_APP, app_data, len_data);
+}
+
+int ast_bridge_channel_write_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	return payload_helper_app(ast_bridge_channel_write_action_data,
+		bridge_channel, app_name, app_args, moh_class);
+}
+
+int ast_bridge_channel_queue_app(struct ast_bridge_channel *bridge_channel, const char *app_name, const char *app_args, const char *moh_class)
+{
+	return payload_helper_app(ast_bridge_channel_queue_action_data,
+		bridge_channel, app_name, app_args, moh_class);
+}
+
+void ast_bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
+{
+	if (moh_class) {
+		ast_bridge_channel_write_hold(bridge_channel, moh_class);
+	}
+	if (custom_play) {
+		custom_play(bridge_channel, playfile);
+	} else {
+		ast_stream_and_wait(bridge_channel->chan, playfile, AST_DIGIT_NONE);
+	}
+	if (moh_class) {
+		ast_bridge_channel_write_unhold(bridge_channel);
+	}
+
+	/*
+	 * It may be necessary to resume music on hold after we finish
+	 * playing the announcment.
+	 *
+	 * XXX We have no idea what MOH class was in use before playing
+	 * the file. This method also fails to restore ringing indications.
+	 * the proposed solution is to create a resume_entertainment callback
+	 * for the bridge technology and execute it here.
+	 */
+	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_MOH)) {
+		ast_moh_start(bridge_channel->chan, NULL, NULL);
+	}
+}
+
+struct bridge_playfile {
+	/*! Call this function to play the playfile. (NULL if normal sound file to play) */
+	ast_bridge_custom_play_fn custom_play;
+	/*! Offset into playfile[] where the MOH class name starts.  (zero if no MOH)*/
+	int moh_offset;
+	/*! Filename to play. */
+	char playfile[0];
+};
+
+/*!
+ * \internal
+ * \brief Handle the playfile bridge action.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to play a file on.
+ * \param payload Action frame payload to play a file.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_playfile(struct ast_bridge_channel *bridge_channel, struct bridge_playfile *payload)
+{
+	ast_bridge_channel_playfile(bridge_channel, payload->custom_play, payload->playfile,
+		payload->moh_offset ? &payload->playfile[payload->moh_offset] : NULL);
+}
+
+static int payload_helper_playfile(ast_bridge_channel_post_action_data post_it,
+	struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
+{
+	struct bridge_playfile *payload;
+	size_t len_name = strlen(playfile) + 1;
+	size_t len_moh = !moh_class ? 0 : strlen(moh_class) + 1;
+	size_t len_payload = sizeof(*payload) + len_name + len_moh;
+
+	/* Fill in play file frame data. */
+	payload = alloca(len_payload);
+	payload->custom_play = custom_play;
+	payload->moh_offset = len_moh ? len_name : 0;
+	strcpy(payload->playfile, playfile);/* Safe */
+	if (moh_class) {
+		strcpy(&payload->playfile[payload->moh_offset], moh_class);/* Safe */
+	}
+
+	return post_it(bridge_channel, AST_BRIDGE_ACTION_PLAY_FILE, payload, len_payload);
+}
+
+int ast_bridge_channel_write_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
+{
+	return payload_helper_playfile(ast_bridge_channel_write_action_data,
+		bridge_channel, custom_play, playfile, moh_class);
+}
+
+int ast_bridge_channel_queue_playfile(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_play_fn custom_play, const char *playfile, const char *moh_class)
+{
+	return payload_helper_playfile(ast_bridge_channel_queue_action_data,
+		bridge_channel, custom_play, playfile, moh_class);
+}
+
+struct bridge_custom_callback {
+	/*! Call this function on the bridge channel thread. */
+	ast_bridge_custom_callback_fn callback;
+	/*! Size of the payload if it exists.  A number otherwise. */
+	size_t payload_size;
+	/*! Nonzero if the payload exists. */
+	char payload_exists;
+	/*! Payload to give to callback. */
+	char payload[0];
+};
+
+/*!
+ * \internal
+ * \brief Handle the do custom callback bridge action.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel to run the application on.
+ * \param data Action frame data to run the application.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_do_callback(struct ast_bridge_channel *bridge_channel, struct bridge_custom_callback *data)
+{
+	data->callback(bridge_channel, data->payload_exists ? data->payload : NULL, data->payload_size);
+}
+
+static int payload_helper_cb(ast_bridge_channel_post_action_data post_it,
+	struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+{
+	struct bridge_custom_callback *cb_data;
+	size_t len_data = sizeof(*cb_data) + (payload ? payload_size : 0);
+
+	/* Sanity check. */
+	if (!callback) {
+		ast_assert(0);
+		return -1;
+	}
+
+	/* Fill in custom callback frame data. */
+	cb_data = alloca(len_data);
+	cb_data->callback = callback;
+	cb_data->payload_size = payload_size;
+	cb_data->payload_exists = payload && payload_size;
+	if (cb_data->payload_exists) {
+		memcpy(cb_data->payload, payload, payload_size);/* Safe */
+	}
+
+	return post_it(bridge_channel, AST_BRIDGE_ACTION_CALLBACK, cb_data, len_data);
+}
+
+int ast_bridge_channel_write_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+{
+	return payload_helper_cb(ast_bridge_channel_write_action_data,
+		bridge_channel, callback, payload, payload_size);
+}
+
+int ast_bridge_channel_queue_callback(struct ast_bridge_channel *bridge_channel, ast_bridge_custom_callback_fn callback, const void *payload, size_t payload_size)
+{
+	return payload_helper_cb(ast_bridge_channel_queue_action_data,
+		bridge_channel, callback, payload, payload_size);
+}
+
+struct bridge_park {
+	int parker_uuid_offset;
+	int app_data_offset;
+	/* buffer used for holding those strings */
+	char parkee_uuid[0];
+};
+
+static void bridge_channel_park(struct ast_bridge_channel *bridge_channel, struct bridge_park *payload)
+{
+	ast_bridge_channel_park(bridge_channel, payload->parkee_uuid,
+		&payload->parkee_uuid[payload->parker_uuid_offset],
+		payload->app_data_offset ? &payload->parkee_uuid[payload->app_data_offset] : NULL);
+}
+
+static int payload_helper_park(ast_bridge_channel_post_action_data post_it,
+	struct ast_bridge_channel *bridge_channel,
+	const char *parkee_uuid,
+	const char *parker_uuid,
+	const char *app_data)
+{
+	struct bridge_park *payload;
+	size_t len_parkee_uuid = strlen(parkee_uuid) + 1;
+	size_t len_parker_uuid = strlen(parker_uuid) + 1;
+	size_t len_app_data = !app_data ? 0 : strlen(app_data) + 1;
+	size_t len_payload = sizeof(*payload) + len_parker_uuid + len_parkee_uuid + len_app_data;
+
+	payload = alloca(len_payload);
+	payload->app_data_offset = len_app_data ? len_parkee_uuid + len_parker_uuid : 0;
+	payload->parker_uuid_offset = len_parkee_uuid;
+	strcpy(payload->parkee_uuid, parkee_uuid);
+	strcpy(&payload->parkee_uuid[payload->parker_uuid_offset], parker_uuid);
+	if (app_data) {
+		strcpy(&payload->parkee_uuid[payload->app_data_offset], app_data);
+	}
+
+	return post_it(bridge_channel, AST_BRIDGE_ACTION_PARK, payload, len_payload);
+}
+
+int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, const char *parkee_uuid, const char *parker_uuid, const char *app_data)
+{
+	return payload_helper_park(ast_bridge_channel_write_action_data,
+		bridge_channel, parkee_uuid, parker_uuid, app_data);
+}
+
+static int bridge_channel_interval_ready(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook;
+	int ready;
+
+	ast_heap_wrlock(features->interval_hooks);
+	hook = ast_heap_peek(features->interval_hooks, 1);
+	ready = hook && ast_tvdiff_ms(hook->parms.timer.trip_time, ast_tvnow()) <= 0;
+	ast_heap_unlock(features->interval_hooks);
+
+	return ready;
+}
+
+int ast_bridge_notify_talking(struct ast_bridge_channel *bridge_channel, int started_talking)
+{
+	struct ast_frame action = {
+		.frametype = AST_FRAME_BRIDGE_ACTION,
+		.subclass.integer = started_talking
+			? AST_BRIDGE_ACTION_TALKING_START : AST_BRIDGE_ACTION_TALKING_STOP,
+	};
+
+	return ast_bridge_channel_queue_frame(bridge_channel, &action);
+}
+
+struct ast_bridge_channel *ast_bridge_channel_peer(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	struct ast_bridge_channel *other = NULL;
+
+	if (bridge_channel->in_bridge && bridge->num_channels == 2) {
+		AST_LIST_TRAVERSE(&bridge->channels, other, entry) {
+			if (other != bridge_channel) {
+				break;
+			}
+		}
+	}
+
+	return other;
+}
+
+struct ast_bridge_channel *bridge_find_channel(struct ast_bridge *bridge, struct ast_channel *chan)
+{
+	struct ast_bridge_channel *bridge_channel;
+
+	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
+		if (bridge_channel->chan == chan) {
+			break;
+		}
+	}
+
+	return bridge_channel;
+}
+
+/*!
+ * \internal
+ * \brief Suspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to suspend.
+ *
+ * \note This function assumes bridge_channel->bridge is locked.
+ *
+ * \return Nothing
+ */
+void bridge_channel_suspend_nolock(struct ast_bridge_channel *bridge_channel)
+{
+	bridge_channel->suspended = 1;
+	if (bridge_channel->in_bridge) {
+		--bridge_channel->bridge->num_active;
+	}
+
+	/* Get technology bridge threads off of the channel. */
+	if (bridge_channel->bridge->technology->suspend) {
+		bridge_channel->bridge->technology->suspend(bridge_channel->bridge, bridge_channel);
+	}
+}
+
+/*!
+ * \internal
+ * \brief Suspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to suspend.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_suspend(struct ast_bridge_channel *bridge_channel)
+{
+	ast_bridge_channel_lock_bridge(bridge_channel);
+	bridge_channel_suspend_nolock(bridge_channel);
+	ast_bridge_unlock(bridge_channel->bridge);
+}
+
+/*!
+ * \internal
+ * \brief Unsuspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to unsuspend.
+ *
+ * \note This function assumes bridge_channel->bridge is locked.
+ *
+ * \return Nothing
+ */
+void bridge_channel_unsuspend_nolock(struct ast_bridge_channel *bridge_channel)
+{
+	bridge_channel->suspended = 0;
+	if (bridge_channel->in_bridge) {
+		++bridge_channel->bridge->num_active;
+	}
+
+	/* Wake technology bridge threads to take care of channel again. */
+	if (bridge_channel->bridge->technology->unsuspend) {
+		bridge_channel->bridge->technology->unsuspend(bridge_channel->bridge, bridge_channel);
+	}
+
+	/* Wake suspended channel. */
+	ast_bridge_channel_lock(bridge_channel);
+	ast_cond_signal(&bridge_channel->cond);
+	ast_bridge_channel_unlock(bridge_channel);
+}
+
+/*!
+ * \internal
+ * \brief Unsuspend a channel from a bridge.
+ *
+ * \param bridge_channel Channel to unsuspend.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
+{
+	ast_bridge_channel_lock_bridge(bridge_channel);
+	bridge_channel_unsuspend_nolock(bridge_channel);
+	ast_bridge_unlock(bridge_channel->bridge);
+}
+
+/*! \brief Internal function that activates interval hooks on a bridge channel */
+static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_hook *hook;
+	struct timeval start;
+
+	ast_heap_wrlock(bridge_channel->features->interval_hooks);
+	start = ast_tvnow();
+	while ((hook = ast_heap_peek(bridge_channel->features->interval_hooks, 1))) {
+		int interval;
+		unsigned int execution_time;
+
+		if (ast_tvdiff_ms(hook->parms.timer.trip_time, start) > 0) {
+			ast_debug(1, "Hook %p on %p(%s) wants to happen in the future, stopping our traversal\n",
+				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+			break;
+		}
+		ao2_ref(hook, +1);
+		ast_heap_unlock(bridge_channel->features->interval_hooks);
+
+		ast_debug(1, "Executing hook %p on %p(%s)\n",
+			hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+		interval = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
+
+		ast_heap_wrlock(bridge_channel->features->interval_hooks);
+		if (ast_heap_peek(bridge_channel->features->interval_hooks,
+			hook->parms.timer.heap_index) != hook
+			|| !ast_heap_remove(bridge_channel->features->interval_hooks, hook)) {
+			/* Interval hook is already removed from the bridge_channel. */
+			ao2_ref(hook, -1);
+			continue;
+		}
+		ao2_ref(hook, -1);
+
+		if (interval < 0) {
+			ast_debug(1, "Removed interval hook %p from %p(%s)\n",
+				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+			ao2_ref(hook, -1);
+			continue;
+		}
+		if (interval) {
+			/* Set new interval for the hook. */
+			hook->parms.timer.interval = interval;
+		}
+
+		ast_debug(1, "Updating interval hook %p with interval %u on %p(%s)\n",
+			hook, hook->parms.timer.interval, bridge_channel,
+			ast_channel_name(bridge_channel->chan));
+
+		/* resetting start */
+		start = ast_tvnow();
+
+		/*
+		 * Resetup the interval hook for the next interval.  We may need
+		 * to skip over any missed intervals because the hook was
+		 * delayed or took too long.
+		 */
+		execution_time = ast_tvdiff_ms(start, hook->parms.timer.trip_time);
+		while (hook->parms.timer.interval < execution_time) {
+			execution_time -= hook->parms.timer.interval;
+		}
+		hook->parms.timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->parms.timer.interval - execution_time, 1000));
+		hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1);
+
+		if (ast_heap_push(bridge_channel->features->interval_hooks, hook)) {
+			/* Could not push the hook back onto the heap. */
+			ao2_ref(hook, -1);
+		}
+	}
+	ast_heap_unlock(bridge_channel->features->interval_hooks);
+}
+
+static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf)
+{
+	return ast_bridge_channel_write_action_data(bridge_channel,
+		AST_BRIDGE_ACTION_DTMF_STREAM, dtmf, strlen(dtmf) + 1);
+}
+
+/*!
+ * \brief Internal function that executes a feature on a bridge channel
+ * \note Neither the bridge nor the bridge_channel locks should be held when entering
+ * this function.
+ */
+static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook = NULL;
+	char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
+	size_t dtmf_len = 0;
+	unsigned int digit_timeout;
+	RAII_VAR(struct ast_features_general_config *, gen_cfg, NULL, ao2_cleanup);
+
+	ast_channel_lock(bridge_channel->chan);
+	gen_cfg = ast_get_chan_features_general_config(bridge_channel->chan);
+	if (!gen_cfg) {
+		ast_log(LOG_ERROR, "Unable to retrieve features configuration.\n");
+		ast_channel_unlock(bridge_channel->chan);
+		return;
+	}
+	digit_timeout = gen_cfg->featuredigittimeout;
+	ast_channel_unlock(bridge_channel->chan);
+
+	/* 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(ast_channel_flags(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. */
+	do {
+		int res;
+
+		/* If the above timed out simply exit */
+		res = ast_waitfordigit(bridge_channel->chan, digit_timeout);
+		if (!res) {
+			ast_debug(1, "DTMF feature string collection on %p(%s) timed out\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan));
+			break;
+		}
+		if (res < 0) {
+			ast_debug(1, "DTMF feature string collection failed on %p(%s) for some reason\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan));
+			break;
+		}
+
+/* BUGBUG need to record the duration of DTMF digits so when the string is played back, they are reproduced. */
+		/* 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 %p(%s) is now '%s'\n",
+			bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
+
+		/* See if a DTMF feature hook matches or can match */
+		hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY);
+		if (!hook) {
+			ast_debug(1, "No DTMF feature hooks on %p(%s) match '%s'\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
+			break;
+		}
+		if (strlen(hook->parms.dtmf.code) == dtmf_len) {
+			ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n",
+				hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
+			break;
+		}
+		ao2_ref(hook, -1);
+		hook = NULL;
+
+		/* Stop if we have reached the maximum length of a DTMF feature string. */
+	} while (dtmf_len < ARRAY_LEN(dtmf) - 1);
+
+	/* Since we are done bringing DTMF in return to using both begin and end frames */
+	ast_clear_flag(ast_channel_flags(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) {
+		int remove_me;
+
+		remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
+		if (remove_me) {
+			ast_debug(1, "DTMF hook %p is being removed from %p(%s)\n",
+				hook, bridge_channel, ast_channel_name(bridge_channel->chan));
+			ao2_unlink(features->dtmf_hooks, hook);
+		}
+		ao2_ref(hook, -1);
+
+		/*
+		 * If we are handing the channel off to an external hook for
+		 * ownership, we are not guaranteed what kind of state it will
+		 * come back in.  If the channel hungup, we need to detect that
+		 * here if the hook did not already change the state.
+		 */
+		if (bridge_channel->chan && ast_check_hangup_locked(bridge_channel->chan)) {
+			bridge_channel_handle_hangup(bridge_channel);
+		}
+	} else if (features->dtmf_passthrough) {
+		bridge_channel_write_dtmf_stream(bridge_channel, dtmf);
+	}
+}
+
+static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+
+	if (features->talker_cb) {
+		features->talker_cb(bridge_channel, features->talker_pvt_data, talking);
+	}
+}
+
+/*! \brief Internal function that plays back DTMF on a bridge channel */
+static void bridge_channel_dtmf_stream(struct ast_bridge_channel *bridge_channel, const char *dtmf)
+{
+	ast_debug(1, "Playing DTMF stream '%s' out to %p(%s)\n",
+		dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
+	ast_dtmf_stream(bridge_channel->chan, NULL, dtmf, 0, 0);
+}
+
+static void bridge_channel_blind_transfer(struct ast_bridge_channel *bridge_channel,
+		struct blind_transfer_data *blind_data)
+{
+	ast_async_goto(bridge_channel->chan, blind_data->context, blind_data->exten, 1);
+	bridge_channel_handle_hangup(bridge_channel);
+}
+
+static void after_bridge_move_channel(struct ast_channel *chan_bridged, void *data)
+{
+	RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
+	struct ast_party_connected_line connected_target;
+	unsigned char connected_line_data[1024];
+	int payload_size;
+
+	ast_party_connected_line_init(&connected_target);
+
+	ast_channel_lock(chan_target);
+	ast_party_connected_line_copy(&connected_target, ast_channel_connected(chan_target));
+	ast_channel_unlock(chan_target);
+	ast_party_id_reset(&connected_target.priv);
+
+	if (ast_channel_move(chan_target, chan_bridged)) {
+		ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
+		ast_party_connected_line_free(&connected_target);
+		return;
+	}
+
+	if ((payload_size = ast_connected_line_build_data(connected_line_data,
+		sizeof(connected_line_data), &connected_target, NULL)) != -1) {
+		struct ast_control_read_action_payload *frame_payload;
+		int frame_size;
+
+		frame_size = payload_size + sizeof(*frame_payload);
+		frame_payload = ast_alloca(frame_size);
+		frame_payload->action = AST_FRAME_READ_ACTION_CONNECTED_LINE_MACRO;
+		frame_payload->payload_size = payload_size;
+		memcpy(frame_payload->payload, connected_line_data, payload_size);
+		ast_queue_control_data(chan_target, AST_CONTROL_READ_ACTION, frame_payload, frame_size);
+	}
+
+	ast_party_connected_line_free(&connected_target);
+}
+
+static void after_bridge_move_channel_fail(enum ast_after_bridge_cb_reason reason, void *data)
+{
+	RAII_VAR(struct ast_channel *, chan_target, data, ao2_cleanup);
+
+	ast_log(LOG_WARNING, "Unable to complete transfer: %s\n",
+		ast_after_bridge_cb_reason_string(reason));
+	ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
+}
+
+static void bridge_channel_attended_transfer(struct ast_bridge_channel *bridge_channel,
+		const char *target_chan_name)
+{
+	RAII_VAR(struct ast_channel *, chan_target, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_channel *, chan_bridged, NULL, ao2_cleanup);
+
+	chan_target = ast_channel_get_by_name(target_chan_name);
+	if (!chan_target) {
+		/* Dang, it disappeared somehow */
+		bridge_channel_handle_hangup(bridge_channel);
+		return;
+	}
+
+	ast_bridge_channel_lock(bridge_channel);
+	chan_bridged = bridge_channel->chan;
+	ast_assert(chan_bridged != NULL);
+	ao2_ref(chan_bridged, +1);
+	ast_bridge_channel_unlock(bridge_channel);
+
+	if (ast_after_bridge_callback_set(chan_bridged, after_bridge_move_channel,
+		after_bridge_move_channel_fail, ast_channel_ref(chan_target))) {
+		ast_softhangup(chan_target, AST_SOFTHANGUP_DEV);
+
+		/* Release the ref we tried to pass to ast_after_bridge_callback_set(). */
+		ast_channel_unref(chan_target);
+	}
+	bridge_channel_handle_hangup(bridge_channel);
+}
+
+/*!
+ * \internal
+ * \brief Handle bridge channel bridge action frame.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to execute the action on.
+ * \param action What to do.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_handle_action(struct ast_bridge_channel *bridge_channel, struct ast_frame *action)
+{
+	switch (action->subclass.integer) {
+	case AST_BRIDGE_ACTION_INTERVAL:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_interval(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_FEATURE:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_feature(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_DTMF_STREAM:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_dtmf_stream(bridge_channel, action->data.ptr);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_TALKING_START:
+	case AST_BRIDGE_ACTION_TALKING_STOP:
+		bridge_channel_talking(bridge_channel,
+			action->subclass.integer == AST_BRIDGE_ACTION_TALKING_START);
+		break;
+	case AST_BRIDGE_ACTION_PLAY_FILE:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_playfile(bridge_channel, action->data.ptr);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_RUN_APP:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_run_app(bridge_channel, action->data.ptr);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_CALLBACK:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_do_callback(bridge_channel, action->data.ptr);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_PARK:
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_park(bridge_channel, action->data.ptr);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+		break;
+	case AST_BRIDGE_ACTION_BLIND_TRANSFER:
+		bridge_channel_blind_transfer(bridge_channel, action->data.ptr);
+		break;
+	case AST_BRIDGE_ACTION_ATTENDED_TRANSFER:
+		bridge_channel_attended_transfer(bridge_channel, action->data.ptr);
+		break;
+	default:
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Check if a bridge should dissolve and do it.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel causing the check.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \return Nothing
+ */
+static void bridge_dissolve_check(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+
+	if (bridge->dissolved) {
+		return;
+	}
+
+	if (!bridge->num_channels
+		&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_EMPTY)) {
+		/* Last channel leaving the bridge turns off the lights. */
+		bridge_dissolve(bridge);
+		return;
+	}
+
+	switch (bridge_channel->state) {
+	case AST_BRIDGE_CHANNEL_STATE_END:
+		/* Do we need to dissolve the bridge because this channel hung up? */
+		if (ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)
+			|| (bridge_channel->features->usable
+				&& ast_test_flag(&bridge_channel->features->feature_flags,
+					AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP))) {
+			bridge_dissolve(bridge);
+			return;
+		}
+		break;
+	default:
+		break;
+	}
+/* BUGBUG need to implement AST_BRIDGE_CHANNEL_FLAG_LONELY support here */
+}
+
+/*!
+ * \internal
+ * \brief Pull the bridge channel out of its current bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to pull.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \return Nothing
+ */
+void bridge_channel_pull(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+
+	if (!bridge_channel->in_bridge) {
+		return;
+	}
+	bridge_channel->in_bridge = 0;
+
+	ast_debug(1, "Bridge %s: pulling %p(%s)\n",
+		bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
+
+	ast_verb(3, "Channel %s left '%s' %s-bridge <%s>\n",
+		ast_channel_name(bridge_channel->chan),
+		bridge->technology->name,
+		bridge->v_table->name,
+		bridge->uniqueid);
+
+/* BUGBUG This is where incoming HOLD/UNHOLD memory should write UNHOLD into bridge. (if not local optimizing) */
+/* BUGBUG This is where incoming DTMF begin/end memory should write DTMF end into bridge. (if not local optimizing) */
+	if (!bridge_channel->just_joined) {
+		/* Tell the bridge technology we are leaving so they tear us down */
+		ast_debug(1, "Bridge %s: %p(%s) is leaving %s technology\n",
+			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
+			bridge->technology->name);
+		if (bridge->technology->leave) {
+			bridge->technology->leave(bridge, bridge_channel);
+		}
+	}
+
+	/* Remove channel from the bridge */
+	if (!bridge_channel->suspended) {
+		--bridge->num_active;
+	}
+	--bridge->num_channels;
+	AST_LIST_REMOVE(&bridge->channels, bridge_channel, entry);
+	bridge->v_table->pull(bridge, bridge_channel);
+
+	ast_bridge_channel_clear_roles(bridge_channel);
+
+	/* If we are not going to be hung up after leaving a bridge, and we were an
+	 * outgoing channel, clear the outgoing flag.
+	 */
+	if (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING)
+			&& (ast_channel_softhangup_internal_flag(bridge_channel->chan) &
+				(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE))) {
+		ast_clear_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_OUTGOING);
+	}
+
+	bridge_dissolve_check(bridge_channel);
+
+	bridge->reconfigured = 1;
+	ast_bridge_publish_leave(bridge, bridge_channel->chan);
+}
+
+/*!
+ * \internal
+ * \brief Push the bridge channel into its specified bridge.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to push.
+ *
+ * \note On entry, bridge_channel->bridge is already locked.
+ *
+ * \retval 0 on success.
+ * \retval -1 on failure.  The channel did not get pushed.
+ */
+int bridge_channel_push(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge *bridge = bridge_channel->bridge;
+	struct ast_bridge_channel *swap;
+
+	ast_assert(!bridge_channel->in_bridge);
+
+	swap = bridge_find_channel(bridge, bridge_channel->swap);
+	bridge_channel->swap = NULL;
+
+	if (swap) {
+		ast_debug(1, "Bridge %s: pushing %p(%s) by swapping with %p(%s)\n",
+			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
+			swap, ast_channel_name(swap->chan));
+	} else {
+		ast_debug(1, "Bridge %s: pushing %p(%s)\n",
+			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
+	}
+
+	/* Add channel to the bridge */
+	if (bridge->dissolved
+		|| bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT
+		|| (swap && swap->state != AST_BRIDGE_CHANNEL_STATE_WAIT)
+		|| bridge->v_table->push(bridge, bridge_channel, swap)
+		|| ast_bridge_channel_establish_roles(bridge_channel)) {
+		ast_debug(1, "Bridge %s: pushing %p(%s) into bridge failed\n",
+			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan));
+		ast_bridge_features_remove(bridge_channel->features, AST_BRIDGE_HOOK_REMOVE_ON_PULL);
+		return -1;
+	}
+	bridge_channel->in_bridge = 1;
+	bridge_channel->just_joined = 1;
+	AST_LIST_INSERT_TAIL(&bridge->channels, bridge_channel, entry);
+	++bridge->num_channels;
+	if (!bridge_channel->suspended) {
+		++bridge->num_active;
+	}
+
+	ast_verb(3, "Channel %s %s%s%s '%s' %s-bridge <%s>\n",
+		ast_channel_name(bridge_channel->chan),
+		swap ? "swapped with " : "joined",
+		swap ? ast_channel_name(swap->chan) : "",
+		swap ? " into" : "",
+		bridge->technology->name,
+		bridge->v_table->name,
+		bridge->uniqueid);
+
+	ast_bridge_publish_enter(bridge, bridge_channel->chan);
+	if (swap) {
+		ast_bridge_change_state(swap, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+		bridge_channel_pull(swap);
+	}
+
+	/* Clear any BLINDTRANSFER and ATTENDEDTRANSFER since the transfer has completed. */
+	pbx_builtin_setvar_helper(bridge_channel->chan, "BLINDTRANSFER", NULL);
+	pbx_builtin_setvar_helper(bridge_channel->chan, "ATTENDEDTRANSFER", NULL);
+
+	bridge->reconfigured = 1;
+	return 0;
+}
+
+/*!
+ * \internal
+ * \brief Handle bridge channel control frame action.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to execute the control frame action on.
+ * \param fr Control frame to handle.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_handle_control(struct ast_bridge_channel *bridge_channel, struct ast_frame *fr)
+{
+	struct ast_channel *chan;
+	struct ast_option_header *aoh;
+	int is_caller;
+	int intercept_failed;
+
+	chan = bridge_channel->chan;
+	switch (fr->subclass.integer) {
+	case AST_CONTROL_REDIRECTING:
+		is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
+		bridge_channel_suspend(bridge_channel);
+		intercept_failed = ast_channel_redirecting_sub(NULL, chan, fr, 1)
+			&& ast_channel_redirecting_macro(NULL, chan, fr, is_caller, 1);
+		bridge_channel_unsuspend(bridge_channel);
+		if (intercept_failed) {
+			ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+		}
+		break;
+	case AST_CONTROL_CONNECTED_LINE:
+		is_caller = !ast_test_flag(ast_channel_flags(chan), AST_FLAG_OUTGOING);
+		bridge_channel_suspend(bridge_channel);
+		intercept_failed = ast_channel_connected_line_sub(NULL, chan, fr, 1)
+			&& ast_channel_connected_line_macro(NULL, chan, fr, is_caller, 1);
+		bridge_channel_unsuspend(bridge_channel);
+		if (intercept_failed) {
+			ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+		}
+		break;
+	case AST_CONTROL_HOLD:
+	case AST_CONTROL_UNHOLD:
+/*
+ * BUGBUG bridge_channels should remember sending/receiving an outstanding HOLD to/from the bridge
+ *
+ * When the sending channel is pulled from the bridge it needs to write into the bridge an UNHOLD before being pulled.
+ * When the receiving channel is pulled from the bridge it needs to generate its own UNHOLD.
+ * Something similar needs to be done for DTMF begin/end.
+ */
+	case AST_CONTROL_VIDUPDATE:
+	case AST_CONTROL_SRCUPDATE:
+	case AST_CONTROL_SRCCHANGE:
+	case AST_CONTROL_T38_PARAMETERS:
+/* BUGBUG may have to do something with a jitter buffer for these. */
+		ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+		break;
+	case AST_CONTROL_OPTION:
+		/*
+		 * Forward option Requests, but only ones we know are safe These
+		 * are ONLY sent by chan_iax2 and I'm not convinced that they
+		 * are useful. I haven't deleted them entirely because I just am
+		 * not sure of the ramifications of removing them.
+		 */
+		aoh = fr->data.ptr;
+		if (aoh && aoh->flag == AST_OPTION_FLAG_REQUEST) {
+			switch (ntohs(aoh->option)) {
+			case AST_OPTION_TONE_VERIFY:
+			case AST_OPTION_TDD:
+			case AST_OPTION_RELAXDTMF:
+			case AST_OPTION_AUDIO_MODE:
+			case AST_OPTION_DIGIT_DETECT:
+			case AST_OPTION_FAX_DETECT:
+				ast_channel_setoption(chan, ntohs(aoh->option), aoh->data,
+					fr->datalen - sizeof(*aoh), 0);
+				break;
+			default:
+				break;
+			}
+		}
+		break;
+	case AST_CONTROL_ANSWER:
+		if (ast_channel_state(chan) != AST_STATE_UP) {
+			ast_answer(chan);
+		} else {
+			ast_indicate(chan, -1);
+		}
+		break;
+	default:
+		ast_indicate_data(chan, fr->subclass.integer, fr->data.ptr, fr->datalen);
+		break;
+	}
+}
+
+/*!
+ * \internal
+ * \brief Handle bridge channel write frame to channel.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to write outgoing frame.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_handle_write(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_frame *fr;
+	char nudge;
+
+	ast_bridge_channel_lock(bridge_channel);
+	if (read(bridge_channel->alert_pipe[0], &nudge, sizeof(nudge)) < 0) {
+		if (errno != EINTR && errno != EAGAIN) {
+			ast_log(LOG_WARNING, "read() failed for alert pipe on %p(%s): %s\n",
+				bridge_channel, ast_channel_name(bridge_channel->chan), strerror(errno));
+		}
+	}
+	fr = AST_LIST_REMOVE_HEAD(&bridge_channel->wr_queue, frame_list);
+	ast_bridge_channel_unlock(bridge_channel);
+	if (!fr) {
+		return;
+	}
+	switch (fr->frametype) {
+	case AST_FRAME_BRIDGE_ACTION:
+		bridge_channel_handle_action(bridge_channel, fr);
+		break;
+	case AST_FRAME_CONTROL:
+		bridge_channel_handle_control(bridge_channel, fr);
+		break;
+	case AST_FRAME_NULL:
+		break;
+	default:
+		/* Write the frame to the channel. */
+		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_SIMPLE;
+		ast_write(bridge_channel->chan, fr);
+		break;
+	}
+	ast_frfree(fr);
+}
+
+/*!
+ * \internal
+ * \brief Handle bridge channel interval expiration.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to check interval on.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_timer *interval_timer;
+
+	interval_timer = bridge_channel->features->interval_timer;
+	if (interval_timer) {
+		if (ast_wait_for_input(ast_timer_fd(interval_timer), 0) == 1) {
+			ast_timer_ack(interval_timer, 1);
+			if (bridge_channel_interval_ready(bridge_channel)) {
+/* BUGBUG since this is now only run by the channel thread, there is no need to queue the action once this intervals become a first class wait item in bridge_channel_wait(). */
+				struct ast_frame interval_action = {
+					.frametype = AST_FRAME_BRIDGE_ACTION,
+					.subclass.integer = AST_BRIDGE_ACTION_INTERVAL,
+				};
+
+				ast_bridge_channel_queue_frame(bridge_channel, &interval_action);
+			}
+		}
+	}
+}
+
+/*! \brief Internal function to handle DTMF from a channel */
+static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook;
+	char dtmf[2];
+
+/* BUGBUG the feature hook matching needs to be done here.  Any matching feature hook needs to be queued onto the bridge_channel.  Also the feature hook digit timeout needs to be handled. */
+/* BUGBUG the AMI atxfer action just sends DTMF end events to initiate DTMF atxfer and dial the extension.  Another reason the DTMF hook matching needs rework. */
+	/* 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 */
+	dtmf[0] = frame->subclass.integer;
+	dtmf[1] = '\0';
+	hook = ao2_find(features->dtmf_hooks, dtmf, OBJ_PARTIAL_KEY);
+	if (hook) {
+		struct ast_frame action = {
+			.frametype = AST_FRAME_BRIDGE_ACTION,
+			.subclass.integer = AST_BRIDGE_ACTION_FEATURE,
+		};
+
+		ast_frfree(frame);
+		frame = NULL;
+		ast_bridge_channel_queue_frame(bridge_channel, &action);
+		ao2_ref(hook, -1);
+	}
+
+	return frame;
+}
+
+
+/*!
+ * \internal
+ * \brief Feed notification that a frame is waiting on a channel into the bridging core
+ *
+ * \param bridge_channel Bridge channel the notification was received on
+ */
+static void bridge_handle_trip(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_frame *frame;
+
+	if (bridge_channel->features->mute) {
+		frame = ast_read_noaudio(bridge_channel->chan);
+	} else {
+		frame = ast_read(bridge_channel->chan);
+	}
+
+	if (!frame) {
+		bridge_channel_handle_hangup(bridge_channel);
+		return;
+	}
+	switch (frame->frametype) {
+	case AST_FRAME_CONTROL:
+		switch (frame->subclass.integer) {
+		case AST_CONTROL_HANGUP:
+			bridge_channel_handle_hangup(bridge_channel);
+			ast_frfree(frame);
+			return;
+/* BUGBUG This is where incoming HOLD/UNHOLD memory should register.  Write UNHOLD into bridge when this channel is pulled. */
+		default:
+			break;
+		}
+		break;
+	case AST_FRAME_DTMF_BEGIN:
+		frame = bridge_handle_dtmf(bridge_channel, frame);
+		if (!frame) {
+			return;
+		}
+		/* Fall through */
+	case AST_FRAME_DTMF_END:
+		if (!bridge_channel->features->dtmf_passthrough) {
+			ast_frfree(frame);
+			return;
+		}
+/* BUGBUG This is where incoming DTMF begin/end memory should register.  Write DTMF end into bridge when this channel is pulled. */
+		break;
+	default:
+		break;
+	}
+
+	/* Simply write the frame out to the bridge technology. */
+/* BUGBUG The tech is where AST_CONTROL_ANSWER hook should go. (early bridge) */
+/* BUGBUG The tech is where incoming BUSY/CONGESTION hangup should happen? (early bridge) */
+	bridge_channel_write_frame(bridge_channel, frame);
+	ast_frfree(frame);
+}
+
+/*!
+ * \internal
+ * \brief Wait for something to happen on the bridge channel and handle it.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Channel to wait.
+ *
+ * \note Each channel does writing/reading in their own thread.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel)
+{
+	int ms = -1;
+	int outfd;
+	struct ast_channel *chan;
+
+	/* Wait for data to either come from the channel or us to be signaled */
+	ast_bridge_channel_lock(bridge_channel);
+	if (bridge_channel->state != AST_BRIDGE_CHANNEL_STATE_WAIT) {
+	} else if (bridge_channel->suspended) {
+/* BUGBUG the external party use of suspended will go away as will these references because this is the bridge channel thread */
+		ast_debug(1, "Bridge %s: %p(%s) is going into a signal wait\n",
+			bridge_channel->bridge->uniqueid, bridge_channel,
+			ast_channel_name(bridge_channel->chan));
+		ast_cond_wait(&bridge_channel->cond, ao2_object_get_lockaddr(bridge_channel));
+	} else {
+		ast_debug(10, "Bridge %s: %p(%s) is going into a waitfor\n",
+			bridge_channel->bridge->uniqueid, bridge_channel,
+			ast_channel_name(bridge_channel->chan));
+		bridge_channel->waiting = 1;
+		ast_bridge_channel_unlock(bridge_channel);
+		outfd = -1;
+/* BUGBUG need to make the next expiring active interval setup ms timeout rather than holding up the chan reads. */
+		chan = ast_waitfor_nandfds(&bridge_channel->chan, 1,
+			&bridge_channel->alert_pipe[0], 1, NULL, &outfd, &ms);
+		bridge_channel->waiting = 0;
+		if (ast_channel_softhangup_internal_flag(bridge_channel->chan) & AST_SOFTHANGUP_UNBRIDGE) {
+			ast_channel_clear_softhangup(bridge_channel->chan, AST_SOFTHANGUP_UNBRIDGE);
+			ast_bridge_channel_lock_bridge(bridge_channel);
+			bridge_channel->bridge->reconfigured = 1;
+			bridge_reconfigured(bridge_channel->bridge, 0);
+			ast_bridge_unlock(bridge_channel->bridge);
+		}
+		ast_bridge_channel_lock(bridge_channel);
+		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_FRAME;
+		ast_bridge_channel_unlock(bridge_channel);
+		if (!bridge_channel->suspended
+			&& bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+			if (chan) {
+				bridge_channel_handle_interval(bridge_channel);
+				bridge_handle_trip(bridge_channel);
+			} else if (-1 < outfd) {
+				bridge_channel_handle_write(bridge_channel);
+			}
+		}
+		bridge_channel->activity = AST_BRIDGE_CHANNEL_THREAD_IDLE;
+		return;
+	}
+	ast_bridge_channel_unlock(bridge_channel);
+}
+
+/*!
+ * \internal
+ * \brief Handle bridge channel join event.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is joining.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_handle_join(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook;
+	struct ao2_iterator iter;
+
+	/* Run any join hooks. */
+	iter = ao2_iterator_init(features->join_hooks, AO2_ITERATOR_UNLINK);
+	hook = ao2_iterator_next(&iter);
+	if (hook) {
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		do {
+			hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
+			ao2_ref(hook, -1);
+		} while ((hook = ao2_iterator_next(&iter)));
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+	}
+	ao2_iterator_destroy(&iter);
+}
+
+/*!
+ * \internal
+ * \brief Handle bridge channel leave event.
+ * \since 12.0.0
+ *
+ * \param bridge_channel Which channel is leaving.
+ *
+ * \return Nothing
+ */
+static void bridge_channel_handle_leave(struct ast_bridge_channel *bridge_channel)
+{
+	struct ast_bridge_features *features = bridge_channel->features;
+	struct ast_bridge_hook *hook;
+	struct ao2_iterator iter;
+
+	/* Run any leave hooks. */
+	iter = ao2_iterator_init(features->leave_hooks, AO2_ITERATOR_UNLINK);
+	hook = ao2_iterator_next(&iter);
+	if (hook) {
+		bridge_channel_suspend(bridge_channel);
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		do {
+			hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
+			ao2_ref(hook, -1);
+		} while ((hook = ao2_iterator_next(&iter)));
+		ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
+		bridge_channel_unsuspend(bridge_channel);
+	}
+	ao2_iterator_destroy(&iter);
+}
+
+/*! \brief Join a channel to a bridge and handle anything the bridge may want us to do */
+void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
+{
+	ast_format_copy(&bridge_channel->read_format, ast_channel_readformat(bridge_channel->chan));
+	ast_format_copy(&bridge_channel->write_format, ast_channel_writeformat(bridge_channel->chan));
+
+	ast_debug(1, "Bridge %s: %p(%s) is joining\n",
+		bridge_channel->bridge->uniqueid,
+		bridge_channel, ast_channel_name(bridge_channel->chan));
+
+	/*
+	 * Get "in the bridge" before pushing the channel for any
+	 * masquerades on the channel to happen before bridging.
+	 */
+	ast_channel_lock(bridge_channel->chan);
+	ast_channel_internal_bridge_set(bridge_channel->chan, bridge_channel->bridge);
+	ast_channel_unlock(bridge_channel->chan);
+
+	/* Add the jitterbuffer if the channel requires it */
+	ast_jb_enable_for_channel(bridge_channel->chan);
+
+	/*
+	 * Directly locking the bridge is safe here because nobody else
+	 * knows about this bridge_channel yet.
+	 */
+	ast_bridge_lock(bridge_channel->bridge);
+
+	if (!bridge_channel->bridge->callid) {
+		bridge_channel->bridge->callid = ast_read_threadstorage_callid();
+	}
+
+	if (bridge_channel_push(bridge_channel)) {
+		ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_HANGUP);
+	}
+	bridge_reconfigured(bridge_channel->bridge, 1);
+
+	if (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+		/*
+		 * Indicate a source change since this channel is entering the
+		 * bridge system only if the bridge technology is not MULTIMIX
+		 * capable.  The MULTIMIX technology has already done it.
+		 */
+		if (!(bridge_channel->bridge->technology->capabilities
+			& AST_BRIDGE_CAPABILITY_MULTIMIX)) {
+			ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE);
+		}
+
+		ast_bridge_unlock(bridge_channel->bridge);
+		bridge_channel_handle_join(bridge_channel);
+		while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
+			/* Wait for something to do. */
+			bridge_channel_wait(bridge_channel);
+		}
+		bridge_channel_handle_leave(bridge_channel);
+		ast_bridge_channel_lock_bridge(bridge_channel);
+	}
+
+	bridge_channel_pull(bridge_channel);
+	bridge_reconfigured(bridge_channel->bridge, 1);
+
+	ast_bridge_unlock(bridge_channel->bridge);
+
+	/* Indicate a source change since this channel is leaving the bridge system. */
+	ast_indicate(bridge_channel->chan, AST_CONTROL_SRCCHANGE);
+
+/* BUGBUG Revisit in regards to moving channels between bridges and local channel optimization. */
+/* BUGBUG This is where outgoing HOLD/UNHOLD memory should write UNHOLD to channel. */
+	/* Complete any partial DTMF digit before exiting the bridge. */
+	if (ast_channel_sending_dtmf_digit(bridge_channel->chan)) {
+		ast_channel_end_dtmf(bridge_channel->chan,
+			ast_channel_sending_dtmf_digit(bridge_channel->chan),
+			ast_channel_sending_dtmf_tv(bridge_channel->chan), "bridge end");
+	}
+
+	/*
+	 * Wait for any dual redirect to complete.
+	 *
+	 * Must be done while "still in the bridge" for ast_async_goto()
+	 * to work right.
+	 */
+	while (ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_BRIDGE_DUAL_REDIRECT_WAIT)) {
+		sched_yield();
+	}
+	ast_channel_lock(bridge_channel->chan);
+	ast_channel_internal_bridge_set(bridge_channel->chan, NULL);
+	ast_channel_unlock(bridge_channel->chan);
+
+	ast_bridge_channel_restore_formats(bridge_channel);
+}
+
+void ast_bridge_channel_leave_bridge(struct ast_bridge_channel *bridge_channel)
+{
+	ast_bridge_change_state(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+}
+
+void ast_bridge_channel_leave_bridge_nolock(struct ast_bridge_channel *bridge_channel)
+{
+	ast_bridge_change_state_nolock(bridge_channel, AST_BRIDGE_CHANNEL_STATE_END);
+}
\ No newline at end of file
diff --git a/main/channel.c b/main/channel.c
index a1a3730baf..4738e37d47 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -6740,7 +6740,7 @@ void ast_do_masquerade(struct ast_channel *original)
 		 * The clonechan was sending a DTMF digit that was not completed
 		 * before the masquerade.
 		 */
-		ast_bridge_end_dtmf(original, clone_sending_dtmf_digit, clone_sending_dtmf_tv,
+		ast_channel_end_dtmf(original, clone_sending_dtmf_digit, clone_sending_dtmf_tv,
 			"masquerade");
 	}
 
@@ -10406,3 +10406,24 @@ int ast_channel_unsuppress(struct ast_channel *chan, unsigned int direction, enu
 
 	return 0;
 }
+
+void ast_channel_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why)
+{
+	int dead;
+	long duration;
+
+	ast_channel_lock(chan);
+	dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
+		|| (ast_channel_softhangup_internal_flag(chan)
+			& ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE));
+	ast_channel_unlock(chan);
+	if (dead) {
+		/* Channel is a zombie or a real hangup. */
+		return;
+	}
+
+	duration = ast_tvdiff_ms(ast_tvnow(), start);
+	ast_senddigit_end(chan, digit, duration);
+	ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n",
+		digit, ast_channel_name(chan), why, duration);
+}
\ No newline at end of file
diff --git a/main/features.c b/main/features.c
index 1150f7eb1c..1e578d2b9e 100644
--- a/main/features.c
+++ b/main/features.c
@@ -3161,27 +3161,6 @@ static void clear_dialed_interfaces(struct ast_channel *chan)
 	ast_channel_unlock(chan);
 }
 
-void ast_bridge_end_dtmf(struct ast_channel *chan, char digit, struct timeval start, const char *why)
-{
-	int dead;
-	long duration;
-
-	ast_channel_lock(chan);
-	dead = ast_test_flag(ast_channel_flags(chan), AST_FLAG_ZOMBIE)
-		|| (ast_channel_softhangup_internal_flag(chan)
-			& ~(AST_SOFTHANGUP_ASYNCGOTO | AST_SOFTHANGUP_UNBRIDGE));
-	ast_channel_unlock(chan);
-	if (dead) {
-		/* Channel is a zombie or a real hangup. */
-		return;
-	}
-
-	duration = ast_tvdiff_ms(ast_tvnow(), start);
-	ast_senddigit_end(chan, digit, duration);
-	ast_log(LOG_DTMF, "DTMF end '%c' simulated on %s due to %s, duration %ld ms\n",
-		digit, ast_channel_name(chan), why, duration);
-}
-
 /*!
  * \internal
  * \brief Setup bridge builtin features.
diff --git a/res/parking/parking_bridge.c b/res/parking/parking_bridge.c
index 75bd62bf9d..4c91998300 100644
--- a/res/parking/parking_bridge.c
+++ b/res/parking/parking_bridge.c
@@ -24,12 +24,13 @@
  */
 
 #include "asterisk.h"
-#include "asterisk/logger.h"
 #include "res_parking.h"
 #include "asterisk/astobj2.h"
-#include "asterisk/features.h"
+#include "asterisk/logger.h"
 #include "asterisk/say.h"
 #include "asterisk/term.h"
+#include "asterisk/features.h"
+#include "asterisk/bridging_internal.h"
 
 struct ast_bridge_parking
 {
@@ -447,11 +448,11 @@ struct ast_bridge *bridge_parking_new(struct parking_lot *bridge_lot)
 {
 	void *bridge;
 
-	bridge = ast_bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table);
-	bridge = ast_bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
+	bridge = bridge_alloc(sizeof(struct ast_bridge_parking), &ast_bridge_parking_v_table);
+	bridge = bridge_base_init(bridge, AST_BRIDGE_CAPABILITY_HOLDING,
 		AST_BRIDGE_FLAG_MERGE_INHIBIT_TO | AST_BRIDGE_FLAG_MERGE_INHIBIT_FROM
 		| AST_BRIDGE_FLAG_SWAP_INHIBIT_FROM);
 	bridge = ast_bridge_parking_init(bridge, bridge_lot);
-	bridge = ast_bridge_register(bridge);
+	bridge = bridge_register(bridge);
 	return bridge;
 }
diff --git a/res/parking/parking_bridge_features.c b/res/parking/parking_bridge_features.c
index aee4edbab2..e8ec1809bc 100644
--- a/res/parking/parking_bridge_features.c
+++ b/res/parking/parking_bridge_features.c
@@ -33,6 +33,9 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
 #include "asterisk/logger.h"
 #include "asterisk/pbx.h"
 #include "asterisk/bridging.h"
+#include "asterisk/bridging_internal.h"
+#include "asterisk/bridging_channel_internal.h"
+#include "asterisk/bridging_channel.h"
 #include "asterisk/bridging_features.h"
 #include "asterisk/features.h"
 #include "asterisk/say.h"
-- 
GitLab