Index: pjproject/trunk/pjmedia/include/pjmedia/transport_ice.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/transport_ice.h	(revision 1434)
+++ pjproject/trunk/pjmedia/include/pjmedia/transport_ice.h	(revision 1435)
@@ -39,4 +39,20 @@
 PJ_BEGIN_DECL
 
+
+/**
+ * Structure containing callbacks to receive ICE notifications.
+ */
+typedef struct pjmedia_ice_cb
+{
+    /**
+     * This callback will be called when ICE negotiation completes.
+     *
+     * @param tp	PJMEDIA ICE transport.
+     * @param status	ICE negotiation result, PJ_SUCCESS on success.
+     */
+    void    (*on_ice_complete)(pjmedia_transport *tp,
+			       pj_status_t status);
+
+} pjmedia_ice_cb;
 
 /**
@@ -48,4 +64,5 @@
  * @param comp_cnt	Number of components to be created.
  * @param stun_cfg	Pointer to STUN configuration settings.
+ * @param cb		Optional callbacks.
  * @param p_tp		Pointer to receive the media transport instance.
  *
@@ -56,4 +73,5 @@
 					unsigned comp_cnt,
 					pj_stun_config *stun_cfg,
+					const pjmedia_ice_cb *cb,
 					pjmedia_transport **p_tp);
 
Index: pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 1434)
+++ pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 1435)
@@ -26,4 +26,5 @@
     pjmedia_transport	 base;
     pj_ice_strans	*ice_st;
+    pjmedia_ice_cb	 cb;
 
     pj_time_val		 start_ice;
@@ -96,4 +97,5 @@
 				       unsigned comp_cnt,
 				       pj_stun_config *stun_cfg,
+				       const pjmedia_ice_cb *cb,
 	    			       pjmedia_transport **p_tp)
 {
@@ -123,4 +125,7 @@
     tp_ice->base.op = &tp_ice_op;
     tp_ice->base.type = PJMEDIA_TRANSPORT_TYPE_ICE;
+
+    if (cb)
+	pj_memcpy(&tp_ice->cb, cb, sizeof(pjmedia_ice_cb));
 
     ice_st->user_data = (void*)tp_ice;
@@ -687,5 +692,5 @@
 
 static void ice_on_ice_complete(pj_ice_strans *ice_st, 
-			        pj_status_t status)
+			        pj_status_t result)
 {
     struct transport_ice *tp_ice = (struct transport_ice*) ice_st->user_data;
@@ -699,29 +704,32 @@
     PJ_TIME_VAL_SUB(end_ice, tp_ice->start_ice);
 
-    if (status != PJ_SUCCESS) {
+    if (result != PJ_SUCCESS) {
 	char errmsg[PJ_ERR_MSG_SIZE];
-	pj_strerror(status, errmsg, sizeof(errmsg));
+	pj_strerror(result, errmsg, sizeof(errmsg));
 	PJ_LOG(1,(ice_st->obj_name, 
 		  "ICE negotiation failed after %d:%03ds: %s", 
 		  (int)end_ice.sec, (int)end_ice.msec,
 		  errmsg));
-	return;
-    }
-
-    check = &ice_st->ice->valid_list.checks[0];
+    } else {
+	check = &ice_st->ice->valid_list.checks[0];
     
-    lcand = check->lcand;
-    rcand = check->rcand;
-
-    pj_ansi_strcpy(src_addr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
-    pj_ansi_strcpy(dst_addr, pj_inet_ntoa(rcand->addr.ipv4.sin_addr));
-
-    PJ_LOG(3,(ice_st->obj_name, 
-	      "ICE negotiation completed in %d.%03ds. Sending from "
-	      "%s:%d to %s:%d",
-	      (int)end_ice.sec, (int)end_ice.msec,
-	      src_addr, pj_ntohs(lcand->addr.ipv4.sin_port),
-	      dst_addr, pj_ntohs(rcand->addr.ipv4.sin_port)));
-}
-
-
+	lcand = check->lcand;
+	rcand = check->rcand;
+
+	pj_ansi_strcpy(src_addr, pj_inet_ntoa(lcand->addr.ipv4.sin_addr));
+	pj_ansi_strcpy(dst_addr, pj_inet_ntoa(rcand->addr.ipv4.sin_addr));
+
+	PJ_LOG(4,(ice_st->obj_name, 
+		  "ICE negotiation completed in %d.%03ds. Sending from "
+		  "%s:%d to %s:%d",
+		  (int)end_ice.sec, (int)end_ice.msec,
+		  src_addr, pj_ntohs(lcand->addr.ipv4.sin_port),
+		  dst_addr, pj_ntohs(rcand->addr.ipv4.sin_port)));
+    }
+
+    /* Notify application */
+    if (tp_ice->cb.on_ice_complete)
+	(*tp_ice->cb.on_ice_complete)(&tp_ice->base, result);
+}
+
+
Index: pjproject/trunk/pjnath/include/pjnath/ice_session.h
===================================================================
--- pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 1434)
+++ pjproject/trunk/pjnath/include/pjnath/ice_session.h	(revision 1435)
@@ -465,4 +465,5 @@
     pj_bool_t		 is_complete;		    /**< Complete?	    */
     pj_status_t		 ice_status;		    /**< Error status.	    */
+    pj_timer_entry	 completion_timer;	    /**< To call callback.  */
     pj_ice_sess_cb	 cb;			    /**< Callback.	    */
 
Index: pjproject/trunk/pjnath/src/pjnath/ice_session.c
===================================================================
--- pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1434)
+++ pjproject/trunk/pjnath/src/pjnath/ice_session.c	(revision 1435)
@@ -337,4 +337,10 @@
     }
 
+    if (ice->completion_timer.id) {
+	pj_timer_heap_cancel(ice->stun_cfg.timer_heap, 
+			     &ice->completion_timer);
+	ice->completion_timer.id = PJ_FALSE;
+    }
+
     for (i=0; i<ice->comp_cnt; ++i) {
 	if (ice->comp[i].stun_sess) {
@@ -946,4 +952,18 @@
 }
 
+/* Timer callback to call on_ice_complete() callback */
+static void on_completion_timer(pj_timer_heap_t *th, 
+			        pj_timer_entry *te)
+{
+    pj_ice_sess *ice = (pj_ice_sess*) te->user_data;
+
+    PJ_UNUSED_ARG(th);
+
+    te->id = PJ_FALSE;
+
+    if (ice->cb.on_ice_complete)
+	(*ice->cb.on_ice_complete)(ice, ice->ice_status);
+}
+
 /* This function is called when ICE processing completes */
 static void on_ice_complete(pj_ice_sess *ice, pj_status_t status)
@@ -963,5 +983,13 @@
 	/* Call callback */
 	if (ice->cb.on_ice_complete) {
-	    (*ice->cb.on_ice_complete)(ice, status);
+	    pj_time_val delay = {0, 0};
+
+	    ice->completion_timer.cb = &on_completion_timer;
+	    ice->completion_timer.user_data = (void*) ice;
+	    ice->completion_timer.id = PJ_TRUE;
+
+	    pj_timer_heap_schedule(ice->stun_cfg.timer_heap, 
+				   &ice->completion_timer,
+				   &delay);
 	}
     }
Index: pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1434)
+++ pjproject/trunk/pjsip-apps/src/pjsua/pjsua_app.c	(revision 1435)
@@ -1676,4 +1676,12 @@
 		  "Media for call %d is suspended (hold) by remote",
 		  call_id));
+    } else if (call_info.media_status == PJSUA_CALL_MEDIA_ERROR) {
+	pj_str_t reason = pj_str("ICE negotiation failed");
+
+	PJ_LOG(1,(THIS_FILE,
+		  "Media has reported error, disconnecting call"));
+
+	pjsua_call_hangup(call_id, 500, &reason, NULL);
+
     } else {
 	PJ_LOG(3,(THIS_FILE, 
Index: pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 1434)
+++ pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 1435)
@@ -2376,4 +2376,7 @@
     PJSUA_CALL_MEDIA_REMOTE_HOLD,
 
+    /** The media has reported error (e.g. ICE negotiation) */
+    PJSUA_CALL_MEDIA_ERROR
+
 } pjsua_call_media_status;
 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1434)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 1435)
@@ -538,4 +538,40 @@
 
 
+/* This callback is called when ICE negotiation completes */
+static void on_ice_complete(pjmedia_transport *tp, pj_status_t result)
+{
+    unsigned id, c;
+    pj_bool_t found = PJ_FALSE;
+
+    /* We're only interested with failure case */
+    if (result == PJ_SUCCESS)
+	return;
+
+    /* Find call which has this media transport */
+
+    PJSUA_LOCK();
+
+    for (id=0, c=0; id<PJSUA_MAX_CALLS && c<pjsua_var.call_cnt; ++id) {
+	pjsua_call *call = &pjsua_var.calls[id];
+	if (call->inv) {
+	    ++c;
+
+	    if (call->med_tp == tp) {
+		call->media_st = PJSUA_CALL_MEDIA_ERROR;
+		call->media_dir = PJMEDIA_DIR_NONE;
+		found = PJ_TRUE;
+		break;
+	    }
+	}
+    }
+
+    PJSUA_UNLOCK();
+
+    if (found && pjsua_var.ua_cfg.cb.on_call_media_state) {
+	pjsua_var.ua_cfg.cb.on_call_media_state(id);
+    }
+}
+
+
 /* Create ICE media transports (when ice is enabled) */
 static pj_status_t create_ice_media_transports(pjsua_transport_config *cfg)
@@ -557,4 +593,5 @@
     for (i=0; i<pjsua_var.ua_cfg.max_calls; ++i) {
 	pj_ice_strans_comp comp;
+	pjmedia_ice_cb ice_cb;
 	int next_port;
 #if PJMEDIA_ADVERTISE_RTCP
@@ -564,6 +601,9 @@
 #endif
 
+	pj_bzero(&ice_cb, sizeof(pjmedia_ice_cb));
+	ice_cb.on_ice_complete = &on_ice_complete;
+
 	status = pjmedia_ice_create(pjsua_var.med_endpt, NULL, COMP_CNT,
-				    &pjsua_var.stun_cfg, 
+				    &pjsua_var.stun_cfg, &ice_cb,
 				    &pjsua_var.calls[i].med_tp);
 	if (status != PJ_SUCCESS) {
