Index: pjproject/trunk/pjmedia/include/pjmedia/config.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 567)
+++ pjproject/trunk/pjmedia/include/pjmedia/config.h	(revision 568)
@@ -156,4 +156,14 @@
 
 /**
+ * Support for sending and decoding RTCP port in SDP (RFC 3605).
+ * Default is yes.
+ */
+#ifndef PJMEDIA_HAS_RTCP_IN_SDP
+#   define PJMEDIA_HAS_RTCP_IN_SDP		1
+#endif
+
+
+
+/**
  * @}
  */
Index: pjproject/trunk/pjmedia/include/pjmedia/errno.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 567)
+++ pjproject/trunk/pjmedia/include/pjmedia/errno.h	(revision 568)
@@ -141,7 +141,12 @@
 /**
  * @hideinitializer
- * Invalid fmtp attribute.
+ * Invalid SDP "fmtp" attribute.
  */
 #define PJMEDIA_SDP_EINFMTP	    (PJMEDIA_ERRNO_START+34)    /* 220034 */
+/**
+ * @hideinitializer
+ * Invalid SDP "rtcp" attribute.
+ */
+#define PJMEDIA_SDP_EINRTCP	    (PJMEDIA_ERRNO_START+35)    /* 220035 */
 
 
Index: pjproject/trunk/pjmedia/include/pjmedia/sdp.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/sdp.h	(revision 567)
+++ pjproject/trunk/pjmedia/include/pjmedia/sdp.h	(revision 568)
@@ -292,4 +292,30 @@
 PJ_DECL(pj_status_t) pjmedia_sdp_attr_get_fmtp(const pjmedia_sdp_attr *attr,
 					       pjmedia_sdp_fmtp *fmtp);
+
+
+/**
+ * This structure describes SDP \a rtcp attribute.
+ */
+typedef struct pjmedia_sdp_rtcp_attr
+{
+    unsigned	port;	    /**< RTCP port number.	    */
+    pj_str_t	net_type;   /**< Optional network type.	    */
+    pj_str_t	addr_type;  /**< Optional address type.	    */
+    pj_str_t	addr;	    /**< Optional address.	    */
+} pjmedia_sdp_rtcp_attr;
+
+
+/**
+ * Parse a generic SDP attribute to get SDP rtcp attribute values.
+ *
+ * @param attr		Generic attribute to be converted to rtcp, which
+ *			name must be "rtcp".
+ * @param rtcp		SDP rtcp attribute to be initialized.
+ *
+ * @return		PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr,
+					       pjmedia_sdp_rtcp_attr *rtcp);
+
 
 
Index: pjproject/trunk/pjmedia/include/pjmedia/stream.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 567)
+++ pjproject/trunk/pjmedia/include/pjmedia/stream.h	(revision 568)
@@ -92,4 +92,7 @@
     pjmedia_dir		dir;	    /**< Media direction.		    */
     pj_sockaddr_in	rem_addr;   /**< Remote RTP address		    */
+    pj_sockaddr_in	rem_rtcp;   /**< Optional remote RTCP address. If
+					 sin_family is zero, the RTP address
+					 will be calculated from RTP.	    */
     pjmedia_codec_info	fmt;	    /**< Incoming codec format info.	    */
     pjmedia_codec_param *param;	    /**< Optional codec param.		    */
Index: pjproject/trunk/pjmedia/include/pjmedia/transport.h
===================================================================
--- pjproject/trunk/pjmedia/include/pjmedia/transport.h	(revision 567)
+++ pjproject/trunk/pjmedia/include/pjmedia/transport.h	(revision 568)
@@ -74,4 +74,5 @@
 			  void *user_data,
 			  const pj_sockaddr_t *rem_addr,
+			  const pj_sockaddr_t *rem_rtcp,
 			  unsigned addr_len,
 			  void (*rtp_cb)(void *user_data,
@@ -155,4 +156,8 @@
  *		    called.
  * @param rem_addr  Remote RTP address to send RTP packet to.
+ * @param rem_rtcp  Optional remote RTCP address. If the argument is NULL
+ *		    or if the address is zero, the RTCP address will be
+ *		    calculated from the RTP address (which is RTP port
+ *		    plus one).
  * @param addr_len  Length of the remote address.
  * @param rtp_cb    Callback to be called when RTP packet is received on
@@ -166,4 +171,5 @@
 					        void *user_data,
 					        const pj_sockaddr_t *rem_addr,
+						const pj_sockaddr_t *rem_rtcp,
 					        unsigned addr_len,
 					        void (*rtp_cb)(void *user_data,
@@ -174,5 +180,6 @@
 							        pj_ssize_t))
 {
-    return tp->op->attach(tp, user_data, rem_addr, addr_len, rtp_cb, rtcp_cb);
+    return tp->op->attach(tp, user_data, rem_addr, rem_rtcp, addr_len, 
+			  rtp_cb, rtcp_cb);
 }
 
Index: pjproject/trunk/pjmedia/src/pjmedia/endpoint.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 567)
+++ pjproject/trunk/pjmedia/src/pjmedia/endpoint.c	(revision 568)
@@ -328,8 +328,24 @@
     pj_strdup (pool, &m->desc.transport, &STR_RTP_AVP);
 
-    /* Add format and rtpmap for each codec. */
+    /* Init media line and attribute list. */
     m->desc.fmt_count = 0;
     m->attr_count = 0;
 
+    /* Add "rtcp" attribute */
+#if 1
+    {
+	attr = pj_pool_alloc(pool, sizeof(pjmedia_sdp_attr));
+	attr->name = pj_str("rtcp");
+	attr->value.ptr = pj_pool_alloc(pool, 80);
+	attr->value.slen = 
+	    pj_ansi_snprintf(attr->value.ptr, 80,
+			    ":%u IN IP4 %s",
+			    pj_ntohs(sock_info[0].rtp_addr_name.sin_port),
+			    pj_inet_ntoa(sock_info[0].rtp_addr_name.sin_addr));
+	pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
+    }
+#endif
+
+    /* Add format and rtpmap for each codec */
     for (i=0; i<endpt->codec_mgr.codec_cnt; ++i) {
 
Index: pjproject/trunk/pjmedia/src/pjmedia/errno.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/errno.c	(revision 567)
+++ pjproject/trunk/pjmedia/src/pjmedia/errno.c	(revision 568)
@@ -56,4 +56,5 @@
     PJ_BUILD_ERR( PJMEDIA_SDP_EINPT,	    "Invalid SDP payload type in media line" ),
     PJ_BUILD_ERR( PJMEDIA_SDP_EINFMTP,	    "Invalid SDP fmtp attribute" ),
+    PJ_BUILD_ERR( PJMEDIA_SDP_EINRTCP,	    "Invalid SDP rtcp attribyte" ),
 
     /* SDP negotiator errors. */
Index: pjproject/trunk/pjmedia/src/pjmedia/sdp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/sdp.c	(revision 567)
+++ pjproject/trunk/pjmedia/src/pjmedia/sdp.c	(revision 568)
@@ -60,5 +60,5 @@
 static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
 			parse_context *ctx);
-
+static void on_scanner_error(pj_scanner *scanner);
 
 /*
@@ -242,9 +242,13 @@
 						 pjmedia_sdp_rtpmap *rtpmap)
 {
-    const char *p = attr->value.ptr;
-    const char *end = attr->value.ptr + attr->value.slen;
+    pj_scanner scanner;
     pj_str_t token;
+    pj_status_t status = -1;
+    PJ_USE_EXCEPTION;
 
     PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtpmap")==0, PJ_EINVALIDOP);
+
+    pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
+		 PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
 
     /* rtpmap sample:
@@ -252,67 +256,54 @@
      */
 
-    /* Eat the first ':' */
-    if (*p != ':') return PJMEDIA_SDP_EINRTPMAP;
-
-    /* Get ':' */
-    ++p;
-
-    /* Get payload type. */
-    token.ptr = (char*)p;
-    while (pj_isdigit(*p) && p!=end)
-	++p;
-    token.slen = p - token.ptr;
-    if (token.slen == 0)
-	return PJMEDIA_SDP_EINRTPMAP;
-
-    rtpmap->pt = token;
-
-    /* Expecting space after payload type. */
-    if (*p != ' ') return PJMEDIA_SDP_EINRTPMAP;
-
-    /* Get space. */
-    ++p;
-
-    /* Get encoding name. */
-    token.ptr = (char*)p;
-    while (*p != '/' && p != end)
-	++p;
-    token.slen = p - token.ptr;
-    if (token.slen == 0)
-	return PJMEDIA_SDP_EINRTPMAP;
-    rtpmap->enc_name = token;
-
-    /* Expecting '/' after encoding name. */
-    if (*p != '/') return PJMEDIA_SDP_EINRTPMAP;
-
-    /* Get '/' */
-    ++p;
-
-    /* Get the clock rate. */
-    token.ptr = (char*)p;
-    while (p != end && pj_isdigit(*p))
-	++p;
-    token.slen = p - token.ptr;
-    if (token.slen == 0)
-	return PJMEDIA_SDP_EINRTPMAP;
-
-    rtpmap->clock_rate = pj_strtoul(&token);
-
-    /* Expecting either '/' or EOF */
-    if (p != end && *p != '/')
-	return PJMEDIA_SDP_EINRTPMAP;
-
-    if (p != end) {
-	++p;
-	token.ptr = (char*)p;
-	token.slen = end-p;
-	rtpmap->param = token;
-    } else {
-	rtpmap->param.ptr = NULL;
-	rtpmap->param.slen = 0;
-    }
-
-    return PJ_SUCCESS;
-
+    /* Init */
+    rtpmap->pt.slen = rtpmap->param.slen = rtpmap->enc_name.slen = 0;
+
+    /* Parse */
+    PJ_TRY {
+	/* Eat the first ':' */
+	if (pj_scan_get_char(&scanner) != ':') {
+	    status = PJMEDIA_SDP_EINRTPMAP;
+	    goto on_return;
+	}
+
+
+	/* Get payload type. */
+	pj_scan_get(&scanner, &cs_token, &rtpmap->pt);
+
+
+	/* Get encoding name. */
+	pj_scan_get(&scanner, &cs_token, &rtpmap->enc_name);
+
+	/* Expecting '/' after encoding name. */
+	if (pj_scan_get_char(&scanner) != '/') {
+	    status = PJMEDIA_SDP_EINRTPMAP;
+	    goto on_return;
+	}
+
+
+	/* Get the clock rate. */
+	pj_scan_get(&scanner, &cs_token, &token);
+	rtpmap->clock_rate = pj_strtoul(&token);
+
+	/* Expecting either '/' or EOF */
+	if (*scanner.curptr == '/') {
+	    pj_scan_get_char(&scanner);
+	    rtpmap->param.ptr = scanner.curptr;
+	    rtpmap->param.slen = scanner.end - scanner.curptr;
+	} else {
+	    rtpmap->param.slen = 0;
+	}
+
+	status = PJ_SUCCESS;
+    }
+    PJ_CATCH(SYNTAX_ERROR) {
+	status = PJMEDIA_SDP_EINRTPMAP;
+    }
+    PJ_END;
+
+
+on_return:
+    pj_scan_fini(&scanner);
+    return status;
 }
 
@@ -359,4 +350,68 @@
 }
 
+
+PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtcp(const pjmedia_sdp_attr *attr,
+					      pjmedia_sdp_rtcp_attr *rtcp)
+{
+    pj_scanner scanner;
+    pj_str_t token;
+    pj_status_t status = -1;
+    PJ_USE_EXCEPTION;
+
+    PJ_ASSERT_RETURN(pj_strcmp2(&attr->name, "rtcp")==0, PJ_EINVALIDOP);
+
+    /* fmtp BNF:
+     *	a=rtcp:<port> [nettype addrtype address]
+     */
+
+    pj_scan_init(&scanner, (char*)attr->value.ptr, attr->value.slen,
+		 PJ_SCAN_AUTOSKIP_WS, &on_scanner_error);
+
+    /* Init */
+    rtcp->net_type.slen = rtcp->addr_type.slen = rtcp->addr.slen = 0;
+
+    /* Parse */
+    PJ_TRY {
+
+	/* Get the first ":" */
+	if (pj_scan_get_char(&scanner) != ':')
+	{
+	    status = PJMEDIA_SDP_EINRTCP;
+	    goto on_return;
+	}
+
+	/* Get the port */
+	pj_scan_get(&scanner, &cs_token, &token);
+	rtcp->port = pj_strtoul(&token);
+
+	/* Have address? */
+	if (!pj_scan_is_eof(&scanner)) {
+
+	    /* Get network type */
+	    pj_scan_get(&scanner, &cs_token, &rtcp->net_type);
+
+	    /* Get address type */
+	    pj_scan_get(&scanner, &cs_token, &rtcp->addr_type);
+
+	    /* Get the address */
+	    pj_scan_get(&scanner, &cs_token, &rtcp->addr);
+
+	}
+
+	status = PJ_SUCCESS;
+
+    }
+    PJ_CATCH(SYNTAX_ERROR) {
+	status = PJMEDIA_SDP_EINRTCP;
+    }
+    PJ_END;
+
+on_return:
+    pj_scan_fini(&scanner);
+    return status;
+}
+
+
+
 PJ_DEF(pj_status_t) pjmedia_sdp_attr_to_rtpmap(pj_pool_t *pool,
 					       const pjmedia_sdp_attr *attr,
@@ -377,6 +432,6 @@
 {
     pjmedia_sdp_attr *attr;
-    char tempbuf[64], *p, *endbuf;
-    int i;
+    char tempbuf[64];
+    int len;
 
     /* Check arguments. */
@@ -387,10 +442,4 @@
 		     PJMEDIA_SDP_EINRTPMAP);
 
-    /* Check size. */
-    i = rtpmap->enc_name.slen + rtpmap->param.slen + 32;
-    if (i >= sizeof(tempbuf)-1) {
-	pj_assert(!"rtpmap attribute is too long");
-	return PJMEDIA_SDP_ERTPMAPTOOLONG;
-    }
 
     attr = pj_pool_alloc(pool, sizeof(pjmedia_sdp_attr));
@@ -400,35 +449,20 @@
     attr->name.slen = 6;
 
-    p = tempbuf;
-    endbuf = tempbuf+sizeof(tempbuf);
-
-    /* Add colon */
-    *p++ = ':';
-
-    /* Add payload type. */
-    pj_memcpy(p, rtpmap->pt.ptr, rtpmap->pt.slen);
-    p += rtpmap->pt.slen;
-    *p++ = ' ';
-
-    /* Add encoding name. */
-    for (i=0; i<rtpmap->enc_name.slen; ++i)
-	p[i] = rtpmap->enc_name.ptr[i];
-    p += rtpmap->enc_name.slen;
-    *p++ = '/';
-
-    /* Add clock rate. */
-    p += pj_utoa(rtpmap->clock_rate, p);
-
-    /* Add parameter if necessary. */
-    if (rtpmap->param.slen > 0) {
-	*p++ = '/';
-	for (i=0; i<rtpmap->param.slen; ++i)
-	    p[i] = rtpmap->param.ptr[i];
-	p += rtpmap->param.slen;
-    }
-
-    *p = '\0';
-
-    attr->value.slen = p-tempbuf;
+    /* Format: ":pt enc_name/clock_rate[/param]" */
+    len = pj_ansi_snprintf(tempbuf, sizeof(tempbuf), 
+			   ":%.*s %.*s/%u%s%.*s",
+			   (int)rtpmap->pt.slen,
+			   rtpmap->pt.ptr,
+			   (int)rtpmap->enc_name.slen,
+			   rtpmap->enc_name.ptr,
+			   rtpmap->clock_rate,
+			   (rtpmap->param.slen ? "/" : ""),
+			   rtpmap->param.slen,
+			   rtpmap->param.ptr);
+
+    if (len < 1 || len > sizeof(tempbuf))
+	return PJMEDIA_SDP_ERTPMAPTOOLONG;
+
+    attr->value.slen = len;
     attr->value.ptr = pj_pool_alloc(pool, attr->value.slen);
     pj_memcpy(attr->value.ptr, tempbuf, attr->value.slen);
@@ -441,24 +475,19 @@
 static int print_connection_info( pjmedia_sdp_conn *c, char *buf, int len)
 {
-    char *p = buf;
-
-    if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
+    int printed;
+
+    printed = pj_ansi_snprintf(buf, len, "c=%.*s %.*s %.*s\r\n",
+			       (int)c->net_type.slen,
+			       c->net_type.ptr,
+			       (int)c->addr_type.slen,
+			       c->addr_type.ptr,
+			       (int)c->addr.slen,
+			       c->addr.ptr);
+    if (printed < 1 || printed > len)
 	return -1;
-    }
-    *p++ = 'c';
-    *p++ = '=';
-    pj_memcpy(p, c->net_type.ptr, c->net_type.slen);
-    p += c->net_type.slen;
-    *p++ = ' ';
-    pj_memcpy(p, c->addr_type.ptr, c->addr_type.slen);
-    p += c->addr_type.slen;
-    *p++ = ' ';
-    pj_memcpy(p, c->addr.ptr, c->addr.slen);
-    p += c->addr.slen;
-    *p++ = '\r';
-    *p++ = '\n';
-
-    return p-buf;
-}
+
+    return printed;
+}
+
 
 PJ_DEF(pjmedia_sdp_conn*) pjmedia_sdp_conn_clone (pj_pool_t *pool, 
Index: pjproject/trunk/pjmedia/src/pjmedia/session.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 567)
+++ pjproject/trunk/pjmedia/src/pjmedia/session.c	(revision 568)
@@ -79,5 +79,4 @@
     pj_status_t status;
 
-
     
     /* Validate arguments: */
@@ -156,12 +155,39 @@
 
     /* Set remote address: */
-
-    si->rem_addr.sin_family = PJ_AF_INET;
-    si->rem_addr.sin_port = pj_htons(rem_m->desc.port);
-    if (pj_inet_aton(&rem_conn->addr, &si->rem_addr.sin_addr) == 0) {
-
+    status = pj_sockaddr_in_init(&si->rem_addr, &rem_conn->addr, 
+				 rem_m->desc.port);
+    if (status != PJ_SUCCESS) {
 	/* Invalid IP address. */
 	return PJMEDIA_EINVALIDIP;
     }
+
+    /* If "rtcp" attribute is present in the SDP, set the RTCP address
+     * from that attribute. Otherwise, calculate from RTP address.
+     */
+    attr = pjmedia_sdp_attr_find2(rem_m->attr_count, rem_m->attr,
+				  "rtcp", NULL);
+    if (attr) {
+	pjmedia_sdp_rtcp_attr rtcp;
+	status = pjmedia_sdp_attr_get_rtcp(attr, &rtcp);
+	if (status == PJ_SUCCESS) {
+	    if (rtcp.addr.slen) {
+		status = pj_sockaddr_in_init(&si->rem_rtcp, &rtcp.addr,
+					     (pj_uint16_t)rtcp.port);
+	    } else {
+		pj_sockaddr_in_init(&si->rem_rtcp, NULL, 
+				    (pj_uint16_t)rtcp.port);
+		si->rem_rtcp.sin_addr.s_addr = si->rem_addr.sin_addr.s_addr;
+	    }
+	}
+    }
+    
+    if (si->rem_rtcp.sin_addr.s_addr == 0) {
+	int rtcp_port;
+
+	pj_memcpy(&si->rem_rtcp, &si->rem_addr, sizeof(pj_sockaddr_in));
+	rtcp_port = pj_ntohs(si->rem_addr.sin_port) + 1;
+	si->rem_rtcp.sin_port = pj_htons((pj_uint16_t)rtcp_port);
+    }
+
 
     /* And codec must be numeric! */
Index: pjproject/trunk/pjmedia/src/pjmedia/stream.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 567)
+++ pjproject/trunk/pjmedia/src/pjmedia/stream.c	(revision 568)
@@ -1068,5 +1068,5 @@
 
     /* Only attach transport when stream is ready. */
-    status = (*tp->op->attach)(tp, stream, &info->rem_addr, 
+    status = (*tp->op->attach)(tp, stream, &info->rem_addr, &info->rem_rtcp,
 			       sizeof(info->rem_addr), &on_rx_rtp,
 			       &on_rx_rtcp);
Index: pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c
===================================================================
--- pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 567)
+++ pjproject/trunk/pjmedia/src/pjmedia/transport_udp.c	(revision 568)
@@ -72,4 +72,6 @@
     pj_sock_t		rtcp_sock;	/**< RTCP socket		    */
     pj_sockaddr_in	rtcp_addr_name;	/**< Published RTCP address.	    */
+    pj_sockaddr_in	rtcp_src_addr;	/**< Actual source RTCP address.    */
+    int			rtcp_addr_len;	/**< Length of RTCP src address.    */
     pj_ioqueue_key_t   *rtcp_key;	/**< RTCP socket key in ioqueue	    */
     pj_ioqueue_op_key_t rtcp_read_op;	/**< Pending read operation	    */
@@ -90,4 +92,5 @@
 				       void *user_data,
 				       const pj_sockaddr_t *rem_addr,
+				       const pj_sockaddr_t *rem_rtcp,
 				       unsigned addr_len,
 				       void (*rtp_cb)(void*,
@@ -276,6 +279,8 @@
     /* Kick of pending RTCP read from the ioqueue */
     size = sizeof(tp->rtcp_pkt);
-    status = pj_ioqueue_recv(tp->rtcp_key, &tp->rtcp_read_op,
-			     tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC);
+    tp->rtcp_addr_len = sizeof(tp->rtcp_src_addr);
+    status = pj_ioqueue_recvfrom( tp->rtcp_key, &tp->rtcp_read_op,
+				  tp->rtcp_pkt, &size, PJ_IOQUEUE_ALWAYS_ASYNC,
+				  &tp->rtcp_src_addr, &tp->rtcp_addr_len);
     if (status != PJ_EPENDING)
 	goto on_error;
@@ -385,15 +390,6 @@
 		if (udp->rtp_src_cnt >= PJMEDIA_RTP_NAT_PROBATION_CNT) {
 		
-		    pj_uint16_t port;
-
 		    /* Set remote RTP address to source address */
 		    udp->rem_rtp_addr = udp->rtp_src_addr;
-
-		    /* Also update remote RTCP address */
-		    pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, 
-			      sizeof(pj_sockaddr_in));
-		    port = (pj_uint16_t)
-			   (pj_ntohs(udp->rem_rtp_addr.sin_port)+1);
-		    udp->rem_rtcp_addr.sin_port = pj_htons(port);
 
 		    /* Reset counter */
@@ -404,4 +400,26 @@
 			      pj_inet_ntoa(udp->rtp_src_addr.sin_addr),
 			      pj_ntohs(udp->rtp_src_addr.sin_port)));
+
+		    /* Also update remote RTCP address if actual RTCP source
+		     * address is not heard yet.
+		     */
+		    if (udp->rtcp_src_addr.sin_addr.s_addr == 0) {
+			pj_uint16_t port;
+
+			pj_memcpy(&udp->rem_rtcp_addr, &udp->rem_rtp_addr, 
+				  sizeof(pj_sockaddr_in));
+			port = (pj_uint16_t)
+			       (pj_ntohs(udp->rem_rtp_addr.sin_port)+1);
+			udp->rem_rtcp_addr.sin_port = pj_htons(port);
+
+			pj_memcpy(&udp->rtcp_src_addr, &udp->rem_rtcp_addr, 
+				  sizeof(pj_sockaddr_in));
+
+			PJ_LOG(4,(udp->base.name,
+				  "Remote RTCP address switched to %s:%d",
+				  pj_inet_ntoa(udp->rtcp_src_addr.sin_addr),
+				  pj_ntohs(udp->rtcp_src_addr.sin_port)));
+
+		    }
 		}
 	    }
@@ -441,7 +459,28 @@
 	    (*cb)(user_data, udp->rtcp_pkt, bytes_read);
 
+	/* Check if RTCP source address is the same as the configured
+	 * remote address, and switch the address when they are
+	 * different.
+	 */
+	if ((udp->options & PJMEDIA_UDP_NO_SRC_ADDR_CHECKING)==0 &&
+	    ((udp->rem_rtcp_addr.sin_addr.s_addr != 
+	       udp->rtcp_src_addr.sin_addr.s_addr) ||
+	     (udp->rem_rtcp_addr.sin_port != 
+	       udp->rtcp_src_addr.sin_port)))
+	{
+	    pj_memcpy(&udp->rem_rtcp_addr, &udp->rtcp_src_addr,
+		      sizeof(pj_sockaddr_in));
+	    PJ_LOG(4,(udp->base.name,
+		      "Remote RTCP address switched to %s:%d",
+		      pj_inet_ntoa(udp->rtcp_src_addr.sin_addr),
+		      pj_ntohs(udp->rtcp_src_addr.sin_port)));
+	}
+
 	bytes_read = sizeof(udp->rtcp_pkt);
-	status = pj_ioqueue_recv(udp->rtcp_key, &udp->rtcp_read_op,
-				     udp->rtcp_pkt, &bytes_read, 0);
+	udp->rtcp_addr_len = sizeof(udp->rtcp_src_addr);
+	status = pj_ioqueue_recvfrom(udp->rtcp_key, &udp->rtcp_read_op,
+				     udp->rtcp_pkt, &bytes_read, 0,
+				     &udp->rtcp_src_addr, 
+				     &udp->rtcp_addr_len);
 
     } while (status == PJ_SUCCESS);
@@ -453,4 +492,5 @@
 				       void *user_data,
 				       const pj_sockaddr_t *rem_addr,
+				       const pj_sockaddr_t *rem_rtcp,
 				       unsigned addr_len,
 				       void (*rtp_cb)(void*,
@@ -462,4 +502,5 @@
 {
     struct transport_udp *udp = (struct transport_udp*) tp;
+    const pj_sockaddr_in *rtcp_addr;
 
     /* Validate arguments */
@@ -474,8 +515,17 @@
     pj_memcpy(&udp->rem_rtp_addr, rem_addr, sizeof(pj_sockaddr_in));
 
-    /* Guess RTCP address from RTP address */
-    pj_memcpy(&udp->rem_rtcp_addr, rem_addr, sizeof(pj_sockaddr_in));
-    udp->rem_rtcp_addr.sin_port = (pj_uint16_t) pj_htons((pj_uint16_t)(
-				    pj_ntohs(udp->rem_rtp_addr.sin_port)+1));
+    /* Copy remote RTP address, if one is specified. */
+    rtcp_addr = rem_rtcp;
+    if (rtcp_addr && rtcp_addr->sin_addr.s_addr != 0) {
+	pj_memcpy(&udp->rem_rtcp_addr, rem_rtcp, sizeof(pj_sockaddr_in));
+
+    } else {
+	int rtcp_port;
+
+	/* Otherwise guess the RTCP address from the RTP address */
+	pj_memcpy(&udp->rem_rtcp_addr, rem_addr, sizeof(pj_sockaddr_in));
+	rtcp_port = pj_ntohs(udp->rem_rtp_addr.sin_port) + 1;
+	udp->rem_rtcp_addr.sin_port = pj_htons((pj_uint16_t)rtcp_port);
+    }
 
     /* Save the callbacks */
Index: pjproject/trunk/pjsip-apps/src/samples/siprtp.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 567)
+++ pjproject/trunk/pjsip-apps/src/samples/siprtp.c	(revision 568)
@@ -1285,4 +1285,5 @@
     status = pjmedia_transport_attach(audio->transport, audio, 
 				      &audio->si.rem_addr, 
+				      &audio->si.rem_rtcp,
 				      sizeof(pj_sockaddr_in),
 				      &on_rx_rtp,
Index: pjproject/trunk/pjsip-apps/src/samples/siprtp_report.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/siprtp_report.c	(revision 567)
+++ pjproject/trunk/pjsip-apps/src/samples/siprtp_report.c	(revision 568)
@@ -162,7 +162,7 @@
 	   audio->rtcp.stat.rx.loss * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.loss),
 	   audio->rtcp.stat.rx.dup, 
-	   audio->rtcp.stat.rx.dup * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.dup),
+	   audio->rtcp.stat.rx.dup * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.loss),
 	   audio->rtcp.stat.rx.reorder, 
-	   audio->rtcp.stat.rx.reorder * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.reorder),
+	   audio->rtcp.stat.rx.reorder * 100.0 / (audio->rtcp.stat.rx.pkt + audio->rtcp.stat.rx.loss),
 	   "",
 	   audio->rtcp.stat.rx.loss_period.min / 1000.0, 
@@ -205,7 +205,7 @@
 	   audio->rtcp.stat.tx.loss * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.loss),
 	   audio->rtcp.stat.tx.dup, 
-	   audio->rtcp.stat.tx.dup * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.dup),
+	   audio->rtcp.stat.tx.dup * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.loss),
 	   audio->rtcp.stat.tx.reorder, 
-	   audio->rtcp.stat.tx.reorder * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.reorder),
+	   audio->rtcp.stat.tx.reorder * 100.0 / (audio->rtcp.stat.tx.pkt + audio->rtcp.stat.tx.loss),
 	   "",
 	   audio->rtcp.stat.tx.loss_period.min / 1000.0, 
Index: pjproject/trunk/pjsip-apps/src/samples/streamutil.c
===================================================================
--- pjproject/trunk/pjsip-apps/src/samples/streamutil.c	(revision 567)
+++ pjproject/trunk/pjsip-apps/src/samples/streamutil.c	(revision 568)
@@ -607,7 +607,7 @@
 	   stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
 	   stat.rx.dup, 
-	   stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.dup),
+	   stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
 	   stat.rx.reorder, 
-	   stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.reorder),
+	   stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
 	   "",
 	   stat.rx.loss_period.min / 1000.0, 
@@ -650,7 +650,7 @@
 	   stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
 	   stat.tx.dup, 
-	   stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.dup),
+	   stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
 	   stat.tx.reorder, 
-	   stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.reorder),
+	   stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
 	   "",
 	   stat.tx.loss_period.min / 1000.0, 
Index: pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c
===================================================================
--- pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 567)
+++ pjproject/trunk/pjsip/src/pjsua-lib/pjsua_call.c	(revision 568)
@@ -1305,7 +1305,7 @@
 	       stat.rx.loss * 100.0 / (stat.rx.pkt + stat.rx.loss),
 	       stat.rx.dup, 
-	       stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.dup),
+	       stat.rx.dup * 100.0 / (stat.rx.pkt + stat.rx.loss),
 	       stat.rx.reorder, 
-	       stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.reorder),
+	       stat.rx.reorder * 100.0 / (stat.rx.pkt + stat.rx.loss),
 	       indent, indent,
 	       stat.rx.loss_period.min / 1000.0, 
@@ -1364,7 +1364,7 @@
 	       stat.tx.loss * 100.0 / (stat.tx.pkt + stat.tx.loss),
 	       stat.tx.dup, 
-	       stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.dup),
+	       stat.tx.dup * 100.0 / (stat.tx.pkt + stat.tx.loss),
 	       stat.tx.reorder, 
-	       stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.reorder),
+	       stat.tx.reorder * 100.0 / (stat.tx.pkt + stat.tx.loss),
 
 	       indent, indent,
