[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