Perform the initial renaming of the Bridging API
[asterisk/asterisk.git] / main / bridging.c
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: