[asterisk-commits] qwell: branch qwell/fun_with_transports r383721 - in /team/qwell/fun_with_tra...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Mon Mar 25 10:50:40 CDT 2013


Author: qwell
Date: Mon Mar 25 10:50:36 2013
New Revision: 383721

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383721
Log:
Multiple revisions 383718,383720

........
  r383718 | file | 2013-03-25 10:44:54 -0500 (Mon, 25 Mar 2013) | 2 lines
  
  No longer needed.
........
  r383720 | mmichelson | 2013-03-25 10:48:25 -0500 (Mon, 25 Mar 2013) | 7 lines
  
  Add direct media support.
  
  Now when calls are established, if possible, reinvites will be
  sent to the endpoints so they will send their media directly to
  each other.
........

Merged revisions 383718,383720 from http://svn.asterisk.org/svn/asterisk/team/group/pimp_my_sip

Modified:
    team/qwell/fun_with_transports/   (props changed)
    team/qwell/fun_with_transports/channels/chan_gulp.c
    team/qwell/fun_with_transports/include/asterisk/autoconfig.h.in
    team/qwell/fun_with_transports/include/asterisk/res_sip.h
    team/qwell/fun_with_transports/include/asterisk/res_sip_session.h
    team/qwell/fun_with_transports/res/res_sip/config_domain_aliases.c
    team/qwell/fun_with_transports/res/res_sip/sip_configuration.c
    team/qwell/fun_with_transports/res/res_sip_sdp_audio.c
    team/qwell/fun_with_transports/res/res_sip_session.c
    team/qwell/fun_with_transports/res/res_sip_session.exports.in

Propchange: team/qwell/fun_with_transports/
------------------------------------------------------------------------------
--- svnmerge-integrated (original)
+++ svnmerge-integrated Mon Mar 25 10:50:36 2013
@@ -1,1 +1,1 @@
-/team/group/pimp_my_sip:1-383510,383523-383672,383707-383717 /trunk:1-383466
+/team/group/pimp_my_sip:1-383720 /trunk:1-383466

Modified: team/qwell/fun_with_transports/channels/chan_gulp.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/channels/chan_gulp.c?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/channels/chan_gulp.c (original)
+++ team/qwell/fun_with_transports/channels/chan_gulp.c Mon Mar 25 10:50:36 2013
@@ -146,6 +146,7 @@
 };
 
 /*! \brief SIP session interaction functions */
+static void gulp_session_begin(struct ast_sip_session *session);
 static void gulp_session_end(struct ast_sip_session *session);
 static int gulp_incoming_request(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
 static void gulp_incoming_response(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
@@ -153,9 +154,17 @@
 /*! \brief SIP session supplement structure */
 static struct ast_sip_session_supplement gulp_supplement = {
 	.method = "INVITE",
+	.session_begin = gulp_session_begin,
 	.session_end = gulp_session_end,
 	.incoming_request = gulp_incoming_request,
 	.incoming_response = gulp_incoming_response,
+};
+
+static int gulp_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_data *rdata);
+
+static struct ast_sip_session_supplement gulp_ack_supplement = {
+	.method = "ACK",
+	.incoming_request = gulp_incoming_ack,
 };
 
 /*! \brief Dialplan function for constructing a dial string for calling all contacts */
@@ -240,26 +249,135 @@
 static enum ast_rtp_glue_result gulp_get_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance **instance)
 {
 	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+	struct ast_sip_endpoint *endpoint;
 
 	if (!pvt || !pvt->session || !pvt->media[SIP_MEDIA_AUDIO]->rtp) {
 		return AST_RTP_GLUE_RESULT_FORBID;
 	}
 
+	endpoint = pvt->session->endpoint;
+
 	*instance = pvt->media[SIP_MEDIA_AUDIO]->rtp;
 	ao2_ref(*instance, +1);
 
+	ast_assert(endpoint != NULL);
+	if (endpoint->direct_media) {
+		return AST_RTP_GLUE_RESULT_REMOTE;
+	}
+
 	return AST_RTP_GLUE_RESULT_LOCAL;
 }
 
 /*! \brief Function called by RTP engine to get peer capabilities */
 static void gulp_get_codec(struct ast_channel *chan, struct ast_format_cap *result)
 {
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+	ast_format_cap_copy(result, pvt->session->endpoint->codecs);
+}
+
+static int send_direct_media_request(void *data)
+{
+	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
+	return ast_sip_session_refresh(session, NULL, NULL, session->endpoint->direct_media_method, 1);
+}
+
+static struct ast_datastore_info direct_media_mitigation_info = { };
+
+static int direct_media_mitigate_glare(struct ast_sip_session *session)
+{
+	RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
+
+	if (session->endpoint->direct_media_glare_mitigation == 
+			AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE) {
+		return 0;
+	}
+
+	datastore = ast_sip_session_get_datastore(session, "direct_media_mitigation");
+	if (!datastore) {
+		return 0;
+	}
+
+	/* Removing the datastore ensures we won't try to mitigate glare on subsequent reinvites */
+	ast_sip_session_remove_datastore(session, "direct_media_mitigation");
+
+	if ((session->endpoint->direct_media_glare_mitigation ==
+			AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING &&
+			session->inv_session->role == PJSIP_ROLE_UAC) ||
+			(session->endpoint->direct_media_glare_mitigation ==
+			AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING &&
+			session->inv_session->role == PJSIP_ROLE_UAS)) {
+		return 1;
+	}
+
+	return 0;
+}
+
+static int check_for_rtp_changes(struct ast_channel *chan, struct ast_rtp_instance *rtp,
+		struct ast_sip_session_media *media, int rtcp_fd)
+{
+	int changed = 0;
+
+	if (rtp) {
+		changed = ast_rtp_instance_get_and_cmp_remote_address(rtp, &media->direct_media_addr);
+		if (media->rtp) {
+			ast_channel_set_fd(chan, rtcp_fd, -1);
+			ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_RTCP, 0);
+		}
+	} else if (!ast_sockaddr_isnull(&media->direct_media_addr)){
+		ast_sockaddr_setnull(&media->direct_media_addr);
+		changed = 1;
+		if (media->rtp) {
+			ast_rtp_instance_set_prop(media->rtp, AST_RTP_PROPERTY_RTCP, 1);
+			ast_channel_set_fd(chan, rtcp_fd, ast_rtp_instance_fd(media->rtp, 1));
+		}
+	}
+
+	return changed;
 }
 
 /*! \brief Function called by RTP engine to change where the remote party should send media */
-static int gulp_set_rtp_peer(struct ast_channel *chan, struct ast_rtp_instance *rtp, struct ast_rtp_instance *vrtp, struct ast_rtp_instance *tpeer, const struct ast_format_cap *cap, int nat_active)
-{
-	return -1;
+static int gulp_set_rtp_peer(struct ast_channel *chan,
+		struct ast_rtp_instance *rtp,
+		struct ast_rtp_instance *vrtp,
+		struct ast_rtp_instance *tpeer,
+		const struct ast_format_cap *cap,
+		int nat_active)
+{
+	struct gulp_pvt *pvt = ast_channel_tech_pvt(chan);
+	struct ast_sip_session *session = pvt->session;
+	int changed = 0;
+
+	/* Don't try to do any direct media shenanigans on early bridges */
+	if ((rtp || vrtp || tpeer) && !ast_bridged_channel(chan)) {
+		return 0;
+	}
+
+	if (nat_active && session->endpoint->disable_direct_media_on_nat) {
+		return 0;
+	}
+
+	if (pvt->media[SIP_MEDIA_AUDIO]) {
+		changed |= check_for_rtp_changes(chan, rtp, pvt->media[SIP_MEDIA_AUDIO], 1);
+	}
+	if (pvt->media[SIP_MEDIA_VIDEO]) {
+		changed |= check_for_rtp_changes(chan, vrtp, pvt->media[SIP_MEDIA_VIDEO], 3);
+	}
+
+	if (direct_media_mitigate_glare(session)) {
+		return 0;
+	}
+
+	if (cap && !ast_format_cap_is_empty(cap) && !ast_format_cap_identical(session->direct_media_cap, cap)) {
+		ast_format_cap_copy(session->direct_media_cap, cap);
+		changed = 1;
+	}
+
+	if (changed) {
+		ao2_ref(session, +1);
+		ast_sip_push_task(session->serializer, send_direct_media_request, session);
+	}
+
+	return 0;
 }
 
 /*! \brief Local glue for interacting with the RTP engine core */
@@ -981,6 +1099,25 @@
 	return 0;
 }
 
+static void gulp_session_begin(struct ast_sip_session *session)
+{
+	RAII_VAR(struct ast_datastore *, datastore, NULL, ao2_cleanup);
+
+	if (session->endpoint->direct_media_glare_mitigation ==
+			AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE) {
+		return;
+	}
+
+	datastore = ast_sip_session_alloc_datastore(&direct_media_mitigation_info,
+			"direct_media_glare_mitigation");
+
+	if (!datastore) {
+		return;
+	}
+
+	ast_sip_session_add_datastore(session, datastore);
+}
+
 /*! \brief Function called when the session ends */
 static void gulp_session_end(struct ast_sip_session *session)
 {
@@ -1003,7 +1140,6 @@
 	pjsip_tx_data *packet = NULL;
 	int res = AST_PBX_FAILED;
 
-	/* We only care about new sessions */
 	if (session->channel) {
 		return 0;
 	}
@@ -1066,6 +1202,16 @@
 	default:
 		break;
 	}
+}
+
+static int gulp_incoming_ack(struct ast_sip_session *session, struct pjsip_rx_data *rdata)
+{
+	if (rdata->msg_info.msg->line.req.method.id == PJSIP_ACK_METHOD) {
+		if (session->endpoint->direct_media) {
+			ast_queue_control(session->channel, AST_CONTROL_SRCCHANGE);
+		}
+	}
+	return 0;
 }
 
 /*!
@@ -1103,6 +1249,12 @@
 		goto end;
 	}
 
+	if (ast_sip_session_register_supplement(&gulp_ack_supplement)) {
+		ast_log(LOG_ERROR, "Unable to register Gulp ACK supplement\n");
+		ast_sip_session_unregister_supplement(&gulp_supplement);
+		goto end;
+	}
+
 	return 0;
 
 end:

Modified: team/qwell/fun_with_transports/include/asterisk/autoconfig.h.in
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/include/asterisk/autoconfig.h.in?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/include/asterisk/autoconfig.h.in (original)
+++ team/qwell/fun_with_transports/include/asterisk/autoconfig.h.in Mon Mar 25 10:50:36 2013
@@ -294,7 +294,7 @@
 /* Define if your system has the GLOB_NOMAGIC headers. */
 #undef HAVE_GLOB_NOMAGIC
 
-/* Define if your system has the GMIME libraries. */
+/* Define to 1 if you have the GMime library. */
 #undef HAVE_GMIME
 
 /* Define to indicate the GSM library */
@@ -306,7 +306,7 @@
 /* Define to indicate that gsm.h has no prefix for its location */
 #undef HAVE_GSM_HEADER
 
-/* Define if your system has the GTK2 libraries. */
+/* Define to 1 if you have the gtk2 library. */
 #undef HAVE_GTK2
 
 /* Define to 1 if you have the Hoard Memory Allocator library. */
@@ -324,7 +324,7 @@
 /* Define to 1 if you have the Iksemel Jabber library. */
 #undef HAVE_IKSEMEL
 
-/* Define if your system has the ILBC libraries. */
+/* Define to 1 if you have the System iLBC library. */
 #undef HAVE_ILBC
 
 /* Define if your system has the UW IMAP Toolkit c-client library. */
@@ -376,7 +376,7 @@
 /* Define to 1 if you have the OpenLDAP library. */
 #undef HAVE_LDAP
 
-/* Define if your system has the LIBEDIT libraries. */
+/* Define to 1 if you have the NetBSD Editline library library. */
 #undef HAVE_LIBEDIT
 
 /* Define to 1 if you have the <libintl.h> header file. */
@@ -551,7 +551,7 @@
 /* Define to indicate presence of the pg_encoding_to_char API. */
 #undef HAVE_PGSQL_pg_encoding_to_char
 
-/* Define if your system has the PJPROJECT libraries. */
+/* Define to 1 if you have the PJPROJECT library. */
 #undef HAVE_PJPROJECT
 
 /* Define to 1 if your system defines IP_PKTINFO. */
@@ -854,19 +854,19 @@
 /* Define to 1 if you have the `strtoq' function. */
 #undef HAVE_STRTOQ
 
-/* Define to 1 if `ifr_ifru.ifru_hwaddr' is a member of `struct ifreq'. */
+/* Define to 1 if `ifr_ifru.ifru_hwaddr' is member of `struct ifreq'. */
 #undef HAVE_STRUCT_IFREQ_IFR_IFRU_IFRU_HWADDR
 
-/* Define to 1 if `uid' is a member of `struct sockpeercred'. */
+/* Define to 1 if `uid' is member of `struct sockpeercred'. */
 #undef HAVE_STRUCT_SOCKPEERCRED_UID
 
-/* Define to 1 if `st_blksize' is a member of `struct stat'. */
+/* Define to 1 if `st_blksize' is member of `struct stat'. */
 #undef HAVE_STRUCT_STAT_ST_BLKSIZE
 
-/* Define to 1 if `cr_uid' is a member of `struct ucred'. */
+/* Define to 1 if `cr_uid' is member of `struct ucred'. */
 #undef HAVE_STRUCT_UCRED_CR_UID
 
-/* Define to 1 if `uid' is a member of `struct ucred'. */
+/* Define to 1 if `uid' is member of `struct ucred'. */
 #undef HAVE_STRUCT_UCRED_UID
 
 /* Define to 1 if you have the mISDN Supplemental Services library. */
@@ -1144,11 +1144,11 @@
 /* Define to the one symbol short name of this package. */
 #undef PACKAGE_TARNAME
 
-/* Define to the home page for this package. */
-#undef PACKAGE_URL
-
 /* Define to the version of this package. */
 #undef PACKAGE_VERSION
+
+/* Define to 1 if the C compiler supports function prototypes. */
+#undef PROTOTYPES
 
 /* Define to necessary symbol if this constant uses a non-standard name on
    your system. */
@@ -1168,6 +1168,11 @@
 
 /* Define to the type of arg 5 for `select'. */
 #undef SELECT_TYPE_ARG5
+
+/* Define to 1 if the `setvbuf' function takes the buffering type as its
+   second argument and the buffer pointer as the third, as on System V before
+   release 3. */
+#undef SETVBUF_REVERSED
 
 /* The size of `char *', as computed by sizeof. */
 #undef SIZEOF_CHAR_P
@@ -1204,49 +1209,53 @@
 /* Define to a type of the same size as fd_set.fds_bits[[0]] */
 #undef TYPEOF_FD_SET_FDS_BITS
 
-/* Enable extensions on AIX 3, Interix.  */
+/* Define to 1 if on AIX 3.
+   System headers sometimes define this.
+   We just want to avoid a redefinition error message.  */
 #ifndef _ALL_SOURCE
 # undef _ALL_SOURCE
 #endif
+
+/* Define to 1 if running on Darwin. */
+#undef _DARWIN_UNLIMITED_SELECT
+
+/* Number of bits in a file offset, on hosts where this is settable. */
+#undef _FILE_OFFSET_BITS
+
 /* Enable GNU extensions on systems that have them.  */
 #ifndef _GNU_SOURCE
 # undef _GNU_SOURCE
 #endif
-/* Enable threading extensions on Solaris.  */
+
+/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
+#undef _LARGEFILE_SOURCE
+
+/* Define for large files, on AIX-style hosts. */
+#undef _LARGE_FILES
+
+/* Define to 1 if on MINIX. */
+#undef _MINIX
+
+/* Define to 2 if the system does not provide POSIX.1 features except with
+   this defined. */
+#undef _POSIX_1_SOURCE
+
+/* Define to 1 if you need to in order for `stat' and other things to work. */
+#undef _POSIX_SOURCE
+
+/* Enable extensions on Solaris.  */
+#ifndef __EXTENSIONS__
+# undef __EXTENSIONS__
+#endif
 #ifndef _POSIX_PTHREAD_SEMANTICS
 # undef _POSIX_PTHREAD_SEMANTICS
 #endif
-/* Enable extensions on HP NonStop.  */
 #ifndef _TANDEM_SOURCE
 # undef _TANDEM_SOURCE
 #endif
-/* Enable general extensions on Solaris.  */
-#ifndef __EXTENSIONS__
-# undef __EXTENSIONS__
-#endif
-
-
-/* Define to 1 if running on Darwin. */
-#undef _DARWIN_UNLIMITED_SELECT
-
-/* Number of bits in a file offset, on hosts where this is settable. */
-#undef _FILE_OFFSET_BITS
-
-/* Define to 1 to make fseeko visible on some hosts (e.g. glibc 2.2). */
-#undef _LARGEFILE_SOURCE
-
-/* Define for large files, on AIX-style hosts. */
-#undef _LARGE_FILES
-
-/* Define to 1 if on MINIX. */
-#undef _MINIX
-
-/* Define to 2 if the system does not provide POSIX.1 features except with
-   this defined. */
-#undef _POSIX_1_SOURCE
-
-/* Define to 1 if you need to in order for `stat' and other things to work. */
-#undef _POSIX_SOURCE
+
+/* Define like PROTOTYPES; this can be used by system headers. */
+#undef __PROTOTYPES
 
 /* Define to empty if `const' does not conform to ANSI C. */
 #undef const

Modified: team/qwell/fun_with_transports/include/asterisk/res_sip.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/include/asterisk/res_sip.h?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/include/asterisk/res_sip.h (original)
+++ team/qwell/fun_with_transports/include/asterisk/res_sip.h Mon Mar 25 10:50:36 2013
@@ -228,6 +228,26 @@
 	AST_SIP_ENDPOINT_IDENTIFY_BY_USERNAME = (1 << 0),
 	/*! Identify based on source location of the SIP message */
 	AST_SIP_ENDPOINT_IDENTIFY_BY_LOCATION = (1 << 1),
+};
+
+enum ast_sip_session_refresh_method {
+	/*! Use reinvite to negotiate direct media */
+	AST_SIP_SESSION_REFRESH_METHOD_INVITE,
+	/*! Use UPDATE to negotiate direct media */
+	AST_SIP_SESSION_REFRESH_METHOD_UPDATE,
+};
+
+enum ast_sip_direct_media_glare_mitigation {
+	/*! Take no special action to mitigate reinvite glare */
+	AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE,
+	/*! Do not send an initial direct media session refresh on outgoing call legs
+	 * Subsequent session refreshes will be sent no matter the session direction
+	 */
+	AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING,
+	/*! Do not send an initial direct media session refresh on incoming call legs
+	 * Subsequent session refreshes will be sent no matter the session direction
+	 */
+	AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING,
 };
 
 /*!
@@ -289,6 +309,14 @@
 	unsigned int qualify_frequency;
 	/*! Method(s) by which the endpoint should be identified. */
 	enum ast_sip_endpoint_identifier_type ident_method;
+	/*! Boolean indicating if direct_media is permissible */
+	unsigned int direct_media;
+	/*! When using direct media, which method should be used */
+	enum ast_sip_session_refresh_method direct_media_method;
+	/*! Take steps to mitigate glare for direct media */
+	enum ast_sip_direct_media_glare_mitigation direct_media_glare_mitigation;
+	/*! Do not attempt direct media session refreshes if a media NAT is detected */
+	unsigned int disable_direct_media_on_nat;
 };
 
 /*!

Modified: team/qwell/fun_with_transports/include/asterisk/res_sip_session.h
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/include/asterisk/res_sip_session.h?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/include/asterisk/res_sip_session.h (original)
+++ team/qwell/fun_with_transports/include/asterisk/res_sip_session.h Mon Mar 25 10:50:36 2013
@@ -19,9 +19,13 @@
 #ifndef _RES_SIP_SESSION_H
 #define _RES_SIP_SESSION_H
 
+/* Needed for pj_timer_entry definition */
+#include "pjlib.h"
 #include "asterisk/linkedlists.h"
 /* Needed for AST_MAX_EXTENSION constant */
 #include "asterisk/channel.h"
+/* Needed for ast_sockaddr struct */
+#include "asterisk/netsock.h"
 
 /* Forward declarations */
 struct ast_sip_endpoint;
@@ -46,6 +50,8 @@
 struct ast_sip_session_media {
 	/*! \brief RTP instance itself */
 	struct ast_rtp_instance *rtp;
+	/*! \brief Direct media address */
+	struct ast_sockaddr direct_media_addr;
 	/*! \brief SDP handler that setup the RTP */
 	struct ast_sip_session_sdp_handler *handler;
 	/*! \brief Stream is on hold */
@@ -53,6 +59,12 @@
 	/*! \brief Stream type this session media handles */
 	char stream_type[1];
 };
+
+/*!
+ * \brief Opaque structure representing a request that could not be sent
+ * due to an outstanding INVITE transaction
+ */
+struct ast_sip_session_delayed_request;
 
 /*!
  * \brief A structure describing a SIP session
@@ -79,7 +91,16 @@
 	struct ao2_container *media;
 	/* Serializer for tasks relating to this SIP session */
 	struct ast_taskprocessor *serializer;
+	/* Requests that could not be sent due to current inv_session state */
+	AST_LIST_HEAD_NOLOCK(, ast_sip_session_delayed_request) delayed_requests;
+	/* When we need to reschedule a reinvite, we use this structure to do it */
+	pj_timer_entry rescheduled_reinvite;
+	/* Format capabilities pertaining to direct media */
+	struct ast_format_cap *direct_media_cap;
 };
+
+typedef int (*ast_sip_session_request_creation_cb)(struct ast_sip_session *session, pjsip_tx_data *tdata);
+typedef int (*ast_sip_session_response_cb)(struct ast_sip_session *session, pjsip_rx_data *rdata);
 
 /*!
  * \brief A supplement to SIP message processing
@@ -365,18 +386,29 @@
 int ast_sip_session_get_identity(struct pjsip_rx_data *rdata, struct ast_party_id *id);
 
 /*!
- * \brief Send a reinvite on a session
+ * \brief Send a reinvite or UPDATE on a session
  *
  * This method will inspect the session in order to construct an appropriate
- * reinvite. As with any outgoing request in res_sip_session, this will
- * call into registered supplements in case they wish to add anything.
+ * session refresh request. As with any outgoing request in res_sip_session,
+ * this will call into registered supplements in case they wish to add anything.
+ *
+ * Note: The on_request_creation callback may or may not be called in the same
+ * thread where this function is called. Request creation may need to be delayed
+ * due to the current INVITE transaction state.
  * 
  * \param session The session on which the reinvite will be sent
- * \param response_cb Optional callback that can be called when the reinvite response is received. The callback is identical in nature to the incoming_response() callback for session supplements.
- * \retval 0 Successfully sent reinvite
- * \retval -1 Failure to send reinvite
- */
-int ast_sip_session_send_reinvite(struct ast_sip_session *session, int (*response_cb)(struct ast_sip_session *session, struct pjsip_rx_data *rdata));
+ * \param on_request_creation Callback called when request is created
+ * \param on_response Callback called when response for request is received
+ * \param method The method that should be used when constructing the session refresh
+ * \param generate_new_sdp Boolean to indicate if a new SDP should be created
+ * \retval 0 Successfully sent refresh
+ * \retval -1 Failure to send refresh
+ */
+int ast_sip_session_refresh(struct ast_sip_session *session,
+		ast_sip_session_request_creation_cb on_request_creation,
+		ast_sip_session_response_cb on_response,
+		enum ast_sip_session_refresh_method method,
+		int generate_new_sdp);
 
 /*!
  * \brief Send a SIP response
@@ -400,4 +432,18 @@
  */
 void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data *tdata);
 
+/*!
+ * \brief Send a SIP request and get called back when a response is received
+ *
+ * This will send the request out exactly the same as ast_sip_send_request() does.
+ * The difference is that when a response arrives, the specified callback will be
+ * called into
+ *
+ * \param session The session on which to send the request
+ * \param tdata The request to send
+ * \param on_response Callback to be called when a response is received
+ */
+void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip_tx_data *tdata,
+		ast_sip_session_response_cb on_response);
+
 #endif /* _RES_SIP_SESSION_H */

Modified: team/qwell/fun_with_transports/res/res_sip/config_domain_aliases.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip/config_domain_aliases.c?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip/config_domain_aliases.c (original)
+++ team/qwell/fun_with_transports/res/res_sip/config_domain_aliases.c Mon Mar 25 10:50:36 2013
@@ -18,8 +18,6 @@
 
 #include "asterisk.h"
 
-#undef bzero
-#define bzero bzero
 #include "pjsip.h"
 #include "pjlib.h"
 #include "asterisk/res_sip.h"

Modified: team/qwell/fun_with_transports/res/res_sip/sip_configuration.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip/sip_configuration.c?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip/sip_configuration.c (original)
+++ team/qwell/fun_with_transports/res/res_sip/sip_configuration.c Mon Mar 25 10:50:36 2013
@@ -173,6 +173,41 @@
 			return -1;
 		}
 	}
+	return 0;
+}
+
+static int direct_media_method_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct ast_sip_endpoint *endpoint = obj;
+
+	if (!strcasecmp(var->value, "invite") || !strcasecmp(var->value, "reinvite")) {
+		endpoint->direct_media_method = AST_SIP_SESSION_REFRESH_METHOD_INVITE;
+	} else if (!strcasecmp(var->value, "update")) {
+		endpoint->direct_media_method = AST_SIP_SESSION_REFRESH_METHOD_UPDATE;
+	} else {
+		ast_log(LOG_NOTICE, "Unrecognized option value %s for %s on endpoint %s\n",
+				var->value, var->name, ast_sorcery_object_get_id(endpoint));
+		return -1;
+	}
+	return 0;
+}
+
+static int direct_media_glare_mitigation_handler(const struct aco_option *opt, struct ast_variable *var, void *obj)
+{
+	struct ast_sip_endpoint *endpoint = obj;
+
+	if (!strcasecmp(var->value, "none")) {
+		endpoint->direct_media_glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_NONE;
+	} else if (!strcasecmp(var->value, "outgoing")) {
+		endpoint->direct_media_glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_OUTGOING;
+	} else if (!strcasecmp(var->value, "incoming")) {
+		endpoint->direct_media_glare_mitigation = AST_SIP_DIRECT_MEDIA_GLARE_MITIGATION_INCOMING;
+	} else {
+		ast_log(LOG_NOTICE, "Unrecognized option value %s for %s on endpoint %s\n",
+				var->value, var->name, ast_sorcery_object_get_id(endpoint));
+		return -1;
+	}
+
 	return 0;
 }
 
@@ -237,6 +272,10 @@
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "auth", "", auth_handler, NULL, 0, 0);
 	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "external_media_address", "", OPT_STRINGFIELD_T, 0, STRFLDSET(struct ast_sip_endpoint, external_media_address));
 	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "identify_by", "username,location", ident_handler, NULL, 0, 0);
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "direct_media", "yes", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, direct_media));
+	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_method", "invite", direct_media_method_handler, NULL, 0, 0);
+	ast_sorcery_object_field_register_custom(sip_sorcery, "endpoint", "direct_media_glare_mitigation", "none", direct_media_glare_mitigation_handler, NULL, 0, 0);
+	ast_sorcery_object_field_register(sip_sorcery, "endpoint", "disable_direct_media_on_nat", "no", OPT_BOOL_T, 1, FLDSET(struct ast_sip_endpoint, disable_direct_media_on_nat));
 
 	if (ast_sip_initialize_sorcery_transport(sip_sorcery)) {
 		ast_log(LOG_ERROR, "Failed to register SIP transport support with sorcery\n");

Modified: team/qwell/fun_with_transports/res/res_sip_sdp_audio.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip_sdp_audio.c?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip_sdp_audio.c (original)
+++ team/qwell/fun_with_transports/res/res_sip_sdp_audio.c Mon Mar 25 10:50:36 2013
@@ -206,6 +206,15 @@
 	pj_str_t stmp;
 	pjmedia_sdp_attr *attr;
 	int index = 0, min_packet_size = 0, noncodec = (session->endpoint->dtmf == AST_SIP_DTMF_RFC_4733) ? AST_RTP_DTMF : 0;
+	RAII_VAR(struct ast_format_cap *, cap, ast_format_cap_alloc_nolock(), ast_format_cap_destroy);
+	int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
+		!ast_format_cap_is_empty(session->direct_media_cap);
+
+	if (direct_media_enabled) {
+		ast_format_cap_joint_copy(session->endpoint->codecs, session->direct_media_cap, cap);
+	} else {
+		ast_format_cap_copy(cap, session->endpoint->codecs);
+	}
 
 	if (!ast_format_cap_has_type(session->endpoint->codecs, AST_FORMAT_TYPE_AUDIO)) {
 		/* If no audio formats are configured don't add a stream */
@@ -224,7 +233,9 @@
 	media->desc.transport = STR_RTP_AVP;
 
 	/* Add connection level details */
-	if (ast_strlen_zero(session->endpoint->external_media_address)) {
+	if (direct_media_enabled) {
+		ast_copy_string(hostip, ast_sockaddr_stringify_fmt(&session_media->direct_media_addr, AST_SOCKADDR_STR_ADDR), sizeof(hostip));
+	} else if (ast_strlen_zero(session->endpoint->external_media_address)) {
 		pj_sockaddr localaddr;
 
 		if (pj_gethostip(session->endpoint->rtp_ipv6 ? pj_AF_INET6() : pj_AF_INET(), &localaddr)) {
@@ -239,7 +250,7 @@
 	media->conn->addr_type = session->endpoint->rtp_ipv6 ? STR_IP6 : STR_IP4;
 	pj_strdup2(pool, &media->conn->addr, hostip);
 	ast_rtp_instance_get_local_address(session_media->rtp, &addr);
-	media->desc.port = (pj_uint16_t) ast_sockaddr_port(&addr);
+	media->desc.port = direct_media_enabled ? ast_sockaddr_port(&session_media->direct_media_addr) : (pj_uint16_t) ast_sockaddr_port(&addr);
 	media->desc.port_count = 1;
 
 	/* Add ICE attributes and candidates */
@@ -248,6 +259,7 @@
 	/* Add formats */
 	for (index = 0; (index < AST_CODEC_PREF_SIZE); index++) {
 		struct ast_format format;
+		struct ast_format compat_format;
 		int rtp_code;
 		pjmedia_sdp_rtpmap rtpmap;
 		struct ast_codec_pref *pref = &ast_rtp_instance_get_codecs(session_media->rtp)->pref;
@@ -256,8 +268,10 @@
 			break;
 		} else if (AST_FORMAT_GET_TYPE(format.id) != AST_FORMAT_TYPE_AUDIO) {
 			continue;
+		} else if (!ast_format_cap_get_compatible_format(cap, &format, &compat_format)) {
+			continue;
 		} else if ((rtp_code = ast_rtp_codecs_payload_code(ast_rtp_instance_get_codecs(session_media->rtp),
-								   1, &format, 0)) == -1) {
+								   1, &compat_format, 0)) == -1) {
 			return -1;
 		}
 
@@ -415,6 +429,8 @@
 	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;
+	int direct_media_enabled = !ast_sockaddr_isnull(&session_media->direct_media_addr) &&
+		!ast_format_cap_is_empty(session->direct_media_cap);
 
 	if (!session->channel) {
 		return 1;
@@ -491,7 +507,11 @@
 	}
 
 	/* 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);
+	if (direct_media_enabled) {
+		ast_format_cap_joint_copy(session->endpoint->codecs, session->direct_media_cap, cap);
+	} else {
+		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))) {

Modified: team/qwell/fun_with_transports/res/res_sip_session.c
URL: http://svnview.digium.com/svn/asterisk/team/qwell/fun_with_transports/res/res_sip_session.c?view=diff&rev=383721&r1=383720&r2=383721
==============================================================================
--- team/qwell/fun_with_transports/res/res_sip_session.c (original)
+++ team/qwell/fun_with_transports/res/res_sip_session.c Mon Mar 25 10:50:36 2013
@@ -28,6 +28,7 @@
 #include <pjsip_ua.h>
 #include <pjlib.h>
 
+#include "asterisk/res_sip.h"
 #include "asterisk/res_sip_session.h"
 #include "asterisk/datastore.h"
 #include "asterisk/module.h"
@@ -569,22 +570,168 @@
 	return 0;
 }
 
-int ast_sip_session_send_reinvite(struct ast_sip_session *session, int (*response_cb)(struct ast_sip_session *session, struct pjsip_rx_data *rdata))
-{
-	/* XXX STUB */
+/*!
+ * \brief Structure used for sending delayed requests
+ *
+ * Requests are typically delayed because the current transaction
+ * state of an INVITE. Once the pending INVITE transaction terminates,
+ * the delayed request will be sent
+ */
+struct ast_sip_session_delayed_request {
+	/*! Method of the request */
+	char method[15];
+	/*! Callback to call when the delayed request is created. */
+	ast_sip_session_request_creation_cb on_request_creation;
+	/*! Callback to call when the delayed request receives a response */
+	ast_sip_session_response_cb on_response;
+	/*! Request to send */
+	pjsip_tx_data *tdata;
+	AST_LIST_ENTRY(ast_sip_session_delayed_request) next;
+};
+
+static struct ast_sip_session_delayed_request *delayed_request_alloc(const char *method,
+		ast_sip_session_request_creation_cb on_request_creation,
+		ast_sip_session_response_cb on_response,
+		pjsip_tx_data *tdata)
+{
+	struct ast_sip_session_delayed_request *delay = ast_calloc(1, sizeof(*delay));
+	if (!delay) {
+		return NULL;
+	}
+	ast_copy_string(delay->method, method, sizeof(delay->method));
+	delay->on_request_creation = on_request_creation;
+	delay->on_response = on_response;
+	delay->tdata = tdata;
+	return delay;
+}
+
+static int send_delayed_request(struct ast_sip_session *session, struct ast_sip_session_delayed_request *delay)
+{
+	ast_debug(3, "Sending delayed %s request to %s\n", delay->method, ast_sorcery_object_get_id(session->endpoint));
+
+	if (delay->tdata) {
+		ast_sip_session_send_request_with_cb(session, delay->tdata, delay->on_response);
+		return 0;
+	}
+
+	if (!strcmp(delay->method, "INVITE")) {
+		ast_sip_session_refresh(session, delay->on_request_creation,
+				delay->on_response, AST_SIP_SESSION_REFRESH_METHOD_INVITE, 1);
+	} else if (!strcmp(delay->method, "UPDATE")) {
+		ast_sip_session_refresh(session, delay->on_request_creation,
+				delay->on_response, AST_SIP_SESSION_REFRESH_METHOD_UPDATE, 1);
+	} else {
+		ast_log(LOG_WARNING, "Unexpected delayed %s request with no existing request structure\n", delay->method);
+		return -1;
+	}
 	return 0;
 }
 
+static int queued_delayed_request_send(void *data)
+{
+	RAII_VAR(struct ast_sip_session *, session, data, ao2_cleanup);
+	RAII_VAR(struct ast_sip_session_delayed_request *, delay, NULL, ast_free_ptr);
+
+	delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next);
+	if (!delay) {
+		return 0;
+	}
+
+	return send_delayed_request(session, delay);
+}
+
+static void queue_delayed_request(struct ast_sip_session *session)
+{
+	if (AST_LIST_EMPTY(&session->delayed_requests)) {
+		/* No delayed request to send, so just return */
+		return;
+	}
+
+	ast_debug(3, "Queuing delayed request to run for %s\n",
+			ast_sorcery_object_get_id(session->endpoint));
+
+	ao2_ref(session, +1);
+	ast_sip_push_task(session->serializer, queued_delayed_request_send, session);
+}
+
+static int delay_request(struct ast_sip_session *session, ast_sip_session_request_creation_cb on_request,
+		ast_sip_session_response_cb on_response, const char *method, pjsip_tx_data *tdata)
+{
+	struct ast_sip_session_delayed_request *delay = delayed_request_alloc(method,
+			on_request, on_response, tdata);
+
+	if (!delay) {
+		return -1;
+	}
+
+	AST_LIST_INSERT_TAIL(&session->delayed_requests, delay, next);
+	return 0;
+}
+
+static pjmedia_sdp_session *generate_session_refresh_sdp(struct ast_sip_session *session)
+{
+	pjsip_inv_session *inv_session = session->inv_session;
+	const pjmedia_sdp_session *previous_sdp;
+
+	if (pjmedia_sdp_neg_was_answer_remote(inv_session->neg)) {
+		pjmedia_sdp_neg_get_active_remote(inv_session->neg, &previous_sdp);
+	} else {
+		pjmedia_sdp_neg_get_active_local(inv_session->neg, &previous_sdp);
+	}
+	return create_local_sdp(inv_session, session, previous_sdp);
+}
+
+int ast_sip_session_refresh(struct ast_sip_session *session,
+		ast_sip_session_request_creation_cb on_request_creation, ast_sip_session_response_cb on_response,
+		enum ast_sip_session_refresh_method method, int generate_new_sdp)
+{
+	pjsip_inv_session *inv_session = session->inv_session;
+	pjmedia_sdp_session *new_sdp = NULL;
+	pjsip_tx_data *tdata;
+
+	if (inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		/* Don't try to do anything with a hung-up call */
+		ast_debug(3, "Not sending reinvite to %s because of disconnected state...\n",
+				ast_sorcery_object_get_id(session->endpoint));
+		return 0;
+	}
+
+	if (inv_session->invite_tsx) {
+		/* We can't send a reinvite yet, so delay it */
+		ast_debug(3, "Delaying sending reinvite to %s because of outstanding transaction...\n",
+				ast_sorcery_object_get_id(session->endpoint));
+		return delay_request(session, on_request_creation, on_response, "INVITE", NULL);
+	}
+
+	if (generate_new_sdp) {
+		new_sdp = generate_session_refresh_sdp(session);
+		if (!new_sdp) {
+			ast_log(LOG_ERROR, "Failed to generate session refresh SDP. Not sending session refresh\n");
+			return -1;
+		}
+	}
+
+	if (method == AST_SIP_SESSION_REFRESH_METHOD_INVITE) {
+		if (pjsip_inv_reinvite(inv_session, NULL, new_sdp, &tdata)) {
+			ast_log(LOG_WARNING, "Failed to create reinvite properly.\n");
+			return -1;
+		}
+	} else if (pjsip_inv_update(inv_session, NULL, new_sdp, &tdata)) {
+		ast_log(LOG_WARNING, "Failed to create UPDATE properly.\n");
+		return -1;
+	}
+	if (on_request_creation) {
+		if (on_request_creation(session, tdata)) {
+			return -1;
+		}
+	}
+	ast_sip_session_send_request_with_cb(session, tdata, on_response);
+	return 0;
+}
+
 void ast_sip_session_send_response(struct ast_sip_session *session, pjsip_tx_data *tdata)
 {
 	handle_outgoing_response(session, tdata);
-	pjsip_inv_send_msg(session->inv_session, tdata);
-	return;
-}
-
-void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
-{
-	handle_outgoing_request(session, tdata);
 	pjsip_inv_send_msg(session->inv_session, tdata);
 	return;
 }
@@ -594,7 +741,6 @@
 static pj_status_t session_stop(void);
 static pj_status_t session_unload(void);
 static pj_bool_t session_on_rx_request(pjsip_rx_data *rdata);
-static void session_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event);
 
 static pjsip_module session_module = {
 	.name = {"Session Module", 14},
@@ -604,8 +750,28 @@
 	.start = session_start,
 	.stop = session_stop,
 	.on_rx_request = session_on_rx_request,
-	.on_tsx_state = session_on_tsx_state,
 };
+
+void ast_sip_session_send_request_with_cb(struct ast_sip_session *session, pjsip_tx_data *tdata,
+		ast_sip_session_response_cb on_response)
+{
+	pjsip_inv_session *inv_session = session->inv_session;
+
+	if (inv_session->state == PJSIP_INV_STATE_DISCONNECTED) {
+		/* Don't try to do anything with a hung-up call */
+		return;
+	}
+
+	tdata->mod_data[session_module.id] = on_response;
+	handle_outgoing_request(session, tdata);
+	pjsip_inv_send_msg(session->inv_session, tdata);
+	return;
+}
+
+void ast_sip_session_send_request(struct ast_sip_session *session, pjsip_tx_data *tdata)
+{
+	ast_sip_session_send_request_with_cb(session, tdata, NULL);
+}
 
 /*!
  * \brief Called when the PJSIP core loads us
@@ -685,8 +851,11 @@
 {
 	struct ast_sip_session *session = obj;
 	struct ast_sip_session_supplement *supplement;
-
-	ast_debug(3, "Destroying SIP session\n");
+	struct ast_sip_session_delayed_request *delay;
+
+	ast_debug(3, "Destroying SIP session with endpoint %s\n",
+			ast_sorcery_object_get_id(session->endpoint));
+
 	while ((supplement = AST_LIST_REMOVE_HEAD(&session->supplements, next))) {
 		if (supplement->session_destroy) {
 			supplement->session_destroy(session);
@@ -698,6 +867,9 @@
 	ao2_cleanup(session->datastores);
 	ao2_cleanup(session->media);
 	AST_LIST_HEAD_DESTROY(&session->supplements);
+	while ((delay = AST_LIST_REMOVE_HEAD(&session->delayed_requests, next))) {
+		ast_free(delay);
+	}
 	ao2_cleanup(session->endpoint);
 }
 
@@ -769,6 +941,8 @@
 			iter->session_begin(session);
 		}
 	}
+	session->direct_media_cap = ast_format_cap_alloc_nolock();
+	AST_LIST_HEAD_INIT_NOLOCK(&session->delayed_requests);
 	ao2_ref(session, +1);
 	return session;
 }
@@ -1061,10 +1235,101 @@
 	return handled;
 }
 
-static void session_on_tsx_state(pjsip_transaction *tsx, pjsip_event *event)
-{
-	/* XXX STUB */
-}
+struct reschedule_reinvite_data {
+	struct ast_sip_session *session;
+	struct ast_sip_session_delayed_request *delay;
+};
+
+static struct reschedule_reinvite_data *reschedule_reinvite_data_alloc(
+		struct ast_sip_session *session, struct ast_sip_session_delayed_request *delay)
+{
+	struct reschedule_reinvite_data *rrd = ast_malloc(sizeof(*rrd));
+	if (!rrd) {
+		return NULL;
+	}
+	ao2_ref(session, +1);
+	rrd->session = session;
+	rrd->delay = delay;
+	return rrd;
+}
+
+static void reschedule_reinvite_data_destroy(struct reschedule_reinvite_data *rrd)
+{
+	ao2_cleanup(rrd->session);
+	ast_free(rrd->delay);
+	ast_free(rrd);
+}
+
+static int really_resend_reinvite(void *data)
+{
+	RAII_VAR(struct reschedule_reinvite_data *, rrd, data, reschedule_reinvite_data_destroy);
+
+	return send_delayed_request(rrd->session, rrd->delay);
+}
+
+static void resend_reinvite(pj_timer_heap_t *timer, pj_timer_entry *entry)
+{
+	struct reschedule_reinvite_data *rrd = entry->user_data;
+
+	ast_sip_push_task(rrd->session->serializer, really_resend_reinvite, entry->user_data);
+}
+

[... 171 lines stripped ...]



More information about the asterisk-commits mailing list