[asterisk-commits] kmoore: branch group/pimp_my_sip r383186 - in /team/group/pimp_my_sip: channe...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Fri Mar 15 08:21:19 CDT 2013


Author: kmoore
Date: Fri Mar 15 08:21:15 2013
New Revision: 383186

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383186
Log:
Make media type handling in res_sip_session more generic

Abstract media type restrictions out of res_sip_session and move them
to chan_gulp where they're actually needed. Due to the change, the
SDP handler callbacks have been modified to accept a
ast_sip_session_media struct and had a destroy function added and
ast_sip_session_media_position has been removed from res_sip_session.h

Review: https://reviewboard.asterisk.org/r/2380/
(closes issue ASTERISK-21184)
Patch-by: Kinsey Moore

Modified:
    team/group/pimp_my_sip/channels/chan_gulp.c
    team/group/pimp_my_sip/include/asterisk/res_sip_session.h
    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=383186&r1=383185&r2=383186
==============================================================================
--- team/group/pimp_my_sip/channels/chan_gulp.c (original)
+++ team/group/pimp_my_sip/channels/chan_gulp.c Fri Mar 15 08:21:15 2013
@@ -82,6 +82,35 @@
 static const char desc[] = "Gulp SIP Channel";
 static const char channel_type[] = "Gulp";
 
+/*!
+ * \brief Positions of various media
+ */
+enum sip_session_media_position {
+	/*! \brief First is audio */
+	SIP_MEDIA_AUDIO = 0,
+	/*! \brief Second is video */
+	SIP_MEDIA_VIDEO,
+	/*! \brief Last is the size for media details */
+	SIP_MEDIA_SIZE,
+};
+
+struct gulp_pvt {
+	struct ast_sip_session *session;
+	struct ast_sip_session_media *media[SIP_MEDIA_SIZE];
+};
+
+static void gulp_pvt_dtor(void *obj)
+{
+	struct gulp_pvt *pvt = obj;
+	int i;
+	ao2_cleanup(pvt->session);
+	pvt->session = NULL;
+	for (i = 0; i < SIP_MEDIA_SIZE; ++i) {
+		ao2_cleanup(pvt->media[i]);
+		pvt->media[i] = NULL;
+	}
+}
+
 /* \brief Asterisk core interaction functions */
 static struct ast_channel *gulp_request(const char *type, struct ast_format_cap *cap, const struct ast_channel *requestor, const char *data, int *cause);
 static int gulp_sendtext(struct ast_channel *ast, const char *text);
@@ -210,14 +239,14 @@
 /*! \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)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(chan);
-
-	if (!session || !session->media[AST_SIP_MEDIA_AUDIO].rtp) {
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+
+	if (!pvt || !pvt->session || !pvt->media[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;
+	*instance = pvt->media[SIP_MEDIA_AUDIO]->rtp;
+	ao2_ref(*instance, +1);
 
 	return AST_RTP_GLUE_RESULT_LOCAL;
 }
@@ -246,16 +275,28 @@
 {
 	struct ast_channel *chan;
 	struct ast_format fmt;
+	struct gulp_pvt *pvt;
+
+	if (!(pvt = ao2_alloc(sizeof(*pvt), gulp_pvt_dtor))) {
+		return NULL;
+	}
 
 	if (!(chan = ast_channel_alloc(1, state, "", S_OR(cid_name, ""), "", "", "", linkedid, 0, "Gulp/%s-%.*s", ast_sorcery_object_get_id(session->endpoint),
 		(int)session->inv_session->dlg->call_id->id.slen, session->inv_session->dlg->call_id->id.ptr))) {
+		ao2_cleanup(pvt);
 		return NULL;
 	}
 
 	ast_channel_tech_set(chan, &gulp_tech);
 
 	ao2_ref(session, +1);
-	ast_channel_tech_pvt_set(chan, session);
+	pvt->session = session;
+	/* If res_sip_session is ever updated to create/destroy ast_sip_session_media
+	 * during a call such as if multiple same-type stream support is introduced,
+	 * these will need to be recaptured as well */
+	pvt->media[SIP_MEDIA_AUDIO] = ao2_find(session->media, "audio", OBJ_KEY);
+	pvt->media[SIP_MEDIA_VIDEO] = ao2_find(session->media, "video", OBJ_KEY);
+	ast_channel_tech_pvt_set(chan, pvt);
 
 	ast_format_cap_copy(ast_channel_nativeformats(chan), session->endpoint->codecs);
 	ast_codec_choose(&session->endpoint->prefs, session->endpoint->codecs, 1, &fmt);
@@ -295,7 +336,8 @@
 /*! \brief Function called by core when we should answer a Gulp session */
 static int gulp_answer(struct ast_channel *ast)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session = pvt->session;
 
 	if (ast_channel_state(ast) == AST_STATE_UP) {
 		return 0;
@@ -315,15 +357,20 @@
 /*! \brief Function called by core to read any waiting frames */
 static struct ast_frame *gulp_read(struct ast_channel *ast)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
 	struct ast_frame *f;
+	struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO];
+
+	if (!media) {
+		return &ast_null_frame;
+	}
 
 	switch (ast_channel_fdno(ast)) {
 	case 0:
-		f = ast_rtp_instance_read(session->media[AST_SIP_MEDIA_AUDIO].rtp, 0);
+		f = ast_rtp_instance_read(media->rtp, 0);
 		break;
 	case 1:
-		f = ast_rtp_instance_read(session->media[AST_SIP_MEDIA_AUDIO].rtp, 1);
+		f = ast_rtp_instance_read(media->rtp, 1);
 		break;
 	default:
 		f = &ast_null_frame;
@@ -344,11 +391,17 @@
 /*! \brief Function called by core to write frames */
 static int gulp_write(struct ast_channel *ast, struct ast_frame *frame)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
 	int res = 0;
+	struct ast_sip_session_media *media;
 
 	switch (frame->frametype) {
 	case AST_FRAME_VOICE:
+		media = pvt->media[SIP_MEDIA_AUDIO];
+
+		if (!media) {
+			return 0;
+		}
 		if (!(ast_format_cap_iscompatible(ast_channel_nativeformats(ast), &frame->subclass.format))) {
 			char buf[256];
 
@@ -360,8 +413,8 @@
 				ast_getformatname(ast_channel_writeformat(ast)));
 			return 0;
 		}
-		if (session->media[AST_SIP_MEDIA_AUDIO].rtp) {
-			res = ast_rtp_instance_write(session->media[AST_SIP_MEDIA_AUDIO].rtp, frame);
+		if (media->rtp) {
+			res = ast_rtp_instance_write(media->rtp, frame);
 		}
 		break;
 	default:
@@ -387,7 +440,8 @@
 /*! \brief Function called by core to change the underlying owner channel */
 static int gulp_fixup(struct ast_channel *oldchan, struct ast_channel *newchan)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(newchan);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(newchan);
+	struct ast_sip_session *session = pvt->session;
 	struct fixup_data fix_data;
 	fix_data.session = session;
 	fix_data.chan = newchan;
@@ -459,7 +513,8 @@
 static int gulp_indicate(struct ast_channel *ast, int condition, const void *data, size_t datalen)
 {
 	int res = 0;
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session = pvt->session;
 	int response_code = 0;
 
 	switch (condition) {
@@ -548,14 +603,18 @@
 /*! \brief Function called by core to start a DTMF digit */
 static int gulp_digit_begin(struct ast_channel *chan, char digit)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(chan);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+	struct ast_sip_session *session = pvt->session;
 	int res = 0;
+	struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO];
 
 	switch (session->endpoint->dtmf) {
 	case AST_SIP_DTMF_RFC_4733:
-		if (session->media[AST_SIP_MEDIA_AUDIO].rtp) {
-			ast_rtp_instance_dtmf_begin(session->media[AST_SIP_MEDIA_AUDIO].rtp, digit);
-		}
+		if (!media || !media->rtp) {
+			return -1;
+		}
+
+		ast_rtp_instance_dtmf_begin(media->rtp, digit);
 	case AST_SIP_DTMF_NONE:
 		break;
 	case AST_SIP_DTMF_INBAND:
@@ -571,17 +630,21 @@
 /*! \brief Function called by core to stop a DTMF digit */
 static int gulp_digit_end(struct ast_channel *ast, char digit, unsigned int duration)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session = pvt->session;
 	int res = 0;
+	struct ast_sip_session_media *media = pvt->media[SIP_MEDIA_AUDIO];
 
 	switch (session->endpoint->dtmf) {
 	case AST_SIP_DTMF_INFO:
 		/* TODO: Send INFO dtmf here */
 		break;
 	case AST_SIP_DTMF_RFC_4733:
-		if (session->media[AST_SIP_MEDIA_AUDIO].rtp) {
-			ast_rtp_instance_dtmf_end_with_duration(session->media[AST_SIP_MEDIA_AUDIO].rtp, digit, duration);
-		}
+		if (!media || !media->rtp) {
+			return -1;
+		}
+
+		ast_rtp_instance_dtmf_end_with_duration(media->rtp, digit, duration);
 	case AST_SIP_DTMF_NONE:
 		break;
 	case AST_SIP_DTMF_INBAND:
@@ -610,7 +673,8 @@
 /*! \brief Function called by core to actually start calling a remote party */
 static int gulp_call(struct ast_channel *ast, const char *dest, int timeout)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session = pvt->session;
 
 	ao2_ref(session, +1);
 	if (ast_sip_push_task_synchronous(session->serializer, call, session)) {
@@ -697,7 +761,8 @@
 	pjsip_tx_data *packet = NULL;
 	struct hangup_data *h_data = data;
 	struct ast_channel *ast = h_data->chan;
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session = pvt->session;
 	int cause = h_data->cause;
 
 	if (((status = pjsip_inv_end_session(session->inv_session, cause ? cause : 603, NULL, &packet)) == PJ_SUCCESS) && packet) {
@@ -711,7 +776,7 @@
 	session->channel = NULL;
 	ast_channel_tech_pvt_set(ast, NULL);
 
-	ao2_cleanup(session);
+	ao2_cleanup(pvt);
 	ao2_cleanup(h_data);
 	return 0;
 }
@@ -719,7 +784,8 @@
 /*! \brief Function called by core to hang up a Gulp session */
 static int gulp_hangup(struct ast_channel *ast)
 {
-	struct ast_sip_session *session = ast_channel_tech_pvt(ast);
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(ast);
+	struct ast_sip_session *session = pvt->session;
 	int cause = hangup_cause2sip(ast_channel_hangupcause(session->channel));
 	struct hangup_data *h_data = hangup_data_alloc(cause, ast);
 	if (!h_data) {
@@ -740,7 +806,7 @@
 	session->channel = NULL;
 	ast_channel_tech_pvt_set(ast, NULL);
 
-	ao2_cleanup(session);
+	ao2_cleanup(pvt);
 	return -1;
 }
 

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=383186&r1=383185&r2=383186
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip_session.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip_session.h Fri Mar 15 08:21:15 2013
@@ -37,17 +37,7 @@
 struct pjmedia_sdp_session;
 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,
-};
+struct ast_sip_session_sdp_handler;
 
 /*!
  * \brief A structure containing SIP session media information
@@ -55,8 +45,12 @@
 struct ast_sip_session_media {
 	/*! \brief RTP instance itself */
 	struct ast_rtp_instance *rtp;
+	/*! \brief SDP handler that setup the RTP */
+	struct ast_sip_session_sdp_handler *handler;
 	/*! \brief Stream is on hold */
 	unsigned int held:1;
+	/*! \brief Stream type this session media handles */
+	char stream_type[1];
 };
 
 /*!
@@ -81,7 +75,7 @@
 	/* Datastores added to the session by supplements to the session */
 	struct ao2_container *datastores;
 	/* Media streams */
-	struct ast_sip_session_media media[AST_SIP_MEDIA_SIZE];
+	struct ao2_container *media;
 	/* Serializer for tasks relating to this SIP session */
 	struct ast_taskprocessor *serializer;
 };
@@ -166,34 +160,38 @@
 	/*!
 	 * \brief Set session details based on a stream in an incoming SDP offer or answer
 	 * \param session The session for which the media is being negotiated
+	 * \param session_media The media to be setup for this session
 	 * \param sdp The entire SDP. Useful for getting "global" information, such as connections or attributes
 	 * \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 (*negotiate_incoming_sdp_stream)(struct ast_sip_session *session, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream);
+	int (*negotiate_incoming_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, 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 session_media The media to be setup for this session
 	 * \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);
+	int (*handle_incoming_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, 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
 	 * \param session The session for which media is being added
+	 * \param session_media The media to be setup for this session
 	 * \param sdp The entire SDP as currently built
 	 * \retval 0 This handler has no stream to add. 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 SDP negotiation will be abandoned.
 	 * \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);
+	int (*create_outgoing_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp);
 	/*!
 	 * \brief Apply a negotiated SDP media stream
 	 * \param session The session for which media is being applied
+	 * \param session_media The media to be setup for this session
 	 * \param local The entire local negotiated SDP
 	 * \param local_stream The local stream which to apply
 	 * \param remote The entire remote negotiated SDP
@@ -202,9 +200,15 @@
 	 * \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,
+	int (*apply_negotiated_sdp_stream)(struct ast_sip_session *session, struct ast_sip_session_media *session_media, 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. */
+	/*!
+	 * \brief Destroy a session_media created by this handler
+	 * \param session The session for which media is being destroyed
+	 * \param session_media The media to destroy
+	 */
+	void (*stream_destroy)(struct ast_sip_session_media *session_media);
+	/*! Next item in the list. */
 	AST_LIST_ENTRY(ast_sip_session_sdp_handler) next;
 };
 

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=383186&r1=383185&r2=383186
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_sdp_audio.c (original)
+++ team/group/pimp_my_sip/res/res_sip_sdp_audio.c Fri Mar 15 08:21:15 2013
@@ -53,13 +53,13 @@
 static struct ast_sched_context *sched;
 
 /*! \brief Forward declarations for SDP handler functions */
-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,
+static int audio_negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, 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 ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp);
+static int audio_apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, 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);
+static void audio_stream_destroy(struct ast_sip_session_media *session_media);
 
 /*! \brief SDP handler for 'audio' media stream */
 static struct ast_sip_session_sdp_handler audio_sdp_handler = {
@@ -67,15 +67,11 @@
 	.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,
+	.stream_destroy = audio_stream_destroy,
 };
 
-/*! \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, unsigned int ipv6)
+static int audio_create_rtp(struct ast_sip_session *session, struct ast_sip_session_media *session_media, unsigned int ipv6)
 {
 	pj_sockaddr addr;
 	char hostip[PJ_INET6_ADDRSTRLEN+2];
@@ -91,21 +87,21 @@
 		return -1;
 	}
 
-	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);
+	if (!(session_media->rtp = ast_rtp_instance_new("asterisk", sched, &tmp, NULL))) {
+		return -1;
+	}
+
+	ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_RTCP, 1);
+	ast_rtp_instance_set_prop(session_media->rtp, AST_RTP_PROPERTY_NAT, session->endpoint->rtp_symmetric);
+
+	ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session_media->rtp),
+					 session_media->rtp, &session->endpoint->prefs);
 
 	return 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)
+static int audio_negotiate_incoming_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, const struct pjmedia_sdp_session *sdp, const struct pjmedia_sdp_media *stream)
 {
 	char host[NI_MAXHOST];
 	RAII_VAR(struct ast_sockaddr *, addrs, NULL, ast_free_ptr);
@@ -124,7 +120,7 @@
 	}
 
 	/* 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))) {
+	if (!session_media->rtp && audio_create_rtp(session, session_media, ast_sockaddr_is_ipv6(addrs))) {
 		return -1;
 	}
 
@@ -133,7 +129,7 @@
 }
 
 /*! \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 int audio_create_outgoing_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, struct pjmedia_sdp_session *sdp)
 {
 	pj_pool_t *pool = session->inv_session->pool_active;
 	static const pj_str_t STR_AUDIO = { "audio", 5};
@@ -152,7 +148,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[AST_SIP_MEDIA_AUDIO].rtp && audio_create_rtp(session, session->endpoint->rtp_ipv6)) {
+	} else if (!session_media->rtp && audio_create_rtp(session, session_media, session->endpoint->rtp_ipv6)) {
 		return -1;
 	}
 
@@ -166,7 +162,7 @@
 	media->desc.transport = STR_RTP_AVP;
 
 	/* Add connection level details */
-	ast_rtp_instance_get_local_address(session->media[AST_SIP_MEDIA_AUDIO].rtp, &addr);
+	ast_rtp_instance_get_local_address(session_media->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));
@@ -178,13 +174,13 @@
 		struct ast_format format;
 		int rtp_code;
 		pjmedia_sdp_rtpmap rtpmap;
-		struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp)->pref;
+		struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session_media->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[AST_SIP_MEDIA_AUDIO].rtp),
+		} else if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp),
 								   1, &format, 0)) == -1) {
 			return -1;
 		}
@@ -212,7 +208,7 @@
 		int rtp_code;
 		pjmedia_sdp_rtpmap rtpmap;
 
-		if (!(noncodec & index) || (rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session->media[AST_SIP_MEDIA_AUDIO].rtp),
+		if (!(noncodec & index) || (rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp),
 										   0, NULL, index)) == -1) {
 			continue;
 		}
@@ -253,7 +249,7 @@
 }
 
 /*! \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,
+static int audio_apply_negotiated_sdp_stream(struct ast_sip_session *session, struct ast_sip_session_media *session_media, 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;
@@ -271,7 +267,7 @@
 	}
 
 	/* Create an RTP instance if need be */
-	if (!session->media[AST_SIP_MEDIA_AUDIO].rtp && audio_create_rtp(session, session->endpoint->rtp_ipv6)) {
+	if (!session_media->rtp && audio_create_rtp(session, session_media, session->endpoint->rtp_ipv6)) {
 		return -1;
 	}
 
@@ -292,7 +288,7 @@
 
 	/* 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_instance_set_remote_address(session_media->rtp, addrs);
 
 	ast_rtp_codecs_payloads_initialize(&codecs);
 
@@ -323,11 +319,11 @@
 		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;
+		struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session_media->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);
+				session_media->rtp), codec);
 
 			if (!format.asterisk_format) {
 				continue;
@@ -336,8 +332,8 @@
 			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);
+		ast_rtp_codecs_packetization_set(ast_rtp_instance_get_codecs(session_media->rtp),
+			session_media->rtp, pref);
 	}
 
 	/* Using the configured codecs and the codecs in this SDP we determine the joint formats for *audio only* */
@@ -356,8 +352,8 @@
 		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);
+	ast_rtp_codecs_payloads_copy(&codecs, ast_rtp_instance_get_codecs(session_media->rtp),
+				     session_media->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));
@@ -372,25 +368,25 @@
 	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) ||
+	ast_channel_set_fd(session->channel, 0, ast_rtp_instance_fd(session_media->rtp, 0));
+	ast_channel_set_fd(session->channel, 1, ast_rtp_instance_fd(session_media->rtp, 1));
+
+	if (session_media->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;
+		session_media->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_rtp_instance_stop(session_media->rtp);
 		ast_queue_frame(session->channel, &ast_null_frame);
-		session->media[AST_SIP_MEDIA_AUDIO].held = 1;
+		session_media->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_instance_activate(session_media->rtp);
 	}
 
 	ast_rtp_codecs_payloads_destroy(&codecs);
@@ -398,14 +394,14 @@
 }
 
 /*! \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) {
+static void audio_stream_destroy(struct ast_sip_session_media *session_media)
+{
+	if (!session_media->rtp) {
 		return;
 	}
 
-	ast_rtp_instance_stop(session->media[AST_SIP_MEDIA_AUDIO].rtp);
-	ast_rtp_instance_destroy(session->media[AST_SIP_MEDIA_AUDIO].rtp);
+	ast_rtp_instance_stop(session_media->rtp);
+	ast_rtp_instance_destroy(session_media->rtp);
 }
 
 /*!
@@ -430,11 +426,6 @@
 		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;
@@ -446,8 +437,6 @@
 		ast_sched_context_destroy(sched);
 	}
 
-	ast_sip_session_unregister_supplement(&audio_session_supplement);
-
 	return AST_MODULE_LOAD_FAILURE;
 }
 
@@ -455,7 +444,6 @@
 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=383186&r1=383185&r2=383186
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_session.c (original)
+++ team/group/pimp_my_sip/res/res_sip_session.c Fri Mar 15 08:21:15 2013
@@ -88,6 +88,23 @@
 	return strcmp(handler_list1->stream_type, stream_type2) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
+static int session_media_hash(const void *obj, int flags)
+{
+	const struct ast_sip_session_media *session_media = obj;
+	const char *stream_type = flags & OBJ_KEY ? obj : session_media->stream_type;
+
+	return ast_str_hash(stream_type);
+}
+
+static int session_media_cmp(void *obj, void *arg, int flags)
+{
+	struct ast_sip_session_media *session_media1 = obj;
+	struct ast_sip_session_media *session_media2 = arg;
+	const char *stream_type2 = flags & OBJ_KEY ? arg : session_media2->stream_type;
+
+	return strcmp(session_media1->stream_type, stream_type2) ? 0 : CMP_MATCH | CMP_STOP;
+}
+
 int ast_sip_session_register_sdp_handler(struct ast_sip_session_sdp_handler *handler, const char *stream_type)
 {
 	RAII_VAR(struct sdp_handler_list *, handler_list,
@@ -155,9 +172,160 @@
 	ao2_callback_data(sdp_handlers, OBJ_KEY | OBJ_UNLINK | OBJ_NODATA, remove_handler, (void *)stream_type, handler);
 }
 
+static int validate_port_hash(const void *obj, int flags)
+{
+	const int *port = obj;
+	return *port;
+}
+
+static int validate_port_cmp(void *obj, void *arg, int flags)
+{
+	int *port1 = obj;
+	int *port2 = arg;
+
+	return *port1 == *port2 ? CMP_MATCH | CMP_STOP : 0;
+}
+
+struct bundle_assoc {
+	int port;
+	char tag[1];
+};
+
+static int bundle_assoc_hash(const void *obj, int flags)
+{
+	const struct bundle_assoc *assoc = obj;
+	const char *tag = flags & OBJ_KEY ? obj : assoc->tag;
+
+	return ast_str_hash(tag);
+}
+
+static int bundle_assoc_cmp(void *obj, void *arg, int flags)
+{
+	struct bundle_assoc *assoc1 = obj;
+	struct bundle_assoc *assoc2 = arg;
+	const char *tag2 = flags & OBJ_KEY ? arg : assoc2->tag;
+
+	return strcmp(assoc1->tag, tag2) ? 0 : CMP_MATCH | CMP_STOP;
+}
+
+/* return must be ast_freed */
+static pjmedia_sdp_attr *media_get_mid(pjmedia_sdp_media *media)
+{
+	pjmedia_sdp_attr *attr = pjmedia_sdp_media_find_attr2(media, "mid", NULL);
+	if (!attr) {
+		return NULL;
+	}
+
+	return attr;
+}
+
+static int get_bundle_port(const pjmedia_sdp_session *sdp, const char *mid)
+{
+	int i;
+	for (i = 0; i < sdp->media_count; ++i) {
+		pjmedia_sdp_attr *mid_attr = media_get_mid(sdp->media[i]);
+		if (mid_attr && !pj_strcmp2(&mid_attr->value, mid)) {
+			return sdp->media[i]->desc.port;
+		}
+	}
+
+	return -1;
+}
+
+static int validate_incoming_sdp(const pjmedia_sdp_session *sdp)
+{
+	int i;
+	RAII_VAR(struct ao2_container *, portlist, ao2_container_alloc(5, validate_port_hash, validate_port_cmp), ao2_cleanup);
+	RAII_VAR(struct ao2_container *, bundle_assoc_list, ao2_container_alloc(5, bundle_assoc_hash, bundle_assoc_cmp), ao2_cleanup);
+
+	/* check for bundles (for websocket RTP multiplexing, there can be more than one) */
+	for (i = 0; i < sdp->attr_count; ++i) {
+		char *bundle_list;
+		int bundle_port = 0;
+		if (pj_stricmp2(&sdp->attr[i]->name, "group")) {
+			continue;
+		}
+
+		/* check to see if this group is a bundle */
+		if (7 >= sdp->attr[i]->value.slen || pj_strnicmp2(&sdp->attr[i]->value, "bundle ", 7)) {
+			continue;
+		}
+
+		bundle_list = ast_alloca(sdp->attr[i]->value.slen - 6);
+		strncpy(bundle_list, sdp->attr[i]->value.ptr + 7, sdp->attr[i]->value.slen - 7);
+		bundle_list[sdp->attr[i]->value.slen - 7] = '\0';
+		while (bundle_list) {
+			char *item;
+			RAII_VAR(struct bundle_assoc *, assoc, NULL, ao2_cleanup);
+			item = strsep(&bundle_list, " ,");
+			if (!bundle_port) {
+				RAII_VAR(int *, port, ao2_alloc(sizeof(int), NULL), ao2_cleanup);
+				RAII_VAR(int *, port_match, NULL, ao2_cleanup);
+				bundle_port = get_bundle_port(sdp, item);
+				if (bundle_port < 0) {
+					return -1;
+				}
+				port_match = ao2_find(portlist, &bundle_port, OBJ_KEY);
+				if (port_match) {
+					/* bundle port aready consumed by a different bundle */
+					return -1;
+				}
+				*port = bundle_port;
+				ao2_link(portlist, port);
+			}
+			assoc = ao2_alloc(sizeof(*assoc) + strlen(item), NULL);
+			if (!assoc) {
+				return -1;
+			}
+
+			/* safe use of strcpy */
+			strcpy(assoc->tag, item);
+			assoc->port = bundle_port;
+			ao2_link(bundle_assoc_list, assoc);
+		}
+	}
+
+	/* validate all streams */
+	for (i = 0; i < sdp->media_count; ++i) {
+		RAII_VAR(int *, port, ao2_alloc(sizeof(int), NULL), ao2_cleanup);
+		RAII_VAR(int *, port_match, NULL, ao2_cleanup);
+		RAII_VAR(int *, bundle_match, NULL, ao2_cleanup);
+		*port = sdp->media[i]->desc.port;
+		port_match = ao2_find(portlist, port, OBJ_KEY);
+		if (port_match) {
+			RAII_VAR(struct bundle_assoc *, assoc, NULL, ao2_cleanup);
+			pjmedia_sdp_attr *mid = media_get_mid(sdp->media[i]);
+			char *mid_val;
+
+			if (!mid) {
+				/* not part of a bundle */
+				return -1;
+			}
+
+			mid_val = ast_alloca(mid->value.slen + 1);
+			strncpy(mid_val, mid->value.ptr, mid->value.slen);
+			mid_val[mid->value.slen] = '\0';
+
+			assoc = ao2_find(bundle_assoc_list, mid_val, OBJ_KEY);
+			if (!assoc || assoc->port != *port) {
+				/* This port already exists elsewhere in the SDP
+				 * and is not an appropriate bundle port, fail
+				 * catastrophically */
+				return -1;
+			}
+		}
+		ao2_link(portlist, port);
+	}
+	return 0;
+}
+
 static int handle_incoming_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *sdp)
 {
 	int i;
+	if (validate_incoming_sdp(sdp)) {
+		return -1;
+	}
+
 	for (i = 0; i < sdp->media_count; ++i) {
 		/* See if there are registered handlers for this media stream type */
 		char media[20];
@@ -173,15 +341,22 @@
 			continue;
 		}
 		AST_LIST_TRAVERSE(&handler_list->list, handler, next) {
-			int res = handler->negotiate_incoming_sdp_stream(session, sdp, sdp->media[i]);
+			int res;
+			RAII_VAR(struct ast_sip_session_media *, session_media, NULL, ao2_cleanup);
+			session_media = ao2_find(session->media, handler_list->stream_type, OBJ_KEY);
+			if (!session_media || session_media->handler) {
+				/* There is only one slot for this stream type and it has already been claimed
+				 * so it will go unhandled */
+				break;
+			}
+			res = handler->negotiate_incoming_sdp_stream(session, session_media, 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 {
+			}
+			if (res > 0) {
 				/* Handled by this handler. Move to the next stream */
+				session_media->handler = handler;
 				break;
 			}
 		}
@@ -189,9 +364,21 @@
 	return 0;
 }
 
-static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote)
-{
+struct handle_negotiated_sdp_cb {
+	struct ast_sip_session *session;
+	const pjmedia_sdp_session *local;
+	const pjmedia_sdp_session *remote;
+};
+
+static int handle_negotiated_sdp_session_media(void *obj, void *arg, int flags)
+{
+	struct ast_sip_session_media *session_media = obj;
+	struct handle_negotiated_sdp_cb *callback_data = arg;
+	struct ast_sip_session *session = callback_data->session;
+	const pjmedia_sdp_session *local = callback_data->local;
+	const pjmedia_sdp_session *remote = callback_data->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];
@@ -201,26 +388,56 @@
 		/* We need a null-terminated version of the media string */
 		ast_copy_pj_str(media, &local->media[i]->desc.media, sizeof(media));
 
+		/* stream type doesn't match the one we're looking to fill */
+		if (strcasecmp(session_media->stream_type, media)) {
+			continue;
+		}
+
+		handler = session_media->handler;
+		if (handler) {
+			int res = handler->apply_negotiated_sdp_stream(session, session_media, local, local->media[i], remote, remote->media[i]);
+			if (res >= 0) {
+				return CMP_MATCH;
+			}
+			return 0;
+		}
+
 		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]);
+			int res = handler->apply_negotiated_sdp_stream(session, session_media, local, local->media[i], remote, remote->media[i]);
 			if (res < 0) {
 				/* Catastrophic failure. Abort! */
-				return -1;
-			} else if (res == 0) {
-				/* Not handled yet. Move to the next handler */
-				continue;
-			} else {
+				return 0;
+			}
+			if (res > 0) {
 				/* Handled by this handler. Move to the next stream */
-				break;
+				session_media->handler = handler;
+				return CMP_MATCH;
 			}
 		}
 	}
-	return 0;
+	return CMP_MATCH;
+}
+
+static int handle_negotiated_sdp(struct ast_sip_session *session, const pjmedia_sdp_session *local, const pjmedia_sdp_session *remote)
+{
+	RAII_VAR(struct ao2_iterator *, successful, NULL, ao2_iterator_cleanup);
+	struct handle_negotiated_sdp_cb callback_data = {
+		.session = session,
+		.local = local,
+		.remote = remote,
+	};
+
+	successful = ao2_callback(session->media, OBJ_MULTIPLE, handle_negotiated_sdp_session_media, &callback_data);
+	if (successful && ao2_container_count(successful->c) == ao2_container_count(session->media)) {
+		/* Nothing experienced a catastrophic failure */
+		return 0;
+	}
+	return -1;
 }
 
 AST_RWLIST_HEAD_STATIC(session_supplements, ast_sip_session_supplement);
@@ -259,6 +476,7 @@
 }
 
 #define DATASTORE_BUCKETS 53
+#define MEDIA_BUCKETS 7
 
 static void session_datastore_destroy(void *obj)
 {
@@ -452,6 +670,14 @@
 	return strcmp(datastore1->uid, uid2) ? 0 : CMP_MATCH | CMP_STOP;
 }
 
+static void session_media_dtor(void *obj)
+{
+	struct ast_sip_session_media *session_media = obj;

[... 126 lines stripped ...]



More information about the asterisk-commits mailing list