[asterisk-commits] dlee: branch dlee/ASTERISK-21969 r397812 - in /team/dlee/ASTERISK-21969: ./ i...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Tue Aug 27 13:53:34 CDT 2013


Author: dlee
Date: Tue Aug 27 13:53:26 2013
New Revision: 397812

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=397812
Log:
ARI: Music on Hold/Background Music for bridges

Adds ARI functions to be able to turn on/off music on hold in a
bridge. It actually functions more as a background music without
further actions on the bridge since if the rest of the channels
in the bridge aren't explicitly muted, they will still be able
to communicate.

(closes issue ASTERISK-21974)
Reported by: Matt Jordan
Review: https://reviewboard.asterisk.org/r/2688/
........

Merged revisions 397505 from http://svn.asterisk.org/svn/asterisk/trunk

Modified:
    team/dlee/ASTERISK-21969/   (props changed)
    team/dlee/ASTERISK-21969/include/asterisk/stasis_app.h
    team/dlee/ASTERISK-21969/res/ari/resource_bridges.c
    team/dlee/ASTERISK-21969/res/ari/resource_bridges.h
    team/dlee/ASTERISK-21969/res/res_ari_bridges.c
    team/dlee/ASTERISK-21969/res/res_stasis.c
    team/dlee/ASTERISK-21969/rest-api/api-docs/bridges.json

Propchange: team/dlee/ASTERISK-21969/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Tue Aug 27 13:53:26 2013
@@ -1,1 +1,1 @@
-/trunk:1-397504
+/trunk:1-397505

Modified: team/dlee/ASTERISK-21969/include/asterisk/stasis_app.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ASTERISK-21969/include/asterisk/stasis_app.h?view=diff&rev=397812&r1=397811&r2=397812
==============================================================================
--- team/dlee/ASTERISK-21969/include/asterisk/stasis_app.h (original)
+++ team/dlee/ASTERISK-21969/include/asterisk/stasis_app.h Tue Aug 27 13:53:26 2013
@@ -335,6 +335,28 @@
 	const char *bridge_id);
 
 /*!
+ * \brief Finds or creates an announcer channel in a bridge that can play music on hold.
+ *
+ * \param bridge Bridge we want an MOH channel for
+ *
+ * \return NULL if the music on hold channel fails to be created or join the bridge for any reason.
+ * \return Pointer to the ;1 end of the announcer channel chain.
+ */
+struct ast_channel *stasis_app_bridge_moh_channel(
+	struct ast_bridge *bridge);
+
+/*!
+ * \brief Breaks down MOH channels playing on the bridge created by stasis_app_bridge_moh_channel
+ *
+ * \param bridge Bridge we want to stop the MOH on
+ *
+ * \return -1 if no moh channel could be found and stopped
+ * \return 0 on success
+ */
+int stasis_app_bridge_moh_stop(
+	struct ast_bridge *bridge);
+
+/*!
  * \brief Add a channel to the bridge.
  *
  * \param control Control whose channel should be added to the bridge

Modified: team/dlee/ASTERISK-21969/res/ari/resource_bridges.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ASTERISK-21969/res/ari/resource_bridges.c?view=diff&rev=397812&r1=397811&r2=397812
==============================================================================
--- team/dlee/ASTERISK-21969/res/ari/resource_bridges.c (original)
+++ team/dlee/ASTERISK-21969/res/ari/resource_bridges.c Tue Aug 27 13:53:26 2013
@@ -43,6 +43,7 @@
 #include "asterisk/bridge.h"
 #include "asterisk/format_cap.h"
 #include "asterisk/file.h"
+#include "asterisk/musiconhold.h"
 
 /*!
  * \brief Finds a bridge, filling the response with an error, if appropriate.
@@ -498,6 +499,48 @@
 	ast_ari_response_created(response, recording_url, json);
 }
 
+void ast_ari_moh_start_bridge(struct ast_variable *headers, struct ast_moh_start_bridge_args *args, struct ast_ari_response *response)
+{
+	RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
+	struct ast_channel *moh_channel;
+	const char *moh_class = args->moh_class;
+
+	if (!bridge) {
+		/* The response is provided by find_bridge() */
+		return;
+	}
+
+	moh_channel = stasis_app_bridge_moh_channel(bridge);
+	if (!moh_channel) {
+		ast_ari_response_alloc_failed(response);
+		return;
+	}
+
+	ast_moh_start(moh_channel, moh_class, NULL);
+
+	ast_ari_response_no_content(response);
+
+}
+
+void ast_ari_moh_stop_bridge(struct ast_variable *headers, struct ast_moh_stop_bridge_args *args, struct ast_ari_response *response)
+{
+	RAII_VAR(struct ast_bridge *, bridge, find_bridge(response, args->bridge_id), ao2_cleanup);
+
+	if (!bridge) {
+		/* the response is provided by find_bridge() */
+		return;
+	}
+
+	if (stasis_app_bridge_moh_stop(bridge)) {
+		ast_ari_response_error(
+			response, 409, "Conflict",
+			"Bridge isn't playing music");
+		return;
+	}
+
+	ast_ari_response_no_content(response);
+}
+
 void ast_ari_get_bridge(struct ast_variable *headers, struct ast_get_bridge_args *args, struct ast_ari_response *response)
 {
 	RAII_VAR(struct ast_bridge_snapshot *, snapshot, ast_bridge_snapshot_get_latest(args->bridge_id), ao2_cleanup);

Modified: team/dlee/ASTERISK-21969/res/ari/resource_bridges.h
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ASTERISK-21969/res/ari/resource_bridges.h?view=diff&rev=397812&r1=397811&r2=397812
==============================================================================
--- team/dlee/ASTERISK-21969/res/ari/resource_bridges.h (original)
+++ team/dlee/ASTERISK-21969/res/ari/resource_bridges.h Tue Aug 27 13:53:26 2013
@@ -133,6 +133,36 @@
  * \param[out] response HTTP response
  */
 void ast_ari_remove_channel_from_bridge(struct ast_variable *headers, struct ast_remove_channel_from_bridge_args *args, struct ast_ari_response *response);
+/*! \brief Argument struct for ast_ari_moh_start_bridge() */
+struct ast_moh_start_bridge_args {
+	/*! \brief Bridge's id */
+	const char *bridge_id;
+	/*! \brief Channel's id */
+	const char *moh_class;
+};
+/*!
+ * \brief Play music on hold to a bridge or change the MOH class that is playing.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_moh_start_bridge(struct ast_variable *headers, struct ast_moh_start_bridge_args *args, struct ast_ari_response *response);
+/*! \brief Argument struct for ast_ari_moh_stop_bridge() */
+struct ast_moh_stop_bridge_args {
+	/*! \brief Bridge's id */
+	const char *bridge_id;
+};
+/*!
+ * \brief Stop playing music on hold to a bridge.
+ *
+ * This will only stop music on hold being played via bridges/{bridgeId}/mohStart.
+ *
+ * \param headers HTTP headers
+ * \param args Swagger parameters
+ * \param[out] response HTTP response
+ */
+void ast_ari_moh_stop_bridge(struct ast_variable *headers, struct ast_moh_stop_bridge_args *args, struct ast_ari_response *response);
 /*! \brief Argument struct for ast_ari_play_on_bridge() */
 struct ast_play_on_bridge_args {
 	/*! \brief Bridge's id */

Modified: team/dlee/ASTERISK-21969/res/res_ari_bridges.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ASTERISK-21969/res/res_ari_bridges.c?view=diff&rev=397812&r1=397811&r2=397812
==============================================================================
--- team/dlee/ASTERISK-21969/res/res_ari_bridges.c (original)
+++ team/dlee/ASTERISK-21969/res/res_ari_bridges.c Tue Aug 27 13:53:26 2013
@@ -485,6 +485,128 @@
 	return;
 }
 /*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/mohStart.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_moh_start_bridge_cb(
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_moh_start_bridge_args args = {};
+	struct ast_variable *i;
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = get_params; i; i = i->next) {
+		if (strcmp(i->name, "mohClass") == 0) {
+			args.moh_class = (i->value);
+		} else
+		{}
+	}
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "bridgeId") == 0) {
+			args.bridge_id = (i->value);
+		} else
+		{}
+	}
+	ast_ari_moh_start_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Bridge not found */
+	case 409: /* Bridge not in Stasis application */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/mohStart\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/mohStart\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
+ * \brief Parameter parsing callback for /bridges/{bridgeId}/mohStop.
+ * \param get_params GET parameters in the HTTP request.
+ * \param path_vars Path variables extracted from the request.
+ * \param headers HTTP headers.
+ * \param[out] response Response to the HTTP request.
+ */
+static void ast_ari_moh_stop_bridge_cb(
+	struct ast_variable *get_params, struct ast_variable *path_vars,
+	struct ast_variable *headers, struct ast_ari_response *response)
+{
+	struct ast_moh_stop_bridge_args args = {};
+	struct ast_variable *i;
+#if defined(AST_DEVMODE)
+	int is_valid;
+	int code;
+#endif /* AST_DEVMODE */
+
+	for (i = path_vars; i; i = i->next) {
+		if (strcmp(i->name, "bridgeId") == 0) {
+			args.bridge_id = (i->value);
+		} else
+		{}
+	}
+	ast_ari_moh_stop_bridge(headers, &args, response);
+#if defined(AST_DEVMODE)
+	code = response->response_code;
+
+	switch (code) {
+	case 0: /* Implementation is still a stub, or the code wasn't set */
+		is_valid = response->message == NULL;
+		break;
+	case 500: /* Internal Server Error */
+	case 501: /* Not Implemented */
+	case 404: /* Bridge not found */
+	case 409: /* Bridge not in Stasis application */
+		is_valid = 1;
+		break;
+	default:
+		if (200 <= code && code <= 299) {
+			is_valid = ast_ari_validate_void(
+				response->message);
+		} else {
+			ast_log(LOG_ERROR, "Invalid error response %d for /bridges/{bridgeId}/mohStop\n", code);
+			is_valid = 0;
+		}
+	}
+
+	if (!is_valid) {
+		ast_log(LOG_ERROR, "Response validation failed for /bridges/{bridgeId}/mohStop\n");
+		ast_ari_response_error(response, 500,
+			"Internal Server Error", "Response validation failed");
+	}
+#endif /* AST_DEVMODE */
+
+fin: __attribute__((unused))
+	return;
+}
+/*!
  * \brief Parameter parsing callback for /bridges/{bridgeId}/play.
  * \param get_params GET parameters in the HTTP request.
  * \param path_vars Path variables extracted from the request.
@@ -652,6 +774,24 @@
 	.path_segment = "removeChannel",
 	.callbacks = {
 		[AST_HTTP_POST] = ast_ari_remove_channel_from_bridge_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/bridges.{format} */
+static struct stasis_rest_handlers bridges_bridgeId_mohStart = {
+	.path_segment = "mohStart",
+	.callbacks = {
+		[AST_HTTP_POST] = ast_ari_moh_start_bridge_cb,
+	},
+	.num_children = 0,
+	.children = {  }
+};
+/*! \brief REST handler for /api-docs/bridges.{format} */
+static struct stasis_rest_handlers bridges_bridgeId_mohStop = {
+	.path_segment = "mohStop",
+	.callbacks = {
+		[AST_HTTP_POST] = ast_ari_moh_stop_bridge_cb,
 	},
 	.num_children = 0,
 	.children = {  }
@@ -682,8 +822,8 @@
 		[AST_HTTP_GET] = ast_ari_get_bridge_cb,
 		[AST_HTTP_DELETE] = ast_ari_delete_bridge_cb,
 	},
-	.num_children = 4,
-	.children = { &bridges_bridgeId_addChannel,&bridges_bridgeId_removeChannel,&bridges_bridgeId_play,&bridges_bridgeId_record, }
+	.num_children = 6,
+	.children = { &bridges_bridgeId_addChannel,&bridges_bridgeId_removeChannel,&bridges_bridgeId_mohStart,&bridges_bridgeId_mohStop,&bridges_bridgeId_play,&bridges_bridgeId_record, }
 };
 /*! \brief REST handler for /api-docs/bridges.{format} */
 static struct stasis_rest_handlers bridges = {

Modified: team/dlee/ASTERISK-21969/res/res_stasis.c
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ASTERISK-21969/res/res_stasis.c?view=diff&rev=397812&r1=397811&r2=397812
==============================================================================
--- team/dlee/ASTERISK-21969/res/res_stasis.c (original)
+++ team/dlee/ASTERISK-21969/res/res_stasis.c Tue Aug 27 13:53:26 2013
@@ -65,6 +65,11 @@
 #include "asterisk/strings.h"
 #include "stasis/app.h"
 #include "stasis/control.h"
+#include "asterisk/core_unreal.h"
+#include "asterisk/musiconhold.h"
+#include "asterisk/causes.h"
+#include "asterisk/stringfields.h"
+#include "asterisk/bridge_after.h"
 
 /*! Time to wait for a frame in the application */
 #define MAX_WAIT_MS 200
@@ -95,6 +100,8 @@
 struct ao2_container *app_controls;
 
 struct ao2_container *app_bridges;
+
+struct ao2_container *app_bridges_moh;
 
 /*! AO2 hash function for \ref app */
 static int app_hash(const void *obj, const int flags)
@@ -216,6 +223,234 @@
 	} else {
 		return 0;
 	}
+}
+
+/*!
+ *  Used with app_bridges_moh, provides links between bridges and existing music
+ *  on hold channels that are being used with them.
+ */
+struct stasis_app_bridge_moh_wrapper {
+	AST_DECLARE_STRING_FIELDS(
+		AST_STRING_FIELD(channel_id);
+		AST_STRING_FIELD(bridge_id);
+	);
+};
+
+static void stasis_app_bridge_moh_wrapper_destructor(void *obj)
+{
+	struct stasis_app_bridge_moh_wrapper *wrapper = obj;
+	ast_string_field_free_memory(wrapper);
+}
+
+/*! AO2 hash function for the bridges moh container */
+static int bridges_moh_hash_fn(const void *obj, const int flags)
+{
+	const struct stasis_app_bridge_moh_wrapper *wrapper;
+	const char *key;
+
+	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+	case OBJ_KEY:
+		key = obj;
+		return ast_str_hash(key);
+	case OBJ_POINTER:
+		wrapper = obj;
+		return ast_str_hash(wrapper->bridge_id);
+	default:
+		/* Hash can only work on something with a full key. */
+		ast_assert(0);
+		return 0;
+	}
+}
+
+static int bridges_moh_sort_fn(const void *obj_left, const void *obj_right, const int flags)
+{
+	const struct stasis_app_bridge_moh_wrapper *left = obj_left;
+	const struct stasis_app_bridge_moh_wrapper *right = obj_right;
+	const char *right_key = obj_right;
+	int cmp;
+
+	switch (flags & (OBJ_POINTER | OBJ_KEY | OBJ_PARTIAL_KEY)) {
+	case OBJ_POINTER:
+		right_key = right->bridge_id;
+		/* Fall through */
+	case OBJ_KEY:
+		cmp = strcmp(left->bridge_id, right_key);
+		break;
+	case OBJ_PARTIAL_KEY:
+		cmp = strncmp(left->bridge_id, right_key, strlen(right_key));
+		break;
+	default:
+		/* Sort can only work on something with a full or partial key. */
+		ast_assert(0);
+		cmp = 0;
+		break;
+	}
+	return cmp;
+}
+
+/*! Removes the bridge to music on hold channel link */
+static void remove_bridge_moh(char *bridge_id)
+{
+	RAII_VAR(struct stasis_app_bridge_moh_wrapper *, moh_wrapper, ao2_find(app_bridges_moh, bridge_id, OBJ_KEY), ao2_cleanup);
+
+	if (moh_wrapper) {
+		ao2_unlink_flags(app_bridges_moh, moh_wrapper, OBJ_NOLOCK);
+	}
+	ast_free(bridge_id);
+}
+
+/*! After bridge failure callback for moh channels */
+static void moh_after_bridge_cb_failed(enum ast_bridge_after_cb_reason reason, void *data)
+{
+	char *bridge_id = data;
+
+	remove_bridge_moh(bridge_id);
+}
+
+/*! After bridge callback for moh channels */
+static void moh_after_bridge_cb(struct ast_channel *chan, void *data)
+{
+	char *bridge_id = data;
+
+	remove_bridge_moh(bridge_id);
+}
+
+/*! Request a bridge MOH channel */
+static struct ast_channel *prepare_bridge_moh_channel(void)
+{
+	RAII_VAR(struct ast_format_cap *, cap, NULL, ast_format_cap_destroy);
+	struct ast_format format;
+
+	cap = ast_format_cap_alloc_nolock();
+	if (!cap) {
+		return NULL;
+	}
+
+	ast_format_cap_add(cap, ast_format_set(&format, AST_FORMAT_SLINEAR, 0));
+
+	return ast_request("Announcer", cap, NULL, "ARI_MOH", NULL);
+}
+
+/*! Provides the moh channel with a thread so it can actually play its music */
+static void *moh_channel_thread(void *data)
+{
+	struct ast_channel *moh_channel = data;
+
+	while (!ast_safe_sleep(moh_channel, 1000));
+
+	ast_moh_stop(moh_channel);
+	ast_hangup(moh_channel);
+
+	return NULL;
+}
+
+/*!
+ * \internal
+ * \brief Creates, pushes, and links a channel for playing music on hold to bridge
+ *
+ * \param bridge Which bridge this moh channel exists for
+ *
+ * \retval NULL if the channel could not be created, pushed, or linked
+ * \retval Reference to the channel on success
+ */
+static struct ast_channel *bridge_moh_create(struct ast_bridge *bridge)
+{
+	RAII_VAR(struct stasis_app_bridge_moh_wrapper *, new_wrapper, NULL, ao2_cleanup);
+	RAII_VAR(char *, bridge_id, ast_strdup(bridge->uniqueid), ast_free);
+	struct ast_channel *chan;
+	pthread_t threadid;
+
+	if (!bridge_id) {
+		return NULL;
+	}
+
+	chan = prepare_bridge_moh_channel();
+
+	if (!chan) {
+		return NULL;
+	}
+
+	/* The after bridge callback assumes responsibility of the bridge_id. */
+	ast_bridge_set_after_callback(chan, moh_after_bridge_cb, moh_after_bridge_cb_failed, bridge_id);
+
+	bridge_id = NULL;
+
+	if (ast_unreal_channel_push_to_bridge(chan, bridge,
+		AST_BRIDGE_CHANNEL_FLAG_IMMOVABLE | AST_BRIDGE_CHANNEL_FLAG_LONELY)) {
+		ast_hangup(chan);
+		return NULL;
+	}
+
+	new_wrapper = ao2_alloc_options(sizeof(*new_wrapper), stasis_app_bridge_moh_wrapper_destructor, AO2_ALLOC_OPT_LOCK_NOLOCK);
+	if (!new_wrapper) {
+		ast_hangup(chan);
+		return NULL;
+	}
+
+	if (ast_string_field_init(new_wrapper, 32)) {
+		ast_hangup(chan);
+		return NULL;
+	}
+
+	ast_string_field_set(new_wrapper, bridge_id, bridge->uniqueid);
+	ast_string_field_set(new_wrapper, channel_id, ast_channel_uniqueid(chan));
+
+	if (!ao2_link(app_bridges_moh, new_wrapper)) {
+		ast_hangup(chan);
+		return NULL;
+	}
+
+	if (ast_pthread_create_detached(&threadid, NULL, moh_channel_thread, chan)) {
+		ast_log(LOG_ERROR, "Failed to create channel thread. Abandoning MOH channel creation.\n");
+		ao2_unlink_flags(app_bridges_moh, new_wrapper, OBJ_NOLOCK);
+		ast_hangup(chan);
+		return NULL;
+	}
+
+	return chan;
+}
+
+struct ast_channel *stasis_app_bridge_moh_channel(struct ast_bridge *bridge)
+{
+	RAII_VAR(struct stasis_app_bridge_moh_wrapper *, moh_wrapper, NULL, ao2_cleanup);
+
+	SCOPED_AO2LOCK(lock, app_bridges_moh);
+
+	moh_wrapper = ao2_find(app_bridges_moh, bridge->uniqueid, OBJ_KEY | OBJ_NOLOCK);
+
+	if (!moh_wrapper) {
+		struct ast_channel *bridge_moh_channel = bridge_moh_create(bridge);
+		return bridge_moh_channel;
+	}
+
+	return ast_channel_get_by_name(moh_wrapper->channel_id);
+}
+
+int stasis_app_bridge_moh_stop(struct ast_bridge *bridge)
+{
+	RAII_VAR(struct stasis_app_bridge_moh_wrapper *, moh_wrapper, NULL, ao2_cleanup);
+	struct ast_channel *chan;
+
+	SCOPED_AO2LOCK(lock, app_bridges_moh);
+
+	moh_wrapper = ao2_find(app_bridges_moh, bridge->uniqueid, OBJ_KEY | OBJ_NOLOCK);
+
+	if (!moh_wrapper) {
+		return -1;
+	}
+
+	chan = ast_channel_get_by_name(moh_wrapper->channel_id);
+	if (!chan) {
+		return -1;
+	}
+
+	ast_moh_stop(chan);
+	ast_softhangup(chan, AST_CAUSE_NORMAL_CLEARING);
+	ao2_cleanup(chan);
+
+	ao2_unlink_flags(app_bridges_moh, moh_wrapper, OBJ_NOLOCK);
+
+	return 0;
 }
 
 struct ast_bridge *stasis_app_bridge_find_by_id(
@@ -575,6 +810,14 @@
         app_bridges = ao2_container_alloc(BRIDGES_NUM_BUCKETS, bridges_hash,
 		bridges_compare);
 
+	app_bridges_moh = ao2_container_alloc_hash(
+		AO2_ALLOC_OPT_LOCK_MUTEX, AO2_CONTAINER_ALLOC_OPT_DUPS_REJECT,
+		37, bridges_moh_hash_fn, bridges_moh_sort_fn, NULL);
+
+	if (!app_bridges_moh) {
+		return AST_MODULE_LOAD_FAILURE;
+	}
+
 	return AST_MODULE_LOAD_SUCCESS;
 }
 
@@ -588,6 +831,9 @@
 
 	ao2_cleanup(app_bridges);
 	app_bridges = NULL;
+
+	ao2_cleanup(app_bridges_moh);
+	app_bridges_moh = NULL;
 
 	return 0;
 }

Modified: team/dlee/ASTERISK-21969/rest-api/api-docs/bridges.json
URL: http://svnview.digium.com/svn/asterisk/team/dlee/ASTERISK-21969/rest-api/api-docs/bridges.json?view=diff&rev=397812&r1=397811&r2=397812
==============================================================================
--- team/dlee/ASTERISK-21969/rest-api/api-docs/bridges.json (original)
+++ team/dlee/ASTERISK-21969/rest-api/api-docs/bridges.json Tue Aug 27 13:53:26 2013
@@ -171,6 +171,79 @@
 							"required": true,
 							"allowMultiple": true,
 							"dataType": "string"
+						}
+					]
+				}
+			]
+		},
+		{
+			"path": "/bridges/{bridgeId}/mohStart",
+			"description": "Play music on hold to a bridge",
+			"operations": [
+				{
+					"httpMethod": "POST",
+					"summary": "Play music on hold to a bridge or change the MOH class that is playing.",
+					"nickname": "mohStartBridge",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "bridgeId",
+							"description": "Bridge's id",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						},
+						{
+							"name": "mohClass",
+							"description": "Channel's id",
+							"paramType": "query",
+							"required": false,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Bridge not found"
+						},
+						{
+							"code": 409,
+							"reason": "Bridge not in Stasis application"
+						}
+					]
+				}
+			]
+		},
+		{
+			"path": "/bridges/{bridgeId}/mohStop",
+			"description": "Stop music on hold for a bridge",
+			"operations": [
+				{
+					"httpMethod": "POST",
+					"summary": "Stop playing music on hold to a bridge.",
+					"notes": "This will only stop music on hold being played via bridges/{bridgeId}/mohStart.",
+					"nickname": "mohStopBridge",
+					"responseClass": "void",
+					"parameters": [
+						{
+							"name": "bridgeId",
+							"description": "Bridge's id",
+							"paramType": "path",
+							"required": true,
+							"allowMultiple": false,
+							"dataType": "string"
+						}
+					],
+					"errorResponses": [
+						{
+							"code": 404,
+							"reason": "Bridge not found"
+						},
+						{
+							"code": 409,
+							"reason": "Bridge not in Stasis application"
 						}
 					]
 				}




More information about the asterisk-commits mailing list