This patch adds support for controlling a playback operation from the

Asterisk REST interface.

This adds the /playback/{playbackId}/control resource, which may be
POSTed to to pause, unpause, reverse, forward or restart the media
playback.

Attempts to control a playback that is not currently playing will
either return a 404 Not Found (because the playback object no longer
exists) or a 409 Conflict (because the playback object is still in the
queue to be played).

This patch also adds skipms and offsetms parameters to the
/channels/{channelId}/play resource.

(closes issue ASTERISK-21587)
Review: https://reviewboard.asterisk.org/r/2559


git-svn-id: https://origsvn.digium.com/svn/asterisk/trunk@389603 65c4cc65-6c06-0410-ace0-fbb531ad65f3
changes/78/78/1
David M. Lee 12 years ago
parent 10ba6bf8a8
commit 557125664d

@ -652,6 +652,17 @@ int ast_linear_stream(struct ast_channel *chan, const char *filename, int fd, in
*/
int ast_control_streamfile(struct ast_channel *chan, const char *file, const char *fwd, const char *rev, const char *stop, const char *pause, const char *restart, int skipms, long *offsetms);
/*!
* \brief Version of ast_control_streamfile() which allows the language of the
* media file to be specified.
*
* \retval 0 on success
* \retval Non-zero on failure
*/
int ast_control_streamfile_lang(struct ast_channel *chan, const char *file,
const char *fwd, const char *rev, const char *stop, const char *suspend,
const char *restart, int skipms, const char *lang, long *offsetms);
/*!
* \brief Stream a file with fast forward, pause, reverse, restart.
* \param chan

@ -1203,7 +1203,7 @@ int ast_queue_hangup(struct ast_channel *chan);
int ast_queue_hangup_with_cause(struct ast_channel *chan, int cause);
/*!
* \brief Queue a control frame with payload
* \brief Queue a control frame without payload
*
* \param chan channel to queue frame onto
* \param control type of control frame

@ -174,6 +174,18 @@ struct ast_channel_snapshot *stasis_app_control_get_snapshot(
void stasis_app_control_publish(
struct stasis_app_control *control, struct stasis_message *message);
/*!
* \brief Queue a control frame without payload.
*
* \param control Control to publish to.
* \param frame_type type of control frame.
*
* \return zero on success
* \return non-zero on failure
*/
int stasis_app_control_queue_control(struct stasis_app_control *control,
enum ast_control_frame_type frame_type);
/*!
* \brief Increment the res_stasis reference count.
*

@ -39,18 +39,34 @@ enum stasis_app_playback_state {
STASIS_PLAYBACK_STATE_QUEUED,
/*! The media is currently playing */
STASIS_PLAYBACK_STATE_PLAYING,
/*! The media is currently playing */
STASIS_PLAYBACK_STATE_PAUSED,
/*! The media has stopped playing */
STASIS_PLAYBACK_STATE_COMPLETE,
/*! The playback was canceled. */
STASIS_PLAYBACK_STATE_CANCELED,
/*! The playback was stopped. */
STASIS_PLAYBACK_STATE_STOPPED,
/*! Enum end sentinel. */
STASIS_PLAYBACK_STATE_MAX,
};
enum stasis_app_playback_media_control {
/*! Valid operation for controlling a playback. */
enum stasis_app_playback_media_operation {
/*! Stop the playback operation. */
STASIS_PLAYBACK_STOP,
/*! Restart the media from the beginning. */
STASIS_PLAYBACK_RESTART,
/*! Pause playback. */
STASIS_PLAYBACK_PAUSE,
STASIS_PLAYBACK_PLAY,
STASIS_PLAYBACK_REWIND,
STASIS_PLAYBACK_FAST_FORWARD,
STASIS_PLAYBACK_SPEED_UP,
STASIS_PLAYBACK_SLOW_DOWN,
/*! Resume paused playback. */
STASIS_PLAYBACK_UNPAUSE,
/*! Rewind playback. */
STASIS_PLAYBACK_REVERSE,
/*! Fast forward playback. */
STASIS_PLAYBACK_FORWARD,
/*! Enum end sentinel. */
STASIS_PLAYBACK_MEDIA_OP_MAX,
};
/*!
@ -62,12 +78,15 @@ enum stasis_app_playback_media_control {
*
* \param control Control for \c res_stasis.
* \param file Base filename for the file to play.
* \param language Selects the file based on language.
* \param skipms Number of milliseconds to skip for forward/reverse operations.
* \param offsetms Number of milliseconds to skip before playing.
* \return Playback control object.
* \return \c NULL on error.
*/
struct stasis_app_playback *stasis_app_control_play_uri(
struct stasis_app_control *control, const char *file,
const char *language);
const char *language, int skipms, long offsetms);
/*!
* \brief Gets the current state of a playback operation.
@ -95,18 +114,27 @@ const char *stasis_app_playback_get_id(
* \return Associated \ref stasis_app_playback object.
* \return \c NULL if \a id not found.
*/
struct ast_json *stasis_app_playback_find_by_id(const char *id);
struct stasis_app_playback *stasis_app_playback_find_by_id(const char *id);
struct ast_json *stasis_app_playback_to_json(
const struct stasis_app_playback *playback);
enum stasis_playback_oper_results {
STASIS_PLAYBACK_OPER_OK,
STASIS_PLAYBACK_OPER_FAILED,
STASIS_PLAYBACK_OPER_NOT_PLAYING,
};
/*!
* \brief Controls the media for a given playback operation.
*
* \param playback Playback control object.
* \param control Media control operation.
* \return 0 on success
* \return non-zero on error.
* \return \c STASIS_PLAYBACK_OPER_OK on success.
* \return \ref stasis_playback_oper_results indicating failure.
*/
int stasis_app_playback_control(struct stasis_app_playback *playback,
enum stasis_app_playback_media_control control);
enum stasis_playback_oper_results stasis_app_playback_operation(
struct stasis_app_playback *playback,
enum stasis_app_playback_media_operation operation);
/*!
* \brief Message type for playback updates. The data is an

@ -930,6 +930,7 @@ static int control_streamfile(struct ast_channel *chan,
const char *restart,
int skipms,
long *offsetms,
const char *lang,
ast_waitstream_fr_cb cb)
{
char *breaks = NULL;
@ -945,6 +946,9 @@ static int control_streamfile(struct ast_channel *chan,
if (offsetms) {
offset = *offsetms * 8; /* XXX Assumes 8kHz */
}
if (lang == NULL) {
lang = ast_channel_language(chan);
}
if (stop) {
blen += strlen(stop);
@ -982,7 +986,7 @@ static int control_streamfile(struct ast_channel *chan,
for (;;) {
ast_stopstream(chan);
res = ast_streamfile(chan, file, ast_channel_language(chan));
res = ast_streamfile(chan, file, lang);
if (!res) {
if (pause_restart_point) {
ast_seekstream(ast_channel_stream(chan), pause_restart_point, SEEK_SET);
@ -1093,7 +1097,7 @@ int ast_control_streamfile_w_cb(struct ast_channel *chan,
long *offsetms,
ast_waitstream_fr_cb cb)
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, cb);
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, NULL, cb);
}
int ast_control_streamfile(struct ast_channel *chan, const char *file,
@ -1101,7 +1105,14 @@ int ast_control_streamfile(struct ast_channel *chan, const char *file,
const char *stop, const char *suspend,
const char *restart, int skipms, long *offsetms)
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, NULL);
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, NULL, NULL);
}
int ast_control_streamfile_lang(struct ast_channel *chan, const char *file,
const char *fwd, const char *rev, const char *stop, const char *suspend,
const char *restart, int skipms, const char *lang, long *offsetms)
{
return control_streamfile(chan, file, fwd, rev, stop, suspend, restart, skipms, offsetms, lang, NULL);
}
int ast_play_and_wait(struct ast_channel *chan, const char *fn)

@ -330,6 +330,12 @@ static void stasis_http_play_on_channel_cb(
if (strcmp(i->name, "lang") == 0) {
args.lang = (i->value);
} else
if (strcmp(i->name, "offsetms") == 0) {
args.offsetms = atoi(i->value);
} else
if (strcmp(i->name, "skipms") == 0) {
args.skipms = atoi(i->value);
} else
{}
}
for (i = path_vars; i; i = i->next) {

@ -46,8 +46,8 @@ ASTERISK_FILE_VERSION(__FILE__, "$Revision$")
/*! 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
/*! Default number of milliseconds of media to skip */
#define PLAYBACK_DEFAULT_SKIPMS 3000
#define SOUND_URI_SCHEME "sound:"
#define RECORDING_URI_SCHEME "recording:"
@ -64,10 +64,17 @@ struct stasis_app_playback {
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;
/*! Number of milliseconds to skip before playing */
long offsetms;
/*! Number of milliseconds to skip for forward/reverse operations */
int skipms;
/*! Number of milliseconds of media that has been played */
long playedms;
/*! Current playback state */
enum stasis_app_playback_state state;
};
static int playback_hash(const void *obj, int flags)
@ -97,30 +104,21 @@ static const char *state_to_string(enum stasis_app_playback_state state)
return "queued";
case STASIS_PLAYBACK_STATE_PLAYING:
return "playing";
case STASIS_PLAYBACK_STATE_PAUSED:
return "paused";
case STASIS_PLAYBACK_STATE_STOPPED:
case STASIS_PLAYBACK_STATE_COMPLETE:
case STASIS_PLAYBACK_STATE_CANCELED:
/* It doesn't really matter how we got here, but all of these
* states really just mean 'done' */
return "done";
case STASIS_PLAYBACK_STATE_MAX:
break;
}
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);
@ -129,7 +127,7 @@ static void playback_publish(struct stasis_app_playback *playback)
ast_assert(playback != NULL);
json = playback_to_json(playback);
json = stasis_app_playback_to_json(playback);
if (json == NULL) {
return;
}
@ -144,24 +142,54 @@ static void playback_publish(struct stasis_app_playback *playback)
stasis_app_control_publish(playback->control, message);
}
static void playback_set_state(struct stasis_app_playback *playback,
enum stasis_app_playback_state state)
static void playback_cleanup(struct stasis_app_playback *playback)
{
ao2_unlink_flags(playbacks, playback,
OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
}
static int playback_first_update(struct stasis_app_playback *playback,
const char *uniqueid)
{
int res;
SCOPED_AO2LOCK(lock, playback);
playback->state = state;
if (playback->state == STASIS_PLAYBACK_STATE_CANCELED) {
ast_log(LOG_NOTICE, "%s: Playback canceled for %s\n",
uniqueid, playback->media);
res = -1;
} else {
res = 0;
playback->state = STASIS_PLAYBACK_STATE_PLAYING;
}
playback_publish(playback);
return res;
}
static void playback_cleanup(struct stasis_app_playback *playback)
static void playback_final_update(struct stasis_app_playback *playback,
long playedms, int res, const char *uniqueid)
{
playback_set_state(playback, STASIS_PLAYBACK_STATE_COMPLETE);
SCOPED_AO2LOCK(lock, playback);
ao2_unlink_flags(playbacks, playback,
OBJ_POINTER | OBJ_UNLINK | OBJ_NODATA);
playback->playedms = playedms;
if (res == 0) {
playback->state = STASIS_PLAYBACK_STATE_COMPLETE;
} else {
if (playback->state == STASIS_PLAYBACK_STATE_STOPPED) {
ast_log(LOG_NOTICE, "%s: Playback stopped for %s\n",
uniqueid, playback->media);
} else {
ast_log(LOG_WARNING, "%s: Playback failed for %s\n",
uniqueid, playback->media);
playback->state = STASIS_PLAYBACK_STATE_STOPPED;
}
}
playback_publish(playback);
}
static void *__app_control_play_uri(struct stasis_app_control *control,
static void *play_uri(struct stasis_app_control *control,
struct ast_channel *chan, void *data)
{
RAII_VAR(struct stasis_app_playback *, playback, NULL,
@ -169,6 +197,8 @@ static void *__app_control_play_uri(struct stasis_app_control *control,
RAII_VAR(struct ast_json *, json, NULL, ast_json_unref);
const char *file;
int res;
long offsetms;
/* Even though these local variables look fairly pointless, the avoid
* having a bunch of NULL's passed directly into
* ast_control_streamfile() */
@ -177,13 +207,17 @@ static void *__app_control_play_uri(struct stasis_app_control *control,
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);
offsetms = playback->offsetms;
res = playback_first_update(playback, ast_channel_uniqueid(chan));
if (res != 0) {
return NULL;
}
if (ast_channel_state(chan) != AST_STATE_UP) {
ast_answer(chan);
@ -201,13 +235,11 @@ static void *__app_control_play_uri(struct stasis_app_control *control,
return NULL;
}
res = ast_control_streamfile(chan, file, fwd, rev, stop, pause,
restart, skipms, &offsetms);
res = ast_control_streamfile_lang(chan, file, fwd, rev, stop, pause,
restart, playback->skipms, playback->language, &offsetms);
if (res != 0) {
ast_log(LOG_WARNING, "%s: Playback failed for %s",
ast_channel_uniqueid(chan), playback->media);
}
playback_final_update(playback, offsetms, res,
ast_channel_uniqueid(chan));
return NULL;
}
@ -221,11 +253,15 @@ static void playback_dtor(void *obj)
struct stasis_app_playback *stasis_app_control_play_uri(
struct stasis_app_control *control, const char *uri,
const char *language)
const char *language, int skipms, long offsetms)
{
RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
char id[AST_UUID_STR_LEN];
if (skipms < 0 || offsetms < 0) {
return NULL;
}
ast_debug(3, "%s: Sending play(%s) command\n",
stasis_app_control_get_channel_id(control), uri);
@ -234,20 +270,26 @@ struct stasis_app_playback *stasis_app_control_play_uri(
return NULL;
}
if (skipms == 0) {
skipms = PLAYBACK_DEFAULT_SKIPMS;
}
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;
playback->skipms = skipms;
playback->offsetms = offsetms;
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);
playback->state = STASIS_PLAYBACK_STATE_QUEUED;
playback_publish(playback);
/* A ref is kept in the playbacks container; no need to bump */
stasis_app_send_command_async(control, play_uri, playback);
/* Although this should be bumped for the caller */
ao2_ref(playback, +1);
return playback;
}
@ -266,26 +308,152 @@ const char *stasis_app_playback_get_id(
return control->id;
}
struct ast_json *stasis_app_playback_find_by_id(const char *id)
struct stasis_app_playback *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);
ao2_ref(playback, +1);
return playback;
}
struct ast_json *stasis_app_playback_to_json(
const 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);
}
int stasis_app_playback_control(struct stasis_app_playback *playback,
enum stasis_app_playback_media_control control)
typedef int (*playback_opreation_cb)(struct stasis_app_playback *playback);
static int playback_noop(struct stasis_app_playback *playback)
{
return 0;
}
static int playback_cancel(struct stasis_app_playback *playback)
{
SCOPED_AO2LOCK(lock, playback);
playback->state = STASIS_PLAYBACK_STATE_CANCELED;
return 0;
}
static int playback_stop(struct stasis_app_playback *playback)
{
SCOPED_AO2LOCK(lock, playback);
playback->state = STASIS_PLAYBACK_STATE_STOPPED;
return stasis_app_control_queue_control(playback->control,
AST_CONTROL_STREAM_STOP);
}
static int playback_restart(struct stasis_app_playback *playback)
{
return stasis_app_control_queue_control(playback->control,
AST_CONTROL_STREAM_RESTART);
}
static int playback_pause(struct stasis_app_playback *playback)
{
SCOPED_AO2LOCK(lock, playback);
ast_assert(0); /* TODO */
return -1;
playback->state = STASIS_PLAYBACK_STATE_PAUSED;
playback_publish(playback);
return stasis_app_control_queue_control(playback->control,
AST_CONTROL_STREAM_SUSPEND);
}
static int playback_unpause(struct stasis_app_playback *playback)
{
SCOPED_AO2LOCK(lock, playback);
playback->state = STASIS_PLAYBACK_STATE_PLAYING;
playback_publish(playback);
return stasis_app_control_queue_control(playback->control,
AST_CONTROL_STREAM_SUSPEND);
}
static int playback_reverse(struct stasis_app_playback *playback)
{
return stasis_app_control_queue_control(playback->control,
AST_CONTROL_STREAM_REVERSE);
}
static int playback_forward(struct stasis_app_playback *playback)
{
return stasis_app_control_queue_control(playback->control,
AST_CONTROL_STREAM_FORWARD);
}
/*!
* \brief A sparse array detailing how commands should be handled in the
* various playback states. Unset entries imply invalid operations.
*/
playback_opreation_cb operations[STASIS_PLAYBACK_STATE_MAX][STASIS_PLAYBACK_MEDIA_OP_MAX] = {
[STASIS_PLAYBACK_STATE_QUEUED][STASIS_PLAYBACK_STOP] = playback_cancel,
[STASIS_PLAYBACK_STATE_QUEUED][STASIS_PLAYBACK_RESTART] = playback_noop,
[STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_STOP] = playback_stop,
[STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_RESTART] = playback_restart,
[STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_PAUSE] = playback_pause,
[STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_UNPAUSE] = playback_noop,
[STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_REVERSE] = playback_reverse,
[STASIS_PLAYBACK_STATE_PLAYING][STASIS_PLAYBACK_FORWARD] = playback_forward,
[STASIS_PLAYBACK_STATE_PAUSED][STASIS_PLAYBACK_STOP] = playback_stop,
[STASIS_PLAYBACK_STATE_PAUSED][STASIS_PLAYBACK_PAUSE] = playback_noop,
[STASIS_PLAYBACK_STATE_PAUSED][STASIS_PLAYBACK_UNPAUSE] = playback_unpause,
[STASIS_PLAYBACK_STATE_COMPLETE][STASIS_PLAYBACK_STOP] = playback_noop,
[STASIS_PLAYBACK_STATE_CANCELED][STASIS_PLAYBACK_STOP] = playback_noop,
[STASIS_PLAYBACK_STATE_STOPPED][STASIS_PLAYBACK_STOP] = playback_noop,
};
enum stasis_playback_oper_results stasis_app_playback_operation(
struct stasis_app_playback *playback,
enum stasis_app_playback_media_operation operation)
{
playback_opreation_cb cb;
SCOPED_AO2LOCK(lock, playback);
ast_assert(playback->state >= 0 && playback->state < STASIS_PLAYBACK_STATE_MAX);
if (operation < 0 || operation >= STASIS_PLAYBACK_MEDIA_OP_MAX) {
ast_log(LOG_ERROR, "Invalid playback operation %d\n", operation);
return -1;
}
cb = operations[playback->state][operation];
if (!cb) {
if (playback->state != STASIS_PLAYBACK_STATE_PLAYING) {
/* So we can be specific in our error message. */
return STASIS_PLAYBACK_OPER_NOT_PLAYING;
} else {
/* And, really, all operations should be valid during
* playback */
ast_log(LOG_ERROR,
"Unhandled operation during playback: %d\n",
operation);
return STASIS_PLAYBACK_OPER_FAILED;
}
}
return cb(playback) ?
STASIS_PLAYBACK_OPER_FAILED : STASIS_PLAYBACK_OPER_OK;
}
static int load_module(void)

@ -174,6 +174,12 @@ void stasis_app_control_publish(
stasis_publish(ast_channel_topic(control->channel), message);
}
int stasis_app_control_queue_control(struct stasis_app_control *control,
enum ast_control_frame_type frame_type)
{
return ast_queue_control(control->channel, frame_type);
}
int control_dispatch_all(struct stasis_app_control *control,
struct ast_channel *chan)
{

@ -163,13 +163,28 @@ void stasis_http_play_on_channel(struct ast_variable *headers,
return;
}
if (args->skipms < 0) {
stasis_http_response_error(
response, 500, "Internal Server Error",
"skipms cannot be negative");
return;
}
if (args->offsetms < 0) {
stasis_http_response_error(
response, 500, "Internal Server Error",
"offsetms cannot be negative");
return;
}
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, language,
args->skipms, args->offsetms);
if (!playback) {
stasis_http_response_error(
response, 500, "Internal Server Error",
"Failed to answer channel");
"Failed to queue media for playback");
return;
}

@ -200,8 +200,12 @@ 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 */
/*! \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 Start playback of media.

@ -34,7 +34,9 @@ void stasis_http_get_playback(struct ast_variable *headers,
struct ast_get_playback_args *args,
struct stasis_http_response *response)
{
RAII_VAR(struct ast_json *, playback, NULL, ast_json_unref);
RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
RAII_VAR(struct ast_json *, json, 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",
@ -42,13 +44,94 @@ void stasis_http_get_playback(struct ast_variable *headers,
return;
}
stasis_http_response_ok(response, ast_json_ref(playback));
json = stasis_app_playback_to_json(playback);
if (json == NULL) {
stasis_http_response_error(response, 500,
"Internal Server Error", "Error building response");
return;
}
stasis_http_response_ok(response, ast_json_ref(json));
}
void stasis_http_stop_playback(struct ast_variable *headers, struct ast_stop_playback_args *args, struct stasis_http_response *response)
void stasis_http_stop_playback(struct ast_variable *headers,
struct ast_stop_playback_args *args,
struct stasis_http_response *response)
{
ast_log(LOG_ERROR, "TODO: stasis_http_stop_playback\n");
RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
enum stasis_playback_oper_results res;
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;
}
res = stasis_app_playback_operation(playback, STASIS_PLAYBACK_STOP);
switch (res) {
case STASIS_PLAYBACK_OPER_OK:
stasis_http_response_no_content(response);
return;
case STASIS_PLAYBACK_OPER_FAILED:
stasis_http_response_error(response, 500,
"Internal Server Error", "Could not stop playback");
return;
case STASIS_PLAYBACK_OPER_NOT_PLAYING:
/* Stop operation should be valid even when not playing */
ast_assert(0);
stasis_http_response_error(response, 500,
"Internal Server Error", "Could not stop playback");
return;
}
}
void stasis_http_control_playback(struct ast_variable *headers, struct ast_control_playback_args *args, struct stasis_http_response *response)
void stasis_http_control_playback(struct ast_variable *headers,
struct ast_control_playback_args *args,
struct stasis_http_response *response)
{
ast_log(LOG_ERROR, "TODO: stasis_http_control_playback\n");
RAII_VAR(struct stasis_app_playback *, playback, NULL, ao2_cleanup);
enum stasis_app_playback_media_operation oper;
enum stasis_playback_oper_results res;
if (strcmp(args->operation, "unpause") == 0) {
oper = STASIS_PLAYBACK_UNPAUSE;
} else if (strcmp(args->operation, "pause") == 0) {
oper = STASIS_PLAYBACK_PAUSE;
} else if (strcmp(args->operation, "restart") == 0) {
oper = STASIS_PLAYBACK_RESTART;
} else if (strcmp(args->operation, "reverse") == 0) {
oper = STASIS_PLAYBACK_REVERSE;
} else if (strcmp(args->operation, "forward") == 0) {
oper = STASIS_PLAYBACK_FORWARD;
} else {
stasis_http_response_error(response, 400,
"Bad Request", "Invalid operation %s",
args->operation);
return;
}
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;
}
res = stasis_app_playback_operation(playback, oper);
switch (res) {
case STASIS_PLAYBACK_OPER_OK:
stasis_http_response_no_content(response);
return;
case STASIS_PLAYBACK_OPER_FAILED:
stasis_http_response_error(response, 500,
"Internal Server Error", "Could not %s playback",
args->operation);
return;
case STASIS_PLAYBACK_OPER_NOT_PLAYING:
stasis_http_response_error(response, 409, "Conflict",
"Can only %s while media is playing", args->operation);
return;
}
}

@ -408,11 +408,28 @@
},
{
"name": "lang",
"description": "For sounds, selects language for sound",
"description": "For sounds, selects language for sound.",
"paramType": "query",
"required": false,
"allowMultiple": false,
"dataType": "string"
},
{
"name": "offsetms",
"description": "Number of media to skip before playing.",
"paramType": "query",
"required": false,
"allowMultiple": false,
"dataType": "int"
},
{
"name": "skipms",
"description": "Number of milliseconds to skip for forward/reverse operations.",
"paramType": "query",
"required": false,
"allowMultiple": false,
"dataType": "int",
"defaultValue": 3000
}
],
"errorResponses": [

@ -73,16 +73,29 @@
"allowableValues": {
"valueType": "LIST",
"values": [
"play",
"restart",
"pause",
"rewind",
"fast-forward",
"speed-up",
"slow-down"
"unpause",
"reverse",
"forward"
]
}
}
]
],
"errorResponses": [
{
"code": 400,
"reason": "The provided operation parameter was invalid"
},
{
"code": 404,
"reason": "The playback cannot be found"
},
{
"code": 409,
"reason": "The operation cannot be performed in the playback's current state"
}
]
}
]
}

Loading…
Cancel
Save