mirror of
https://github.com/asterisk/asterisk.git
synced 2026-07-28 00:10:25 -07:00
Refactor ast_bridge_features struct.
* Reduced the number of hook containers to just dtmf_hooks, interval_hooks, and other_hooks. As a result, several functions dealing with the different hook containers could be combined. * Extended the generic hook struct for DTMF and interval hooks instead of using a variant record. * Merged the special talk detector hook into the other_hooks container. * Replaced ast_bridge_features_set_talk_detector() with ast_bridge_talk_detector_hook(). (issue ASTERISK-22107) git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@395322 65c4cc65-6c06-0410-ace0-fbb531ad65f3
This commit is contained in:
+14
-10
@@ -1402,25 +1402,26 @@ static void conf_handle_talker_destructor(void *pvt_data)
|
||||
ast_free(pvt_data);
|
||||
}
|
||||
|
||||
static void conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *pvt_data, int talking)
|
||||
static int conf_handle_talker_cb(struct ast_bridge_channel *bridge_channel, void *pvt_data, int talking)
|
||||
{
|
||||
const char *conf_name = pvt_data;
|
||||
struct confbridge_conference *conference = ao2_find(conference_bridges, conf_name, OBJ_KEY);
|
||||
struct ast_json *talking_extras;
|
||||
|
||||
if (!conference) {
|
||||
return;
|
||||
/* Remove the hook since the conference does not exist. */
|
||||
return -1;
|
||||
}
|
||||
|
||||
talking_extras = ast_json_pack("{s: s}",
|
||||
"talking_status", talking ? "on" : "off");
|
||||
|
||||
"talking_status", talking ? "on" : "off");
|
||||
if (!talking_extras) {
|
||||
return;
|
||||
return 0;
|
||||
}
|
||||
|
||||
send_conf_stasis(conference, bridge_channel->chan, confbridge_talking_type(), talking_extras, 0);
|
||||
ast_json_unref(talking_extras);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int conf_get_pin(struct ast_channel *chan, struct confbridge_user *user)
|
||||
@@ -1599,14 +1600,17 @@ static int confbridge_exec(struct ast_channel *chan, const char *data)
|
||||
/* Set a talker indicate call back if talking detection is requested */
|
||||
if (ast_test_flag(&user.u_profile, USER_OPT_TALKER_DETECT)) {
|
||||
char *conf_name = ast_strdup(args.conf_name); /* this is freed during feature cleanup */
|
||||
if (!(conf_name)) {
|
||||
|
||||
if (!conf_name) {
|
||||
res = -1; /* invalid PIN */
|
||||
goto confbridge_cleanup;
|
||||
}
|
||||
ast_bridge_features_set_talk_detector(&user.features,
|
||||
conf_handle_talker_cb,
|
||||
conf_handle_talker_destructor,
|
||||
conf_name);
|
||||
if (ast_bridge_talk_detector_hook(&user.features, conf_handle_talker_cb,
|
||||
conf_name, conf_handle_talker_destructor, AST_BRIDGE_HOOK_REMOVE_ON_PULL)) {
|
||||
ast_free(conf_name);
|
||||
res = -1;
|
||||
goto confbridge_cleanup;
|
||||
}
|
||||
}
|
||||
|
||||
/* Look for a conference bridge matching the provided name */
|
||||
|
||||
@@ -149,13 +149,43 @@ typedef void (*ast_bridge_hook_pvt_destructor)(void *hook_pvt);
|
||||
* \param bridge_channel Channel executing the feature
|
||||
* \param talking TRUE if the channel is now talking
|
||||
*
|
||||
* \retval 0 success
|
||||
* \retval -1 failure
|
||||
* \retval 0 Keep the callback hook.
|
||||
* \retval -1 Remove the callback hook.
|
||||
*/
|
||||
typedef void (*ast_bridge_talking_indicate_callback)(struct ast_bridge_channel *bridge_channel, void *pvt_data, int talking);
|
||||
typedef int (*ast_bridge_talking_indicate_callback)(struct ast_bridge_channel *bridge_channel, void *pvt_data, int talking);
|
||||
|
||||
enum ast_bridge_hook_remove_flags {
|
||||
/*! The hook is removed when the channel is pulled from the bridge. */
|
||||
AST_BRIDGE_HOOK_REMOVE_ON_PULL = (1 << 0),
|
||||
/*! The hook is removed when the bridge's personality changes. */
|
||||
AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE = (1 << 1),
|
||||
};
|
||||
|
||||
typedef void (*ast_bridge_talking_indicate_destructor)(void *pvt_data);
|
||||
enum ast_bridge_hook_type {
|
||||
/*! The hook type has not been specified. */
|
||||
AST_BRIDGE_HOOK_TYPE_NONE,
|
||||
AST_BRIDGE_HOOK_TYPE_DTMF,
|
||||
AST_BRIDGE_HOOK_TYPE_TIMER,
|
||||
AST_BRIDGE_HOOK_TYPE_HANGUP,
|
||||
AST_BRIDGE_HOOK_TYPE_JOIN,
|
||||
AST_BRIDGE_HOOK_TYPE_LEAVE,
|
||||
AST_BRIDGE_HOOK_TYPE_TALK,
|
||||
};
|
||||
|
||||
/* BUGBUG Need to be able to selectively remove DTMF, hangup, and interval hooks. */
|
||||
/*! \brief Structure that is the essence of a feature hook. */
|
||||
struct ast_bridge_hook {
|
||||
/*! Callback that is called when hook is tripped */
|
||||
ast_bridge_hook_callback callback;
|
||||
/*! Callback to destroy hook_pvt data right before destruction. */
|
||||
ast_bridge_hook_pvt_destructor destructor;
|
||||
/*! Unique data that was passed into us */
|
||||
void *hook_pvt;
|
||||
/*! Flags determining when hooks should be removed from a bridge channel */
|
||||
struct ast_flags remove_flags;
|
||||
/*! What kind of hook this is. */
|
||||
enum ast_bridge_hook_type type;
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Maximum length of a DTMF feature string
|
||||
@@ -163,13 +193,21 @@ typedef void (*ast_bridge_talking_indicate_destructor)(void *pvt_data);
|
||||
#define MAXIMUM_DTMF_FEATURE_STRING (11 + 1)
|
||||
|
||||
/*! Extra parameters for a DTMF feature hook. */
|
||||
struct ast_bridge_hook_dtmf {
|
||||
struct ast_bridge_hook_dtmf_parms {
|
||||
/*! DTMF String that is examined during a feature hook lookup */
|
||||
char code[MAXIMUM_DTMF_FEATURE_STRING];
|
||||
};
|
||||
|
||||
/*! DTMF specific hook. */
|
||||
struct ast_bridge_hook_dtmf {
|
||||
/*! Generic feature hook information. */
|
||||
struct ast_bridge_hook generic;
|
||||
/*! Extra parameters for a DTMF feature hook. */
|
||||
struct ast_bridge_hook_dtmf_parms dtmf;
|
||||
};
|
||||
|
||||
/*! Extra parameters for an interval timer hook. */
|
||||
struct ast_bridge_hook_timer {
|
||||
struct ast_bridge_hook_timer_parms {
|
||||
/*! Time at which the hook should actually trip */
|
||||
struct timeval trip_time;
|
||||
/*! Heap index for interval hook */
|
||||
@@ -180,33 +218,12 @@ struct ast_bridge_hook_timer {
|
||||
unsigned int seqno;
|
||||
};
|
||||
|
||||
enum ast_bridge_hook_remove_flags {
|
||||
/*! The hook is removed when the channel is pulled from the bridge. */
|
||||
AST_BRIDGE_HOOK_REMOVE_ON_PULL = (1 << 0),
|
||||
/*! The hook is removed when the bridge's personality changes. */
|
||||
AST_BRIDGE_HOOK_REMOVE_ON_PERSONALITY_CHANGE = (1 << 1),
|
||||
};
|
||||
|
||||
/* BUGBUG Need to be able to selectively remove DTMF, hangup, and interval hooks. */
|
||||
/*! \brief Structure that is the essence of a feature hook. */
|
||||
struct ast_bridge_hook {
|
||||
/*! Linked list information */
|
||||
AST_LIST_ENTRY(ast_bridge_hook) entry;
|
||||
/*! Callback that is called when hook is tripped */
|
||||
ast_bridge_hook_callback callback;
|
||||
/*! Callback to destroy hook_pvt data right before destruction. */
|
||||
ast_bridge_hook_pvt_destructor destructor;
|
||||
/*! Unique data that was passed into us */
|
||||
void *hook_pvt;
|
||||
/*! Flags determining when hooks should be removed from a bridge channel */
|
||||
struct ast_flags remove_flags;
|
||||
/*! Extra hook parameters. */
|
||||
union {
|
||||
/*! Extra parameters for a DTMF feature hook. */
|
||||
struct ast_bridge_hook_dtmf dtmf;
|
||||
/*! Extra parameters for an interval timer hook. */
|
||||
struct ast_bridge_hook_timer timer;
|
||||
} parms;
|
||||
/*! Timer specific hook. */
|
||||
struct ast_bridge_hook_timer {
|
||||
/*! Generic feature hook information. */
|
||||
struct ast_bridge_hook generic;
|
||||
/*! Extra parameters for an interval timer hook. */
|
||||
struct ast_bridge_hook_timer_parms timer;
|
||||
};
|
||||
|
||||
#define BRIDGE_FEATURES_INTERVAL_RATE 10
|
||||
@@ -217,24 +234,14 @@ struct ast_bridge_hook {
|
||||
struct ast_bridge_features {
|
||||
/*! Attached DTMF feature hooks */
|
||||
struct ao2_container *dtmf_hooks;
|
||||
/*! Attached hangup interception hooks container */
|
||||
struct ao2_container *hangup_hooks;
|
||||
/*! Attached bridge channel join interception hooks container */
|
||||
struct ao2_container *join_hooks;
|
||||
/*! Attached bridge channel leave interception hooks container */
|
||||
struct ao2_container *leave_hooks;
|
||||
/*! Attached miscellaneous other hooks. */
|
||||
struct ao2_container *other_hooks;
|
||||
/*! Attached interval hooks */
|
||||
struct ast_heap *interval_hooks;
|
||||
/*! Used to determine when interval based features should be checked */
|
||||
struct ast_timer *interval_timer;
|
||||
/*! Limits feature data */
|
||||
struct ast_bridge_features_limits *limits;
|
||||
/*! Callback to indicate when a bridge channel has started and stopped talking */
|
||||
ast_bridge_talking_indicate_callback talker_cb;
|
||||
/*! Callback to destroy any pvt data stored for the talker. */
|
||||
ast_bridge_talking_indicate_destructor talker_destructor_cb;
|
||||
/*! Talker callback pvt data */
|
||||
void *talker_pvt_data;
|
||||
/*! Feature flags that are enabled */
|
||||
struct ast_flags feature_flags;
|
||||
/*! Used to assign the sequence number to the next interval hook added. */
|
||||
@@ -575,19 +582,37 @@ int ast_bridge_interval_hook(struct ast_bridge_features *features,
|
||||
enum ast_bridge_hook_remove_flags remove_flags);
|
||||
|
||||
/*!
|
||||
* \brief Set a callback on the features structure to receive talking notifications on.
|
||||
* \brief Attach a bridge channel talk detection hook to a bridge features structure
|
||||
*
|
||||
* \param features Bridge features structure
|
||||
* \param talker_cb Callback function to execute when talking events occur in the bridge core.
|
||||
* \param pvt_data Optional unique data that will be passed with the talking events.
|
||||
* \param talker_destructor Optional destructor callback for pvt data.
|
||||
* \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.
|
||||
*
|
||||
* \return Nothing
|
||||
* \retval 0 on success
|
||||
* \retval -1 on failure (The caller must cleanup any hook_pvt resources.)
|
||||
*
|
||||
* Example usage:
|
||||
*
|
||||
* \code
|
||||
* struct ast_bridge_features features;
|
||||
* ast_bridge_features_init(&features);
|
||||
* ast_bridge_talk_hook(&features, talk_callback, NULL, NULL, 0);
|
||||
* \endcode
|
||||
*
|
||||
* This makes the bridging technology call talk_callback when a
|
||||
* channel is recognized as starting and stopping talking. A
|
||||
* pointer to useful data may be provided to the hook_pvt
|
||||
* parameter.
|
||||
*
|
||||
* \note This hook is currently only supported by softmix.
|
||||
*/
|
||||
void ast_bridge_features_set_talk_detector(struct ast_bridge_features *features,
|
||||
ast_bridge_talking_indicate_callback talker_cb,
|
||||
ast_bridge_talking_indicate_destructor talker_destructor,
|
||||
void *pvt_data);
|
||||
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);
|
||||
|
||||
/*!
|
||||
* \brief Enable a built in feature on a bridge features structure
|
||||
|
||||
+93
-136
@@ -3508,6 +3508,55 @@ int ast_bridge_dtmf_hook(struct ast_bridge_features *features,
|
||||
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);
|
||||
if (!hook) {
|
||||
return -1;
|
||||
}
|
||||
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;
|
||||
}
|
||||
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,
|
||||
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;
|
||||
@@ -3518,10 +3567,10 @@ int ast_bridge_dtmf_hook(struct ast_bridge_features *features,
|
||||
if (!hook) {
|
||||
return -1;
|
||||
}
|
||||
ast_copy_string(hook->parms.dtmf.code, dtmf, sizeof(hook->parms.dtmf.code));
|
||||
hook->type = type;
|
||||
|
||||
/* Once done we put it in the container. */
|
||||
res = ao2_link(features->dtmf_hooks, hook) ? 0 : -1;
|
||||
res = ao2_link(features->other_hooks, hook) ? 0 : -1;
|
||||
if (res) {
|
||||
/*
|
||||
* Could not link the hook into the container.
|
||||
@@ -3542,30 +3591,8 @@ int ast_bridge_hangup_hook(struct ast_bridge_features *features,
|
||||
ast_bridge_hook_pvt_destructor destructor,
|
||||
enum ast_bridge_hook_remove_flags remove_flags)
|
||||
{
|
||||
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);
|
||||
if (!hook) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Once done we put it in the container. */
|
||||
res = ao2_link(features->hangup_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;
|
||||
}
|
||||
ao2_ref(hook, -1);
|
||||
|
||||
return res;
|
||||
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,
|
||||
@@ -3574,30 +3601,8 @@ int ast_bridge_join_hook(struct ast_bridge_features *features,
|
||||
ast_bridge_hook_pvt_destructor destructor,
|
||||
enum ast_bridge_hook_remove_flags remove_flags)
|
||||
{
|
||||
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);
|
||||
if (!hook) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Once done we put it in the container. */
|
||||
res = ao2_link(features->join_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;
|
||||
}
|
||||
ao2_ref(hook, -1);
|
||||
|
||||
return res;
|
||||
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,
|
||||
@@ -3606,40 +3611,20 @@ int ast_bridge_leave_hook(struct ast_bridge_features *features,
|
||||
ast_bridge_hook_pvt_destructor destructor,
|
||||
enum ast_bridge_hook_remove_flags remove_flags)
|
||||
{
|
||||
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);
|
||||
if (!hook) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Once done we put it in the container. */
|
||||
res = ao2_link(features->leave_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;
|
||||
}
|
||||
ao2_ref(hook, -1);
|
||||
|
||||
return res;
|
||||
return bridge_other_hook(features, callback, hook_pvt, destructor, remove_flags,
|
||||
AST_BRIDGE_HOOK_TYPE_LEAVE);
|
||||
}
|
||||
|
||||
void ast_bridge_features_set_talk_detector(struct ast_bridge_features *features,
|
||||
ast_bridge_talking_indicate_callback talker_cb,
|
||||
ast_bridge_talking_indicate_destructor talker_destructor,
|
||||
void *pvt_data)
|
||||
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)
|
||||
{
|
||||
features->talker_cb = talker_cb;
|
||||
features->talker_destructor_cb = talker_destructor;
|
||||
features->talker_pvt_data = pvt_data;
|
||||
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_interval_hook(struct ast_bridge_features *features,
|
||||
@@ -3649,7 +3634,7 @@ int ast_bridge_interval_hook(struct ast_bridge_features *features,
|
||||
ast_bridge_hook_pvt_destructor destructor,
|
||||
enum ast_bridge_hook_remove_flags remove_flags)
|
||||
{
|
||||
struct ast_bridge_hook *hook;
|
||||
struct ast_bridge_hook_timer *hook;
|
||||
int res;
|
||||
|
||||
if (!features ||!interval || !callback) {
|
||||
@@ -3665,17 +3650,18 @@ int ast_bridge_interval_hook(struct ast_bridge_features *features,
|
||||
}
|
||||
|
||||
/* Allocate new hook and setup it's various variables */
|
||||
hook = bridge_hook_generic(sizeof(*hook), callback, hook_pvt, destructor,
|
||||
remove_flags);
|
||||
hook = (struct ast_bridge_hook_timer *) bridge_hook_generic(sizeof(*hook), callback,
|
||||
hook_pvt, destructor, remove_flags);
|
||||
if (!hook) {
|
||||
return -1;
|
||||
}
|
||||
hook->parms.timer.interval = interval;
|
||||
hook->parms.timer.trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(hook->parms.timer.interval, 1000));
|
||||
hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &features->interval_sequence, +1);
|
||||
hook->generic.type = AST_BRIDGE_HOOK_TYPE_TIMER;
|
||||
hook->timer.interval = interval;
|
||||
hook->timer.trip_time = ast_tvadd(ast_tvnow(), ast_samp2tv(hook->timer.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->parms.timer.interval, features);
|
||||
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);
|
||||
@@ -3686,7 +3672,7 @@ int ast_bridge_interval_hook(struct ast_bridge_features *features,
|
||||
* Remove the hook_pvt destructor call from the hook since we
|
||||
* are returning failure to install the hook.
|
||||
*/
|
||||
hook->destructor = NULL;
|
||||
hook->generic.destructor = NULL;
|
||||
ao2_ref(hook, -1);
|
||||
}
|
||||
|
||||
@@ -3836,24 +3822,22 @@ static void hooks_remove_heap(struct ast_heap *hooks, enum ast_bridge_hook_remov
|
||||
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->hangup_hooks, remove_flags);
|
||||
hooks_remove_container(features->join_hooks, remove_flags);
|
||||
hooks_remove_container(features->leave_hooks, remove_flags);
|
||||
hooks_remove_container(features->other_hooks, remove_flags);
|
||||
hooks_remove_heap(features->interval_hooks, remove_flags);
|
||||
}
|
||||
|
||||
static int interval_hook_time_cmp(void *a, void *b)
|
||||
{
|
||||
struct ast_bridge_hook *hook_a = a;
|
||||
struct ast_bridge_hook *hook_b = b;
|
||||
struct ast_bridge_hook_timer *hook_a = a;
|
||||
struct ast_bridge_hook_timer *hook_b = b;
|
||||
int cmp;
|
||||
|
||||
cmp = ast_tvcmp(hook_b->parms.timer.trip_time, hook_a->parms.timer.trip_time);
|
||||
cmp = ast_tvcmp(hook_b->timer.trip_time, hook_a->timer.trip_time);
|
||||
if (cmp) {
|
||||
return cmp;
|
||||
}
|
||||
|
||||
cmp = hook_b->parms.timer.seqno - hook_a->parms.timer.seqno;
|
||||
cmp = hook_b->timer.seqno - hook_a->timer.seqno;
|
||||
return cmp;
|
||||
}
|
||||
|
||||
@@ -3875,21 +3859,21 @@ static int interval_hook_time_cmp(void *a, void *b)
|
||||
*/
|
||||
static int bridge_dtmf_hook_sort(const void *obj_left, const void *obj_right, int flags)
|
||||
{
|
||||
const struct ast_bridge_hook *hook_left = obj_left;
|
||||
const struct ast_bridge_hook *hook_right = obj_right;
|
||||
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->parms.dtmf.code;
|
||||
right_key = hook_right->dtmf.code;
|
||||
/* Fall through */
|
||||
case OBJ_KEY:
|
||||
cmp = strcasecmp(hook_left->parms.dtmf.code, right_key);
|
||||
cmp = strcasecmp(hook_left->dtmf.code, right_key);
|
||||
break;
|
||||
case OBJ_PARTIAL_KEY:
|
||||
cmp = strncasecmp(hook_left->parms.dtmf.code, right_key, strlen(right_key));
|
||||
cmp = strncasecmp(hook_left->dtmf.code, right_key, strlen(right_key));
|
||||
break;
|
||||
}
|
||||
return cmp;
|
||||
@@ -3908,30 +3892,16 @@ int ast_bridge_features_init(struct ast_bridge_features *features)
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize the hangup hooks container */
|
||||
features->hangup_hooks = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL,
|
||||
/* Initialize the miscellaneous other hooks container */
|
||||
features->other_hooks = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL,
|
||||
NULL);
|
||||
if (!features->hangup_hooks) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize the join hooks container */
|
||||
features->join_hooks = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL,
|
||||
NULL);
|
||||
if (!features->join_hooks) {
|
||||
return -1;
|
||||
}
|
||||
|
||||
/* Initialize the leave hooks container */
|
||||
features->leave_hooks = ao2_container_alloc_list(AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL,
|
||||
NULL);
|
||||
if (!features->leave_hooks) {
|
||||
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, parms.timer.heap_index));
|
||||
offsetof(struct ast_bridge_hook_timer, timer.heap_index));
|
||||
if (!features->interval_hooks) {
|
||||
return -1;
|
||||
}
|
||||
@@ -3942,7 +3912,7 @@ int ast_bridge_features_init(struct ast_bridge_features *features)
|
||||
/* BUGBUG 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 *hook;
|
||||
struct ast_bridge_hook_timer *hook;
|
||||
|
||||
/* Destroy the interval hooks heap. */
|
||||
if (features->interval_hooks) {
|
||||
@@ -3964,22 +3934,9 @@ void ast_bridge_features_cleanup(struct ast_bridge_features *features)
|
||||
features->limits = NULL;
|
||||
}
|
||||
|
||||
if (features->talker_destructor_cb && features->talker_pvt_data) {
|
||||
features->talker_destructor_cb(features->talker_pvt_data);
|
||||
features->talker_pvt_data = NULL;
|
||||
}
|
||||
|
||||
/* Destroy the leave hooks container. */
|
||||
ao2_cleanup(features->leave_hooks);
|
||||
features->leave_hooks = NULL;
|
||||
|
||||
/* Destroy the join hooks container. */
|
||||
ao2_cleanup(features->join_hooks);
|
||||
features->join_hooks = NULL;
|
||||
|
||||
/* Destroy the hangup hooks container. */
|
||||
ao2_cleanup(features->hangup_hooks);
|
||||
features->hangup_hooks = NULL;
|
||||
/* 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);
|
||||
|
||||
+60
-60
@@ -410,15 +410,18 @@ static void bridge_channel_handle_hangup(struct ast_bridge_channel *bridge_chann
|
||||
struct ao2_iterator iter;
|
||||
|
||||
/* Run any hangup hooks. */
|
||||
iter = ao2_iterator_init(features->hangup_hooks, 0);
|
||||
iter = ao2_iterator_init(features->other_hooks, 0);
|
||||
for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
|
||||
int remove_me;
|
||||
|
||||
if (hook->type != AST_BRIDGE_HOOK_TYPE_HANGUP) {
|
||||
continue;
|
||||
}
|
||||
remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
|
||||
if (remove_me) {
|
||||
ast_debug(1, "Hangup hook %p is being removed from %p(%s)\n",
|
||||
hook, bridge_channel, ast_channel_name(bridge_channel->chan));
|
||||
ao2_unlink(features->hangup_hooks, hook);
|
||||
ao2_unlink(features->other_hooks, hook);
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&iter);
|
||||
@@ -698,12 +701,12 @@ int ast_bridge_channel_write_park(struct ast_bridge_channel *bridge_channel, con
|
||||
static int bridge_channel_interval_ready(struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
struct ast_bridge_features *features = bridge_channel->features;
|
||||
struct ast_bridge_hook *hook;
|
||||
struct ast_bridge_hook_timer *hook;
|
||||
int ready;
|
||||
|
||||
ast_heap_wrlock(features->interval_hooks);
|
||||
hook = ast_heap_peek(features->interval_hooks, 1);
|
||||
ready = hook && ast_tvdiff_ms(hook->parms.timer.trip_time, ast_tvnow()) <= 0;
|
||||
ready = hook && ast_tvdiff_ms(hook->timer.trip_time, ast_tvnow()) <= 0;
|
||||
ast_heap_unlock(features->interval_hooks);
|
||||
|
||||
return ready;
|
||||
@@ -833,7 +836,7 @@ static void bridge_channel_unsuspend(struct ast_bridge_channel *bridge_channel)
|
||||
/*! \brief Internal function that activates interval hooks on a bridge channel */
|
||||
static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
struct ast_bridge_hook *hook;
|
||||
struct ast_bridge_hook_timer *hook;
|
||||
struct timeval start;
|
||||
|
||||
ast_heap_wrlock(bridge_channel->features->interval_hooks);
|
||||
@@ -842,7 +845,7 @@ static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel)
|
||||
int interval;
|
||||
unsigned int execution_time;
|
||||
|
||||
if (ast_tvdiff_ms(hook->parms.timer.trip_time, start) > 0) {
|
||||
if (ast_tvdiff_ms(hook->timer.trip_time, start) > 0) {
|
||||
ast_debug(1, "Hook %p on %p(%s) wants to happen in the future, stopping our traversal\n",
|
||||
hook, bridge_channel, ast_channel_name(bridge_channel->chan));
|
||||
break;
|
||||
@@ -852,11 +855,12 @@ static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel)
|
||||
|
||||
ast_debug(1, "Executing hook %p on %p(%s)\n",
|
||||
hook, bridge_channel, ast_channel_name(bridge_channel->chan));
|
||||
interval = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
|
||||
interval = hook->generic.callback(bridge_channel->bridge, bridge_channel,
|
||||
hook->generic.hook_pvt);
|
||||
|
||||
ast_heap_wrlock(bridge_channel->features->interval_hooks);
|
||||
if (ast_heap_peek(bridge_channel->features->interval_hooks,
|
||||
hook->parms.timer.heap_index) != hook
|
||||
hook->timer.heap_index) != hook
|
||||
|| !ast_heap_remove(bridge_channel->features->interval_hooks, hook)) {
|
||||
/* Interval hook is already removed from the bridge_channel. */
|
||||
ao2_ref(hook, -1);
|
||||
@@ -872,11 +876,11 @@ static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel)
|
||||
}
|
||||
if (interval) {
|
||||
/* Set new interval for the hook. */
|
||||
hook->parms.timer.interval = interval;
|
||||
hook->timer.interval = interval;
|
||||
}
|
||||
|
||||
ast_debug(1, "Updating interval hook %p with interval %u on %p(%s)\n",
|
||||
hook, hook->parms.timer.interval, bridge_channel,
|
||||
hook, hook->timer.interval, bridge_channel,
|
||||
ast_channel_name(bridge_channel->chan));
|
||||
|
||||
/* resetting start */
|
||||
@@ -887,12 +891,12 @@ static void bridge_channel_interval(struct ast_bridge_channel *bridge_channel)
|
||||
* to skip over any missed intervals because the hook was
|
||||
* delayed or took too long.
|
||||
*/
|
||||
execution_time = ast_tvdiff_ms(start, hook->parms.timer.trip_time);
|
||||
while (hook->parms.timer.interval < execution_time) {
|
||||
execution_time -= hook->parms.timer.interval;
|
||||
execution_time = ast_tvdiff_ms(start, hook->timer.trip_time);
|
||||
while (hook->timer.interval < execution_time) {
|
||||
execution_time -= hook->timer.interval;
|
||||
}
|
||||
hook->parms.timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->parms.timer.interval - execution_time, 1000));
|
||||
hook->parms.timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1);
|
||||
hook->timer.trip_time = ast_tvadd(start, ast_samp2tv(hook->timer.interval - execution_time, 1000));
|
||||
hook->timer.seqno = ast_atomic_fetchadd_int((int *) &bridge_channel->features->interval_sequence, +1);
|
||||
|
||||
if (ast_heap_push(bridge_channel->features->interval_hooks, hook)) {
|
||||
/* Could not push the hook back onto the heap. */
|
||||
@@ -916,7 +920,7 @@ static int bridge_channel_write_dtmf_stream(struct ast_bridge_channel *bridge_ch
|
||||
static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
struct ast_bridge_features *features = bridge_channel->features;
|
||||
struct ast_bridge_hook *hook = NULL;
|
||||
struct ast_bridge_hook_dtmf *hook = NULL;
|
||||
char dtmf[MAXIMUM_DTMF_FEATURE_STRING] = "";
|
||||
size_t dtmf_len = 0;
|
||||
unsigned int digit_timeout;
|
||||
@@ -965,7 +969,7 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
|
||||
bridge_channel, ast_channel_name(bridge_channel->chan), dtmf);
|
||||
break;
|
||||
}
|
||||
if (strlen(hook->parms.dtmf.code) == dtmf_len) {
|
||||
if (strlen(hook->dtmf.code) == dtmf_len) {
|
||||
ast_debug(1, "DTMF feature hook %p matched DTMF string '%s' on %p(%s)\n",
|
||||
hook, dtmf, bridge_channel, ast_channel_name(bridge_channel->chan));
|
||||
break;
|
||||
@@ -983,7 +987,8 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
|
||||
if (hook) {
|
||||
int remove_me;
|
||||
|
||||
remove_me = hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
|
||||
remove_me = hook->generic.callback(bridge_channel->bridge, bridge_channel,
|
||||
hook->generic.hook_pvt);
|
||||
if (remove_me) {
|
||||
ast_debug(1, "DTMF hook %p is being removed from %p(%s)\n",
|
||||
hook, bridge_channel, ast_channel_name(bridge_channel->chan));
|
||||
@@ -1008,10 +1013,27 @@ static void bridge_channel_feature(struct ast_bridge_channel *bridge_channel)
|
||||
static void bridge_channel_talking(struct ast_bridge_channel *bridge_channel, int talking)
|
||||
{
|
||||
struct ast_bridge_features *features = bridge_channel->features;
|
||||
struct ast_bridge_hook *hook;
|
||||
struct ao2_iterator iter;
|
||||
|
||||
if (features->talker_cb) {
|
||||
features->talker_cb(bridge_channel, features->talker_pvt_data, talking);
|
||||
/* Run any talk detection hooks. */
|
||||
iter = ao2_iterator_init(features->other_hooks, 0);
|
||||
for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
|
||||
int remove_me;
|
||||
ast_bridge_talking_indicate_callback talk_cb;
|
||||
|
||||
if (hook->type != AST_BRIDGE_HOOK_TYPE_TALK) {
|
||||
continue;
|
||||
}
|
||||
talk_cb = (ast_bridge_talking_indicate_callback) hook->callback;
|
||||
remove_me = talk_cb(bridge_channel, hook->hook_pvt, talking);
|
||||
if (remove_me) {
|
||||
ast_debug(1, "Talk detection hook %p is being removed from %p(%s)\n",
|
||||
hook, bridge_channel, ast_channel_name(bridge_channel->chan));
|
||||
ao2_unlink(features->other_hooks, hook);
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&iter);
|
||||
}
|
||||
|
||||
/*! \brief Internal function that plays back DTMF on a bridge channel */
|
||||
@@ -1533,7 +1555,7 @@ static void bridge_channel_handle_interval(struct ast_bridge_channel *bridge_cha
|
||||
static struct ast_frame *bridge_handle_dtmf(struct ast_bridge_channel *bridge_channel, struct ast_frame *frame)
|
||||
{
|
||||
struct ast_bridge_features *features = bridge_channel->features;
|
||||
struct ast_bridge_hook *hook;
|
||||
struct ast_bridge_hook_dtmf *hook;
|
||||
char dtmf[2];
|
||||
|
||||
/* BUGBUG the feature hook matching needs to be done here. Any matching feature hook needs to be queued onto the bridge_channel. Also the feature hook digit timeout needs to be handled. */
|
||||
@@ -1678,58 +1700,36 @@ static void bridge_channel_wait(struct ast_bridge_channel *bridge_channel)
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Handle bridge channel join event.
|
||||
* \brief Handle bridge channel join/leave event.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param bridge_channel Which channel is joining.
|
||||
* \param bridge_channel Which channel is involved.
|
||||
* \param type Specified join/leave event.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void bridge_channel_handle_join(struct ast_bridge_channel *bridge_channel)
|
||||
static void bridge_channel_event_join_leave(struct ast_bridge_channel *bridge_channel, enum ast_bridge_hook_type type)
|
||||
{
|
||||
struct ast_bridge_features *features = bridge_channel->features;
|
||||
struct ast_bridge_hook *hook;
|
||||
struct ao2_iterator iter;
|
||||
|
||||
/* Run any join hooks. */
|
||||
iter = ao2_iterator_init(features->join_hooks, AO2_ITERATOR_UNLINK);
|
||||
hook = ao2_iterator_next(&iter);
|
||||
if (hook) {
|
||||
bridge_channel_suspend(bridge_channel);
|
||||
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
||||
do {
|
||||
hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
|
||||
ao2_ref(hook, -1);
|
||||
} while ((hook = ao2_iterator_next(&iter)));
|
||||
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
||||
bridge_channel_unsuspend(bridge_channel);
|
||||
/* Run the specified hooks. */
|
||||
iter = ao2_iterator_init(features->other_hooks, 0);
|
||||
for (; (hook = ao2_iterator_next(&iter)); ao2_ref(hook, -1)) {
|
||||
if (hook->type == type) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
ao2_iterator_destroy(&iter);
|
||||
}
|
||||
|
||||
/*!
|
||||
* \internal
|
||||
* \brief Handle bridge channel leave event.
|
||||
* \since 12.0.0
|
||||
*
|
||||
* \param bridge_channel Which channel is leaving.
|
||||
*
|
||||
* \return Nothing
|
||||
*/
|
||||
static void bridge_channel_handle_leave(struct ast_bridge_channel *bridge_channel)
|
||||
{
|
||||
struct ast_bridge_features *features = bridge_channel->features;
|
||||
struct ast_bridge_hook *hook;
|
||||
struct ao2_iterator iter;
|
||||
|
||||
/* Run any leave hooks. */
|
||||
iter = ao2_iterator_init(features->leave_hooks, AO2_ITERATOR_UNLINK);
|
||||
hook = ao2_iterator_next(&iter);
|
||||
if (hook) {
|
||||
/* Found the first specified hook to run. */
|
||||
bridge_channel_suspend(bridge_channel);
|
||||
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
||||
do {
|
||||
hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
|
||||
if (hook->type == type) {
|
||||
hook->callback(bridge_channel->bridge, bridge_channel, hook->hook_pvt);
|
||||
ao2_unlink(features->other_hooks, hook);
|
||||
}
|
||||
ao2_ref(hook, -1);
|
||||
} while ((hook = ao2_iterator_next(&iter)));
|
||||
ast_indicate(bridge_channel->chan, AST_CONTROL_SRCUPDATE);
|
||||
@@ -1786,12 +1786,12 @@ void bridge_channel_join(struct ast_bridge_channel *bridge_channel)
|
||||
}
|
||||
|
||||
ast_bridge_unlock(bridge_channel->bridge);
|
||||
bridge_channel_handle_join(bridge_channel);
|
||||
bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_JOIN);
|
||||
while (bridge_channel->state == AST_BRIDGE_CHANNEL_STATE_WAIT) {
|
||||
/* Wait for something to do. */
|
||||
bridge_channel_wait(bridge_channel);
|
||||
}
|
||||
bridge_channel_handle_leave(bridge_channel);
|
||||
bridge_channel_event_join_leave(bridge_channel, AST_BRIDGE_HOOK_TYPE_LEAVE);
|
||||
ast_bridge_channel_lock_bridge(bridge_channel);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user