diff options
author | Jonathan Rose <jrose@digium.com> | 2014-04-18 20:09:24 +0000 |
---|---|---|
committer | Jonathan Rose <jrose@digium.com> | 2014-04-18 20:09:24 +0000 |
commit | b9d7dfcc62c80d2b2827dd7b70701dfb21512c13 (patch) | |
tree | 5d564284aeb95084327ad944a2e358829e67ba6a /res/res_stasis.c | |
parent | 06657c92e61340a9bc3e0d89fa676f93e6581ef1 (diff) |
ARI: Make bridges/{bridgeID}/play queue sound files
Previously multiple play actions against a bridge at one time would cause
the sounds to play simultaneously on the bridge. Now if a sound is already
playing, the play action will queue playback to occur after the completion
of other sounds currently on the queue.
(closes issue ASTERISK-22677)
Reported by: John Bigelow
Review: https://reviewboard.asterisk.org/r/3379/
........
Merged revisions 412639 from http://svn.asterisk.org/svn/asterisk/branches/12
git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@412641 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res/res_stasis.c')
-rw-r--r-- | res/res_stasis.c | 146 |
1 files changed, 129 insertions, 17 deletions
diff --git a/res/res_stasis.c b/res/res_stasis.c index 32ac7b686..d9542cd21 100644 --- a/res/res_stasis.c +++ b/res/res_stasis.c @@ -103,6 +103,8 @@ struct ao2_container *app_bridges; struct ao2_container *app_bridges_moh; +struct ao2_container *app_bridges_playback; + const char *stasis_app_name(const struct stasis_app *app) { return app_name(app); @@ -341,26 +343,26 @@ static int bridges_compare(void *obj, void *arg, int flags) } /*! - * Used with app_bridges_moh, provides links between bridges and existing music - * on hold channels that are being used with them. + * Used with app_bridges_moh and app_bridge_control, they provide links + * between bridges and channels used for ARI application purposes */ -struct stasis_app_bridge_moh_wrapper { +struct stasis_app_bridge_channel_wrapper { AST_DECLARE_STRING_FIELDS( AST_STRING_FIELD(channel_id); AST_STRING_FIELD(bridge_id); ); }; -static void stasis_app_bridge_moh_wrapper_destructor(void *obj) +static void stasis_app_bridge_channel_wrapper_destructor(void *obj) { - struct stasis_app_bridge_moh_wrapper *wrapper = obj; + struct stasis_app_bridge_channel_wrapper *wrapper = obj; ast_string_field_free_memory(wrapper); } /*! AO2 hash function for the bridges moh container */ -static int bridges_moh_hash_fn(const void *obj, const int flags) +static int bridges_channel_hash_fn(const void *obj, const int flags) { - const struct stasis_app_bridge_moh_wrapper *wrapper; + const struct stasis_app_bridge_channel_wrapper *wrapper; const char *key; switch (flags & OBJ_SEARCH_MASK) { @@ -379,10 +381,10 @@ static int bridges_moh_hash_fn(const void *obj, const int flags) return ast_str_hash(key); } -static int bridges_moh_sort_fn(const void *obj_left, const void *obj_right, const int flags) +static int bridges_channel_sort_fn(const void *obj_left, const void *obj_right, const int flags) { - const struct stasis_app_bridge_moh_wrapper *left = obj_left; - const struct stasis_app_bridge_moh_wrapper *right = obj_right; + const struct stasis_app_bridge_channel_wrapper *left = obj_left; + const struct stasis_app_bridge_channel_wrapper *right = obj_right; const char *right_key = obj_right; int cmp; @@ -469,7 +471,7 @@ static void *moh_channel_thread(void *data) */ static struct ast_channel *bridge_moh_create(struct ast_bridge *bridge) { - RAII_VAR(struct stasis_app_bridge_moh_wrapper *, new_wrapper, NULL, ao2_cleanup); + RAII_VAR(struct stasis_app_bridge_channel_wrapper *, new_wrapper, NULL, ao2_cleanup); RAII_VAR(char *, bridge_id, ast_strdup(bridge->uniqueid), ast_free); struct ast_channel *chan; pthread_t threadid; @@ -498,7 +500,7 @@ static struct ast_channel *bridge_moh_create(struct ast_bridge *bridge) } new_wrapper = ao2_alloc_options(sizeof(*new_wrapper), - stasis_app_bridge_moh_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); + stasis_app_bridge_channel_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); if (!new_wrapper) { ast_hangup(chan); return NULL; @@ -528,7 +530,7 @@ static struct ast_channel *bridge_moh_create(struct ast_bridge *bridge) struct ast_channel *stasis_app_bridge_moh_channel(struct ast_bridge *bridge) { - RAII_VAR(struct stasis_app_bridge_moh_wrapper *, moh_wrapper, NULL, ao2_cleanup); + RAII_VAR(struct stasis_app_bridge_channel_wrapper *, moh_wrapper, NULL, ao2_cleanup); { SCOPED_AO2LOCK(lock, app_bridges_moh); @@ -544,7 +546,7 @@ struct ast_channel *stasis_app_bridge_moh_channel(struct ast_bridge *bridge) int stasis_app_bridge_moh_stop(struct ast_bridge *bridge) { - RAII_VAR(struct stasis_app_bridge_moh_wrapper *, moh_wrapper, NULL, ao2_cleanup); + RAII_VAR(struct stasis_app_bridge_channel_wrapper *, moh_wrapper, NULL, ao2_cleanup); struct ast_channel *chan; moh_wrapper = ao2_find(app_bridges_moh, bridge->uniqueid, OBJ_SEARCH_KEY | OBJ_UNLINK); @@ -564,6 +566,92 @@ int stasis_app_bridge_moh_stop(struct ast_bridge *bridge) return 0; } +/*! Removes the bridge to playback channel link */ +static void remove_bridge_playback(char *bridge_id) +{ + struct stasis_app_bridge_channel_wrapper *wrapper; + struct stasis_app_control *control; + + wrapper = ao2_find(app_bridges_playback, bridge_id, OBJ_SEARCH_KEY | OBJ_UNLINK); + + if (wrapper) { + control = stasis_app_control_find_by_channel_id(wrapper->channel_id); + if (control) { + ao2_unlink(app_controls, control); + ao2_ref(control, -1); + } + ao2_ref(wrapper, -1); + } + ast_free(bridge_id); +} + +static void playback_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data) +{ + char *bridge_id = data; + + remove_bridge_playback(bridge_id); +} + +static void playback_after_bridge_cb(struct ast_channel *chan, void *data) +{ + char *bridge_id = data; + + remove_bridge_playback(bridge_id); +} + +int stasis_app_bridge_playback_channel_add(struct ast_bridge *bridge, + struct ast_channel *chan, + struct stasis_app_control *control) +{ + RAII_VAR(struct stasis_app_bridge_channel_wrapper *, new_wrapper, NULL, ao2_cleanup); + char *bridge_id = ast_strdup(bridge->uniqueid); + + if (!bridge_id) { + return -1; + } + + if (ast_bridge_set_after_callback(chan, + playback_after_bridge_cb, playback_after_bridge_cb_failed, bridge_id)) { + ast_free(bridge_id); + return -1; + } + + new_wrapper = ao2_alloc_options(sizeof(*new_wrapper), + stasis_app_bridge_channel_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK); + if (!new_wrapper) { + return -1; + } + + if (ast_string_field_init(new_wrapper, 32)) { + return -1; + } + + ast_string_field_set(new_wrapper, bridge_id, bridge->uniqueid); + ast_string_field_set(new_wrapper, channel_id, ast_channel_uniqueid(chan)); + + if (!ao2_link(app_bridges_playback, new_wrapper)) { + return -1; + } + + ao2_link(app_controls, control); + return 0; +} + +struct ast_channel *stasis_app_bridge_playback_channel_find(struct ast_bridge *bridge) +{ + struct stasis_app_bridge_channel_wrapper *playback_wrapper; + struct ast_channel *chan; + + playback_wrapper = ao2_find(app_bridges_playback, bridge->uniqueid, OBJ_SEARCH_KEY); + if (!playback_wrapper) { + return NULL; + } + + chan = ast_channel_get_by_name(playback_wrapper->channel_id); + ao2_ref(playback_wrapper, -1); + return chan; +} + struct ast_bridge *stasis_app_bridge_find_by_id( const char *bridge_id) { @@ -720,13 +808,31 @@ static int send_end_msg(struct stasis_app *app, struct ast_channel *chan) void stasis_app_control_execute_until_exhausted(struct ast_channel *chan, struct stasis_app_control *control) { while (!control_is_done(control)) { - int command_count = control_dispatch_all(control, chan); + int command_count; + command_count = control_dispatch_all(control, chan); + + ao2_lock(control); + + if (control_command_count(control)) { + /* If the command queue isn't empty, something added to the queue before it was locked. */ + ao2_unlock(control); + continue; + } + if (command_count == 0 || ast_channel_fdno(chan) == -1) { + control_mark_done(control); + ao2_unlock(control); break; } + ao2_unlock(control); } } +int stasis_app_control_is_done(struct stasis_app_control *control) +{ + return control_is_done(control); +} + /*! /brief Stasis dialplan application callback */ int stasis_app_exec(struct ast_channel *chan, const char *app_name, int argc, char *argv[]) @@ -1230,6 +1336,9 @@ static int unload_module(void) ao2_cleanup(app_bridges_moh); app_bridges_moh = NULL; + ao2_cleanup(app_bridges_playback); + app_bridges_playback = NULL; + return 0; } @@ -1268,8 +1377,11 @@ static int load_module(void) app_bridges = ao2_container_alloc(BRIDGES_NUM_BUCKETS, bridges_hash, bridges_compare); app_bridges_moh = ao2_container_alloc_hash( AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, - 37, bridges_moh_hash_fn, bridges_moh_sort_fn, NULL); - if (!apps_registry || !app_controls || !app_bridges || !app_bridges_moh) { + 37, bridges_channel_hash_fn, bridges_channel_sort_fn, NULL); + app_bridges_playback = ao2_container_alloc_hash( + AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT, + 37, bridges_channel_hash_fn, bridges_channel_sort_fn, NULL); + if (!apps_registry || !app_controls || !app_bridges || !app_bridges_moh || !app_bridges_playback) { unload_module(); return AST_MODULE_LOAD_FAILURE; } |