summaryrefslogtreecommitdiff
path: root/res/ari
diff options
context:
space:
mode:
authorMatt Jordan <mjordan@digium.com>2016-04-18 18:17:08 -0500
committerJoshua Colp <jcolp@digium.com>2016-05-17 14:01:22 -0300
commit03d88b56565301d0552676ceb72f059b9267bca7 (patch)
treeb2dbce242a7c8323c8fd5110539fd6308af2bcb6 /res/ari
parent040522100b3332af877e496ab8316993f6f02b4e (diff)
ARI: Add the ability to play multiple media URIs in a single operation
Many ARI applications will want to play multiple media files in a row to a resource. The most common use case is when building long-ish IVR prompts made up of multiple, smaller sound files. Today, that requires building a small state machine, listening for each PlaybackFinished event, and triggering the next sound file to play. While not especially challenging, it is tedious work. Since requiring developers to write tedious code to do normal activities stinks, this patch adds the ability to play back a list of media files to a resource. Each of the 'play' operations on supported resources (channels and bridges) now accepts a comma delineated list of media URIs to play. A single Playback resource is created as a handle to the entire list. The operation of playing a list is identical to playing a single media URI, save that a new event, PlaybackContinuing, is raised instead of a PlaybackFinished for each non-final media URI. When the entire list is finished being played, a PlaybackFinished event is raised. In order to help inform applications where they are in the list playback, the Playback resource now includes a new, optional attribute, 'next_media_uri', that contains the next URI in the list to be played. It's important to note the following: - If an offset is provided to the 'play' operations, it only applies to the first media URI, as it would be weird to skip n seconds forward in every media resource. - Operations that control the position of the media only affect the current media being played. For example, once a media resource in the list completes, a 'reverse' operation on a subsequent media resource will not start a previously completed media resource at the appropiate offset. - This patch does not add any new operations to control the list. Hopefully, user feedback and/or future patches would add that if people want it. ASTERISK-26022 #close Change-Id: Ie1ea5356573447b8f51f2e7964915ea01792f16f
Diffstat (limited to 'res/ari')
-rw-r--r--res/ari/ari_model_validators.c94
-rw-r--r--res/ari/ari_model_validators.h24
-rw-r--r--res/ari/resource_bridges.c42
-rw-r--r--res/ari/resource_bridges.h20
-rw-r--r--res/ari/resource_channels.c7
-rw-r--r--res/ari/resource_channels.h20
6 files changed, 176 insertions, 31 deletions
diff --git a/res/ari/ari_model_validators.c b/res/ari/ari_model_validators.c
index 623d5b541..8f05db035 100644
--- a/res/ari/ari_model_validators.c
+++ b/res/ari/ari_model_validators.c
@@ -1744,6 +1744,15 @@ int ast_ari_validate_playback(struct ast_json *json)
res = 0;
}
} else
+ if (strcmp("next_media_uri", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI Playback field next_media_uri failed validation\n");
+ res = 0;
+ }
+ } else
if (strcmp("state", ast_json_object_iter_key(iter)) == 0) {
int prop_is_valid;
has_state = 1;
@@ -4741,6 +4750,9 @@ int ast_ari_validate_event(struct ast_json *json)
if (strcmp("PeerStatusChange", discriminator) == 0) {
return ast_ari_validate_peer_status_change(json);
} else
+ if (strcmp("PlaybackContinuing", discriminator) == 0) {
+ return ast_ari_validate_playback_continuing(json);
+ } else
if (strcmp("PlaybackFinished", discriminator) == 0) {
return ast_ari_validate_playback_finished(json);
} else
@@ -4930,6 +4942,9 @@ int ast_ari_validate_message(struct ast_json *json)
if (strcmp("PeerStatusChange", discriminator) == 0) {
return ast_ari_validate_peer_status_change(json);
} else
+ if (strcmp("PlaybackContinuing", discriminator) == 0) {
+ return ast_ari_validate_playback_continuing(json);
+ } else
if (strcmp("PlaybackFinished", discriminator) == 0) {
return ast_ari_validate_playback_finished(json);
} else
@@ -5216,6 +5231,85 @@ ari_validator ast_ari_validate_peer_status_change_fn(void)
return ast_ari_validate_peer_status_change;
}
+int ast_ari_validate_playback_continuing(struct ast_json *json)
+{
+ int res = 1;
+ struct ast_json_iter *iter;
+ int has_type = 0;
+ int has_application = 0;
+ int has_playback = 0;
+
+ for (iter = ast_json_object_iter(json); iter; iter = ast_json_object_iter_next(json, iter)) {
+ if (strcmp("type", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_type = 1;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI PlaybackContinuing field type failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("application", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_application = 1;
+ prop_is_valid = ast_ari_validate_string(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI PlaybackContinuing field application failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("timestamp", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ prop_is_valid = ast_ari_validate_date(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI PlaybackContinuing field timestamp failed validation\n");
+ res = 0;
+ }
+ } else
+ if (strcmp("playback", ast_json_object_iter_key(iter)) == 0) {
+ int prop_is_valid;
+ has_playback = 1;
+ prop_is_valid = ast_ari_validate_playback(
+ ast_json_object_iter_value(iter));
+ if (!prop_is_valid) {
+ ast_log(LOG_ERROR, "ARI PlaybackContinuing field playback failed validation\n");
+ res = 0;
+ }
+ } else
+ {
+ ast_log(LOG_ERROR,
+ "ARI PlaybackContinuing has undocumented field %s\n",
+ ast_json_object_iter_key(iter));
+ res = 0;
+ }
+ }
+
+ if (!has_type) {
+ ast_log(LOG_ERROR, "ARI PlaybackContinuing missing required field type\n");
+ res = 0;
+ }
+
+ if (!has_application) {
+ ast_log(LOG_ERROR, "ARI PlaybackContinuing missing required field application\n");
+ res = 0;
+ }
+
+ if (!has_playback) {
+ ast_log(LOG_ERROR, "ARI PlaybackContinuing missing required field playback\n");
+ res = 0;
+ }
+
+ return res;
+}
+
+ari_validator ast_ari_validate_playback_continuing_fn(void)
+{
+ return ast_ari_validate_playback_continuing;
+}
+
int ast_ari_validate_playback_finished(struct ast_json *json)
{
int res = 1;
diff --git a/res/ari/ari_model_validators.h b/res/ari/ari_model_validators.h
index 0bcdb0fa2..2634528ba 100644
--- a/res/ari/ari_model_validators.h
+++ b/res/ari/ari_model_validators.h
@@ -1187,6 +1187,24 @@ int ast_ari_validate_peer_status_change(struct ast_json *json);
ari_validator ast_ari_validate_peer_status_change_fn(void);
/*!
+ * \brief Validator for PlaybackContinuing.
+ *
+ * Event showing the continuation of a media playback operation from one media URI to the next in the list.
+ *
+ * \param json JSON object to validate.
+ * \returns True (non-zero) if valid.
+ * \returns False (zero) if invalid.
+ */
+int ast_ari_validate_playback_continuing(struct ast_json *json);
+
+/*!
+ * \brief Function pointer to ast_ari_validate_playback_continuing().
+ *
+ * See \ref ast_ari_model_validators.h for more details.
+ */
+ari_validator ast_ari_validate_playback_continuing_fn(void);
+
+/*!
* \brief Validator for PlaybackFinished.
*
* Event showing the completion of a media playback operation.
@@ -1457,6 +1475,7 @@ ari_validator ast_ari_validate_application_fn(void);
* - id: string (required)
* - language: string
* - media_uri: string (required)
+ * - next_media_uri: string
* - state: string (required)
* - target_uri: string (required)
* DeviceState
@@ -1670,6 +1689,11 @@ ari_validator ast_ari_validate_application_fn(void);
* - timestamp: Date
* - endpoint: Endpoint (required)
* - peer: Peer (required)
+ * PlaybackContinuing
+ * - type: string (required)
+ * - application: string (required)
+ * - timestamp: Date
+ * - playback: Playback (required)
* PlaybackFinished
* - type: string (required)
* - application: string (required)
diff --git a/res/ari/resource_bridges.c b/res/ari/resource_bridges.c
index 57c1c2738..cec443dba 100644
--- a/res/ari/resource_bridges.c
+++ b/res/ari/resource_bridges.c
@@ -332,7 +332,8 @@ static struct ast_channel *prepare_bridge_media_channel(const char *type)
* \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_media medias to play
+ * \param args_media_count number of media items in \c media
* \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
@@ -346,7 +347,8 @@ static struct ast_channel *prepare_bridge_media_channel(const char *type)
* \retval -1 operation failed
* \retval operation was successful
*/
-static int ari_bridges_play_helper(const char *args_media,
+static int ari_bridges_play_helper(const char **args_media,
+ size_t args_media_count,
const char *args_lang,
int args_offset_ms,
int args_skipms,
@@ -371,8 +373,8 @@ static int ari_bridges_play_helper(const char *args_media,
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,
+ playback = stasis_app_control_play_uri(control, args_media, args_media_count,
+ language, bridge->uniqueid, STASIS_PLAYBACK_TARGET_BRIDGE, args_skipms,
args_offset_ms, args_playback_id);
if (!playback) {
@@ -396,7 +398,8 @@ static int ari_bridges_play_helper(const char *args_media,
return 0;
}
-static void ari_bridges_play_new(const char *args_media,
+static void ari_bridges_play_new(const char **args_media,
+ size_t args_media_count,
const char *args_lang,
int args_offset_ms,
int args_skipms,
@@ -449,9 +452,9 @@ static void ari_bridges_play_new(const char *args_media,
}
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)) {
+ if (ari_bridges_play_helper(args_media, args_media_count, args_lang,
+ args_offset_ms, args_skipms, args_playback_id, response, bridge,
+ control, &json, &playback_url)) {
ao2_unlock(control);
return;
}
@@ -497,7 +500,8 @@ enum play_found_result {
* \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_media medias to play
+ * \param args_media_count number of media items in \c media
* \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
@@ -511,7 +515,8 @@ enum play_found_result {
* \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,
+static enum play_found_result ari_bridges_play_found(const char **args_media,
+ size_t args_media_count,
const char *args_lang,
int args_offset_ms,
int args_skipms,
@@ -537,9 +542,9 @@ static enum play_found_result ari_bridges_play_found(const char *args_media,
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)) {
+ if (ari_bridges_play_helper(args_media, args_media_count,
+ args_lang, args_offset_ms, args_skipms, args_playback_id,
+ response, bridge, control, &json, &playback_url)) {
ao2_unlock(control);
return PLAY_FOUND_FAILURE;
}
@@ -551,7 +556,8 @@ static enum play_found_result ari_bridges_play_found(const char *args_media,
static void ari_bridges_handle_play(
const char *args_bridge_id,
- const char *args_media,
+ const char **args_media,
+ size_t args_media_count,
const char *args_lang,
int args_offset_ms,
int args_skipms,
@@ -574,15 +580,15 @@ static void ari_bridges_handle_play(
* 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,
+ if (ari_bridges_play_found(args_media, args_media_count, 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,
+ ari_bridges_play_new(args_media, args_media_count, args_lang, args_offset_ms,
args_skipms, args_playback_id, response, bridge);
}
@@ -593,6 +599,7 @@ void ast_ari_bridges_play(struct ast_variable *headers,
{
ari_bridges_handle_play(args->bridge_id,
args->media,
+ args->media_count,
args->lang,
args->offsetms,
args->skipms,
@@ -606,6 +613,7 @@ void ast_ari_bridges_play_with_id(struct ast_variable *headers,
{
ari_bridges_handle_play(args->bridge_id,
args->media,
+ args->media_count,
args->lang,
args->offsetms,
args->skipms,
diff --git a/res/ari/resource_bridges.h b/res/ari/resource_bridges.h
index 36ff6a017..17a3b8365 100644
--- a/res/ari/resource_bridges.h
+++ b/res/ari/resource_bridges.h
@@ -245,11 +245,15 @@ void ast_ari_bridges_stop_moh(struct ast_variable *headers, struct ast_ari_bridg
struct ast_ari_bridges_play_args {
/*! Bridge's id */
const char *bridge_id;
- /*! Media's URI to play. */
- const char *media;
+ /*! Array of Media URIs to play. */
+ const char **media;
+ /*! Length of media array. */
+ size_t media_count;
+ /*! Parsing context for media. */
+ char *media_parse;
/*! For sounds, selects language for sound. */
const char *lang;
- /*! Number of media to skip before playing. */
+ /*! Number of milliseconds to skip before playing. Only applies to the first URI if multiple media URIs are specified. */
int offsetms;
/*! Number of milliseconds to skip for forward/reverse operations. */
int skipms;
@@ -283,11 +287,15 @@ struct ast_ari_bridges_play_with_id_args {
const char *bridge_id;
/*! Playback ID. */
const char *playback_id;
- /*! Media's URI to play. */
- const char *media;
+ /*! Array of Media URIs to play. */
+ const char **media;
+ /*! Length of media array. */
+ size_t media_count;
+ /*! Parsing context for media. */
+ char *media_parse;
/*! For sounds, selects language for sound. */
const char *lang;
- /*! Number of media to skip before playing. */
+ /*! Number of milliseconds to skip before playing. Only applies to the first URI if multiple media URIs are specified. */
int offsetms;
/*! Number of milliseconds to skip for forward/reverse operations. */
int skipms;
diff --git a/res/ari/resource_channels.c b/res/ari/resource_channels.c
index edf1a20e6..b42581c84 100644
--- a/res/ari/resource_channels.c
+++ b/res/ari/resource_channels.c
@@ -469,7 +469,8 @@ void ast_ari_channels_stop_silence(struct ast_variable *headers,
static void ari_channels_handle_play(
const char *args_channel_id,
- const char *args_media,
+ const char **args_media,
+ size_t args_media_count,
const char *args_lang,
int args_offsetms,
int args_skipms,
@@ -515,7 +516,7 @@ static void ari_channels_handle_play(
language = S_OR(args_lang, snapshot->language);
- playback = stasis_app_control_play_uri(control, args_media, language,
+ playback = stasis_app_control_play_uri(control, args_media, args_media_count, language,
args_channel_id, STASIS_PLAYBACK_TARGET_CHANNEL, args_skipms, args_offsetms, args_playback_id);
if (!playback) {
ast_ari_response_error(
@@ -551,6 +552,7 @@ void ast_ari_channels_play(struct ast_variable *headers,
ari_channels_handle_play(
args->channel_id,
args->media,
+ args->media_count,
args->lang,
args->offsetms,
args->skipms,
@@ -565,6 +567,7 @@ void ast_ari_channels_play_with_id(struct ast_variable *headers,
ari_channels_handle_play(
args->channel_id,
args->media,
+ args->media_count,
args->lang,
args->offsetms,
args->skipms,
diff --git a/res/ari/resource_channels.h b/res/ari/resource_channels.h
index 89b466d00..c690d70c8 100644
--- a/res/ari/resource_channels.h
+++ b/res/ari/resource_channels.h
@@ -505,11 +505,15 @@ void ast_ari_channels_stop_silence(struct ast_variable *headers, struct ast_ari_
struct ast_ari_channels_play_args {
/*! Channel's id */
const char *channel_id;
- /*! Media's URI to play. */
- const char *media;
+ /*! Array of Media URIs to play. */
+ const char **media;
+ /*! Length of media array. */
+ size_t media_count;
+ /*! Parsing context for media. */
+ char *media_parse;
/*! For sounds, selects language for sound. */
const char *lang;
- /*! Number of media to skip before playing. */
+ /*! Number of milliseconds to skip before playing. Only applies to the first URI if multiple media URIs are specified. */
int offsetms;
/*! Number of milliseconds to skip for forward/reverse operations. */
int skipms;
@@ -543,11 +547,15 @@ struct ast_ari_channels_play_with_id_args {
const char *channel_id;
/*! Playback ID. */
const char *playback_id;
- /*! Media's URI to play. */
- const char *media;
+ /*! Array of Media URIs to play. */
+ const char **media;
+ /*! Length of media array. */
+ size_t media_count;
+ /*! Parsing context for media. */
+ char *media_parse;
/*! For sounds, selects language for sound. */
const char *lang;
- /*! Number of media to skip before playing. */
+ /*! Number of milliseconds to skip before playing. Only applies to the first URI if multiple media URIs are specified. */
int offsetms;
/*! Number of milliseconds to skip for forward/reverse operations. */
int skipms;