Index: /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1989)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1990)
@@ -171,5 +171,4 @@
     puts  ("");
     puts  ("Media Options:");
-    puts  ("  --use-ice           Enable ICE (default:no)");
     puts  ("  --add-codec=name    Manually add codec (default is to enable all)");
     puts  ("  --dis-codec=name    Disable codec (can be specified multiple times)");
@@ -189,5 +188,4 @@
     puts  ("  --rec-file=file     Open file recorder (extension can be .wav or .mp3");
     puts  ("  --auto-rec          Automatically record conversation");
-    puts  ("  --rtp-port=N        Base port to try for RTP (default=4000)");
     puts  ("  --quality=N         Specify media quality (0-10, default=6)");
     puts  ("  --ptime=MSEC        Override codec ptime to MSEC (default=specific)");
@@ -195,9 +193,19 @@
     puts  ("  --ec-tail=MSEC      Set echo canceller tail length (default=256)");
     puts  ("  --ilbc-mode=MODE    Set iLBC codec mode (20 or 30, default is 20)");
+    puts  ("  --capture-dev=id    Audio capture device ID (default=-1)");
+    puts  ("  --playback-dev=id   Audio playback device ID (default=-1)");
+
+    puts  ("");
+    puts  ("Media Transport Options:");
+    puts  ("  --use-ice           Enable ICE (default:no)");
+    puts  ("  --ice-no-host       Disable ICE host candidates");
+    puts  ("  --rtp-port=N        Base port to try for RTP (default=4000)");
     puts  ("  --rx-drop-pct=PCT   Drop PCT percent of RX RTP (for pkt lost sim, default: 0)");
     puts  ("  --tx-drop-pct=PCT   Drop PCT percent of TX RTP (for pkt lost sim, default: 0)");
-    puts  ("  --capture-dev=id    Audio capture device ID (default=-1)");
-    puts  ("  --playback-dev=id   Audio playback device ID (default=-1)");
-
+    puts  ("  --use-turn          Enable TURN relay with ICE (default:no)");
+    puts  ("  --turn-srv          Domain or host name of TURN server (\"NAME:PORT\" format)");
+    puts  ("  --turn-tcp          Use TCP connection to TURN server (default no)");
+    puts  ("  --turn-user         TURN username");
+    puts  ("  --turn-passwd       TURN password");
 
     puts  ("");
@@ -393,4 +401,6 @@
 	   OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_SND_CLOCK_RATE, OPT_STEREO,
 	   OPT_USE_ICE, OPT_USE_SRTP, OPT_SRTP_SECURE,
+	   OPT_USE_TURN,OPT_ICE_NO_HOST, OPT_TURN_SRV, OPT_TURN_TCP,
+	   OPT_TURN_USER, OPT_TURN_PASSWD,
 	   OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, 
 	   OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
@@ -452,5 +462,13 @@
 	{ "rec-file",   1, 0, OPT_REC_FILE},
 	{ "rtp-port",	1, 0, OPT_RTP_PORT},
+
 	{ "use-ice",    0, 0, OPT_USE_ICE},
+	{ "use-turn",	0, 0, OPT_USE_TURN},
+	{ "ice-no-host",0, 0, OPT_ICE_NO_HOST},
+	{ "turn-srv",	1, 0, OPT_TURN_SRV},
+	{ "turn-tcp",	0, 0, OPT_TURN_TCP},
+	{ "turn-user",	1, 0, OPT_TURN_USER},
+	{ "turn-passwd",1, 0, OPT_TURN_PASSWD},
+
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 	{ "use-srtp",   1, 0, OPT_USE_SRTP},
@@ -826,4 +844,31 @@
 	    break;
 
+	case OPT_USE_TURN:
+	    cfg->media_cfg.enable_turn = PJ_TRUE;
+	    break;
+
+	case OPT_ICE_NO_HOST:
+	    cfg->media_cfg.ice_no_host_cands = PJ_TRUE;
+	    break;
+
+	case OPT_TURN_SRV:
+	    cfg->media_cfg.turn_server = pj_str(pj_optarg);
+	    break;
+
+	case OPT_TURN_TCP:
+	    cfg->media_cfg.turn_conn_type = PJ_TURN_TP_TCP;
+	    break;
+
+	case OPT_TURN_USER:
+	    cfg->media_cfg.turn_auth_cred.type = PJ_STUN_AUTH_CRED_STATIC;
+	    cfg->media_cfg.turn_auth_cred.data.static_cred.realm = pj_str("*");
+	    cfg->media_cfg.turn_auth_cred.data.static_cred.username = pj_str(pj_optarg);
+	    break;
+
+	case OPT_TURN_PASSWD:
+	    cfg->media_cfg.turn_auth_cred.data.static_cred.data_type = PJ_STUN_PASSWD_PLAIN;
+	    cfg->media_cfg.turn_auth_cred.data.static_cred.data = pj_str(pj_optarg);
+	    break;
+
 #if defined(PJMEDIA_HAS_SRTP) && (PJMEDIA_HAS_SRTP != 0)
 	case OPT_USE_SRTP:
@@ -1360,7 +1405,39 @@
 #endif
 
-    /* Media */
+    /* Media Transport*/
     if (config->media_cfg.enable_ice)
 	pj_strcat2(&cfg, "--use-ice\n");
+
+    if (config->media_cfg.enable_turn)
+	pj_strcat2(&cfg, "--use-turn\n");
+
+    if (config->media_cfg.ice_no_host_cands)
+	pj_strcat2(&cfg, "--ice-no-host\n");
+
+    if (config->media_cfg.turn_server.slen) {
+	pj_ansi_sprintf(line, "--turn-srv %.*s\n",
+			(int)config->media_cfg.turn_server.slen,
+			config->media_cfg.turn_server.ptr);
+	pj_strcat2(&cfg, line);
+    }
+
+    if (config->media_cfg.turn_conn_type == PJ_TURN_TP_TCP)
+	pj_strcat2(&cfg, "--turn-tcp\n");
+
+    if (config->media_cfg.turn_auth_cred.data.static_cred.username.slen) {
+	pj_ansi_sprintf(line, "--turn-user %.*s\n",
+			(int)config->media_cfg.turn_auth_cred.data.static_cred.username.slen,
+			config->media_cfg.turn_auth_cred.data.static_cred.username.ptr);
+	pj_strcat2(&cfg, line);
+    }
+
+    if (config->media_cfg.turn_auth_cred.data.static_cred.data.slen) {
+	pj_ansi_sprintf(line, "--turn-passwd %.*s\n",
+			(int)config->media_cfg.turn_auth_cred.data.static_cred.data.slen,
+			config->media_cfg.turn_auth_cred.data.static_cred.data.ptr);
+	pj_strcat2(&cfg, line);
+    }
+
+    /* Media */
     if (config->null_audio)
 	pj_strcat2(&cfg, "--null-audio\n");
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 1989)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 1990)
@@ -1029,6 +1029,6 @@
     /**
      * Specify domain name to be resolved with DNS SRV resolution to get the
-     * address of the STUN servers. Alternatively application may specify
-     * \a stun_host and \a stun_relay_host instead.
+     * address of the STUN server. Alternatively application may specify
+     * \a stun_host instead.
      *
      * If DNS SRV resolution failed for this domain, then DNS A resolution
@@ -1042,9 +1042,4 @@
      */
     pj_str_t	    stun_host;
-
-    /**
-     * Specify STUN relay server to be used.
-     */
-    pj_str_t	    stun_relay_host;
 
     /**
@@ -3887,7 +3882,31 @@
 
     /**
-     * Enable ICE media relay.
-     */
-    pj_bool_t		enable_relay;
+     * Disable ICE host candidates.
+     */
+    pj_bool_t		ice_no_host_cands;
+
+    /**
+     * Enable TURN relay candidate in ICE.
+     */
+    pj_bool_t		enable_turn;
+
+    /**
+     * Specify TURN domain name or host name, in in "DOMAIN:PORT" or 
+     * "HOST:PORT" format.
+     */
+    pj_str_t		turn_server;
+
+    /**
+     * Specify the connection type to be used to the TURN server. Valid
+     * values are PJ_TURN_TP_UDP or PJ_TURN_TP_TCP.
+     *
+     * Default: PJ_TURN_TP_UDP
+     */
+    pj_turn_tp_type	turn_conn_type;
+
+    /**
+     * Specify the credential to authenticate with the TURN server.
+     */
+    pj_stun_auth_cred	turn_auth_cred;
 };
 
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 1989)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 1990)
@@ -53,4 +53,5 @@
 					 call was triggered by xfer.	    */
     pjmedia_transport	*med_tp;    /**< Current media transport.	    */
+    pj_status_t		 med_tp_st; /**< Media transport status.	    */
     pjmedia_transport	*med_orig;  /**< Original media transport	    */
     pj_timer_entry	 refresh_tm;/**< Timer to send re-INVITE.	    */
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 1989)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 1990)
@@ -114,5 +114,4 @@
     pj_strdup_with_null(pool, &dst->stun_domain, &src->stun_domain);
     pj_strdup_with_null(pool, &dst->stun_host, &src->stun_host);
-    pj_strdup_with_null(pool, &dst->stun_relay_host, &src->stun_relay_host);
 }
 
@@ -173,4 +172,6 @@
     cfg->ec_tail_len = PJSUA_DEFAULT_EC_TAIL_LEN;
     cfg->jb_init = cfg->jb_min_pre = cfg->jb_max_pre = cfg->jb_max = -1;
+
+    cfg->turn_conn_type = PJ_TURN_TP_UDP;
 }
 
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1989)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1990)
@@ -37,4 +37,13 @@
 
 
+static void pjsua_media_config_dup(pj_pool_t *pool,
+				   pjsua_media_config *dst,
+				   const pjsua_media_config *src)
+{
+    pj_memcpy(dst, src, sizeof(*src));
+    pj_strdup(pool, &dst->turn_server, &src->turn_server);
+    pj_stun_auth_cred_dup(pool, &dst->turn_auth_cred, &src->turn_auth_cred);
+}
+
 /**
  * Init media subsystems.
@@ -50,5 +59,5 @@
 
     /* Copy configuration */
-    pj_memcpy(&pjsua_var.media_cfg, cfg, sizeof(*cfg));
+    pjsua_media_config_dup(pjsua_var.pool, &pjsua_var.media_cfg, cfg);
 
     /* Normalize configuration */
@@ -582,44 +591,79 @@
 
 /* This callback is called when ICE negotiation completes */
-static void on_ice_complete(pjmedia_transport *tp, pj_status_t result)
-{
-    unsigned id, c;
+static void on_ice_complete(pjmedia_transport *tp, 
+			    pj_ice_strans_op op,
+			    pj_status_t result)
+{
+    unsigned id;
     pj_bool_t found = PJ_FALSE;
 
-    /* We're only interested with failure case */
-    if (result == PJ_SUCCESS)
+    /* Find call which has this media transport */
+
+    PJSUA_LOCK();
+
+    for (id=0; id<pjsua_var.ua_cfg.max_calls; ++id) {
+	if (pjsua_var.calls[id].med_tp == tp ||
+	    pjsua_var.calls[id].med_orig == tp) 
+	{
+	    found = PJ_TRUE;
+	    break;
+	}
+    }
+
+    PJSUA_UNLOCK();
+
+    if (!found)
 	return;
 
-    /* Find call which has this media transport */
-
-    PJSUA_LOCK();
-
-    for (id=0, c=0; id<PJSUA_MAX_CALLS && c<pjsua_var.call_cnt; ++id) {
-	pjsua_call *call = &pjsua_var.calls[id];
-	if (call->inv) {
-	    ++c;
-
-	    if (call->med_tp == tp) {
-		call->media_st = PJSUA_CALL_MEDIA_ERROR;
-		call->media_dir = PJMEDIA_DIR_NONE;
-		found = PJ_TRUE;
-		break;
+    switch (op) {
+    case PJ_ICE_STRANS_OP_INIT:
+	pjsua_var.calls[id].med_tp_st = result;
+	break;
+    case PJ_ICE_STRANS_OP_NEGOTIATION:
+	if (result != PJ_SUCCESS) {
+	    pjsua_var.calls[id].media_st = PJSUA_CALL_MEDIA_ERROR;
+	    pjsua_var.calls[id].media_dir = PJMEDIA_DIR_NONE;
+
+	    if (pjsua_var.ua_cfg.cb.on_call_media_state) {
+		pjsua_var.ua_cfg.cb.on_call_media_state(id);
 	    }
 	}
-    }
-
-    PJSUA_UNLOCK();
-
-    if (found && pjsua_var.ua_cfg.cb.on_call_media_state) {
-	pjsua_var.ua_cfg.cb.on_call_media_state(id);
-    }
-}
-
+	break;
+    }
+}
+
+
+/* Parse "HOST:PORT" format */
+static pj_status_t parse_host_port(const pj_str_t *host_port,
+				   pj_str_t *host, pj_uint16_t *port)
+{
+    pj_str_t str_port;
+
+    str_port.ptr = pj_strchr(host_port, ':');
+    if (str_port.ptr != NULL) {
+	int iport;
+
+	host->ptr = host_port->ptr;
+	host->slen = (str_port.ptr - host->ptr);
+	str_port.ptr++;
+	str_port.slen = host_port->slen - host->slen - 1;
+	iport = (int)pj_strtoul(&str_port);
+	if (iport < 1 || iport > 65535)
+	    return PJ_EINVAL;
+	*port = (pj_uint16_t)iport;
+    } else {
+	*host = *host_port;
+	*port = 0;
+    }
+
+    return PJ_SUCCESS;
+}
 
 /* Create ICE media transports (when ice is enabled) */
-static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
-{
+static pj_status_t create_ice_media_transports(void)
+{
+    char stunip[PJ_INET6_ADDRSTRLEN];
+    pj_ice_strans_cfg ice_cfg;
     unsigned i;
-    pj_sockaddr_in addr;
     pj_status_t status;
 
@@ -631,25 +675,55 @@
     }
 
-    pj_sockaddr_in_init(&addr, 0, (pj_uint16_t)cfg->port);
+    /* Create ICE stream transport configuration */
+    pj_ice_strans_cfg_default(&ice_cfg);
+    pj_stun_config_init(&ice_cfg.stun_cfg, &pjsua_var.cp.factory, 0,
+		        pjsip_endpt_get_ioqueue(pjsua_var.endpt),
+			pjsip_endpt_get_timer_heap(pjsua_var.endpt));
+    
+    ice_cfg.af = pj_AF_INET();
+    ice_cfg.resolver = pjsua_var.resolver;
+    
+    /* Configure STUN settings */
+    if (pj_sockaddr_has_addr(&pjsua_var.stun_srv)) {
+	pj_sockaddr_print(&pjsua_var.stun_srv, stunip, sizeof(stunip), 0);
+	ice_cfg.stun.server = pj_str(stunip);
+	ice_cfg.stun.port = pj_sockaddr_get_port(&pjsua_var.stun_srv);
+    }
+    ice_cfg.stun.no_host_cands = pjsua_var.media_cfg.ice_no_host_cands;
+
+    /* Configure TURN settings */
+    if (pjsua_var.media_cfg.enable_turn) {
+	status = parse_host_port(&pjsua_var.media_cfg.turn_server,
+				 &ice_cfg.turn.server,
+				 &ice_cfg.turn.port);
+	if (status != PJ_SUCCESS || ice_cfg.turn.server.slen == 0) {
+	    PJ_LOG(1,(THIS_FILE, "Invalid TURN server setting"));
+	    return PJ_EINVAL;
+	}
+	if (ice_cfg.turn.port == 0)
+	    ice_cfg.turn.port = 3479;
+	ice_cfg.turn.conn_type = pjsua_var.media_cfg.turn_conn_type;
+	pj_memcpy(&ice_cfg.turn.auth_cred, 
+		  &pjsua_var.media_cfg.turn_auth_cred,
+		  sizeof(ice_cfg.turn.auth_cred));
+    }
 
     /* Create each media transport */
     for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
-	pj_ice_strans_comp comp;
 	pjmedia_ice_cb ice_cb;
-	int next_port;
 	char name[32];
-#if PJMEDIA_ADVERTISE_RTCP
-	enum { COMP_CNT=2 };
-#else
-	enum { COMP_CNT=1 };
-#endif
+	unsigned comp_cnt;
 
 	pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
 	ice_cb.on_ice_complete = &on_ice_complete;
-
 	pj_ansi_snprintf(name, sizeof(name), "icetp%02d", i);
-			 
-	status = pjmedia_ice_create(pjsua_var.med_endpt, name, COMP_CNT,
-				    &pjsua_var.stun_cfg, &ice_cb,
+	pjsua_var.calls[i].med_tp_st = PJ_EPENDING;
+
+	comp_cnt = 1;
+	if (PJMEDIA_ADVERTISE_RTCP)
+	    ++comp_cnt;
+
+	status = pjmedia_ice_create(pjsua_var.med_endpt, name, comp_cnt,
+				    &ice_cfg, &ice_cb,
 				    &pjsua_var.calls[i].med_tp);
 	if (status != PJ_SUCCESS) {
@@ -659,4 +733,17 @@
 	}
 
+	/* Wait until transport is initialized, or time out */
+	PJSUA_UNLOCK();
+	while (pjsua_var.calls[i].med_tp_st == PJ_EPENDING) {
+	    pjsua_handle_events(100);
+	}
+	PJSUA_LOCK();
+	if (pjsua_var.calls[i].med_tp_st != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Error initializing ICE media transport",
+		         pjsua_var.calls[i].med_tp_st);
+	    status = pjsua_var.calls[i].med_tp_st;
+	    goto on_error;
+	}
+
 	pjmedia_transport_simulate_lost(pjsua_var.calls[i].med_tp,
 				        PJMEDIA_DIR_ENCODING,
@@ -666,38 +753,4 @@
 				        PJMEDIA_DIR_DECODING,
 				        pjsua_var.media_cfg.rx_drop_pct);
-
-	status = pjmedia_ice_start_init(pjsua_var.calls[i].med_tp, 0, &addr,
-				        &pjsua_var.stun_srv.ipv4, NULL);
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, "Error starting ICE transport",
-		         status);
-	    goto on_error;
-	}
-
-	pjmedia_ice_get_comp(pjsua_var.calls[i].med_tp, 1, &comp);
-	next_port = pj_ntohs(comp.local_addr.ipv4.sin_port);
-	next_port += 2;
-	addr.sin_port = pj_htons((pj_uint16_t)next_port);
-    }
-
-    /* Wait until all ICE transports are ready */
-    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
-
-	/* Wait until interface status is PJ_SUCCESS */
-	for (;;) {
-	    status = pjmedia_ice_get_init_status(pjsua_var.calls[i].med_tp);
-	    if (status == PJ_EPENDING)
-		pjsua_handle_events(100);
-	    else
-		break;
-	}
-
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, 
-			 "Error adding STUN address to ICE media transport",
-		         status);
-	    goto on_error;
-	}
-
     }
 
@@ -745,5 +798,5 @@
 
     if (pjsua_var.media_cfg.enable_ice) {
-	status = create_ice_media_transports(&cfg);
+	status = create_ice_media_transports();
     } else {
 	status = create_udp_media_transports(&cfg);
@@ -897,5 +950,6 @@
     pjsua_call *call = &pjsua_var.calls[call_id];
 
-    pjmedia_transport_media_stop(call->med_tp);
+    //see ticket #525
+    //pjmedia_transport_media_stop(call->med_tp);
 
     if (call->conf_slot != PJSUA_INVALID_ID) {
