[Asterisk-code-review] channel: Add ability to request an outgoing channel with str... (asterisk[master])

Jenkins2 asteriskteam at digium.com
Thu Apr 27 17:53:53 CDT 2017


Jenkins2 has submitted this change and it was merged. ( https://gerrit.asterisk.org/5522 )

Change subject: channel: Add ability to request an outgoing channel with stream topology.
......................................................................


channel: Add ability to request an outgoing channel with stream topology.

This change extends the ast_request functionality by adding another
function and callback to create an outgoing channel with a requested
stream topology. Fallback is provided by either converting the
requested stream topology into a format capabilities structure if
the channel driver does not support streams or by converting the
requested format capabilities into a stream topology if the channel
driver does support streams.

The Dial application has also been updated to request an outgoing
channel with the stream topology of the calling channel.

ASTERISK-26959

Change-Id: Ifa9037a672ac21d42dd7125aa09816dc879a70e6
---
M apps/app_dial.c
M include/asterisk/channel.h
M include/asterisk/stream.h
M main/channel.c
M main/stream.c
M tests/test_stream.c
6 files changed, 230 insertions(+), 47 deletions(-)

Approvals:
  Kevin Harwell: Looks good to me, but someone else must approve
  Mark Michelson: Looks good to me, approved
  Jenkins2: Approved for Submit



diff --git a/apps/app_dial.c b/apps/app_dial.c
index c8fcf46..79e2a9b 100644
--- a/apps/app_dial.c
+++ b/apps/app_dial.c
@@ -66,6 +66,7 @@
 #include "asterisk/bridge_after.h"
 #include "asterisk/features_config.h"
 #include "asterisk/max_forwards.h"
+#include "asterisk/stream.h"
 
 /*** DOCUMENTATION
 	<application name="Dial" language="en_US">
@@ -970,16 +971,16 @@
 		c = o->chan = NULL;
 		cause = AST_CAUSE_BUSY;
 	} else {
-		struct ast_format_cap *nativeformats;
+		struct ast_stream_topology *topology;
 
 		ast_channel_lock(in);
-		nativeformats = ao2_bump(ast_channel_nativeformats(in));
+		topology = ast_stream_topology_clone(ast_channel_get_stream_topology(in));
 		ast_channel_unlock(in);
 
 		/* Setup parameters */
-		c = o->chan = ast_request(tech, nativeformats, NULL, in, stuff, &cause);
+		c = o->chan = ast_request_with_stream_topology(tech, topology, NULL, in, stuff, &cause);
 
-		ao2_cleanup(nativeformats);
+		ast_stream_topology_free(topology);
 
 		if (c) {
 			if (single && !caller_entertained) {
@@ -2444,7 +2445,7 @@
 		char *tech = strsep(&number, "/");
 		size_t tech_len;
 		size_t number_len;
-		struct ast_format_cap *nativeformats;
+		struct ast_stream_topology *topology;
 
 		num_dialed++;
 		if (ast_strlen_zero(number)) {
@@ -2496,13 +2497,13 @@
 		 */
 		ast_party_connected_line_copy(&tmp->connected, ast_channel_connected(chan));
 
-		nativeformats = ao2_bump(ast_channel_nativeformats(chan));
+		topology = ast_stream_topology_clone(ast_channel_get_stream_topology(chan));
 
 		ast_channel_unlock(chan);
 
-		tc = ast_request(tmp->tech, nativeformats, NULL, chan, tmp->number, &cause);
+		tc = ast_request_with_stream_topology(tmp->tech, topology, NULL, chan, tmp->number, &cause);
 
-		ao2_cleanup(nativeformats);
+		ast_stream_topology_free(topology);
 
 		if (!tc) {
 			/* If we can't, just go on to the next call */
diff --git a/include/asterisk/channel.h b/include/asterisk/channel.h
index 32c9c7f..70856a9 100644
--- a/include/asterisk/channel.h
+++ b/include/asterisk/channel.h
@@ -203,6 +203,8 @@
 
 typedef unsigned long long ast_group_t;
 
+struct ast_stream_topology;
+
 /*! \todo Add an explanation of an Asterisk generator
 */
 struct ast_generator {
@@ -629,6 +631,26 @@
 	 * \retval non-NULL channel on success
 	 */
 	struct ast_channel *(* const requester)(const char *type, struct ast_format_cap *cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
+
+	/*!
+	 * \brief Requester - to set up call data structures (pvt's) with stream topology
+	 *
+	 * \param type type of channel to request
+	 * \param topology Stream topology for requested channel
+	 * \param assignedid Unique ID string to assign to channel
+	 * \param requestor channel asking for data
+	 * \param addr destination of the call
+	 * \param cause Cause of failure
+	 *
+	 * \details
+	 * Request a channel of a given type, with addr as optional information used
+	 * by the low level module
+	 *
+	 * \retval NULL failure
+	 * \retval non-NULL channel on success
+	 */
+	struct ast_channel *(* const requester_with_stream_topology)(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
+
 
 	int (* const devicestate)(const char *device_number);	/*!< Devicestate call back */
 	int (* const presencestate)(const char *presence_provider, char **subtype, char **message); /*!< Presencestate callback */
@@ -1393,6 +1415,25 @@
  */
 struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
 
+/*!
+ * \brief Requests a channel (specifying stream topology)
+ *
+ * \param type type of channel to request
+ * \param topology Stream topology for requested channel
+ * \param assignedids Unique ID to create channel with
+ * \param requestor channel asking for data
+ * \param addr destination of the call
+ * \param cause Cause of failure
+ *
+ * \details
+ * Request a channel of a given type, with addr as optional information used
+ * by the low level module
+ *
+ * \retval NULL failure
+ * \retval non-NULL channel on success
+ */
+struct ast_channel *ast_request_with_stream_topology(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause);
+
 enum ast_channel_requestor_relationship {
 	/*! The requestor is the future bridge peer of the channel. */
 	AST_CHANNEL_REQUESTOR_BRIDGE_PEER,
diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h
index 1becae2..924bfb6 100644
--- a/include/asterisk/stream.h
+++ b/include/asterisk/stream.h
@@ -375,6 +375,22 @@
 	struct ast_format_cap *cap);
 
 /*!
+ * \brief A helper function that, given a stream topology, creates a format
+ * capabilities structure containing all formats from all streams.
+ *
+ * \param topology The topology of streams
+ *
+  * \retval non-NULL success
+  * \retval NULL failure
+  *
+  * \note The stream topology is NOT altered by this function.
+  *
+  * \since 15
+  */
+struct ast_format_cap *ast_format_cap_from_stream_topology(
+    struct ast_stream_topology *topology);
+
+/*!
  * \brief Gets the first stream of a specific type from the topology
  *
  * \param topology The topology of streams
diff --git a/main/channel.c b/main/channel.c
index 31f3639..e37d665 100644
--- a/main/channel.c
+++ b/main/channel.c
@@ -5976,7 +5976,8 @@
 	return 0;
 }
 
-struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+static struct ast_channel *request_channel(const char *type, struct ast_format_cap *request_cap, struct ast_stream_topology *topology,
+	const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
 {
 	struct chanlist *chan;
 	struct ast_channel *c;
@@ -5993,13 +5994,47 @@
 	}
 
 	AST_RWLIST_TRAVERSE(&backends, chan, list) {
+		if (strcasecmp(type, chan->tech->type)) {
+			continue;
+		}
+
+		break;
+	}
+
+	AST_RWLIST_UNLOCK(&backends);
+
+	if (!chan) {
+		ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
+		*cause = AST_CAUSE_NOSUCHDRIVER;
+		return NULL;
+	}
+
+	/* Allow either format capabilities or stream topology to be provided and adapt */
+	if (chan->tech->requester_with_stream_topology) {
+		struct ast_stream_topology *tmp_converted_topology = NULL;
+
+		if (!topology && request_cap) {
+			/* Turn the requested capabilities into a stream topology */
+			topology = tmp_converted_topology = ast_stream_topology_create_from_format_cap(request_cap);
+		}
+
+		c = chan->tech->requester_with_stream_topology(type, topology, assignedids, requestor, addr, cause);
+
+		ast_stream_topology_free(tmp_converted_topology);
+		if (!c) {
+			return NULL;
+		}
+	} else if (chan->tech->requester) {
+		struct ast_format_cap *tmp_converted_cap = NULL;
 		struct ast_format_cap *tmp_cap;
 		RAII_VAR(struct ast_format *, tmp_fmt, NULL, ao2_cleanup);
 		RAII_VAR(struct ast_format *, best_audio_fmt, NULL, ao2_cleanup);
 		struct ast_format_cap *joint_cap;
 
-		if (strcasecmp(type, chan->tech->type))
-			continue;
+		if (!request_cap && topology) {
+			/* Turn the request stream topology into capabilities */
+			request_cap = tmp_converted_cap = ast_format_cap_from_stream_topology(topology);
+		}
 
 		/* find the best audio format to use */
 		tmp_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
@@ -6018,13 +6053,10 @@
 					ast_format_cap_get_names(chan->tech->capabilities, &tech_codecs),
 					ast_format_cap_get_names(request_cap, &request_codecs));
 				*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
-				AST_RWLIST_UNLOCK(&backends);
+				ao2_cleanup(tmp_converted_cap);
 				return NULL;
 			}
 		}
-		AST_RWLIST_UNLOCK(&backends);
-		if (!chan->tech->requester)
-			return NULL;
 
 		/* XXX Only the audio format calculated as being the best for translation
 		 * purposes is used for the request. This is because we don't have the ability
@@ -6033,50 +6065,58 @@
 		 */
 		joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
 		if (!joint_cap) {
+			ao2_cleanup(tmp_converted_cap);
 			return NULL;
 		}
 		ast_format_cap_append_from_cap(joint_cap, request_cap, AST_MEDIA_TYPE_UNKNOWN);
 		ast_format_cap_remove_by_type(joint_cap, AST_MEDIA_TYPE_AUDIO);
 		ast_format_cap_append(joint_cap, best_audio_fmt, 0);
+		ao2_cleanup(tmp_converted_cap);
 
-		if (!(c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause))) {
+		c = chan->tech->requester(type, joint_cap, assignedids, requestor, addr, cause);
+
+		if (!c) {
 			ao2_ref(joint_cap, -1);
 			return NULL;
 		}
-
-		if (requestor) {
-			ast_callid callid;
-
-			ast_channel_lock_both(c, (struct ast_channel *) requestor);
-
-			/* Set the newly created channel's callid to the same as the requestor. */
-			callid = ast_channel_callid(requestor);
-			if (callid) {
-				ast_channel_callid_set(c, callid);
-			}
-
-			ast_channel_unlock(c);
-			ast_channel_unlock((struct ast_channel *) requestor);
-		}
-
-		ao2_ref(joint_cap, -1);
-
-		if (set_security_requirements(requestor, c)) {
-			ast_log(LOG_WARNING, "Setting security requirements failed\n");
-			ast_hangup(c);
-			*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
-			return NULL;
-		}
-
-		/* no need to generate a Newchannel event here; it is done in the channel_alloc call */
-		return c;
+	} else {
+		return NULL;
 	}
 
-	ast_log(LOG_WARNING, "No channel type registered for '%s'\n", type);
-	*cause = AST_CAUSE_NOSUCHDRIVER;
-	AST_RWLIST_UNLOCK(&backends);
+	if (requestor) {
+		ast_callid callid;
 
-	return NULL;
+		ast_channel_lock_both(c, (struct ast_channel *) requestor);
+
+		/* Set the newly created channel's callid to the same as the requestor. */
+		callid = ast_channel_callid(requestor);
+		if (callid) {
+			ast_channel_callid_set(c, callid);
+		}
+
+		ast_channel_unlock(c);
+		ast_channel_unlock((struct ast_channel *) requestor);
+	}
+
+	if (set_security_requirements(requestor, c)) {
+		ast_log(LOG_WARNING, "Setting security requirements failed\n");
+		ast_hangup(c);
+		*cause = AST_CAUSE_BEARERCAPABILITY_NOTAVAIL;
+		return NULL;
+	}
+
+	/* no need to generate a Newchannel event here; it is done in the channel_alloc call */
+	return c;
+}
+
+struct ast_channel *ast_request(const char *type, struct ast_format_cap *request_cap, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+{
+	return request_channel(type, request_cap, NULL, assignedids, requestor, addr, cause);
+}
+
+struct ast_channel *ast_request_with_stream_topology(const char *type, struct ast_stream_topology *topology, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *addr, int *cause)
+{
+	return request_channel(type, NULL, topology, assignedids, requestor, addr, cause);
 }
 
 /*!
diff --git a/main/stream.c b/main/stream.c
index 9d36dbf..cf2633e 100644
--- a/main/stream.c
+++ b/main/stream.c
@@ -392,6 +392,32 @@
 	return topology;
 }
 
+struct ast_format_cap *ast_format_cap_from_stream_topology(
+    struct ast_stream_topology *topology)
+{
+	struct ast_format_cap *caps;
+	int i;
+
+	ast_assert(topology != NULL);
+
+	caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	if (!caps) {
+		return NULL;
+	}
+
+	for (i = 0; i < AST_VECTOR_SIZE(&topology->streams); i++) {
+		struct ast_stream *stream = AST_VECTOR_GET(&topology->streams, i);
+
+		if (!stream->formats) {
+			continue;
+		}
+
+		ast_format_cap_append_from_cap(caps, stream->formats, AST_MEDIA_TYPE_UNKNOWN);
+	}
+
+	return caps;
+}
+
 struct ast_stream *ast_stream_topology_get_first_stream_by_type(
 	const struct ast_stream_topology *topology,
 	enum ast_media_type type)
diff --git a/tests/test_stream.c b/tests/test_stream.c
index 3bab67c..7eecf37 100644
--- a/tests/test_stream.c
+++ b/tests/test_stream.c
@@ -1773,6 +1773,63 @@
 	return res;
 }
 
+AST_TEST_DEFINE(format_cap_from_stream_topology)
+{
+	RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
+	RAII_VAR(struct ast_format_cap *, stream_caps, NULL, ao2_cleanup);
+	struct ast_stream_topology *topology;
+
+	switch (cmd) {
+	case TEST_INIT:
+		info->name = "format_cap_from_stream_topology";
+		info->category = "/main/stream/";
+		info->summary = "stream topology to format capabilities conversion test";
+		info->description =
+			"Test that converting a stream topology to format capabilities results in expected formats";
+		return AST_TEST_NOT_RUN;
+	case TEST_EXECUTE:
+		break;
+	}
+
+	caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	if (!caps) {
+		ast_test_status_update(test, "Could not allocate an empty format capabilities structure\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_format_cap_append(caps, ast_format_ulaw, 0)) {
+		ast_test_status_update(test, "Failed to append a ulaw format to capabilities for channel nativeformats\n");
+		return AST_TEST_FAIL;
+	}
+
+	if (ast_format_cap_append(caps, ast_format_h264, 0)) {
+		ast_test_status_update(test, "Failed to append an h264 format to capabilities for channel nativeformats\n");
+		return AST_TEST_FAIL;
+	}
+
+	topology = ast_stream_topology_create_from_format_cap(caps);
+	if (!topology) {
+		ast_test_status_update(test, "Failed to create a stream topology from format capabilities of ulaw and h264\n");
+		return AST_TEST_FAIL;
+	}
+
+	stream_caps = ast_format_cap_from_stream_topology(topology);
+	if (!stream_caps) {
+		ast_test_status_update(test, "Failed to create a format capabilities from a stream topology\n");
+		ast_stream_topology_free(topology);
+		return AST_TEST_FAIL;
+	}
+
+	ast_stream_topology_free(topology);
+
+	if (!ast_format_cap_identical(caps, stream_caps)) {
+		ast_test_status_update(test, "Converting format capabilities into topology and back resulted in different formats\n");
+		return AST_TEST_FAIL;
+	}
+
+	return AST_TEST_PASS;
+}
+
 static int unload_module(void)
 {
 	AST_TEST_UNREGISTER(stream_create);
@@ -1797,6 +1854,7 @@
 	AST_TEST_UNREGISTER(stream_topology_change_request_from_channel_non_multistream);
 	AST_TEST_UNREGISTER(stream_topology_change_request_from_application);
 	AST_TEST_UNREGISTER(stream_topology_change_request_from_channel);
+	AST_TEST_UNREGISTER(format_cap_from_stream_topology);
 	return 0;
 }
 
@@ -1823,6 +1881,7 @@
 	AST_TEST_REGISTER(stream_topology_change_request_from_channel_non_multistream);
 	AST_TEST_REGISTER(stream_topology_change_request_from_application);
 	AST_TEST_REGISTER(stream_topology_change_request_from_channel);
+	AST_TEST_REGISTER(format_cap_from_stream_topology);
 	return AST_MODULE_LOAD_SUCCESS;
 }
 

-- 
To view, visit https://gerrit.asterisk.org/5522
To unsubscribe, visit https://gerrit.asterisk.org/settings

Gerrit-MessageType: merged
Gerrit-Change-Id: Ifa9037a672ac21d42dd7125aa09816dc879a70e6
Gerrit-PatchSet: 3
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: George Joseph <gjoseph at digium.com>
Gerrit-Reviewer: Jenkins2
Gerrit-Reviewer: Joshua Colp <jcolp at digium.com>
Gerrit-Reviewer: Kevin Harwell <kharwell at digium.com>
Gerrit-Reviewer: Mark Michelson <mmichelson at digium.com>
Gerrit-Reviewer: Matthew Fredrickson <creslin at digium.com>



More information about the asterisk-code-review mailing list