Index: pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 6070)
+++ pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 6071)
@@ -292,4 +292,6 @@
 			    PJMEDIA_TRANSPORT_SO_SNDBUF_SIZE;
     }
+    if (ice_st_cfg.send_buf_size == 0)
+    	ice_st_cfg.send_buf_size = PJMEDIA_MAX_MTU;
 
     /* Create ICE */
@@ -1888,4 +1890,5 @@
 {
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
+    pj_status_t status;
 
     /* Simulate packet lost on TX direction */
@@ -1899,7 +1902,11 @@
     }
 
-    return pj_ice_strans_sendto(tp_ice->ice_st, 1, 
-			        pkt, size, &tp_ice->remote_rtp,
-				tp_ice->addr_len);
+    status = pj_ice_strans_sendto2(tp_ice->ice_st, 1, 
+			           pkt, size, &tp_ice->remote_rtp,
+				   tp_ice->addr_len);
+    if (status == PJ_EPENDING)
+        status = PJ_SUCCESS;
+
+    return status;
 }
 
@@ -1921,11 +1928,18 @@
 
     if (tp_ice->comp_cnt > 1 || tp_ice->use_rtcp_mux) {
+        pj_status_t status;
         unsigned comp_id = (tp_ice->use_rtcp_mux? 1: 2);
+
 	if (addr == NULL) {
 	    addr = &tp_ice->remote_rtcp;
 	    addr_len = pj_sockaddr_get_len(addr);
 	}	  
-	return pj_ice_strans_sendto(tp_ice->ice_st, comp_id, pkt, size,
-				    addr, addr_len);
+
+	status = pj_ice_strans_sendto2(tp_ice->ice_st, comp_id, pkt, size,
+				       addr, addr_len);
+	if (status == PJ_EPENDING)
+	    status = PJ_SUCCESS;
+
+	return status;
     } else {
 	return PJ_SUCCESS;
Index: pjproject/trunk/pjnath/include/pjnath/ice_session.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 6070)
+++ pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 6071)
@@ -935,5 +935,8 @@
  * @param data_len	Size of data or packet, in bytes.
  *
- * @return		PJ_SUCCESS if data is sent successfully.
+ * @return		If the callback \a on_tx_pkt() is called, this
+ *			will contain the return value of the callback.
+ *			Otherwise, it will indicate failure with
+ * 			the appropriate error code.
  */
 PJ_DECL(pj_status_t) pj_ice_sess_send_data(pj_ice_sess *ice,
Index: pjproject/trunk/pjnath/include/pjnath/ice_strans.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 6070)
+++ pjproject/trunk/pjnath/include/pjnath/ice_strans.h	(revision 6071)
@@ -99,5 +99,5 @@
  *    pair. The ICE stream transport may not be able to send data while 
  *    negotiation is in progress.\n\n
- *  - application sends data by using #pj_ice_strans_sendto(). Incoming
+ *  - application sends data by using #pj_ice_strans_sendto2(). Incoming
  *    data will be reported in \a on_rx_data() callback of the
  *    #pj_ice_strans_cb.\n\n
@@ -114,4 +114,12 @@
  */
 
+/* Deprecated API pj_ice_strans_sendto() due to its limitations. See
+ * below for more info and refer to
+ * https://trac.pjsip.org/repos/ticket/2229 for more details.
+ */
+#ifndef DEPRECATED_FOR_TICKET_2229
+#  define DEPRECATED_FOR_TICKET_2229	0
+#endif
+
 /** Forward declaration for ICE stream transport. */
 typedef struct pj_ice_strans pj_ice_strans;
@@ -160,4 +168,17 @@
 			  const pj_sockaddr_t *src_addr,
 			  unsigned src_addr_len);
+
+    /**
+     * This callback is optional and will be called to notify the status of
+     * async send operations.
+     *
+     * @param ice_st	    The ICE stream transport.
+     * @param sent	    If value is positive non-zero it indicates the
+     *			    number of data sent. When the value is negative,
+     *			    it contains the error code which can be retrieved
+     *			    by negating the value (i.e. status=-sent).
+     */
+    void    (*on_data_sent)(pj_ice_strans *sock,
+			    pj_ssize_t sent);
 
     /**
@@ -416,4 +437,24 @@
      */
     pj_ice_strans_turn_cfg turn_tp[PJ_ICE_MAX_TURN];
+
+    /**
+     * Number of send buffers used for pj_ice_strans_sendto2(). If the send
+     * buffers are full, pj_ice_strans_sendto()/sendto2() will return
+     * PJ_EBUSY.
+     *
+     * Set this to 0 to disable buffering (then application will have to
+     * maintain the buffer passed to pj_ice_strans_sendto()/sendto2()
+     * until it has been sent).
+     *
+     * Default: 4
+     */
+    unsigned 		 num_send_buf;
+
+    /**
+     * Buffer size used for pj_ice_strans_sendto2().
+     *
+     * Default: 0 (size determined by the size of the first packet sent).
+     */
+    unsigned 		 send_buf_size;
 
     /**
@@ -905,4 +946,5 @@
 
 
+#if !DEPRECATED_FOR_TICKET_2229
 /**
  * Send outgoing packet using this transport. 
@@ -917,4 +959,17 @@
  * address, as negotiated by ICE.
  *
+ * Limitations:
+ * 1. This function cannot inform the app whether the data has been sent,
+ *    or currently still pending.
+ * 2. In case that the data is still pending, the application has no way
+ *    of knowing the status of the send operation (whether it's a success
+ *    or failure).
+ * Due to these limitations, the API is deprecated and will be removed
+ * in the future.
+ *
+ * Note that application shouldn't mix using pj_ice_strans_sendto() and
+ * pj_ice_strans_sendto2() to avoid inconsistent calling of
+ * on_data_sent() callback.
+ *
  * @param ice_st	The ICE stream transport.
  * @param comp_id	Component ID.
@@ -924,5 +979,6 @@
  * @param dst_addr_len	Length of destination address.
  *
- * @return		PJ_SUCCESS if data is sent successfully.
+ * @return		PJ_SUCCESS if data has been sent, or will be sent
+ *			later. No callback will be called.
  */
 PJ_DECL(pj_status_t) pj_ice_strans_sendto(pj_ice_strans *ice_st,
@@ -932,4 +988,42 @@
 					  const pj_sockaddr_t *dst_addr,
 					  int dst_addr_len);
+#endif
+
+
+/**
+ * Send outgoing packet using this transport. 
+ * Application can send data (normally RTP or RTCP packets) at any time
+ * by calling this function. This function takes a destination
+ * address as one of the arguments, and this destination address should
+ * be taken from the default transport address of the component (that is
+ * the address in SDP c= and m= lines, or in a=rtcp attribute). 
+ * If ICE negotiation is in progress, this function will send the data 
+ * to the destination address. Otherwise if ICE negotiation has completed
+ * successfully, this function will send the data to the nominated remote 
+ * address, as negotiated by ICE.
+ *
+ * Note that application shouldn't mix using pj_ice_strans_sendto() and
+ * pj_ice_strans_sendto2() to avoid inconsistent calling of
+ * on_data_sent() callback.
+ *
+ * @param ice_st	The ICE stream transport.
+ * @param comp_id	Component ID.
+ * @param data		The data or packet to be sent.
+ * @param data_len	Size of data or packet, in bytes.
+ * @param dst_addr	The destination address.
+ * @param dst_addr_len	Length of destination address.
+ *
+ * @return		PJ_SUCCESS if data has been sent, or
+ *		    	PJ_EPENDING if data cannot be sent immediately. In
+ *		    	this case the \a on_data_sent() callback will be
+ *		    	called when data is actually sent. Any other return
+ *		    	value indicates error condition.
+ */
+PJ_DECL(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st,
+					   unsigned comp_id,
+					   const void *data,
+					   pj_size_t data_len,
+					   const pj_sockaddr_t *dst_addr,
+					   int dst_addr_len);
 
 
Index: pjproject/trunk/pjnath/include/pjnath/turn_session.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 6070)
+++ pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 6071)
@@ -234,6 +234,9 @@
     /**
      * This callback will be called by the TURN session whenever it
-     * needs to send outgoing message. Since the TURN session doesn't
-     * have a socket on its own, this callback must be implemented.
+     * needs to send data or outgoing messages. Since the TURN session
+     * doesn't have a socket on its own, this callback must be implemented.
+     *
+     * If the callback \a on_stun_send_pkt() is implemented, outgoing
+     * messages will use that callback instead.
      *
      * @param sess	The TURN session.
@@ -251,4 +254,26 @@
 			       const pj_sockaddr_t *dst_addr,
 			       unsigned addr_len);
+
+    /**
+     * This callback will be called by the TURN session whenever it
+     * needs to send outgoing STUN requests/messages for TURN signalling
+     * purposes (data sending will not invoke this callback). If this
+     * callback is not implemented, the callback \a on_send_pkt()
+     * will be called instead.
+     *
+     * @param sess	The TURN session.
+     * @param pkt	The packet/data to be sent.
+     * @param pkt_len	Length of the packet/data.
+     * @param dst_addr	Destination address of the packet.
+     * @param addr_len	Length of the destination address.
+     *
+     * @return		The callback should return the status of the
+     *			send operation. 
+     */
+    pj_status_t (*on_stun_send_pkt)(pj_turn_session *sess,
+			       	    const pj_uint8_t *pkt,
+			       	    unsigned pkt_len,
+			       	    const pj_sockaddr_t *dst_addr,
+			       	    unsigned addr_len);
 
     /**
@@ -763,6 +788,8 @@
  * @param addr_len	Length of the address.
  *
- * @return		PJ_SUCCESS if the operation has been successful,
- *			or the appropriate error code on failure.
+ * @return		If the callback \a on_send_pkt() is called, this
+ *			will contain the return value of the callback.
+ *			Otherwise, it will indicate failure with
+ * 			the appropriate error code.
  */
 PJ_DECL(pj_status_t) pj_turn_session_sendto(pj_turn_session *sess,
Index: pjproject/trunk/pjnath/include/pjnath/turn_sock.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/turn_sock.h	(revision 6070)
+++ pjproject/trunk/pjnath/include/pjnath/turn_sock.h	(revision 6071)
@@ -87,4 +87,21 @@
 		       const pj_sockaddr_t *peer_addr,
 		       unsigned addr_len);
+
+    /**
+     * Notifification when asynchronous send operation has completed.
+     *
+     * @param turn_sock	    The TURN transport.
+     * @param sent	    If value is positive non-zero it indicates the
+     *			    number of data sent. When the value is negative,
+     *			    it contains the error code which can be retrieved
+     *			    by negating the value (i.e. status=-sent).
+     *
+     * @return		    Application should normally return PJ_TRUE to let
+     *			    the TURN transport continue its operation. However
+     *			    it must return PJ_FALSE if it has destroyed the
+     *			    TURN transport in this callback.
+     */
+    pj_bool_t (*on_data_sent)(pj_turn_sock *sock,
+			      pj_ssize_t sent);
 
     /**
@@ -575,6 +592,9 @@
  * @param addr_len	Length of the address.
  *
- * @return		PJ_SUCCESS if the operation has been successful,
- *			or the appropriate error code on failure.
+ * @return		PJ_SUCCESS if data has been sent immediately, or
+ *			PJ_EPENDING if data cannot be sent immediately. In
+ *			this case the \a on_data_sent() callback will be
+ *			called when data is actually sent. Any other return
+ *			value indicates error condition.
  */ 
 PJ_DECL(pj_status_t) pj_turn_sock_sendto(pj_turn_sock *turn_sock,
Index: pjproject/trunk/pjnath/src/pjnath/ice_strans.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 6070)
+++ pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 6071)
@@ -128,4 +128,6 @@
 			    const pj_sockaddr_t *peer_addr,
 			    unsigned addr_len);
+static pj_bool_t turn_on_data_sent(pj_turn_sock *turn_sock,
+				   pj_ssize_t sent);
 static void turn_on_state(pj_turn_sock *turn_sock, pj_turn_state_t old_state,
 			  pj_turn_state_t new_state);
@@ -134,4 +136,5 @@
 
 /* Forward decls */
+static pj_bool_t on_data_sent(pj_ice_strans *ice_st, pj_ssize_t sent);
 static void ice_st_on_destroy(void *obj);
 static void destroy_ice_st(pj_ice_strans *ice_st);
@@ -179,4 +182,14 @@
 
 
+/* Pending send buffer */
+typedef struct pending_send
+{
+    void       	       *buffer;
+    unsigned 		comp_id;
+    pj_size_t 		data_len;
+    pj_sockaddr       	dst_addr;
+    int 		dst_addr_len;
+} pending_send;
+
 /**
  * This structure represents the ICE stream transport.
@@ -185,4 +198,5 @@
 {
     char		    *obj_name;	/**< Log ID.			*/
+    pj_pool_factory	    *pf;	/**< Pool factory.		*/
     pj_pool_t		    *pool;	/**< Pool used by this object.	*/
     void		    *user_data;	/**< Application data.		*/
@@ -198,8 +212,17 @@
     pj_ice_strans_comp	   **comp;	/**< Components array.		*/
 
+    pj_pool_t		    *buf_pool;  /**< Pool for buffers.		*/
+    unsigned		     num_buf;	/**< Number of buffers.		*/
+    unsigned		     buf_idx;	/**< Index of buffer.		*/
+    unsigned		     empty_idx;	/**< Index of empty buffer.	*/
+    unsigned		     buf_size;  /**< Buffer size.		*/
+    pending_send	    *send_buf;	/**< Send buffers.		*/
+    pj_bool_t		     is_pending;/**< Any pending send?		*/
+
     pj_timer_entry	     ka_timer;	/**< STUN keep-alive timer.	*/
 
     pj_bool_t		     destroy_req;/**< Destroy has been called?	*/
     pj_bool_t		     cb_called;	/**< Init error callback called?*/
+    pj_bool_t		     call_send_cb;/**< Need to call send cb?	*/
 };
 
@@ -242,4 +265,6 @@
     pj_ice_strans_turn_cfg_default(&cfg->turn);
     pj_ice_sess_options_default(&cfg->opt);
+
+    cfg->num_send_buf = 4;
 }
 
@@ -368,4 +393,5 @@
     pj_bzero(&turn_sock_cb, sizeof(turn_sock_cb));
     turn_sock_cb.on_rx_data = &turn_on_rx_data;
+    turn_sock_cb.on_data_sent = &turn_on_data_sent;
     turn_sock_cb.on_state = &turn_on_state;
 
@@ -786,4 +812,34 @@
 }
 
+static pj_status_t alloc_send_buf(pj_ice_strans *ice_st, unsigned buf_size)
+{
+    if (buf_size > ice_st->buf_size) {
+        unsigned i;
+        
+        if (ice_st->is_pending) {
+            /* The current buffer is insufficient, but still currently used.*/
+            return PJ_EBUSY;
+        }
+
+    	pj_pool_safe_release(&ice_st->buf_pool);
+
+    	ice_st->buf_pool = pj_pool_create(ice_st->pf, "ice_buf",
+    			       (buf_size + sizeof(pending_send)) *
+    			       ice_st->num_buf, 512, NULL);
+	if (!ice_st->buf_pool)
+	    return PJ_ENOMEM;
+
+	ice_st->buf_size = buf_size;
+	ice_st->send_buf = pj_pool_calloc(ice_st->buf_pool, ice_st->num_buf,
+			       		  sizeof(pending_send));
+	for (i = 0; i < ice_st->num_buf; i++) {
+	    ice_st->send_buf[i].buffer = pj_pool_alloc(ice_st->buf_pool,
+	    					       buf_size);
+	}
+	ice_st->buf_idx = ice_st->empty_idx = 0;
+    }
+    
+    return PJ_SUCCESS;
+}
 
 /*
@@ -816,4 +872,5 @@
     ice_st = PJ_POOL_ZALLOC_T(pool, pj_ice_strans);
     ice_st->pool = pool;
+    ice_st->pf = cfg->stun_cfg.pf;
     ice_st->obj_name = pool->obj_name;
     ice_st->user_data = user_data;
@@ -827,4 +884,13 @@
     if (status != PJ_SUCCESS) {
 	pj_pool_release(pool);
+	pj_log_pop_indent();
+	return status;
+    }
+
+    /* Allocate send buffer */
+    ice_st->num_buf = cfg->num_send_buf;
+    status = alloc_send_buf(ice_st, cfg->send_buf_size);
+    if (status != PJ_SUCCESS) {
+	destroy_ice_st(ice_st);
 	pj_log_pop_indent();
 	return status;
@@ -902,5 +968,6 @@
 
     /* Done */
-    pj_pool_release(ice_st->pool);
+    pj_pool_safe_release(&ice_st->buf_pool);
+    pj_pool_safe_release(&ice_st->pool);
 }
 
@@ -1464,5 +1531,5 @@
     
     /* Protect with group lock, since this may cause race condition with
-     * pj_ice_strans_sendto().
+     * pj_ice_strans_sendto2().
      * See ticket #1877.
      */
@@ -1481,16 +1548,61 @@
 }
 
+static pj_status_t use_buffer( pj_ice_strans *ice_st,
+			       unsigned comp_id,
+			       const void *data,
+			       pj_size_t data_len,
+			       const pj_sockaddr_t *dst_addr,
+			       int dst_addr_len,
+			       void **buffer )
+{
+    unsigned idx;
+    pj_status_t status;
+
+    /* Allocate send buffer, if necessary. */
+    status = alloc_send_buf(ice_st, data_len);
+    if (status != PJ_SUCCESS)
+    	return status;
+    
+    if (ice_st->is_pending && ice_st->empty_idx == ice_st->buf_idx) {
+    	/* We don't use buffer or there's no more empty buffer. */
+    	return PJ_EBUSY;
+    }
+
+    idx = ice_st->empty_idx;
+    ice_st->empty_idx = (ice_st->empty_idx + 1) % ice_st->num_buf;
+    ice_st->send_buf[idx].comp_id = comp_id;
+    ice_st->send_buf[idx].data_len = data_len;
+    pj_assert(ice_st->buf_size >= data_len);
+    pj_memcpy(ice_st->send_buf[idx].buffer, data, data_len);
+    pj_sockaddr_cp(&ice_st->send_buf[idx].dst_addr, dst_addr);
+    ice_st->send_buf[idx].dst_addr_len = dst_addr_len;
+    *buffer = ice_st->send_buf[idx].buffer;
+    
+    if (ice_st->is_pending) {
+        /* We'll continue later since there's still a pending send. */
+    	return PJ_EPENDING;
+    }
+    
+    ice_st->is_pending = PJ_TRUE;
+    ice_st->buf_idx = idx;
+
+    return PJ_SUCCESS;
+}
+
 /*
  * Application wants to send outgoing packet.
  */
-PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
-					  unsigned comp_id,
-					  const void *data,
-					  pj_size_t data_len,
-					  const pj_sockaddr_t *dst_addr,
-					  int dst_addr_len)
+static pj_status_t send_data(pj_ice_strans *ice_st,
+			     unsigned comp_id,
+		      	     const void *data,
+		      	     pj_size_t data_len,
+			     const pj_sockaddr_t *dst_addr,
+			     int dst_addr_len,
+			     pj_bool_t use_buf,
+			     pj_bool_t call_cb)
 {
     pj_ice_strans_comp *comp;
     pj_ice_sess_cand *def_cand;
+    void *buf = (void *)data;
     pj_status_t status;
 
@@ -1501,6 +1613,8 @@
 
     /* Check that default candidate for the component exists */
-    if (comp->default_cand >= comp->cand_cnt)
-	return PJ_EINVALIDOP;
+    if (comp->default_cand >= comp->cand_cnt) {
+	status = PJ_EINVALIDOP;
+	goto on_return;
+    }
 
     /* Protect with group lock, since this may cause race condition with
@@ -1509,4 +1623,14 @@
      */
     pj_grp_lock_acquire(ice_st->grp_lock);
+
+    if (use_buf && ice_st->num_buf > 0) {
+    	status = use_buffer(ice_st, comp_id, data, data_len, dst_addr,
+    			    dst_addr_len, &buf);
+
+    	if (status == PJ_EPENDING || status != PJ_SUCCESS) {
+    	    pj_grp_lock_release(ice_st->grp_lock);
+    	    return status;
+    	}
+    }
 
     /* If ICE is available, send data with ICE, otherwise send with the
@@ -1517,11 +1641,11 @@
      */
     if (ice_st->ice && ice_st->state == PJ_ICE_STRANS_STATE_RUNNING) {
-	status = pj_ice_sess_send_data(ice_st->ice, comp_id, data, data_len);
+	status = pj_ice_sess_send_data(ice_st->ice, comp_id, buf, data_len);
 	
 	pj_grp_lock_release(ice_st->grp_lock);
 	
-	return status;
+	goto on_return;
     } 
-    
+
     pj_grp_lock_release(ice_st->grp_lock);
 
@@ -1542,5 +1666,6 @@
 	    if (comp->turn[tp_idx].sock == NULL) {
 		/* TURN socket error */
-		return PJ_EINVALIDOP;
+		status = PJ_EINVALIDOP;
+		goto on_return;
 	    }
 
@@ -1556,9 +1681,8 @@
 
 	    status = pj_turn_sock_sendto(comp->turn[tp_idx].sock,
-					 (const pj_uint8_t*)data,
+					 (const pj_uint8_t*)buf,
 					 (unsigned)data_len,
 					 dst_addr, dst_addr_len);
-	    return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
-		    PJ_SUCCESS : status;
+	    goto on_return;
 	} else {
     	    const pj_sockaddr_t *dest_addr;
@@ -1573,5 +1697,5 @@
     	    					    dst_addr);
     	    	    if (status != PJ_SUCCESS)
-    	            	return status;
+    	            	goto on_return;
 
     	    	    pj_sockaddr_cp(&comp->dst_addr, dst_addr);
@@ -1586,14 +1710,75 @@
     	    }
 
-	    status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, data,
+	    status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL, buf,
 					 (unsigned)data_len, 0, dest_addr,
 					 dest_addr_len);
-	    return (status==PJ_SUCCESS||status==PJ_EPENDING) ?
-		    PJ_SUCCESS : status;
+	    goto on_return;
 	}
 
     } else
-	return PJ_EINVALIDOP;
-}
+	status = PJ_EINVALIDOP;
+
+on_return:
+    /* We continue later in on_data_sent() callback. */
+    if (status == PJ_EPENDING)
+    	return status;
+
+    if (call_cb) {
+    	on_data_sent(ice_st, (status == PJ_SUCCESS? data_len: -status));
+    } else {
+        pj_grp_lock_acquire(ice_st->grp_lock);
+    	if (ice_st->num_buf > 0) {
+    	    ice_st->buf_idx = (ice_st->buf_idx + 1) % ice_st->num_buf;
+    	    pj_assert (ice_st->buf_idx == ice_st->empty_idx);
+    	}
+    	ice_st->is_pending = PJ_FALSE;
+    	pj_grp_lock_release(ice_st->grp_lock);
+    }
+
+    return status;
+}
+
+
+#if !DEPRECATED_FOR_TICKET_2229
+/*
+ * Application wants to send outgoing packet.
+ */
+PJ_DEF(pj_status_t) pj_ice_strans_sendto( pj_ice_strans *ice_st,
+					  unsigned comp_id,
+					  const void *data,
+					  pj_size_t data_len,
+					  const pj_sockaddr_t *dst_addr,
+					  int dst_addr_len)
+{
+    pj_status_t status;
+
+    PJ_LOG(1, (ice_st->obj_name, "pj_ice_strans_sendto() is deprecated. "
+    				 "Application is recommended to use "
+    				 "pj_ice_strans_sendto2() instead."));
+    status = send_data(ice_st, comp_id, data, data_len, dst_addr,
+    		       dst_addr_len, PJ_TRUE, PJ_FALSE);
+    if (status == PJ_EPENDING)
+    	status = PJ_SUCCESS;
+    
+    return status;
+}
+#endif
+
+
+/*
+ * Application wants to send outgoing packet.
+ */
+PJ_DEF(pj_status_t) pj_ice_strans_sendto2(pj_ice_strans *ice_st,
+					  unsigned comp_id,
+					  const void *data,
+					  pj_size_t data_len,
+					  const pj_sockaddr_t *dst_addr,
+					  int dst_addr_len)
+{
+    ice_st->call_send_cb = PJ_TRUE;
+    return send_data(ice_st, comp_id, data, data_len, dst_addr,
+    		     dst_addr_len, PJ_TRUE, PJ_FALSE);
+}
+
 
 /*
@@ -1706,4 +1891,6 @@
     pj_ice_strans_comp *comp;
     pj_status_t status;
+    void *buf = (void *)pkt;
+    pj_bool_t use_buf = PJ_FALSE;
 #if defined(ENABLE_TRACE) && (ENABLE_TRACE != 0)
     char daddr[PJ_INET6_ADDRSTRLEN];
@@ -1713,4 +1900,18 @@
 
     PJ_ASSERT_RETURN(comp_id && comp_id <= ice_st->comp_cnt, PJ_EINVAL);
+
+    pj_grp_lock_acquire(ice_st->grp_lock);
+    if (ice_st->num_buf > 0 &&
+        ice_st->send_buf[ice_st->buf_idx].buffer != pkt)
+    {
+        use_buf = PJ_TRUE;
+    	status = use_buffer(ice_st, comp_id, pkt, size, dst_addr,
+    			    dst_addr_len, &buf);
+    	if (status == PJ_EPENDING || status != PJ_SUCCESS) {
+    	    pj_grp_lock_release(ice_st->grp_lock);
+    	    return status;
+    	}
+    }
+    pj_grp_lock_release(ice_st->grp_lock);
 
     comp = ice_st->comp[comp_id-1];
@@ -1726,5 +1927,5 @@
 	if (comp->turn[tp_idx].sock) {
 	    status = pj_turn_sock_sendto(comp->turn[tp_idx].sock,
-					 (const pj_uint8_t*)pkt,
+					 (const pj_uint8_t*)buf,
 					 (unsigned)size,
 					 dst_addr, dst_addr_len);
@@ -1742,6 +1943,7 @@
     	    	status = pj_sockaddr_synthesize(pj_AF_INET6(),
     	    					&comp->synth_addr, dst_addr);
-    	    	if (status != PJ_SUCCESS)
-    	    	    return status;
+    	    	if (status != PJ_SUCCESS) {
+    	    	    goto on_return;
+    	    	}
     	    
     	    	pj_sockaddr_cp(&comp->dst_addr, dst_addr);
@@ -1756,5 +1958,5 @@
 
 	status = pj_stun_sock_sendto(comp->stun[tp_idx].sock, NULL,
-				     pkt, (unsigned)size, 0,
+				     buf, (unsigned)size, 0,
 				     dest_addr, dest_addr_len);
     } else {
@@ -1763,5 +1965,16 @@
     }
 
-    return (status==PJ_SUCCESS||status==PJ_EPENDING) ? PJ_SUCCESS : status;
+on_return:
+    if (use_buf && status != PJ_EPENDING) {
+        pj_grp_lock_acquire(ice_st->grp_lock);
+    	if (ice_st->num_buf > 0) {
+    	    ice_st->buf_idx = (ice_st->buf_idx + 1) % ice_st->num_buf;
+    	    pj_assert(ice_st->buf_idx == ice_st->empty_idx);
+    	}
+    	ice_st->is_pending = PJ_FALSE;
+    	pj_grp_lock_release(ice_st->grp_lock);
+    }
+
+    return status;
 }
 
@@ -1784,4 +1997,36 @@
 				 src_addr, src_addr_len);
     }
+}
+
+/* Notifification when asynchronous send operation via STUN/TURN
+ * has completed.
+ */
+static pj_bool_t on_data_sent(pj_ice_strans *ice_st, pj_ssize_t sent)
+{
+    if (ice_st->destroy_req || !ice_st->is_pending)
+	return PJ_TRUE;
+
+    if (ice_st->call_send_cb && ice_st->cb.on_data_sent) {
+	(*ice_st->cb.on_data_sent)(ice_st, sent);
+    }
+
+    pj_grp_lock_acquire(ice_st->grp_lock);
+
+    if (ice_st->num_buf > 0)
+        ice_st->buf_idx = (ice_st->buf_idx + 1) % ice_st->num_buf;
+    
+    if (ice_st->num_buf > 0 && ice_st->buf_idx != ice_st->empty_idx) {
+	/* There's still more pending send. Send it one by one. */
+        pending_send *ps = &ice_st->send_buf[ice_st->buf_idx];
+
+	pj_grp_lock_release(ice_st->grp_lock);
+    	send_data(ice_st, ps->comp_id, ps->buffer, ps->data_len,
+    	    	  &ps->dst_addr, ps->dst_addr_len, PJ_FALSE, PJ_TRUE);
+    } else {
+    	ice_st->is_pending = PJ_FALSE;
+    	pj_grp_lock_release(ice_st->grp_lock);
+    }
+
+    return PJ_TRUE;
 }
 
@@ -1845,8 +2090,12 @@
 				   pj_ssize_t sent)
 {
-    PJ_UNUSED_ARG(stun_sock);
+    sock_user_data *data;
+
     PJ_UNUSED_ARG(send_key);
-    PJ_UNUSED_ARG(sent);
-    return PJ_TRUE;
+
+    data = (sock_user_data *)pj_stun_sock_get_user_data(stun_sock);
+    if (!data || !data->comp || !data->comp->ice_st) return PJ_TRUE;
+
+    return on_data_sent(data->comp->ice_st, sent);
 }
 
@@ -2106,4 +2355,17 @@
 }
 
+/* Notifification when asynchronous send operation to the TURN socket
+ * has completed.
+ */
+static pj_bool_t turn_on_data_sent(pj_turn_sock *turn_sock,
+				   pj_ssize_t sent)
+{
+    sock_user_data *data;
+
+    data = (sock_user_data *)pj_turn_sock_get_user_data(turn_sock);
+    if (!data || !data->comp || !data->comp->ice_st) return PJ_TRUE;
+
+    return on_data_sent(data->comp->ice_st, sent);
+}
 
 /* Callback when TURN client state has changed */
Index: pjproject/trunk/pjnath/src/pjnath/stun_sock.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 6070)
+++ pjproject/trunk/pjnath/src/pjnath/stun_sock.c	(revision 6071)
@@ -638,5 +638,5 @@
 				    pj_sockaddr_get_len(&stun_sock->srv_addr),
 				    tdata);
-    if (status != PJ_SUCCESS && status != PJ_EPENDING)
+    if (status != PJ_SUCCESS)
 	goto on_error;
 
Index: pjproject/trunk/pjnath/src/pjnath/stun_transaction.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 6070)
+++ pjproject/trunk/pjnath/src/pjnath/stun_transaction.c	(revision 6071)
@@ -259,4 +259,6 @@
     /* Send message */
     status = tsx->cb.on_send_msg(tsx, tsx->last_pkt, tsx->last_pkt_size);
+    if (status == PJ_EPENDING || status == PJ_EBUSY)
+    	status = PJ_SUCCESS;
 
     if (status == PJNATH_ESTUNDESTROYED) {
Index: pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 6070)
+++ pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 6071)
@@ -1327,7 +1327,13 @@
 
     sess = (pj_turn_session*) pj_stun_session_get_user_data(stun);
-    return (*sess->cb.on_send_pkt)(sess, (const pj_uint8_t*)pkt, 
-				   (unsigned)pkt_size, 
-				   dst_addr, addr_len);
+    if (*sess->cb.on_stun_send_pkt) {
+    	return (*sess->cb.on_stun_send_pkt)(sess, (const pj_uint8_t*)pkt,
+				       	    (unsigned)pkt_size,
+				       	    dst_addr, addr_len);
+    } else {
+    	return (*sess->cb.on_send_pkt)(sess, (const pj_uint8_t*)pkt,
+				       (unsigned)pkt_size,
+				       dst_addr, addr_len);
+    }
 }
 
Index: pjproject/trunk/pjnath/src/pjnath/turn_sock.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/turn_sock.c	(revision 6070)
+++ pjproject/trunk/pjnath/src/pjnath/turn_sock.c	(revision 6071)
@@ -94,4 +94,7 @@
 
     pj_ioqueue_op_key_t	 send_key;
+    pj_ioqueue_op_key_t	 int_send_key;
+    unsigned 		 pkt_len;
+    unsigned 		 body_len;
 
     /* Data connection, when peer_conn_type==PJ_TURN_TP_TCP (RFC 6062) */
@@ -109,4 +112,9 @@
 				    const pj_sockaddr_t *dst_addr,
 				    unsigned dst_addr_len);
+static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess,
+				    	 const pj_uint8_t *pkt,
+				    	 unsigned pkt_len,
+				    	 const pj_sockaddr_t *dst_addr,
+				    	 unsigned dst_addr_len);
 static void turn_on_channel_bound(pj_turn_session *sess,
 				  const pj_sockaddr_t *peer_addr,
@@ -136,4 +144,7 @@
 			      pj_status_t status,
 			      pj_size_t *remainder);
+static pj_bool_t on_data_sent(pj_turn_sock *turn_sock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent);
 static pj_bool_t on_connect_complete(pj_turn_sock *turn_sock,
 				     pj_status_t status);
@@ -149,4 +160,7 @@
 				    pj_status_t status,
 				    pj_size_t *remainder);
+static pj_bool_t on_data_sent_asock(pj_activesock_t *asock,
+			      	     pj_ioqueue_op_key_t *send_key,
+			      	     pj_ssize_t sent);
 
 /*
@@ -168,4 +182,7 @@
 				       pj_status_t status,
 				       pj_size_t *remainder);
+static pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock,
+			      	       pj_ioqueue_op_key_t *send_key,
+			      	       pj_ssize_t sent);
 static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock,
 					      pj_status_t status);
@@ -328,4 +345,5 @@
     pj_bzero(&sess_cb, sizeof(sess_cb));
     sess_cb.on_send_pkt = &turn_on_send_pkt;
+    sess_cb.on_stun_send_pkt = &turn_on_stun_send_pkt;
     sess_cb.on_channel_bound = &turn_on_channel_bound;
     sess_cb.on_rx_data = &turn_on_rx_data;
@@ -628,4 +646,8 @@
 	return PJ_EINVALIDOP;
 
+    /* TURN session may add some headers to the packet, so we need
+     * to store our actual data length to be sent here.
+     */
+    turn_sock->body_len = pkt_len;
     return pj_turn_session_sendto(turn_sock->sess, pkt, pkt_len, 
 				  addr, addr_len);
@@ -694,4 +716,6 @@
     /* Init send_key */
     pj_ioqueue_op_key_init(&turn_sock->send_key, sizeof(turn_sock->send_key));
+    pj_ioqueue_op_key_init(&turn_sock->int_send_key,
+    			   sizeof(turn_sock->int_send_key));
 
     /* Send Allocate request */
@@ -861,4 +885,38 @@
 }
 
+static pj_bool_t on_data_sent(pj_turn_sock *turn_sock,
+			      pj_ioqueue_op_key_t *send_key,
+			      pj_ssize_t sent)
+{
+    unsigned header_len, sent_size;
+
+    /* Don't report to callback if this is internal message. */
+    if (send_key == &turn_sock->int_send_key) {
+	return PJ_TRUE;
+    }
+
+    if (turn_sock->cb.on_data_sent) {
+        /* Remove the length of packet header from sent size. */
+	header_len = turn_sock->pkt_len - turn_sock->body_len;
+	sent_size = (sent > header_len)? (sent - header_len) : 0;
+	(*turn_sock->cb.on_data_sent)(turn_sock, sent_size);
+    }
+
+    return PJ_TRUE;
+}
+
+
+static pj_bool_t on_data_sent_asock(pj_activesock_t *asock,
+			      	     pj_ioqueue_op_key_t *send_key,
+			      	     pj_ssize_t sent)
+{
+    pj_turn_sock *turn_sock;
+
+    turn_sock = (pj_turn_sock*)pj_activesock_get_user_data(asock);
+
+    return on_data_sent(turn_sock, send_key, sent);
+}
+
+
 #if PJ_HAS_SSL_SOCK
 static pj_bool_t on_data_read_ssl_sock(pj_ssl_sock_t *ssl_sock,
@@ -897,16 +955,15 @@
     }
 
-    return PJ_TRUE;
+    return on_data_sent(turn_sock, op_key, bytes_sent);
 }
 #endif
 
-/*
- * Callback from TURN session to send outgoing packet.
- */
-static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
-				    const pj_uint8_t *pkt,
-				    unsigned pkt_len,
-				    const pj_sockaddr_t *dst_addr,
-				    unsigned dst_addr_len)
+
+static pj_status_t send_pkt(pj_turn_session *sess,
+			    pj_bool_t internal,
+			    const pj_uint8_t *pkt,
+			    unsigned pkt_len,
+			    const pj_sockaddr_t *dst_addr,
+			    unsigned dst_addr_len)
 {
     pj_turn_sock *turn_sock = (pj_turn_sock*) 
@@ -914,4 +971,5 @@
     pj_ssize_t len = pkt_len;
     pj_status_t status = PJ_SUCCESS;
+    pj_ioqueue_key_t *send_key = &turn_sock->send_key;
 
     if (turn_sock == NULL || turn_sock->is_destroying) {
@@ -922,7 +980,11 @@
     }
 
+    if (internal)
+    	send_key = &turn_sock->int_send_key;
+    turn_sock->pkt_len = pkt_len;
+
     if (turn_sock->conn_type == PJ_TURN_TP_UDP) {
 	status = pj_activesock_sendto(turn_sock->active_sock,
-				      &turn_sock->send_key, pkt, &len, 0,
+				      send_key, pkt, &len, 0,
 				      dst_addr, dst_addr_len);
     } else if (turn_sock->alloc_param.peer_conn_type == PJ_TURN_TP_TCP) {
@@ -932,5 +994,5 @@
 	    /* Destination address is TURN server */
 	    status = pj_activesock_send(turn_sock->active_sock,
-					&turn_sock->send_key, pkt, &len, 0);
+					send_key, pkt, &len, 0);
 	} else {
 	    /* Destination address is peer, lookup data connection */
@@ -952,10 +1014,10 @@
     } else  if (turn_sock->conn_type == PJ_TURN_TP_TCP) {
 	status = pj_activesock_send(turn_sock->active_sock,
-				    &turn_sock->send_key, pkt, &len, 0);
+				    send_key, pkt, &len, 0);
     }
 #if PJ_HAS_SSL_SOCK
     else if (turn_sock->conn_type == PJ_TURN_TP_TLS) {
 	status = pj_ssl_sock_send(turn_sock->ssl_sock,
-				  &turn_sock->send_key, pkt, &len, 0);
+				  send_key, pkt, &len, 0);
     }
 #endif
@@ -969,4 +1031,28 @@
 
     return status;
+}
+
+
+/*
+ * Callback from TURN session to send outgoing packet.
+ */
+static pj_status_t turn_on_send_pkt(pj_turn_session *sess,
+				    const pj_uint8_t *pkt,
+				    unsigned pkt_len,
+				    const pj_sockaddr_t *dst_addr,
+				    unsigned dst_addr_len)
+{
+    return send_pkt(sess, PJ_FALSE, pkt, pkt_len,
+    		    dst_addr, dst_addr_len);
+}
+
+static pj_status_t turn_on_stun_send_pkt(pj_turn_session *sess,
+				    	 const pj_uint8_t *pkt,
+				    	 unsigned pkt_len,
+				    	 const pj_sockaddr_t *dst_addr,
+				    	 unsigned dst_addr_len)
+{
+    return send_pkt(sess, PJ_TRUE, pkt, pkt_len,
+    		    dst_addr, dst_addr_len);
 }
 
@@ -1170,4 +1256,5 @@
 	    pj_bzero(&asock_cb, sizeof(asock_cb));
 	    asock_cb.on_data_read = &on_data_read_asock;
+	    asock_cb.on_data_sent = &on_data_sent_asock;
 	    asock_cb.on_connect_complete = &on_connect_complete_asock;
 	    status = pj_activesock_create(turn_sock->pool, sock,
@@ -1399,4 +1486,15 @@
 }
 
+static pj_bool_t dataconn_on_data_sent(pj_activesock_t *asock,
+			      	       pj_ioqueue_op_key_t *send_key,
+			      	       pj_ssize_t sent)
+{
+    tcp_data_conn_t *conn = (tcp_data_conn_t*)
+			    pj_activesock_get_user_data(asock);
+    pj_turn_sock *turn_sock = conn->turn_sock;
+
+    return on_data_sent(turn_sock, send_key, sent);
+}
+
 static pj_bool_t dataconn_on_connect_complete(pj_activesock_t *asock,
 					      pj_status_t status)
@@ -1577,4 +1675,5 @@
     pj_bzero(&asock_cb, sizeof(asock_cb));
     asock_cb.on_data_read = &dataconn_on_data_read;
+    asock_cb.on_data_sent = &dataconn_on_data_sent;
     asock_cb.on_connect_complete = &dataconn_on_connect_complete;
     status = pj_activesock_create(pool, sock,
Index: pjproject/trunk/pjnath/src/pjturn-client/client_main.c
===================================================================
--- pjproject/trunk/pjnath/src/pjturn-client/client_main.c	(revision 6070)
+++ pjproject/trunk/pjnath/src/pjturn-client/client_main.c	(revision 6071)
@@ -479,5 +479,5 @@
 					&peer->mapped_addr, 
 					pj_sockaddr_get_len(&peer->mapped_addr));
-	    if (status != PJ_SUCCESS)
+	    if (status != PJ_SUCCESS && status != PJ_EPENDING)
 		my_perror("turn_udp_sendto() failed", status);
 	    break;
Index: pjproject/trunk/pjsip-apps/src/samples/footprint.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/footprint.c	(revision 6070)
+++ pjproject/trunk/pjsip-apps/src/samples/footprint.c	(revision 6071)
@@ -223,5 +223,5 @@
     pj_ice_strans_start_ice(NULL, NULL, NULL, 0, NULL);
     pj_ice_strans_stop_ice(NULL);
-    pj_ice_strans_sendto(NULL, 0, NULL, 0, NULL, 0);
+    pj_ice_strans_sendto2(NULL, 0, NULL, 0, NULL, 0);
 #endif
 
Index: pjproject/trunk/pjsip-apps/src/samples/icedemo.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/icedemo.c	(revision 6070)
+++ pjproject/trunk/pjsip-apps/src/samples/icedemo.c	(revision 6071)
@@ -994,11 +994,11 @@
     }
 
-    status = pj_ice_strans_sendto(icedemo.icest, comp_id, data, strlen(data),
-				  &icedemo.rem.def_addr[comp_id-1],
-				  pj_sockaddr_get_len(&icedemo.rem.def_addr[comp_id-1]));
-    if (status != PJ_SUCCESS)
+    status = pj_ice_strans_sendto2(icedemo.icest, comp_id, data, strlen(data),
+				   &icedemo.rem.def_addr[comp_id-1],
+				   pj_sockaddr_get_len(&icedemo.rem.def_addr[comp_id-1]));
+    if (status != PJ_SUCCESS && status != PJ_EPENDING)
 	icedemo_perror("Error sending data", status);
     else
-	PJ_LOG(3,(THIS_FILE, "Data sent"));
+	PJ_LOG(3,(THIS_FILE, "Data sent/will be sent"));
 }
 
