[asterisk-commits] file: branch file/chan_jingle2 r365454 - in /team/file/chan_jingle2: channels...

SVN commits to the Asterisk project asterisk-commits at lists.digium.com
Sun May 6 09:42:25 CDT 2012


Author: file
Date: Sun May  6 09:42:21 2012
New Revision: 365454

URL: http://svnview.digium.com/svn/asterisk?view=rev&rev=365454
Log:
Import STUN/TURN/ICE support. This was developed standalone and is being reviewed as such. It is here simply because it is a dependency.

Modified:
    team/file/chan_jingle2/channels/chan_sip.c
    team/file/chan_jingle2/configs/rtp.conf.sample
    team/file/chan_jingle2/include/asterisk/rtp_engine.h
    team/file/chan_jingle2/main/rtp_engine.c
    team/file/chan_jingle2/res/Makefile
    team/file/chan_jingle2/res/res_rtp_asterisk.c

Modified: team/file/chan_jingle2/channels/chan_sip.c
URL: http://svnview.digium.com/svn/asterisk/team/file/chan_jingle2/channels/chan_sip.c?view=diff&rev=365454&r1=365453&r2=365454
==============================================================================
--- team/file/chan_jingle2/channels/chan_sip.c (original)
+++ team/file/chan_jingle2/channels/chan_sip.c Sun May  6 09:42:21 2012
@@ -1320,10 +1320,13 @@
 static int process_sdp_o(const char *o, struct sip_pvt *p);
 static int process_sdp_c(const char *c, struct ast_sockaddr *addr);
 static int process_sdp_a_sendonly(const char *a, int *sendonly);
+static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance);
 static int process_sdp_a_audio(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newaudiortp, int *last_rtpmap_codec);
 static int process_sdp_a_video(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newvideortp, int *last_rtpmap_codec);
 static int process_sdp_a_text(const char *a, struct sip_pvt *p, struct ast_rtp_codecs *newtextrtp, char *red_fmtp, int *red_num_gen, int *red_data_pt, int *last_rtpmap_codec);
 static int process_sdp_a_image(const char *a, struct sip_pvt *p);
+static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf, int debug);
+static void start_ice(struct ast_rtp_instance *instance, int debug);
 static void add_codec_to_sdp(const struct sip_pvt *p, struct ast_format *codec,
 			     struct ast_str **m_buf, struct ast_str **a_buf,
 			     int debug, int *min_packet_size);
@@ -9155,6 +9158,12 @@
 				processed = TRUE;
 			else if (process_sdp_a_image(value, p))
 				processed = TRUE;
+
+			/* We can't stop processing after each of these since it may apply to each */
+			process_sdp_a_ice(value, p, p->rtp);
+			process_sdp_a_ice(value, p, p->vrtp);
+			process_sdp_a_ice(value, p, p->trtp);
+
 			break;
 		}
 
@@ -9355,6 +9364,8 @@
 				if (audio) {
 					if (process_sdp_a_sendonly(value, &sendonly)) {
 						processed = TRUE;
+					} else if (process_sdp_a_ice(value, p, p->rtp)) {
+						processed = TRUE;
 					} else if (!processed_crypto && process_crypto(p, p->rtp, &p->srtp, value)) {
 						processed_crypto = TRUE;
 						processed = TRUE;
@@ -9365,6 +9376,8 @@
 				/* Video specific scanning */
 				else if (video) {
 					if (process_sdp_a_sendonly(value, &vsendonly)) {
+						processed = TRUE;
+					} else if (process_sdp_a_ice(value, p, p->vrtp)) {
 						processed = TRUE;
 					} else if (!processed_crypto && process_crypto(p, p->vrtp, &p->vsrtp, value)) {
 						processed_crypto = TRUE;
@@ -9377,6 +9390,8 @@
 				else if (text) {
 					if (process_sdp_a_text(value, p, &newtextrtp, red_fmtp, &red_num_gen, red_data_pt, &last_rtpmap_codec)) {
 						processed = TRUE;
+					} else if (process_sdp_a_ice(value, p, p->trtp)) {
+						processed = TRUE;
 					} else if (!processed_crypto && process_crypto(p, p->trtp, &p->tsrtp, value)) {
 						processed_crypto = TRUE;
 						processed = TRUE;
@@ -9505,6 +9520,7 @@
 	/* Setup audio address and port */
 	if (p->rtp) {
 		if (portno > 0) {
+			start_ice(p->rtp, debug);
 			ast_sockaddr_set_port(sa, portno);
 			ast_rtp_instance_set_remote_address(p->rtp, sa);
 			if (debug) {
@@ -9552,6 +9568,7 @@
 	/* Setup video address and port */
 	if (p->vrtp) {
 		if (vportno > 0) {
+			start_ice(p->vrtp, debug);
 			ast_sockaddr_set_port(vsa, vportno);
 			ast_rtp_instance_set_remote_address(p->vrtp, vsa);
 			if (debug) {
@@ -9569,6 +9586,7 @@
 	/* Setup text address and port */
 	if (p->trtp) {
 		if (tportno > 0) {
+			start_ice(p->trtp, debug);
 			ast_sockaddr_set_port(tsa, tportno);
 			ast_rtp_instance_set_remote_address(p->trtp, tsa);
 			if (debug) {
@@ -9826,6 +9844,61 @@
 			*sendonly = 0;
 		found = TRUE;
 	}
+	return found;
+}
+
+static int process_sdp_a_ice(const char *a, struct sip_pvt *p, struct ast_rtp_instance *instance)
+{
+	struct ast_rtp_engine_ice *ice;
+	int found = FALSE;
+	char ufrag[256], pwd[256], foundation[32], transport[4], address[24], cand_type[6], relay_address[24] = "";
+	struct ast_rtp_engine_ice_candidate candidate;
+	int port, relay_port = 0;
+
+	if (!instance || !(ice = ast_rtp_instance_get_ice(instance))) {
+		return found;
+	}
+
+	if (sscanf(a, "ice-ufrag: %255s", ufrag) == 1) {
+		ice->set_authentication(instance, ufrag, NULL);
+		found = TRUE;
+	} else if (sscanf(a, "ice-pwd: %255s", pwd) == 1) {
+		ice->set_authentication(instance, NULL, pwd);
+		found = TRUE;
+	} else if (sscanf(a, "candidate:%31s %30u %3s %30u %23s %30u typ %5s %*s %23s %*s %30u", foundation, &candidate.id, transport, &candidate.priority,
+			  address, &port, cand_type, relay_address, &relay_port) >= 7) {
+		candidate.foundation = foundation;
+		candidate.transport = transport;
+
+		ast_sockaddr_parse(&candidate.address, address, PARSE_PORT_FORBID);
+		ast_sockaddr_set_port(&candidate.address, port);
+
+		if (!strcasecmp(cand_type, "host")) {
+			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
+		} else if (!strcasecmp(cand_type, "srflx")) {
+			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
+		} else if (!strcasecmp(cand_type, "relay")) {
+			candidate.type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
+		} else {
+			return found;
+		}
+
+		if (!ast_strlen_zero(relay_address)) {
+			ast_sockaddr_parse(&candidate.relay_address, relay_address, PARSE_PORT_FORBID);
+		}
+
+		if (relay_port) {
+			ast_sockaddr_set_port(&candidate.relay_address, relay_port);
+		}
+
+		ice->add_remote_candidate(instance, &candidate);
+
+		found = TRUE;
+	} else if (!strcasecmp(a, "ice-lite")) {
+		ice->ice_lite(instance);
+		found = TRUE;
+	}
+
 	return found;
 }
 
@@ -11336,6 +11409,67 @@
 	return 0;
 }
 
+/*! \brief Add ICE attributes to SDP */
+static void add_ice_to_sdp(struct ast_rtp_instance *instance, struct ast_str **a_buf, int debug)
+{
+	struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(instance);
+	const char *username, *password;
+	struct ao2_container *candidates;
+	struct ao2_iterator i;
+	struct ast_rtp_engine_ice_candidate *candidate;
+
+	/* If no ICE support is present we can't very well add the attributes */
+	if (!ice || !(candidates = ice->get_local_candidates(instance))) {
+		return;
+	}
+
+	if ((username = ice->get_ufrag(instance))) {
+		ast_str_append(a_buf, 0, "a=ice-ufrag:%s\r\n", username);
+	}
+	if ((password = ice->get_password(instance))) {
+		ast_str_append(a_buf, 0, "a=ice-pwd:%s\r\n", password);
+	}
+
+	i = ao2_iterator_init(candidates, 0);
+
+	while ((candidate = ao2_iterator_next(&i))) {
+		ast_str_append(a_buf, 0, "a=candidate:%s %d %s %d ", candidate->foundation, candidate->id, candidate->transport, candidate->priority);
+		ast_str_append(a_buf, 0, "%s ", ast_sockaddr_stringify_host(&candidate->address));
+		ast_str_append(a_buf, 0, "%s typ ", ast_sockaddr_stringify_port(&candidate->address));
+
+		if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
+			ast_str_append(a_buf, 0, "host");
+		} else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
+			ast_str_append(a_buf, 0, "srflx");
+		} else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_RELAYED) {
+			ast_str_append(a_buf, 0, "relay");
+		}
+
+		if (!ast_sockaddr_isnull(&candidate->relay_address)) {
+			ast_str_append(a_buf, 0, " raddr %s ", ast_sockaddr_stringify_host(&candidate->relay_address));
+			ast_str_append(a_buf, 0, "rport %s", ast_sockaddr_stringify_port(&candidate->relay_address));
+		}
+
+		ast_str_append(a_buf, 0, "\r\n");
+	}
+
+	ao2_iterator_destroy(&i);
+
+	ao2_ref(candidates, -1);
+}
+
+/*! \brief Start ICE negotiation on an RTP instance */
+static void start_ice(struct ast_rtp_instance *instance, int debug)
+{
+	struct ast_rtp_engine_ice *ice = ast_rtp_instance_get_ice(instance);
+
+	if (!ice) {
+		return;
+	}
+
+	ice->start(instance);
+}
+
 /*! \brief Add codec offer to SDP offer/answer body in INVITE or 200 OK */
 static void add_codec_to_sdp(const struct sip_pvt *p,
 	struct ast_format *format,
@@ -11806,6 +11940,10 @@
 			if (debug) {
 				ast_verbose("Video is at %s\n", ast_sockaddr_stringify(&vdest));
 			}
+
+			if (!doing_directmedia) {
+				add_ice_to_sdp(p->vrtp, &a_video, debug);
+			}
 		}
 
 		/* Ok, we need text. Let's add what we need for text and set codecs.
@@ -11818,6 +11956,10 @@
 				t_a_crypto ? "SAVP" : "AVP");
 			if (debug) {  /* XXX should I use tdest below ? */
 				ast_verbose("Text is at %s\n", ast_sockaddr_stringify(&taddr));
+			}
+
+			if (!doing_directmedia) {
+				add_ice_to_sdp(p->trtp, &a_text, debug);
 			}
 		}
 
@@ -11912,6 +12054,10 @@
 		/* XXX don't think you can have ptime for text */
 		if (min_text_packet_size)
 			ast_str_append(&a_text, 0, "a=ptime:%d\r\n", min_text_packet_size);
+
+		if (!doing_directmedia) {
+			add_ice_to_sdp(p->rtp, &a_audio, debug);
+		}
 
 		if (m_audio->len - m_audio->used < 2 || m_video->len - m_video->used < 2 ||
 		    m_text->len - m_text->used < 2 || a_text->len - a_text->used < 2 ||

Modified: team/file/chan_jingle2/configs/rtp.conf.sample
URL: http://svnview.digium.com/svn/asterisk/team/file/chan_jingle2/configs/rtp.conf.sample?view=diff&rev=365454&r1=365453&r2=365454
==============================================================================
--- team/file/chan_jingle2/configs/rtp.conf.sample (original)
+++ team/file/chan_jingle2/configs/rtp.conf.sample Sun May  6 09:42:21 2012
@@ -32,3 +32,23 @@
 ; if rtp packets are dropped from one or both ends after a call is
 ; connected. This option is set to 4 by default.
 ; probation=8
+;
+; Whether to enable or disable ICE support. This option is enabled by default.
+; icesupport=false
+;
+; Address to use for the STUN server when determining the external IP address and port
+; an RTP session can be reached at. This option is disabled by default.
+; stunaddr=
+;
+; Address to use for the TURN relay server when creating a TURN relay session. This option
+; is disabled by default.
+; turnaddr=
+;
+; Port used to contact the TURN relay server on. This option is set to 34780 by default.
+; turnport=34780
+;
+; Username used to authenticate with TURN relay server.
+; turnusername=
+;
+; Password used to authenticate with TURN relay server.
+; turnpassword=

Modified: team/file/chan_jingle2/include/asterisk/rtp_engine.h
URL: http://svnview.digium.com/svn/asterisk/team/file/chan_jingle2/include/asterisk/rtp_engine.h?view=diff&rev=365454&r1=365453&r2=365454
==============================================================================
--- team/file/chan_jingle2/include/asterisk/rtp_engine.h (original)
+++ team/file/chan_jingle2/include/asterisk/rtp_engine.h Sun May  6 09:42:21 2012
@@ -309,6 +309,42 @@
 if (stat == combined) { \
 return 0; \
 }
+
+/*! \brief ICE candidate types */
+enum ast_rtp_ice_candidate_type {
+	AST_RTP_ICE_CANDIDATE_TYPE_HOST,    /*!< ICE host candidate. A host candidate represents the actual local transport address in the host. */
+	AST_RTP_ICE_CANDIDATE_TYPE_SRFLX,   /*!< ICE server reflexive candidate, which represents the public mapped address of the local address. */
+	AST_RTP_ICE_CANDIDATE_TYPE_RELAYED, /*!< ICE relayed candidate, which represents the address allocated in TURN server. */
+};
+
+/*! \brief Structure for an ICE candidate */
+struct ast_rtp_engine_ice_candidate {
+	char *foundation;                     /*!< Foundation identifier */
+	unsigned int id;                      /*!< Component identifier */
+	char *transport;                      /*!< Transport for the media */
+	int priority;                         /*!< Priority which is used if multiple candidates can be used */
+	struct ast_sockaddr address;          /*!< Address of the candidate */
+	struct ast_sockaddr relay_address;    /*!< Relay address for the candidate */
+	enum ast_rtp_ice_candidate_type type; /*!< Type of candidate */
+};
+
+/*! \brief Structure that represents the optional ICE support within an RTP engine */
+struct ast_rtp_engine_ice {
+	/*! Callback for setting received authentication information */
+	void (*set_authentication)(struct ast_rtp_instance *instance, const char *ufrag, const char *password);
+	/*! Callback for adding a remote candidate */
+	void (*add_remote_candidate)(struct ast_rtp_instance *instance, const struct ast_rtp_engine_ice_candidate *candidate);
+	/*! Callback for starting ICE negotiation */
+	void (*start)(struct ast_rtp_instance *instance);
+	/*! Callback for getting local username */
+	const char *(*get_ufrag)(struct ast_rtp_instance *instance);
+	/*! Callback for getting local password */
+	const char *(*get_password)(struct ast_rtp_instance *instance);
+	/*! Callback for getting local candidates */
+	struct ao2_container *(*get_local_candidates)(struct ast_rtp_instance *instance);
+	/*! Callback for telling the ICE support that it is talking to an ice-lite implementation */
+	void (*ice_lite)(struct ast_rtp_instance *instance);
+};
 
 /*! Structure that represents an RTP stack (engine) */
 struct ast_rtp_engine {
@@ -381,6 +417,8 @@
 	void (*available_formats)(struct ast_rtp_instance *instance, struct ast_format_cap *to_endpoint, struct ast_format_cap *to_asterisk, struct ast_format_cap *result);
 	/*! Callback to send CNG */
 	int (*sendcng)(struct ast_rtp_instance *instance, int level);
+	/*! Callback to pointer for optional ICE support */
+	struct ast_rtp_engine_ice *ice;
 	/*! Linked list information */
 	AST_RWLIST_ENTRY(ast_rtp_engine) entry;
 };
@@ -1891,6 +1929,16 @@
  * attribute interface is unloaded, this function must be called to notify the rtp_engine. */
 int ast_rtp_engine_unload_format(const struct ast_format *format);
 
+/*!
+ * \brief Obtain a pointer to the ICE support present on an RTP instance
+ *
+ * \param instance the RTP instance
+ *
+ * \retval ICE support if present
+ * \retval NULL if no ICE support available
+ */
+struct ast_rtp_engine_ice *ast_rtp_instance_get_ice(struct ast_rtp_instance *instance);
+
 #if defined(__cplusplus) || defined(c_plusplus)
 }
 #endif

Modified: team/file/chan_jingle2/main/rtp_engine.c
URL: http://svnview.digium.com/svn/asterisk/team/file/chan_jingle2/main/rtp_engine.c?view=diff&rev=365454&r1=365453&r2=365454
==============================================================================
--- team/file/chan_jingle2/main/rtp_engine.c (original)
+++ team/file/chan_jingle2/main/rtp_engine.c Sun May  6 09:42:21 2012
@@ -1886,6 +1886,11 @@
 	return -1;
 }
 
+struct ast_rtp_engine_ice *ast_rtp_instance_get_ice(struct ast_rtp_instance *instance)
+{
+	return instance->engine->ice;
+}
+
 static void set_next_mime_type(const struct ast_format *format, int rtp_code, char *type, char *subtype, unsigned int sample_rate)
 {
 	int x = mime_types_len;

Modified: team/file/chan_jingle2/res/Makefile
URL: http://svnview.digium.com/svn/asterisk/team/file/chan_jingle2/res/Makefile?view=diff&rev=365454&r1=365453&r2=365454
==============================================================================
--- team/file/chan_jingle2/res/Makefile (original)
+++ team/file/chan_jingle2/res/Makefile Sun May  6 09:42:21 2012
@@ -68,3 +68,17 @@
 
 clean::
 	rm -f snmp/*.o snmp/*.i ael/*.o ael/*.i ais/*.o ais/*.i
+	$(MAKE) -C pjproject realclean
+
+pjproject/build.mak:
+	cd pjproject && ./configure AR="" CFLAGS=-fPIC  --disable-floating-point --disable-sound --disable-oss --disable-small-filterng --disable-large-filterng --disable-speex-aec --disable-l16-codec --disable-gsm-codec --disable-g722-codec --disable-g7221-codec --disable-speex-codec --disable-ilbc-codec --disable-g711-codec
+
+include pjproject/build.mak
+
+res_rtp_asterisk.o: pjproject/build.mak $(PJ_LIB_FILES)
+res_rtp_asterisk.o: _ASTCFLAGS+=$(PJ_CFLAGS)
+res_rtp_asterisk.so: _ASTLDFLAGS+=$(PJ_LDFLAGS)
+res_rtp_asterisk.so: LIBS+=$(PJ_LDLIBS)
+
+$(PJ_LIB_FILES):
+	cd pjproject && make dep && make

Modified: team/file/chan_jingle2/res/res_rtp_asterisk.c
URL: http://svnview.digium.com/svn/asterisk/team/file/chan_jingle2/res/res_rtp_asterisk.c?view=diff&rev=365454&r1=365453&r2=365454
==============================================================================
--- team/file/chan_jingle2/res/res_rtp_asterisk.c (original)
+++ team/file/chan_jingle2/res/res_rtp_asterisk.c Sun May  6 09:42:21 2012
@@ -40,6 +40,12 @@
 #include <signal.h>
 #include <fcntl.h>
 
+#undef bzero
+#define bzero bzero
+#include "pjlib.h"
+#include "pjlib-util.h"
+#include "pjnath.h"
+
 #include "asterisk/stun.h"
 #include "asterisk/pbx.h"
 #include "asterisk/frame.h"
@@ -66,6 +72,10 @@
 
 #define MINIMUM_RTP_PORT 1024 /*!< Minimum port number to accept */
 #define MAXIMUM_RTP_PORT 65535 /*!< Maximum port number to accept */
+
+#define DEFAULT_TURN_PORT 34780
+
+#define TURN_ALLOCATION_WAIT_TIME 2000
 
 #define RTCP_PT_FUR     192
 #define RTCP_PT_SR      200
@@ -100,6 +110,30 @@
 #endif
 static int strictrtp;			/*< Only accept RTP frames from a defined source. If we receive an indication of a changing source, enter learning mode. */
 static int learning_min_sequential;	/*< Number of sequential RTP frames needed from a single source during learning mode to accept new source. */
+static int icesupport;
+static struct sockaddr_in stunaddr;
+static pj_str_t turnaddr;
+static int turnport;
+static pj_str_t turnusername;
+static pj_str_t turnpassword;
+
+/*! \brief Pool factory used by pjlib to allocate memory. */
+static pj_caching_pool cachingpool;
+
+/*! \brief Pool used by pjlib functions which require memory allocation. */
+static pj_pool_t *pool;
+
+/*! \brief I/O queue for TURN relay traffic */
+static pj_ioqueue_t *ioqueue;
+
+/*! \brief Timer heap for ICE and TURN stuff */
+static pj_timer_heap_t *timerheap;
+
+/*! \brief Worker thread for ICE/TURN */
+static pj_thread_t *thread;
+
+/*! \brief Notification that the ICE/TURN worker thread should stop */
+static int worker_terminate;
 
 enum strict_rtp_state {
 	STRICT_RTP_OPEN = 0, /*! No RTP packets should be dropped, all sources accepted */
@@ -113,6 +147,14 @@
 #define FLAG_NAT_INACTIVE_NOWARN        (1 << 1)
 #define FLAG_NEED_MARKER_BIT            (1 << 3)
 #define FLAG_DTMF_COMPENSATE            (1 << 4)
+
+#define TRANSPORT_SOCKET_RTP 1
+#define TRANSPORT_SOCKET_RTCP 2
+#define TRANSPORT_TURN_RTP 3
+#define TRANSPORT_TURN_RTCP 4
+
+#define COMPONENT_RTP 1
+#define COMPONENT_RTCP 2
 
 /*! \brief RTP session description */
 struct ast_rtp {
@@ -187,6 +229,23 @@
 	int learning_probation;		/*!< Sequential packets untill source is valid */
 
 	struct rtp_red *red;
+
+	pj_ice_sess *ice;           /*!< ICE session */
+	pj_turn_sock *turn_rtp;     /*!< RTP TURN relay */
+	pj_turn_sock *turn_rtcp;    /*!< RTCP TURN relay */
+	ast_mutex_t lock;           /*!< Lock for synchronization purposes */
+	pj_turn_state_t turn_state; /*!< Current state of the TURN relay session */
+	ast_cond_t cond;            /*!< Condition for signaling */
+	unsigned int passthrough:1; /*!< Bit to indicate that the received packet should be passed through */
+
+	char remote_ufrag[256];  /*!< The remote ICE username */
+	char remote_passwd[256]; /*!< The remote ICE password */
+
+	char local_ufrag[256];  /*!< The local ICE username */
+	char local_passwd[256]; /*!< The local ICE password */
+
+	struct ao2_container *local_candidates;   /*!< The local ICE candidates */
+	struct ao2_container *remote_candidates;  /*!< The remote ICE candidates */
 };
 
 /*!
@@ -293,6 +352,238 @@
 static int ast_rtp_qos_set(struct ast_rtp_instance *instance, int tos, int cos, const char* desc);
 static int ast_rtp_sendcng(struct ast_rtp_instance *instance, int level);
 
+/*! \brief Destructor for locally created ICE candidates */
+static void ast_rtp_ice_candidate_destroy(void *obj)
+{
+	struct ast_rtp_engine_ice_candidate *candidate = obj;
+
+	if (candidate->foundation) {
+		ast_free(candidate->foundation);
+	}
+
+	if (candidate->transport) {
+		ast_free(candidate->transport);
+	}
+}
+
+static void ast_rtp_ice_set_authentication(struct ast_rtp_instance *instance, const char *ufrag, const char *password)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!ast_strlen_zero(ufrag)) {
+		ast_copy_string(rtp->remote_ufrag, ufrag, sizeof(rtp->remote_ufrag));
+	}
+
+	if (!ast_strlen_zero(password)) {
+		ast_copy_string(rtp->remote_passwd, password, sizeof(rtp->remote_passwd));
+	}
+}
+
+static void ast_rtp_ice_add_remote_candidate(struct ast_rtp_instance *instance, const struct ast_rtp_engine_ice_candidate *candidate)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	struct ast_rtp_engine_ice_candidate *remote_candidate;
+
+	if (!rtp->remote_candidates && !(rtp->remote_candidates = ao2_container_alloc(1, NULL, NULL))) {
+		return;
+	}
+
+	if (!(remote_candidate = ao2_alloc(sizeof(*remote_candidate), ast_rtp_ice_candidate_destroy))) {
+		return;
+	}
+
+	remote_candidate->foundation = ast_strdup(candidate->foundation);
+	remote_candidate->id = candidate->id;
+	remote_candidate->transport = ast_strdup(candidate->transport);
+	remote_candidate->priority = candidate->priority;
+	ast_sockaddr_copy(&remote_candidate->address, &candidate->address);
+	ast_sockaddr_copy(&remote_candidate->relay_address, &candidate->relay_address);
+	remote_candidate->type = candidate->type;
+
+	ao2_link(rtp->remote_candidates, remote_candidate);
+	ao2_ref(remote_candidate, -1);
+}
+
+/*! \brief Function used to check if the calling thread is registered with pjlib. If it is not it will be registered. */
+static void pj_thread_register_check(void)
+{
+	pj_thread_desc desc;
+	pj_thread_t *thread;
+
+	if (pj_thread_is_registered() == PJ_TRUE) {
+		return;
+	}
+
+	pj_thread_register("Asterisk Thread", desc, &thread);
+}
+
+static void ast_rtp_ice_start(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	pj_str_t ufrag = pj_str(rtp->remote_ufrag), passwd = pj_str(rtp->remote_passwd);
+	pj_ice_sess_cand candidates[PJ_ICE_MAX_CAND];
+	struct ao2_iterator i;
+	struct ast_rtp_engine_ice_candidate *candidate;
+	int cand_cnt = 0;
+
+	if (!rtp->ice || !rtp->remote_candidates) {
+		return;
+	}
+
+	pj_thread_register_check();
+
+	i = ao2_iterator_init(rtp->remote_candidates, 0);
+
+	while ((candidate = ao2_iterator_next(&i))) {
+		pj_str_t address;
+
+		pj_strdup2(rtp->ice->pool, &candidates[cand_cnt].foundation, candidate->foundation);
+		candidates[cand_cnt].comp_id = candidate->id;
+		candidates[cand_cnt].prio = candidate->priority;
+
+		pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&address, ast_sockaddr_stringify(&candidate->address)), &candidates[cand_cnt].addr);
+
+		if (!ast_sockaddr_isnull(&candidate->relay_address)) {
+			pj_sockaddr_parse(pj_AF_UNSPEC(), 0, pj_cstr(&address, ast_sockaddr_stringify(&candidate->relay_address)), &candidates[cand_cnt].rel_addr);
+		}
+
+		if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_HOST) {
+			candidates[cand_cnt].type = PJ_ICE_CAND_TYPE_HOST;
+		} else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_SRFLX) {
+			candidates[cand_cnt].type = PJ_ICE_CAND_TYPE_SRFLX;
+		} else if (candidate->type == AST_RTP_ICE_CANDIDATE_TYPE_RELAYED) {
+			candidates[cand_cnt].type = PJ_ICE_CAND_TYPE_RELAYED;
+		}
+
+		if (candidate->id == 1 && rtp->turn_rtp) {
+			pj_turn_sock_set_perm(rtp->turn_rtp, 1, &candidates[cand_cnt].addr, 1);
+		} else if (candidate->id == 2 && rtp->turn_rtcp) {
+			pj_turn_sock_set_perm(rtp->turn_rtcp, 1, &candidates[cand_cnt].addr, 1);
+		}
+
+		cand_cnt++;
+	}
+
+	ao2_iterator_destroy(&i);
+
+	if (pj_ice_sess_create_check_list(rtp->ice, &ufrag, &passwd, ao2_container_count(rtp->remote_candidates), &candidates[0]) == PJ_SUCCESS) {
+		pj_ice_sess_start_check(rtp->ice);
+	}
+}
+
+static const char *ast_rtp_ice_get_ufrag(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	return rtp->ice ? rtp->local_ufrag : NULL;
+}
+
+static const char *ast_rtp_ice_get_password(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	return rtp->ice ? rtp->local_passwd : NULL;
+}
+
+static struct ao2_container *ast_rtp_ice_get_local_candidates(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (rtp->local_candidates) {
+		ao2_ref(rtp->local_candidates, +1);
+	}
+
+	return rtp->local_candidates;
+}
+
+static void ast_rtp_ice_lite(struct ast_rtp_instance *instance)
+{
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	if (!rtp->ice) {
+		return;
+	}
+
+	pj_thread_register_check();
+
+	pj_ice_sess_change_role(rtp->ice, PJ_ICE_SESS_ROLE_CONTROLLING);
+}
+
+static void ast_rtp_ice_add_cand(struct ast_rtp *rtp, unsigned comp_id, unsigned transport_id, pj_ice_cand_type type, pj_uint16_t local_pref,
+					const pj_sockaddr_t *addr, const pj_sockaddr_t *base_addr, const pj_sockaddr_t *rel_addr, int addr_len)
+{
+	pj_str_t foundation;
+	struct ast_rtp_engine_ice_candidate *candidate;
+	char address[PJ_INET6_ADDRSTRLEN];
+
+	pj_thread_register_check();
+
+	pj_ice_calc_foundation(rtp->ice->pool, &foundation, type, addr);
+
+	if (!rtp->local_candidates && !(rtp->local_candidates = ao2_container_alloc(1, NULL, NULL))) {
+		return;
+	}
+
+	if (!(candidate = ao2_alloc(sizeof(*candidate), ast_rtp_ice_candidate_destroy))) {
+		return;
+	}
+
+	candidate->foundation = ast_calloc(1, pj_strlen(&foundation) + 1);
+	ast_copy_string(candidate->foundation, pj_strbuf(&foundation), sizeof(candidate->foundation));
+	candidate->id = comp_id;
+	candidate->transport = ast_strdup("UDP");
+
+	ast_sockaddr_parse(&candidate->address, pj_sockaddr_print(addr, address, sizeof(address), 0), 0);
+	ast_sockaddr_set_port(&candidate->address, pj_sockaddr_get_port(addr));
+
+	if (rel_addr) {
+		ast_sockaddr_parse(&candidate->relay_address, pj_sockaddr_print(rel_addr, address, sizeof(address), 0), 0);
+		ast_sockaddr_set_port(&candidate->relay_address, pj_sockaddr_get_port(rel_addr));
+	}
+
+	if (type == PJ_ICE_CAND_TYPE_HOST) {
+		candidate->type = AST_RTP_ICE_CANDIDATE_TYPE_HOST;
+	} else if (type == PJ_ICE_CAND_TYPE_SRFLX) {
+		candidate->type = AST_RTP_ICE_CANDIDATE_TYPE_SRFLX;
+	} else if (type == PJ_ICE_CAND_TYPE_RELAYED) {
+		candidate->type = AST_RTP_ICE_CANDIDATE_TYPE_RELAYED;
+	}
+
+	if (pj_ice_sess_add_cand(rtp->ice, comp_id, transport_id, type, local_pref, &foundation, addr, addr, rel_addr, addr_len, NULL) != PJ_SUCCESS) {
+		ao2_ref(candidate, -1);
+		return;
+	}
+
+	/* By placing the candidate into the ICE session it will have produced the priority, so update the local candidate with it */
+	candidate->priority = rtp->ice->lcand[rtp->ice->lcand_cnt - 1].prio;
+
+	ao2_link(rtp->local_candidates, candidate);
+	ao2_ref(candidate, -1);
+}
+
+static char *generate_random_string(char *buf, size_t size)
+{
+        long val[4];
+        int x;
+
+        for (x=0; x<4; x++)
+                val[x] = ast_random();
+        snprintf(buf, size, "%08lx%08lx%08lx%08lx", val[0], val[1], val[2], val[3]);
+
+        return buf;
+}
+
+/* ICE RTP Engine interface declaration */
+static struct ast_rtp_engine_ice ast_rtp_ice = {
+	.set_authentication = ast_rtp_ice_set_authentication,
+	.add_remote_candidate = ast_rtp_ice_add_remote_candidate,
+	.start = ast_rtp_ice_start,
+	.get_ufrag = ast_rtp_ice_get_ufrag,
+	.get_password = ast_rtp_ice_get_password,
+	.get_local_candidates = ast_rtp_ice_get_local_candidates,
+	.ice_lite = ast_rtp_ice_lite,
+};
+
 /* RTP Engine Declaration */
 static struct ast_rtp_engine asterisk_rtp_engine = {
 	.name = "asterisk",
@@ -320,7 +611,154 @@
 	.stop = ast_rtp_stop,
 	.qos = ast_rtp_qos_set,
 	.sendcng = ast_rtp_sendcng,
+	.ice = &ast_rtp_ice,
 };
+
+static void ast_rtp_on_ice_rx_data(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, void *pkt, pj_size_t size, const pj_sockaddr_t *src_addr, unsigned src_addr_len)
+{
+	struct ast_rtp *rtp = ice->user_data;
+
+	/* Instead of handling the packet here (which really doesn't work with our architecture) we set a bit to indicate that it should be handled after pj_ice_sess_on_rx_pkt
+	 * returns */
+	rtp->passthrough = 1;
+}
+
+static pj_status_t ast_rtp_on_ice_tx_pkt(pj_ice_sess *ice, unsigned comp_id, unsigned transport_id, const void *pkt, pj_size_t size, const pj_sockaddr_t *dst_addr, unsigned dst_addr_len)
+{
+	struct ast_rtp *rtp = ice->user_data;
+	pj_status_t status = PJ_EINVALIDOP;
+
+	if (transport_id == TRANSPORT_SOCKET_RTP) {
+		/* Traffic is destined to go right out the RTP socket we already have */
+		status = pj_sock_sendto(rtp->s, pkt, (pj_ssize_t*)&size, 0, dst_addr, dst_addr_len);
+	} else if (transport_id == TRANSPORT_SOCKET_RTCP) {
+		/* Traffic is destined to go right out the RTCP socket we already have */
+		status = pj_sock_sendto(rtp->rtcp->s, pkt, (pj_ssize_t*)&size, 0, dst_addr, dst_addr_len);
+	} else if (transport_id == TRANSPORT_TURN_RTP) {
+		/* Traffic is going through the RTP TURN relay */
+		if (rtp->turn_rtp) {
+			status = pj_turn_sock_sendto(rtp->turn_rtp, pkt, size, dst_addr, dst_addr_len);
+		}
+	} else if (transport_id == TRANSPORT_TURN_RTCP) {
+		/* Traffic is going through the RTCP TURN relay */
+		if (rtp->turn_rtcp) {
+			status = pj_turn_sock_sendto(rtp->turn_rtcp, pkt, size, dst_addr, dst_addr_len);
+		}
+	}
+
+	return status;
+}
+
+/* ICE Session interface declaration */
+static pj_ice_sess_cb ast_rtp_ice_sess_cb = {
+	.on_rx_data = ast_rtp_on_ice_rx_data,
+	.on_tx_pkt = ast_rtp_on_ice_tx_pkt,
+};
+
+static void ast_rtp_on_turn_rx_rtp_data(pj_turn_sock *turn_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len)
+{
+	struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+	struct ast_sockaddr dest;
+	
+	ast_rtp_instance_get_local_address(instance, &dest);
+
+	ast_sendto(rtp->s, pkt, pkt_len, 0, &dest);
+}
+
+static void ast_rtp_on_turn_rtp_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, pj_turn_state_t new_state)
+{
+	struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
+	struct ast_rtp *rtp = NULL;
+
+	/* If this is a leftover from an already destroyed RTP instance just ignore the state change */
+	if (!instance) {
+		return;
+	}
+
+	rtp = ast_rtp_instance_get_data(instance);
+
+	/* If the TURN session is being destroyed we need to remove it from the RTP instance */
+	if (new_state == PJ_TURN_STATE_DESTROYING) {
+		rtp->turn_rtp = NULL;
+		return;
+	}
+
+	/* We store the new state so the other thread can actually handle it */
+	ast_mutex_lock(&rtp->lock);
+	rtp->turn_state = new_state;
+
+	/* If this is a state that the main thread should be notified about do so */
+	if (new_state == PJ_TURN_STATE_READY || new_state == PJ_TURN_STATE_DEALLOCATING || new_state == PJ_TURN_STATE_DEALLOCATED) {
+		ast_cond_signal(&rtp->cond);
+	}
+
+	ast_mutex_unlock(&rtp->lock);
+}
+
+/* RTP TURN Socket interface declaration */
+static pj_turn_sock_cb ast_rtp_turn_rtp_sock_cb = {
+	.on_rx_data = ast_rtp_on_turn_rx_rtp_data,
+	.on_state = ast_rtp_on_turn_rtp_state,
+};
+
+static void ast_rtp_on_turn_rx_rtcp_data(pj_turn_sock *turn_sock, void *pkt, unsigned pkt_len, const pj_sockaddr_t *peer_addr, unsigned addr_len)
+{
+	struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
+	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
+
+	ast_sendto(rtp->rtcp->s, pkt, pkt_len, 0, &rtp->rtcp->us);
+}
+
+static void ast_rtp_on_turn_rtcp_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state, pj_turn_state_t new_state)
+{
+	struct ast_rtp_instance *instance = pj_turn_sock_get_user_data(turn_sock);
+	struct ast_rtp *rtp = NULL;
+
+	/* If this is a leftover from an already destroyed RTP instance just ignore the state change */
+	if (!instance) {
+		return;
+	}
+
+	rtp = ast_rtp_instance_get_data(instance);
+
+	/* If the TURN session is being destroyed we need to remove it from the RTP instance */
+	if (new_state == PJ_TURN_STATE_DESTROYING) {
+		rtp->turn_rtcp = NULL;
+		return;
+	}
+
+	/* We store the new state so the other thread can actually handle it */
+	ast_mutex_lock(&rtp->lock);
+	rtp->turn_state = new_state;
+
+	/* If this is a state that the main thread should be notified about do so */
+	if (new_state == PJ_TURN_STATE_READY || new_state == PJ_TURN_STATE_DEALLOCATING || new_state == PJ_TURN_STATE_DEALLOCATED) {
+		ast_cond_signal(&rtp->cond);
+	}
+
+       ast_mutex_unlock(&rtp->lock);
+}
+
+/* RTCP TURN Socket interface declaration */
+static pj_turn_sock_cb ast_rtp_turn_rtcp_sock_cb = {
+	.on_rx_data = ast_rtp_on_turn_rx_rtcp_data,
+	.on_state = ast_rtp_on_turn_rtcp_state,
+};
+
+/*! \brief Worker thread for I/O queue and timerheap */
+static int ice_worker_thread(void *data)
+{
+	while (!worker_terminate) {
+		const pj_time_val delay = {0, 10};
+
+		pj_ioqueue_poll(ioqueue, &delay);
+
+		pj_timer_heap_poll(timerheap, NULL);
+	}
+
+	return 0;
+}
 
 static inline int rtp_debug_test_addr(struct ast_sockaddr *addr)
 {
@@ -364,6 +802,24 @@
 	   return len;
 	}
 
+	if (rtp->ice) {
+		pj_str_t combined = pj_str(ast_sockaddr_stringify(sa));
+		pj_sockaddr address;
+
+		pj_thread_register_check();
+
+		pj_sockaddr_parse(pj_AF_UNSPEC(), 0, &combined, &address);
+
+		if (pj_ice_sess_on_rx_pkt(rtp->ice, rtcp ? COMPONENT_RTCP : COMPONENT_RTP, rtcp ? TRANSPORT_SOCKET_RTCP : TRANSPORT_SOCKET_RTP,
+					  buf, len, &address, pj_sockaddr_get_len(&address)) != PJ_SUCCESS) {
+			return -1;
+		}
+		if (!rtp->passthrough) {
+			return 0;
+		}
+		rtp->passthrough = 0;
+	}
+
 	if (res_srtp && srtp && res_srtp->unprotect(srtp, buf, &len, rtcp) < 0) {
 	   return -1;
 	}
@@ -381,28 +837,35 @@
 	return __rtp_recvfrom(instance, buf, size, flags, sa, 0);
 }
 
-static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp)
+static int __rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int rtcp, int *ice)
 {
 	int len = size;
 	void *temp = buf;
 	struct ast_rtp *rtp = ast_rtp_instance_get_data(instance);
 	struct ast_srtp *srtp = ast_rtp_instance_get_srtp(instance);
 
+	*ice = 0;
+
 	if (res_srtp && srtp && res_srtp->protect(srtp, &temp, &len, rtcp) < 0) {
-	   return -1;
+		return -1;
+	}
+
+	if (rtp->ice && (pj_ice_sess_send_data(rtp->ice, rtcp ? COMPONENT_RTCP : COMPONENT_RTP, temp, len) == PJ_SUCCESS)) {
+		*ice = 1;
+		return 0;
 	}
 
 	return ast_sendto(rtcp ? rtp->rtcp->s : rtp->s, temp, len, flags, sa);
 }
 
-static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa)
-{
-	return __rtp_sendto(instance, buf, size, flags, sa, 1);
-}
-
-static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa)
-{
-	return __rtp_sendto(instance, buf, size, flags, sa, 0);
+static int rtcp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
+{
+	return __rtp_sendto(instance, buf, size, flags, sa, 1, ice);
+}
+
+static int rtp_sendto(struct ast_rtp_instance *instance, void *buf, size_t size, int flags, struct ast_sockaddr *sa, int *ice)
+{
+	return __rtp_sendto(instance, buf, size, flags, sa, 0, ice);
 }
 
 static int rtp_get_rate(struct ast_format *format)
@@ -514,17 +977,88 @@
 	return probation;
 }
 
+static void rtp_add_candidates_to_ice(struct ast_rtp_instance *instance, struct ast_rtp *rtp, struct ast_sockaddr *addr, int port, int component,
+				      int transport, const pj_turn_sock_cb *turn_cb, pj_turn_sock **turn_sock)
+{
+	pj_sockaddr address[16];
+	unsigned int count = PJ_ARRAY_SIZE(address), pos = 0;
+
+	/* Add all the local interface IP addresses */
+	pj_enum_ip_interface(ast_sockaddr_is_ipv4(addr) ? pj_AF_INET() : pj_AF_INET6(), &count, address);
+
+	for (pos = 0; pos < count; pos++) {
+		pj_sockaddr_set_port(&address[pos], port);
+		ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_HOST, 65535, &address[pos], &address[pos], NULL,
+				     pj_sockaddr_get_len(&address[pos]));
+	}
+
+	/* If configured to use a STUN server to get our external mapped address do so */
+	if (stunaddr.sin_addr.s_addr && ast_sockaddr_is_ipv4(addr)) {
+		struct sockaddr_in answer;
+
+		if (!ast_stun_request(rtp->s, &stunaddr, NULL, &answer)) {
+			pj_str_t mapped = pj_str(ast_strdupa(ast_inet_ntoa(answer.sin_addr)));
+
+			pj_sockaddr_init(pj_AF_INET(), &address[0], &mapped, ntohs(answer.sin_port));
+
+			ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_SRFLX, 65535, &address[0], &address[0],
+					     NULL, pj_sockaddr_get_len(&address[0]));
+		}
+	}
+
+	/* If configured to use a TURN relay create a session and allocate */
+	if (pj_strlen(&turnaddr) && pj_turn_sock_create(&rtp->ice->stun_cfg, ast_sockaddr_is_ipv4(addr) ? pj_AF_INET() : pj_AF_INET6(), PJ_TURN_TP_TCP,
+							turn_cb, NULL, instance, turn_sock) == PJ_SUCCESS) {
+		pj_stun_auth_cred cred = { 0, };
+		struct timeval wait = ast_tvadd(ast_tvnow(), ast_samp2tv(TURN_ALLOCATION_WAIT_TIME, 1000));
+		struct timespec ts = { .tv_sec = wait.tv_sec, .tv_nsec = wait.tv_usec * 1000, };
+
+		cred.type = PJ_STUN_AUTH_CRED_STATIC;
+		cred.data.static_cred.username = turnusername;
+		cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+		cred.data.static_cred.data = turnpassword;
+
+		/* Because the TURN socket is asynchronous but we are synchronous we need to wait until it is done */
+		ast_mutex_lock(&rtp->lock);
+		pj_turn_sock_alloc(*turn_sock, &turnaddr, turnport, NULL, &cred, NULL);
+		ast_cond_timedwait(&rtp->cond, &rtp->lock, &ts);
+		ast_mutex_unlock(&rtp->lock);
+
+		/* If a TURN session was allocated add it as a candidate */
+		if (rtp->turn_state == PJ_TURN_STATE_READY) {
+			pj_turn_session_info info;
+
+			pj_turn_sock_get_info(*turn_sock, &info);
+
+			if (transport == TRANSPORT_SOCKET_RTP) {
+				transport = TRANSPORT_TURN_RTP;
+			} else if (transport == TRANSPORT_SOCKET_RTCP) {
+				transport = TRANSPORT_TURN_RTCP;
+			}
+
+			ast_rtp_ice_add_cand(rtp, component, transport, PJ_ICE_CAND_TYPE_RELAYED, 65535, &info.relay_addr, &info.relay_addr,
+					     NULL, pj_sockaddr_get_len(&info.relay_addr));
+		}
+	}
+}
+
 static int ast_rtp_new(struct ast_rtp_instance *instance,
 		       struct ast_sched_context *sched, struct ast_sockaddr *addr,
 		       void *data)
 {
 	struct ast_rtp *rtp = NULL;
 	int x, startplace;
+	pj_stun_config stun_config;
+	pj_str_t ufrag, passwd;
 
 	/* Create a new RTP structure to hold all of our data */
 	if (!(rtp = ast_calloc(1, sizeof(*rtp)))) {
 		return -1;
 	}
+
+	/* Initialize synchronization aspects */
+	ast_mutex_init(&rtp->lock);
+	ast_cond_init(&rtp->cond, NULL);
 
 	/* Set default parameters on the newly created RTP structure */
 	rtp->ssrc = ast_random();
@@ -570,11 +1104,28 @@
 		}
 	}
 
+	pj_thread_register_check();
+
+	pj_stun_config_init(&stun_config, &cachingpool.factory, 0, ioqueue, timerheap);
+
+	generate_random_string(rtp->local_ufrag, sizeof(rtp->local_ufrag));
+	ufrag = pj_str(rtp->local_ufrag);
+	generate_random_string(rtp->local_passwd, sizeof(rtp->local_passwd));
+	passwd = pj_str(rtp->local_passwd);
+
+	ast_rtp_instance_set_data(instance, rtp);
+
+	/* Create an ICE session for ICE negotiation */
+	if (icesupport && pj_ice_sess_create(&stun_config, NULL, PJ_ICE_SESS_ROLE_UNKNOWN, 2, &ast_rtp_ice_sess_cb, &ufrag, &passwd, &rtp->ice) == PJ_SUCCESS) {
+		/* Make this available for the callbacks */
+		rtp->ice->user_data = rtp;
+

[... 501 lines stripped ...]



More information about the asterisk-commits mailing list