summaryrefslogtreecommitdiff
path: root/res/ari
diff options
context:
space:
mode:
authorJonathan Rose <jrose@digium.com>2014-04-18 20:09:24 +0000
committerJonathan Rose <jrose@digium.com>2014-04-18 20:09:24 +0000
commitb9d7dfcc62c80d2b2827dd7b70701dfb21512c13 (patch)
tree5d564284aeb95084327ad944a2e358829e67ba6a /res/ari
parent06657c92e61340a9bc3e0d89fa676f93e6581ef1 (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/ari')
-rw-r--r--res/ari/resource_bridges.c255
-rw-r--r--res/ari/resource_bridges.h38
-rw-r--r--res/ari/resource_channels.c11
3 files changed, 258 insertions, 46 deletions
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index d78601fae..eeb4237d0 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -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;
}
diff --git a/res/ari/resource_bridges.h b/res/ari/resource_bridges.h
index 404760c91..f8cd6139a 100644
--- a/res/ari/resource_bridges.h
+++ b/res/ari/resource_bridges.h
@@ -253,6 +253,8 @@ struct ast_ari_bridges_play_args {
int offsetms;
/*! \brief Number of milliseconds to skip for forward/reverse operations. */
int skipms;
+ /*! \brief Playback Id. */
+ const char *playback_id;
};
/*!
* \brief Body parsing function for /bridges/{bridgeId}/play.
@@ -275,6 +277,42 @@ int ast_ari_bridges_play_parse_body(
* \param[out] response HTTP response
*/
void ast_ari_bridges_play(struct ast_variable *headers, struct ast_ari_bridges_play_args *args, struct ast_ari_response *response);
+/*! \brief Argument struct for ast_ari_bridges_play_with_id() */
+struct ast_ari_bridges_play_with_id_args {
+ /*! \brief Bridge's id */
+ const char *bridge_id;
+ /*! \brief Playback ID. */
+ const char *playback_id;
+ /*! \brief Media's URI to play. */
+ const char *media;
+ /*! \brief For sounds, selects language for sound. */
+ const char *lang;
+ /*! \brief Number of media to skip before playing. */
+ int offsetms;
+ /*! \brief Number of milliseconds to skip for forward/reverse operations. */
+ int skipms;
+};
+/*!
+ * \brief Body parsing function for /bridges/{bridgeId}/play/{playbackId}.
+ * \param body The JSON body from which to parse parameters.
+ * \param[out] args The args structure to parse into.
+ * \retval zero on success
+ * \retval non-zero on failure
+ */
+int ast_ari_bridges_play_with_id_parse_body(
+ struct ast_json *body,
+ struct ast_ari_bridges_play_with_id_args *args);
+
+/*!
+ * \brief Start playback of media on a bridge.
+ *
+ * The media URI may be any of a number of URI's. Currently sound: and recording: URI's are supported. This operation creates a playback resource that can be used to control the playback of media (pause, rewind, fast forward, etc.)
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP 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);
/*! \brief Argument struct for ast_ari_bridges_record() */
struct ast_ari_bridges_record_args {
/*! \brief Bridge's id */
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index 9d127b2ea..30ced1b2f 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -411,9 +411,9 @@ static void ari_channels_handle_play(
return;
}
- ast_asprintf(&playback_url, "/playback/%s",
- stasis_app_playback_get_id(playback));
- if (!playback_url) {
+ if (ast_asprintf(&playback_url, "/playback/%s",
+ stasis_app_playback_get_id(playback)) == -1) {
+ playback_url = NULL;
ast_ari_response_error(
response, 500, "Internal Server Error",
"Out of memory");
@@ -579,8 +579,9 @@ void ast_ari_channels_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_error(
response, 500, "Internal Server Error",
"Out of memory");