[Asterisk-code-review] chan pjsip: Multistream: Use underlying multistream structures. (asterisk[master])

Mark Michelson asteriskteam at digium.com
Mon Jun 5 13:24:55 CDT 2017


Mark Michelson has uploaded a new change for review. ( https://gerrit.asterisk.org/5760 )

Change subject: chan_pjsip: Multistream: Use underlying multistream structures.
......................................................................

chan_pjsip: Multistream: Use underlying multistream structures.

This commit is step one towards making chan_pjsip capable of supporting
multiple streams of a given type. The basic thing that's been done here
is to make it so that rather than using ast_format_cap structures in
places, we instead are using ast_stream_topology and ast_stream. There
is no functionality change being made by this commit; you still can only
have a single audio and single video stream on a call with a PJSIP
endpoint.

Throughout this commit some XXX comments have been added in places where
some assumptions may no longer hold when actually making the code
support multistream.

Change-Id: I0358ea3108e2c06ae43308abddf216a23deccb90
---
M channels/chan_pjsip.c
M channels/pjsip/dialplan_functions.c
M include/asterisk/res_pjsip.h
M include/asterisk/res_pjsip_session.h
M include/asterisk/stream.h
M res/res_pjsip/pjsip_configuration.c
M res/res_pjsip_sdp_rtp.c
M res/res_pjsip_session.c
8 files changed, 244 insertions(+), 78 deletions(-)


  git pull ssh://gerrit.asterisk.org:29418/asterisk refs/changes/60/5760/1

diff --git a/channels/chan_pjsip.c b/channels/chan_pjsip.c
index 5bf339e..41c12ee 100644
--- a/channels/chan_pjsip.c
+++ b/channels/chan_pjsip.c
@@ -64,6 +64,7 @@
 
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_session.h"
+#include "asterisk/stream.h"
 
 #include "pjsip/include/chan_pjsip.h"
 #include "pjsip/include/dialplan_functions.h"
@@ -430,6 +431,37 @@
 	}
 }
 
+/*!
+ * \brief Determine if topologies have compatible formats
+ *
+ * This will return true if ANY formats in the topology are compatible with one another.
+ * It does not guarantee that each individual stream is compatible with the other streams.
+ *
+ * XXX When supporting true multistream, we will need to be sure to mark which streams from
+ * top1 are compatible with which streams from top2. Then the ones that are not compatible
+ * will need to be marked as "removed" so that they are negotiated as expected.
+ *
+ * \param top1 Topology 1
+ * \param top2 Topology 2
+ * \retval 1 The topologies have at least one compatible format
+ * \retval 0 The topologies have no compatible formats or an error occurred.
+ */
+static int compatible_formats_exist(struct ast_stream_topology *top1, struct ast_stream_topology *top2)
+{
+	int i;
+	struct ast_format_cap *cap1;
+	struct ast_format_cap *cap2;
+
+	cap1 = ast_format_cap_from_stream_topology(top1);
+	cap2 = ast_format_cap_from_stream_topology(top2);
+
+	if (!cap1 || !cap2) {
+		return 0;
+	}
+
+	return ast_format_cap_iscompatible(cap1, cap2);
+}
+
 /*! \brief Function called to create a new PJSIP Asterisk channel */
 static struct ast_channel *chan_pjsip_new(struct ast_sip_session *session, int state, const char *exten, const char *title, const struct ast_assigned_ids *assignedids, const struct ast_channel *requestor, const char *cid_name)
 {
@@ -440,10 +472,6 @@
 	struct ast_variable *var;
 
 	if (!(pvt = ao2_alloc(sizeof(*pvt), chan_pjsip_pvt_dtor))) {
-		return NULL;
-	}
-	caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
-	if (!caps) {
 		return NULL;
 	}
 
@@ -457,14 +485,12 @@
 		ast_sorcery_object_get_id(session->endpoint),
 		(unsigned) ast_atomic_fetchadd_int((int *) &chan_idx, +1));
 	if (!chan) {
-		ao2_ref(caps, -1);
 		return NULL;
 	}
 
 	ast_channel_tech_set(chan, &chan_pjsip_tech);
 
 	if (!(channel = ast_sip_channel_pvt_alloc(pvt, session))) {
-		ao2_ref(caps, -1);
 		ast_channel_unlock(chan);
 		ast_hangup(chan);
 		return NULL;
@@ -474,11 +500,17 @@
 
 	ast_channel_tech_pvt_set(chan, channel);
 
-	if (!ast_format_cap_count(session->req_caps) ||
-		!ast_format_cap_iscompatible(session->req_caps, session->endpoint->media.codecs)) {
-		ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
+	if (!ast_stream_topology_get_count(session->req_topology) ||
+		!compatible_formats_exist(session->req_topology, session->endpoint->media.codecs)) {
+		caps = ast_format_cap_from_stream_topology(session->endpoint->media.codecs);
 	} else {
-		ast_format_cap_append_from_cap(caps, session->req_caps, AST_MEDIA_TYPE_UNKNOWN);
+		caps = ast_format_cap_from_stream_topology(session->req_topology);
+	}
+
+	if (!caps) {
+		ast_channel_unlock(chan);
+		ast_hangup(chan);
+		return NULL;
 	}
 
 	ast_channel_nativeformats_set(chan, caps);
@@ -2105,7 +2137,7 @@
 
 struct request_data {
 	struct ast_sip_session *session;
-	struct ast_format_cap *caps;
+	struct ast_stream_topology *topology;
 	const char *dest;
 	int cause;
 };
@@ -2179,7 +2211,7 @@
 		}
 	}
 
-	if (!(session = ast_sip_session_create_outgoing(endpoint, NULL, args.aor, request_user, req_data->caps))) {
+	if (!(session = ast_sip_session_create_outgoing(endpoint, NULL, args.aor, request_user, req_data->topology))) {
 		ast_log(LOG_ERROR, "Failed to create outgoing session to endpoint '%s'\n", endpoint_name);
 		req_data->cause = AST_CAUSE_NO_ROUTE_DESTINATION;
 		return -1;
@@ -2196,8 +2228,12 @@
 	struct request_data req_data;
 	RAII_VAR(struct ast_sip_session *, session, NULL, ao2_cleanup);
 
-	req_data.caps = cap;
+	req_data.topology = ast_stream_topology_create_from_format_cap(cap);
 	req_data.dest = data;
+
+	if (!req_data.topology) {
+		return NULL;
+	}
 
 	if (ast_sip_push_task_synchronous(NULL, request, &req_data)) {
 		*cause = req_data.cause;
@@ -2205,6 +2241,7 @@
 	}
 
 	session = req_data.session;
+	ast_stream_topology_free(req_data.topology);
 
 	if (!(session->channel = chan_pjsip_new(session, AST_STATE_DOWN, NULL, NULL, assignedids, requestor, NULL))) {
 		/* Session needs to be terminated prematurely */
diff --git a/channels/pjsip/dialplan_functions.c b/channels/pjsip/dialplan_functions.c
index 332df70..dd4cca8 100644
--- a/channels/pjsip/dialplan_functions.c
+++ b/channels/pjsip/dialplan_functions.c
@@ -441,6 +441,7 @@
 #include "asterisk/pbx.h"
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_session.h"
+#include "asterisk/stream.h"
 #include "include/chan_pjsip.h"
 #include "include/dialplan_functions.h"
 
@@ -924,26 +925,20 @@
 	return 0;
 }
 
-static int media_offer_read_av(struct ast_sip_session *session, char *buf,
-			       size_t len, enum ast_media_type media_type)
+static void append_format_names(struct ast_format_cap *caps, char *buf, size_t *len)
 {
 	int i, size = 0;
 
-	for (i = 0; i < ast_format_cap_count(session->req_caps); i++) {
-		struct ast_format *fmt = ast_format_cap_get_format(session->req_caps, i);
-
-		if (ast_format_get_type(fmt) != media_type) {
-			ao2_ref(fmt, -1);
-			continue;
-		}
+	for (i = 0; i < ast_format_cap_count(caps); i++) {
+		struct ast_format *fmt = ast_format_cap_get_format(caps, i);
 
 		/* add one since we'll include a comma */
 		size = strlen(ast_format_get_name(fmt)) + 1;
-		if (len < size) {
+		if (*len < size) {
 			ao2_ref(fmt, -1);
 			break;
 		}
-		len -= size;
+		*len -= size;
 
 		/* no reason to use strncat here since we have already ensured buf has
                    enough space, so strcat can be safely used */
@@ -952,8 +947,29 @@
 
 		ao2_ref(fmt, -1);
 	}
+}
 
-	if (size) {
+static int media_offer_read_av(struct ast_sip_session *session, char *buf,
+			       size_t len, enum ast_media_type media_type)
+{
+	int i;
+
+	for (i = 0; i < ast_stream_topology_get_count(session->req_topology); ++i) {
+		struct ast_stream *stream;
+		struct ast_format_cap *caps;
+
+		stream = ast_stream_topology_get_stream(session->req_topology, i);
+
+		if (ast_stream_get_type(stream) != media_type) {
+			continue;
+		}
+
+		caps = ast_stream_get_formats(stream);
+
+		append_format_names(caps, buf, &len);
+	}
+
+	if (!ast_strlen_zero(buf)) {
 		/* remove the extra comma */
 		buf[strlen(buf) - 1] = '\0';
 	}
@@ -969,9 +985,22 @@
 static int media_offer_write_av(void *obj)
 {
 	struct media_offer_data *data = obj;
+	struct ast_stream *stream;
+	struct ast_format_cap *caps;
 
-	ast_format_cap_remove_by_type(data->session->req_caps, data->media_type);
-	ast_format_cap_update_by_allow_disallow(data->session->req_caps, data->value, 1);
+	/* XXX This method won't work when it comes time to do multistream support. The proper way to do this
+	 * will either be to
+	 * a) Alter all media streams of a particular type.
+	 * b) Change the dialplan function to be able to specify which stream to alter and alter only that
+	 * one stream
+	 */
+	stream = ast_stream_topology_get_first_stream_by_type(data->session->req_topology, data->media_type);
+	if (!stream) {
+		return 0;
+	}
+	caps = ast_stream_get_formats(stream);
+	ast_format_cap_remove_by_type(caps, data->media_type);
+	ast_format_cap_update_by_allow_disallow(caps, data->value, 1);
 
 	return 0;
 }
diff --git a/include/asterisk/res_pjsip.h b/include/asterisk/res_pjsip.h
index 59f1710..04a2b65 100644
--- a/include/asterisk/res_pjsip.h
+++ b/include/asterisk/res_pjsip.h
@@ -665,7 +665,7 @@
 	/*! T.38 (FoIP) options */
 	struct ast_sip_t38_configuration t38;
 	/*! Configured codecs */
-	struct ast_format_cap *codecs;
+	struct ast_stream_topology *codecs;
 	/*! DSCP TOS bits for audio streams */
 	unsigned int tos_audio;
 	/*! Priority for audio streams */
diff --git a/include/asterisk/res_pjsip_session.h b/include/asterisk/res_pjsip_session.h
index 10e55f1..9bffa16 100644
--- a/include/asterisk/res_pjsip_session.h
+++ b/include/asterisk/res_pjsip_session.h
@@ -140,7 +140,7 @@
 	/*! Identity of endpoint this session deals with */
 	struct ast_party_id id;
 	/*! Requested capabilities */
-	struct ast_format_cap *req_caps;
+	struct ast_stream_topology *req_topology;
 	/*! Optional DSP, used only for inband DTMF/Fax-CNG detection if configured */
 	struct ast_dsp *dsp;
 	/*! Whether the termination of the session should be deferred */
@@ -448,11 +448,11 @@
  * \param contact The contact that this session will communicate with
  * \param location Name of the location to call, be it named location or explicit URI. Overrides contact if present.
  * \param request_user Optional request user to place in the request URI if permitted
- * \param req_caps The requested capabilities
+ * \param req_topology The requested capabilities
  */
 struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint,
 	struct ast_sip_contact *contact, const char *location, const char *request_user,
-	struct ast_format_cap *req_caps);
+	struct ast_stream_topology *req_topology);
 
 /*!
  * \brief Terminate a session and, if possible, send the provided response code
diff --git a/include/asterisk/stream.h b/include/asterisk/stream.h
index b453ab9..cf60d42 100644
--- a/include/asterisk/stream.h
+++ b/include/asterisk/stream.h
@@ -371,7 +371,7 @@
  * since a new format capabilities structure is created for each media type.
  *
  * \note Each stream will have its name set to the corresponding media type.
- * For example: "AST_MEDIA_TYPE_AUDIO".
+ * For example: "audio".
  *
  * \note Each stream will be set to the sendrecv state.
  *
diff --git a/res/res_pjsip/pjsip_configuration.c b/res/res_pjsip/pjsip_configuration.c
index 679c883..c8a9fde 100644
--- a/res/res_pjsip/pjsip_configuration.c
+++ b/res/res_pjsip/pjsip_configuration.c
@@ -22,6 +22,7 @@
 #include "asterisk/test.h"
 #include "asterisk/statsd.h"
 #include "asterisk/pbx.h"
+#include "asterisk/stream.h"
 
 /*! \brief Number of buckets for persistent endpoint information */
 #define PERSISTENT_BUCKETS 53
@@ -359,6 +360,59 @@
 	}
 
 	*buf = ast_strdup(*buf);
+	return 0;
+}
+
+static int codec_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct ast_sip_endpoint *endpoint = obj;
+	struct ast_format_cap *cap;
+	struct ast_stream_topology *topology;
+
+	cap = ast_format_cap_from_stream_topology(endpoint->media.codecs);
+	if (!cap) {
+		return -1;
+	}
+	if (ast_format_cap_update_by_allow_disallow(cap, var->value, aco_option_get_flags(opt))) {
+		ao2_ref(cap, -1);
+		return -1;
+	}
+	/* XXX This topology creation may not work as-is for multistream.
+	 * The problem is that the conversion from format_cap to topology
+	 * will only create a stream per media type. If the topology should
+	 * have multiple audio/video streams, then a different method
+	 * will need to be used
+	 */
+	topology = ast_stream_topology_create_from_format_cap(cap);
+	if (!topology) {
+		ao2_ref(cap, -1);
+		return -1;
+	}
+	ast_stream_topology_free(endpoint->media.codecs);
+	endpoint->media.codecs = topology;
+	ao2_ref(cap, -1);
+	return 0;
+}
+
+
+static int codec_to_str(const void *obj, const intptr_t *args, char **buf)
+{
+	const struct ast_sip_endpoint *endpoint;
+	struct ast_str *codecs;
+	struct ast_format_cap *cap;
+
+	endpoint = obj;
+	codecs = ast_str_alloca(AST_FORMAT_CAP_NAMES_LEN);
+
+	cap = ast_format_cap_from_stream_topology(endpoint->media.codecs);
+	if (!cap) {
+		return -1;
+	}
+
+	*buf = ast_strdup(ast_format_cap_get_names(cap, &codecs));
+	if (!*buf) {
+		return -1;
+	}
 	return 0;
 }
 
@@ -1831,8 +1885,8 @@
 
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "type", "", OPT_NOOP_T, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "context", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, context));
-	ast_sorcery_object_field_register_alias(sip_sorcery, "endpoint", "disallow", "", OPT_CODEC_T, 0, FLDSET(struct ast_sip_endpoint, media.codecs));
-	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "allow", "", OPT_CODEC_T, 1, FLDSET(struct ast_sip_endpoint, media.codecs));
+	ast_sorcery_object_field_register_custom_alias(sip_sorcery, "endpoint", "disallow", "", codec_handler, codec_to_str, NULL, 0, 0);
+	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "allow", "", codec_handler, codec_to_str, NULL, 1, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmf_mode", "rfc4733", dtmf_handler, dtmf_to_str, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.ipv6));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, media.rtp.symmetric));
@@ -2059,7 +2113,7 @@
 
 	ast_string_field_free_memory(endpoint);
 
-	ao2_ref(endpoint->media.codecs, -1);
+	ast_stream_topology_free(endpoint->media.codecs);
 	subscription_configuration_destroy(&endpoint->subscription);
 	info_configuration_destroy(&endpoint->info);
 	media_configuration_destroy(&endpoint->media);
@@ -2101,7 +2155,7 @@
 		ao2_cleanup(endpoint);
 		return NULL;
 	}
-	if (!(endpoint->media.codecs = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT))) {
+	if (!(endpoint->media.codecs = ast_stream_topology_alloc())) {
 		ao2_cleanup(endpoint);
 		return NULL;
 	}
diff --git a/res/res_pjsip_sdp_rtp.c b/res/res_pjsip_sdp_rtp.c
index 97e365c..49f2f5e 100644
--- a/res/res_pjsip_sdp_rtp.c
+++ b/res/res_pjsip_sdp_rtp.c
@@ -51,6 +51,7 @@
 #include "asterisk/sdp_srtp.h"
 #include "asterisk/dsp.h"
 #include "asterisk/linkedlists.h"       /* for AST_LIST_NEXT */
+#include "asterisk/stream.h"
 
 #include "asterisk/res_pjsip.h"
 #include "asterisk/res_pjsip_session.h"
@@ -91,18 +92,6 @@
 	case AST_MEDIA_TYPE_END: break;
 	}
 	return -1;
-}
-
-/*! \brief Remove all other cap types but the one given */
-static void format_cap_only_type(struct ast_format_cap *caps, enum ast_media_type media_type)
-{
-	int i = 0;
-	while (i <= AST_MEDIA_TYPE_TEXT) {
-		if (i != media_type && i != AST_MEDIA_TYPE_UNKNOWN) {
-			ast_format_cap_remove_by_type(caps, i);
-		}
-		i += 1;
-	}
 }
 
 static int send_keepalive(const void *data)
@@ -358,6 +347,10 @@
 	int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
 		ast_format_cap_count(session->direct_media_cap);
 	int dsp_features = 0;
+	struct ast_stream *req_stream;
+	struct ast_format_cap *req_caps;
+	struct ast_stream *endpoint_stream;
+	struct ast_format_cap *endpoint_caps;
 
 	if (!(caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
 	    !(peer = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT)) ||
@@ -366,12 +359,17 @@
 		return -1;
 	}
 
+	endpoint_stream = ast_stream_topology_get_first_stream_by_type(session->endpoint->media.codecs, media_type);
+	if (!endpoint_stream) {
+		return -1;
+	}
+	endpoint_caps = ast_stream_get_formats(endpoint_stream);
+
 	/* get the endpoint capabilities */
 	if (direct_media_enabled) {
-		ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps);
-		format_cap_only_type(caps, media_type);
+		ast_format_cap_get_compatible(endpoint_caps, session->direct_media_cap, caps);
 	} else {
-		ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type);
+		ast_format_cap_append_from_cap(caps, endpoint_caps, media_type);
 	}
 
 	/* get the capabilities on the peer */
@@ -402,7 +400,18 @@
 	ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp),
 		session_media->rtp);
 
-	ast_format_cap_append_from_cap(session->req_caps, joint, AST_MEDIA_TYPE_UNKNOWN);
+	req_stream = ast_stream_topology_get_first_stream_by_type(session->req_topology, media_type);
+	if (!req_stream) {
+		req_stream = ast_stream_alloc(ast_codec_media_type2str(media_type), media_type);
+		if (!req_stream) {
+			return -1;
+		}
+		ast_stream_set_formats(req_stream, joint);
+		ast_stream_topology_append_stream(session->req_topology, req_stream);
+	} else {
+		req_caps = ast_stream_get_formats(req_stream);
+		ast_format_cap_replace_from_cap(req_caps, joint, AST_MEDIA_TYPE_UNKNOWN);
+	}
 
 	if (session->channel) {
 		ast_channel_lock(session->channel);
@@ -968,7 +977,7 @@
 	}
 
 	/* If no type formats have been configured reject this stream */
-	if (!ast_format_cap_has_type(session->endpoint->media.codecs, media_type)) {
+	if (!ast_stream_topology_get_first_stream_by_type(session->endpoint->media.codecs, media_type)) {
 		ast_debug(3, "Endpoint has no codecs for media type '%s', declining stream\n", session_media->stream_type);
 		return 0;
 	}
@@ -1165,18 +1174,26 @@
 	int rtp_code;
 	RAII_VAR(struct ast_format_cap *, caps, NULL, ao2_cleanup);
 	enum ast_media_type media_type = stream_to_media_type(session_media->stream_type);
-	int use_override_prefs = ast_format_cap_count(session->req_caps);
+	int use_override_prefs = ast_stream_topology_get_count(session->req_topology);
+	/* XXX Getting first stream by media type is not going to cut it when we're creating multiple streams of the same type */
+	struct ast_stream *req_stream = ast_stream_topology_get_first_stream_by_type(session->req_topology, media_type);
+	struct ast_stream *endpoint_config_stream = ast_stream_topology_get_first_stream_by_type(session->endpoint->media.codecs, media_type);
+	struct ast_format_cap *req_caps;
+	struct ast_format_cap *endpoint_config_caps;
 
 	int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
 		ast_format_cap_count(session->direct_media_cap);
 
-	if ((use_override_prefs && !ast_format_cap_has_type(session->req_caps, media_type)) ||
-	    (!use_override_prefs && !ast_format_cap_has_type(session->endpoint->media.codecs, media_type))) {
+	if ((use_override_prefs && !req_stream) ||
+	    (!use_override_prefs && !endpoint_config_stream)) {
 		/* If no type formats are configured don't add a stream */
 		return 0;
 	} else if (!session_media->rtp && create_rtp(session, session_media)) {
 		return -1;
 	}
+
+	req_caps = req_stream ? ast_stream_get_formats(req_stream) : NULL;
+	endpoint_config_caps = ast_stream_get_formats(endpoint_config_stream);
 
 	set_ice_components(session, session_media);
 	enable_rtcp(session, session_media, NULL);
@@ -1244,12 +1261,12 @@
 	}
 
 	if (direct_media_enabled) {
-		ast_format_cap_get_compatible(session->endpoint->media.codecs, session->direct_media_cap, caps);
-	} else if (!ast_format_cap_count(session->req_caps) ||
-		!ast_format_cap_iscompatible(session->req_caps, session->endpoint->media.codecs)) {
-		ast_format_cap_append_from_cap(caps, session->endpoint->media.codecs, media_type);
+		ast_format_cap_get_compatible(endpoint_config_caps, session->direct_media_cap, caps);
+	} else if (!req_caps || !ast_format_cap_count(req_caps) ||
+		!ast_format_cap_iscompatible(req_caps, endpoint_config_caps)) {
+		ast_format_cap_append_from_cap(caps, endpoint_config_caps, media_type);
 	} else {
-		ast_format_cap_append_from_cap(caps, session->req_caps, media_type);
+		ast_format_cap_append_from_cap(caps, req_caps, media_type);
 	}
 
 	for (index = 0; index < ast_format_cap_count(caps); ++index) {
diff --git a/res/res_pjsip_session.c b/res/res_pjsip_session.c
index 9e3abf2..df5593d 100644
--- a/res/res_pjsip_session.c
+++ b/res/res_pjsip_session.c
@@ -47,6 +47,7 @@
 #include "asterisk/features_config.h"
 #include "asterisk/pickup.h"
 #include "asterisk/test.h"
+#include "asterisk/stream.h"
 
 #define SDP_HANDLER_BUCKETS 11
 
@@ -1330,7 +1331,7 @@
 	ao2_cleanup(session->endpoint);
 	ao2_cleanup(session->aor);
 	ao2_cleanup(session->contact);
-	ao2_cleanup(session->req_caps);
+	ast_stream_topology_free(session->req_topology);
 	ao2_cleanup(session->direct_media_cap);
 
 	ast_dsp_free(session->dsp);
@@ -1422,8 +1423,8 @@
 	if (!session->direct_media_cap) {
 		return NULL;
 	}
-	session->req_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
-	if (!session->req_caps) {
+	session->req_topology = ast_stream_topology_alloc();
+	if (!session->req_topology) {
 		return NULL;
 	}
 	session->datastores = ao2_container_alloc(DATASTORE_BUCKETS, datastore_hash, datastore_cmp);
@@ -1704,7 +1705,7 @@
 
 struct ast_sip_session *ast_sip_session_create_outgoing(struct ast_sip_endpoint *endpoint,
 	struct ast_sip_contact *contact, const char *location, const char *request_user,
-	struct ast_format_cap *req_caps)
+	struct ast_stream_topology *req_topology)
 {
 	const char *uri = NULL;
 	RAII_VAR(struct ast_sip_aor *, found_aor, NULL, ao2_cleanup);
@@ -1767,22 +1768,50 @@
 	session->aor = ao2_bump(found_aor);
 	ast_party_id_copy(&session->id, &endpoint->id.self);
 
-	if (ast_format_cap_count(req_caps)) {
-		/* get joint caps between req_caps and endpoint caps */
-		struct ast_format_cap *joint_caps = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+	if (ast_stream_topology_get_count(req_topology) > 0) {
+		/* get joint caps between req_topology and endpoint topology */
+		int i;
 
-		ast_format_cap_get_compatible(req_caps, endpoint->media.codecs, joint_caps);
+		for (i = 0; i < ast_stream_topology_get_count(req_topology); ++i) {
+			struct ast_stream *req_stream;
+			struct ast_stream *config_stream;
+			struct ast_format_cap *req_cap;
+			struct ast_format_cap *config_cap;
+			struct ast_format_cap *joint_cap;
+			enum ast_media_type type;
 
-		/* if joint caps */
-		if (ast_format_cap_count(joint_caps)) {
-			/* copy endpoint caps into session->req_caps */
-			ast_format_cap_append_from_cap(session->req_caps,
-				endpoint->media.codecs, AST_MEDIA_TYPE_UNKNOWN);
-			/* replace instances of joint caps equivalents in session->req_caps */
-			ast_format_cap_replace_from_cap(session->req_caps, joint_caps,
-				AST_MEDIA_TYPE_UNKNOWN);
+			req_stream = ast_stream_topology_get_stream(req_topology, i);
+			type = ast_stream_get_type(req_stream);
+			/* XXX The retrieval of config_stream here is currently assuming that there
+			 * is but one stream of a given media type. This may need to change when
+			 * supporting true multistream mode. If all streams support the same codecs,
+			 * then this will still work though.
+			 */
+			config_stream = ast_stream_topology_get_first_stream_by_type(endpoint->media.codecs, type);
+
+			if (!config_stream) {
+				continue;
+			}
+
+			req_cap = ast_stream_get_formats(req_stream);
+			config_cap = ast_stream_get_formats(config_stream);
+
+			joint_cap = ast_format_cap_alloc(AST_FORMAT_CAP_FLAG_DEFAULT);
+			if (!joint_cap) {
+				continue;
+			}
+
+			ast_format_cap_get_compatible(req_cap, config_cap, joint_cap);
+			if (ast_format_cap_count(joint_cap) > 0) {
+				/* copy endpoint caps into req_caps */
+				ast_format_cap_append_from_cap(req_cap,
+						config_cap, AST_MEDIA_TYPE_UNKNOWN);
+				/* replace instances of joint caps equivalents in req_caps */
+				ast_format_cap_replace_from_cap(req_cap, joint_cap,
+						AST_MEDIA_TYPE_UNKNOWN);
+				ast_stream_topology_append_stream(session->req_topology, ast_stream_clone(req_stream));
+			}
 		}
-		ao2_cleanup(joint_caps);
 	}
 
 	if (pjsip_dlg_add_usage(dlg, &session_module, NULL) != PJ_SUCCESS) {

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

Gerrit-MessageType: newchange
Gerrit-Change-Id: I0358ea3108e2c06ae43308abddf216a23deccb90
Gerrit-PatchSet: 1
Gerrit-Project: asterisk
Gerrit-Branch: master
Gerrit-Owner: Mark Michelson <mmichelson at digium.com>



More information about the asterisk-code-review mailing list