Index: /pjproject/trunk/pjmedia/include/pjmedia.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia.h	(revision 1097)
+++ /pjproject/trunk/pjmedia/include/pjmedia.h	(revision 1098)
@@ -55,4 +55,5 @@
 #include <pjmedia/tonegen.h>
 #include <pjmedia/transport.h>
+#include <pjmedia/transport_ice.h>
 #include <pjmedia/transport_udp.h>
 #include <pjmedia/wav_playlist.h>
Index: /pjproject/trunk/pjmedia/include/pjmedia/transport.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/transport.h	(revision 1097)
+++ /pjproject/trunk/pjmedia/include/pjmedia/transport.h	(revision 1098)
@@ -179,4 +179,9 @@
 struct pjmedia_transport_op
 {
+    /**
+     * Get media socket info from the specified transport.
+     *
+     * Application should call #pjmedia_transport_get_info() instead
+     */
     pj_status_t (*get_info)(pjmedia_transport *tp,
 			    pjmedia_sock_info *info);
@@ -255,4 +260,18 @@
 
 
+/** 
+ * Media transport type.
+ */
+typedef enum pjmedia_transport_type
+{
+    /** Media transport using standard UDP */
+    PJMEDIA_TRANSPORT_TYPE_UDP,
+
+    /** Media transport using ICE */
+    PJMEDIA_TRANSPORT_TYPE_ICE
+
+} pjmedia_transport_type;
+
+
 /**
  * This structure declares stream transport. A stream transport is called
@@ -263,11 +282,25 @@
 {
     /** Transport name (for logging purpose). */
-    char		  name[PJ_MAX_OBJ_NAME];
+    char		     name[PJ_MAX_OBJ_NAME];
+
+    /** Transport type. */
+    pjmedia_transport_type   type;
 
     /** Transport's "virtual" function table. */
-    pjmedia_transport_op *op;
+    pjmedia_transport_op    *op;
 };
 
 
+/**
+ * Get media socket info from the specified transport. The socket info
+ * contains information about the local address of this transport, and
+ * would be needed for example to fill in the "c=" and "m=" line of local 
+ * SDP.
+ *
+ * @param tp	    The transport.
+ * @param info	    Media socket info to be initialized.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
 PJ_INLINE(pj_status_t) pjmedia_transport_get_info(pjmedia_transport *tp,
 						  pjmedia_sock_info *info)
Index: /pjproject/trunk/pjmedia/include/pjmedia/transport_ice.h
===================================================================
--- /pjproject/trunk/pjmedia/include/pjmedia/transport_ice.h	(revision 1097)
+++ /pjproject/trunk/pjmedia/include/pjmedia/transport_ice.h	(revision 1098)
@@ -42,10 +42,10 @@
 PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
 					const char *name,
+					unsigned comp_cnt,
 					pj_stun_config *stun_cfg,
-					pj_dns_resolver *resolver,
-					pj_bool_t enable_relay,
-					const pj_str_t *stun_name,
 					pjmedia_transport **p_tp);
 PJ_DECL(pj_status_t) pjmedia_ice_destroy(pjmedia_transport *tp);
+
+PJ_DECL(pj_ice_st*) pjmedia_ice_get_ice_st(pjmedia_transport *tp);
 
 
@@ -54,7 +54,7 @@
 					  const pj_str_t *local_ufrag,
 					  const pj_str_t *local_passwd);
-PJ_DECL(pj_status_t) pjmedia_ice_build_sdp(pjmedia_transport *tp,
-					   pj_pool_t *pool,
-					   pjmedia_sdp_session *sdp);
+PJ_DECL(pj_status_t) pjmedia_ice_modify_sdp(pjmedia_transport *tp,
+					    pj_pool_t *pool,
+					    pjmedia_sdp_session *sdp);
 PJ_DECL(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp,
 					   pj_pool_t *pool,
Index: /pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 1097)
+++ /pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 1098)
@@ -26,5 +26,5 @@
     pj_ice_st		*ice_st;
 
-    void  *user_data;
+    void  *stream;
     void (*rtp_cb)(void*,
 		   const void*,
@@ -42,5 +42,5 @@
 			       pjmedia_sock_info *info);
 static pj_status_t tp_attach( pjmedia_transport *tp,
-			      void *user_data,
+			      void *stream,
 			      const pj_sockaddr_t *rem_addr,
 			      const pj_sockaddr_t *rem_rtcp,
@@ -93,13 +93,12 @@
 PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
 				       const char *name,
+				       unsigned comp_cnt,
 				       pj_stun_config *stun_cfg,
-				       pj_dns_resolver *resolver,
-				       pj_bool_t enable_relay,
-				       const pj_str_t *stun_name,
-				       pjmedia_transport **p_tp)
+	    			       pjmedia_transport **p_tp)
 {
     pj_ice_st *ice_st;
     pj_ice_st_cb ice_st_cb;
     struct transport_ice *tp_ice;
+    unsigned i;
     pj_status_t status;
 
@@ -118,22 +117,9 @@
 	return status;
 
-    /* Add component 1 (RTP) */
-    status = pj_ice_st_add_comp(ice_st, 1);
-    if (status != PJ_SUCCESS) 
-	goto on_error;
-
-    /* Add host candidates. */
-    status = pj_ice_st_add_all_host_interfaces(ice_st, 1, 0, PJ_FALSE, NULL);
-    if (status != PJ_SUCCESS)
-	goto on_error;
-
-    /* Configure STUN server for ICE */
-    if (stun_name) {
-	status = pj_ice_st_set_stun(ice_st, resolver, enable_relay, stun_name);
+    /* Add components */
+    for (i=0; i<comp_cnt; ++i) {
+	status = pj_ice_st_add_comp(ice_st, i+1);
 	if (status != PJ_SUCCESS) 
 	    goto on_error;
-
-	/* Add STUN candidates */
-	status = pj_ice_st_add_stun_interface(ice_st, 1, 0, PJ_FALSE, NULL);
     }
 
@@ -143,4 +129,5 @@
     pj_ansi_strcpy(tp_ice->base.name, ice_st->obj_name);
     tp_ice->base.op = &tp_ice_op;
+    tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE;
 
     ice_st->user_data = (void*)tp_ice;
@@ -158,5 +145,5 @@
 
 
-PJ_DEF(pj_status_t) pjmedia_ice_close(pjmedia_transport *tp)
+PJ_DEF(pj_status_t) pjmedia_ice_destroy(pjmedia_transport *tp)
 {
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
@@ -164,8 +151,17 @@
     if (tp_ice->ice_st) {
 	pj_ice_st_destroy(tp_ice->ice_st);
-	tp_ice->ice_st = NULL;
+	//Must not touch tp_ice after ice_st is destroyed!
+	//(it has the pool)
+	//tp_ice->ice_st = NULL;
     }
 
     return PJ_SUCCESS;
+}
+
+
+PJ_DECL(pj_ice_st*) pjmedia_ice_get_ice_st(pjmedia_transport *tp)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    return tp_ice->ice_st;
 }
 
@@ -181,7 +177,7 @@
 
 
-PJ_DEF(pj_status_t) pjmedia_ice_build_sdp(pjmedia_transport *tp,
-					  pj_pool_t *pool,
-					  pjmedia_sdp_session *sdp)
+PJ_DEF(pj_status_t) pjmedia_ice_modify_sdp(pjmedia_transport *tp,
+					   pj_pool_t *pool,
+					   pjmedia_sdp_session *sdp)
 {
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
@@ -203,5 +199,5 @@
     sdp->attr[sdp->attr_count++] = attr;
 
-    /* Add all candidates */
+    /* Add all candidates (to media level) */
     cand_cnt = pj_ice_get_cand_cnt(tp_ice->ice_st->ice);
     for (i=0; i<cand_cnt; ++i) {
@@ -256,5 +252,5 @@
 	value = pj_str(buffer);
 	attr = pjmedia_sdp_attr_create(pool, "candidate", &value);
-	sdp->attr[sdp->attr_count++] = attr;
+	sdp->media[0]->attr[sdp->media[0]->attr_count++] = attr;
     }
 
@@ -378,8 +374,8 @@
     /* Get all candidates */
     cand_cnt = 0;
-    for (i=0; i<rem_sdp->attr_count; ++i) {
+    for (i=0; i<rem_sdp->media[0]->attr_count; ++i) {
 	pjmedia_sdp_attr *attr;
 
-	attr = rem_sdp->attr[i];
+	attr = rem_sdp->media[0]->attr[i];
 	if (pj_strcmp2(&attr->name, "candidate")!=0)
 	    continue;
@@ -442,5 +438,5 @@
 
 static pj_status_t tp_attach( pjmedia_transport *tp,
-			      void *user_data,
+			      void *stream,
 			      const pj_sockaddr_t *rem_addr,
 			      const pj_sockaddr_t *rem_rtcp,
@@ -455,5 +451,5 @@
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
 
-    tp_ice->user_data = user_data;
+    tp_ice->stream = stream;
     tp_ice->rtp_cb = rtp_cb;
     tp_ice->rtcp_cb = rtcp_cb;
@@ -474,5 +470,5 @@
     tp_ice->rtp_cb = NULL;
     tp_ice->rtcp_cb = NULL;
-    tp_ice->user_data = NULL;
+    tp_ice->stream = NULL;
 
     PJ_UNUSED_ARG(strm);
@@ -515,7 +511,7 @@
 
     if (comp_id==1 && tp_ice->rtp_cb)
-	(*tp_ice->rtp_cb)(tp_ice->user_data, pkt, size);
+	(*tp_ice->rtp_cb)(tp_ice->stream, pkt, size);
     else if (comp_id==2 && tp_ice->rtcp_cb)
-	(*tp_ice->rtcp_cb)(tp_ice->user_data, pkt, size);
+	(*tp_ice->rtcp_cb)(tp_ice->stream, pkt, size);
 
     PJ_UNUSED_ARG(cand_id);
Index: /pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c
===================================================================
--- /pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 1097)
+++ /pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 1098)
@@ -240,4 +240,5 @@
     pj_ansi_strcpy(tp->base.name, name);
     tp->base.op = &transport_udp_op;
+    tp->base.type = PJMEDIA_TRANSPORT_TYPE_UDP;
 
     /* Copy socket infos */
Index: /pjproject/trunk/pjnath/include/pjnath/ice_stream_transport.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/ice_stream_transport.h	(revision 1097)
+++ /pjproject/trunk/pjnath/include/pjnath/ice_stream_transport.h	(revision 1098)
@@ -72,4 +72,5 @@
     pj_ice_st		*ice_st;
     pj_ice_cand_type	 type;
+    pj_status_t		 status;
     unsigned		 comp_id;
     int			 cand_id;
@@ -151,5 +152,5 @@
 						   pj_bool_t notify,
 						   void *notify_data);
-
+PJ_DECL(pj_status_t) pj_ice_st_get_interfaces_status(pj_ice_st *ice_st);
 
 PJ_DECL(pj_status_t) pj_ice_st_init_ice(pj_ice_st *ice_st,
Index: /pjproject/trunk/pjnath/src/pjnath/ice.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1097)
+++ /pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1098)
@@ -889,4 +889,5 @@
 {
     if (!ice->is_complete) {
+	char errmsg[PJ_ERR_MSG_SIZE];
 
 	ice->is_complete = PJ_TRUE;
@@ -894,5 +895,7 @@
     
 	/* Log message */
-	LOG((ice->obj_name, "ICE process complete"));
+	LOG((ice->obj_name, "ICE process complete, status=%s", 
+	     pj_strerror(status, errmsg, sizeof(errmsg)).ptr));
+
 	dump_checklist("Dumping checklist", ice, &ice->clist);
 	dump_valid_list("Dumping valid list", ice);
@@ -1137,5 +1140,5 @@
     dump_checklist("Checklist created:", ice, clist);
 
-    pj_mutex_lock(ice->mutex);
+    pj_mutex_unlock(ice->mutex);
 
     return PJ_SUCCESS;
Index: /pjproject/trunk/pjnath/src/pjnath/ice_stream_transport.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_stream_transport.c	(revision 1097)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_stream_transport.c	(revision 1098)
@@ -152,5 +152,4 @@
 }
 
-
 /* 
  * This is callback called by ioqueue on incoming packet 
@@ -192,5 +191,4 @@
 }
 
-
 /* 
  * Destroy an interface 
@@ -207,5 +205,4 @@
     }
 }
-
 
 /* 
@@ -242,5 +239,4 @@
 }
 
-
 static void destroy_ice_st(pj_ice_st *ice_st, pj_status_t reason)
 {
@@ -274,5 +270,4 @@
 }
 
-
 /*
  * Destroy ICE stream transport.
@@ -283,5 +278,4 @@
     return PJ_SUCCESS;
 }
-
 
 /*
@@ -301,5 +295,4 @@
 }
 
-
 /*
  * Set STUN server address.
@@ -320,5 +313,4 @@
 }
 
-
 /*
  * Add new component.
@@ -350,5 +342,4 @@
     return PJ_SUCCESS;
 }
-
 
 /* Add interface */
@@ -406,7 +397,9 @@
     add_interface(ice_st, is, p_itf_id, notify, notify_data);
 
-    return PJ_SUCCESS;
-}
-
+    /* Set interface status to SUCCESS */
+    is->status = PJ_SUCCESS;
+
+    return PJ_SUCCESS;
+}
 
 /*
@@ -434,5 +427,4 @@
 					NULL, notify, notify_data);
 }
-
 
 /*
@@ -454,5 +446,4 @@
 }
 
-
 /*
  * Add TURN mapping interface.
@@ -473,4 +464,23 @@
 }
 
+PJ_DEF(pj_status_t) pj_ice_st_get_interfaces_status(pj_ice_st *ice_st)
+{
+    unsigned i;
+    pj_status_t worst = PJ_SUCCESS;
+
+    for (i=0; i<ice_st->itf_cnt; ++i) {
+	pj_ice_st_interface *itf = ice_st->itfs[i];
+
+	if (itf->status == PJ_SUCCESS) {
+	    /* okay */
+	} else if (itf->status == PJ_EPENDING && worst==PJ_SUCCESS) {
+	    worst = itf->status;
+	} else {
+	    worst = itf->status;
+	}
+    }
+
+    return worst;
+}
 
 /*
@@ -538,5 +548,4 @@
 }
 
-
 /*
  * Enum candidates
@@ -568,5 +577,4 @@
     return PJ_SUCCESS;
 }
-
 
 /*
@@ -589,5 +597,4 @@
 }
 
-
 /*
  * Stop ICE!
@@ -602,5 +609,4 @@
     return PJ_SUCCESS;
 }
-
 
 /*
@@ -618,6 +624,4 @@
 }
 
-
-
 /*
  * Callback called by ICE session when ICE processing is complete, either
@@ -631,5 +635,4 @@
     }
 }
-
 
 /*
@@ -668,5 +671,4 @@
 }
 
-
 /*
  * Callback called by ICE session when it receives application data.
Index: /pjproject/trunk/pjsip-apps/build/pjsua.dsp
===================================================================
--- /pjproject/trunk/pjsip-apps/build/pjsua.dsp	(revision 1097)
+++ /pjproject/trunk/pjsip-apps/build/pjsua.dsp	(revision 1098)
@@ -43,5 +43,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "NDEBUG"
@@ -69,5 +69,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_CONSOLE" /D "_MBCS" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../../pjsip/include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../../pjmedia/include" /I "../../pjnath/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_CONSOLE" /D "_MBCS" /FR /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
Index: /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1097)
+++ /pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1098)
@@ -145,6 +145,5 @@
     puts  ("  --outbound=url      Set the URL of global outbound proxy server");
     puts  ("                      May be specified multiple times");
-    puts  ("  --use-stun1=FORMAT  where FORMAT=host[:port]");
-    puts  ("  --use-stun2=FORMAT  Resolve local IP with the specified STUN servers");
+    puts  ("  --stun-srv=name     Set STUN server host or domain");
     puts  ("");
     puts  ("TLS Options:");
@@ -160,4 +159,5 @@
     puts  ("");
     puts  ("Media Options:");
+    puts  ("  --use-ice           Enable ICE (default:no)");
     puts  ("  --add-codec=name    Manually add codec (default is to enable all)");
     puts  ("  --clock-rate=N      Override sound device clock rate");
@@ -326,8 +326,8 @@
 	   OPT_REGISTRAR, OPT_REG_TIMEOUT, OPT_PUBLISH, OPT_ID, OPT_CONTACT,
 	   OPT_REALM, OPT_USERNAME, OPT_PASSWORD,
-	   OPT_NAMESERVER, OPT_USE_STUN1, OPT_USE_STUN2, 
+	   OPT_NAMESERVER, OPT_STUN_SRV,
 	   OPT_ADD_BUDDY, OPT_OFFER_X_MS_MSG, OPT_NO_PRESENCE,
 	   OPT_AUTO_ANSWER, OPT_AUTO_HANGUP, OPT_AUTO_PLAY, OPT_AUTO_LOOP,
-	   OPT_AUTO_CONF, OPT_CLOCK_RATE,
+	   OPT_AUTO_CONF, OPT_CLOCK_RATE, OPT_USE_ICE,
 	   OPT_PLAY_FILE, OPT_PLAY_TONE, OPT_RTP_PORT, OPT_ADD_CODEC, 
 	   OPT_ILBC_MODE, OPT_REC_FILE, OPT_AUTO_REC,
@@ -367,6 +367,5 @@
 	{ "password",	1, 0, OPT_PASSWORD},
 	{ "nameserver", 1, 0, OPT_NAMESERVER},
-	{ "use-stun1",  1, 0, OPT_USE_STUN1},
-	{ "use-stun2",  1, 0, OPT_USE_STUN2},
+	{ "stun-srv",   1, 0, OPT_STUN_SRV},
 	{ "add-buddy",  1, 0, OPT_ADD_BUDDY},
 	{ "offer-x-ms-msg",0,0,OPT_OFFER_X_MS_MSG},
@@ -382,4 +381,5 @@
 	{ "rec-file",   1, 0, OPT_REC_FILE},
 	{ "rtp-port",	1, 0, OPT_RTP_PORT},
+	{ "use-ice",    0, 0, OPT_USE_ICE},
 	{ "add-codec",  1, 0, OPT_ADD_CODEC},
 	{ "complexity",	1, 0, OPT_COMPLEXITY},
@@ -442,5 +442,4 @@
     pj_optind = 0;
     while((c=pj_getopt_long(argc,argv, "", long_options,&option_index))!=-1) {
-	char *p;
 	pj_str_t tmp;
 	long lval;
@@ -633,39 +632,6 @@
 	    break;
 
-	case OPT_USE_STUN1:   /* STUN server 1 */
-	    p = pj_ansi_strchr(pj_optarg, ':');
-	    if (p) {
-		*p = '\0';
-		cfg->udp_cfg.stun_config.stun_srv1 = pj_str(pj_optarg);
-		cfg->udp_cfg.stun_config.stun_port1 = pj_strtoul(pj_cstr(&tmp, p+1));
-		if (cfg->udp_cfg.stun_config.stun_port1 < 1 || cfg->udp_cfg.stun_config.stun_port1 > 65535) {
-		    PJ_LOG(1,(THIS_FILE, 
-			      "Error: expecting port number with "
-			      "option --use-stun1"));
-		    return PJ_EINVAL;
-		}
-	    } else {
-		cfg->udp_cfg.stun_config.stun_port1 = 3478;
-		cfg->udp_cfg.stun_config.stun_srv1 = pj_str(pj_optarg);
-	    }
-	    cfg->udp_cfg.use_stun = PJ_TRUE;
-	    break;
-
-	case OPT_USE_STUN2:   /* STUN server 2 */
-	    p = pj_ansi_strchr(pj_optarg, ':');
-	    if (p) {
-		*p = '\0';
-		cfg->udp_cfg.stun_config.stun_srv2 = pj_str(pj_optarg);
-		cfg->udp_cfg.stun_config.stun_port2 = pj_strtoul(pj_cstr(&tmp,p+1));
-		if (cfg->udp_cfg.stun_config.stun_port2 < 1 || cfg->udp_cfg.stun_config.stun_port2 > 65535) {
-		    PJ_LOG(1,(THIS_FILE, 
-			      "Error: expecting port number with "
-			      "option --use-stun2"));
-		    return PJ_EINVAL;
-		}
-	    } else {
-		cfg->udp_cfg.stun_config.stun_port2 = 3478;
-		cfg->udp_cfg.stun_config.stun_srv2 = pj_str(pj_optarg);
-	    }
+	case OPT_STUN_SRV:   /* STUN server */
+	    cfg->cfg.stun_srv = pj_str(pj_optarg);
 	    break;
 
@@ -727,4 +693,8 @@
 	case OPT_REC_FILE:
 	    cfg->rec_file = pj_str(pj_optarg);
+	    break;
+
+	case OPT_USE_ICE:
+	    cfg->media_cfg.enable_ice = PJ_TRUE;
 	    break;
 
@@ -1114,19 +1084,11 @@
 
     /* STUN */
-    if (config->udp_cfg.stun_config.stun_port1) {
-	pj_ansi_sprintf(line, "--use-stun1 %.*s:%d\n",
-			(int)config->udp_cfg.stun_config.stun_srv1.slen, 
-			config->udp_cfg.stun_config.stun_srv1.ptr, 
-			config->udp_cfg.stun_config.stun_port1);
+    if (config->cfg.stun_srv.slen) {
+	pj_ansi_sprintf(line, "--stun-srv %.*s\n",
+			(int)config->cfg.stun_srv.slen, 
+			config->cfg.stun_srv.ptr);
 	pj_strcat2(&cfg, line);
     }
 
-    if (config->udp_cfg.stun_config.stun_port2) {
-	pj_ansi_sprintf(line, "--use-stun2 %.*s:%d\n",
-			(int)config->udp_cfg.stun_config.stun_srv2.slen, 
-			config->udp_cfg.stun_config.stun_srv2.ptr, 
-			config->udp_cfg.stun_config.stun_port2);
-	pj_strcat2(&cfg, line);
-    }
 
     /* TLS */
@@ -1175,4 +1137,6 @@
 
     /* Media */
+    if (config->media_cfg.enable_ice)
+	pj_strcat2(&cfg, "--use-ice\n");
     if (config->null_audio)
 	pj_strcat2(&cfg, "--null-audio\n");
@@ -2963,9 +2927,4 @@
 	return status;
 
-    /* Copy udp_cfg STUN config to rtp_cfg */
-    app_config.rtp_cfg.use_stun = app_config.udp_cfg.use_stun;
-    app_config.rtp_cfg.stun_config = app_config.udp_cfg.stun_config;
-
-
     /* Initialize application callbacks */
     app_config.cfg.cb.on_call_state = &on_call_state;
Index: /pjproject/trunk/pjsip/build/pjsua_lib.dsp
===================================================================
--- /pjproject/trunk/pjsip/build/pjsua_lib.dsp	(revision 1097)
+++ /pjproject/trunk/pjsip/build/pjsua_lib.dsp	(revision 1098)
@@ -42,5 +42,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_MBCS" /D "_LIB" /YX /FD /c
-# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /I "../../pjnath/include" /D "NDEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "NDEBUG"
@@ -66,5 +66,5 @@
 # PROP Target_Dir ""
 # ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_MBCS" /D "_LIB" /YX /FD /GZ /c
-# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjmedia/include" /I "../../pjlib-util/include" /I "../../pjlib/include" /I "../../pjnath/include" /D "_DEBUG" /D PJ_WIN32=1 /D PJ_M_I386=1 /D "WIN32" /D "_MBCS" /D "_LIB" /FR /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
Index: /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h	(revision 1097)
+++ /pjproject/trunk/pjsip/include/pjsip/sip_endpoint.h	(revision 1098)
@@ -157,4 +157,13 @@
 					pj_timer_entry *entry );
 
+/**
+ * Get the timer heap instance of the SIP endpoint.
+ *
+ * @param endpt	    The endpoint.
+ *
+ * @return	    The timer heap instance.
+ */
+PJ_DECL(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt);
+
 
 /**
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 1097)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 1098)
@@ -903,4 +903,12 @@
     pj_str_t	    outbound_proxy[4];
 
+    /**
+     * Specify STUN server. This server will be first resolved with DNS SRV
+     * to get the actual server address. If DNS SRV resolution failed, or
+     * when nameserver is not configured, the server will be resolved using
+     * DNS A resolution (i.e. gethostbyname()).
+     */
+    pj_str_t	    stun_srv;
+
     /** 
      * Number of credentials in the credential array.
@@ -1002,4 +1010,5 @@
 
     pj_strdup_with_null(pool, &dst->user_agent, &src->user_agent);
+    pj_strdup_with_null(pool, &dst->stun_srv, &src->stun_srv);
 }
 
@@ -1335,58 +1344,4 @@
 
 /**
- * This structure describes STUN configuration for SIP and media transport,
- * and is embedded inside pjsua_transport_config structure.
- */
-typedef struct pjsua_stun_config
-{
-    /**
-     * The first STUN server IP address or hostname.
-     */
-    pj_str_t	stun_srv1;
-
-    /**
-     * Port number of the first STUN server.
-     * If zero, default STUN port will be used.
-     */
-    unsigned	stun_port1;
-    
-    /**
-     * Optional second STUN server IP address or hostname, for which the
-     * result of the mapping request will be compared to. If the value
-     * is empty, only one STUN server will be used.
-     */
-    pj_str_t	stun_srv2;
-
-    /**
-     * Port number of the second STUN server.
-     * If zero, default STUN port will be used.
-     */
-    unsigned	stun_port2;
-
-} pjsua_stun_config;
-
-
-
-/**
- * Call this function to initialize STUN config with default values.
- * STUN config is normally embedded inside pjsua_transport_config, so
- * normally there is no need to call this function and rather just
- * call pjsua_transport_config_default() instead.
- *
- * @param cfg	    The STUN config to be initialized.
- *
- * \par Python:
- * The corresponding Python function creates and initialize the config:
- * \code
-    stun_cfg = py_pjsua.stun_config_default()
- * \endcode
- */
-PJ_INLINE(void) pjsua_stun_config_default(pjsua_stun_config *cfg)
-{
-    pj_bzero(cfg, sizeof(*cfg));
-}
-
-
-/**
  * Transport configuration for creating transports for both SIP
  * and media. Before setting some values to this structure, application
@@ -1436,14 +1391,4 @@
 
     /**
-     * Flag to indicate whether STUN should be used.
-     */
-    pj_bool_t		use_stun;
-
-    /**
-     * STUN configuration, must be specified when STUN is used.
-     */
-    pjsua_stun_config	stun_config;
-
-    /**
      * This specifies TLS settings for TLS transport. It is only be used
      * when this transport config is being used to create a SIP TLS
@@ -1469,40 +1414,5 @@
 {
     pj_bzero(cfg, sizeof(*cfg));
-    pjsua_stun_config_default(&cfg->stun_config);
     pjsip_tls_setting_default(&cfg->tls_setting);
-}
-
-
-/**
- * This is a utility function to normalize STUN config. It's only
- * used internally by the library.
- *
- * @param cfg	    The STUN config to be initialized.
- *
- * \par Python:
- * \code
-    py_pjsua.normalize_stun_config(cfg)
- * \code
- */
-PJ_INLINE(void) pjsua_normalize_stun_config( pjsua_stun_config *cfg )
-{
-    if (cfg->stun_srv1.slen) {
-
-	if (cfg->stun_port1 == 0)
-	    cfg->stun_port1 = 3478;
-
-	if (cfg->stun_srv2.slen == 0) {
-	    cfg->stun_srv2 = cfg->stun_srv1;
-	    cfg->stun_port2 = cfg->stun_port1;
-	} else {
-	    if (cfg->stun_port2 == 0)
-		cfg->stun_port2 = 3478;
-	}
-
-    } else {
-	cfg->stun_port1 = 0;
-	cfg->stun_srv2.slen = 0;
-	cfg->stun_port2 = 0;
-    }
 }
 
@@ -1523,17 +1433,6 @@
 					   const pjsua_transport_config *src)
 {
+    PJ_UNUSED_ARG(pool);
     pj_memcpy(dst, src, sizeof(*src));
-
-    if (src->stun_config.stun_srv1.slen) {
-	pj_strdup_with_null(pool, &dst->stun_config.stun_srv1,
-			    &src->stun_config.stun_srv1);
-    }
-
-    if (src->stun_config.stun_srv2.slen) {
-	pj_strdup_with_null(pool, &dst->stun_config.stun_srv2,
-			    &src->stun_config.stun_srv2);
-    }
-
-    pjsua_normalize_stun_config(&dst->stun_config);
 }
 
@@ -3531,4 +3430,13 @@
     int			jb_max;
 
+    /**
+     * Enable ICE
+     */
+    pj_bool_t		enable_ice;
+
+    /**
+     * Enable ICE media relay.
+     */
+    pj_bool_t		enable_relay;
 };
 
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 1097)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 1098)
@@ -187,4 +187,10 @@
     pj_thread_t		*thread[4];	    /**< Array of threads.	*/
 
+    /* STUN and resolver */
+    pj_stun_config	 stun_cfg;  /**< Global STUN settings.		*/
+    pj_sockaddr		 stun_srv;  /**< Resolved STUN server address	*/
+    pj_status_t		 stun_status; /**< STUN server status.		*/
+    pj_dns_resolver	*resolver;  /**< DNS resolver.			*/
+
     /* Account: */
     unsigned		 acc_cnt;	     /**< Number of accounts.	*/
@@ -271,4 +277,8 @@
 
 
+/**
+ * Resolve STUN server.
+ */
+pj_status_t pjsua_resolve_stun_server(pj_bool_t wait);
 
 /**
@@ -276,4 +286,18 @@
  */
 pj_bool_t pjsua_call_on_incoming(pjsip_rx_data *rdata);
+
+/*
+ * Media channel.
+ */
+pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
+				    pjsip_role_e role);
+pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, 
+					   pj_pool_t *pool,
+					   pjmedia_sdp_session **p_sdp);
+pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
+				       pjmedia_sdp_session *local_sdp,
+				       pjmedia_sdp_session *remote_sdp);
+pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id);
+
 
 /**
Index: /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 1097)
+++ /pjproject/trunk/pjsip/src/pjsip/sip_endpoint.c	(revision 1098)
@@ -759,4 +759,12 @@
 
 /*
+ * Get the timer heap instance of the SIP endpoint.
+ */
+PJ_DEF(pj_timer_heap_t*) pjsip_endpt_get_timer_heap(pjsip_endpoint *endpt)
+{
+    return endpt->timer_heap;
+}
+
+/*
  * This is the callback that is called by the transport manager when it 
  * receives a message from the network.
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 1097)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 1098)
@@ -61,6 +61,4 @@
 
 
-/* Destroy the call's media */
-static pj_status_t call_destroy_media(int call_id);
 
 /* Create inactive SDP for call hold. */
@@ -303,8 +301,13 @@
     }
 
-    /* Get media capability from media endpoint: */
-
-    status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, dlg->pool, 1, 
-				       &call->skinfo, &offer);
+    /* Init media channel */
+    status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAC);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error initializing media channel", status);
+	goto on_error;
+    }
+
+    /* Create SDP offer */
+    status = pjsua_media_channel_create_sdp(call->index, dlg->pool, &offer);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "pjmedia unable to create SDP", status);
@@ -404,4 +407,5 @@
     if (call_id != -1) {
 	reset_call(call_id);
+	pjsua_media_channel_deinit(call_id);
     }
 
@@ -527,8 +531,6 @@
 
 
-    /* Get media capability from media endpoint: */
-    status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, 
-				       rdata->tp_info.pool, 1,
-				       &call->skinfo, &answer );
+    /* Init media channel */
+    status = pjsua_media_channel_init(call->index, PJSIP_ROLE_UAS);
     if (status != PJ_SUCCESS) {
 	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
@@ -539,4 +541,15 @@
 
 
+    /* Get media capability from media endpoint: */
+    status = pjsua_media_channel_create_sdp(call->index, rdata->tp_info.pool, &answer);
+    if (status != PJ_SUCCESS) {
+	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
+				      NULL, NULL);
+	pjsua_media_channel_deinit(call->index);
+	PJSUA_UNLOCK();
+	return PJ_TRUE;
+    }
+
+
     /* Verify that we can handle the request. */
     status = pjsip_inv_verify_request(rdata, &options, answer, NULL,
@@ -562,4 +575,5 @@
 	}
 
+	pjsua_media_channel_deinit(call->index);
 	PJSUA_UNLOCK();
 	return PJ_TRUE;
@@ -581,4 +595,5 @@
 	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
 				      NULL, NULL);
+	pjsua_media_channel_deinit(call->index);
 	PJSUA_UNLOCK();
 	return PJ_TRUE;
@@ -591,4 +606,5 @@
 	pjsip_endpt_respond_stateless(pjsua_var.endpt, rdata, 500, NULL,
 				      NULL, NULL);
+	pjsua_media_channel_deinit(call->index);
 	PJSUA_UNLOCK();
 	return PJ_TRUE;
@@ -619,4 +635,5 @@
 	pjsip_dlg_terminate(dlg);
 	 */
+	pjsua_media_channel_deinit(call->index);
 	PJSUA_UNLOCK();
 	return PJ_TRUE;
@@ -650,4 +667,5 @@
 	pjsip_dlg_respond(dlg, rdata, 500, NULL, NULL, NULL);
 	pjsip_inv_terminate(inv, 500, PJ_FALSE);
+	pjsua_media_channel_deinit(call->index);
 	PJSUA_UNLOCK();
 	return PJ_TRUE;
@@ -655,6 +673,7 @@
     } else {
 	status = pjsip_inv_send_msg(inv, response);
-	if (status != PJ_SUCCESS)
+	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Unable to send 100 response", status);
+	}
     }
 
@@ -1192,6 +1211,5 @@
     PJ_UNUSED_ARG(unhold);
     PJ_TODO(create_active_inactive_sdp_based_on_unhold_arg);
-    status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, call->inv->pool, 
-				       1, &call->skinfo, &sdp);
+    status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &sdp);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to get SDP from media endpoint", 
@@ -1886,32 +1904,4 @@
 
 /*
- * Destroy the call's media
- */
-static pj_status_t call_destroy_media(int call_id)
-{
-    pjsua_call *call = &pjsua_var.calls[call_id];
-
-    if (call->conf_slot != PJSUA_INVALID_ID) {
-	pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
-	call->conf_slot = PJSUA_INVALID_ID;
-    }
-
-    if (call->session) {
-	/* Destroy session (this will also close RTP/RTCP sockets). */
-	pjmedia_session_destroy(call->session);
-	call->session = NULL;
-
-	PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed", 
-			     call_id));
-
-    }
-
-    call->media_st = PJSUA_CALL_MEDIA_NONE;
-
-    return PJ_SUCCESS;
-}
-
-
-/*
  * This callback receives notification from invite session when the
  * session state has changed.
@@ -2035,5 +2025,5 @@
 
 	if (call)
-	    call_destroy_media(call->index);
+	    pjsua_media_channel_deinit(call->index);
 
 	/* Free call */
@@ -2078,20 +2068,4 @@
 
 /*
- * DTMF callback from the stream.
- */
-static void dtmf_callback(pjmedia_stream *strm, void *user_data,
-			  int digit)
-{
-    PJ_UNUSED_ARG(strm);
-
-    if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
-	pjsua_call_id call_id;
-
-	call_id = (pjsua_call_id)user_data;
-	pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
-    }
-}
-
-/*
  * Callback to be called when SDP offer/answer negotiation has just completed
  * in the session. This function will start/update media if negotiation
@@ -2101,12 +2075,7 @@
 				       pj_status_t status)
 {
-    int prev_media_st = 0;
     pjsua_call *call;
-    pjmedia_session_info sess_info;
-    const pjmedia_sdp_session *local_sdp;
-    const pjmedia_sdp_session *remote_sdp;
-    pjmedia_port *media_port;
-    pj_str_t port_name;
-    char tmp[PJSIP_MAX_URL_SIZE];
+    pjmedia_sdp_session *local_sdp;
+    pjmedia_sdp_session *remote_sdp;
 
     PJSUA_LOCK();
@@ -2119,5 +2088,5 @@
 
 	/* Stop/destroy media, if any */
-	call_destroy_media(call->index);
+	pjsua_media_channel_deinit(call->index);
 
 	/* Disconnect call if we're not in the middle of initializing an
@@ -2134,13 +2103,6 @@
     }
 
-    /* Destroy existing media session, if any. */
-
-    if (call) {
-	prev_media_st = call->media_st;
-	call_destroy_media(call->index);
-    }
 
     /* Get local and remote SDP */
-
     status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
     if (status != PJ_SUCCESS) {
@@ -2164,153 +2126,14 @@
     }
 
-    /* Create media session info based on SDP parameters. 
-     * We only support one stream per session at the moment
-     */    
-    status = pjmedia_session_info_from_sdp( call->inv->dlg->pool, 
-					    pjsua_var.med_endpt, 
-					    1,&sess_info, 
-					    local_sdp, remote_sdp);
+    status = pjsua_media_channel_update(call->index, local_sdp, remote_sdp);
     if (status != PJ_SUCCESS) {
 	pjsua_perror(THIS_FILE, "Unable to create media session", 
 		     status);
 	call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
+	pjsua_media_channel_deinit(call->index);
 	PJSUA_UNLOCK();
 	return;
     }
 
-    /* Check if media is put on-hold */
-    if (sess_info.stream_cnt == 0 || 
-	sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
-    {
-
-	/* Determine who puts the call on-hold */
-	if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
-	    if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
-		/* It was local who offer hold */
-		call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
-	    } else {
-		call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
-	    }
-	}
-
-	call->media_dir = PJMEDIA_DIR_NONE;
-
-    } else {
-
-	/* Override ptime, if this option is specified. */
-	if (pjsua_var.media_cfg.ptime != 0) {
-	    sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
-		(pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
-	    if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
-		sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
-	}
-
-	/* Disable VAD, if this option is specified. */
-	if (pjsua_var.media_cfg.no_vad) {
-	    sess_info.stream_info[0].param->setting.vad = 0;
-	}
-
-
-	/* Optionally, application may modify other stream settings here
-	 * (such as jitter buffer parameters, codec ptime, etc.)
-	 */
-	sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init;
-	sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
-	sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
-	sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max;
-
-	/* Create session based on session info. */
-	status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
-					 &call->med_tp,
-					 call, &call->session );
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, "Unable to create media session", 
-			 status);
-	    //call_disconnect(inv, PJSIP_SC_UNSUPPORTED_MEDIA_TYPE);
-	    PJSUA_UNLOCK();
-	    return;
-	}
-
-	/* If DTMF callback is installed by application, install our
-	 * callback to the session.
-	 */
-	if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
-	    pjmedia_session_set_dtmf_callback(call->session, 0, 
-					      &dtmf_callback, 
-					      (void*)(call->index));
-	}
-
-	/* Get the port interface of the first stream in the session.
-	 * We need the port interface to add to the conference bridge.
-	 */
-	pjmedia_session_get_port(call->session, 0, &media_port);
-
-
-	/*
-	 * Add the call to conference bridge.
-	 */
-	port_name.ptr = tmp;
-	port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
-					 call->inv->dlg->remote.info->uri,
-					 tmp, sizeof(tmp));
-	if (port_name.slen < 1) {
-	    port_name = pj_str("call");
-	}
-	status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
-					media_port, 
-					&port_name,
-					(unsigned*)&call->conf_slot);
-	if (status != PJ_SUCCESS) {
-	    pjsua_perror(THIS_FILE, "Unable to create conference slot", 
-			 status);
-	    call_destroy_media(call->index);
-	    //call_disconnect(inv, PJSIP_SC_INTERNAL_SERVER_ERROR);
-	    PJSUA_UNLOCK();
-	    return;
-	}
-
-	/* Call's media state is active */
-	call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
-	call->media_dir = sess_info.stream_info[0].dir;
-    }
-
-    /* Print info. */
-    {
-	char info[80];
-	int info_len = 0;
-	unsigned i;
-
-	for (i=0; i<sess_info.stream_cnt; ++i) {
-	    int len;
-	    const char *dir;
-	    pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
-
-	    switch (strm_info->dir) {
-	    case PJMEDIA_DIR_NONE:
-		dir = "inactive";
-		break;
-	    case PJMEDIA_DIR_ENCODING:
-		dir = "sendonly";
-		break;
-	    case PJMEDIA_DIR_DECODING:
-		dir = "recvonly";
-		break;
-	    case PJMEDIA_DIR_ENCODING_DECODING:
-		dir = "sendrecv";
-		break;
-	    default:
-		dir = "unknown";
-		break;
-	    }
-	    len = pj_ansi_sprintf( info+info_len,
-				   ", stream #%d: %.*s (%s)", i,
-				   (int)strm_info->fmt.encoding_name.slen,
-				   strm_info->fmt.encoding_name.ptr,
-				   dir);
-	    if (len > 0)
-		info_len += len;
-	}
-	PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
-    }
 
     /* Call application callback, if any */
@@ -2414,7 +2237,5 @@
 	PJ_LOG(4,(THIS_FILE, "Call %d: received updated media offer",
 		  call->index));
-	status = pjmedia_endpt_create_sdp( pjsua_var.med_endpt, 
-					   call->inv->pool, 1,
-					   &call->skinfo, &answer);	
+	status = pjsua_media_channel_create_sdp(call->index, call->inv->pool, &answer);
     }
 
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 1097)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 1098)
@@ -43,4 +43,6 @@
     unsigned i;
 
+    pj_bzero(&pjsua_var, sizeof(pjsua_var));
+
     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.acc); ++i)
 	pjsua_var.acc[i].index = i;
@@ -48,4 +50,6 @@
     for (i=0; i<PJ_ARRAY_SIZE(pjsua_var.tpdata); ++i)
 	pjsua_var.tpdata[i].index = i;
+
+    pjsua_var.stun_status = PJ_EUNKNOWN;
 }
 
@@ -467,13 +471,14 @@
     if (ua_cfg->nameserver_count) {
 #if PJSIP_HAS_RESOLVER
-	pj_dns_resolver *resv;
 	unsigned i;
 
 	/* Create DNS resolver */
-	status = pjsip_endpt_create_resolver(pjsua_var.endpt, &resv);
+	status = pjsip_endpt_create_resolver(pjsua_var.endpt, 
+					     &pjsua_var.resolver);
 	PJ_ASSERT_RETURN(status == PJ_SUCCESS, status);
 
 	/* Configure nameserver for the DNS resolver */
-	status = pj_dns_resolver_set_ns(resv, ua_cfg->nameserver_count,
+	status = pj_dns_resolver_set_ns(pjsua_var.resolver, 
+					ua_cfg->nameserver_count,
 					ua_cfg->nameserver, NULL);
 	if (status != PJ_SUCCESS) {
@@ -483,5 +488,5 @@
 
 	/* Set this DNS resolver to be used by the SIP resolver */
-	status = pjsip_endpt_set_resolver(pjsua_var.endpt, resv);
+	status = pjsip_endpt_set_resolver(pjsua_var.endpt, pjsua_var.resolver);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Error setting DNS resolver", status);
@@ -551,4 +556,11 @@
 
 
+    /* Start resolving STUN server */
+    status = pjsua_resolve_stun_server(PJ_FALSE);
+    if (status != PJ_SUCCESS && status != PJ_EPENDING) {
+	pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
+	return status;
+    }
+
     /* Initialize PJSUA media subsystem */
     status = pjsua_media_subsys_init(media_cfg);
@@ -636,4 +648,47 @@
 	pj_gettimeofday(&now);
     } while (PJ_TIME_VAL_LT(now, timeout));
+}
+
+/*
+ * Resolve STUN server.
+ */
+pj_status_t pjsua_resolve_stun_server(pj_bool_t wait)
+{
+    if (pjsua_var.stun_status == PJ_EUNKNOWN) {
+	/* Initialize STUN configuration */
+	pj_stun_config_init(&pjsua_var.stun_cfg, &pjsua_var.cp.factory, 0,
+			    pjsip_endpt_get_ioqueue(pjsua_var.endpt),
+			    pjsip_endpt_get_timer_heap(pjsua_var.endpt));
+
+	/* Start STUN server resolution */
+	/* For now just do DNS A resolution */
+
+	if (pjsua_var.ua_cfg.stun_srv.slen == 0) {
+	    pjsua_var.stun_status = PJ_SUCCESS;
+	} else {
+	    pj_hostent he;
+
+	    pjsua_var.stun_status = 
+		pj_gethostbyname(&pjsua_var.ua_cfg.stun_srv, &he);
+
+	    if (pjsua_var.stun_status == PJ_SUCCESS) {
+		pj_sockaddr_in_init(&pjsua_var.stun_srv.ipv4, NULL, 0);
+		pjsua_var.stun_srv.ipv4.sin_addr = *(pj_in_addr*)he.h_addr;
+		pjsua_var.stun_srv.ipv4.sin_port = pj_htons((pj_uint16_t)3478);
+	    }
+	}
+	return pjsua_var.stun_status;
+
+    } else if (pjsua_var.stun_status == PJ_EPENDING) {
+	/* STUN server resolution has been started, wait for the
+	 * result.
+	 */
+	pj_assert(!"Should not happen");
+	return PJ_EBUG;
+
+    } else {
+	/* STUN server has been resolved, return the status */
+	return pjsua_var.stun_status;
+    }
 }
 
@@ -820,12 +875,18 @@
 static pj_status_t create_sip_udp_sock(pj_in_addr bound_addr,
 				       int port,
-				       pj_bool_t use_stun,
-				       const pjsua_stun_config *stun_param,
 				       pj_sock_t *p_sock,
 				       pj_sockaddr_in *p_pub_addr)
 {
-    pjsua_stun_config stun;
+    char ip_addr[32];
+    pj_str_t stun_srv;
     pj_sock_t sock;
     pj_status_t status;
+
+    /* Make sure STUN server resolution has completed */
+    status = pjsua_resolve_stun_server(PJ_TRUE);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
+	return status;
+    }
 
     status = pj_sock_socket(PJ_AF_INET, PJ_SOCK_DGRAM, 0, &sock);
@@ -857,10 +918,9 @@
     }
 
-    /* Copy and normalize STUN param */
-    if (use_stun) {
-	pj_memcpy(&stun, stun_param, sizeof(*stun_param));
-	pjsua_normalize_stun_config(&stun);
+    if (pjsua_var.stun_srv.addr.sa_family != 0) {
+	pj_ansi_strcpy(ip_addr,pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
+	stun_srv = pj_str(ip_addr);
     } else {
-	pj_bzero(&stun, sizeof(pjsua_stun_config));
+	stun_srv.slen = 0;
     }
 
@@ -868,14 +928,12 @@
      * the name of local host.
      */
-    if (stun.stun_srv1.slen) {
+    if (stun_srv.slen) {
 	/*
 	 * STUN is specified, resolve the address with STUN.
 	 */
 	status = pjstun_get_mapped_addr(&pjsua_var.cp.factory, 1, &sock,
-				         &stun.stun_srv1, 
-					  stun.stun_port1,
-					 &stun.stun_srv2, 
-					  stun.stun_port2,
-				          p_pub_addr);
+				         &stun_srv, 3478,
+					 &stun_srv, 3478,
+				         p_pub_addr);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Error resolving with STUN", status);
@@ -987,5 +1045,4 @@
 	 */
 	status = create_sip_udp_sock(bound_addr.sin_addr, cfg->port, 
-				     cfg->use_stun, &cfg->stun_config, 
 				     &sock, &pub_addr);
 	if (status != PJ_SUCCESS)
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1097)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1098)
@@ -216,4 +216,12 @@
     pj_sock_t sock[2];
 
+    /* Make sure STUN server resolution has completed */
+    status = pjsua_resolve_stun_server(PJ_TRUE);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error resolving STUN server", status);
+	return status;
+    }
+
+
     if (next_rtp_port == 0)
 	next_rtp_port = (pj_uint16_t)cfg->port;
@@ -273,10 +281,15 @@
 	 * and make sure that the mapped RTCP port is adjacent with the RTP.
 	 */
-	if (cfg->stun_config.stun_srv1.slen) {
+	if (pjsua_var.stun_srv.addr.sa_family != 0) {
+	    char ip_addr[32];
+	    pj_str_t stun_srv;
+
+	    pj_ansi_strcpy(ip_addr, 
+			   pj_inet_ntoa(pjsua_var.stun_srv.ipv4.sin_addr));
+	    stun_srv = pj_str(ip_addr);
+
 	    status=pjstun_get_mapped_addr(&pjsua_var.cp.factory, 2, sock,
-					   &cfg->stun_config.stun_srv1, 
-					   cfg->stun_config.stun_port1,
-					   &cfg->stun_config.stun_srv2, 
-					   cfg->stun_config.stun_port2,
+					   &stun_srv, 3478,
+					   &stun_srv, 3478,
 					   mapped_addr);
 	    if (status != PJ_SUCCESS) {
@@ -489,37 +502,14 @@
 
 
-/*
- * Create UDP media transports for all the calls. This function creates
- * one UDP media transport for each call.
- */
-PJ_DEF(pj_status_t) 
-pjsua_media_transports_create(const pjsua_transport_config *app_cfg)
-{
-    pjsua_transport_config cfg;
+/* Create normal UDP media transports */
+static pj_status_t create_udp_media_transports(pjsua_transport_config *cfg)
+{
     unsigned i;
     pj_status_t status;
 
-
-    /* Make sure pjsua_init() has been called */
-    PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
-
-    PJSUA_LOCK();
-
-    /* Delete existing media transports */
-    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
-	if (pjsua_var.calls[i].med_tp != NULL) {
-	    pjsua_var.calls[i].med_tp->op->destroy(pjsua_var.calls[i].med_tp);
-	    pjsua_var.calls[i].med_tp = NULL;
-	}
-    }
-
-    /* Copy config */
-    pj_memcpy(&cfg, app_cfg, sizeof(*app_cfg));
-    pjsua_normalize_stun_config(&cfg.stun_config);
-
     /* Create each media transport */
     for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
 
-	status = create_rtp_rtcp_sock(&cfg, &pjsua_var.calls[i].skinfo);
+	status = create_rtp_rtcp_sock(cfg, &pjsua_var.calls[i].skinfo);
 	if (status != PJ_SUCCESS) {
 	    pjsua_perror(THIS_FILE, "Unable to create RTP/RTCP socket",
@@ -546,6 +536,4 @@
     }
 
-    PJSUA_UNLOCK();
-
     return PJ_SUCCESS;
 
@@ -553,12 +541,418 @@
     for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
 	if (pjsua_var.calls[i].med_tp != NULL) {
-	    pjsua_var.calls[i].med_tp->op->destroy(pjsua_var.calls[i].med_tp);
+	    pjmedia_transport_close(pjsua_var.calls[i].med_tp);
 	    pjsua_var.calls[i].med_tp = NULL;
 	}
     }
 
+    return status;
+}
+
+
+/* Create ICE media transports (when ice is enabled) */
+static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
+{
+    unsigned i;
+    pj_status_t status;
+
+    /* Create each media transport */
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+	pj_ice_st *ice_st;
+
+	status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, 1,
+				    &pjsua_var.stun_cfg, 
+				    &pjsua_var.calls[i].med_tp);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Unable to create ICE media transport",
+		         status);
+	    goto on_error;
+	}
+
+	ice_st = pjmedia_ice_get_ice_st(pjsua_var.calls[i].med_tp);
+
+	/* Add host candidates for RTP */
+	status = pj_ice_st_add_all_host_interfaces(ice_st, 1, 0, 
+						   PJ_FALSE, NULL);
+	if (status != PJ_SUCCESS) {
+	    pjsua_perror(THIS_FILE, "Error adding ICE host candidates",
+		         status);
+	    goto on_error;
+	}
+
+	/* Configure STUN server */
+	if (pjsua_var.stun_srv.addr.sa_family != 0) {
+	
+	    status = pj_ice_st_set_stun_addr(ice_st, 
+					     pjsua_var.media_cfg.enable_relay,
+					     &pjsua_var.stun_srv.ipv4);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, "Error setting ICE's STUN server",
+			     status);
+		goto on_error;
+	    }
+
+	    /* Add STUN server reflexive candidate for RTP */
+	    status = pj_ice_st_add_stun_interface(ice_st, 1, 0, 
+						  PJ_FALSE, NULL);
+	    if (status != PJ_SUCCESS) {
+		pjsua_perror(THIS_FILE, "Error adding ICE address",
+			     status);
+		goto on_error;
+	    }
+	}
+    }
+
+    /* Wait until all ICE transports are ready */
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+	pj_ice_st *ice_st;
+
+	ice_st = pjmedia_ice_get_ice_st(pjsua_var.calls[i].med_tp);
+
+	/* Wait until interface status is PJ_SUCCESS */
+	for (;;) {
+	    status = pj_ice_st_get_interfaces_status(ice_st);
+	    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;
+	}
+
+	/* Get transport info */
+	pjmedia_transport_get_info(pjsua_var.calls[i].med_tp,
+				   &pjsua_var.calls[i].skinfo);
+
+    }
+
+    return PJ_SUCCESS;
+
+on_error:
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+	if (pjsua_var.calls[i].med_tp != NULL) {
+	    pjmedia_transport_close(pjsua_var.calls[i].med_tp);
+	    pjsua_var.calls[i].med_tp = NULL;
+	}
+    }
+
+    return status;
+}
+
+
+/*
+ * Create UDP media transports for all the calls. This function creates
+ * one UDP media transport for each call.
+ */
+PJ_DEF(pj_status_t) 
+pjsua_media_transports_create(const pjsua_transport_config *app_cfg)
+{
+    pjsua_transport_config cfg;
+    unsigned i;
+    pj_status_t status;
+
+
+    /* Make sure pjsua_init() has been called */
+    PJ_ASSERT_RETURN(pjsua_var.ua_cfg.max_calls>0, PJ_EINVALIDOP);
+
+    PJSUA_LOCK();
+
+    /* Delete existing media transports */
+    for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
+	if (pjsua_var.calls[i].med_tp != NULL) {
+	    pjmedia_transport_close(pjsua_var.calls[i].med_tp);
+	    pjsua_var.calls[i].med_tp = NULL;
+	}
+    }
+
+    /* Copy config */
+    pjsua_transport_config_dup(pjsua_var.pool, &cfg, app_cfg);
+
+    if (pjsua_var.media_cfg.enable_ice) {
+	status = create_ice_media_transports(&cfg);
+    } else {
+	status = create_udp_media_transports(&cfg);
+    }
+
+
     PJSUA_UNLOCK();
 
     return status;
+}
+
+
+pj_status_t pjsua_media_channel_init(pjsua_call_id call_id,
+				     pjsip_role_e role)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+
+    if (pjsua_var.media_cfg.enable_ice) {
+	pj_ice_role ice_role;
+	pj_status_t status;
+
+	ice_role = (role==PJSIP_ROLE_UAC ? PJ_ICE_ROLE_CONTROLLING : 
+				           PJ_ICE_ROLE_CONTROLLED);
+
+	/* Restart ICE */
+	pjmedia_ice_stop_ice(call->med_tp);
+
+	status = pjmedia_ice_init_ice(call->med_tp, ice_role, NULL, NULL);
+	if (status != PJ_SUCCESS)
+	    return status;
+    }
+
+    return PJ_SUCCESS;
+}
+
+pj_status_t pjsua_media_channel_create_sdp(pjsua_call_id call_id, 
+					   pj_pool_t *pool,
+					   pjmedia_sdp_session **p_sdp)
+{
+    pjmedia_sdp_session *sdp;
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pj_status_t status;
+
+    status = pjmedia_endpt_create_sdp(pjsua_var.med_endpt, pool, 1,
+				      &call->skinfo, &sdp);
+    if (status != PJ_SUCCESS)
+	goto on_error;
+
+    if (pjsua_var.media_cfg.enable_ice) {
+	status = pjmedia_ice_modify_sdp(call->med_tp, pool, sdp);
+	if (status != PJ_SUCCESS)
+	    goto on_error;
+    }
+
+    *p_sdp = sdp;
+    return PJ_SUCCESS;
+
+on_error:
+    pjsua_media_channel_deinit(call_id);
+    return status;
+
+}
+
+
+static void stop_media_session(pjsua_call_id call_id)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+
+    if (call->conf_slot != PJSUA_INVALID_ID) {
+	pjmedia_conf_remove_port(pjsua_var.mconf, call->conf_slot);
+	call->conf_slot = PJSUA_INVALID_ID;
+    }
+
+    if (call->session) {
+	pjmedia_session_destroy(call->session);
+	call->session = NULL;
+
+	PJ_LOG(4,(THIS_FILE, "Media session for call %d is destroyed", 
+			     call_id));
+
+    }
+
+    call->media_st = PJSUA_CALL_MEDIA_NONE;
+}
+
+pj_status_t pjsua_media_channel_deinit(pjsua_call_id call_id)
+{
+    pjsua_call *call = &pjsua_var.calls[call_id];
+
+    stop_media_session(call_id);
+
+    if (pjsua_var.media_cfg.enable_ice) {
+	pjmedia_ice_stop_ice(call->med_tp);
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+/*
+ * DTMF callback from the stream.
+ */
+static void dtmf_callback(pjmedia_stream *strm, void *user_data,
+			  int digit)
+{
+    PJ_UNUSED_ARG(strm);
+
+    if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
+	pjsua_call_id call_id;
+
+	call_id = (pjsua_call_id)user_data;
+	pjsua_var.ua_cfg.cb.on_dtmf_digit(call_id, digit);
+    }
+}
+
+
+pj_status_t pjsua_media_channel_update(pjsua_call_id call_id,
+				       pjmedia_sdp_session *local_sdp,
+				       pjmedia_sdp_session *remote_sdp)
+{
+    int prev_media_st = 0;
+    pjsua_call *call = &pjsua_var.calls[call_id];
+    pjmedia_session_info sess_info;
+    pjmedia_port *media_port;
+    pj_str_t port_name;
+    char tmp[PJSIP_MAX_URL_SIZE];
+    pj_status_t status;
+
+    /* Destroy existing media session, if any. */
+    prev_media_st = call->media_st;
+    stop_media_session(call->index);
+
+    /* Create media session info based on SDP parameters. 
+     * We only support one stream per session at the moment
+     */    
+    status = pjmedia_session_info_from_sdp( call->inv->dlg->pool, 
+					    pjsua_var.med_endpt, 
+					    1,&sess_info, 
+					    local_sdp, remote_sdp);
+    if (status != PJ_SUCCESS)
+	return status;
+
+
+    /* Check if media is put on-hold */
+    if (sess_info.stream_cnt == 0 || 
+	sess_info.stream_info[0].dir == PJMEDIA_DIR_NONE)
+    {
+
+	/* Determine who puts the call on-hold */
+	if (prev_media_st == PJSUA_CALL_MEDIA_ACTIVE) {
+	    if (pjmedia_sdp_neg_was_answer_remote(call->inv->neg)) {
+		/* It was local who offer hold */
+		call->media_st = PJSUA_CALL_MEDIA_LOCAL_HOLD;
+	    } else {
+		call->media_st = PJSUA_CALL_MEDIA_REMOTE_HOLD;
+	    }
+	}
+
+	call->media_dir = PJMEDIA_DIR_NONE;
+
+	/* Shutdown transport */
+	/* No need because we need keepalive? */
+
+    } else {
+
+	/* Start ICE */
+	if (pjsua_var.media_cfg.enable_ice) {
+	    status = pjmedia_ice_start_ice(call->med_tp, call->inv->pool, 
+					   remote_sdp);
+	    if (status != PJ_SUCCESS)
+		return status;
+	}
+
+	/* Override ptime, if this option is specified. */
+	if (pjsua_var.media_cfg.ptime != 0) {
+	    sess_info.stream_info[0].param->setting.frm_per_pkt = (pj_uint8_t)
+		(pjsua_var.media_cfg.ptime / sess_info.stream_info[0].param->info.frm_ptime);
+	    if (sess_info.stream_info[0].param->setting.frm_per_pkt == 0)
+		sess_info.stream_info[0].param->setting.frm_per_pkt = 1;
+	}
+
+	/* Disable VAD, if this option is specified. */
+	if (pjsua_var.media_cfg.no_vad) {
+	    sess_info.stream_info[0].param->setting.vad = 0;
+	}
+
+
+	/* Optionally, application may modify other stream settings here
+	 * (such as jitter buffer parameters, codec ptime, etc.)
+	 */
+	sess_info.stream_info[0].jb_init = pjsua_var.media_cfg.jb_init;
+	sess_info.stream_info[0].jb_min_pre = pjsua_var.media_cfg.jb_min_pre;
+	sess_info.stream_info[0].jb_max_pre = pjsua_var.media_cfg.jb_max_pre;
+	sess_info.stream_info[0].jb_max = pjsua_var.media_cfg.jb_max;
+
+	/* Create session based on session info. */
+	status = pjmedia_session_create( pjsua_var.med_endpt, &sess_info,
+					 &call->med_tp,
+					 call, &call->session );
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+	/* If DTMF callback is installed by application, install our
+	 * callback to the session.
+	 */
+	if (pjsua_var.ua_cfg.cb.on_dtmf_digit) {
+	    pjmedia_session_set_dtmf_callback(call->session, 0, 
+					      &dtmf_callback, 
+					      (void*)(call->index));
+	}
+
+	/* Get the port interface of the first stream in the session.
+	 * We need the port interface to add to the conference bridge.
+	 */
+	pjmedia_session_get_port(call->session, 0, &media_port);
+
+
+	/*
+	 * Add the call to conference bridge.
+	 */
+	port_name.ptr = tmp;
+	port_name.slen = pjsip_uri_print(PJSIP_URI_IN_REQ_URI,
+					 call->inv->dlg->remote.info->uri,
+					 tmp, sizeof(tmp));
+	if (port_name.slen < 1) {
+	    port_name = pj_str("call");
+	}
+	status = pjmedia_conf_add_port( pjsua_var.mconf, call->inv->pool,
+					media_port, 
+					&port_name,
+					(unsigned*)&call->conf_slot);
+	if (status != PJ_SUCCESS) {
+	    return status;
+	}
+
+	/* Call's media state is active */
+	call->media_st = PJSUA_CALL_MEDIA_ACTIVE;
+	call->media_dir = sess_info.stream_info[0].dir;
+    }
+
+    /* Print info. */
+    {
+	char info[80];
+	int info_len = 0;
+	unsigned i;
+
+	for (i=0; i<sess_info.stream_cnt; ++i) {
+	    int len;
+	    const char *dir;
+	    pjmedia_stream_info *strm_info = &sess_info.stream_info[i];
+
+	    switch (strm_info->dir) {
+	    case PJMEDIA_DIR_NONE:
+		dir = "inactive";
+		break;
+	    case PJMEDIA_DIR_ENCODING:
+		dir = "sendonly";
+		break;
+	    case PJMEDIA_DIR_DECODING:
+		dir = "recvonly";
+		break;
+	    case PJMEDIA_DIR_ENCODING_DECODING:
+		dir = "sendrecv";
+		break;
+	    default:
+		dir = "unknown";
+		break;
+	    }
+	    len = pj_ansi_sprintf( info+info_len,
+				   ", stream #%d: %.*s (%s)", i,
+				   (int)strm_info->fmt.encoding_name.slen,
+				   strm_info->fmt.encoding_name.ptr,
+				   dir);
+	    if (len > 0)
+		info_len += len;
+	}
+	PJ_LOG(4,(THIS_FILE,"Media updates%s", info));
+    }
+
+    return PJ_SUCCESS;
 }
 
