[svn-commits] file: branch group/pimp_my_sip r381610 - in /team/group/pimp_my_sip: channels...

SVN commits to the Digium repositories svn-commits at lists.digium.com
Sat Feb 16 10:15:25 CST 2013


Author: file
Date: Sat Feb 16 10:15:22 2013
New Revision: 381610

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=381610
Log:
Improve media handling and add some additional functionality.

1. The act of negotiating a media stream and applying the negotiated stream are now separate operations.
2. Hold/unhold is now implemented.
3. RTP over IPv6 is now implemented.
4. Use of the ptime attribute works.
5. Local Packet2Packet bridging works.
6. Symmetric RTP can now be enabled on a per-endpoint basis.

Review: https://reviewboard.asterisk.org/r/2318/

Modified:
    team/group/pimp_my_sip/channels/chan_gulp.c
    team/group/pimp_my_sip/configs/res_sip.conf.sample
    team/group/pimp_my_sip/include/asterisk/res_sip.h
    team/group/pimp_my_sip/include/asterisk/res_sip_session.h
    team/group/pimp_my_sip/res/res_sip/sip_configuration.c
    team/group/pimp_my_sip/res/res_sip_sdp_audio.c
    team/group/pimp_my_sip/res/res_sip_session.c

Modified: team/group/pimp_my_sip/channels/chan_gulp.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/channels/chan_gulp.c?view=diff&rev=381610&r1=381609&r2=381610
==============================================================================
--- team/group/pimp_my_sip/channels/chan_gulp.c (original)
+++ team/group/pimp_my_sip/channels/chan_gulp.c Sat Feb 16 10:15:22 2013
@@ -107,10 +107,19 @@
 	.incoming_response = gulp_incoming_response,
 };
 
-/*! \brief Function called by RTP engine to get local RTP peer */
+/*! \brief Function called by RTP engine to get local audio RTP peer */
 static enum ast_rtp_glue_result gulp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
 {
-	return AST_RTP_GLUE_RESULT_FORBID;
+	struct ast_sip_session *session = ast_channel_tech_pvt(chan);
+
+	if (!session || !session->media[AST_SIP_MEDIA_AUDIO].rtp) {
+		return AST_RTP_GLUE_RESULT_FORBID;
+	}
+
+	ao2_ref(session->media[AST_SIP_MEDIA_AUDIO].rtp, +1);
+	*instance = session->media[AST_SIP_MEDIA_AUDIO].rtp;
+
+	return AST_RTP_GLUE_RESULT_LOCAL;
 }
 
 /*! \brief Function called by RTP engine to get peer capabilities */
@@ -156,11 +165,6 @@
 	ast_format_copy(ast_channel_readformat(chan), &fmt);
 	ast_format_copy(ast_channel_rawreadformat(chan), &fmt);
 
-	if (session->media.audio) {
-		ast_channel_set_fd(chan, 0, ast_rtp_instance_fd(session->media.audio, 0));
-		ast_channel_set_fd(chan, 1, ast_rtp_instance_fd(session->media.audio, 1));
-	}
-
 	if (state == AST_STATE_RING) {
 		ast_channel_rings_set(chan, 1);
 	}
@@ -216,16 +220,16 @@
 
 	switch (ast_channel_fdno(ast)) {
 	case 0:
-		f = ast_rtp_instance_read(session->media.audio, 0);
+		f = ast_rtp_instance_read(session->media[AST_SIP_MEDIA_AUDIO].rtp, 0);
 		break;
 	case 1:
-		f = ast_rtp_instance_read(session->media.audio, 1);
+		f = ast_rtp_instance_read(session->media[AST_SIP_MEDIA_AUDIO].rtp, 1);
 		break;
 	default:
 		f = &ast_null_frame;
 	}
 
-	if (f->frametype == AST_FRAME_VOICE) {
+	if (f && f->frametype == AST_FRAME_VOICE) {
 		if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &f->subclass.format))) {
 			ast_debug(1, "Oooh, format changed to %s\n", ast_getformatname(&f->subclass.format));
 			ast_format_cap_set(ast_channel_nativeformats(ast), &f->subclass.format);
@@ -256,8 +260,8 @@
 				ast_getformatname(ast_channel_writeformat(ast)));
 			return 0;
 		}
-		if (session->media.audio) {
-			res = ast_rtp_instance_write(session->media.audio, frame);
+		if (session->media[AST_SIP_MEDIA_AUDIO].rtp) {
+			res = ast_rtp_instance_write(session->media[AST_SIP_MEDIA_AUDIO].rtp, frame);
 		}
 		break;
 	default:
@@ -449,7 +453,9 @@
 
 	switch (session->endpoint->dtmf) {
 	case AST_SIP_DTMF_RFC_4733:
-		ast_rtp_instance_dtmf_begin(session->media.audio, digit);
+		if (session->media[AST_SIP_MEDIA_AUDIO].rtp) {
+			ast_rtp_instance_dtmf_begin(session->media[AST_SIP_MEDIA_AUDIO].rtp, digit);
+		}
 	case AST_SIP_DTMF_NONE:
 		break;
 	case AST_SIP_DTMF_INBAND:
@@ -473,7 +479,9 @@
 		/* TODO: Send INFO dtmf here */
 		break;
 	case AST_SIP_DTMF_RFC_4733:
-		ast_rtp_instance_dtmf_end_with_duration(session->media.audio, digit, duration);
+		if (session->media[AST_SIP_MEDIA_AUDIO].rtp) {
+			ast_rtp_instance_dtmf_end_with_duration(session->media[AST_SIP_MEDIA_AUDIO].rtp, digit, duration);
+		}
 	case AST_SIP_DTMF_NONE:
 		break;
 	case AST_SIP_DTMF_INBAND:
@@ -864,11 +872,9 @@
 		}
 		break;
 	case 183:
-		ast_rtp_instance_activate(session->media.audio);
 		ast_queue_control(session->channel, AST_CONTROL_PROGRESS);
 		break;
 	case 200:
-		ast_rtp_instance_activate(session->media.audio);
 		ast_queue_control(session->channel, AST_CONTROL_ANSWER);
 		break;
 	default:

Modified: team/group/pimp_my_sip/configs/res_sip.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/configs/res_sip.conf.sample?view=diff&rev=381610&r1=381609&r2=381610
==============================================================================
--- team/group/pimp_my_sip/configs/res_sip.conf.sample (original)
+++ team/group/pimp_my_sip/configs/res_sip.conf.sample Sat Feb 16 10:15:22 2013
@@ -18,3 +18,7 @@
 ;timers=yes               ; Enable or disable session timers support - valid options are: yes, no, required, forced
 ;timers_min_se=90         ; Minimum session timers expiration period, in seconds
 ;timers_sess_expires=1800 ; Session timers expiration period, in seconds
+;mohsuggest=example	      ; What musiconhold class to suggest that the peer channel use when this endpoint places them on hold
+;rtp_ipv6=yes             ; Force IPv6 for RTP transport
+;rtp_symmetric=yes        ; Enable symmetric RTP support
+;use_ptime=yes            ; Whether to use the ptime value received from the endpoint or not

Modified: team/group/pimp_my_sip/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/include/asterisk/res_sip.h?view=diff&rev=381610&r1=381609&r2=381610
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip.h Sat Feb 16 10:15:22 2013
@@ -218,6 +218,8 @@
 		AST_STRING_FIELD(context);
 		/*! Name of an explicit transport to use */
 		AST_STRING_FIELD(transport);
+                /*! Musiconhold class to suggest that the other side use when placing on hold */
+                AST_STRING_FIELD(mohsuggest);
 	);
 	/*! Identification information for this endpoint */
 	struct ast_party_id id;
@@ -235,12 +237,18 @@
 	size_t num_auths;
 	/*! DTMF mode to use with this endpoint */
 	enum ast_sip_dtmf_mode dtmf;
+	/*! Whether IPv6 RTP is enabled or not */
+	unsigned int rtp_ipv6;
+	/*! Whether symmetric RTP is enabled or not */
+	unsigned int rtp_symmetric;
+	/*! Whether to use the "ptime" attribute received from the endpoint or not */
+	unsigned int use_ptime;
 	/*! Enabled SIP extensions */
 	unsigned int extensions;
 	/*! Minimum session expiration period, in seconds */
-	unsigned min_se;
+	unsigned int min_se;
 	/*! Session expiration period, in seconds */
-	unsigned sess_expires;
+	unsigned int sess_expires;
 	/*! List of outbound registrations */
 	AST_LIST_HEAD_NOLOCK(, ast_sip_registration) registrations;
 	/*! Frequency to send OPTIONS requests to endpoint. 0 is disabled. */

Modified: team/group/pimp_my_sip/include/asterisk/res_sip_session.h
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/include/asterisk/res_sip_session.h?view=diff&rev=381610&r1=381609&r2=381610
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip_session.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip_session.h Sat Feb 16 10:15:22 2013
@@ -38,11 +38,25 @@
 struct ast_rtp_instance;
 
 /*!
+ * \brief Positions of various media
+ */
+enum ast_sip_session_media_position {
+	/*! \brief First is audio */
+	AST_SIP_MEDIA_AUDIO = 0,
+	/*! \brief Second is video */
+	AST_SIP_MEDIA_VIDEO,
+	/*! \brief Last is the size for media details */
+	AST_SIP_MEDIA_SIZE,
+};
+
+/*!
  * \brief A structure containing SIP session media information
  */
 struct ast_sip_session_media {
-	/* RTP instance for audio */
-	struct ast_rtp_instance *audio;
+	/*! \brief RTP instance itself */
+	struct ast_rtp_instance *rtp;
+	/*! \brief Stream is on hold */
+	unsigned int held:1;
 };
 
 /*!
@@ -66,8 +80,8 @@
 	AST_LIST_HEAD(, ast_sip_session_supplement) supplements;
 	/* Datastores added to the session by supplements to the session */
 	struct ao2_container *datastores;
-	/* Media information */
-	struct ast_sip_session_media media;
+	/* Media streams */
+	struct ast_sip_session_media media[AST_SIP_MEDIA_SIZE];
 	/* Workspace for tasks relating to this SIP session */
 	struct ast_sip_work *work;
 };
@@ -158,6 +172,15 @@
 	 * \retval <0 There was an error encountered. No further operation will take place and the current negotiation will be abandoned.
 	 * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called.
 	 */
+	int (*negotiate_incoming_sdp_stream)(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream);
+	/*!
+	 * \brief Create an SDP media stream and add it to the outgoing SDP offer or answer
+	 * \param session The session for which media is being added
+	 * \param stream The stream on which to operate
+	 * \retval 0 The stream was not handled by this handler. If there are other registered handlers for this stream type, they will be called.
+	 * \retval <0 There was an error encountered. No further operation will take place and the current negotiation will be abandoned.
+	 * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called.
+	 */
 	int (*handle_incoming_sdp_stream)(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, struct pjmedia_sdp_media *stream);
 	/*!
 	 * \brief Create an SDP media stream and add it to the outgoing SDP offer or answer
@@ -168,6 +191,19 @@
 	 * \retval >0 The handler has a stream to be added to the SDP. No further handler of this stream type will be called.
 	 */
 	int (*create_outgoing_sdp_stream)(struct ast_sip_session *session, struct pjmedia_sdp_session *sdp);
+	/*!
+	 * \brief Apply a negotiated SDP media stream
+	 * \param session The session for which media is being applied
+	 * \param local The entire local negotiated SDP
+	 * \param local_stream The local stream which to apply
+	 * \param remote The entire remote negotiated SDP
+	 * \param remote_stream The remote stream which to apply
+	 * \retval 0 The stream was not applied by this handler. If there are other registered handlers for this stream type, they will be called.
+	 * \retval <0 There was an error encountered. No further operation will take place and the current application will be abandoned.
+	 * \retval >0 The stream was handled by this handler. No further handler of this stream type will be called.
+	 */
+	int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
+		const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream);
 	/*! Next item int he list. */
 	AST_LIST_ENTRY(ast_sip_session_sdp_handler) next;
 };

Modified: team/group/pimp_my_sip/res/res_sip/sip_configuration.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip/sip_configuration.c?view=diff&rev=381610&r1=381609&r2=381610
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/sip_configuration.c (original)
+++ team/group/pimp_my_sip/res/res_sip/sip_configuration.c Sat Feb 16 10:15:22 2013
@@ -276,7 +276,11 @@
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "qualify_frequency", 0, OPT_UINT_T, PARSE_IN_RANGE, FLDSET(struct ast_sip_endpoint, qualify_frequency), 0, 86400);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "host", "", host_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "dtmfmode", "rfc4733", dtmf_handler, NULL, 0, 0);
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_ipv6", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_ipv6));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "rtp_symmetric", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, rtp_symmetric));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "use_ptime", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, use_ptime));
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "transport", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, transport));
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "mohsuggest", "default", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, mohsuggest));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "100rel", "yes", prack_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "timers", "yes", timers_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "timers_min_se", "90", OPT_UINT_T, 0, FLDSET(struct ast_sip_endpoint, min_se));

Modified: team/group/pimp_my_sip/res/res_sip_sdp_audio.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_sdp_audio.c?view=diff&rev=381610&r1=381609&r2=381610
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_sdp_audio.c (original)
+++ team/group/pimp_my_sip/res/res_sip_sdp_audio.c Sat Feb 16 10:15:22 2013
@@ -54,24 +54,35 @@
 static struct ast_sched_context *sched;
 
 /*! \brief Forward declarations for SDP handler functions */
-static int audio_handle_incoming_sdp_stream(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, struct pjmedia_sdp_media *stream);
+static int audio_negotiate_incoming_sdp_stream(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream);
 static int audio_create_outgoing_sdp_stream(struct ast_sip_session *session, struct pjmedia_sdp_session *sdp);
+static int audio_apply_negotiated_sdp_stream(struct ast_sip_session *session, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
+	const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream);
+
+/*! \brief Forward declaration for session supplement functions */
+static void audio_session_destroy(struct ast_sip_session *session);
 
 /*! \brief SDP handler for 'audio' media stream */
 static struct ast_sip_session_sdp_handler audio_sdp_handler = {
 	.id = "audio",
-	.handle_incoming_sdp_stream = audio_handle_incoming_sdp_stream,
+	.negotiate_incoming_sdp_stream = audio_negotiate_incoming_sdp_stream,
 	.create_outgoing_sdp_stream = audio_create_outgoing_sdp_stream,
+	.apply_negotiated_sdp_stream = audio_apply_negotiated_sdp_stream,
 };
 
+/*! \brief Session supplement for 'audio' media stream */
+static struct ast_sip_session_supplement audio_session_supplement = {
+	.session_destroy = audio_session_destroy,
+};
+
 /*! \brief Internal function which creates an RTP instance */
-static int audio_create_rtp(struct ast_sip_session *session)
+static int audio_create_rtp(struct ast_sip_session *session, unsigned int ipv6)
 {
 	pj_sockaddr addr;
 	char hostip[PJ_INET6_ADDRSTRLEN+2];
 	struct ast_sockaddr tmp;
 
-	if (pj_gethostip(pj_AF_INET(), &addr) != PJ_SUCCESS) {
+	if (pj_gethostip(ipv6 ? pj_AF_INET6() : pj_AF_INET(), &addr) != PJ_SUCCESS) {
 		return -1;
 	}
 
@@ -81,137 +92,56 @@
 		return -1;
 	}
 
-	if (!(session->media.audio = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
-		return -1;
-	}
-
-	ast_rtp_instance_set_prop(session->media.audio, AST_RTP_PROPERTY_RTCP, 1);
-
-	ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session->media.audio), session->media.audio, &session->endpoint->prefs);
-	
+	if (!(session->media[AST_SIP_MEDIA_AUDIO].rtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
+		return -1;
+	}
+
+	ast_rtp_instance_set_prop(session->media[AST_SIP_MEDIA_AUDIO].rtp, AST_RTP_PROPERTY_RTCP, 1);
+	ast_rtp_instance_set_prop(session->media[AST_SIP_MEDIA_AUDIO].rtp, AST_RTP_PROPERTY_NAT, session->endpoint->rtp_symmetric);
+
+	ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp),
+					 session->media[AST_SIP_MEDIA_AUDIO].rtp, &session->endpoint->prefs);
+
 	return 0;
 }
 
-/*! \brief Function which handles an incoming 'audio' stream */
-static int audio_handle_incoming_sdp_stream(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, struct pjmedia_sdp_media *stream)
-{
-	int res = 1, addrs_cnt, format, othercapability = 0;
+/*! \brief Function which negotiates an incoming 'audio' stream */
+static int audio_negotiate_incoming_sdp_stream(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
+{
 	char host[NI_MAXHOST];
-	struct ast_sockaddr *addrs;
-	struct ast_rtp_codecs codecs;
-	const pjmedia_sdp_attr *attr;
-	struct ast_format_cap *cap, *jointcap = NULL, *peercap = NULL;
-//	struct ast_format fmt;
-
-	/* If the stream has been rejected stop media if active */
-	if (!stream->desc.port) {
-		if (session->media.audio) {
-			ast_rtp_instance_stop(session->media.audio);
-		}
-
-		return 1;
-	}
-
-	/* Create an RTP instance if need be */
-	if (!session->media.audio && audio_create_rtp(session)) {
-		return -1;
-	}
-
-	if (!stream->conn) {
-		snprintf(host, sizeof(host), "%.*s", (int) pj_strlen(&sdp->conn->addr), pj_strbuf(&sdp->conn->addr));
-	} else {
-		snprintf(host, sizeof(host), "%.*s", (int) pj_strlen(&stream->conn->addr), pj_strbuf(&stream->conn->addr));
-	}
+	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
+
+	/* If no audio formats have been configured reject this stream */
+	if (!ast_format_cap_has_type(session->endpoint->codecs, AST_FORMAT_TYPE_AUDIO)) {
+		return 0;
+	}
+
+	ast_copy_pj_str(host, stream->conn ? &stream->conn->addr : &sdp->conn->addr, sizeof(host));
 
 	/* Ensure that the address provided is valid */
-	if ((addrs_cnt = ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC)) <= 0) {
-		ast_log(LOG_ERROR, "Failed to resolve %s\n", host);
-		/* The provided host was actually invalid so we kill the session as it would just end up being broken */
-		if (session->channel) {
-			ast_channel_hangupcause_set(session->channel, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
-		}
-		return -1;
-	}
-
-	/* To properly apply formats to the channel we need to keep track of capabilities */
-	if (!(cap = ast_format_cap_alloc_nolock()) ||
-	    !(peercap = ast_format_cap_alloc_nolock())) {
-		ast_log(LOG_ERROR, "Failed to allocate audio capabilities\n");
-		res = -1;
-		goto cleanup;
-	}
-
-	/* Apply connection information to the RTP instance */
-	ast_sockaddr_set_port(addrs, stream->desc.port);
-	ast_rtp_instance_set_remote_address(session->media.audio, addrs);
-	ast_free(addrs);
-
-	ast_rtp_codecs_payloads_initialize(&codecs);
-
-	/* Iterate through provided formats */
-	for (format = 0; format < stream->desc.fmt_count; format++) {
-		/* The payload is kept as a string for things like t38 but for audio it is always numerical */
-		ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, pj_strtoul(&stream->desc.fmt[format]));
-
-		/* Look for the optional rtpmap attribute */
-		if ((attr = pjmedia_sdp_media_find_attr2(stream, "rtpmap", &stream->desc.fmt[format]))) {
-			pjmedia_sdp_rtpmap *rtpmap;
-
-			/* Interpret the attribute as an rtpmap */
-			if ((pjmedia_sdp_attr_to_rtpmap(session->inv_session->pool_active, attr, &rtpmap)) == PJ_SUCCESS) {
-				char name[32];
-
-				snprintf(name, sizeof(name), "%.*s", (int) pj_strlen(&rtpmap->enc_name), pj_strbuf(&rtpmap->enc_name));
-				ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL, pj_strtoul(&stream->desc.fmt[format]),
-									     "audio", name, 0, rtpmap->clock_rate);
-			}
-		}
-	}
-
-	ast_rtp_codecs_payload_formats(&codecs, peercap, &othercapability);
-
-	/* Using the configured codecs and the codecs in this SDP we determine the joint formats for *audio only* */
-	ast_format_cap_copy(cap, session->endpoint->codecs);
-	ast_format_cap_remove_bytype(cap, AST_FORMAT_TYPE_VIDEO);
-
-	if (!(jointcap = ast_format_cap_joint(cap, peercap))) {
-		char usbuf[64];
-		char thembuf[64];
-		if (session->channel) {
-			ast_channel_hangupcause_set(session->channel, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
-		}
-		ast_getformatname_multiple(usbuf, sizeof(usbuf), cap);
-		ast_getformatname_multiple(thembuf, sizeof(thembuf), peercap);
-		ast_log(LOG_WARNING, "No joint capabilities between our configuration(%s) and incoming SDP(%s)\n", usbuf, thembuf);
-		res = -1;
-		goto cleanup;
-	}
-
-	ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session->media.audio), session->media.audio);
-
-	/* Now that we have joint formats for audio remove the existing ones from the channel and add the new ones */
-//	ast_format_cap_copy(cap, ast_channel_nativeformats(session->channel));
-//	ast_format_cap_remove_bytype(cap, AST_FORMAT_TYPE_AUDIO);
-//	ast_format_cap_append(cap, jointcap);
-
-	/* Apply the new formats to the channel, potentially changing read/write formats while doing so */
-//	ast_format_cap_copy(ast_channel_nativeformats(session->channel), cap);
-//	ast_codec_choose(&session->endpoint->prefs, cap, 1, &fmt);
-//	ast_set_read_format(session->channel, &fmt);
-//	ast_set_write_format(session->channel, &fmt);
-
-cleanup:
-	ast_format_cap_destroy(peercap);
-	ast_format_cap_destroy(jointcap);
-	ast_format_cap_destroy(cap);
-	ast_rtp_codecs_payloads_destroy(&codecs);
-
-	return res;
+	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) {
+		/* The provided host was actually invalid so we error out this negotiation */
+		return -1;
+	}
+
+	/* Using the connection information create an appropriate RTP instance */
+	if (!session->media[AST_SIP_MEDIA_AUDIO].rtp && audio_create_rtp(session, ast_sockaddr_is_ipv6(addrs))) {
+		return -1;
+	}
+
+	/* pjmedia takes care of the formats and such */
+	return 1;
 }
 
 /*! \brief Function which creates an outgoing 'audio' stream */
 static int audio_create_outgoing_sdp_stream(struct ast_sip_session *session, struct pjmedia_sdp_session *sdp)
 {
+	static const pj_str_t STR_AUDIO = { "audio", 5};
+	static const pj_str_t STR_IN = { "IN", 2 };
+	static const pj_str_t STR_IP4 = { "IP4", 3};
+	static const pj_str_t STR_IP6 = { "IP6", 3};
+	static const pj_str_t STR_RTP_AVP = { "RTP/AVP", 7 };
+	static const pj_str_t STR_SENDRECV = { "sendrecv", 8 };
 	pj_pool_t *pool = session->inv_session->pool_active;
 	pjmedia_sdp_media *media;
 	struct ast_sockaddr addr;
@@ -223,7 +153,7 @@
 	if (!ast_format_cap_has_type(session->endpoint->codecs, AST_FORMAT_TYPE_AUDIO)) {
 		/* If no audio formats are configured don't add a stream */
 		return 0;
-	} else if (!session->media.audio && audio_create_rtp(session)) {
+	} else if (!session->media[AST_SIP_MEDIA_AUDIO].rtp && audio_create_rtp(session, session->endpoint->rtp_ipv6)) {
 		return -1;
 	}
 
@@ -233,13 +163,13 @@
 	}
 
 	/* TODO: This should eventually support SRTP */
-	pj_strdup2(pool, &media->desc.media, "audio");
-	pj_strdup2(pool, &media->desc.transport, "RTP/AVP");
+	media->desc.media = STR_AUDIO;
+	media->desc.transport = STR_RTP_AVP;
 
 	/* Add connection level details */
-	ast_rtp_instance_get_local_address(session->media.audio, &addr);
-	pj_strdup2(pool, &media->conn->net_type, "IN");
-	pj_strdup2(pool, &media->conn->addr_type, (ast_sockaddr_is_ipv6(&addr) && !ast_sockaddr_is_ipv4_mapped(&addr)) ? "IP6" : "IP4");
+	ast_rtp_instance_get_local_address(session->media[AST_SIP_MEDIA_AUDIO].rtp, &addr);
+	media->conn->net_type = STR_IN;
+	media->conn->addr_type = (ast_sockaddr_is_ipv6(&addr) && !ast_sockaddr_is_ipv4_mapped(&addr)) ? STR_IP6 : STR_IP4;
 	pj_strdup2(pool, &media->conn->addr, ast_sockaddr_stringify_addr_remote(&addr));
 	media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr);
 	media->desc.port_count = 1;
@@ -249,13 +179,14 @@
 		struct ast_format format;
 		int rtp_code;
 		pjmedia_sdp_rtpmap rtpmap;
-		struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session->media.audio)->pref;
+		struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp)->pref;
 
 		if (!ast_codec_pref_index(&session->endpoint->prefs, index, &format)) {
 			break;
 		} else if (AST_FORMAT_GET_TYPE(format.id) != AST_FORMAT_TYPE_AUDIO) {
 			continue;
-		} else if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session->media.audio), 1, &format, 0)) == -1) {
+		} else if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp),
+								   1, &format, 0)) == -1) {
 			return -1;
 		}
 
@@ -282,7 +213,8 @@
 		int rtp_code;
 		pjmedia_sdp_rtpmap rtpmap;
 
-		if (!(noncodec & index) || (rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session->media.audio), 0, NULL, index)) == -1) {
+		if (!(noncodec & index) || (rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp),
+										   0, NULL, index)) == -1) {
 			continue;
 		}
 
@@ -310,10 +242,167 @@
 		media->attr[media->attr_count++] = attr;
 	}
 
+	/* Add the sendrecv attribute - we purposely don't keep track because pjmedia-sdp will automatically change our offer for us */
+	attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+	attr->name = STR_SENDRECV;
+	media->attr[media->attr_count++] = attr;
+
 	/* Add the media stream to the SDP */
 	sdp->media[sdp->media_count++] = media;
 
 	return 1;
+}
+
+/*! \brief Function which applies a negotiated SDP stream */
+static int audio_apply_negotiated_sdp_stream(struct ast_sip_session *session, const struct pjmedia_sdp_session *local, const struct pjmedia_sdp_media *local_stream,
+	const struct pjmedia_sdp_session *remote, const struct pjmedia_sdp_media *remote_stream)
+{
+	int format, othercapability = 0;
+	char host[NI_MAXHOST];
+	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
+	struct ast_rtp_codecs codecs;
+	const pjmedia_sdp_attr *attr;
+	RAII_VAR(struct ast_format_cap *, cap, NULL, ast_format_cap_destroy);
+	RAII_VAR(struct ast_format_cap *, jointcap, NULL, ast_format_cap_destroy);
+	RAII_VAR(struct ast_format_cap *, peercap, NULL, ast_format_cap_destroy);
+	struct ast_format fmt;
+
+	/* Create an RTP instance if need be */
+	if (!session->media[AST_SIP_MEDIA_AUDIO].rtp && audio_create_rtp(session, session->endpoint->rtp_ipv6)) {
+		return -1;
+	}
+
+	ast_copy_pj_str(host, remote_stream->conn ? &remote_stream->conn->addr : &remote->conn->addr, sizeof(host));
+
+	/* Ensure that the address provided is valid */
+	if (ast_sockaddr_resolve(&addrs, host, PARSE_PORT_FORBID, AST_AF_UNSPEC) <= 0) {
+		/* The provided host was actually invalid so we error out this negotiation */
+		return -1;
+	}
+
+	/* To properly apply formats to the channel we need to keep track of capabilities */
+	if (!(cap = ast_format_cap_alloc_nolock()) ||
+	    !(peercap = ast_format_cap_alloc_nolock())) {
+		ast_log(LOG_ERROR, "Failed to allocate audio capabilities\n");
+		return -1;
+	}
+
+	/* Apply connection information to the RTP instance */
+	ast_sockaddr_set_port(addrs, remote_stream->desc.port);
+	ast_rtp_instance_set_remote_address(session->media[AST_SIP_MEDIA_AUDIO].rtp, addrs);
+
+	ast_rtp_codecs_payloads_initialize(&codecs);
+
+	/* Iterate through provided formats */
+	for (format = 0; format < local_stream->desc.fmt_count; format++) {
+		/* The payload is kept as a string for things like t38 but for audio it is always numerical */
+		ast_rtp_codecs_payloads_set_m_type(&codecs, NULL, pj_strtoul(&local_stream->desc.fmt[format]));
+
+		/* Look for the optional rtpmap attribute */
+		if ((attr = pjmedia_sdp_media_find_attr2(local_stream, "rtpmap", &local_stream->desc.fmt[format]))) {
+			pjmedia_sdp_rtpmap *rtpmap;
+
+			/* Interpret the attribute as an rtpmap */
+			if ((pjmedia_sdp_attr_to_rtpmap(session->inv_session->pool_active, attr, &rtpmap)) == PJ_SUCCESS) {
+				char name[32];
+
+				ast_copy_pj_str(name, &rtpmap->enc_name, sizeof(name));
+				ast_rtp_codecs_payloads_set_rtpmap_type_rate(&codecs, NULL, pj_strtoul(&local_stream->desc.fmt[format]),
+									     "audio", name, 0, rtpmap->clock_rate);
+			}
+		}
+	}
+
+	ast_rtp_codecs_payload_formats(&codecs, peercap, &othercapability);
+
+	/* Apply packetization if available and configured to do so */
+	if (session->endpoint->use_ptime && (attr = pjmedia_sdp_media_find_attr2(remote_stream, "ptime", NULL))) {
+		pj_str_t value = attr->value;
+		unsigned long framing = pj_strtoul(pj_strltrim(&value));
+		int codec;
+		struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp)->pref;
+
+		for (codec = 0; codec < AST_RTP_MAX_PT; codec++) {
+			struct ast_rtp_payload_type format = ast_rtp_codecs_payload_lookup(ast_rtp_instance_get_codecs(
+				session->media[AST_SIP_MEDIA_AUDIO].rtp), codec);
+
+			if (!format.asterisk_format) {
+				continue;
+			}
+
+			ast_codec_pref_setsize(pref, &format.format, framing);
+		}
+
+		ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp),
+			session->media[AST_SIP_MEDIA_AUDIO].rtp, pref);
+	}
+
+	/* Using the configured codecs and the codecs in this SDP we determine the joint formats for *audio only* */
+	ast_format_cap_copy(cap, session->endpoint->codecs);
+	ast_format_cap_remove_bytype(cap, AST_FORMAT_TYPE_VIDEO);
+
+	if (!(jointcap = ast_format_cap_joint(cap, peercap))) {
+		char usbuf[64], thembuf[64];
+
+		ast_channel_hangupcause_set(session->channel, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+		ast_getformatname_multiple(usbuf, sizeof(usbuf), cap);
+		ast_getformatname_multiple(thembuf, sizeof(thembuf), peercap);
+		ast_log(LOG_WARNING, "No joint capabilities between our configuration(%s) and incoming SDP(%s)\n", usbuf, thembuf);
+
+		ast_rtp_codecs_payloads_destroy(&codecs);
+		return -1;
+	}
+
+	ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp),
+				     session->media[AST_SIP_MEDIA_AUDIO].rtp);
+
+	/* Now that we have joint formats for audio remove the existing ones from the channel and add the new ones */
+	ast_format_cap_copy(cap, ast_channel_nativeformats(session->channel));
+	ast_format_cap_remove_bytype(cap, AST_FORMAT_TYPE_AUDIO);
+	ast_format_cap_append(cap, jointcap);
+
+	/* Apply the new formats to the channel, potentially changing read/write formats while doing so */
+	ast_format_cap_copy(ast_channel_nativeformats(session->channel), cap);
+	ast_codec_choose(&session->endpoint->prefs, cap, 1, &fmt);
+	ast_format_copy(ast_channel_rawwriteformat(session->channel), &fmt);
+	ast_format_copy(ast_channel_rawreadformat(session->channel), &fmt);
+	ast_set_read_format(session->channel, ast_channel_readformat(session->channel));
+	ast_set_write_format(session->channel, ast_channel_writeformat(session->channel));
+
+	ast_channel_set_fd(session->channel, 0, ast_rtp_instance_fd(session->media[AST_SIP_MEDIA_AUDIO].rtp, 0));
+	ast_channel_set_fd(session->channel, 1, ast_rtp_instance_fd(session->media[AST_SIP_MEDIA_AUDIO].rtp, 1));
+
+	if (session->media[AST_SIP_MEDIA_AUDIO].held && (!ast_sockaddr_isnull(addrs) ||
+							  !pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL))) {
+		/* The remote side has taken us off hold */
+		ast_queue_control(session->channel, AST_CONTROL_UNHOLD);
+		ast_queue_frame(session->channel, &ast_null_frame);
+		session->media[AST_SIP_MEDIA_AUDIO].held = 0;
+	} else if (ast_sockaddr_isnull(addrs) || ast_sockaddr_is_any(addrs) || pjmedia_sdp_media_find_attr2(remote_stream, "sendonly", NULL)) {
+		/* The remote side has put us on hold */
+		ast_queue_control_data(session->channel, AST_CONTROL_HOLD, S_OR(session->endpoint->mohsuggest, NULL),
+				       !ast_strlen_zero(session->endpoint->mohsuggest) ? strlen(session->endpoint->mohsuggest) + 1 : 0);
+		ast_rtp_instance_stop(session->media[AST_SIP_MEDIA_AUDIO].rtp);
+		ast_queue_frame(session->channel, &ast_null_frame);
+		session->media[AST_SIP_MEDIA_AUDIO].held = 1;
+	} else {
+		/* The remote side has not changed state, but make sure the instance is active */
+		ast_rtp_instance_activate(session->media[AST_SIP_MEDIA_AUDIO].rtp);
+	}
+
+	ast_rtp_codecs_payloads_destroy(&codecs);
+	return 1;
+}
+
+/*! \brief Function which destroys the audio RTP instance when session ends */
+static void audio_session_destroy(struct ast_sip_session *session)
+{
+	if (!session->media[AST_SIP_MEDIA_AUDIO].rtp) {
+		return;
+	}
+
+	ast_rtp_instance_stop(session->media[AST_SIP_MEDIA_AUDIO].rtp);
+	ast_rtp_instance_destroy(session->media[AST_SIP_MEDIA_AUDIO].rtp);
 }
 
 /*!
@@ -338,6 +427,11 @@
 		goto end;
 	}
 
+	if (ast_sip_session_register_supplement(&audio_session_supplement)) {
+		ast_log(LOG_ERROR, "Unable to register session supplement for audio media stream\n");
+		goto end;
+	}
+
 	if (ast_sip_session_register_sdp_handler(&audio_sdp_handler, "audio")) {
 		ast_log(LOG_ERROR, "Unable to register SDP handler for 'audio' stream type\n");
 		goto end;
@@ -349,6 +443,7 @@
 		ast_sched_context_destroy(sched);
 	}
 
+	ast_sip_session_unregister_supplement(&audio_session_supplement);
 
 	return AST_MODULE_LOAD_FAILURE;
 }
@@ -357,6 +452,7 @@
 static int unload_module(void)
 {
 	ast_sip_session_unregister_sdp_handler(&audio_sdp_handler, "audio");
+	ast_sip_session_unregister_supplement(&audio_session_supplement);
 	ast_sched_context_destroy(sched);
 	return 0;
 }

Modified: team/group/pimp_my_sip/res/res_sip_session.c
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/res/res_sip_session.c?view=diff&rev=381610&r1=381609&r2=381610
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_session.c (original)
+++ team/group/pimp_my_sip/res/res_sip_session.c Sat Feb 16 10:15:22 2013
@@ -38,6 +38,7 @@
 #include "asterisk/lock.h"
 #include "asterisk/uuid.h"
 #include "asterisk/pbx.h"
+#include "asterisk/causes.h"
 
 #define SDP_HANDLER_BUCKETS 11
 
@@ -172,7 +173,41 @@
 			continue;
 		}
 		AST_LIST_TRAVERSE(&handler_list->list, handler, next) {
-			int res = handler->handle_incoming_sdp_stream(session, sdp, sdp->media[i]);
+			int res = handler->negotiate_incoming_sdp_stream(session, sdp, sdp->media[i]);
+			if (res < 0) {
+				/* Catastrophic failure. Abort! */
+				return -1;
+			} else if (res == 0) {
+				/* Not handled yet. Move to the next handler */
+				continue;
+			} else {
+				/* Handled by this handler. Move to the next stream */
+				break;
+			}
+		}
+	}
+	return 0;
+}
+
+static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote)
+{
+	int i;
+	for (i = 0; i < local->media_count; ++i) {
+		/* See if there are registered handlers for this media stream type */
+		char media[20];
+		struct ast_sip_session_sdp_handler *handler;
+		RAII_VAR(struct sdp_handler_list *, handler_list, NULL, ao2_cleanup);
+
+		/* We need a null-terminated version of the media string */
+		ast_copy_pj_str(media, &local->media[i]->desc.media, sizeof(media));
+
+		handler_list = ao2_find(sdp_handlers, media, OBJ_KEY);
+		if (!handler_list) {
+			ast_debug(1, "No registered SDP handlers for media type '%s'\n", media);
+			continue;
+		}
+		AST_LIST_TRAVERSE(&handler_list->list, handler, next) {
+			int res = handler->apply_negotiated_sdp_stream(session, local, local->media[i], remote, remote->media[i]);
 			if (res < 0) {
 				/* Catastrophic failure. Abort! */
 				return -1;
@@ -1162,6 +1197,10 @@
 
 static struct pjmedia_sdp_session *create_local_sdp(pjsip_inv_session *inv, struct ast_sip_session *session, const pjmedia_sdp_session *offer)
 {
+	static const pj_str_t STR_ASTERISK = { "Asterisk", 8 };
+	static const pj_str_t STR_IN = { "IN", 2 };
+	static const pj_str_t STR_IP4 = { "IP4", 3 };
+	static const pj_str_t STR_IP6 = { "IP6", 3 };
 	pjmedia_sdp_session *local;
 
 	if (!(local = PJ_POOL_ZALLOC_T(inv->pool, pjmedia_sdp_session))) {
@@ -1175,13 +1214,9 @@
 		local->origin.id = offer->origin.id;
 	}
 
-	pj_strdup2(inv->dlg->pool, &local->origin.user, "Asterisk");
-
-	/* XXX Hardcoded origin net stuff for now. Replace with transport
-	 * stuff later
-	 */
-	pj_strdup2(inv->dlg->pool, &local->origin.net_type, "IN");
-	pj_strdup2(inv->dlg->pool, &local->origin.addr_type, "IP4");
+	local->origin.user = STR_ASTERISK;
+	local->origin.net_type = STR_IN;
+	local->origin.addr_type = session->endpoint->rtp_ipv6 ? STR_IP6 : STR_IP4;
 	local->origin.addr = *pj_gethostname();
 	local->name = local->origin.user;
 
@@ -1243,6 +1278,7 @@
 
 struct media_update_data {
 	struct ast_sip_session *session;
+	pjsip_inv_session *inv_session;
 	pj_status_t status;
 };
 
@@ -1252,7 +1288,7 @@
 	ao2_cleanup(mud->session);
 }
 
-static struct media_update_data *media_update_data_alloc(struct ast_sip_session *session, pj_status_t status)
+static struct media_update_data *media_update_data_alloc(struct ast_sip_session *session, pjsip_inv_session *inv_session, pj_status_t status)
 {
 	struct media_update_data *mud = ao2_alloc(sizeof(*mud), media_update_data_destroy);
 	if (!mud) {
@@ -1260,6 +1296,7 @@
 	}
 	ao2_ref(session, +1);
 	mud->session = session;
+	mud->inv_session = inv_session;
 	mud->status = status;
 	return mud;
 }
@@ -1268,13 +1305,19 @@
 {
 	struct media_update_data *mud = data;
 	struct ast_sip_session *session = mud->session;
-	struct pjsip_inv_session *inv = session->inv_session;
-	const pjmedia_sdp_session *remote;
-
-	/* TODO: Split up handling of incoming and actual applying */
-	if (pjmedia_sdp_neg_get_active_remote(inv->neg, &remote) == PJ_SUCCESS) {
-		handle_incoming_sdp(session, remote);
-	}
+	struct pjsip_inv_session *inv = mud->inv_session;
+	const pjmedia_sdp_session *local, *remote;
+
+	if ((mud->status != PJ_SUCCESS) || (pjmedia_sdp_neg_get_active_local(inv->neg, &local) != PJ_SUCCESS) ||
+		(pjmedia_sdp_neg_get_active_remote(inv->neg, &remote) != PJ_SUCCESS)) {
+		if (session->channel) {
+			ast_channel_hangupcause_set(session->channel, AST_CAUSE_BEARERCAPABILITY_NOTAVAIL);
+			ast_queue_hangup(session->channel);
+		}
+		return 0;
+	}
+
+	handle_negotiated_sdp(session, local, remote);
 	ao2_ref(mud, -1);
 	return 0;
 }
@@ -1282,7 +1325,7 @@
 static void session_inv_on_media_update(pjsip_inv_session *inv, pj_status_t status)
 {
 	struct ast_sip_session *session = inv->mod_data[session_module.id];
-	struct media_update_data *mud = media_update_data_alloc(session, status);
+	struct media_update_data *mud = media_update_data_alloc(session, inv, status);
 	if (!mud) {
 		return;
 	}
@@ -1303,7 +1346,6 @@
 	.on_new_session = session_inv_on_new_session,
 	.on_tsx_state_changed = session_inv_on_tsx_state_changed,
 	.on_rx_offer = session_inv_on_rx_offer,
-//	.on_create_offer = session_inv_on_create_offer,
 	.on_media_update = session_inv_on_media_update,
 	.on_redirected = session_inv_on_redirected,
 };




More information about the svn-commits mailing list