Index: pjproject/trunk/pjmedia/build/pjmedia.dsp
===================================================================
--- pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 1096)
+++ pjproject/trunk/pjmedia/build/pjmedia.dsp	(revision 1097)
@@ -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 "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /I "../src/pjmedia-codec" /D "NDEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /c
+# ADD CPP /nologo /MD /W4 /GX /Zi /O2 /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /I "../src/pjmedia-codec" /I "../../pjnath/include" /D "NDEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /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 "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /I "../src/pjmedia-codec" /D "_DEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
+# ADD CPP /nologo /MTd /W4 /Gm /GX /ZI /Od /I "../include" /I "../../pjlib/include" /I "../../pjlib-util/include" /I "../src/pjmedia/portaudio" /I "../src/pjmedia-codec" /I "../../pjnath/include" /D "_DEBUG" /D "PA_NO_ASIO" /D "WIN32" /D "_MBCS" /D "_LIB" /D PJ_WIN32=1 /D PJ_M_I386=1 /FR /FD /GZ /c
 # SUBTRACT CPP /YX
 # ADD BASE RSC /l 0x409 /d "_DEBUG"
@@ -245,4 +245,8 @@
 # Begin Source File
 
+SOURCE=..\src\pjmedia\transport_ice.c
+# End Source File
+# Begin Source File
+
 SOURCE=..\src\pjmedia\transport_udp.c
 # End Source File
@@ -398,4 +402,8 @@
 
 SOURCE=..\include\pjmedia\transport.h
+# End Source File
+# Begin Source File
+
+SOURCE=..\include\pjmedia\transport_ice.h
 # End Source File
 # Begin Source File
Index: pjproject/trunk/pjmedia/include/pjmedia/transport.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/transport.h	(revision 1096)
+++ pjproject/trunk/pjmedia/include/pjmedia/transport.h	(revision 1097)
@@ -27,4 +27,5 @@
 
 #include <pjmedia/types.h>
+#include <pjmedia/errno.h>
 
 /**
@@ -178,4 +179,7 @@
 struct pjmedia_transport_op
 {
+    pj_status_t (*get_info)(pjmedia_transport *tp,
+			    pjmedia_sock_info *info);
+
     /**
      * This function is called by the stream when the transport is about
@@ -266,4 +270,13 @@
 
 
+PJ_INLINE(pj_status_t) pjmedia_transport_get_info(pjmedia_transport *tp,
+						  pjmedia_sock_info *info)
+{
+    if (tp->op->get_info)
+	return (*tp->op->get_info)(tp, info);
+    else
+	return PJ_ENOTSUP;
+}
+
 
 /**
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 1097)
@@ -0,0 +1,78 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#ifndef __pjmedia_ice_H__
+#define __pjmedia_ice_H__
+
+
+/**
+ * @file transport_ice.h
+ * @brief Stream transport with ICE.
+ */
+
+#include <pjmedia/stream.h>
+#include <pjnath/ice_stream_transport.h>
+
+
+/**
+ * @defgroup PJMEDIA_TRANSPORT_ICE ICE Socket Transport
+ * @ingroup PJMEDIA_TRANSPORT_H
+ * @brief Implementation of media transport with ICE.
+ * @{
+ */
+
+PJ_BEGIN_DECL
+
+
+PJ_DECL(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
+					const char *name,
+					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_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp,
+					  pj_ice_role role,
+					  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_start_ice(pjmedia_transport *tp,
+					   pj_pool_t *pool,
+					   pjmedia_sdp_session *rem_sdp);
+PJ_DECL(pj_status_t) pjmedia_ice_stop_ice(pjmedia_transport *tp);
+
+
+
+
+
+PJ_END_DECL
+
+
+/**
+ * @}
+ */
+
+
+#endif	/* __pjmedia_ice_H__ */
+
+
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 1097)
@@ -0,0 +1,549 @@
+/* $Id$ */
+/* 
+ * Copyright (C) 2003-2007 Benny Prijono <benny@prijono.org>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, write to the Free Software
+ * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA 
+ */
+#include <pjmedia/transport_ice.h>
+#include <pjnath/errno.h>
+#include <pj/assert.h>
+
+struct transport_ice
+{
+    pjmedia_transport	 base;
+    pj_ice_st		*ice_st;
+
+    void  *user_data;
+    void (*rtp_cb)(void*,
+		   const void*,
+		   pj_ssize_t);
+    void (*rtcp_cb)(void*,
+		    const void*,
+		    pj_ssize_t);
+};
+
+
+/*
+ * These are media transport operations.
+ */
+static pj_status_t tp_get_info(pjmedia_transport *tp,
+			       pjmedia_sock_info *info);
+static pj_status_t tp_attach( pjmedia_transport *tp,
+			      void *user_data,
+			      const pj_sockaddr_t *rem_addr,
+			      const pj_sockaddr_t *rem_rtcp,
+			      unsigned addr_len,
+			      void (*rtp_cb)(void*,
+					     const void*,
+					     pj_ssize_t),
+			      void (*rtcp_cb)(void*,
+					      const void*,
+					      pj_ssize_t));
+static void	   tp_detach( pjmedia_transport *tp,
+			      void *strm);
+static pj_status_t tp_send_rtp( pjmedia_transport *tp,
+			        const void *pkt,
+			        pj_size_t size);
+static pj_status_t tp_send_rtcp( pjmedia_transport *tp,
+			         const void *pkt,
+			         pj_size_t size);
+
+/*
+ * And these are ICE callbacks.
+ */
+static void ice_on_rx_data(pj_ice_st *ice_st,
+			   unsigned comp_id, unsigned cand_id,
+			   void *pkt, pj_size_t size,
+			   const pj_sockaddr_t *src_addr,
+			   unsigned src_addr_len);
+static void ice_on_stun_srv_resolved(pj_ice_st *ice_st,
+				     pj_status_t status);
+static void ice_on_interface_status(pj_ice_st *ice_st,
+				    void *notify_data,
+				    pj_status_t status,
+				    int itf_id);
+static void ice_on_ice_complete(pj_ice_st *ice_st, 
+			        pj_status_t status);
+
+
+static pjmedia_transport_op tp_ice_op = 
+{
+    &tp_get_info,
+    &tp_attach,
+    &tp_detach,
+    &tp_send_rtp,
+    &tp_send_rtcp,
+    &pjmedia_ice_destroy
+};
+
+
+
+PJ_DEF(pj_status_t) pjmedia_ice_create(pjmedia_endpt *endpt,
+				       const char *name,
+				       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_ice_st *ice_st;
+    pj_ice_st_cb ice_st_cb;
+    struct transport_ice *tp_ice;
+    pj_status_t status;
+
+    PJ_UNUSED_ARG(endpt);
+
+    /* Configure ICE callbacks */
+    pj_bzero(&ice_st_cb, sizeof(ice_st_cb));
+    ice_st_cb.on_ice_complete = &ice_on_ice_complete;
+    ice_st_cb.on_interface_status = &ice_on_interface_status;
+    ice_st_cb.on_rx_data = &ice_on_rx_data;
+    ice_st_cb.on_stun_srv_resolved = &ice_on_stun_srv_resolved;
+
+    /* Create ICE */
+    status = pj_ice_st_create(stun_cfg, name, NULL, &ice_st_cb, &ice_st);
+    if (status != PJ_SUCCESS)
+	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);
+	if (status != PJ_SUCCESS) 
+	    goto on_error;
+
+	/* Add STUN candidates */
+	status = pj_ice_st_add_stun_interface(ice_st, 1, 0, PJ_FALSE, NULL);
+    }
+
+    /* Create transport instance and attach to ICE */
+    tp_ice = PJ_POOL_ZALLOC_T(ice_st->pool, struct transport_ice);
+    tp_ice->ice_st = ice_st;
+    pj_ansi_strcpy(tp_ice->base.name, ice_st->obj_name);
+    tp_ice->base.op = &tp_ice_op;
+
+    ice_st->user_data = (void*)tp_ice;
+
+    /* Done */
+    if (p_tp)
+	*p_tp = &tp_ice->base;
+
+    return PJ_SUCCESS;
+
+on_error:
+    pj_ice_st_destroy(ice_st);
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_ice_close(pjmedia_transport *tp)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+
+    if (tp_ice->ice_st) {
+	pj_ice_st_destroy(tp_ice->ice_st);
+	tp_ice->ice_st = NULL;
+    }
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_ice_init_ice(pjmedia_transport *tp,
+					 pj_ice_role role,
+					 const pj_str_t *local_ufrag,
+					 const pj_str_t *local_passwd)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    return pj_ice_st_init_ice(tp_ice->ice_st, role, local_ufrag, local_passwd);
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_ice_build_sdp(pjmedia_transport *tp,
+					  pj_pool_t *pool,
+					  pjmedia_sdp_session *sdp)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    enum { MAXLEN = 256 };
+    char *buffer;
+    pjmedia_sdp_attr *attr;
+    unsigned i, cand_cnt;
+
+    buffer = pj_pool_alloc(pool, MAXLEN);
+
+    /* Create ice-ufrag attribute */
+    attr = pjmedia_sdp_attr_create(pool, "ice-ufrag", 
+				   &tp_ice->ice_st->ice->rx_ufrag);
+    sdp->attr[sdp->attr_count++] = attr;
+
+    /* Create ice-pwd attribute */
+    attr = pjmedia_sdp_attr_create(pool, "ice-pwd", 
+				   &tp_ice->ice_st->ice->rx_pass);
+    sdp->attr[sdp->attr_count++] = attr;
+
+    /* Add all candidates */
+    cand_cnt = pj_ice_get_cand_cnt(tp_ice->ice_st->ice);
+    for (i=0; i<cand_cnt; ++i) {
+	pj_ice_cand *cand;
+	pj_str_t value;
+	int len;
+
+	pj_ice_get_cand(tp_ice->ice_st->ice, i, &cand);
+
+	len = pj_ansi_snprintf( buffer, MAXLEN,
+				"%.*s %d UDP %u %s %d typ ",
+				(int)cand->foundation.slen,
+				cand->foundation.ptr,
+				cand->comp_id,
+				cand->prio,
+				pj_inet_ntoa(cand->addr.ipv4.sin_addr),
+				(int)pj_ntohs(cand->addr.ipv4.sin_port));
+	if (len < 1 || len >= MAXLEN)
+	    return PJ_ENAMETOOLONG;
+
+	switch (cand->type) {
+	case PJ_ICE_CAND_TYPE_HOST:
+	    len = pj_ansi_snprintf(buffer+len, MAXLEN-len,
+			     "host");
+	    break;
+	case PJ_ICE_CAND_TYPE_SRFLX:
+	    len = pj_ansi_snprintf(buffer+len, MAXLEN-len,
+			     "srflx raddr %s rport %d",
+			     pj_inet_ntoa(cand->base_addr.ipv4.sin_addr),
+			     (int)pj_ntohs(cand->base_addr.ipv4.sin_port));
+	    break;
+	case PJ_ICE_CAND_TYPE_RELAYED:
+	    PJ_TODO(RELATED_ADDR_FOR_RELAYED_ADDR);
+	    len = pj_ansi_snprintf(buffer+len, MAXLEN-len,
+			     "srflx raddr %s rport %d",
+			     pj_inet_ntoa(cand->base_addr.ipv4.sin_addr),
+			     (int)pj_ntohs(cand->base_addr.ipv4.sin_port));
+	    break;
+	case PJ_ICE_CAND_TYPE_PRFLX:
+	    len = pj_ansi_snprintf(buffer+len, MAXLEN-len,
+			     "prflx raddr %s rport %d",
+			     pj_inet_ntoa(cand->base_addr.ipv4.sin_addr),
+			     (int)pj_ntohs(cand->base_addr.ipv4.sin_port));
+	    break;
+	default:
+	    pj_assert(!"Invalid candidate type");
+	    break;
+	}
+	if (len < 1 || len >= MAXLEN)
+	    return PJ_ENAMETOOLONG;
+
+	value = pj_str(buffer);
+	attr = pjmedia_sdp_attr_create(pool, "candidate", &value);
+	sdp->attr[sdp->attr_count++] = attr;
+    }
+
+    /* Done */
+    return PJ_SUCCESS;
+
+}
+
+
+static pj_status_t parse_cand(pj_pool_t *pool,
+			      const pj_str_t *orig_input,
+			      pj_ice_cand *cand)
+{
+    pj_str_t input;
+    char *token, *host;
+    pj_str_t s;
+    pj_status_t status = PJ_EICEINCANDSDP;
+
+    pj_bzero(cand, sizeof(*cand));
+    pj_strdup_with_null(pool, &input, orig_input);
+
+    /* Foundation */
+    token = strtok(input.ptr, " ");
+    if (!token)
+	goto on_return;
+    pj_strdup2(pool, &cand->foundation, token);
+
+    /* Component ID */
+    token = strtok(NULL, " ");
+    if (!token)
+	goto on_return;
+    cand->comp_id = atoi(token);
+
+    /* Transport */
+    token = strtok(NULL, " ");
+    if (!token)
+	goto on_return;
+    if (strcmp(token, "UDP") != 0)
+	goto on_return;
+
+    /* Priority */
+    token = strtok(NULL, " ");
+    if (!token)
+	goto on_return;
+    cand->prio = atoi(token);
+
+    /* Host */
+    host = strtok(NULL, " ");
+    if (!host)
+	goto on_return;
+    if (pj_sockaddr_in_init(&cand->addr.ipv4, pj_cstr(&s, host), 0))
+	goto on_return;
+
+    /* Port */
+    token = strtok(NULL, " ");
+    if (!token)
+	goto on_return;
+    cand->addr.ipv4.sin_port = pj_htons((pj_uint16_t)atoi(token));
+
+    /* typ */
+    token = strtok(NULL, " ");
+    if (!token)
+	goto on_return;
+    if (strcmp(token, "typ") != 0)
+	goto on_return;
+
+    /* candidate type */
+    token = strtok(NULL, " ");
+    if (!token)
+	goto on_return;
+
+    if (strcmp(token, "host") == 0) {
+	cand->type = PJ_ICE_CAND_TYPE_HOST;
+
+    } else if (strcmp(token, "srflx") == 0) {
+	cand->type = PJ_ICE_CAND_TYPE_SRFLX;
+
+    } else if (strcmp(token, "relay") == 0) {
+	cand->type = PJ_ICE_CAND_TYPE_RELAYED;
+
+    } else if (strcmp(token, "prflx") == 0) {
+	cand->type = PJ_ICE_CAND_TYPE_PRFLX;
+
+    } else {
+	goto on_return;
+    }
+
+
+    status = PJ_SUCCESS;
+
+on_return:
+    return status;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_ice_start_ice(pjmedia_transport *tp,
+					  pj_pool_t *pool,
+					  pjmedia_sdp_session *rem_sdp)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    pjmedia_sdp_attr *attr;
+    unsigned i, cand_cnt;
+    pj_ice_cand cand[PJ_ICE_MAX_CAND];
+    pj_str_t uname, pass;
+    pj_status_t status;
+
+    /* Find ice-ufrag attribute */
+    attr = pjmedia_sdp_attr_find2(rem_sdp->attr_count, rem_sdp->attr,
+				  "ice-ufrag", NULL);
+    if (attr == NULL)
+	return PJ_EICEMISSINGSDP;
+    uname = attr->value;
+
+    /* Find ice-pwd attribute */
+    attr = pjmedia_sdp_attr_find2(rem_sdp->attr_count, rem_sdp->attr,
+				  "ice-pwd", NULL);
+    if (attr == NULL)
+	return PJ_EICEMISSINGSDP;
+    pass = attr->value;
+
+    /* Get all candidates */
+    cand_cnt = 0;
+    for (i=0; i<rem_sdp->attr_count; ++i) {
+	pjmedia_sdp_attr *attr;
+
+	attr = rem_sdp->attr[i];
+	if (pj_strcmp2(&attr->name, "candidate")!=0)
+	    continue;
+
+	status = parse_cand(pool, &attr->value, &cand[cand_cnt]);
+	if (status != PJ_SUCCESS)
+	    return status;
+
+	cand_cnt++;
+    }
+
+    /* Start ICE */
+    return pj_ice_st_start_ice(tp_ice->ice_st, &uname, &pass, cand_cnt, cand);
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_ice_stop_ice(pjmedia_transport *tp)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    return pj_ice_st_stop_ice(tp_ice->ice_st);
+}
+
+
+static pj_status_t tp_get_info(pjmedia_transport *tp,
+			       pjmedia_sock_info *info)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    int rel_idx = -1, srflx_idx = -1, host_idx = -1, idx = -1;
+    unsigned i;
+
+    pj_bzero(info, sizeof(*info));
+    info->rtp_sock = info->rtcp_sock = PJ_INVALID_SOCKET;
+
+    for (i=0; i<tp_ice->ice_st->itf_cnt; ++i) {
+	pj_ice_st_interface *itf = tp_ice->ice_st->itfs[i];
+
+	if (itf->type == PJ_ICE_CAND_TYPE_HOST && host_idx == -1)
+	    host_idx = i;
+	else if (itf->type == PJ_ICE_CAND_TYPE_RELAYED && rel_idx == -1)
+	    rel_idx = i;
+	else if (itf->type == PJ_ICE_CAND_TYPE_SRFLX && srflx_idx == -1)
+	    srflx_idx = i;
+    }
+
+    if (idx == -1 && srflx_idx != -1)
+	idx = srflx_idx;
+    else if (idx == -1 && rel_idx != -1)
+	idx = rel_idx;
+    else if (idx == -1 && host_idx != -1)
+	idx = host_idx;
+
+    PJ_ASSERT_RETURN(idx != -1, PJ_EBUG);
+
+    pj_memcpy(&info->rtp_addr_name, &tp_ice->ice_st->itfs[idx]->addr,
+	      sizeof(pj_sockaddr_in));
+
+    return PJ_SUCCESS;
+}
+
+
+static pj_status_t tp_attach( pjmedia_transport *tp,
+			      void *user_data,
+			      const pj_sockaddr_t *rem_addr,
+			      const pj_sockaddr_t *rem_rtcp,
+			      unsigned addr_len,
+			      void (*rtp_cb)(void*,
+					     const void*,
+					     pj_ssize_t),
+			      void (*rtcp_cb)(void*,
+					      const void*,
+					      pj_ssize_t))
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+
+    tp_ice->user_data = user_data;
+    tp_ice->rtp_cb = rtp_cb;
+    tp_ice->rtcp_cb = rtcp_cb;
+
+    PJ_UNUSED_ARG(rem_addr);
+    PJ_UNUSED_ARG(rem_rtcp);
+    PJ_UNUSED_ARG(addr_len);
+
+    return PJ_SUCCESS;
+}
+
+
+static void tp_detach(pjmedia_transport *tp,
+		      void *strm)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+
+    tp_ice->rtp_cb = NULL;
+    tp_ice->rtcp_cb = NULL;
+    tp_ice->user_data = NULL;
+
+    PJ_UNUSED_ARG(strm);
+}
+
+
+static pj_status_t tp_send_rtp(pjmedia_transport *tp,
+			       const void *pkt,
+			       pj_size_t size)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    return pj_ice_st_send_data(tp_ice->ice_st, 1, pkt, size);
+}
+
+
+static pj_status_t tp_send_rtcp(pjmedia_transport *tp,
+			        const void *pkt,
+			        pj_size_t size)
+{
+#if 0
+    struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    return pj_ice_st_send_data(tp_ice->ice_st, 1, pkt, size);
+#else
+    PJ_TODO(SUPPORT_RTCP);
+    PJ_UNUSED_ARG(tp);
+    PJ_UNUSED_ARG(pkt);
+    PJ_UNUSED_ARG(size);
+    return PJ_SUCCESS;
+#endif
+}
+
+
+static void ice_on_rx_data(pj_ice_st *ice_st,
+			   unsigned comp_id, unsigned cand_id,
+			   void *pkt, pj_size_t size,
+			   const pj_sockaddr_t *src_addr,
+			   unsigned src_addr_len)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
+
+    if (comp_id==1 && tp_ice->rtp_cb)
+	(*tp_ice->rtp_cb)(tp_ice->user_data, pkt, size);
+    else if (comp_id==2 && tp_ice->rtcp_cb)
+	(*tp_ice->rtcp_cb)(tp_ice->user_data, pkt, size);
+
+    PJ_UNUSED_ARG(cand_id);
+    PJ_UNUSED_ARG(src_addr);
+    PJ_UNUSED_ARG(src_addr_len);
+}
+
+
+static void ice_on_stun_srv_resolved(pj_ice_st *ice_st,
+				     pj_status_t status)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
+}
+
+
+static void ice_on_interface_status(pj_ice_st *ice_st,
+				    void *notify_data,
+				    pj_status_t status,
+				    int itf_id)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
+}
+
+
+static void ice_on_ice_complete(pj_ice_st *ice_st, 
+			        pj_status_t status)
+{
+    struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
+}
+
+
Index: pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 1096)
+++ pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 1097)
@@ -94,4 +94,6 @@
                        pj_ssize_t bytes_read);
 
+static pj_status_t transport_get_info(pjmedia_transport *tp,
+				      pjmedia_sock_info *info);
 static pj_status_t transport_attach(   pjmedia_transport *tp,
 				       void *user_data,
@@ -117,4 +119,5 @@
 static pjmedia_transport_op transport_udp_op = 
 {
+    &transport_get_info,
     &transport_attach,
     &transport_detach,
@@ -325,13 +328,5 @@
 				pjmedia_transport_udp_info *inf)
 {
-    struct transport_udp *udp = (struct transport_udp*)tp;
-    PJ_ASSERT_RETURN(tp && inf, PJ_EINVAL);
-
-    inf->skinfo.rtp_sock = udp->rtp_sock;
-    inf->skinfo.rtp_addr_name = udp->rtp_addr_name;
-    inf->skinfo.rtcp_sock = udp->rtcp_sock;
-    inf->skinfo.rtcp_addr_name = udp->rtcp_addr_name;
-
-    return PJ_SUCCESS;
+    return transport_get_info(tp, &inf->skinfo);
 }
 
@@ -528,4 +523,20 @@
 
 
+/* Called to get the transport info */
+static pj_status_t transport_get_info(pjmedia_transport *tp,
+				      pjmedia_sock_info *info)
+{
+    struct transport_udp *udp = (struct transport_udp*)tp;
+    PJ_ASSERT_RETURN(tp && info, PJ_EINVAL);
+
+    info->rtp_sock = udp->rtp_sock;
+    info->rtp_addr_name = udp->rtp_addr_name;
+    info->rtcp_sock = udp->rtcp_sock;
+    info->rtcp_addr_name = udp->rtcp_addr_name;
+
+    return PJ_SUCCESS;
+}
+
+
 /* Called by application to initialize the transport */
 static pj_status_t transport_attach(   pjmedia_transport *tp,
Index: pjproject/trunk/pjnath/include/pjnath/errno.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 1096)
+++ pjproject/trunk/pjnath/include/pjnath/errno.h	(revision 1097)
@@ -164,10 +164,19 @@
  */
 #define PJ_ENOICE		    -1
-
- /**
+/**
  * @hideinitializer
  * ICE check is in progress
  */
 #define PJ_EICEINPROGRESS	    -1
+/**
+ * @hideinitializer
+ * Missing ICE SDP attribute
+ */
+#define PJ_EICEMISSINGSDP	    -1
+/**
+ * @hideinitializer
+ * Invalid SDP "candidate" attribute
+ */
+#define PJ_EICEINCANDSDP	    -1
 
 
Index: pjproject/trunk/pjnath/src/pjnath/ice.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1096)
+++ pjproject/trunk/pjnath/src/pjnath/ice.c	(revision 1097)
@@ -26,4 +26,5 @@
 #include <pj/os.h>
 #include <pj/pool.h>
+#include <pj/rand.h>
 #include <pj/string.h>
 
Index: pjproject/trunk/pjproject.dsw
===================================================================
--- pjproject/trunk/pjproject.dsw	(revision 1096)
+++ pjproject/trunk/pjproject.dsw	(revision 1097)
@@ -85,4 +85,16 @@
 ###############################################################################
 
+Project: "pjnath"=.\pjnath\build\pjnath.dsp - Package Owner=<4>
+
+Package=<5>
+{{{
+}}}
+
+Package=<4>
+{{{
+}}}
+
+###############################################################################
+
 Project: "pjsip_core"=.\pjsip\build\pjsip_core.dsp - Package Owner=<4>
 
@@ -153,4 +165,7 @@
     Project_Dep_Name pjsua_lib
     End Project Dependency
+    Begin Project Dependency
+    Project_Dep_Name pjnath
+    End Project Dependency
 }}}
 
