[asterisk-commits] dlee: trunk r389603 - in /trunk: include/asterisk/ main/ res/ res/stasis/ res...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Thu May 23 15:21:20 CDT 2013


Author: dlee
Date: Thu May 23 15:21:16 2013
New Revision: 389603

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389603
Log:
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

Modified:
    trunk/include/asterisk/app.h
    trunk/include/asterisk/channel.h
    trunk/include/asterisk/stasis_app.h
    trunk/include/asterisk/stasis_app_playback.h
    trunk/main/app.c
    trunk/res/res_stasis_http_channels.c
    trunk/res/res_stasis_playback.c
    trunk/res/stasis/control.c
    trunk/res/stasis_http/resource_channels.c
    trunk/res/stasis_http/resource_channels.h
    trunk/res/stasis_http/resource_playback.c
    trunk/rest-api/api-docs/channels.json
    trunk/rest-api/api-docs/playback.json

Modified: trunk/include/asterisk/app.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/app.h?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/include/asterisk/app.h (original)
+++ trunk/include/asterisk/app.h Thu May 23 15:21:16 2013
@@ -653,6 +653,17 @@
 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
  * \param file filename

Modified: trunk/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/channel.h?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/include/asterisk/channel.h (original)
+++ trunk/include/asterisk/channel.h Thu May 23 15:21:16 2013
@@ -1203,7 +1203,7 @@
 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

Modified: trunk/include/asterisk/stasis_app.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_app.h?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/include/asterisk/stasis_app.h (original)
+++ trunk/include/asterisk/stasis_app.h Thu May 23 15:21:16 2013
@@ -175,6 +175,18 @@
 	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.
  *
  * This ensures graceful shutdown happens in the proper order.

Modified: trunk/include/asterisk/stasis_app_playback.h
URL: http://svnview.digium.com/svn/asterisk/trunk/include/asterisk/stasis_app_playback.h?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/include/asterisk/stasis_app_playback.h (original)
+++ trunk/include/asterisk/stasis_app_playback.h Thu May 23 15:21:16 2013
@@ -39,18 +39,34 @@
 	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 @@
  *
  * \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 @@
  * \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

Modified: trunk/main/app.c
URL: http://svnview.digium.com/svn/asterisk/trunk/main/app.c?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/main/app.c (original)
+++ trunk/main/app.c Thu May 23 15:21:16 2013
@@ -930,6 +930,7 @@
 	const char *restart,
 	int skipms,
 	long *offsetms,
+	const char *lang,
 	ast_waitstream_fr_cb cb)
 {
 	char *breaks = NULL;
@@ -945,6 +946,9 @@
 	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 @@
 
 	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 @@
 	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 @@
 			   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)

Modified: trunk/res/res_stasis_http_channels.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_stasis_http_channels.c?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/res/res_stasis_http_channels.c (original)
+++ trunk/res/res_stasis_http_channels.c Thu May 23 15:21:16 2013
@@ -330,6 +330,12 @@
 		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) {

Modified: trunk/res/res_stasis_playback.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/res_stasis_playback.c?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/res/res_stasis_playback.c (original)
+++ trunk/res/res_stasis_playback.c Thu May 23 15:21:16 2013
@@ -46,8 +46,8 @@
 /*! 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 @@
 		AST_STRING_FIELD(media);	/*!< Playback media uri */
 		AST_STRING_FIELD(language);	/*!< Preferred language */
 		);
+	/*! 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;
-	/*! Control object for the channel we're playing back to */
-	struct stasis_app_control *control;
 };
 
 static int playback_hash(const void *obj, int flags)
@@ -97,28 +104,19 @@
 		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)
@@ -129,7 +127,7 @@
 
 	ast_assert(playback != NULL);
 
-	json = playback_to_json(playback);
+	json = stasis_app_playback_to_json(playback);
 	if (json == NULL) {
 		return;
 	}
@@ -144,24 +142,54 @@
 	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,
+static int playback_first_update(struct stasis_app_playback *playback,
+	const char *uniqueid)
+{
+	int res;
+	SCOPED_AO2LOCK(lock, playback);
+
+	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_final_update(struct stasis_app_playback *playback,
+	long playedms, int res, const char *uniqueid)
+{
+	SCOPED_AO2LOCK(lock, playback);
+
+	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 *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 @@
 	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 @@
 	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 @@
 		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);
-	}
+	res = ast_control_streamfile_lang(chan, file, fwd, rev, stop, pause,
+		restart, playback->skipms, playback->language, &offsetms);
+
+	playback_final_update(playback, offsetms, res,
+		ast_channel_uniqueid(chan));
 
 	return NULL;
 }
@@ -221,17 +253,25 @@
 
 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);
 
 	playback = ao2_alloc(sizeof(*playback), playback_dtor);
 	if (!playback || ast_string_field_init(playback, 128) ){
 		return NULL;
+	}
+
+	if (skipms == 0) {
+		skipms = PLAYBACK_DEFAULT_SKIPMS;
 	}
 
 	ast_uuid_generate_str(id, sizeof(id));
@@ -239,15 +279,17 @@
 	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 @@
 	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)
-{
-	SCOPED_AO2LOCK(lock, playback);
-	ast_assert(0); /* TODO */
-	return -1;
+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);
+	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)

Modified: trunk/res/stasis/control.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/stasis/control.c?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/res/stasis/control.c (original)
+++ trunk/res/stasis/control.c Thu May 23 15:21:16 2013
@@ -174,6 +174,12 @@
 	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)
 {

Modified: trunk/res/stasis_http/resource_channels.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/stasis_http/resource_channels.c?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/res/stasis_http/resource_channels.c (original)
+++ trunk/res/stasis_http/resource_channels.c Thu May 23 15:21:16 2013
@@ -163,13 +163,28 @@
 		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;
 	}
 

Modified: trunk/res/stasis_http/resource_channels.h
URL: http://svnview.digium.com/svn/asterisk/trunk/res/stasis_http/resource_channels.h?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/res/stasis_http/resource_channels.h (original)
+++ trunk/res/stasis_http/resource_channels.h Thu May 23 15:21:16 2013
@@ -200,8 +200,12 @@
 	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.

Modified: trunk/res/stasis_http/resource_playback.c
URL: http://svnview.digium.com/svn/asterisk/trunk/res/stasis_http/resource_playback.c?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/res/stasis_http/resource_playback.c (original)
+++ trunk/res/stasis_http/resource_playback.c Thu May 23 15:21:16 2013
@@ -34,7 +34,9 @@
 	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 @@
 		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;
+	}
 }

Modified: trunk/rest-api/api-docs/channels.json
URL: http://svnview.digium.com/svn/asterisk/trunk/rest-api/api-docs/channels.json?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/rest-api/api-docs/channels.json (original)
+++ trunk/rest-api/api-docs/channels.json Thu May 23 15:21:16 2013
@@ -408,11 +408,28 @@
 						},
 						{
 							"name": "lang",
-							"description": "For sounds, selects language for sound",
-							"paramType": "query",
-							"required": false,
-							"allowMultiple": false,
-							"dataType": "string"
+							"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": [

Modified: trunk/rest-api/api-docs/playback.json
URL: http://svnview.digium.com/svn/asterisk/trunk/rest-api/api-docs/playback.json?view=diff&rev=389603&r1=389602&r2=389603
==============================================================================
--- trunk/rest-api/api-docs/playback.json (original)
+++ trunk/rest-api/api-docs/playback.json Thu May 23 15:21:16 2013
@@ -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"
+						}
+]
 				}
 			]
 		}




More information about the asterisk-commits mailing list