Perform the initial renaming of the Bridging API
authorMatthew Jordan <mjordan@digium.com>
Wed, 24 Jul 2013 15:38:18 +0000 (15:38 +0000)
committerMatthew Jordan <mjordan@digium.com>
Wed, 24 Jul 2013 15:38:18 +0000 (15:38 +0000)
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

17 files changed:
apps/app_agent_pool.c
bridges/bridge_builtin_features.c
bridges/bridge_builtin_interval_features.c
include/asterisk/bridging.h
include/asterisk/bridging_channel.h [new file with mode: 0644]
include/asterisk/bridging_channel_internal.h [new file with mode: 0644]
include/asterisk/bridging_internal.h
include/asterisk/bridging_technology.h
include/asterisk/channel.h
include/asterisk/features.h
main/bridging.c
main/bridging_basic.c
main/bridging_channel.c [new file with mode: 0644]
main/channel.c
main/features.c
res/parking/parking_bridge.c
res/parking/parking_bridge_features.c

index 92209e1..fb73d86 100644 (file)
@@ -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);
        }
 
index 87b74d4..8554495 100644 (file)
@@ -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;
 }
 
index a4fd97b..2ca3f7d 100644 (file)
@@ -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;
index 0bba614..bc16ae0 100644 (file)
@@ -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,76 +310,10 @@ 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
  *
  * \param capabilities The capabilities that we require to be used on the bridge
@@ -825,20 +614,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
  *
  * \param bridge Bridge to suspend the channel from
@@ -943,101 +718,6 @@ enum ast_bridge_optimization ast_bridges_allow_optimization(struct ast_bridge *c
                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
  *
@@ -1053,37 +733,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
  *
@@ -1100,319 +749,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 (file)
index 0000000..dc55903
--- /dev/null
@@ -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 (file)
index 0000000..8f5eb4d
--- /dev/null
@@ -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
index 132c42b..cafa693 100644 (file)
  * \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:
@@ -33,6 +38,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.
  * \since 12.0.0
@@ -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 */
index 4e68067..e037b74 100644 (file)
@@ -42,6 +42,22 @@ enum ast_bridge_preference {
 };
 
 /*!
+ * \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
  */
 struct ast_bridge_technology {
index d8db470..f6cdd4e 100644 (file)
@@ -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 */
index 162a7fc..96eec98 100644 (file)
@@ -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);
 
index fe38d4c..5f001d1 100644 (file)
@@ -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 = {
@@ -526,49 +277,6 @@ static void bridge_dissolve(struct ast_bridge *bridge)
 
 /*!
  * \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.
  * \since 12.0.0
  *
@@ -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:
index 3353066..27cbce3 100644 (file)
@@ -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 (file)
index 0000000..4500dfb
--- /dev/null
@@ -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
index a1a3730..4738e37 100644 (file)
@@ -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
index 1150f7e..1e578d2 100644 (file)
@@ -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.
index 75bd62b..4c91998 100644 (file)
  */
 
 #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;
 }
index aee4edb..e8ec180 100644 (file)
@@ -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"