[asterisk-commits] mmichelson: branch group/pimp_my_sip r383720 - in /team/group/pimp_my_sip: ch...

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


Author: mmichelson
Date: Mon Mar 25 10:48:25 2013
New Revision: 383720

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=383720
Log:
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.


Modified:
    team/group/pimp_my_sip/channels/chan_gulp.c
    team/group/pimp_my_sip/include/asterisk/autoconfig.h.in
    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
    team/group/pimp_my_sip/res/res_sip_session.exports.in

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=383720&r1=383719&r2=383720
==============================================================================
--- team/group/pimp_my_sip/channels/chan_gulp.c (original)
+++ team/group/pimp_my_sip/channels/chan_gulp.c Mon Mar 25 10:48:25 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/group/pimp_my_sip/include/asterisk/autoconfig.h.in
URL: http://svnview.digium.com/svn/asterisk/team/group/pimp_my_sip/include/asterisk/autoconfig.h.in?view=diff&rev=383720&r1=383719&r2=383720
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/autoconfig.h.in (original)
+++ team/group/pimp_my_sip/include/asterisk/autoconfig.h.in Mon Mar 25 10:48:25 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/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=383720&r1=383719&r2=383720
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip.h Mon Mar 25 10:48:25 2013
@@ -227,6 +227,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,
 };
 
 /*!
@@ -288,6 +308,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/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=383720&r1=383719&r2=383720
==============================================================================
--- team/group/pimp_my_sip/include/asterisk/res_sip_session.h (original)
+++ team/group/pimp_my_sip/include/asterisk/res_sip_session.h Mon Mar 25 10:48:25 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/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=383720&r1=383719&r2=383720
==============================================================================
--- team/group/pimp_my_sip/res/res_sip/sip_configuration.c (original)
+++ team/group/pimp_my_sip/res/res_sip/sip_configuration.c Mon Mar 25 10:48:25 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/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=383720&r1=383719&r2=383720
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_sdp_audio.c (original)
+++ team/group/pimp_my_sip/res/res_sip_sdp_audio.c Mon Mar 25 10:48:25 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/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=383720&r1=383719&r2=383720
==============================================================================
--- team/group/pimp_my_sip/res/res_sip_session.c (original)
+++ team/group/pimp_my_sip/res/res_sip_session.c Mon Mar 25 10:48:25 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);
+}
+
+static void reschedule_reinvite(struct ast_sip_session *session, ast_sip_session_response_cb on_response, pjsip_tx_data *tdata)
+{
+	struct ast_sip_session_delayed_request *delay = delayed_request_alloc("INVITE",
+			NULL, on_response, tdata);
+	pjsip_inv_session *inv = session->inv_session;
+	struct reschedule_reinvite_data *rrd = reschedule_reinvite_data_alloc(session, delay);
+	pj_time_val tv;
+	
+	if (!rrd || !delay) {
+		return;
+	}
+
+	tv.sec = 0;
+	if (inv->role == PJSIP_ROLE_UAC) {
+		tv.msec = 2100 + ast_random() % 2000;
+	} else {
+		tv.msec = ast_random() % 2000;
+	}
+
+	pj_timer_entry_init(&session->rescheduled_reinvite, 0, rrd, resend_reinvite);
+
+	pjsip_endpt_schedule_timer(ast_sip_get_pjsip_endpoint(), &session->rescheduled_reinvite, &tv);
+}
+
+static void __print_debug_details(const char *function, pjsip_inv_session *inv, pjsip_transaction *tsx, pjsip_event *e)
+{
+	struct ast_sip_session *session;
+	ast_debug(5, "Function %s called on event %s\n", function, pjsip_event_str(e->type));
+	if (!inv) {
+		ast_debug(5, "Transaction %p does not belong to an inv_session?\n", tsx);
+		ast_debug(5, "The transaction state is %s\n", pjsip_tsx_state_str(tsx->state));
+		return;
+	}
+	session = inv->mod_data[session_module.id];
+	if (!session) {
+		ast_debug(5, "inv_session %p has no ast session\n", inv);
+	} else {
+		ast_debug(5, "The state change pertains to the session with %s\n",
+				ast_sorcery_object_get_id(session->endpoint));
+	}
+	if (inv->invite_tsx) {
+		ast_debug(5, "The inv session still has an invite_tsx (%p)\n", inv->invite_tsx);
+	} else {
+		ast_debug(5, "The inv session does NOT have an invite_tsx\n");
+	}
+	if (tsx) {
+		ast_debug(5, "The transaction involved in this state change is %p\n", tsx);

[... 124 lines stripped ...]



More information about the asterisk-commits mailing list