Skip to content
Snippets Groups Projects
bridge.c 164 KiB
Newer Older
  • Learn to ignore specific revisions
  • /*
     * Asterisk -- An open source telephony toolkit.
     *
     * Copyright (C) 2007 - 2009, Digium, Inc.
     *
     * Joshua Colp <jcolp@digium.com>
     *
     * See http://www.asterisk.org for more information about
     * the Asterisk project. Please do not directly contact
     * any of the maintainers of this project for assistance;
     * the project provides a web site, mailing lists and IRC
     * channels for your use.
     *
     * This program is free software, distributed under the terms of
     * the GNU General Public License Version 2. See the LICENSE file
     * at the top of the source tree.
     */
    
    /*! \file
     *
    
     * \brief Bridging API
    
     *
     * \author Joshua Colp <jcolp@digium.com>
     */
    
    
    /*** MODULEINFO
    	<support_level>core</support_level>
     ***/
    
    
    /*** DOCUMENTATION
    	<manager name="BridgeTechnologyList" language="en_US">
    		<synopsis>
    			List available bridging technologies and their statuses.
    		</synopsis>
    		<syntax>
    			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
    		</syntax>
    		<description>
    			<para>Returns detailed information about the available bridging technologies.</para>
    		</description>
    
    		<see-also>
    			<ref type="manager">BridgeTechnologySuspend</ref>
    			<ref type="manager">BridgeTechnologyUnsuspend</ref>
    		</see-also>
    
    	</manager>
    	<manager name="BridgeTechnologySuspend" language="en_US">
    		<synopsis>
    			Suspend a bridging technology.
    		</synopsis>
    		<syntax>
    			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
    			<parameter name="BridgeTechnology" required="true">
    				<para>The name of the bridging technology to suspend.</para>
    			</parameter>
    		</syntax>
    		<description>
    			<para>Marks a bridging technology as suspended, which prevents subsequently created bridges from using it.</para>
    		</description>
    
    		<see-also>
    			<ref type="manager">BridgeTechnologySuspend</ref>
    			<ref type="manager">BridgeTechnologyUnsuspend</ref>
    		</see-also>
    
    	</manager>
    	<manager name="BridgeTechnologyUnsuspend" language="en_US">
    		<synopsis>
    			Unsuspend a bridging technology.
    		</synopsis>
    		<syntax>
    			<xi:include xpointer="xpointer(/docs/manager[@name='Login']/syntax/parameter[@name='ActionID'])" />
    			<parameter name="BridgeTechnology" required="true">
    				<para>The name of the bridging technology to unsuspend.</para>
    			</parameter>
    		</syntax>
    		<description>
    			<para>Clears a previously suspended bridging technology, which allows subsequently created bridges to use it.</para>
    		</description>
    
    		<see-also>
    			<ref type="manager">BridgeTechnologyList</ref>
    			<ref type="manager">BridgeTechnologySuspend</ref>
    		</see-also>
    
    #include "asterisk.h"
    
    #include "asterisk/logger.h"
    #include "asterisk/channel.h"
    #include "asterisk/options.h"
    #include "asterisk/utils.h"
    #include "asterisk/lock.h"
    #include "asterisk/linkedlists.h"
    
    #include "asterisk/bridge.h"
    #include "asterisk/bridge_internal.h"
    #include "asterisk/bridge_channel_internal.h"
    
    #include "asterisk/bridge_features.h"
    
    #include "asterisk/bridge_basic.h"
    #include "asterisk/bridge_technology.h"
    #include "asterisk/bridge_channel.h"
    #include "asterisk/bridge_after.h"
    #include "asterisk/stasis_bridges.h"
    
    #include "asterisk/stasis_channels.h"
    
    #include "asterisk/stasis_cache_pattern.h"
    
    #include "asterisk/app.h"
    #include "asterisk/file.h"
    #include "asterisk/module.h"
    #include "asterisk/astobj2.h"
    
    #include "asterisk/_private.h"
    #include "asterisk/heap.h"
    #include "asterisk/say.h"
    #include "asterisk/timing.h"
    #include "asterisk/stringfields.h"
    #include "asterisk/musiconhold.h"
    #include "asterisk/features.h"
    #include "asterisk/cli.h"
    #include "asterisk/parking.h"
    
    
    /*! All bridges container. */
    static struct ao2_container *bridges;
    
    
    static AST_RWLIST_HEAD_STATIC(bridge_technologies, ast_bridge_technology);
    
    
    static unsigned int optimization_id;
    
    
    /* Initial starting point for the bridge array of channels */
    #define BRIDGE_ARRAY_START 128
    
    /* Grow rate of bridge array of channels */
    #define BRIDGE_ARRAY_GROW 32
    
    
    /* Variable name - stores peer information about the most recent blind transfer */
    #define BLINDTRANSFER "BLINDTRANSFER"
    
    /* Variable name - stores peer information about the most recent attended transfer */
    #define ATTENDEDTRANSFER "ATTENDEDTRANSFER"
    
    
    static void cleanup_video_mode(struct ast_bridge *bridge);
    
    
    /*! Default DTMF keys for built in features */
    static char builtin_features_dtmf[AST_BRIDGE_BUILTIN_END][MAXIMUM_DTMF_FEATURE_STRING];
    
    /*! Function handlers for the built in features */
    
    static ast_bridge_hook_callback builtin_features_handlers[AST_BRIDGE_BUILTIN_END];
    
    /*! Function handlers for built in interval features */
    static ast_bridge_builtin_set_limits_fn builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_END];
    
    /*! Bridge manager service request */
    struct bridge_manager_request {
    	/*! List of bridge service requests. */
    	AST_LIST_ENTRY(bridge_manager_request) node;
    	/*! Refed bridge requesting service. */
    	struct ast_bridge *bridge;
    };
    
    struct bridge_manager_controller {
    	/*! Condition, used to wake up the bridge manager thread. */
    	ast_cond_t cond;
    	/*! Queue of bridge service requests. */
    	AST_LIST_HEAD_NOLOCK(, bridge_manager_request) service_requests;
    	/*! Manager thread */
    	pthread_t thread;
    	/*! TRUE if the manager needs to stop. */
    	unsigned int stop:1;
    };
    
    /*! Bridge manager controller. */
    static struct bridge_manager_controller *bridge_manager;
    
    
    struct ao2_container *ast_bridges(void)
    {
    	return ao2_bump(bridges);
    }
    
    
    /*!
     * \internal
     * \brief Request service for a bridge from the bridge manager.
     * \since 12.0.0
     *
     * \param bridge Requesting service.
     */
    static void bridge_manager_service_req(struct ast_bridge *bridge)
    {
    	struct bridge_manager_request *request;
    
    	ao2_lock(bridge_manager);
    	if (bridge_manager->stop) {
    		ao2_unlock(bridge_manager);
    		return;
    	}
    
    	/* Create the service request. */
    	request = ast_calloc(1, sizeof(*request));
    	if (!request) {
    		/* Well. This isn't good. */
    		ao2_unlock(bridge_manager);
    		return;
    	}
    	ao2_ref(bridge, +1);
    	request->bridge = bridge;
    
    	/* Put request into the queue and wake the bridge manager. */
    	AST_LIST_INSERT_TAIL(&bridge_manager->service_requests, request, node);
    	ast_cond_signal(&bridge_manager->cond);
    	ao2_unlock(bridge_manager);
    }
    
    
    int __ast_bridge_technology_register(struct ast_bridge_technology *technology, struct ast_module *module)
    {
    
    
    	/* Perform a sanity check to make sure the bridge technology conforms to our needed requirements */
    
    	if (ast_strlen_zero(technology->name)
    		|| !technology->capabilities
    		|| !technology->write) {
    		ast_log(LOG_WARNING, "Bridge technology %s failed registration sanity check.\n",
    			technology->name);
    
    		return -1;
    	}
    
    	AST_RWLIST_WRLOCK(&bridge_technologies);
    
    	/* Look for duplicate bridge technology already using this name, or already registered */
    	AST_RWLIST_TRAVERSE(&bridge_technologies, current, entry) {
    		if ((!strcasecmp(current->name, technology->name)) || (current == technology)) {
    
    			ast_log(LOG_WARNING, "A bridge technology of %s already claims to exist in our world.\n",
    				technology->name);
    
    			AST_RWLIST_UNLOCK(&bridge_technologies);
    			return -1;
    		}
    	}
    
    	/* Copy module pointer so reference counting can keep the module from unloading */
    	technology->mod = module;
    
    
    	/* Find the correct position to insert the technology. */
    	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&bridge_technologies, current, entry) {
    		/* Put the highest preference tech's first in the list. */
    		if (technology->preference >= current->preference) {
    			AST_RWLIST_INSERT_BEFORE_CURRENT(technology, entry);
    
    			break;
    		}
    	}
    	AST_RWLIST_TRAVERSE_SAFE_END;
    
    	if (!current) {
    		/* Insert our new bridge technology to the end of the list. */
    		AST_RWLIST_INSERT_TAIL(&bridge_technologies, technology, entry);
    	}
    
    
    	AST_RWLIST_UNLOCK(&bridge_technologies);
    
    
    	ast_verb(2, "Registered bridge technology %s\n", technology->name);
    
    
    	return 0;
    }
    
    int ast_bridge_technology_unregister(struct ast_bridge_technology *technology)
    {
    
    
    	AST_RWLIST_WRLOCK(&bridge_technologies);
    
    	/* Ensure the bridge technology is registered before removing it */
    	AST_RWLIST_TRAVERSE_SAFE_BEGIN(&bridge_technologies, current, entry) {
    		if (current == technology) {
    			AST_RWLIST_REMOVE_CURRENT(entry);
    
    			ast_verb(2, "Unregistered bridge technology %s\n", technology->name);
    
    
    	AST_RWLIST_UNLOCK(&bridge_technologies);
    
    	return current ? 0 : -1;
    }
    
    
     * \brief Put an action onto the specified bridge. Don't dup the action frame.
    
     * \param bridge What to queue the action on.
     * \param action What to do.
    
    static void bridge_queue_action_nodup(struct ast_bridge *bridge, struct ast_frame *action)
    
    	ast_debug(1, "Bridge %s: queueing action type:%u sub:%d\n",
    
    		bridge->uniqueid, action->frametype, action->subclass.integer);
    
    	ast_bridge_lock(bridge);
    	AST_LIST_INSERT_TAIL(&bridge->action_queue, action, frame_list);
    	ast_bridge_unlock(bridge);
    	bridge_manager_service_req(bridge);
    
    int ast_bridge_queue_action(struct ast_bridge *bridge, struct ast_frame *action)
    
    	struct ast_frame *dup;
    
    	dup = ast_frdup(action);
    	if (!dup) {
    		return -1;
    
    	bridge_queue_action_nodup(bridge, dup);
    	return 0;
    }
    
    void bridge_dissolve(struct ast_bridge *bridge, int cause)
    
    {
    	struct ast_bridge_channel *bridge_channel;
    
    	struct ast_frame action = {
    		.frametype = AST_FRAME_BRIDGE_ACTION,
    
    		.subclass.integer = BRIDGE_CHANNEL_ACTION_DEFERRED_DISSOLVING,
    
    	if (cause <= 0) {
    		cause = AST_CAUSE_NORMAL_CLEARING;
    	}
    	bridge->cause = cause;
    
    	ast_debug(1, "Bridge %s: dissolving bridge with cause %d(%s)\n",
    		bridge->uniqueid, cause, ast_cause2str(cause));
    
    
    	AST_LIST_TRAVERSE(&bridge->channels, bridge_channel, entry) {
    
    		ast_bridge_channel_leave_bridge(bridge_channel,
    			BRIDGE_CHANNEL_STATE_END_NO_DISSOLVE, cause);
    
    	/* Must defer dissolving bridge because it is already locked. */
    	ast_bridge_queue_action(bridge, &action);
    
     * \brief Check if a bridge should dissolve because of a stolen channel and do it.
    
     * \param bridge Bridge to check.
     * \param bridge_channel Stolen channel causing the check.  It is not in the bridge to check and may be in another bridge.
    
     * \note On entry, bridge and bridge_channel->bridge are already locked.
    
    static void bridge_dissolve_check_stolen(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
    
    	if (bridge->dissolved) {
    		return;
    	}
    
    	if (bridge_channel->features->usable
    		&& ast_test_flag(&bridge_channel->features->feature_flags,
    			AST_BRIDGE_CHANNEL_FLAG_DISSOLVE_HANGUP)) {
    		/* The stolen channel controlled the bridge it was stolen from. */
    
    		return;
    	}
    	if (bridge->num_channels < 2
    		&& ast_test_flag(&bridge->feature_flags, AST_BRIDGE_FLAG_DISSOLVE_HANGUP)) {
    		/*
    		 * The stolen channel has not left enough channels to keep the
    		 * bridge alive.  Assume the stolen channel hung up.
    		 */
    
    /*!
     * \internal
     * \brief Update connected line information after a bridge has been reconfigured.
     *
     * \param bridge The bridge itself.
     */
    static void bridge_reconfigured_connected_line_update(struct ast_bridge *bridge)
    {
    	struct ast_party_connected_line connected;
    	struct ast_bridge_channel *bridge_channel = AST_LIST_FIRST(&bridge->channels), *peer;
    	unsigned char data[1024];
    	size_t datalen;
    
    	if (!bridge_channel ||
    		!(bridge->technology->capabilities & (AST_BRIDGE_CAPABILITY_1TO1MIX | AST_BRIDGE_CAPABILITY_NATIVE)) ||
    		!(peer = ast_bridge_channel_peer(bridge_channel)) ||
    		ast_test_flag(ast_channel_flags(bridge_channel->chan), AST_FLAG_ZOMBIE) ||
    		ast_test_flag(ast_channel_flags(peer->chan), AST_FLAG_ZOMBIE) ||
    		ast_check_hangup_locked(bridge_channel->chan) ||
    		ast_check_hangup_locked(peer->chan)) {
    		return;
    	}
    
    	ast_party_connected_line_init(&connected);
    
    	ast_channel_lock(bridge_channel->chan);
    	ast_connected_line_copy_from_caller(&connected, ast_channel_caller(bridge_channel->chan));
    	ast_channel_unlock(bridge_channel->chan);
    
    	if ((datalen = ast_connected_line_build_data(data, sizeof(data), &connected, NULL)) != (size_t) -1) {
    		ast_bridge_channel_queue_control_data(peer, AST_CONTROL_CONNECTED_LINE, data, datalen);
    	}
    
    	ast_channel_lock(peer->chan);
    	ast_connected_line_copy_from_caller(&connected, ast_channel_caller(peer->chan));
    	ast_channel_unlock(peer->chan);
    
    	if ((datalen = ast_connected_line_build_data(data, sizeof(data), &connected, NULL)) != (size_t) -1) {
    		ast_bridge_channel_queue_control_data(bridge_channel, AST_CONTROL_CONNECTED_LINE, data, datalen);
    	}
    
    	ast_party_connected_line_free(&connected);
    }
    
    
     * \brief Complete joining a channel to the bridge.
    
     * \param bridge What to operate upon.
     * \param bridge_channel What is joining the bridge technology.
    
     * \note On entry, bridge is already locked.
    
    static void bridge_channel_complete_join(struct ast_bridge *bridge, struct ast_bridge_channel *bridge_channel)
    
    	/* 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)) {
    
    		/* We cannot leave the channel partially in the bridge so we must kick it out */
    		ast_debug(1, "Bridge %s: %p(%s) failed to join %s technology (Kicking it out)\n",
    
    			bridge->uniqueid, bridge_channel, ast_channel_name(bridge_channel->chan),
    			bridge->technology->name);
    
    		bridge_channel->just_joined = 1;
    
    		ast_bridge_channel_leave_bridge(bridge_channel, BRIDGE_CHANNEL_STATE_END, 0);
    
    	bridge_channel->just_joined = 0;
    
    
    	/*
    	 * When a channel joins the bridge its streams need to be mapped to the bridge's
    	 * media types vector. This way all streams map to the same media type index for
    	 * a given channel.
    	 */
    
    	if (bridge_channel->bridge->technology->stream_topology_changed) {
    		bridge_channel->bridge->technology->stream_topology_changed(
    			bridge_channel->bridge, bridge_channel);
    	} else {
    		ast_bridge_channel_stream_map(bridge_channel);
    	}
    
     * \brief Complete joining new channels to the bridge.
    
     * \param bridge Check for new channels on this bridge.
    
     * \note On entry, bridge is already locked.
    
    static void bridge_complete_join(struct ast_bridge *bridge)
    
    	struct ast_bridge_channel *bridge_channel;
    
    	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) {
    
    		bridge_channel_queue_deferred_frames(bridge_channel);
    
    		if (!bridge_channel->just_joined) {
    			continue;
    		}
    		bridge_channel_complete_join(bridge, bridge_channel);
    
    /*! \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 (%u <= %u). 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;
    		}
    
    		if (!ast_module_running_ref(current->mod)) {
    			ast_debug(1, "Bridge technology %s is not running, skipping.\n", current->name);
    			continue;
    		}
    		if (best) {
    			ast_module_unref(best->mod);
    		}
    
    	if (best) {
    		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.
     */
    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,
    
    		.creator = bridge->creator,
    		.name = bridge->name,
    		.uniqueid = 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);
    
     * \brief Handle bridge action frame.
    
     * \param bridge What to execute the action on.
     * \param action What to do.
     *
     * \note On entry, bridge is already locked.
    
    Alexander Traud's avatar
    Alexander Traud committed
     * \note Can be called by the bridge destructor
    
    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 BRIDGE_CHANNEL_ACTION_DEFERRED_TECH_DESTROY:
    
    		ast_bridge_unlock(bridge);
    		bridge_tech_deferred_destroy(bridge, action);
    		ast_bridge_lock(bridge);
    		break;
    
    	case BRIDGE_CHANNEL_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.
     */
    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 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) {
    
    		bridge_topics_destroy(bridge);
    
    	/* 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;
    
    	AST_VECTOR_FREE(&bridge->media_types);
    
    
    
    	cleanup_video_mode(bridge);
    
    	ast_string_field_free_memory(bridge);
    
    	ao2_cleanup(bridge->current_snapshot);
    
    struct ast_bridge *bridge_register(struct ast_bridge *bridge)
    
    	if (bridge) {
    		bridge->construction_completed = 1;
    
    		ast_bridge_lock(bridge);
    
    		ast_bridge_publish_state(bridge);
    
    		ast_bridge_unlock(bridge);
    
    		if (!ao2_link(bridges, bridge)) {
    
    			bridge = NULL;
    		}
    	}
    	return bridge;
    
    struct ast_bridge *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) {
    		return NULL;
    	}
    
    	if (ast_string_field_init(bridge, 80)) {
    		ao2_cleanup(bridge);
    		return NULL;
    
    	AST_VECTOR_INIT(&bridge->media_types, AST_MEDIA_TYPE_END);
    
    
    struct ast_bridge *bridge_base_init(struct ast_bridge *self, uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
    
    	char uuid_hold[AST_UUID_STR_LEN];
    
    
    	if (!self) {
    		return NULL;
    
    	if (!ast_strlen_zero(id)) {
    		ast_string_field_set(self, uniqueid, id);
    	} else {
    		ast_uuid_generate_str(uuid_hold, AST_UUID_STR_LEN);
    		ast_string_field_set(self, uniqueid, uuid_hold);
    	}
    
    	ast_string_field_set(self, creator, creator);
    	if (!ast_strlen_zero(creator)) {
    		ast_string_field_set(self, name, name);
    	}
    
    
    	ast_set_flag(&self->feature_flags, flags);
    	self->allowed_capabilities = capabilities;
    
    
    	if (!(flags & AST_BRIDGE_FLAG_INVISIBLE)) {
    		if (bridge_topics_init(self) != 0) {
    			ast_log(LOG_WARNING, "Bridge %s: Could not initialize topics\n",
    				self->uniqueid);
    			ao2_ref(self, -1);
    			return NULL;
    		}
    
    	/* 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 (!(flags & AST_BRIDGE_FLAG_INVISIBLE)) {
    		if (!ast_bridge_topic(self)) {
    			ao2_ref(self, -1);
    			return NULL;
    		}
    
    	self->creationtime = ast_tvnow();
    
    
     * \brief ast_bridge base class destructor.
    
     * \param self Bridge to operate upon.
     *
     * \note Stub because of nothing to do.
    
    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.
     */
    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)
    
     * \brief ast_bridge base pull method.
    
     * \param self Bridge to operate upon.
     * \param bridge_channel Bridge channel to pull.
     *
     * \note On entry, self is already locked.
    
    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);
    
     * \brief ast_bridge base notify_masquerade method.
    
     * \param self Bridge to operate upon.
     * \param bridge_channel Bridge channel that was masqueraded.
     *
     * \note On entry, self is already locked.
    
    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)
    
    /*!
     * \internal
     * \brief ast_bridge base push_peek method.
     * \since 13.2.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_peek(struct ast_bridge *self, struct ast_bridge_channel *bridge_channel, struct ast_bridge_channel *swap)
    {
    	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,
    
    	.push_peek = bridge_base_push_peek,
    
    struct ast_bridge *ast_bridge_base_new(uint32_t capabilities, unsigned int flags, const char *creator, const char *name, const char *id)
    
    	void *bridge;
    
    	bridge = bridge_alloc(sizeof(struct ast_bridge), &ast_bridge_base_v_table);
    
    	bridge = bridge_base_init(bridge, capabilities, flags, creator, name, id);
    
    	bridge = bridge_register(bridge);
    	return bridge;
    
    int ast_bridge_destroy(struct ast_bridge *bridge, int cause)
    
    	ast_debug(1, "Bridge %s: telling all channels to leave the party\n", bridge->uniqueid);
    	ast_bridge_lock(bridge);
    
    	ast_bridge_unlock(bridge);
    
    	ao2_ref(bridge, -1);
    
     * \brief Perform the smart bridge operation.
    
     * \param bridge Work on this bridge.
     *
     * \details
     * Basically see if a new bridge technology should be used instead
     * of the current one.
    
     * \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,
    
    		.creator = bridge->creator,
    		.name = bridge->name,
    		.uniqueid = bridge->uniqueid,
    
    		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