Newer
Older
Mark Michelson
committed
}
if (merge.dest == chan_bridge) {
return AST_BRIDGE_OPTIMIZE_MERGE_TO_CHAN_BRIDGE;
} else {
return AST_BRIDGE_OPTIMIZE_MERGE_TO_PEER_BRIDGE;
}
}
Richard Mudgett
committed
/*!
* \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)
Richard Mudgett
committed
{
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. */
Richard Mudgett
committed
ast_bridge_lock(bridge);
if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
Richard Mudgett
committed
ast_bridge_unlock(bridge);
return -1;
}
bridge_channel_internal_suspend_nolock(bridge_channel);
Richard Mudgett
committed
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. */
Richard Mudgett
committed
ast_bridge_lock(bridge);
if (!(bridge_channel = bridge_find_channel(bridge, chan))) {
Richard Mudgett
committed
ast_bridge_unlock(bridge);
return -1;
}
bridge_channel_internal_unsuspend_nolock(bridge_channel);
Richard Mudgett
committed
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.
*/
Richard Mudgett
committed
3091
3092
3093
3094
3095
3096
3097
3098
3099
3100
3101
3102
3103
3104
3105
3106
3107
3108
3109
3110
3111
3112
3113
3114
3115
3116
3117
3118
3119
3120
3121
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);
return 0;
}
Richard Mudgett
committed
3139
3140
3141
3142
3143
3144
3145
3146
3147
3148
3149
3150
3151
3152
3153
3154
3155
3156
3157
3158
3159
3160
3161
3162
3163
3164
3165
3166
3167
3168
3169
3170
3171
3172
3173
3174
3175
3176
3177
3178
3179
3180
3181
3182
3183
3184
3185
3186
3187
3188
3189
3190
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.
Richard Mudgett
committed
*
* \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)
Richard Mudgett
committed
{
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);
Richard Mudgett
committed
}
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)
Richard Mudgett
committed
{
struct ast_bridge_hook_dtmf *hook;
Richard Mudgett
committed
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);
Richard Mudgett
committed
if (!hook) {
return -1;
}
hook->generic.type = AST_BRIDGE_HOOK_TYPE_DTMF;
ast_copy_string(hook->dtmf.code, dtmf, sizeof(hook->dtmf.code));
Richard Mudgett
committed
/* 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;
Richard Mudgett
committed
ao2_ref(hook, -1);
return res;
}
/*!
* \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,
Richard Mudgett
committed
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)
Richard Mudgett
committed
{
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,
remove_flags);
Richard Mudgett
committed
if (!hook) {
return -1;
}
Richard Mudgett
committed
/* Once done we put it in the container. */
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;
}
Richard Mudgett
committed
ao2_ref(hook, -1);
return res;
}
int ast_bridge_hangup_hook(struct ast_bridge_features *features,
Richard Mudgett
committed
ast_bridge_hook_callback callback,
void *hook_pvt,
ast_bridge_hook_pvt_destructor destructor,
enum ast_bridge_hook_remove_flags remove_flags)
Richard Mudgett
committed
{
return bridge_other_hook(features, callback, hook_pvt, destructor, remove_flags,
AST_BRIDGE_HOOK_TYPE_HANGUP);
}
Richard Mudgett
committed
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);
Richard Mudgett
committed
}
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)
Richard Mudgett
committed
{
return bridge_other_hook(features, callback, hook_pvt, destructor, remove_flags,
AST_BRIDGE_HOOK_TYPE_LEAVE);
Richard Mudgett
committed
}
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)
Richard Mudgett
committed
{
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);
Richard Mudgett
committed
}
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);
}
Richard Mudgett
committed
int ast_bridge_interval_hook(struct ast_bridge_features *features,
Richard Mudgett
committed
enum ast_bridge_hook_timer_option flags,
Richard Mudgett
committed
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)
Richard Mudgett
committed
{
struct ast_bridge_hook_timer *hook;
Richard Mudgett
committed
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);
Richard Mudgett
committed
if (!hook) {
return -1;
}
hook->generic.type = AST_BRIDGE_HOOK_TYPE_TIMER;
hook->timer.interval = interval;
Richard Mudgett
committed
hook->timer.trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(interval, 1000));
hook->timer.seqno = ast_atomic_fetchadd_int((int *) &features->interval_sequence, +1);
Richard Mudgett
committed
hook->timer.flags = flags;
Richard Mudgett
committed
ast_debug(1, "Putting interval hook %p with interval %u in the heap on features %p\n",
hook, hook->timer.interval, features);
Richard Mudgett
committed
ast_heap_wrlock(features->interval_hooks);
res = ast_heap_push(features->interval_hooks, hook);
ast_heap_unlock(features->interval_hooks);
Richard Mudgett
committed
if (res) {
/*
* 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;
Richard Mudgett
committed
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)
Richard Mudgett
committed
{
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",
Richard Mudgett
committed
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);
Richard Mudgett
committed
}
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)
Richard Mudgett
committed
{
if (builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_LIMITS]) {
ast_bridge_builtin_set_limits_fn callback;
Richard Mudgett
committed
callback = builtin_interval_handlers[AST_BRIDGE_BUILTIN_INTERVAL_LIMITS];
return callback(features, limits, remove_flags);
Richard Mudgett
committed
}
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.
Richard Mudgett
committed
* \since 12.0.0
*
* \param obj Feature hook object.
* \param arg Removal flags
Richard Mudgett
committed
* \param flags Not used
Richard Mudgett
committed
*
* \retval CMP_MATCH if hook's remove_flags match the removal flags set.
Richard Mudgett
committed
* \retval 0 if not match.
*/
Richard Mudgett
committed
static int hook_remove_match(void *obj, void *arg, int flags)
Richard Mudgett
committed
{
struct ast_bridge_hook *hook = obj;
Richard Mudgett
committed
enum ast_bridge_hook_remove_flags *remove_flags = arg;
Richard Mudgett
committed
Richard Mudgett
committed
if (ast_test_flag(&hook->remove_flags, *remove_flags)) {
Richard Mudgett
committed
return CMP_MATCH;
} else {
return 0;
}
}
/*!
* \internal
* \brief Remove all hooks with appropriate remove_flags in the container.
Richard Mudgett
committed
* \since 12.0.0
*
* \param hooks Hooks container to work on.
Richard Mudgett
committed
* \param remove_flags Determinator for whether hook is removed
Richard Mudgett
committed
*
* \return Nothing
*/
Richard Mudgett
committed
static void hooks_remove_container(struct ao2_container *hooks, enum ast_bridge_hook_remove_flags remove_flags)
Richard Mudgett
committed
{
ao2_callback(hooks, OBJ_UNLINK | OBJ_NODATA | OBJ_MULTIPLE,
Richard Mudgett
committed
hook_remove_match, &remove_flags);
Richard Mudgett
committed
}
/*!
* \internal
* \brief Remove all hooks in the heap with appropriate remove_flags set.
Richard Mudgett
committed
* \since 12.0.0
*
* \param hooks Hooks heap to work on.
Richard Mudgett
committed
* \param remove_flags Determinator for whether hook is removed
Richard Mudgett
committed
*
* \return Nothing
*/
Richard Mudgett
committed
static void hooks_remove_heap(struct ast_heap *hooks, enum ast_bridge_hook_remove_flags remove_flags)
Richard Mudgett
committed
{
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);
Richard Mudgett
committed
if (ast_test_flag(&hook->remove_flags, remove_flags)) {
Richard Mudgett
committed
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)
Richard Mudgett
committed
{
Richard Mudgett
committed
hooks_remove_container(features->dtmf_hooks, remove_flags);
hooks_remove_container(features->other_hooks, remove_flags);
Richard Mudgett
committed
hooks_remove_heap(features->interval_hooks, remove_flags);
Richard Mudgett
committed
}
static int interval_hook_time_cmp(void *a, void *b)
{
struct ast_bridge_hook_timer *hook_a = a;
struct ast_bridge_hook_timer *hook_b = b;
Richard Mudgett
committed
int cmp;
cmp = ast_tvcmp(hook_b->timer.trip_time, hook_a->timer.trip_time);
Richard Mudgett
committed
if (cmp) {
return cmp;
}
cmp = hook_b->timer.seqno - hook_a->timer.seqno;
Richard Mudgett
committed
3558
3559
3560
3561
3562
3563
3564
3565
3566
3567
3568
3569
3570
3571
3572
3573
3574
3575
3576
3577
3578
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;
Richard Mudgett
committed
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;
Richard Mudgett
committed
/* Fall through */
case OBJ_KEY:
cmp = strcasecmp(hook_left->dtmf.code, right_key);
Richard Mudgett
committed
break;
case OBJ_PARTIAL_KEY:
cmp = strncasecmp(hook_left->dtmf.code, right_key, strlen(right_key));
Richard Mudgett
committed
break;
}
return cmp;
}
3599
3600
3601
3602
3603
3604
3605
3606
3607
3608
3609
3610
3611
3612
3613
3614
3615
3616
3617
3618
3619
3620
3621
3622
3623
3624
3625
3626
3627
3628
3629
3630
3631
3632
3633
3634
3635
3636
3637
3638
3639
3640
3641
3642
3643
3644
3645
3646
3647
3648
3649
3650
3651
3652
3653
3654
3655
3656
/*! \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. */
Richard Mudgett
committed
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,
Richard Mudgett
committed
NULL);
Richard Mudgett
committed
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));
Richard Mudgett
committed
if (!features->interval_hooks) {
return -1;
}
Richard Mudgett
committed
return 0;
}
/* XXX ASTERISK-21271 make ast_bridge_features_cleanup() static when make ast_bridge_join() requires features to be allocated. */
Richard Mudgett
committed
void ast_bridge_features_cleanup(struct ast_bridge_features *features)
{
struct ast_bridge_hook_timer *hook;
Richard Mudgett
committed
/* 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;
Richard Mudgett
committed
3705
3706
3707
3708
3709
3710
3711
3712
3713
3714
3715
3716
3717
3718
3719
3720
3721
3722
3723
3724
3725
3726
3727
3728
3729
3730
3731
3732
3733
3734
3735
3736
3737
/* 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;
Richard Mudgett
committed
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;
Richard Mudgett
committed
ast_bridge_unlock(bridge);
}
static void cleanup_video_mode(struct ast_bridge *bridge)
{
switch (bridge->softmix.video_mode.mode) {
Richard Mudgett
committed
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);
Richard Mudgett
committed
}
break;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
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);
Richard Mudgett
committed
}
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);
Richard Mudgett
committed
}
}
memset(&bridge->softmix.video_mode, 0, sizeof(bridge->softmix.video_mode));
Richard Mudgett
committed
}
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_bridge_publish_state(bridge);
Richard Mudgett
committed
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;
Richard Mudgett
committed
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;
Richard Mudgett
committed
/* 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)) {
Richard Mudgett
committed
return;
}
ast_bridge_lock(bridge);
data = &bridge->softmix.video_mode.mode_data.talker_src_data;
Richard Mudgett
committed
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_bridge_publish_state(bridge);
Richard Mudgett
committed
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_bridge_publish_state(bridge);
Richard Mudgett
committed
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) {
Richard Mudgett
committed
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) {
Richard Mudgett
committed
res = 1;
}
break;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc) {
Richard Mudgett
committed
res++;
}
if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc) {
Richard Mudgett
committed
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) {
Richard Mudgett
committed
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) {
Richard Mudgett
committed
res = 1;
}
break;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_vsrc == chan) {
Richard Mudgett
committed
res = 1;
} else if (bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc == chan) {
Richard Mudgett
committed
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) {
Richard Mudgett
committed
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);
Richard Mudgett
committed
}
bridge->softmix.video_mode.mode_data.single_src_data.chan_vsrc = NULL;
Richard Mudgett
committed
}
break;
case AST_BRIDGE_VIDEO_MODE_TALKER_SRC:
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);
Richard Mudgett
committed
}
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;
Richard Mudgett
committed
}
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);
Richard Mudgett
committed
}
bridge->softmix.video_mode.mode_data.talker_src_data.chan_old_vsrc = NULL;
Richard Mudgett
committed
}
Richard Mudgett
committed
ast_bridge_unlock(bridge);
}
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";
}
}
Richard Mudgett
committed
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;
Richard Mudgett
committed
return hash;
Richard Mudgett
committed
static int channel_cmp(void *obj, void *arg, int flags)
Richard Mudgett
committed
const struct ast_channel *left = obj;
const struct ast_channel *right = arg;
const char *right_name = arg;
int cmp;
Richard Mudgett
committed
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;
Richard Mudgett
committed
return cmp ? 0 : CMP_MATCH;
Richard Mudgett
committed
struct ao2_container *ast_bridge_peers_nolock(struct ast_bridge *bridge)
Richard Mudgett
committed
struct ao2_container *channels;
struct ast_bridge_channel *iter;
Richard Mudgett
committed
channels = ao2_container_alloc_options(AO2_ALLOC_OPT_LOCK_NOLOCK,
13, channel_hash, channel_cmp);
if (!channels) {
return NULL;
Richard Mudgett
committed
AST_LIST_TRAVERSE(&bridge->channels, iter, entry) {
ao2_link(channels, iter->chan);
Richard Mudgett
committed
return channels;
}
Richard Mudgett
committed
struct ao2_container *ast_bridge_peers(struct ast_bridge *bridge)
{