Index: /pjproject/trunk/pjnath/include/pjnath/config.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/config.h	(revision 5480)
+++ /pjproject/trunk/pjnath/include/pjnath/config.h	(revision 5481)
@@ -264,5 +264,5 @@
  */
 #ifndef PJ_ICE_MAX_TURN
-#   define PJ_ICE_MAX_TURN			    2
+#   define PJ_ICE_MAX_TURN			    3
 #endif
 
Index: /pjproject/trunk/pjnath/include/pjnath/turn_session.h
===================================================================
--- /pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 5480)
+++ /pjproject/trunk/pjnath/include/pjnath/turn_session.h	(revision 5481)
@@ -330,4 +330,14 @@
      */
     int	    ka_interval;
+
+    /**
+     * The requested ADDRESS-FAMILY. Default is zero to request relay with
+     * address family matched to the one specified in TURN session creation.
+     * Valid values are zero, pj_AF_INET(), and pj_AF_INET6().
+     *
+     * Default value is zero.
+     */
+    int	    af;
+
 
 } pj_turn_alloc_param;
Index: /pjproject/trunk/pjnath/src/pjnath/ice_strans.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 5480)
+++ /pjproject/trunk/pjnath/src/pjnath/ice_strans.c	(revision 5481)
@@ -1992,4 +1992,19 @@
 	comp->default_cand = (unsigned)(cand - comp->cand_list);
 
+	/* Prefer IPv4 relay as default candidate for better connectivity
+	 * with IPv4 endpoints.
+	 */
+	if (cand->addr.addr.sa_family != pj_AF_INET()) {
+	    for (i=0; i<comp->cand_cnt; ++i) {
+		if (comp->cand_list[i].type == PJ_ICE_CAND_TYPE_RELAYED &&
+		    comp->cand_list[i].addr.addr.sa_family == pj_AF_INET() &&
+		    comp->cand_list[i].status == PJ_SUCCESS)
+		{
+		    comp->default_cand = i;
+		    break;
+		}
+	    }
+	}
+
 	PJ_LOG(4,(comp->ice_st->obj_name,
 		  "Comp %d: TURN allocation complete, relay address is %s",
Index: /pjproject/trunk/pjnath/src/pjnath/turn_session.c
===================================================================
--- /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5480)
+++ /pjproject/trunk/pjnath/src/pjnath/turn_session.c	(revision 5481)
@@ -725,4 +725,10 @@
 		     PJ_EINVALIDOP);
 
+    /* Verify address family in allocation param */
+    if (param && param->af) {
+	PJ_ASSERT_RETURN(param->af==pj_AF_INET() || param->af==pj_AF_INET6(),
+			 PJ_EINVAL);
+    }
+
     pj_grp_lock_acquire(sess->grp_lock);
 
@@ -771,9 +777,19 @@
     }
 
-    /* Include ADDRESS-FAMILY for IPv6 request */
-    if (sess->af == pj_AF_INET6()) {
-	enum { IPV6_AF_TYPE = 0x02 << 24 };
-	pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
-				  PJ_STUN_ATTR_REQ_ADDR_TYPE, IPV6_AF_TYPE);
+    /* Include ADDRESS-FAMILY if requested */
+    if (sess->alloc_param.af || sess->af == pj_AF_INET6()) {
+	enum  { IPV4_AF_TYPE = 0x01 << 24,
+		IPV6_AF_TYPE = 0x02 << 24 };
+
+	if (sess->alloc_param.af == pj_AF_INET6() ||
+	    (sess->alloc_param.af == 0 && sess->af == pj_AF_INET6()))
+	{
+	    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
+				      PJ_STUN_ATTR_REQ_ADDR_TYPE, IPV6_AF_TYPE);
+	} else if (sess->alloc_param.af == pj_AF_INET()) {
+	    /* For IPv4, only add the attribute when explicitly requested */
+	    pj_stun_msg_add_uint_attr(tdata->pool, tdata->msg,
+				      PJ_STUN_ATTR_REQ_ADDR_TYPE, IPV4_AF_TYPE);
+	}
     }
 
@@ -1359,5 +1375,10 @@
 	return;
     }
-    if (raddr_attr && raddr_attr->sockaddr.addr.sa_family != sess->af) {
+    if (raddr_attr &&
+	((sess->alloc_param.af != 0 &&
+	  raddr_attr->sockaddr.addr.sa_family != sess->alloc_param.af) ||
+	 (sess->alloc_param.af == 0 &&
+	  raddr_attr->sockaddr.addr.sa_family != sess->af)))
+    {
 	on_session_fail(sess, method, PJNATH_EINSTUNMSG,
 			pj_cstr(&s, "Error: Mismatched RELAY-ADDRESS "
Index: /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c
===================================================================
--- /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 5480)
+++ /pjproject/trunk/pjsip/src/pjsua-lib/pjsua_media.c	(revision 5481)
@@ -927,8 +927,14 @@
 	ice_cfg.turn_tp_cnt = 1;
 	pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[0]);
-	if (use_ipv6 && PJ_ICE_MAX_TURN >= 2) {
-	    ice_cfg.turn_tp_cnt = 2;
+	if (use_ipv6 && PJ_ICE_MAX_TURN >= 3) {
+	    ice_cfg.turn_tp_cnt = 3;
+
 	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[1]);
 	    ice_cfg.turn_tp[1].af = pj_AF_INET6();
+
+	    /* Additional candidate: IPv4 relay via IPv6 TURN server */
+	    pj_ice_strans_turn_cfg_default(&ice_cfg.turn_tp[2]);
+	    ice_cfg.turn_tp[2].af = pj_AF_INET6();
+	    ice_cfg.turn_tp[2].alloc_param.af = pj_AF_INET();
 	}
 
@@ -957,4 +963,11 @@
 		      &acc_cfg->turn_cfg.turn_auth_cred,
 		      sizeof(ice_cfg.turn_tp[1].auth_cred));
+
+	    ice_cfg.turn_tp[2].server    = ice_cfg.turn_tp[0].server;
+	    ice_cfg.turn_tp[2].port      = ice_cfg.turn_tp[0].port;
+	    ice_cfg.turn_tp[2].conn_type = ice_cfg.turn_tp[0].conn_type;
+	    pj_memcpy(&ice_cfg.turn_tp[2].auth_cred, 
+		      &acc_cfg->turn_cfg.turn_auth_cred,
+		      sizeof(ice_cfg.turn_tp[2].auth_cred));
 	}
 
@@ -967,4 +980,8 @@
 	    pj_memcpy(&ice_cfg.turn_tp[1].cfg.qos_params, &cfg->qos_params,
 		      sizeof(cfg->qos_params));
+
+	    ice_cfg.turn_tp[2].cfg.qos_type = cfg->qos_type;
+	    pj_memcpy(&ice_cfg.turn_tp[2].cfg.qos_params, &cfg->qos_params,
+		      sizeof(cfg->qos_params));
 	}
 
@@ -984,10 +1001,18 @@
 	    ice_cfg.turn_tp[1].cfg.port_range =
 			    ice_cfg.turn_tp[0].cfg.port_range;
+
+	    pj_sockaddr_init(pj_AF_INET6(),
+			     &ice_cfg.turn_tp[2].cfg.bound_addr,
+			     &IN6_ADDR_ANY, (pj_uint16_t)cfg->port);
+	    ice_cfg.turn_tp[2].cfg.port_range =
+			    ice_cfg.turn_tp[0].cfg.port_range;
 	}
 
 	/* Configure max packet size */
 	ice_cfg.turn_tp[0].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
-	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1)
+	if (use_ipv6 && ice_cfg.turn_tp_cnt > 1) {
 	    ice_cfg.turn_tp[1].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+	    ice_cfg.turn_tp[2].cfg.max_pkt_size = PJMEDIA_MAX_MRU;
+	}
     }
 
