Index: pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 5752)
+++ pjproject/trunk/pjmedia/src/pjmedia/transport_ice.c	(revision 5788)
@@ -82,4 +82,6 @@
     pj_sockaddr		 rtcp_src_addr;	/**< Actual source RTCP address.    */
     unsigned		 rtcp_src_cnt;  /**< How many pkt from this addr.   */
+    pj_bool_t		 enable_rtcp_mux;/**< Enable RTP& RTCP multiplexing?*/
+    pj_bool_t		 use_rtcp_mux;	/**< Use RTP & RTCP multiplexing?   */
 
     unsigned		 tx_drop_pct;	/**< Percent of tx pkts to drop.    */
@@ -194,4 +196,5 @@
 static const pj_str_t STR_IP6		= { "IP6", 3 };
 static const pj_str_t STR_RTCP		= { "rtcp", 4 };
+static const pj_str_t STR_RTCP_MUX	= { "rtcp-mux", 8 };
 static const pj_str_t STR_BANDW_RR	= { "RR", 2 };
 static const pj_str_t STR_BANDW_RS	= { "RS", 2 };
@@ -505,5 +508,6 @@
 					 unsigned media_index,
 					 unsigned comp_cnt,
-					 pj_bool_t restart_session)
+					 pj_bool_t restart_session,
+					 pj_bool_t rtcp_mux)
 {
     enum { 
@@ -759,5 +763,13 @@
         }
     }
-    
+
+    /* Add a=rtcp-mux attribute */
+    if (rtcp_mux) {
+	pjmedia_sdp_attr *attr;
+
+	attr = PJ_POOL_ZALLOC_T(sdp_pool, pjmedia_sdp_attr);
+    	attr->name = STR_RTCP_MUX;
+    	m->attr[m->attr_count++] = attr;
+    }
 
     return PJ_SUCCESS;
@@ -895,5 +907,6 @@
     /* Encode ICE in SDP */
     status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, 
-				   tp_ice->comp_cnt, PJ_FALSE);
+				   tp_ice->comp_cnt, PJ_FALSE,
+				   tp_ice->enable_rtcp_mux);
     if (status != PJ_SUCCESS) {
 	set_no_ice(tp_ice, "Error encoding SDP answer", status);
@@ -923,4 +936,13 @@
 
     rem_m = rem_sdp->media[media_index];
+
+    /* Check if remote wants RTCP mux */
+    if (tp_ice->enable_rtcp_mux) {
+        pjmedia_sdp_attr *attr;
+
+	attr = pjmedia_sdp_attr_find(rem_m->attr_count, rem_m->attr, 
+				     &STR_RTCP_MUX, NULL);
+	tp_ice->use_rtcp_mux = (attr? PJ_TRUE: PJ_FALSE);
+    }
 
     /* Get the "ice-ufrag" and "ice-pwd" attributes */
@@ -1064,5 +1086,7 @@
 
     /* Check matched component count and ice_mismatch */
-    if (comp1_found && (tp_ice->comp_cnt==1 || !has_rtcp)) {
+    if (comp1_found &&
+        (tp_ice->comp_cnt==1 || !has_rtcp || tp_ice->use_rtcp_mux))
+    {
 	sdp_state->match_comp_cnt = 1;
 	sdp_state->ice_mismatch = PJ_FALSE;
@@ -1123,4 +1147,58 @@
 }
 
+/* Encode information in SDP if ICE is not used */
+static pj_status_t encode_no_ice_in_sdp( struct transport_ice *tp_ice,
+					 pj_pool_t *pool,
+					 pjmedia_sdp_session *sdp_local,
+					 const pjmedia_sdp_session *rem_sdp,
+					 unsigned media_index)
+{
+    if (tp_ice->enable_rtcp_mux) {
+        pjmedia_sdp_media *m = sdp_local->media[media_index];
+        pjmedia_sdp_attr *attr;
+        pj_bool_t add_rtcp_mux = PJ_TRUE;
+
+	if (rem_sdp)
+	    add_rtcp_mux = tp_ice->use_rtcp_mux;
+	else {
+	    /* For subsequent offer, set it to false first since
+	     * we are still waiting for remote answer.
+	     */
+	    tp_ice->use_rtcp_mux = PJ_FALSE;
+	}
+
+        /* Remove RTCP attribute because for subsequent offers/answers,
+         * the address (obtained from transport_get_info() ) may be
+         * incorrect if we are not yet confirmed to use RTCP mux
+         * (because we are still waiting for remote answer) or
+         * if remote rejects it.
+         */
+        pjmedia_sdp_attr_remove_all(&m->attr_count, m->attr, "rtcp");
+	
+	if (!tp_ice->use_rtcp_mux && tp_ice->comp_cnt > 1) {
+	    pj_ice_sess_cand cand;
+	    pj_status_t status;
+	    
+	    status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 2, &cand);
+	    if (status == PJ_SUCCESS) {
+	   	/* Add RTCP attribute if the remote doesn't offer or
+	    	 * rejects it.
+	    	 */
+	    	attr = pjmedia_sdp_attr_create_rtcp(pool, &cand.addr);	
+	    	if (attr)
+	            pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
+	    }
+	}
+
+	/* Add a=rtcp-mux attribute. */
+	if (add_rtcp_mux) {
+	    attr = PJ_POOL_ZALLOC_T(pool, pjmedia_sdp_attr);
+    	    attr->name = STR_RTCP_MUX;
+    	    m->attr[m->attr_count++] = attr;
+	}
+    }
+    return PJ_SUCCESS;
+}
+
 
 /* Verify incoming offer and create initial answer */
@@ -1151,4 +1229,6 @@
     if (tp_ice->rem_offer_state.match_comp_cnt==0) {
 	set_no_ice(tp_ice, "No ICE found in SDP offer", PJ_SUCCESS);
+	encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, rem_sdp,
+			     media_index);
 	return PJ_SUCCESS;
     }
@@ -1164,5 +1244,5 @@
     status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index, 
 				   tp_ice->rem_offer_state.match_comp_cnt,
-				   PJ_FALSE);
+				   PJ_FALSE, tp_ice->use_rtcp_mux);
     if (status != PJ_SUCCESS) {
 	set_no_ice(tp_ice, "Error encoding SDP answer", status);
@@ -1184,4 +1264,5 @@
     if (pj_ice_strans_has_sess(tp_ice->ice_st) == PJ_FALSE) {
 	/* We don't have ICE */
+	encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, NULL, media_index);
 	return PJ_SUCCESS;
     }
@@ -1189,5 +1270,5 @@
     comp_cnt = pj_ice_strans_get_running_comp_cnt(tp_ice->ice_st);
     return encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
-				 comp_cnt, PJ_FALSE);
+				 comp_cnt, PJ_FALSE, tp_ice->enable_rtcp_mux);
 }
 
@@ -1218,4 +1299,6 @@
 	if (tp_ice->rem_offer_state.match_comp_cnt == 0) {
 	    /* Remote no longer offers ICE */
+	    encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, rem_sdp,
+			     	 media_index);
 	    return PJ_SUCCESS;
 	}
@@ -1228,5 +1311,6 @@
 	status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
 				       tp_ice->rem_offer_state.match_comp_cnt,
-				       tp_ice->rem_offer_state.ice_restart);
+				       tp_ice->rem_offer_state.ice_restart,
+				       tp_ice->use_rtcp_mux);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -1241,4 +1325,6 @@
 	if (tp_ice->rem_offer_state.match_comp_cnt == 0) {
 	    /* Remote does not support ICE */
+	    encode_no_ice_in_sdp(tp_ice, sdp_pool, loc_sdp, rem_sdp,
+			     	 media_index);
 	    return PJ_SUCCESS;
 	}
@@ -1262,5 +1348,6 @@
 	status = encode_session_in_sdp(tp_ice, sdp_pool, loc_sdp, media_index,
 				       tp_ice->rem_offer_state.match_comp_cnt,
-				       tp_ice->rem_offer_state.ice_restart);
+				       tp_ice->rem_offer_state.ice_restart,
+				       tp_ice->use_rtcp_mux);
 	if (status != PJ_SUCCESS)
 	    return status;
@@ -1291,4 +1378,5 @@
 
     tp_ice->media_option = options;
+    tp_ice->enable_rtcp_mux = ((options & PJMEDIA_TPMED_RTCP_MUX) != 0);
     tp_ice->oa_role = ROLE_NONE;
     tp_ice->initial_sdp = PJ_TRUE;
@@ -1402,5 +1490,6 @@
 	}
 
-	cand_cnt++;
+	if (!tp_ice->use_rtcp_mux || cand[cand_cnt].comp_id < 2)
+	    cand_cnt++;
     }
 
@@ -1655,5 +1744,7 @@
 
     /* Get RTCP default address */
-    if (tp_ice->comp_cnt > 1) {
+    if (tp_ice->use_rtcp_mux) {
+	pj_sockaddr_cp(&info->sock_info.rtcp_addr_name, &cand.addr);
+    } else if (tp_ice->comp_cnt > 1) {
 	status = pj_ice_strans_get_def_cand(tp_ice->ice_st, 2, &cand);
 	if (status != PJ_SUCCESS)
@@ -1750,4 +1841,9 @@
     tp_ice->rtcp_cb = att_param->rtcp_cb;
 
+    /* Check again if we are multiplexing RTP & RTCP. */
+    tp_ice->use_rtcp_mux = (pj_sockaddr_has_addr(&att_param->rem_addr) &&
+    			    pj_sockaddr_cmp(&att_param->rem_addr,
+					    &att_param->rem_rtcp) == 0);
+
     pj_memcpy(&tp_ice->remote_rtp, &att_param->rem_addr, att_param->addr_len);
     pj_memcpy(&tp_ice->remote_rtcp, &att_param->rem_rtcp, att_param->addr_len);
@@ -1815,10 +1911,12 @@
 {
     struct transport_ice *tp_ice = (struct transport_ice*)tp;
-    if (tp_ice->comp_cnt > 1) {
+
+    if (tp_ice->comp_cnt > 1 || tp_ice->use_rtcp_mux) {
+        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, 2, pkt, size, 
+	}	  
+	return pj_ice_strans_sendto(tp_ice->ice_st, comp_id, pkt, size,
 				    addr, addr_len);
     } else {
@@ -1889,8 +1987,10 @@
 					sizeof(addr_text), 3)));
 
-	    /* Also update remote RTCP address if actual RTCP source
-	     * address is not heard yet.
-	     */
-	    if (!pj_sockaddr_has_addr(&tp_ice->rtcp_src_addr)) {
+	    if (tp_ice->use_rtcp_mux) {
+	    	pj_sockaddr_cp(&tp_ice->remote_rtcp, &tp_ice->remote_rtp);
+	    } else if (!pj_sockaddr_has_addr(&tp_ice->rtcp_src_addr)) {
+	        /* Also update remote RTCP address if actual RTCP source
+	         * address is not heard yet.
+	         */
 		pj_uint16_t port;
 
