Skip to content
Snippets Groups Projects
bridge.c 162 KiB
Newer Older
  • Learn to ignore specific revisions
  • 	}
    
    	if (merge.dest == chan_bridge) {
    		return AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE;
    	} else {
    		return AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE;
    	}
    }
    
    
    /*!
     * \internal
     * \brief Adjust the bridge merge inhibit request count.
     * \since 12.0.0
     *
     * \param bridge What to operate on.
     * \param request Inhibit request increment.
     *     (Positive to add requests.  Negative to remove requests.)
     *
     * \note This function assumes bridge is locked.
     *
     * \return Nothing
     */
    
    void bridge_merge_inhibit_nolock(struct ast_bridge *bridge, int request)
    
    {
    	int new_request;
    
    	new_request = bridge->inhibit_merge + request;
    	ast_assert(0 <= new_request);
    	bridge->inhibit_merge = new_request;
    }
    
    void ast_bridge_merge_inhibit(struct ast_bridge *bridge, int request)
    {
    	ast_bridge_lock(bridge);
    	bridge_merge_inhibit_nolock(bridge, request);
    	ast_bridge_unlock(bridge);
    }
    
    int ast_bridge_suspend(struct ast_bridge *bridge, struct ast_channel *chan)
    {
    	struct ast_bridge_channel *bridge_channel;
    
    /* XXX ASTERISK-21271 the case of a disolved bridge while channel is suspended is not handled. */
    /* XXX ASTERISK-21271 suspend/unsuspend needs to be rethought. The caller must block until it has successfully suspended the channel for temporary control. */
    /* XXX ASTERISK-21271 external suspend/unsuspend needs to be eliminated. The channel may be playing a file at the time and stealing it then is not good. */
    
    	if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
    
    	bridge_channel_internal_suspend_nolock(bridge_channel);
    
    
    	ast_bridge_unlock(bridge);
    
    	return 0;
    }
    
    int ast_bridge_unsuspend(struct ast_bridge *bridge, struct ast_channel *chan)
    {
    	struct ast_bridge_channel *bridge_channel;
    
    /* XXX ASTERISK-21271 the case of a disolved bridge while channel is suspended is not handled. */
    
    	if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
    
    	bridge_channel_internal_unsuspend_nolock(bridge_channel);
    
    
    	ast_bridge_unlock(bridge);
    
    	return 0;
    }
    
    void ast_bridge_technology_suspend(struct ast_bridge_technology *technology)
    {
    	technology->suspended = 1;
    }
    
    void ast_bridge_technology_unsuspend(struct ast_bridge_technology *technology)
    {
    
    	/*
    	 * XXX We may want the act of unsuspending a bridge technology
    	 * to prod all existing bridges to see if they should start
    	 * using it.
    	 */
    
    	technology->suspended = 0;
    }
    
    int ast_bridge_features_register(enum ast_bridge_builtin_feature feature, ast_bridge_hook_callback callback, const char *dtmf)
    {
    	if (ARRAY_LEN(builtin_features_handlers) <= feature
    		|| builtin_features_handlers[feature]) {
    		return -1;
    	}
    
    	if (!ast_strlen_zero(dtmf)) {
    		ast_copy_string(builtin_features_dtmf[feature], dtmf, sizeof(builtin_features_dtmf[feature]));
    	}
    
    	builtin_features_handlers[feature] = callback;
    
    	return 0;
    }
    
    int ast_bridge_features_unregister(enum ast_bridge_builtin_feature feature)
    {
    	if (ARRAY_LEN(builtin_features_handlers) <= feature
    		|| !builtin_features_handlers[feature]) {
    		return -1;
    	}
    
    	builtin_features_handlers[feature] = NULL;
    
    	return 0;
    }
    
    
    int ast_bridge_features_do(enum ast_bridge_builtin_feature feature, struct ast_bridge_channel *bridge_channel, void *hook_pvt)
    
    {
    	ast_bridge_hook_callback callback;
    
    	if (ARRAY_LEN(builtin_features_handlers) <= feature) {
    		return -1;
    	}
    
    	callback = builtin_features_handlers[feature];
    	if (!callback) {
    		return -1;
    	}
    
    	callback(bridge_channel, hook_pvt);
    
    int ast_bridge_interval_register(enum ast_bridge_builtin_interval interval, ast_bridge_builtin_set_limits_fn callback)
    {
    	if (ARRAY_LEN(builtin_interval_handlers) <= interval
    		|| builtin_interval_handlers[interval]) {
    		return -1;
    	}
    
    	builtin_interval_handlers[interval] = callback;
    
    	return 0;
    }
    
    int ast_bridge_interval_unregister(enum ast_bridge_builtin_interval interval)
    {
    	if (ARRAY_LEN(builtin_interval_handlers) <= interval
    		|| !builtin_interval_handlers[interval]) {
    		return -1;
    	}
    
    	builtin_interval_handlers[interval] = NULL;
    
    	return 0;
    
    }
    
    /*!
     * \internal
     * \brief Bridge hook destructor.
     * \since 12.0.0
     *
     * \param vhook Object to destroy.
     *
     * \return Nothing
     */
    static void bridge_hook_destroy(void *vhook)
    {
    	struct ast_bridge_hook *hook = vhook;
    
    	if (hook->destructor) {
    		hook->destructor(hook->hook_pvt);
    	}
    }
    
    /*!
     * \internal
     * \brief Allocate and setup a generic bridge hook.
     * \since 12.0.0
     *
     * \param size How big an object to allocate.
     * \param callback Function to execute upon activation
     * \param hook_pvt Unique data
     * \param destructor Optional destructor callback for hook_pvt data
    
     * \param remove_flags Dictates what situations the hook should be removed.
    
     *
     * \retval hook on success.
     * \retval NULL on error.
     */
    static struct ast_bridge_hook *bridge_hook_generic(size_t size,
    	ast_bridge_hook_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    {
    	struct ast_bridge_hook *hook;
    
    	/* Allocate new hook and setup it's basic variables */
    	hook = ao2_alloc_options(size, bridge_hook_destroy, AO2_ALLOC_OPT_LOCK_NOLOCK);
    	if (hook) {
    		hook->callback = callback;
    		hook->destructor = destructor;
    		hook->hook_pvt = hook_pvt;
    
    		ast_set_flag(&hook->remove_flags, remove_flags);
    
    	}
    
    	return hook;
    }
    
    int ast_bridge_dtmf_hook(struct ast_bridge_features *features,
    	const char *dtmf,
    	ast_bridge_hook_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    	struct ast_bridge_hook_dtmf *hook;
    
    	int res;
    
    	/* Allocate new hook and setup it's various variables */
    
    	hook = (struct ast_bridge_hook_dtmf *) bridge_hook_generic(sizeof(*hook), callback,
    		hook_pvt, destructor, remove_flags);
    
    	hook->generic.type = AST_BRIDGE_HOOK_TYPE_DTMF;
    	ast_copy_string(hook->dtmf.code, dtmf, sizeof(hook->dtmf.code));
    
    
    	/* Once done we put it in the container. */
    	res = ao2_link(features->dtmf_hooks, hook) ? 0 : -1;
    
    	if (res) {
    		/*
    		 * Could not link the hook into the container.
    		 *
    		 * Remove the hook_pvt destructor call from the hook since we
    		 * are returning failure to install the hook.
    		 */
    
    		hook->generic.destructor = NULL;
    
    /*!
     * \internal
     * \brief Attach an other hook to a bridge features structure
     *
     * \param features Bridge features structure
     * \param callback Function to execute upon activation
     * \param hook_pvt Unique data
     * \param destructor Optional destructor callback for hook_pvt data
     * \param remove_flags Dictates what situations the hook should be removed.
     * \param type What type of hook is being attached.
     *
     * \retval 0 on success
     * \retval -1 on failure (The caller must cleanup any hook_pvt resources.)
     */
    static int bridge_other_hook(struct ast_bridge_features *features,
    
    	ast_bridge_hook_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    
    	enum ast_bridge_hook_remove_flags remove_flags,
    	enum ast_bridge_hook_type type)
    
    {
    	struct ast_bridge_hook *hook;
    	int res;
    
    	/* Allocate new hook and setup it's various variables */
    	hook = bridge_hook_generic(sizeof(*hook), callback, hook_pvt, destructor,
    
    	hook->type = type;
    
    	res = ao2_link(features->other_hooks, hook) ? 0 : -1;
    
    	if (res) {
    		/*
    		 * Could not link the hook into the container.
    		 *
    		 * Remove the hook_pvt destructor call from the hook since we
    		 * are returning failure to install the hook.
    		 */
    		hook->destructor = NULL;
    	}
    
    int ast_bridge_hangup_hook(struct ast_bridge_features *features,
    
    	ast_bridge_hook_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    	return bridge_other_hook(features, callback, hook_pvt, destructor, remove_flags,
    		AST_BRIDGE_HOOK_TYPE_HANGUP);
    }
    
    int ast_bridge_join_hook(struct ast_bridge_features *features,
    	ast_bridge_hook_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    	enum ast_bridge_hook_remove_flags remove_flags)
    {
    	return bridge_other_hook(features, callback, hook_pvt, destructor, remove_flags,
    		AST_BRIDGE_HOOK_TYPE_JOIN);
    
    }
    
    int ast_bridge_leave_hook(struct ast_bridge_features *features,
    	ast_bridge_hook_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    	return bridge_other_hook(features, callback, hook_pvt, destructor, remove_flags,
    		AST_BRIDGE_HOOK_TYPE_LEAVE);
    
    int ast_bridge_talk_detector_hook(struct ast_bridge_features *features,
    	ast_bridge_talking_indicate_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    	ast_bridge_hook_callback hook_cb = (ast_bridge_hook_callback) callback;
    
    	return bridge_other_hook(features, hook_cb, hook_pvt, destructor, remove_flags,
    		AST_BRIDGE_HOOK_TYPE_TALK);
    
    int ast_bridge_move_hook(struct ast_bridge_features *features,
    	ast_bridge_move_indicate_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    	enum ast_bridge_hook_remove_flags remove_flags)
    {
    	ast_bridge_hook_callback hook_cb = (ast_bridge_hook_callback) callback;
    
    	return bridge_other_hook(features, hook_cb, hook_pvt, destructor, remove_flags,
    		AST_BRIDGE_HOOK_TYPE_MOVE);
    }
    
    
    int ast_bridge_interval_hook(struct ast_bridge_features *features,
    
    	enum ast_bridge_hook_timer_option flags,
    
    	unsigned int interval,
    	ast_bridge_hook_callback callback,
    	void *hook_pvt,
    	ast_bridge_hook_pvt_destructor destructor,
    
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    	struct ast_bridge_hook_timer *hook;
    
    	int res;
    
    	if (!features ||!interval || !callback) {
    		return -1;
    	}
    
    	/* Allocate new hook and setup it's various variables */
    
    	hook = (struct ast_bridge_hook_timer *) bridge_hook_generic(sizeof(*hook), callback,
    		hook_pvt, destructor, remove_flags);
    
    	hook->generic.type = AST_BRIDGE_HOOK_TYPE_TIMER;
    	hook->timer.interval = interval;
    
    	hook->timer.trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(interval, 1000));
    
    	hook->timer.seqno = ast_atomic_fetchadd_int((int *) &features->interval_sequence, +1);
    
    
    	ast_debug(1, "Putting interval hook %p with interval %u in the heap on features %p\n",
    
    		hook, hook->timer.interval, features);
    
    	ast_heap_wrlock(features->interval_hooks);
    	res = ast_heap_push(features->interval_hooks, hook);
    
    	ast_heap_unlock(features->interval_hooks);
    
    		/*
    		 * Could not push the hook into the heap
    		 *
    		 * Remove the hook_pvt destructor call from the hook since we
    		 * are returning failure to install the hook.
    		 */
    
    		hook->generic.destructor = NULL;
    
    		ao2_ref(hook, -1);
    	}
    
    	return res ? -1 : 0;
    }
    
    int ast_bridge_features_enable(struct ast_bridge_features *features,
    	enum ast_bridge_builtin_feature feature,
    	const char *dtmf,
    	void *config,
    	ast_bridge_hook_pvt_destructor destructor,
    
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    {
    	if (ARRAY_LEN(builtin_features_handlers) <= feature
    		|| !builtin_features_handlers[feature]) {
    		return -1;
    	}
    
    	/* If no alternate DTMF stream was provided use the default one */
    	if (ast_strlen_zero(dtmf)) {
    		dtmf = builtin_features_dtmf[feature];
    		/* If no DTMF is still available (ie: it has been disabled) then error out now */
    		if (ast_strlen_zero(dtmf)) {
    
    			ast_debug(1, "Failed to enable built in feature %u on %p, no DTMF string is available for it.\n",
    
    				feature, features);
    			return -1;
    		}
    	}
    
    	/*
    	 * The rest is basically pretty easy.  We create another hook
    	 * using the built in feature's DTMF callback.  Easy as pie.
    	 */
    	return ast_bridge_dtmf_hook(features, dtmf, builtin_features_handlers[feature],
    
    		config, destructor, remove_flags);
    
    }
    
    int ast_bridge_features_limits_construct(struct ast_bridge_features_limits *limits)
    {
    	memset(limits, 0, sizeof(*limits));
    
    	if (ast_string_field_init(limits, 256)) {
    		return -1;
    	}
    
    	return 0;
    }
    
    void ast_bridge_features_limits_destroy(struct ast_bridge_features_limits *limits)
    {
    	ast_string_field_free_memory(limits);
    }
    
    
    int ast_bridge_features_set_limits(struct ast_bridge_features *features,
    
    	struct ast_bridge_features_limits *limits,
    	enum ast_bridge_hook_remove_flags remove_flags)
    
    {
    	if (builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_LIMITS]) {
    
    		ast_bridge_builtin_set_limits_fn callback;
    
    		callback = builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_LIMITS];
    		return callback(features, limits, remove_flags);
    
    	}
    
    	ast_log(LOG_ERROR, "Attempted to set limits without an AST_BRIDGE_BUILTIN_INTERVAL_LIMITS callback registered.\n");
    	return -1;
    }
    
    void ast_bridge_features_set_flag(struct ast_bridge_features *features, unsigned int flag)
    {
    	ast_set_flag(&features->feature_flags, flag);
    	features->usable = 1;
    }
    
    /*!
     * \internal
    
     * \brief ao2 object match hooks with appropriate remove_flags.
    
     * \retval CMP_MATCH if hook's remove_flags match the removal flags set.
    
    static int hook_remove_match(void *obj, void *arg, int flags)
    
    	enum ast_bridge_hook_remove_flags *remove_flags = arg;
    
    	if (ast_test_flag(&hook->remove_flags, *remove_flags)) {
    
     * \brief Remove all hooks with appropriate remove_flags in the container.
    
     * \since 12.0.0
     *
     * \param hooks Hooks container to work on.
    
     * \param remove_flags Determinator for whether hook is removed
    
    static void hooks_remove_container(struct ao2_container *hooks, enum ast_bridge_hook_remove_flags remove_flags)
    
    {
    	ao2_callback(hooks, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
    
     * \brief Remove all hooks in the heap with appropriate remove_flags set.
    
     * \since 12.0.0
     *
     * \param hooks Hooks heap to work on.
    
     * \param remove_flags Determinator for whether hook is removed
    
    static void hooks_remove_heap(struct ast_heap *hooks, enum ast_bridge_hook_remove_flags remove_flags)
    
    {
    	struct ast_bridge_hook *hook;
    	int changed;
    
    	ast_heap_wrlock(hooks);
    	do {
    		int idx;
    
    		changed = 0;
    		for (idx = ast_heap_size(hooks); idx; --idx) {
    			hook = ast_heap_peek(hooks, idx);
    
    			if (ast_test_flag(&hook->remove_flags, remove_flags)) {
    
    				ast_heap_remove(hooks, hook);
    				ao2_ref(hook, -1);
    				changed = 1;
    			}
    		}
    	} while (changed);
    	ast_heap_unlock(hooks);
    }
    
    
    void ast_bridge_features_remove(struct ast_bridge_features *features, enum ast_bridge_hook_remove_flags remove_flags)
    
    	hooks_remove_container(features->dtmf_hooks, remove_flags);
    
    	hooks_remove_container(features->other_hooks, remove_flags);
    
    	hooks_remove_heap(features->interval_hooks, remove_flags);
    
    	struct ast_bridge_hook_timer *hook_a = a;
    	struct ast_bridge_hook_timer *hook_b = b;
    
    	cmp = ast_tvcmp(hook_b->timer.trip_time, hook_a->timer.trip_time);
    
    	cmp = hook_b->timer.seqno - hook_a->timer.seqno;
    
    	return cmp;
    }
    
    /*!
     * \internal
     * \brief DTMF hook container sort comparison function.
     * \since 12.0.0
     *
     * \param obj_left pointer to the (user-defined part) of an object.
     * \param obj_right pointer to the (user-defined part) of an object.
     * \param flags flags from ao2_callback()
     *   OBJ_POINTER - if set, 'obj_right', is an object.
     *   OBJ_KEY - if set, 'obj_right', is a search key item that is not an object.
     *   OBJ_PARTIAL_KEY - if set, 'obj_right', is a partial search key item that is not an object.
     *
     * \retval <0 if obj_left < obj_right
     * \retval =0 if obj_left == obj_right
     * \retval >0 if obj_left > obj_right
     */
    static int bridge_dtmf_hook_sort(const void *obj_left, const void *obj_right, int flags)
    {
    
    	const struct ast_bridge_hook_dtmf *hook_left = obj_left;
    	const struct ast_bridge_hook_dtmf *hook_right = obj_right;
    
    	const char *right_key = obj_right;
    	int cmp;
    
    	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
    	default:
    	case OBJ_POINTER:
    
    		right_key = hook_right->dtmf.code;
    
    		cmp = strcasecmp(hook_left->dtmf.code, right_key);
    
    		cmp = strncasecmp(hook_left->dtmf.code, right_key, strlen(right_key));
    
    /*! \brief Callback for merging hook ao2_containers */
    static int merge_container_cb(void *obj, void *data, int flags)
    {
    	ao2_link(data, obj);
    	return 0;
    }
    
    /*! \brief Wrapper for interval hooks that calls into the wrapped hook */
    static int interval_wrapper_cb(struct ast_bridge_channel *bridge_channel, void *obj)
    {
    	struct ast_bridge_hook_timer *hook = obj;
    
    	return hook->generic.callback(bridge_channel, hook->generic.hook_pvt);
    }
    
    /*! \brief Destructor for the hook wrapper */
    static void interval_wrapper_pvt_dtor(void *obj)
    {
    	ao2_cleanup(obj);
    }
    
    /*! \brief Wrap the provided interval hook and add it to features */
    static void wrap_hook(struct ast_bridge_features *features, struct ast_bridge_hook_timer *hook)
    {
    	/* Break out of the current wrapper if it exists to avoid multiple layers */
    	if (hook->generic.callback == interval_wrapper_cb) {
    		hook = hook->generic.hook_pvt;
    	}
    
    	ast_bridge_interval_hook(features, hook->timer.flags, hook->timer.interval,
    		interval_wrapper_cb, ao2_bump(hook), interval_wrapper_pvt_dtor,
    		hook->generic.remove_flags.flags);
    }
    
    void ast_bridge_features_merge(struct ast_bridge_features *into, const struct ast_bridge_features *from)
    {
    	struct ast_bridge_hook_timer *hook;
    	int idx;
    
    	/* Merge hook containers */
    	ao2_callback(from->dtmf_hooks, 0, merge_container_cb, into->dtmf_hooks);
    	ao2_callback(from->other_hooks, 0, merge_container_cb, into->other_hooks);
    
    	/* Merge hook heaps */
    	ast_heap_wrlock(from->interval_hooks);
    	for (idx = 1; (hook = ast_heap_peek(from->interval_hooks, idx)); idx++) {
    		wrap_hook(into, hook);
    	}
    	ast_heap_unlock(from->interval_hooks);
    
    	/* Merge feature flags */
    	into->feature_flags.flags |= from->feature_flags.flags;
    	into->usable |= from->usable;
    
    	into->mute |= from->mute;
    	into->dtmf_passthrough |= from->dtmf_passthrough;
    }
    
    
    /* XXX ASTERISK-21271 make ast_bridge_features_init() static when make ast_bridge_join() requires features to be allocated. */
    
    int ast_bridge_features_init(struct ast_bridge_features *features)
    {
    	/* Zero out the structure */
    	memset(features, 0, sizeof(*features));
    
    	/* Initialize the DTMF hooks container */
    	features->dtmf_hooks = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX,
    		AO2_CONTAINER_ALLOC_OPT_DUPS_REPLACE, bridge_dtmf_hook_sort, NULL);
    	if (!features->dtmf_hooks) {
    		return -1;
    	}
    
    
    	/* Initialize the miscellaneous other hooks container */
    	features->other_hooks = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL,
    
    	if (!features->other_hooks) {
    
    		return -1;
    	}
    
    	/* Initialize the interval hooks heap */
    	features->interval_hooks = ast_heap_create(8, interval_hook_time_cmp,
    
    		offsetof(struct ast_bridge_hook_timer, timer.heap_index));
    
    	features->dtmf_passthrough = 1;
    
    
    /* XXX ASTERISK-21271 make ast_bridge_features_cleanup() static when make ast_bridge_join() requires features to be allocated. */
    
    void ast_bridge_features_cleanup(struct ast_bridge_features *features)
    {
    
    	struct ast_bridge_hook_timer *hook;
    
    
    	/* Destroy the interval hooks heap. */
    	if (features->interval_hooks) {
    		while ((hook = ast_heap_pop(features->interval_hooks))) {
    			ao2_ref(hook, -1);
    		}
    		features->interval_hooks = ast_heap_destroy(features->interval_hooks);
    	}
    
    
    	/* Destroy the miscellaneous other hooks container. */
    	ao2_cleanup(features->other_hooks);
    	features->other_hooks = NULL;
    
    
    	/* Destroy the DTMF hooks container. */
    	ao2_cleanup(features->dtmf_hooks);
    	features->dtmf_hooks = NULL;
    }
    
    void ast_bridge_features_destroy(struct ast_bridge_features *features)
    {
    	if (!features) {
    		return;
    	}
    	ast_bridge_features_cleanup(features);
    	ast_free(features);
    }
    
    struct ast_bridge_features *ast_bridge_features_new(void)
    {
    	struct ast_bridge_features *features;
    
    	features = ast_malloc(sizeof(*features));
    	if (features) {
    		if (ast_bridge_features_init(features)) {
    			ast_bridge_features_destroy(features);
    			features = NULL;
    		}
    	}
    
    	return features;
    }
    
    void ast_bridge_set_mixing_interval(struct ast_bridge *bridge, unsigned int mixing_interval)
    {
    	ast_bridge_lock(bridge);
    
    	bridge->softmix.internal_mixing_interval = mixing_interval;
    
    	ast_bridge_unlock(bridge);
    }
    
    void ast_bridge_set_internal_sample_rate(struct ast_bridge *bridge, unsigned int sample_rate)
    {
    	ast_bridge_lock(bridge);
    
    	bridge->softmix.internal_sample_rate = sample_rate;
    
    	ast_bridge_unlock(bridge);
    }
    
    static void cleanup_video_mode(struct ast_bridge *bridge)
    {
    
    	switch (bridge->softmix.video_mode.mode) {
    
    	case AST_BRIDGE_VIDEO_MODE_NONE:
    		break;
    	case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
    
    		if (bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc) {
    			ast_channel_unref(bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc);
    
    		if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc) {
    			ast_channel_unref(bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc);
    
    		if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc) {
    			ast_channel_unref(bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc);
    
    	memset(&bridge->softmix.video_mode, 0, sizeof(bridge->softmix.video_mode));
    
    }
    
    void ast_bridge_set_single_src_video_mode(struct ast_bridge *bridge, struct ast_channel *video_src_chan)
    {
    	ast_bridge_lock(bridge);
    	cleanup_video_mode(bridge);
    
    	bridge->softmix.video_mode.mode = AST_BRIDGE_VIDEO_MODE_SINGLE_SRC;
    	bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc = ast_channel_ref(video_src_chan);
    
    	ast_verb(5, "Video source in bridge '%s' (%s) is now '%s' (%s)\n",
    		bridge->name, bridge->uniqueid,
    		ast_channel_name(video_src_chan),
    		ast_channel_uniqueid(video_src_chan));
    
    	ast_indicate(video_src_chan, AST_CONTROL_VIDUPDATE);
    	ast_bridge_unlock(bridge);
    }
    
    void ast_bridge_set_talker_src_video_mode(struct ast_bridge *bridge)
    {
    	ast_bridge_lock(bridge);
    	cleanup_video_mode(bridge);
    
    	bridge->softmix.video_mode.mode = AST_BRIDGE_VIDEO_MODE_TALKER_SRC;
    
    	ast_bridge_unlock(bridge);
    }
    
    void ast_bridge_update_talker_src_video_mode(struct ast_bridge *bridge, struct ast_channel *chan, int talker_energy, int is_keyframe)
    {
    	struct ast_bridge_video_talker_src_data *data;
    
    	/* If the channel doesn't support video, we don't care about it */
    
    	if (!ast_format_cap_has_type(ast_channel_nativeformats(chan), AST_MEDIA_TYPE_VIDEO)) {
    
    	data = &bridge->softmix.video_mode.mode_data.talker_src_data;
    
    
    	if (data->chan_vsrc == chan) {
    		data->average_talking_energy = talker_energy;
    	} else if ((data->average_talking_energy < talker_energy) && is_keyframe) {
    		if (data->chan_old_vsrc) {
    			ast_channel_unref(data->chan_old_vsrc);
    		}
    		if (data->chan_vsrc) {
    			data->chan_old_vsrc = data->chan_vsrc;
    			ast_indicate(data->chan_old_vsrc, AST_CONTROL_VIDUPDATE);
    		}
    		data->chan_vsrc = ast_channel_ref(chan);
    		data->average_talking_energy = talker_energy;
    
    		ast_verb(5, "Video source in bridge '%s' (%s) is now '%s' (%s)\n",
    			bridge->name, bridge->uniqueid,
    			ast_channel_name(data->chan_vsrc),
    			ast_channel_uniqueid(data->chan_vsrc));
    
    		ast_indicate(data->chan_vsrc, AST_CONTROL_VIDUPDATE);
    	} else if ((data->average_talking_energy < talker_energy) && !is_keyframe) {
    		ast_indicate(chan, AST_CONTROL_VIDUPDATE);
    	} else if (!data->chan_vsrc && is_keyframe) {
    		data->chan_vsrc = ast_channel_ref(chan);
    		data->average_talking_energy = talker_energy;
    
    		ast_verb(5, "Video source in bridge '%s' (%s) is now '%s' (%s)\n",
    			bridge->name, bridge->uniqueid,
    			ast_channel_name(data->chan_vsrc),
    			ast_channel_uniqueid(data->chan_vsrc));
    
    		ast_indicate(chan, AST_CONTROL_VIDUPDATE);
    	} else if (!data->chan_old_vsrc && is_keyframe) {
    		data->chan_old_vsrc = ast_channel_ref(chan);
    		ast_indicate(chan, AST_CONTROL_VIDUPDATE);
    	}
    	ast_bridge_unlock(bridge);
    }
    
    int ast_bridge_number_video_src(struct ast_bridge *bridge)
    {
    	int res = 0;
    
    	ast_bridge_lock(bridge);
    
    	switch (bridge->softmix.video_mode.mode) {
    
    	case AST_BRIDGE_VIDEO_MODE_NONE:
    		break;
    	case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
    
    		if (bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc) {
    
    		if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc) {
    
    		if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc) {
    
    			res++;
    		}
    	}
    	ast_bridge_unlock(bridge);
    	return res;
    }
    
    int ast_bridge_is_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
    {
    	int res = 0;
    
    	ast_bridge_lock(bridge);
    
    	switch (bridge->softmix.video_mode.mode) {
    
    	case AST_BRIDGE_VIDEO_MODE_NONE:
    		break;
    	case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
    
    		if (bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc == chan) {
    
    		if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc == chan) {
    
    		} else if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc == chan) {
    
    			res = 2;
    		}
    
    	}
    	ast_bridge_unlock(bridge);
    	return res;
    }
    
    void ast_bridge_remove_video_src(struct ast_bridge *bridge, struct ast_channel *chan)
    {
    	ast_bridge_lock(bridge);
    
    	switch (bridge->softmix.video_mode.mode) {
    
    	case AST_BRIDGE_VIDEO_MODE_NONE:
    		break;
    	case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
    
    		if (bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc == chan) {
    			if (bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc) {
    				ast_channel_unref(bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc);
    
    			bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc = NULL;
    
    		if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc == chan) {
    			if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc) {
    				ast_channel_unref(bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc);
    
    			bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc = NULL;
    			bridge->softmix.video_mode.mode_data.talker_src_data.average_talking_energy = 0;
    
    		if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc == chan) {
    			if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc) {
    				ast_channel_unref(bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc);
    
    			bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc = NULL;
    
    const char *ast_bridge_video_mode_to_string(enum ast_bridge_video_mode_type video_mode)
    {
    	switch (video_mode) {
    	case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
    		return "talker";
    	case AST_BRIDGE_VIDEO_MODE_SINGLE_SRC:
    		return "single";
    	case AST_BRIDGE_VIDEO_MODE_NONE:
    	default:
    		return "none";
    	}
    }
    
    
    static int channel_hash(const void *obj, int flags)
    {
    	const struct ast_channel *chan = obj;
    	const char *name = obj;
    	int hash;
    
    	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
    	default:
    	case OBJ_POINTER:
    		name = ast_channel_name(chan);
    		/* Fall through */
    	case OBJ_KEY:
    		hash = ast_str_hash(name);
    		break;
    	case OBJ_PARTIAL_KEY:
    		/* Should never happen in hash callback. */
    		ast_assert(0);
    		hash = 0;
    		break;
    
    static int channel_cmp(void *obj, void *arg, int flags)
    
    	const struct ast_channel *left = obj;
    	const struct ast_channel *right = arg;
    	const char *right_name = arg;
    	int cmp;
    
    	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
    	default:
    	case OBJ_POINTER:
    		right_name = ast_channel_name(right);
    		/* Fall through */
    	case OBJ_KEY:
    		cmp = strcmp(ast_channel_name(left), right_name);
    		break;
    	case OBJ_PARTIAL_KEY:
    		cmp = strncmp(ast_channel_name(left), right_name, strlen(right_name));
    		break;
    
    struct ao2_container *ast_bridge_peers_nolock(struct ast_bridge *bridge)
    
    	struct ao2_container *channels;
    	struct ast_bridge_channel *iter;
    
    	channels = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK,
    		13, channel_hash, channel_cmp);
    	if (!channels) {
    		return NULL;
    
    
    	AST_LIST_TRAVERSE(&bridge->channels, iter, entry) {
    		ao2_link(channels, iter->chan);
    
    struct ao2_container *ast_bridge_peers(struct ast_bridge *bridge)
    {