summaryrefslogtreecommitdiff
path: root/res
diff options
context:
space:
mode:
authorDavid M. Lee <dlee@digium.com>2013-05-23 20:11:35 +0000
committerDavid M. Lee <dlee@digium.com>2013-05-23 20:11:35 +0000
commit10ba6bf8a8114278ca974861ecebcb3a827d8d5b (patch)
tree1406ede124cf9665cb9f9244f9339b6cb522aa8b /res
parent3464e0919afe398717b93b20fff37560c6d4478f (diff)
This patch implements the REST API's for POST /channels/{channelId}/play
and GET /playback/{playbackId}. This allows an external application to initiate playback of a sound on a channel while the channel is in the Stasis application. /play commands are issued asynchronously, and return immediately with the URL of the associated /playback resource. Playback commands queue up, playing in succession. The /playback resource shows the state of a playback operation as enqueued, playing or complete. (Although the operation will only be in the 'complete' state for a very short time, since it is almost immediately freed up). (closes issue ASTERISK-21283) (closes issue ASTERISK-21586) Review: https://reviewboard.asterisk.org/r/2531/ git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389587 65c4cc65-6c06-0410-ace0-fbb531ad65f3
Diffstat (limited to 'res')
-rw-r--r--res/res_stasis_http.c9
-rw-r--r--res/res_stasis_http_channels.c3
-rw-r--r--res/res_stasis_json_events.c142
-rw-r--r--res/res_stasis_json_events.exports.in6
-rw-r--r--res/res_stasis_playback.c320
-rw-r--r--res/res_stasis_playback.exports.in6
-rw-r--r--res/stasis/control.c9
-rw-r--r--res/stasis_http/resource_channels.c56
-rw-r--r--res/stasis_http/resource_channels.h2
-rw-r--r--res/stasis_http/resource_playback.c15
-rw-r--r--res/stasis_json/resource_channels.h14
-rw-r--r--res/stasis_json/resource_events.h68
12 files changed, 575 insertions, 75 deletions
diff --git a/res/res_stasis_http.c b/res/res_stasis_http.c
index 75a13284e..63e2601e8 100644
--- a/res/res_stasis_http.c
+++ b/res/res_stasis_http.c
@@ -330,6 +330,15 @@ void stasis_http_response_alloc_failed(struct stasis_http_response *response)
response->response_text = "Internal Server Error";
}
+void stasis_http_response_created(struct stasis_http_response *response,
+ const char *url)
+{
+ response->message = ast_json_null();
+ response->response_code = 201;
+ response->response_text = "Created";
+ ast_str_append(&response->headers, 0, "Location: %s\r\n", url);
+}
+
static void add_allow_header(struct stasis_rest_handlers *handler,
struct stasis_http_response *response)
{
diff --git a/res/res_stasis_http_channels.c b/res/res_stasis_http_channels.c
index aa4481903..89c0cf09c 100644
--- a/res/res_stasis_http_channels.c
+++ b/res/res_stasis_http_channels.c
@@ -327,6 +327,9 @@ static void stasis_http_play_on_channel_cb(
if (strcmp(i->name, "media") == 0) {
args.media = (i->value);
} else
+ if (strcmp(i->name, "lang") == 0) {
+ args.lang = (i->value);
+ } else
{}
}
for (i = path_vars; i; i = i->next) {
diff --git a/res/res_stasis_json_events.c b/res/res_stasis_json_events.c
index 10d36be42..e96d84e4f 100644
--- a/res/res_stasis_json_events.c
+++ b/res/res_stasis_json_events.c
@@ -116,30 +116,18 @@ struct ast_json *stasis_json_event_bridge_created_create(
return ast_json_ref(message);
}
-struct ast_json *stasis_json_event_channel_destroyed_create(
- struct ast_channel_snapshot *channel_snapshot,
+struct ast_json *stasis_json_event_playback_finished_create(
struct ast_json *blob
)
{
RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
struct ast_json *validator;
- int ret;
- ast_assert(channel_snapshot != NULL);
ast_assert(blob != NULL);
- ast_assert(ast_json_object_get(blob, "channel") == NULL);
ast_assert(ast_json_object_get(blob, "type") == NULL);
- validator = ast_json_object_get(blob, "cause");
- if (validator) {
- /* do validation? XXX */
- } else {
- /* fail message generation if the required parameter doesn't exist */
- return NULL;
- }
-
- validator = ast_json_object_get(blob, "cause_txt");
+ validator = ast_json_object_get(blob, "playback");
if (validator) {
/* do validation? XXX */
} else {
@@ -152,13 +140,7 @@ struct ast_json *stasis_json_event_channel_destroyed_create(
return NULL;
}
- ret = ast_json_object_set(event,
- "channel", ast_channel_snapshot_to_json(channel_snapshot));
- if (ret) {
- return NULL;
- }
-
- message = ast_json_pack("{s: o}", "channel_destroyed", ast_json_ref(event));
+ message = ast_json_pack("{s: o}", "playback_finished", ast_json_ref(event));
if (!message) {
return NULL;
}
@@ -245,29 +227,23 @@ struct ast_json *stasis_json_event_channel_caller_id_create(
return ast_json_ref(message);
}
-struct ast_json *stasis_json_event_channel_hangup_request_create(
- struct ast_channel_snapshot *channel_snapshot,
+struct ast_json *stasis_json_event_playback_started_create(
struct ast_json *blob
)
{
RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
struct ast_json *validator;
- int ret;
- ast_assert(channel_snapshot != NULL);
ast_assert(blob != NULL);
- ast_assert(ast_json_object_get(blob, "channel") == NULL);
ast_assert(ast_json_object_get(blob, "type") == NULL);
- validator = ast_json_object_get(blob, "soft");
- if (validator) {
- /* do validation? XXX */
- }
-
- validator = ast_json_object_get(blob, "cause");
+ validator = ast_json_object_get(blob, "playback");
if (validator) {
/* do validation? XXX */
+ } else {
+ /* fail message generation if the required parameter doesn't exist */
+ return NULL;
}
event = ast_json_deep_copy(blob);
@@ -275,13 +251,7 @@ struct ast_json *stasis_json_event_channel_hangup_request_create(
return NULL;
}
- ret = ast_json_object_set(event,
- "channel", ast_channel_snapshot_to_json(channel_snapshot));
- if (ret) {
- return NULL;
- }
-
- message = ast_json_pack("{s: o}", "channel_hangup_request", ast_json_ref(event));
+ message = ast_json_pack("{s: o}", "playback_started", ast_json_ref(event));
if (!message) {
return NULL;
}
@@ -350,6 +320,56 @@ struct ast_json *stasis_json_event_application_replaced_create(
return ast_json_ref(message);
}
+struct ast_json *stasis_json_event_channel_destroyed_create(
+ struct ast_channel_snapshot *channel_snapshot,
+ struct ast_json *blob
+ )
+{
+ RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
+ RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
+ struct ast_json *validator;
+ int ret;
+
+ ast_assert(channel_snapshot != NULL);
+ ast_assert(blob != NULL);
+ ast_assert(ast_json_object_get(blob, "channel") == NULL);
+ ast_assert(ast_json_object_get(blob, "type") == NULL);
+
+ validator = ast_json_object_get(blob, "cause");
+ if (validator) {
+ /* do validation? XXX */
+ } else {
+ /* fail message generation if the required parameter doesn't exist */
+ return NULL;
+ }
+
+ validator = ast_json_object_get(blob, "cause_txt");
+ if (validator) {
+ /* do validation? XXX */
+ } else {
+ /* fail message generation if the required parameter doesn't exist */
+ return NULL;
+ }
+
+ event = ast_json_deep_copy(blob);
+ if (!event) {
+ return NULL;
+ }
+
+ ret = ast_json_object_set(event,
+ "channel", ast_channel_snapshot_to_json(channel_snapshot));
+ if (ret) {
+ return NULL;
+ }
+
+ message = ast_json_pack("{s: o}", "channel_destroyed", ast_json_ref(event));
+ if (!message) {
+ return NULL;
+ }
+
+ return ast_json_ref(message);
+}
+
struct ast_json *stasis_json_event_channel_varset_create(
struct ast_channel_snapshot *channel_snapshot,
struct ast_json *blob
@@ -587,6 +607,50 @@ struct ast_json *stasis_json_event_channel_state_change_create(
return ast_json_ref(message);
}
+struct ast_json *stasis_json_event_channel_hangup_request_create(
+ struct ast_channel_snapshot *channel_snapshot,
+ struct ast_json *blob
+ )
+{
+ RAII_VAR(struct ast_json *, message, NULL, ast_json_unref);
+ RAII_VAR(struct ast_json *, event, NULL, ast_json_unref);
+ struct ast_json *validator;
+ int ret;
+
+ ast_assert(channel_snapshot != NULL);
+ ast_assert(blob != NULL);
+ ast_assert(ast_json_object_get(blob, "channel") == NULL);
+ ast_assert(ast_json_object_get(blob, "type") == NULL);
+
+ validator = ast_json_object_get(blob, "soft");
+ if (validator) {
+ /* do validation? XXX */
+ }
+
+ validator = ast_json_object_get(blob, "cause");
+ if (validator) {
+ /* do validation? XXX */
+ }
+
+ event = ast_json_deep_copy(blob);
+ if (!event) {
+ return NULL;
+ }
+
+ ret = ast_json_object_set(event,
+ "channel", ast_channel_snapshot_to_json(channel_snapshot));
+ if (ret) {
+ return NULL;
+ }
+
+ message = ast_json_pack("{s: o}", "channel_hangup_request", ast_json_ref(event));
+ if (!message) {
+ return NULL;
+ }
+
+ return ast_json_ref(message);
+}
+
struct ast_json *stasis_json_event_channel_entered_bridge_create(
struct ast_bridge_snapshot *bridge_snapshot,
struct ast_channel_snapshot *channel_snapshot
diff --git a/res/res_stasis_json_events.exports.in b/res/res_stasis_json_events.exports.in
index e3f59ca76..8be4c849b 100644
--- a/res/res_stasis_json_events.exports.in
+++ b/res/res_stasis_json_events.exports.in
@@ -2,18 +2,20 @@
global:
LINKER_SYMBOL_PREFIXstasis_json_event_channel_userevent_create;
LINKER_SYMBOL_PREFIXstasis_json_event_bridge_created_create;
- LINKER_SYMBOL_PREFIXstasis_json_event_channel_destroyed_create;
+ LINKER_SYMBOL_PREFIXstasis_json_event_playback_finished_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_snapshot_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_caller_id_create;
- LINKER_SYMBOL_PREFIXstasis_json_event_channel_hangup_request_create;
+ LINKER_SYMBOL_PREFIXstasis_json_event_playback_started_create;
LINKER_SYMBOL_PREFIXstasis_json_event_bridge_destroyed_create;
LINKER_SYMBOL_PREFIXstasis_json_event_application_replaced_create;
+ LINKER_SYMBOL_PREFIXstasis_json_event_channel_destroyed_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_varset_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_left_bridge_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_created_create;
LINKER_SYMBOL_PREFIXstasis_json_event_stasis_start_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_dialplan_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_state_change_create;
+ LINKER_SYMBOL_PREFIXstasis_json_event_channel_hangup_request_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_entered_bridge_create;
LINKER_SYMBOL_PREFIXstasis_json_event_channel_dtmf_received_create;
LINKER_SYMBOL_PREFIXstasis_json_event_stasis_end_create;
diff --git a/res/res_stasis_playback.c b/res/res_stasis_playback.c
new file mode 100644
index 000000000..5f54a14b4
--- /dev/null
+++ b/res/res_stasis_playback.c
@@ -0,0 +1,320 @@
+/*
+ * Asterisk -- An open source telephony toolkit.
+ *
+ * Copyright (C) 2013, Digium, Inc.
+ *
+ * David M. Lee, II <dlee@digium.com>
+ *
+ * See http://www.asterisk.org for more information about
+ * the Asterisk project. Please do not directly contact
+ * any of the maintainers of this project for assistance;
+ * the project provides a web site, mailing lists and IRC
+ * channels for your use.
+ *
+ * This program is free software, distributed under the terms of
+ * the GNU General Public License Version 2. See the LICENSE file
+ * at the top of the source tree.
+ */
+
+/*! \file
+ *
+ * \brief res_stasis playback support.
+ *
+ * \author David M. Lee, II <dlee@digium.com>
+ */
+
+/*** MODULEINFO
+ <depend type="module">res_stasis</depend>
+ <support_level>core</support_level>
+ ***/
+
+#include "asterisk.h"
+
+ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+
+#include "asterisk/app.h"
+#include "asterisk/astobj2.h"
+#include "asterisk/file.h"
+#include "asterisk/logger.h"
+#include "asterisk/module.h"
+#include "asterisk/stasis_app_impl.h"
+#include "asterisk/stasis_app_playback.h"
+#include "asterisk/stasis_channels.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/uuid.h"
+
+/*! Number of hash buckets for playback container. Keep it prime! */
+#define PLAYBACK_BUCKETS 127
+
+/*! Number of milliseconds of media to skip */
+#define PLAYBACK_SKIPMS 250
+
+#define SOUND_URI_SCHEME "sound:"
+#define RECORDING_URI_SCHEME "recording:"
+
+STASIS_MESSAGE_TYPE_DEFN(stasis_app_playback_snapshot_type);
+
+/*! Container of all current playbacks */
+static struct ao2_container *playbacks;
+
+/*! Playback control object for res_stasis */
+struct stasis_app_playback {
+ AST_DECLARE_STRING_FIELDS(
+ AST_STRING_FIELD(id); /*!< Playback unique id */
+ AST_STRING_FIELD(media); /*!< Playback media uri */
+ AST_STRING_FIELD(language); /*!< Preferred language */
+ );
+ /*! Current playback state */
+ enum stasis_app_playback_state state;
+ /*! Control object for the channel we're playing back to */
+ struct stasis_app_control *control;
+};
+
+static int playback_hash(const void *obj, int flags)
+{
+ const struct stasis_app_playback *playback = obj;
+ const char *id = flags & OBJ_KEY ? obj : playback->id;
+ return ast_str_hash(id);
+}
+
+static int playback_cmp(void *obj, void *arg, int flags)
+{
+ struct stasis_app_playback *lhs = obj;
+ struct stasis_app_playback *rhs = arg;
+ const char *rhs_id = flags & OBJ_KEY ? arg : rhs->id;
+
+ if (strcmp(lhs->id, rhs_id) == 0) {
+ return CMP_MATCH | CMP_STOP;
+ } else {
+ return 0;
+ }
+}
+
+static const char *state_to_string(enum stasis_app_playback_state state)
+{
+ switch (state) {
+ case STASIS_PLAYBACK_STATE_QUEUED:
+ return "queued";
+ case STASIS_PLAYBACK_STATE_PLAYING:
+ return "playing";
+ case STASIS_PLAYBACK_STATE_COMPLETE:
+ return "done";
+ }
+
+ return "?";
+}
+
+static struct ast_json *playback_to_json(struct stasis_app_playback *playback)
+{
+ RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
+
+ if (playback == NULL) {
+ return NULL;
+ }
+
+ json = ast_json_pack("{s: s, s: s, s: s, s: s}",
+ "id", playback->id,
+ "media_uri", playback->media,
+ "language", playback->language,
+ "state", state_to_string(playback->state));
+
+ return ast_json_ref(json);
+}
+
+static void playback_publish(struct stasis_app_playback *playback)
+{
+ RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
+ RAII_VAR(struct ast_channel_snapshot *, snapshot, NULL, ao2_cleanup);
+ RAII_VAR(struct stasis_message *, message, NULL, ao2_cleanup);
+
+ ast_assert(playback != NULL);
+
+ json = playback_to_json(playback);
+ if (json == NULL) {
+ return;
+ }
+
+ message = ast_channel_blob_create_from_cache(
+ stasis_app_control_get_channel_id(playback->control),
+ stasis_app_playback_snapshot_type(), json);
+ if (message == NULL) {
+ return;
+ }
+
+ stasis_app_control_publish(playback->control, message);
+}
+
+static void playback_set_state(struct stasis_app_playback *playback,
+ enum stasis_app_playback_state state)
+{
+ SCOPED_AO2LOCK(lock, playback);
+
+ playback->state = state;
+ playback_publish(playback);
+}
+
+static void playback_cleanup(struct stasis_app_playback *playback)
+{
+ playback_set_state(playback, STASIS_PLAYBACK_STATE_COMPLETE);
+
+ ao2_unlink_flags(playbacks, playback,
+ OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
+}
+
+static void *__app_control_play_uri(struct stasis_app_control *control,
+ struct ast_channel *chan, void *data)
+{
+ RAII_VAR(struct stasis_app_playback *, playback, NULL,
+ playback_cleanup);
+ RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
+ const char *file;
+ int res;
+ /* Even though these local variables look fairly pointless, the avoid
+ * having a bunch of NULL's passed directly into
+ * ast_control_streamfile() */
+ const char *fwd = NULL;
+ const char *rev = NULL;
+ const char *stop = NULL;
+ const char *pause = NULL;
+ const char *restart = NULL;
+ int skipms = PLAYBACK_SKIPMS;
+ long offsetms = 0;
+
+ playback = data;
+ ast_assert(playback != NULL);
+
+ playback_set_state(playback, STASIS_PLAYBACK_STATE_PLAYING);
+
+ if (ast_channel_state(chan) != AST_STATE_UP) {
+ ast_answer(chan);
+ }
+
+ if (ast_begins_with(playback->media, SOUND_URI_SCHEME)) {
+ /* Play sound */
+ file = playback->media + strlen(SOUND_URI_SCHEME);
+ } else if (ast_begins_with(playback->media, RECORDING_URI_SCHEME)) {
+ /* Play recording */
+ file = playback->media + strlen(RECORDING_URI_SCHEME);
+ } else {
+ /* Play URL */
+ ast_log(LOG_ERROR, "Unimplemented\n");
+ return NULL;
+ }
+
+ res = ast_control_streamfile(chan, file, fwd, rev, stop, pause,
+ restart, skipms, &offsetms);
+
+ if (res != 0) {
+ ast_log(LOG_WARNING, "%s: Playback failed for %s",
+ ast_channel_uniqueid(chan), playback->media);
+ }
+
+ return NULL;
+}
+
+static void playback_dtor(void *obj)
+{
+ struct stasis_app_playback *playback = obj;
+
+ ast_string_field_free_memory(playback);
+}
+
+struct stasis_app_playback *stasis_app_control_play_uri(
+ struct stasis_app_control *control, const char *uri,
+ const char *language)
+{
+ RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
+ char id[AST_UUID_STR_LEN];
+
+ ast_debug(3, "%s: Sending play(%s) command\n",
+ stasis_app_control_get_channel_id(control), uri);
+
+ playback = ao2_alloc(sizeof(*playback), playback_dtor);
+ if (!playback || ast_string_field_init(playback, 128) ){
+ return NULL;
+ }
+
+ ast_uuid_generate_str(id, sizeof(id));
+ ast_string_field_set(playback, id, id);
+ ast_string_field_set(playback, media, uri);
+ ast_string_field_set(playback, language, language);
+ playback->control = control;
+ ao2_link(playbacks, playback);
+
+ playback_set_state(playback, STASIS_PLAYBACK_STATE_QUEUED);
+
+ ao2_ref(playback, +1);
+ stasis_app_send_command_async(
+ control, __app_control_play_uri, playback);
+
+
+ ao2_ref(playback, +1);
+ return playback;
+}
+
+enum stasis_app_playback_state stasis_app_playback_get_state(
+ struct stasis_app_playback *control)
+{
+ SCOPED_AO2LOCK(lock, control);
+ return control->state;
+}
+
+const char *stasis_app_playback_get_id(
+ struct stasis_app_playback *control)
+{
+ /* id is immutable; no lock needed */
+ return control->id;
+}
+
+struct ast_json *stasis_app_playback_find_by_id(const char *id)
+{
+ RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
+ RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
+
+ playback = ao2_find(playbacks, id, OBJ_KEY);
+ if (playback == NULL) {
+ return NULL;
+ }
+
+ json = playback_to_json(playback);
+ return ast_json_ref(json);
+}
+
+int stasis_app_playback_control(struct stasis_app_playback *playback,
+ enum stasis_app_playback_media_control control)
+{
+ SCOPED_AO2LOCK(lock, playback);
+ ast_assert(0); /* TODO */
+ return -1;
+}
+
+static int load_module(void)
+{
+ int r;
+
+ r = STASIS_MESSAGE_TYPE_INIT(stasis_app_playback_snapshot_type);
+ if (r != 0) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+
+ playbacks = ao2_container_alloc(PLAYBACK_BUCKETS, playback_hash,
+ playback_cmp);
+ if (!playbacks) {
+ return AST_MODULE_LOAD_FAILURE;
+ }
+ return AST_MODULE_LOAD_SUCCESS;
+}
+
+static int unload_module(void)
+{
+ ao2_cleanup(playbacks);
+ playbacks = NULL;
+ STASIS_MESSAGE_TYPE_CLEANUP(stasis_app_playback_snapshot_type);
+ return 0;
+}
+
+AST_MODULE_INFO(ASTERISK_GPL_KEY, AST_MODFLAG_GLOBAL_SYMBOLS,
+ "Stasis application playback support",
+ .load = load_module,
+ .unload = unload_module,
+ .nonoptreq = "res_stasis");
diff --git a/res/res_stasis_playback.exports.in b/res/res_stasis_playback.exports.in
new file mode 100644
index 000000000..0ad493c49
--- /dev/null
+++ b/res/res_stasis_playback.exports.in
@@ -0,0 +1,6 @@
+{
+ global:
+ LINKER_SYMBOL_PREFIXstasis_app_*;
+ local:
+ *;
+};
diff --git a/res/stasis/control.c b/res/stasis/control.c
index e32781b5f..1cc818616 100644
--- a/res/stasis/control.c
+++ b/res/stasis/control.c
@@ -56,7 +56,8 @@ struct stasis_app_control *control_create(struct ast_channel *channel)
return NULL;
}
- control->command_queue = ao2_container_alloc_list(0, 0, NULL, NULL);
+ control->command_queue = ao2_container_alloc_list(
+ AO2_ALLOC_OPT_LOCK_MUTEX, 0, NULL, NULL);
control->channel = channel;
@@ -75,10 +76,8 @@ static struct stasis_app_command *exec_command(
return NULL;
}
- ao2_lock(control);
- ao2_ref(command, +1);
+ /* command_queue is a thread safe list; no lock needed */
ao2_link(control->command_queue, command);
- ao2_unlock(control);
ao2_ref(command, +1);
return command;
@@ -182,8 +181,6 @@ int control_dispatch_all(struct stasis_app_control *control,
struct ao2_iterator i;
void *obj;
- SCOPED_AO2LOCK(lock, control);
-
ast_assert(control->channel == chan);
i = ao2_iterator_init(control->command_queue, AO2_ITERATOR_UNLINK);
diff --git a/res/stasis_http/resource_channels.c b/res/stasis_http/resource_channels.c
index 3cc97c511..4e404dc1d 100644
--- a/res/stasis_http/resource_channels.c
+++ b/res/stasis_http/resource_channels.c
@@ -24,6 +24,7 @@
*/
/*** MODULEINFO
+ <depend type="module">res_stasis_app_playback</depend>
<support_level>core</support_level>
***/
@@ -31,10 +32,14 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk/file.h"
#include "asterisk/stasis_app.h"
+#include "asterisk/stasis_app_playback.h"
#include "asterisk/stasis_channels.h"
#include "resource_channels.h"
+#include <limits.h>
+
/*!
* \brief Finds the control object for a channel, filling the response with an
* error, if appropriate.
@@ -131,9 +136,53 @@ void stasis_http_unhold_channel(struct ast_variable *headers, struct ast_unhold_
{
ast_log(LOG_ERROR, "TODO: stasis_http_unhold_channel\n");
}
-void stasis_http_play_on_channel(struct ast_variable *headers, struct ast_play_on_channel_args *args, struct stasis_http_response *response)
+
+void stasis_http_play_on_channel(struct ast_variable *headers,
+ struct ast_play_on_channel_args *args,
+ struct stasis_http_response *response)
{
- ast_log(LOG_ERROR, "TODO: stasis_http_play_on_channel\n");
+ 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;
+
+ ast_assert(response != NULL);
+
+ control = find_control(response, args->channel_id);
+ if (control == NULL) {
+ /* Response filled in by find_control */
+ return;
+ }
+
+ snapshot = stasis_app_control_get_snapshot(control);
+ if (!snapshot) {
+ stasis_http_response_error(
+ response, 404, "Not Found",
+ "Channel not found");
+ return;
+ }
+
+ language = S_OR(args->lang, snapshot->language);
+
+ playback = stasis_app_control_play_uri(control, args->media, language);
+ if (!playback) {
+ stasis_http_response_error(
+ response, 500, "Internal Server Error",
+ "Failed to answer channel");
+ return;
+ }
+
+ ast_asprintf(&playback_url, "/playback/%s",
+ stasis_app_playback_get_id(playback));
+ if (!playback_url) {
+ stasis_http_response_error(
+ response, 500, "Internal Server Error",
+ "Out of memory");
+ return;
+ }
+
+ stasis_http_response_created(response, playback_url);
}
void stasis_http_record_channel(struct ast_variable *headers, struct ast_record_channel_args *args, struct stasis_http_response *response)
{
@@ -143,8 +192,8 @@ void stasis_http_get_channel(struct ast_variable *headers,
struct ast_get_channel_args *args,
struct stasis_http_response *response)
{
- RAII_VAR(struct stasis_caching_topic *, caching_topic, NULL, ao2_cleanup);
RAII_VAR(struct stasis_message *, msg, NULL, ao2_cleanup);
+ struct stasis_caching_topic *caching_topic;
struct ast_channel_snapshot *snapshot;
caching_topic = ast_channel_topic_all_cached();
@@ -154,7 +203,6 @@ void stasis_http_get_channel(struct ast_variable *headers,
"Message bus not initialized");
return;
}
- ao2_ref(caching_topic, +1);
msg = stasis_cache_get(caching_topic, ast_channel_snapshot_type(),
args->channel_id);
diff --git a/res/stasis_http/resource_channels.h b/res/stasis_http/resource_channels.h
index 2c78589b7..8a35072f0 100644
--- a/res/stasis_http/resource_channels.h
+++ b/res/stasis_http/resource_channels.h
@@ -200,6 +200,8 @@ struct ast_play_on_channel_args {
const char *channel_id;
/*! \brief Media's URI to play. */
const char *media;
+ /*! \brief For sounds, selects language for sound */
+ const char *lang;
};
/*!
* \brief Start playback of media.
diff --git a/res/stasis_http/resource_playback.c b/res/stasis_http/resource_playback.c
index 99f2e09eb..f016a0095 100644
--- a/res/stasis_http/resource_playback.c
+++ b/res/stasis_http/resource_playback.c
@@ -27,11 +27,22 @@
ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
+#include "asterisk/stasis_app_playback.h"
#include "resource_playback.h"
-void stasis_http_get_playback(struct ast_variable *headers, struct ast_get_playback_args *args, struct stasis_http_response *response)
+void stasis_http_get_playback(struct ast_variable *headers,
+ struct ast_get_playback_args *args,
+ struct stasis_http_response *response)
{
- ast_log(LOG_ERROR, "TODO: stasis_http_get_playback\n");
+ RAII_VAR(struct ast_json *, playback, NULL, ast_json_unref);
+ playback = stasis_app_playback_find_by_id(args->playback_id);
+ if (playback == NULL) {
+ stasis_http_response_error(response, 404, "Not Found",
+ "Playback not found");
+ return;
+ }
+
+ stasis_http_response_ok(response, ast_json_ref(playback));
}
void stasis_http_stop_playback(struct ast_variable *headers, struct ast_stop_playback_args *args, struct stasis_http_response *response)
{
diff --git a/res/stasis_json/resource_channels.h b/res/stasis_json/resource_channels.h
index 45e1031fc..c561d306a 100644
--- a/res/stasis_json/resource_channels.h
+++ b/res/stasis_json/resource_channels.h
@@ -40,7 +40,17 @@
/*
* JSON models
*
+ * CallerID
+ * - name: string (required)
+ * - number: string (required)
+ * Dialed
* Originated
+ * Playback
+ * - language: string
+ * - media_uri: string (required)
+ * - id: string (required)
+ * - target_uri: string (required)
+ * - state: string (required)
* DialplanCEP
* - priority: long (required)
* - exten: string (required)
@@ -61,10 +71,6 @@
* - hangupsource: string (required)
* - dialplan: DialplanCEP (required)
* - data: string (required)
- * CallerID
- * - name: string (required)
- * - number: string (required)
- * Dialed
*/
#endif /* _ASTERISK_RESOURCE_CHANNELS_H */
diff --git a/res/stasis_json/resource_events.h b/res/stasis_json/resource_events.h
index 63abe0f85..d63181788 100644
--- a/res/stasis_json/resource_events.h
+++ b/res/stasis_json/resource_events.h
@@ -68,18 +68,15 @@ struct ast_json *stasis_json_event_bridge_created_create(
);
/*!
- * \brief Notification that a channel has been destroyed.
+ * \brief Event showing the completion of a media playback operation.
*
- * \param channel The channel to be used to generate this event
* \param blob JSON blob containing the following parameters:
- * - cause: integer - Integer representation of the cause of the hangup (required)
- * - cause_txt: string - Text representation of the cause of the hangup (required)
+ * - playback: Playback - Playback control object (required)
*
* \retval NULL on error
* \retval JSON (ast_json) describing the event
*/
-struct ast_json *stasis_json_event_channel_destroyed_create(
- struct ast_channel_snapshot *channel_snapshot,
+struct ast_json *stasis_json_event_playback_finished_create(
struct ast_json *blob
);
@@ -112,18 +109,15 @@ struct ast_json *stasis_json_event_channel_caller_id_create(
);
/*!
- * \brief A hangup was requested on the channel.
+ * \brief Event showing the start of a media playback operation.
*
- * \param channel The channel on which the hangup was requested.
* \param blob JSON blob containing the following parameters:
- * - soft: boolean - Whether the hangup request was a soft hangup request.
- * - cause: integer - Integer representation of the cause of the hangup.
+ * - playback: Playback - Playback control object (required)
*
* \retval NULL on error
* \retval JSON (ast_json) describing the event
*/
-struct ast_json *stasis_json_event_channel_hangup_request_create(
- struct ast_channel_snapshot *channel_snapshot,
+struct ast_json *stasis_json_event_playback_started_create(
struct ast_json *blob
);
@@ -153,6 +147,22 @@ struct ast_json *stasis_json_event_application_replaced_create(
);
/*!
+ * \brief Notification that a channel has been destroyed.
+ *
+ * \param channel The channel to be used to generate this event
+ * \param blob JSON blob containing the following parameters:
+ * - cause: integer - Integer representation of the cause of the hangup (required)
+ * - cause_txt: string - Text representation of the cause of the hangup (required)
+ *
+ * \retval NULL on error
+ * \retval JSON (ast_json) describing the event
+ */
+struct ast_json *stasis_json_event_channel_destroyed_create(
+ struct ast_channel_snapshot *channel_snapshot,
+ struct ast_json *blob
+ );
+
+/*!
* \brief Channel variable changed.
*
* \param channel The channel on which the variable was set.
@@ -238,6 +248,22 @@ struct ast_json *stasis_json_event_channel_state_change_create(
);
/*!
+ * \brief A hangup was requested on the channel.
+ *
+ * \param channel The channel on which the hangup was requested.
+ * \param blob JSON blob containing the following parameters:
+ * - soft: boolean - Whether the hangup request was a soft hangup request.
+ * - cause: integer - Integer representation of the cause of the hangup.
+ *
+ * \retval NULL on error
+ * \retval JSON (ast_json) describing the event
+ */
+struct ast_json *stasis_json_event_channel_hangup_request_create(
+ struct ast_channel_snapshot *channel_snapshot,
+ struct ast_json *blob
+ );
+
+/*!
* \brief Notification that a channel has entered a bridge.
*
* \param channel The channel to be used to generate this event
@@ -284,19 +310,20 @@ struct ast_json *stasis_json_event_stasis_end_create(
* ChannelUserevent
* - eventname: string (required)
* BridgeCreated
- * ChannelDestroyed
- * - cause: integer (required)
- * - cause_txt: string (required)
+ * PlaybackFinished
+ * - playback: Playback (required)
* ChannelSnapshot
* ChannelCallerId
* - caller_presentation_txt: string (required)
* - caller_presentation: integer (required)
- * ChannelHangupRequest
- * - soft: boolean
- * - cause: integer
+ * PlaybackStarted
+ * - playback: Playback (required)
* BridgeDestroyed
* ApplicationReplaced
* - application: string (required)
+ * ChannelDestroyed
+ * - cause: integer (required)
+ * - cause_txt: string (required)
* ChannelVarset
* - variable: string (required)
* - value: string (required)
@@ -308,6 +335,9 @@ struct ast_json *stasis_json_event_stasis_end_create(
* - application: string (required)
* - application_data: string (required)
* ChannelStateChange
+ * ChannelHangupRequest
+ * - soft: boolean
+ * - cause: integer
* ChannelEnteredBridge
* ChannelDtmfReceived
* - digit: string (required)
@@ -325,10 +355,12 @@ struct ast_json *stasis_json_event_stasis_end_create(
* - application: string (required)
* - channel_hangup_request: ChannelHangupRequest
* - channel_userevent: ChannelUserevent
+ * - playback_started: PlaybackStarted
* - channel_snapshot: ChannelSnapshot
* - channel_dtmf_received: ChannelDtmfReceived
* - channel_caller_id: ChannelCallerId
* - bridge_destroyed: BridgeDestroyed
+ * - playback_finished: PlaybackFinished
* - stasis_end: StasisEnd
* StasisEnd
*/