[svn-commits] qwell: branch qwell/hold_events r389614 - in /team/qwell/hold_events: ./ incl...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Thu May 23 15:32:53 CDT 2013


Author: qwell
Date: Thu May 23 15:32:49 2013
New Revision: 389614

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=389614
Log:
Multiple revisions 389603,389609

........
  r389603 | dlee | 2013-05-23 15:21:16 -0500 (Thu, 23 May 2013) | 18 lines
  
  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
........
  r389609 | file | 2013-05-23 15:25:48 -0500 (Thu, 23 May 2013) | 8 lines
  
  Fix a crash due to the INVITE session being destroyed before the session.
  
  This change ensures that the INVITE session remains valid for the lifetime
  of the session object itself by increasing the session count on the dialog that
  the INVITE session is allocated from. Once this reaches zero (normally as a result
  of decrementing it within the session destructor) the dialog, and INVITE session,
  are destroyed.
........

Merged revisions 389603,389609 from http://svn.asterisk.org/svn/asterisk/trunk

Modified:
    team/qwell/hold_events/   (props changed)
    team/qwell/hold_events/include/asterisk/app.h
    team/qwell/hold_events/include/asterisk/channel.h
    team/qwell/hold_events/include/asterisk/stasis_app.h
    team/qwell/hold_events/include/asterisk/stasis_app_playback.h
    team/qwell/hold_events/main/app.c
    team/qwell/hold_events/res/res_sip_session.c
    team/qwell/hold_events/res/res_stasis_http_channels.c
    team/qwell/hold_events/res/res_stasis_playback.c
    team/qwell/hold_events/res/stasis/control.c
    team/qwell/hold_events/res/stasis_http/resource_channels.c
    team/qwell/hold_events/res/stasis_http/resource_channels.h
    team/qwell/hold_events/res/stasis_http/resource_playback.c
    team/qwell/hold_events/rest-api/api-docs/channels.json
    team/qwell/hold_events/rest-api/api-docs/playback.json

Propchange: team/qwell/hold_events/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Thu May 23 15:32:49 2013
@@ -1,1 +1,1 @@
-/trunk:1-389600
+/trunk:1-389611

Modified: team/qwell/hold_events/include/asterisk/app.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/include/asterisk/app.h?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/include/asterisk/app.h (original)
+++ team/qwell/hold_events/include/asterisk/app.h Thu May 23 15:32:49 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: team/qwell/hold_events/include/asterisk/channel.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/include/asterisk/channel.h?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/include/asterisk/channel.h (original)
+++ team/qwell/hold_events/include/asterisk/channel.h Thu May 23 15:32:49 2013
@@ -1228,7 +1228,7 @@
 int ast_queue_unhold(struct ast_channel *chan);
 
 /*!
- * \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: team/qwell/hold_events/include/asterisk/stasis_app.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/include/asterisk/stasis_app.h?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/include/asterisk/stasis_app.h (original)
+++ team/qwell/hold_events/include/asterisk/stasis_app.h Thu May 23 15:32:49 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: team/qwell/hold_events/include/asterisk/stasis_app_playback.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/include/asterisk/stasis_app_playback.h?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/include/asterisk/stasis_app_playback.h (original)
+++ team/qwell/hold_events/include/asterisk/stasis_app_playback.h Thu May 23 15:32:49 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: team/qwell/hold_events/main/app.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/main/app.c?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/main/app.c (original)
+++ team/qwell/hold_events/main/app.c Thu May 23 15:32:49 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: team/qwell/hold_events/res/res_sip_session.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/res/res_sip_session.c?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/res/res_sip_session.c (original)
+++ team/qwell/hold_events/res/res_sip_session.c Thu May 23 15:32:49 2013
@@ -887,6 +887,10 @@
 	ast_party_id_free(&session->id);
 	ao2_cleanup(session->endpoint);
 	ast_format_cap_destroy(session->req_caps);
+
+	if (session->inv_session) {
+		pjsip_dlg_dec_session(session->inv_session->dlg, &session_module);
+	}
 }
 
 static int add_supplements(struct ast_sip_session *session)
@@ -945,6 +949,7 @@
 	}
 	ast_sip_dialog_set_serializer(inv_session->dlg, session->serializer);
 	ast_sip_dialog_set_endpoint(inv_session->dlg, endpoint);
+	pjsip_dlg_inc_session(inv_session->dlg, &session_module);
 	ao2_ref(endpoint, +1);
 	inv_session->mod_data[session_module.id] = session;
 	session->endpoint = endpoint;

Modified: team/qwell/hold_events/res/res_stasis_http_channels.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/res/res_stasis_http_channels.c?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/res/res_stasis_http_channels.c (original)
+++ team/qwell/hold_events/res/res_stasis_http_channels.c Thu May 23 15:32:49 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: team/qwell/hold_events/res/res_stasis_playback.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/res/res_stasis_playback.c?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/res/res_stasis_playback.c (original)
+++ team/qwell/hold_events/res/res_stasis_playback.c Thu May 23 15:32:49 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: team/qwell/hold_events/res/stasis/control.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/res/stasis/control.c?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/res/stasis/control.c (original)
+++ team/qwell/hold_events/res/stasis/control.c Thu May 23 15:32:49 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: team/qwell/hold_events/res/stasis_http/resource_channels.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/res/stasis_http/resource_channels.c?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/res/stasis_http/resource_channels.c (original)
+++ team/qwell/hold_events/res/stasis_http/resource_channels.c Thu May 23 15:32:49 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: team/qwell/hold_events/res/stasis_http/resource_channels.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/res/stasis_http/resource_channels.h?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/res/stasis_http/resource_channels.h (original)
+++ team/qwell/hold_events/res/stasis_http/resource_channels.h Thu May 23 15:32:49 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: team/qwell/hold_events/res/stasis_http/resource_playback.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/res/stasis_http/resource_playback.c?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/res/stasis_http/resource_playback.c (original)
+++ team/qwell/hold_events/res/stasis_http/resource_playback.c Thu May 23 15:32:49 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: team/qwell/hold_events/rest-api/api-docs/channels.json
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/rest-api/api-docs/channels.json?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/rest-api/api-docs/channels.json (original)
+++ team/qwell/hold_events/rest-api/api-docs/channels.json Thu May 23 15:32:49 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: team/qwell/hold_events/rest-api/api-docs/playback.json
URL: http://svnview.digium.com/svn/asterisk/team/qwell/hold_events/rest-api/api-docs/playback.json?view=diff&rev=389614&r1=389613&r2=389614
==============================================================================
--- team/qwell/hold_events/rest-api/api-docs/playback.json (original)
+++ team/qwell/hold_events/rest-api/api-docs/playback.json Thu May 23 15:32:49 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 svn-commits mailing list