Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4341)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua.h	(revision 4342)
@@ -2657,4 +2657,14 @@
     pj_bool_t		ice_no_rtcp;
 
+    /**
+     * Send re-INVITE/UPDATE every after ICE connectivity check regardless
+     * the default ICE transport address is changed or not. When this is set
+     * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE
+     * transport address is changed.
+     *
+     * Default: yes
+     */
+    pj_bool_t		ice_always_update;
+
 } pjsua_ice_config;
 
@@ -5552,4 +5562,14 @@
 
     /**
+     * Send re-INVITE/UPDATE every after ICE connectivity check regardless
+     * the default ICE transport address is changed or not. When this is set
+     * to PJ_FALSE, re-INVITE/UPDATE will be sent only when the default ICE
+     * transport address is changed.
+     *
+     * Default: yes
+     */
+    pj_bool_t		ice_always_update;
+
+    /**
      * Enable TURN relay candidate in ICE.
      */
Index: /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h
===================================================================
--- /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 4341)
+++ /pjproject/trunk/pjsip/include/pjsua-lib/pjsua_internal.h	(revision 4342)
@@ -156,8 +156,5 @@
 
     struct {
-	pj_timer_entry	 reinv_timer;/**< Reinvite retry timer.		    */
-	pj_uint32_t	 sdp_ver;    /**< SDP version of the bad answer     */
 	int		 retry_cnt;  /**< Retry count.			    */
-        pj_bool_t        pending;    /**< Pending until CONFIRMED state     */
     } lock_codec;		     /**< Data for codec locking when answer
 					  contains multiple codecs.	    */
@@ -186,4 +183,8 @@
     unsigned		 rem_vid_cnt;  /**< No of active video in last remote
 					    offer.			    */
+    
+    pj_timer_entry	 reinv_timer;  /**< Reinvite retry timer.	    */
+    pj_bool_t	 	 reinv_pending;/**< Pending until CONFIRMED state.  */
+    pj_bool_t	 	 reinv_ice_sent;/**< Has reinvite for ICE upd sent? */
 };
 
@@ -817,4 +818,8 @@
 #endif
 
+/*
+ * Schedule check for the need of re-INVITE/UPDATE after media update
+ */
+void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms);
 
 PJ_END_DECL
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 4341)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 4342)
@@ -107,4 +107,10 @@
 static void xfer_server_on_evsub_state( pjsip_evsub *sub, pjsip_event *event);
 
+/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */
+static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry);
+
+/* Check and send reinvite for lock codec and ICE update */
+static pj_status_t process_pending_reinvite(pjsua_call *call);
+
 /*
  * Reset call descriptor.
@@ -129,4 +135,6 @@
     }
     pjsua_call_setting_default(&call->opt);
+    pj_timer_entry_init(&call->reinv_timer, PJ_FALSE,
+			(void*)(pj_size_t)id, &reinv_timer_cb);
 }
 
@@ -2201,9 +2209,8 @@
     }
 
-    /* Stop lock codec timer, if it is active */
-    if (call->lock_codec.reinv_timer.id) {
-	pjsip_endpt_cancel_timer(pjsua_var.endpt, 
-				 &call->lock_codec.reinv_timer);
-	call->lock_codec.reinv_timer.id = PJ_FALSE;
+    /* Stop reinvite timer, if it is active */
+    if (call->reinv_timer.id) {
+	pjsua_cancel_timer(&call->reinv_timer);
+	call->reinv_timer.id = PJ_FALSE;
     }
 
@@ -2895,10 +2902,6 @@
 
 
-/* Proto */
-static pj_status_t perform_lock_codec(pjsua_call *call);
-
-/* Timer callback to send re-INVITE or UPDATE to lock codec */
-static void reinv_timer_cb(pj_timer_heap_t *th,
-			   pj_timer_entry *entry)
+/* Timer callback to send re-INVITE/UPDATE to lock codec or ICE update */
+static void reinv_timer_cb(pj_timer_heap_t *th, pj_timer_entry *entry)
 {
     pjsua_call_id call_id = (pjsua_call_id)(pj_size_t)entry->user_data;
@@ -2909,13 +2912,19 @@
     PJ_UNUSED_ARG(th);
 
-    pjsua_var.calls[call_id].lock_codec.reinv_timer.id = PJ_FALSE;
+    pjsua_var.calls[call_id].reinv_timer.id = PJ_FALSE;
+
+    pj_log_push_indent();
 
     status = acquire_call("reinv_timer_cb()", call_id, &call, &dlg);
-    if (status != PJ_SUCCESS)
+    if (status != PJ_SUCCESS) {
+	pj_log_pop_indent();
 	return;
-
-    status = perform_lock_codec(call);
+    }
+
+    process_pending_reinvite(call);
 
     pjsip_dlg_dec_lock(dlg);
+
+    pj_log_pop_indent();
 }
 
@@ -2923,5 +2932,5 @@
 /* Check if the specified format can be skipped in counting codecs */
 static pj_bool_t is_non_av_fmt(const pjmedia_sdp_media *m,
-				  const pj_str_t *fmt)
+			       const pj_str_t *fmt)
 {
     const pj_str_t STR_TEL = {"telephone-event", 15};
@@ -2956,192 +2965,58 @@
 
 
-/* Send re-INVITE or UPDATE with new SDP offer to select only one codec
- * out of several codecs presented by callee in his answer.
- */
-static pj_status_t perform_lock_codec(pjsua_call *call)
-{
-    const pj_str_t STR_UPDATE = {"UPDATE", 6};
-    const pjmedia_sdp_session *local_sdp = NULL, *new_sdp;
+/* Schedule check for the need of re-INVITE/UPDATE after media update, cases:
+ * - lock codec if remote answerer has given us more than one codecs
+ * - update ICE default transport address if it has changed after ICE
+ *   connectivity check.
+ */
+void pjsua_call_schedule_reinvite_check(pjsua_call *call, unsigned delay_ms)
+{
+    pj_time_val delay;
+
+    /* Stop reinvite timer, if it is active */
+    if (call->reinv_timer.id)
+	pjsua_cancel_timer(&call->reinv_timer);
+
+    delay.sec = 0;
+    delay.msec = delay_ms;
+    pj_time_val_normalize(&delay);
+    call->reinv_timer.id = PJ_TRUE;
+    pjsua_schedule_timer(&call->reinv_timer, &delay);
+}
+
+
+/* Check if lock codec is needed */
+static pj_bool_t check_lock_codec(pjsua_call *call)
+{
+    const pjmedia_sdp_session *local_sdp, *remote_sdp;
+    pj_bool_t has_mult_fmt = PJ_FALSE;
     unsigned i;
-    pj_bool_t rem_can_update;
-    pj_bool_t need_lock_codec = PJ_FALSE;
-    pjsip_tx_data *tdata;
     pj_status_t status;
 
-    PJ_ASSERT_RETURN(call->lock_codec.reinv_timer.id==PJ_FALSE,
-		     PJ_EINVALIDOP);
-
-    /* Verify if another SDP negotiation is in progress, e.g: session timer
-     * or another re-INVITE.
-     */
-    if (call->inv==NULL || call->inv->neg==NULL ||
-	pjmedia_sdp_neg_get_state(call->inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
-    {
-	return PJMEDIA_SDPNEG_EINSTATE;
-    }
-
-    /* Don't do this if call is disconnecting! */
-    if (call->inv->state > PJSIP_INV_STATE_CONFIRMED ||
-	call->inv->cause >= 200)
-    {
-	return PJ_EINVALIDOP;
-    }
-
-    /* Verify if another SDP negotiation has been completed by comparing
-     * the SDP version.
-     */
+    /* Check if lock codec is disabled */
+    if (!pjsua_var.acc[call->acc_id].cfg.lock_codec)
+	return PJ_FALSE;
+
+    /* Check lock codec retry count */
+    if (call->lock_codec.retry_cnt >= LOCK_CODEC_MAX_RETRY)
+        return PJ_FALSE;
+
+    /* Check if we are the answerer, we shouldn't need to lock codec */
+    if (!call->inv->neg || !pjmedia_sdp_neg_was_answer_remote(call->inv->neg))
+        return PJ_FALSE;
+
+    /* Check if remote answerer has given us more than one codecs. */
     status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &local_sdp);
     if (status != PJ_SUCCESS)
-	return status;
-    if (local_sdp->origin.version > call->lock_codec.sdp_ver)
-	return PJMEDIA_SDP_EINVER;
-
-    PJ_LOG(3, (THIS_FILE, "Updating media session to use only one codec.."));
-
-    /* Update the new offer so it contains only a codec. Note that formats
-     * order in the offer should have been matched to the answer, so we can
-     * just directly update the offer without looking-up the answer.
-     */
-    new_sdp = pjmedia_sdp_session_clone(call->inv->pool_prov, local_sdp);
-
-    for (i = 0; i < call->med_cnt; ++i) {
-	unsigned j = 0, codec_cnt = 0;
-	const pjmedia_sdp_media *ref_m;
-	pjmedia_sdp_media *m;
-	pjsua_call_media *call_med = &call->media[i];
-
-	/* Verify if media is deactivated */
-	if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
-	    call_med->state == PJSUA_CALL_MEDIA_ERROR ||
-	    call_med->dir == PJMEDIA_DIR_NONE)
-	{
-	    continue;
-	}
-
-	ref_m = local_sdp->media[i];
-	m = new_sdp->media[i];
-
-	/* Verify that media must be active. */
-	pj_assert(ref_m->desc.port);
-
-	while (j < m->desc.fmt_count) {
-	    pjmedia_sdp_attr *a;
-	    pj_str_t *fmt = &m->desc.fmt[j];
-
-	    if (is_non_av_fmt(m, fmt) || (++codec_cnt == 1)) {
-		++j;
-		continue;
-	    }
-
-	    /* Remove format */
-	    a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "rtpmap", fmt);
-	    if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
-	    a = pjmedia_sdp_attr_find2(m->attr_count, m->attr, "fmtp", fmt);
-	    if (a) pjmedia_sdp_attr_remove(&m->attr_count, m->attr, a);
-	    pj_array_erase(m->desc.fmt, sizeof(m->desc.fmt[0]),
-			   m->desc.fmt_count, j);
-	    --m->desc.fmt_count;
-	}
-	
-	need_lock_codec |= (ref_m->desc.fmt_count > m->desc.fmt_count);
-    }
-
-    /* Last check if SDP trully needs to be updated. It is possible that OA
-     * negotiations have completed and SDP has changed but we didn't
-     * increase the SDP version (should not happen!).
-     */
-    if (!need_lock_codec)
-	return PJ_SUCCESS;
-
-    /* Send UPDATE or re-INVITE */
-    rem_can_update = pjsip_dlg_remote_has_cap(call->inv->dlg,
-					      PJSIP_H_ALLOW,
-					      NULL, &STR_UPDATE) ==
-						PJSIP_DIALOG_CAP_SUPPORTED;
-    if (rem_can_update) {
-	status = pjsip_inv_update(call->inv, NULL, new_sdp, &tdata);
-    } else {
-	status = pjsip_inv_reinvite(call->inv, NULL, new_sdp, &tdata);
-    }
-
-    if (status==PJ_EINVALIDOP &&
-	++call->lock_codec.retry_cnt <= LOCK_CODEC_MAX_RETRY)
-    {
-	/* Ups, let's reschedule again */
-	pj_time_val delay = {0, LOCK_CODEC_RETRY_INTERVAL};
-	pj_time_val_normalize(&delay);
-	call->lock_codec.reinv_timer.id = PJ_TRUE;
-	pjsip_endpt_schedule_timer(pjsua_var.endpt,
-				   &call->lock_codec.reinv_timer, &delay);
-	return status;
-    } else if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE to lock codec",
-		     status);
-	return status;
-    }
-
-    /* Send the UPDATE/re-INVITE request */
-    status = pjsip_inv_send_msg(call->inv, tdata);
-    if (status != PJ_SUCCESS) {
-	pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE in lock codec",
-		     status);
-	return status;
-    }
-
-    return status;
-}
-
-/* Check if remote answerer has given us more than one codecs. If so,
- * create another offer with one codec only to lock down the codec.
- */
-static pj_status_t lock_codec(pjsua_call *call)
-{
-    pjsip_inv_session *inv = call->inv;
-    const pjmedia_sdp_session *local_sdp, *remote_sdp;
-    pj_time_val delay = {0, 0};
-    const pj_str_t st_update = {"UPDATE", 6};
-    unsigned i;
-    pj_bool_t has_mult_fmt = PJ_FALSE;
-    pj_status_t status;
-
-    if (!pjsua_var.acc[call->acc_id].cfg.lock_codec) {
-	return PJ_SUCCESS;
-    }
-
-    /* Stop lock codec timer, if it is active */
-    if (call->lock_codec.reinv_timer.id) {
-	pjsip_endpt_cancel_timer(pjsua_var.endpt,
-				 &call->lock_codec.reinv_timer);
-	call->lock_codec.reinv_timer.id = PJ_FALSE;
-    }
-
-    /* Skip this if we are the answerer */
-    if (!inv->neg || !pjmedia_sdp_neg_was_answer_remote(inv->neg)) {
-        return PJ_SUCCESS;
-    }
-
-    /* Delay this when the SDP negotiation done in call state EARLY and
-     * remote does not support UPDATE method.
-     */
-    if (inv->state == PJSIP_INV_STATE_EARLY && 
-	pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &st_update)!=
-	PJSIP_DIALOG_CAP_SUPPORTED)
-    {
-        call->lock_codec.pending = PJ_TRUE;
-        return PJ_SUCCESS;
-    }
-
-    status = pjmedia_sdp_neg_get_active_local(inv->neg, &local_sdp);
+	return PJ_FALSE;
+    status = pjmedia_sdp_neg_get_active_remote(call->inv->neg, &remote_sdp);
     if (status != PJ_SUCCESS)
-	return status;
-    status = pjmedia_sdp_neg_get_active_remote(inv->neg, &remote_sdp);
-    if (status != PJ_SUCCESS)
-	return status;
-
-    /* Find multiple codecs answer in all media */
-    for (i = 0; i < call->med_cnt; ++i) {
+	return PJ_FALSE;
+
+    for (i = 0; i < call->med_cnt && !has_mult_fmt; ++i) {
 	pjsua_call_media *call_med = &call->media[i];
 	const pjmedia_sdp_media *rem_m, *loc_m;
 	unsigned codec_cnt = 0;
+	unsigned j;
 
 	/* Skip this if the media is inactive or error */
@@ -3164,48 +3039,265 @@
 
 	/* Count the formats in the answer. */
-	if (rem_m->desc.fmt_count==1) {
-	    codec_cnt = 1;
-	} else {
-	    unsigned j;
-	    for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
-		if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]))
-		    ++codec_cnt;
+	for (j=0; j<rem_m->desc.fmt_count && codec_cnt <= 1; ++j) {
+	    if (!is_non_av_fmt(rem_m, &rem_m->desc.fmt[j]) && ++codec_cnt > 1)
+		has_mult_fmt = PJ_TRUE;
+	}
+    }
+
+    /* Reset retry count when remote answer has one codec */
+    if (!has_mult_fmt)
+	call->lock_codec.retry_cnt = 0;
+
+    return has_mult_fmt;
+}
+
+/* Check if ICE setup is complete and if it needs to send reinvite */
+static pj_bool_t check_ice_complete(pjsua_call *call, pj_bool_t *need_reinv)
+{
+    pj_bool_t ice_need_reinv = PJ_FALSE;
+    pj_bool_t ice_complete = PJ_TRUE;
+    unsigned i;
+
+    /* Check if ICE setup is complete and if it needs reinvite */
+    for (i = 0; i < call->med_cnt && ice_complete; ++i) {
+	pjsua_call_media *call_med = &call->media[i];
+	pjmedia_transport_info tpinfo;
+	unsigned j;
+	
+	if (call_med->tp_st == PJSUA_MED_TP_NULL ||
+	    call_med->tp_st == PJSUA_MED_TP_DISABLED ||
+	    call_med->state == PJSUA_CALL_MEDIA_ERROR)
+	{
+	    continue;
+	}
+	
+	pjmedia_transport_info_init(&tpinfo);
+	pjmedia_transport_get_info(call_med->tp, &tpinfo);
+	for (j = 0; j < tpinfo.specific_info_cnt; ++j) {
+	    pjmedia_ice_transport_info *ice_info;
+	    
+	    if (tpinfo.spc_info[j].type != PJMEDIA_TRANSPORT_TYPE_ICE)
+		continue;
+	    
+	    ice_info = (pjmedia_ice_transport_info*)tpinfo.spc_info[j].buffer;
+	    
+	    /* Check if ICE setup not completed yet */
+	    if (ice_info->sess_state < PJ_ICE_STRANS_STATE_RUNNING) {
+		ice_complete = PJ_FALSE;
+		break;
 	    }
-	}
-
-	if (codec_cnt > 1) {
-	    has_mult_fmt = PJ_TRUE;
+	    
+	    /* Check if ICE needs to send reinvite */
+	    if (!ice_need_reinv &&
+		ice_info->sess_state == PJ_ICE_STRANS_STATE_RUNNING &&
+		ice_info->role == PJ_ICE_SESS_ROLE_CONTROLLING)
+	    {
+		pjsua_ice_config *cfg=&pjsua_var.acc[call->acc_id].cfg.ice_cfg;
+		if ((cfg->ice_always_update && !call->reinv_ice_sent) ||
+		    pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
+				    &call_med->rtp_addr))
+		{
+		    ice_need_reinv = PJ_TRUE;
+		}
+	    }
 	    break;
 	}
     }
-
-    /* Each media in the answer already contains single codec. */
-    if (!has_mult_fmt) {
-	call->lock_codec.retry_cnt = 0;
+    
+    if (ice_complete && need_reinv)
+	*need_reinv = ice_need_reinv;
+    
+    return ice_complete;
+}
+
+/* Check and send reinvite for lock codec and ICE update */
+static pj_status_t process_pending_reinvite(pjsua_call *call)
+{
+    const pj_str_t ST_UPDATE = {"UPDATE", 6};
+    pj_pool_t *pool = call->inv->pool_prov;
+    pjsip_inv_session *inv = call->inv;
+    pj_bool_t ice_need_reinv;
+    pj_bool_t ice_completed;
+    pj_bool_t need_lock_codec;
+    pj_bool_t rem_can_update;
+    pjmedia_sdp_session *new_offer;
+    pjsip_tx_data *tdata;
+    unsigned i;
+    pj_status_t status;
+
+    /* Verify if another SDP negotiation is in progress, e.g: session timer
+     * or another re-INVITE.
+     */
+    if (inv==NULL || inv->neg==NULL ||
+	pjmedia_sdp_neg_get_state(inv->neg)!=PJMEDIA_SDP_NEG_STATE_DONE)
+    {
+	return PJMEDIA_SDPNEG_EINSTATE;
+    }
+
+    /* Don't do this if call is disconnecting! */
+    if (inv->state > PJSIP_INV_STATE_CONFIRMED || inv->cause >= 200)
+    {
+	return PJ_EINVALIDOP;
+    }
+
+    /* Delay this when the SDP negotiation done in call state EARLY and
+     * remote does not support UPDATE method.
+     */
+    if (inv->state == PJSIP_INV_STATE_EARLY && 
+	pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL, &ST_UPDATE)!=
+	PJSIP_DIALOG_CAP_SUPPORTED)
+    {
+        call->reinv_pending = PJ_TRUE;
+        return PJ_EPENDING;
+    }
+
+    /* Check if ICE setup is complete and if it needs reinvite */
+    ice_completed = check_ice_complete(call, &ice_need_reinv);
+    if (!ice_completed)
+	return PJ_EPENDING;
+
+    /* Check if we need to lock codec */
+    need_lock_codec = check_lock_codec(call);
+
+    /* Check if reinvite is really needed */
+    if (!need_lock_codec && !ice_need_reinv)
 	return PJ_SUCCESS;
-    }
-
-    /* Remote keeps answering with multiple codecs, let's just give up
-     * locking codec to avoid infinite retry loop.
+
+    
+    /* Okay! So we need to send re-INVITE/UPDATE */
+
+    /* Check if remote support UPDATE */
+    rem_can_update = pjsip_dlg_remote_has_cap(inv->dlg, PJSIP_H_ALLOW, NULL,
+					      &ST_UPDATE) ==
+						PJSIP_DIALOG_CAP_SUPPORTED;
+
+    /* Logging stuff */
+    {
+	const char *ST_ICE_UPDATE = "ICE transport address after "
+				    "ICE negotiation";
+	const char *ST_LOCK_CODEC = "media session to use only one codec";
+	PJ_LOG(4,(THIS_FILE, "Call %d sending %s for updating %s%s%s",
+		  call->index,
+		  (rem_can_update? "UPDATE" : "re-INVITE"),
+		  (ice_need_reinv? ST_ICE_UPDATE : ST_LOCK_CODEC),
+		  (ice_need_reinv && need_lock_codec? " and " : ""),
+		  (ice_need_reinv && need_lock_codec? ST_LOCK_CODEC : "")
+		  ));
+    }
+    
+    /* Generate SDP re-offer */
+    status = pjsua_media_channel_create_sdp(call->index, pool, NULL,
+					    &new_offer, NULL);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Unable to create local SDP", status);
+	return status;
+    }
+
+    /* Update the new offer so it contains only a codec. Note that
+     * SDP nego has removed unmatched codecs from the offer and the codec
+     * order in the offer has been matched to the answer, so we'll override
+     * the codecs in the just generated SDP with the ones from the active
+     * local SDP and leave just one codec for the next SDP re-offer.
      */
-    if (++call->lock_codec.retry_cnt > LOCK_CODEC_MAX_RETRY)
-        return PJ_SUCCESS;
-
-    PJ_LOG(4, (THIS_FILE, "Got answer with multiple codecs, scheduling "
-			  "updating media session to use only one codec.."));
-
-    call->lock_codec.sdp_ver = local_sdp->origin.version;
-
-    /* Can't send UPDATE or re-INVITE now, so just schedule it immediately.
-     * See: https://trac.pjsip.org/repos/ticket/1149
-     */
-    pj_timer_entry_init(&call->lock_codec.reinv_timer, PJ_TRUE,
-			(void*)(pj_size_t)call->index,
-			&reinv_timer_cb);
-    pjsip_endpt_schedule_timer(pjsua_var.endpt,
-			       &call->lock_codec.reinv_timer, &delay);
-
+    if (need_lock_codec) {
+	const pjmedia_sdp_session *ref_sdp;
+	
+	/* Get local active SDP as reference */
+	status = pjmedia_sdp_neg_get_active_local(call->inv->neg, &ref_sdp);
+	if (status != PJ_SUCCESS)
+	    return status;
+	
+	/* Verify media count. Note that remote may add/remove media line
+	 * in the answer. When answer has less media, it must have been
+	 * handled by pjsua_media_channel_update() as disabled media.
+	 * When answer has more media, it must have been ignored (treated
+	 * as non-exist) anywhere. Local media count should not be updated
+	 * at this point, as modifying media count operation (i.e: reinvite,
+	 * update, vid_set_strm) is currently blocking, protected with
+	 * dialog mutex, and eventually reset SDP nego state to LOCAL OFFER.
+	 */
+	if (call->med_cnt != ref_sdp->media_count ||
+	    ref_sdp->media_count != new_offer->media_count)
+	{
+	    /* Anyway, just in case, let's just return error */
+	    return PJMEDIA_SDPNEG_EINSTATE;
+	}
+
+	for (i = 0; i < call->med_cnt; ++i) {
+	    unsigned j, codec_cnt = 0;
+	    const pjmedia_sdp_media *ref_m = ref_sdp->media[i];
+	    pjmedia_sdp_media *m = new_offer->media[i];
+	    pjsua_call_media *call_med = &call->media[i];
+    
+	    /* Verify if media is deactivated */
+	    if (call_med->state == PJSUA_CALL_MEDIA_NONE ||
+		call_med->state == PJSUA_CALL_MEDIA_ERROR ||
+		call_med->dir == PJMEDIA_DIR_NONE)
+	    {
+		continue;
+	    }
+    
+	    /* Reset formats */
+	    m->desc.fmt_count = 0;
+	    pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "rtpmap");
+	    pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "fmtp");
+	    
+	    /* Copy only the first format + any non-AV formats from
+	     * the active local SDP.
+	     */
+	    for (j = 0; j < ref_m->desc.fmt_count; ++j) {
+		const pj_str_t *fmt = &ref_m->desc.fmt[j];
+
+		if (is_non_av_fmt(ref_m, fmt) || (++codec_cnt == 1)) {
+		    pjmedia_sdp_attr *a;
+		    
+		    m->desc.fmt[m->desc.fmt_count++] = *fmt;
+		    a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr,
+					       "rtpmap", fmt);
+		    if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
+		    a = pjmedia_sdp_attr_find2(ref_m->attr_count, ref_m->attr,
+					       "fmtp", fmt);
+		    if (a) pjmedia_sdp_attr_add(&m->attr_count, m->attr, a);
+		}
+    
+	    }
+	}
+    }
+    
+    if (rem_can_update) {
+	status = pjsip_inv_update(inv, NULL, new_offer, &tdata);
+    } else {
+	status = pjsip_inv_reinvite(inv, NULL, new_offer, &tdata);
+    }
+
+    if (status==PJ_EINVALIDOP &&
+	++call->lock_codec.retry_cnt < LOCK_CODEC_MAX_RETRY)
+    {
+	/* Ups, let's reschedule again */
+	pjsua_call_schedule_reinvite_check(call, LOCK_CODEC_RETRY_INTERVAL);
+	return PJ_SUCCESS;
+    } else if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error creating UPDATE/re-INVITE",
+		     status);
+	return status;
+    }
+
+    /* Send the UPDATE/re-INVITE request */
+    status = pjsip_inv_send_msg(inv, tdata);
+    if (status != PJ_SUCCESS) {
+	pjsua_perror(THIS_FILE, "Error sending UPDATE/re-INVITE",
+		     status);
+	return status;
+    }
+
+    /* Update flags */
+    if (ice_need_reinv)
+	call->reinv_ice_sent = PJ_TRUE;
+    if (need_lock_codec)
+	++call->lock_codec.retry_cnt;
+    
     return PJ_SUCCESS;
 }
+
 
 /*
@@ -3243,14 +3335,10 @@
 	    pj_gettimeofday(&call->conn_time);
 
-            /* See if lock codec was pended as media update was done in the
+            /* See if auto reinvite was pended as media update was done in the
              * EARLY state and remote does not support UPDATE.
              */
-            if (call->lock_codec.pending) {
-		pj_status_t status;
-		status = lock_codec(call);
-		if (status != PJ_SUCCESS) {
-		    pjsua_perror(THIS_FILE, "Unable to lock codec", status);
-                }
-                call->lock_codec.pending = PJ_FALSE;
+            if (call->reinv_pending) {
+		call->reinv_pending = PJ_FALSE;
+		pjsua_call_schedule_reinvite_check(call, 0);
 	    }
 	    break;
@@ -3274,9 +3362,8 @@
 	    }
 
-	    /* Stop lock codec timer, if it is active */
-	    if (call->lock_codec.reinv_timer.id) {
-		pjsip_endpt_cancel_timer(pjsua_var.endpt, 
-					 &call->lock_codec.reinv_timer);
-		call->lock_codec.reinv_timer.id = PJ_FALSE;
+	    /* Stop reinvite timer, if it is active */
+	    if (call->reinv_timer.id) {
+		pjsua_cancel_timer(&call->reinv_timer);
+		call->reinv_timer.id = PJ_FALSE;
 	    }
 	    break;
@@ -3561,8 +3648,5 @@
 
     /* Ticket #476: make sure only one codec is specified in the answer. */
-    status = lock_codec(call);
-    if (status != PJ_SUCCESS) {
-        pjsua_perror(THIS_FILE, "Unable to lock codec", status);
-    }
+    pjsua_call_schedule_reinvite_check(call, 0);
 
     /* Call application callback, if any */
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 4341)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_core.c	(revision 4342)
@@ -212,4 +212,5 @@
     dst->ice_opt = src->ice_opt;
     dst->ice_no_rtcp = src->ice_no_rtcp;
+    dst->ice_always_update = src->ice_always_update;
 }
 
@@ -325,4 +326,5 @@
 
     cfg->ice_max_host_cands = -1;
+    cfg->ice_always_update = PJ_TRUE;
     pj_ice_sess_options_default(&cfg->ice_opt);
 
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 4341)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 4342)
@@ -586,73 +586,4 @@
 }
 
-static void med_tp_nego_timer_cb(void *user_data)
-{
-    pjsua_call *call;
-    unsigned med_idx = (unsigned)(((long)user_data) & 0xFFFF);
-    pjsua_call_media *call_med;
-    pjmedia_transport *tp;
-    pjmedia_transport_info tpinfo;
-    pjmedia_ice_transport_info *ii = NULL;
-    unsigned i;
-    pjsip_dialog *dlg = NULL;
-
-    if ((acquire_call("med_tp_nego_timer_cb", ((long)user_data) >> 16,
-	             &call, &dlg) != PJ_SUCCESS) ||
-        (med_idx >= call->med_cnt))
-    {
-        /* Call have been terminated or media has been removed */
-	return;
-    }
-
-    call_med = &call->media[med_idx];
-    tp = call_med->tp;
-
-    if (!tp)
-        return;
-
-    /* Send UPDATE if default transport address is different than
-     * what was advertised (ticket #881)
-     */
-
-    pjmedia_transport_info_init(&tpinfo);
-    pjmedia_transport_get_info(tp, &tpinfo);
-    for (i=0; i<tpinfo.specific_info_cnt; ++i) {
-        if (tpinfo.spc_info[i].type==PJMEDIA_TRANSPORT_TYPE_ICE) {
-	    ii = (pjmedia_ice_transport_info*)
-	    tpinfo.spc_info[i].buffer;
-	    break;
-	}
-    }
-
-    if (ii && ii->role==PJ_ICE_SESS_ROLE_CONTROLLING &&
-	pj_sockaddr_cmp(&tpinfo.sock_info.rtp_addr_name,
-		        &call_med->rtp_addr))
-    {
-        pj_bool_t use_update;
-	const pj_str_t STR_UPDATE = { "UPDATE", 6 };
-	pjsip_dialog_cap_status support_update;
-	pjsip_dialog *dlg;
-
-	dlg = call_med->call->inv->dlg;
-	support_update = pjsip_dlg_remote_has_cap(dlg, PJSIP_H_ALLOW,
-					          NULL, &STR_UPDATE);
-        use_update = (support_update == PJSIP_DIALOG_CAP_SUPPORTED);
-
-	PJ_LOG(4,(THIS_FILE, 
-                  "ICE default transport address has changed for "
-		  "call %d, sending %s",
-		  call_med->call->index,
-		  (use_update ? "UPDATE" : "re-INVITE")));
-
-        if (use_update)
-	    pjsua_call_update(call_med->call->index, 0, NULL);
-	else
-	    pjsua_call_reinvite(call_med->call->index, 0, NULL);
-    }
-
-    if (dlg)
-        pjsip_dlg_dec_lock(dlg);
-}
-
 
 /* This callback is called when ICE negotiation completes */
@@ -662,8 +593,11 @@
 {
     pjsua_call_media *call_med = (pjsua_call_media*)tp->user_data;
+    pjsua_call *call;
 
     if (!call_med)
 	return;
 
+    call = call_med->call;
+    
     switch (op) {
     case PJ_ICE_STRANS_OP_INIT:
@@ -672,16 +606,20 @@
 	break;
     case PJ_ICE_STRANS_OP_NEGOTIATION:
-	if (result != PJ_SUCCESS) {
+	if (result == PJ_SUCCESS) {
+            /* Update RTP address */
+            pjmedia_transport_info tpinfo;
+            pjmedia_transport_info_init(&tpinfo);
+            pjmedia_transport_get_info(call_med->tp, &tpinfo);
+            pj_sockaddr_cp(&call_med->rtp_addr, &tpinfo.sock_info.rtp_addr_name);
+        } else {
 	    call_med->state = PJSUA_CALL_MEDIA_ERROR;
 	    call_med->dir = PJMEDIA_DIR_NONE;
-
-	    if (call_med->call && pjsua_var.ua_cfg.cb.on_call_media_state) {
-		pjsua_var.ua_cfg.cb.on_call_media_state(call_med->call->index);
-	    }
-	} else if (call_med->call) {
-            void *data = (void*)(long)( (call_med->call->index<<16) |
-                                        (call_med->idx & 0xFFFF) );
-            pjsua_schedule_timer2(&med_tp_nego_timer_cb, data, 1);
+	    if (call && pjsua_var.ua_cfg.cb.on_call_media_state) {
+		pjsua_var.ua_cfg.cb.on_call_media_state(call->index);
+	    }
         }
+	/* Check if default ICE transport address is changed */
+        call->reinv_ice_sent = PJ_FALSE;
+	pjsua_call_schedule_reinvite_check(call, 0);
 	break;
     case PJ_ICE_STRANS_OP_KEEP_ALIVE:
@@ -689,5 +627,5 @@
 	    PJ_PERROR(4,(THIS_FILE, result,
 		         "ICE keep alive failure for transport %d:%d",
-		         call_med->call->index, call_med->idx));
+		         call->index, call_med->idx));
 	}
         if (pjsua_var.ua_cfg.cb.on_call_media_transport_state) {
@@ -700,8 +638,8 @@
             info.ext_info = &op;
 	    (*pjsua_var.ua_cfg.cb.on_call_media_transport_state)(
-                call_med->call->index, &info);
+                call->index, &info);
         }
 	if (pjsua_var.ua_cfg.cb.on_ice_transport_error) {
-	    pjsua_call_id id = call_med->call->index;
+	    pjsua_call_id id = call->index;
 	    (*pjsua_var.ua_cfg.cb.on_ice_transport_error)(id, op, result,
 							  NULL);
@@ -856,5 +794,5 @@
     pjmedia_transport_simulate_lost(call_med->tp, PJMEDIA_DIR_DECODING,
 				    pjsua_var.media_cfg.rx_drop_pct);
-
+    
     return PJ_SUCCESS;
 
@@ -1340,5 +1278,5 @@
                 call_med->med_create_cb = &call_media_init_cb;
                 call_med->med_init_cb = cb;
-
+                
 	        return PJ_EPENDING;
 	    }
