mirror of
https://github.com/asterisk/asterisk.git
synced 2025-09-29 18:19:30 +00:00
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
This commit is contained in:
@@ -320,31 +320,93 @@ static struct ast_channel *prepare_bridge_media_channel(const char *type)
|
||||
return ast_request(type, cap, NULL, NULL, "ARI", NULL);
|
||||
}
|
||||
|
||||
void ast_ari_bridges_play(struct ast_variable *headers,
|
||||
struct ast_ari_bridges_play_args *args,
|
||||
struct ast_ari_response *response)
|
||||
/*!
|
||||
* \brief Performs common setup for a bridge playback operation
|
||||
* with both new controls and when existing controls are found.
|
||||
*
|
||||
* \param args_media media string split from arguments
|
||||
* \param args_lang language string split from arguments
|
||||
* \param args_offset_ms milliseconds offset split from arguments
|
||||
* \param args_playback_id string to use for playback split from
|
||||
* arguments (null valid)
|
||||
* \param response ARI response being built
|
||||
* \param bridge Bridge the playback is being peformed on
|
||||
* \param control Control being used for the playback channel
|
||||
* \param json contents of the response to ARI
|
||||
* \param playback_url stores playback URL for use with response
|
||||
*
|
||||
* \retval -1 operation failed
|
||||
* \retval operation was successful
|
||||
*/
|
||||
static int ari_bridges_play_helper(const char *args_media,
|
||||
const char *args_lang,
|
||||
int args_offset_ms,
|
||||
int args_skipms,
|
||||
const char *args_playback_id,
|
||||
struct ast_ari_response *response,
|
||||
struct ast_bridge *bridge,
|
||||
struct stasis_app_control *control,
|
||||
struct ast_json **json,
|
||||
char **playback_url)
|
||||
{
|
||||
RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
|
||||
RAII_VAR(struct ast_channel *, play_channel, NULL, ast_hangup);
|
||||
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
|
||||
RAII_VAR(char *, playback_url, NULL, ast_free);
|
||||
|
||||
const char *language;
|
||||
|
||||
snapshot = stasis_app_control_get_snapshot(control);
|
||||
if (!snapshot) {
|
||||
ast_ari_response_error(
|
||||
response, 500, "Internal Error", "Failed to get control snapshot");
|
||||
return -1;
|
||||
}
|
||||
|
||||
language = S_OR(args_lang, snapshot->language);
|
||||
|
||||
playback = stasis_app_control_play_uri(control, args_media, language,
|
||||
bridge->uniqueid, STASIS_PLAYBACK_TARGET_BRIDGE, args_skipms,
|
||||
args_offset_ms, args_playback_id);
|
||||
|
||||
if (!playback) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
return -1;
|
||||
}
|
||||
|
||||
if (ast_asprintf(playback_url, "/playback/%s",
|
||||
stasis_app_playback_get_id(playback)) == -1) {
|
||||
playback_url = NULL;
|
||||
ast_ari_response_alloc_failed(response);
|
||||
return -1;
|
||||
}
|
||||
|
||||
*json = stasis_app_playback_to_json(playback);
|
||||
if (!*json) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
return -1;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void ari_bridges_play_new(const char *args_media,
|
||||
const char *args_lang,
|
||||
int args_offset_ms,
|
||||
int args_skipms,
|
||||
const char *args_playback_id,
|
||||
struct ast_ari_response *response,
|
||||
struct ast_bridge *bridge)
|
||||
{
|
||||
RAII_VAR(struct ast_channel *, play_channel, NULL, ast_hangup);
|
||||
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
RAII_VAR(struct stasis_forward *, channel_forward, NULL, stasis_forward_cancel);
|
||||
RAII_VAR(char *, playback_url, NULL, ast_free);
|
||||
|
||||
struct stasis_topic *channel_topic;
|
||||
struct stasis_topic *bridge_topic;
|
||||
struct bridge_channel_control_thread_data *thread_data;
|
||||
const char *language;
|
||||
pthread_t threadid;
|
||||
|
||||
ast_assert(response != NULL);
|
||||
|
||||
if (!bridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (!(play_channel = prepare_bridge_media_channel("Announcer"))) {
|
||||
ast_ari_response_error(
|
||||
response, 500, "Internal Error", "Could not create playback channel");
|
||||
@@ -378,34 +440,16 @@ void ast_ari_bridges_play(struct ast_variable *headers,
|
||||
return;
|
||||
}
|
||||
|
||||
snapshot = stasis_app_control_get_snapshot(control);
|
||||
if (!snapshot) {
|
||||
ast_ari_response_error(
|
||||
response, 500, "Internal Error", "Failed to get control snapshot");
|
||||
ao2_lock(control);
|
||||
if (ari_bridges_play_helper(args_media, args_lang, args_offset_ms,
|
||||
args_skipms, args_playback_id, response, bridge, control,
|
||||
&json, &playback_url)) {
|
||||
ao2_unlock(control);
|
||||
return;
|
||||
}
|
||||
ao2_unlock(control);
|
||||
|
||||
language = S_OR(args->lang, snapshot->language);
|
||||
|
||||
playback = stasis_app_control_play_uri(control, args->media, language,
|
||||
args->bridge_id, STASIS_PLAYBACK_TARGET_BRIDGE, args->skipms,
|
||||
args->offsetms, NULL);
|
||||
|
||||
if (!playback) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
|
||||
ast_asprintf(&playback_url, "/playback/%s",
|
||||
stasis_app_playback_get_id(playback));
|
||||
|
||||
if (!playback_url) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
|
||||
json = stasis_app_playback_to_json(playback);
|
||||
if (!json) {
|
||||
if (stasis_app_bridge_playback_channel_add(bridge, play_channel, control)) {
|
||||
ast_ari_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
@@ -435,6 +479,134 @@ void ast_ari_bridges_play(struct ast_variable *headers,
|
||||
ast_ari_response_created(response, playback_url, ast_json_ref(json));
|
||||
}
|
||||
|
||||
enum play_found_result {
|
||||
PLAY_FOUND_SUCCESS,
|
||||
PLAY_FOUND_FAILURE,
|
||||
PLAY_FOUND_CHANNEL_UNAVAILABLE,
|
||||
};
|
||||
|
||||
/*!
|
||||
* \brief Performs common setup for a bridge playback operation
|
||||
* with both new controls and when existing controls are found.
|
||||
*
|
||||
* \param args_media media string split from arguments
|
||||
* \param args_lang language string split from arguments
|
||||
* \param args_offset_ms milliseconds offset split from arguments
|
||||
* \param args_playback_id string to use for playback split from
|
||||
* arguments (null valid)
|
||||
* \param response ARI response being built
|
||||
* \param bridge Bridge the playback is being peformed on
|
||||
* \param found_channel The channel that was found controlling playback
|
||||
*
|
||||
* \retval PLAY_FOUND_SUCCESS The operation was successful
|
||||
* \retval PLAY_FOUND_FAILURE The operation failed (terminal failure)
|
||||
* \retval PLAY_FOUND_CHANNEL_UNAVAILABLE The operation failed because
|
||||
* the channel requested to playback with is breaking down.
|
||||
*/
|
||||
static enum play_found_result ari_bridges_play_found(const char *args_media,
|
||||
const char *args_lang,
|
||||
int args_offset_ms,
|
||||
int args_skipms,
|
||||
const char *args_playback_id,
|
||||
struct ast_ari_response *response,
|
||||
struct ast_bridge *bridge,
|
||||
struct ast_channel *found_channel)
|
||||
{
|
||||
RAII_VAR(struct ast_channel *, play_channel, found_channel, ao2_cleanup);
|
||||
RAII_VAR(struct stasis_app_control *, control, NULL, ao2_cleanup);
|
||||
RAII_VAR(char *, playback_url, NULL, ast_free);
|
||||
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
|
||||
|
||||
control = stasis_app_control_find_by_channel(play_channel);
|
||||
if (!control) {
|
||||
ast_ari_response_error(
|
||||
response, 500, "Internal Error", "Failed to get control snapshot");
|
||||
return PLAY_FOUND_FAILURE;
|
||||
}
|
||||
|
||||
ao2_lock(control);
|
||||
if (stasis_app_control_is_done(control)) {
|
||||
/* We failed to queue the action. Bailout and return that we aren't terminal. */
|
||||
ao2_unlock(control);
|
||||
return PLAY_FOUND_CHANNEL_UNAVAILABLE;
|
||||
}
|
||||
|
||||
if (ari_bridges_play_helper(args_media, args_lang, args_offset_ms,
|
||||
args_skipms, args_playback_id, response, bridge, control,
|
||||
&json, &playback_url)) {
|
||||
ao2_unlock(control);
|
||||
return PLAY_FOUND_FAILURE;
|
||||
}
|
||||
ao2_unlock(control);
|
||||
|
||||
ast_ari_response_created(response, playback_url, ast_json_ref(json));
|
||||
return PLAY_FOUND_SUCCESS;
|
||||
}
|
||||
|
||||
static void ari_bridges_handle_play(
|
||||
const char *args_bridge_id,
|
||||
const char *args_media,
|
||||
const char *args_lang,
|
||||
int args_offset_ms,
|
||||
int args_skipms,
|
||||
const char *args_playback_id,
|
||||
struct ast_ari_response *response)
|
||||
{
|
||||
RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args_bridge_id), ao2_cleanup);
|
||||
struct ast_channel *play_channel;
|
||||
|
||||
ast_assert(response != NULL);
|
||||
|
||||
if (!bridge) {
|
||||
return;
|
||||
}
|
||||
|
||||
while ((play_channel = stasis_app_bridge_playback_channel_find(bridge))) {
|
||||
/* If ari_bridges_play_found fails because the channel is unavailable for
|
||||
* playback, The channel will be removed from the playback list soon. We
|
||||
* can keep trying to get channels from the list until we either get one
|
||||
* that will work or else there isn't a channel for this bridge anymore,
|
||||
* in which case we'll revert to ari_bridges_play_new.
|
||||
*/
|
||||
if (ari_bridges_play_found(args_media, args_lang, args_offset_ms,
|
||||
args_skipms, args_playback_id, response,bridge,
|
||||
play_channel) == PLAY_FOUND_CHANNEL_UNAVAILABLE) {
|
||||
continue;
|
||||
}
|
||||
return;
|
||||
}
|
||||
|
||||
ari_bridges_play_new(args_media, args_lang, args_offset_ms,
|
||||
args_skipms, args_playback_id, response, bridge);
|
||||
}
|
||||
|
||||
|
||||
void ast_ari_bridges_play(struct ast_variable *headers,
|
||||
struct ast_ari_bridges_play_args *args,
|
||||
struct ast_ari_response *response)
|
||||
{
|
||||
ari_bridges_handle_play(args->bridge_id,
|
||||
args->media,
|
||||
args->lang,
|
||||
args->offsetms,
|
||||
args->skipms,
|
||||
args->playback_id,
|
||||
response);
|
||||
}
|
||||
|
||||
void ast_ari_bridges_play_with_id(struct ast_variable *headers,
|
||||
struct ast_ari_bridges_play_with_id_args *args,
|
||||
struct ast_ari_response *response)
|
||||
{
|
||||
ari_bridges_handle_play(args->bridge_id,
|
||||
args->media,
|
||||
args->lang,
|
||||
args->offsetms,
|
||||
args->skipms,
|
||||
args->playback_id,
|
||||
response);
|
||||
}
|
||||
|
||||
void ast_ari_bridges_record(struct ast_variable *headers,
|
||||
struct ast_ari_bridges_record_args *args,
|
||||
struct ast_ari_response *response)
|
||||
@@ -573,8 +745,9 @@ void ast_ari_bridges_record(struct ast_variable *headers,
|
||||
}
|
||||
ast_uri_encode(args->name, uri_encoded_name, uri_name_maxlen, ast_uri_http);
|
||||
|
||||
ast_asprintf(&recording_url, "/recordings/live/%s", uri_encoded_name);
|
||||
if (!recording_url) {
|
||||
if (ast_asprintf(&recording_url, "/recordings/live/%s",
|
||||
uri_encoded_name) == -1) {
|
||||
recording_url = NULL;
|
||||
ast_ari_response_alloc_failed(response);
|
||||
return;
|
||||
}
|
||||
|
Reference in New Issue
Block a user