Index: /pjproject/branches/1.x/pjmedia/include/pjmedia/config.h
===================================================================
--- /pjproject/branches/1.x/pjmedia/include/pjmedia/config.h	(revision 3959)
+++ /pjproject/branches/1.x/pjmedia/include/pjmedia/config.h	(revision 3960)
@@ -490,5 +490,5 @@
  * PJMEDIA_STREAM_ENABLE_XR setting for more info.
  *
- * Default: 1 (yes).
+ * Default: 0 (no).
  */
 #ifndef PJMEDIA_HAS_RTCP_XR
@@ -507,4 +507,17 @@
 #   define PJMEDIA_STREAM_ENABLE_XR		0
 #endif
+
+
+/**
+ * Specify the buffer length for storing any received RTCP SDES text
+ * in a stream session. Usually RTCP contains only the mandatory SDES
+ * field, i.e: CNAME.
+ * 
+ * Default: 64 bytes.
+ */
+#ifndef PJMEDIA_RTCP_RX_SDES_BUF_LEN
+#   define PJMEDIA_RTCP_RX_SDES_BUF_LEN		64
+#endif
+
 
 /**
Index: /pjproject/branches/1.x/pjmedia/include/pjmedia/rtcp.h
===================================================================
--- /pjproject/branches/1.x/pjmedia/include/pjmedia/rtcp.h	(revision 3959)
+++ /pjproject/branches/1.x/pjmedia/include/pjmedia/rtcp.h	(revision 3960)
@@ -55,4 +55,5 @@
  */
 
+ 
 #pragma pack(1)
 
@@ -60,5 +61,5 @@
  * RTCP sender report.
  */
-struct pjmedia_rtcp_sr
+typedef struct pjmedia_rtcp_sr
 {
     pj_uint32_t	    ntp_sec;	    /**< NTP time, seconds part.	*/
@@ -67,15 +68,11 @@
     pj_uint32_t	    sender_pcount;  /**< Sender packet cound.		*/
     pj_uint32_t	    sender_bcount;  /**< Sender octet/bytes count.	*/
-};
-
-/**
- * @see pjmedia_rtcp_sr
- */
-typedef struct pjmedia_rtcp_sr pjmedia_rtcp_sr;
+} pjmedia_rtcp_sr;
+
 
 /**
  * RTCP receiver report.
  */
-struct pjmedia_rtcp_rr
+typedef struct pjmedia_rtcp_rr
 {
     pj_uint32_t	    ssrc;	    /**< SSRC identification.		*/
@@ -95,10 +92,5 @@
     pj_uint32_t	    lsr;	    /**< Last SR.			*/
     pj_uint32_t	    dlsr;	    /**< Delay since last SR.		*/
-};
-
-/**
- * @see pjmedia_rtcp_rr
- */
-typedef struct pjmedia_rtcp_rr pjmedia_rtcp_rr;
+} pjmedia_rtcp_rr;
 
 
@@ -106,5 +98,5 @@
  * RTCP common header.
  */
-struct pjmedia_rtcp_common
+typedef struct pjmedia_rtcp_common
 {
 #if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
@@ -121,10 +113,6 @@
     unsigned	    length:16;	/**< packet length          */
     pj_uint32_t	    ssrc;	/**< SSRC identification    */
-};
-
-/**
- * @see pjmedia_rtcp_common
- */
-typedef struct pjmedia_rtcp_common pjmedia_rtcp_common;
+} pjmedia_rtcp_common;
+
 
 /**
@@ -154,17 +142,26 @@
 
 /**
+ * RTCP SDES structure.
+ */
+typedef struct pjmedia_rtcp_sdes
+{
+    pj_str_t	cname;		/**< RTCP SDES type CNAME.	*/
+    pj_str_t	name;		/**< RTCP SDES type NAME.	*/
+    pj_str_t	email;		/**< RTCP SDES type EMAIL.	*/
+    pj_str_t	phone;		/**< RTCP SDES type PHONE.	*/
+    pj_str_t	loc;		/**< RTCP SDES type LOC.	*/
+    pj_str_t	tool;		/**< RTCP SDES type TOOL.	*/
+    pj_str_t	note;		/**< RTCP SDES type NOTE.	*/
+} pjmedia_rtcp_sdes;
+
+
+/**
  * NTP time representation.
  */
-struct pjmedia_rtcp_ntp_rec
+typedef struct pjmedia_rtcp_ntp_rec
 {
     pj_uint32_t	    hi;		/**< High order 32-bit part.	*/
     pj_uint32_t	    lo;		/**< Lo order 32-bit part.	*/
-};
-
-/**
- * @see pjmedia_rtcp_ntp_rec
- */
-typedef struct pjmedia_rtcp_ntp_rec pjmedia_rtcp_ntp_rec;
-
+} pjmedia_rtcp_ntp_rec;
 
 
@@ -172,5 +169,5 @@
  * Unidirectional RTP stream statistics.
  */
-struct pjmedia_rtcp_stream_stat
+typedef struct pjmedia_rtcp_stream_stat
 {
     pj_time_val	    update;	/**< Time of last update.		    */
@@ -191,12 +188,6 @@
 
     pj_math_stat    jitter;	/**< Jitter statistics (in usec)	    */
-};
-
-
-/**
- * @see pjmedia_rtcp_stream_stat
- */
-typedef struct pjmedia_rtcp_stream_stat pjmedia_rtcp_stream_stat;
-
+
+} pjmedia_rtcp_stream_stat;
 
 
@@ -204,5 +195,5 @@
  * Bidirectional RTP stream statistics.
  */
-struct pjmedia_rtcp_stat
+typedef struct pjmedia_rtcp_stat
 {
     pj_time_val		     start; /**< Time when session was created	    */
@@ -227,11 +218,10 @@
 						(in usec).		    */
 #endif
-};
-
-
-/**
- * @see pjmedia_rtcp_stat
- */
-typedef struct pjmedia_rtcp_stat pjmedia_rtcp_stat;
+
+    pjmedia_rtcp_sdes	     peer_sdes;	/**< Peer SDES.			    */
+    char		     peer_sdes_buf_[PJMEDIA_RTCP_RX_SDES_BUF_LEN];
+					/**< Peer SDES buffer.		    */
+
+} pjmedia_rtcp_stat;
 
 
@@ -240,5 +230,5 @@
  * should only be one RTCP session for a bidirectional RTP streams.
  */
-struct pjmedia_rtcp_session
+typedef struct pjmedia_rtcp_session
 {
     char		   *name;	/**< Name identification.	    */
@@ -279,10 +269,5 @@
     pjmedia_rtcp_xr_session xr_session;
 #endif
-};
-
-/**
- * @see pjmedia_rtcp_session
- */
-typedef struct pjmedia_rtcp_session pjmedia_rtcp_session;
+} pjmedia_rtcp_session;
 
 
@@ -440,4 +425,44 @@
 
 /**
+ * Build an RTCP SDES (source description) packet. This packet can be
+ * appended to other RTCP packets, e.g: RTCP RR/SR, to compose a compound
+ * RTCP packet.
+ *
+ * @param session   The RTCP session.
+ * @param buf	    The buffer to receive RTCP SDES packet.
+ * @param length    On input, it will contain the buffer length.
+ *		    On output, it will contain the generated RTCP SDES
+ *		    packet length.
+ * @param sdes	    The source description, see #pjmedia_rtcp_sdes.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_rtcp_build_rtcp_sdes(
+					    pjmedia_rtcp_session *session, 
+					    void *buf,
+					    pj_size_t *length,
+					    const pjmedia_rtcp_sdes *sdes);
+
+/**
+ * Build an RTCP BYE packet. This packet can be appended to other RTCP
+ * packets, e.g: RTCP RR/SR, to compose a compound RTCP packet.
+ *
+ * @param session   The RTCP session.
+ * @param buf	    The buffer to receive RTCP BYE packet.
+ * @param length    On input, it will contain the buffer length.
+ *		    On output, it will contain the generated RTCP BYE
+ *		    packet length.
+ * @param reason    Optional, the BYE reason.
+ *
+ * @return	    PJ_SUCCESS on success.
+ */
+PJ_DECL(pj_status_t) pjmedia_rtcp_build_rtcp_bye(
+					    pjmedia_rtcp_session *session, 
+					    void *buf,
+					    pj_size_t *length,
+					    const pj_str_t *reason);
+
+
+/**
  * Call this function if RTCP XR needs to be enabled/disabled in the 
  * RTCP session.
Index: /pjproject/branches/1.x/pjmedia/src/pjmedia/rtcp.c
===================================================================
--- /pjproject/branches/1.x/pjmedia/src/pjmedia/rtcp.c	(revision 3959)
+++ /pjproject/branches/1.x/pjmedia/src/pjmedia/rtcp.c	(revision 3960)
@@ -30,5 +30,18 @@
 #define RTCP_SR   200
 #define RTCP_RR   201
+#define RTCP_SDES 202
+#define RTCP_BYE  203
 #define RTCP_XR   207
+
+enum {
+    RTCP_SDES_NULL  = 0,
+    RTCP_SDES_CNAME = 1,
+    RTCP_SDES_NAME  = 2,
+    RTCP_SDES_EMAIL = 3,
+    RTCP_SDES_PHONE = 4,
+    RTCP_SDES_LOC   = 5,
+    RTCP_SDES_TOOL  = 6,
+    RTCP_SDES_NOTE  = 7
+};
 
 #if PJ_HAS_HIGH_RES_TIMER==0
@@ -474,7 +487,7 @@
 
 
-PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess,
-				   const void *pkt,
-				   pj_size_t size)
+static void parse_rtcp_report( pjmedia_rtcp_session *sess,
+			       const void *pkt,
+			       pj_size_t size)
 {
     pjmedia_rtcp_common *common = (pjmedia_rtcp_common*) pkt;
@@ -653,4 +666,140 @@
 
 
+static void parse_rtcp_sdes(pjmedia_rtcp_session *sess,
+			    const void *pkt,
+			    pj_size_t size)
+{
+    pjmedia_rtcp_sdes *sdes = &sess->stat.peer_sdes;
+    char *p, *p_end;
+    char *b, *b_end;
+
+    p = (char*)pkt + 8;
+    p_end = (char*)pkt + size;
+
+    pj_bzero(sdes, sizeof(*sdes));
+    b = sess->stat.peer_sdes_buf_;
+    b_end = b + sizeof(sess->stat.peer_sdes_buf_);
+
+    while (p < p_end) {
+	pj_uint8_t sdes_type, sdes_len;
+	pj_str_t sdes_value = {NULL, 0};
+
+	sdes_type = *p++;
+
+	/* Check for end of SDES item list */
+	if (sdes_type == RTCP_SDES_NULL || p == p_end)
+	    break;
+
+	sdes_len = *p++;
+
+	/* Check for corrupted SDES packet */
+	if (p + sdes_len > p_end)
+	    break;
+
+	/* Get SDES item */
+	if (b + sdes_len < b_end) {
+	    pj_memcpy(b, p, sdes_len);
+	    sdes_value.ptr = b;
+	    sdes_value.slen = sdes_len;
+	    b += sdes_len;
+	} else {
+	    /* Insufficient SDES buffer */
+	    PJ_LOG(5, (sess->name,
+		    "Unsufficient buffer to save RTCP SDES type %d:%.*s",
+		    sdes_type, sdes_len, p));
+	    p += sdes_len;
+	    continue;
+	}
+
+	switch (sdes_type) {
+	case RTCP_SDES_CNAME:
+	    sdes->cname = sdes_value;
+	    break;
+	case RTCP_SDES_NAME:
+	    sdes->name = sdes_value;
+	    break;
+	case RTCP_SDES_EMAIL:
+	    sdes->email = sdes_value;
+	    break;
+	case RTCP_SDES_PHONE:
+	    sdes->phone = sdes_value;
+	    break;
+	case RTCP_SDES_LOC:
+	    sdes->loc = sdes_value;
+	    break;
+	case RTCP_SDES_TOOL:
+	    sdes->tool = sdes_value;
+	    break;
+	case RTCP_SDES_NOTE:
+	    sdes->note = sdes_value;
+	    break;
+	default:
+	    TRACE_((sess->name, "Received unknown RTCP SDES type %d:%.*s",
+		    sdes_type, sdes_value.slen, sdes_value.ptr));
+	    break;
+	}
+
+	p += sdes_len;
+    }
+}
+
+
+static void parse_rtcp_bye(pjmedia_rtcp_session *sess,
+			   const void *pkt,
+			   pj_size_t size)
+{
+    pj_str_t reason = {"-", 1};
+
+    /* Check and get BYE reason */
+    if (size > 8) {
+	reason.slen = *((pj_uint8_t*)pkt+8);
+	pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9),
+		  reason.slen);
+	reason.ptr = sess->stat.peer_sdes_buf_;
+    }
+
+    /* Just print RTCP BYE log */
+    PJ_LOG(5, (sess->name, "Received RTCP BYE, reason: %.*s",
+	       reason.slen, reason.ptr));
+}
+
+
+PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess,
+				   const void *pkt,
+				   pj_size_t size)
+{
+    pj_uint8_t *p, *p_end;
+
+    p = (pj_uint8_t*)pkt;
+    p_end = p + size;
+    while (p < p_end) {
+	pjmedia_rtcp_common *common = (pjmedia_rtcp_common*)p;
+	unsigned len;
+
+	len = (pj_ntohs((pj_uint16_t)common->length)+1) * 4;
+	switch(common->pt) {
+	case RTCP_SR:
+	case RTCP_RR:
+	case RTCP_XR:
+	    parse_rtcp_report(sess, p, len);
+	    break;
+	case RTCP_SDES:
+	    parse_rtcp_sdes(sess, p, len);
+	    break;
+	case RTCP_BYE:
+	    parse_rtcp_bye(sess, p, len);
+	    break;
+	default:
+	    /* Ignore unknown RTCP */
+	    TRACE_((sess->name, "Received unknown RTCP packet type=%d",
+		    common->pt));
+	    break;
+	}
+
+	p += len;
+    }
+}
+
+
 PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess, 
 				     void **ret_p_pkt, int *len)
@@ -805,4 +954,126 @@
 }
 
+
+PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_sdes(
+					    pjmedia_rtcp_session *session, 
+					    void *buf,
+					    pj_size_t *length,
+					    const pjmedia_rtcp_sdes *sdes)
+{
+    pjmedia_rtcp_common *hdr;
+    pj_uint8_t *p;
+    unsigned len;
+
+    PJ_ASSERT_RETURN(session && buf && length && sdes, PJ_EINVAL);
+
+    /* Verify SDES item length */
+    if (sdes->cname.slen > 255 || sdes->name.slen  > 255 ||
+	sdes->email.slen > 255 || sdes->phone.slen > 255 ||
+	sdes->loc.slen   > 255 || sdes->tool.slen  > 255 ||
+	sdes->note.slen  > 255)
+    {
+	return PJ_EINVAL;
+    }
+
+    /* Verify buffer length */
+    len = sizeof(*hdr);
+    if (sdes->cname.slen) len += sdes->cname.slen + 2;
+    if (sdes->name.slen)  len += sdes->name.slen  + 2;
+    if (sdes->email.slen) len += sdes->email.slen + 2;
+    if (sdes->phone.slen) len += sdes->phone.slen + 2;
+    if (sdes->loc.slen)   len += sdes->loc.slen   + 2;
+    if (sdes->tool.slen)  len += sdes->tool.slen  + 2;
+    if (sdes->note.slen)  len += sdes->note.slen  + 2;
+    len++; /* null termination */
+    len = ((len+3)/4) * 4;
+    if (len > *length)
+	return PJ_ETOOSMALL;
+
+    /* Build RTCP SDES header */
+    hdr = (pjmedia_rtcp_common*)buf;
+    pj_memcpy(hdr, &session->rtcp_sr_pkt.common,  sizeof(*hdr));
+    hdr->pt = RTCP_SDES;
+    hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
+
+    /* Build RTCP SDES items */
+    p = (pj_uint8_t*)hdr + sizeof(*hdr);
+#define BUILD_SDES_ITEM(SDES_NAME, SDES_TYPE) \
+    if (sdes->SDES_NAME.slen) { \
+	*p++ = SDES_TYPE; \
+	*p++ = (pj_uint8_t)sdes->SDES_NAME.slen; \
+	pj_memcpy(p, sdes->SDES_NAME.ptr, sdes->SDES_NAME.slen); \
+	p += sdes->SDES_NAME.slen; \
+    }
+    BUILD_SDES_ITEM(cname, RTCP_SDES_CNAME);
+    BUILD_SDES_ITEM(name,  RTCP_SDES_NAME);
+    BUILD_SDES_ITEM(email, RTCP_SDES_EMAIL);
+    BUILD_SDES_ITEM(phone, RTCP_SDES_PHONE);
+    BUILD_SDES_ITEM(loc,   RTCP_SDES_LOC);
+    BUILD_SDES_ITEM(tool,  RTCP_SDES_TOOL);
+    BUILD_SDES_ITEM(note,  RTCP_SDES_NOTE);
+#undef BUILD_SDES_ITEM
+
+    /* Null termination */
+    *p++ = 0;
+
+    /* Pad to 32bit */
+    while ((p-(pj_uint8_t*)buf) % 4)
+	*p++ = 0;
+
+    /* Finally */
+    pj_assert((int)len == p-(pj_uint8_t*)buf);
+    *length = len;
+
+    return PJ_SUCCESS;
+}
+
+
+PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_bye(pjmedia_rtcp_session *session,
+						void *buf,
+						pj_size_t *length,
+						const pj_str_t *reason)
+{
+    pjmedia_rtcp_common *hdr;
+    pj_uint8_t *p;
+    unsigned len;
+
+    PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL);
+
+    /* Verify BYE reason length */
+    if (reason && reason->slen > 255)
+	return PJ_EINVAL;
+
+    /* Verify buffer length */
+    len = sizeof(*hdr);
+    if (reason && reason->slen) len += reason->slen + 1;
+    len = ((len+3)/4) * 4;
+    if (len > *length)
+	return PJ_ETOOSMALL;
+
+    /* Build RTCP BYE header */
+    hdr = (pjmedia_rtcp_common*)buf;
+    pj_memcpy(hdr, &session->rtcp_sr_pkt.common,  sizeof(*hdr));
+    hdr->pt = RTCP_BYE;
+    hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
+
+    /* Write RTCP BYE reason */
+    p = (pj_uint8_t*)hdr + sizeof(*hdr);
+    if (reason && reason->slen) {
+	*p++ = (pj_uint8_t)reason->slen;
+	pj_memcpy(p, reason->ptr, reason->slen);
+	p += reason->slen;
+    }
+
+    /* Pad to 32bit */
+    while ((p-(pj_uint8_t*)buf) % 4)
+	*p++ = 0;
+
+    pj_assert((int)len == p-(pj_uint8_t*)buf);
+    *length = len;
+
+    return PJ_SUCCESS;
+}
+
+
 PJ_DEF(pj_status_t) pjmedia_rtcp_enable_xr( pjmedia_rtcp_session *sess, 
 					    pj_bool_t enable)
Index: /pjproject/branches/1.x/pjmedia/src/pjmedia/stream.c
===================================================================
--- /pjproject/branches/1.x/pjmedia/src/pjmedia/stream.c	(revision 3959)
+++ /pjproject/branches/1.x/pjmedia/src/pjmedia/stream.c	(revision 3960)
@@ -235,4 +235,10 @@
 
 
+static pj_status_t send_rtcp(pjmedia_stream *stream,
+			     pj_bool_t with_sdes,
+			     pj_bool_t with_bye,
+			     pj_bool_t with_xr);
+
+
 #if TRACE_JB
 
@@ -415,6 +421,5 @@
 
     /* Send RTCP */
-    pjmedia_rtcp_build_rtcp(&stream->rtcp, &pkt, &pkt_len);
-    pjmedia_transport_send_rtcp(stream->transport, pkt, pkt_len);
+    send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE);
 
 #elif PJMEDIA_STREAM_ENABLE_KA == PJMEDIA_STREAM_KA_USER
@@ -904,4 +909,112 @@
 
 
+static pj_status_t send_rtcp(pjmedia_stream *stream,
+			     pj_bool_t with_sdes,
+			     pj_bool_t with_bye,
+			     pj_bool_t with_xr)
+{
+    void *sr_rr_pkt;
+    pj_uint8_t *pkt;
+    int len, max_len;
+    pj_status_t status;
+
+    /* Build RTCP RR/SR packet */
+    pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
+
+#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0)
+    with_xr = PJ_FALSE;
+#endif
+
+    if (with_sdes || with_bye || with_xr) {
+	pkt = (pj_uint8_t*) stream->enc->out_pkt;
+	pj_memcpy(pkt, sr_rr_pkt, len);
+	max_len = stream->enc->out_pkt_size;
+    } else {
+	pkt = sr_rr_pkt;
+	max_len = len;
+    }
+
+    /* Build RTCP SDES packet */
+    if (with_sdes) {
+	pjmedia_rtcp_sdes sdes;
+	unsigned sdes_len;
+
+	pj_bzero(&sdes, sizeof(sdes));
+	sdes.cname = stream->cname;
+	sdes_len = max_len - len;
+	status = pjmedia_rtcp_build_rtcp_sdes(&stream->rtcp, pkt+len,
+					      &sdes_len, &sdes);
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(stream->port.info.name.ptr, status,
+        			     "Error generating RTCP SDES"));
+	} else {
+	    len += sdes_len;
+	}
+    }
+
+    /* Build RTCP XR packet */
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+    if (with_xr) {
+	int i;
+	pjmedia_jb_state jb_state;
+	void *xr_pkt;
+	int xr_len;
+
+	/* Update RTCP XR with current JB states */
+	pjmedia_jbuf_get_state(stream->jb, &jb_state);
+    	    
+	i = jb_state.avg_delay;
+	status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
+					     PJMEDIA_RTCP_XR_INFO_JB_NOM, i);
+	pj_assert(status == PJ_SUCCESS);
+
+	i = jb_state.max_delay;
+	status = pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
+					     PJMEDIA_RTCP_XR_INFO_JB_MAX, i);
+	pj_assert(status == PJ_SUCCESS);
+
+	pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0,
+				   &xr_pkt, &xr_len);
+
+	if (xr_len + len <= max_len) {
+	    pj_memcpy(pkt+len, xr_pkt, xr_len);
+	    len += xr_len;
+
+	    /* Send the RTCP XR to third-party destination if specified */
+	    if (stream->rtcp_xr_dest_len) {
+		pjmedia_transport_send_rtcp2(stream->transport, 
+					     &stream->rtcp_xr_dest,
+					     stream->rtcp_xr_dest_len, 
+					     xr_pkt, xr_len);
+	    }
+
+	} else {
+	    PJ_PERROR(4,(stream->port.info.name.ptr, PJ_ETOOBIG,
+        		 "Error generating RTCP-XR"));
+	}
+    }
+#endif
+
+    /* Build RTCP BYE packet */
+    if (with_bye) {
+	unsigned bye_len;
+
+	bye_len = max_len - len;
+	status = pjmedia_rtcp_build_rtcp_bye(&stream->rtcp, pkt+len,
+					     &bye_len, NULL);
+	if (status != PJ_SUCCESS) {
+	    PJ_PERROR(4,(stream->port.info.name.ptr, status,
+        			     "Error generating RTCP BYE"));
+	} else {
+	    len += bye_len;
+	}
+    }
+
+    /* Send! */
+    status = pjmedia_transport_send_rtcp(stream->transport, pkt, len);
+
+    return status;
+}
+
 /**
  * check_tx_rtcp()
@@ -917,5 +1030,4 @@
      */
 
-
     if (stream->rtcp_last_tx == 0) {
 	
@@ -923,130 +1035,31 @@
 
     } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
-	
-	void *rtcp_pkt;
-	int len;
+	pj_bool_t with_xr = PJ_FALSE;
 	pj_status_t status;
 
-	pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
-
-	status=pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+	if (stream->rtcp.xr_enabled) {
+	    if (stream->rtcp_xr_last_tx == 0) {
+		stream->rtcp_xr_last_tx = timestamp;
+	    } else if (timestamp - stream->rtcp_xr_last_tx >= 
+		       stream->rtcp_xr_interval)
+	    {
+		with_xr = PJ_TRUE;
+
+		/* Update last tx RTCP XR */
+		stream->rtcp_xr_last_tx = timestamp;
+	    }
+	}
+#endif
+
+	status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled, PJ_FALSE,
+			   with_xr);
 	if (status != PJ_SUCCESS) {
 	    PJ_PERROR(4,(stream->port.info.name.ptr, status,
-		         "Error sending RTCP"));
+        		 "Error sending RTCP"));
 	}
 
 	stream->rtcp_last_tx = timestamp;
     }
-
-#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
-    if (stream->rtcp.xr_enabled) {
-
-	if (stream->rtcp_xr_last_tx == 0) {
-    	
-	    stream->rtcp_xr_last_tx = timestamp;
-
-	} else if (timestamp - stream->rtcp_xr_last_tx >= 
-		   stream->rtcp_xr_interval)
-	{
-	    int i;
-	    pjmedia_jb_state jb_state;
-	    void *rtcp_pkt;
-	    int len;
-
-	    /* Update RTCP XR with current JB states */
-	    pjmedia_jbuf_get_state(stream->jb, &jb_state);
-    	    
-	    i = jb_state.avg_delay;
-	    pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
-					PJMEDIA_RTCP_XR_INFO_JB_NOM,
-					i);
-
-	    i = jb_state.max_delay;
-	    pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
-					PJMEDIA_RTCP_XR_INFO_JB_MAX,
-					i);
-
-	    /* Build RTCP XR packet */
-	    pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0, 
-				       &rtcp_pkt, &len);
-
-	    /* Send the RTCP XR to remote address */
-	    pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
-
-	    /* Send the RTCP XR to third-party destination if specified */
-	    if (stream->rtcp_xr_dest_len) {
-		pjmedia_transport_send_rtcp2(stream->transport, 
-					     &stream->rtcp_xr_dest,
-					     stream->rtcp_xr_dest_len, 
-					     rtcp_pkt, len);
-	    }
-
-	    /* Update last tx RTCP XR */
-	    stream->rtcp_xr_last_tx = timestamp;
-	}
-    }
-#endif
-}
-
-/* Build RTCP SDES packet */
-static unsigned create_rtcp_sdes(pjmedia_stream *stream, pj_uint8_t *pkt,
-				 unsigned max_len)
-{
-    pjmedia_rtcp_common hdr;
-    pj_uint8_t *p = pkt;
-
-    /* SDES header */
-    hdr.version = 2;
-    hdr.p = 0;
-    hdr.count = 1;
-    hdr.pt = 202;
-    hdr.length = 2 + (4+stream->cname.slen+3)/4 - 1;
-    if (max_len < (hdr.length << 2)) {
-	pj_assert(!"Not enough buffer for SDES packet");
-	return 0;
-    }
-    hdr.length = pj_htons((pj_uint16_t)hdr.length);
-    hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
-    pj_memcpy(p, &hdr, sizeof(hdr));
-    p += sizeof(hdr);
-
-    /* CNAME item */
-    *p++ = 1;
-    *p++ = (pj_uint8_t)stream->cname.slen;
-    pj_memcpy(p, stream->cname.ptr, stream->cname.slen);
-    p += stream->cname.slen;
-
-    /* END */
-    *p++ = '\0';
-    *p++ = '\0';
-
-    /* Pad to 32bit */
-    while ((p-pkt) % 4)
-	*p++ = '\0';
-
-    return (p - pkt);
-}
-
-/* Build RTCP BYE packet */
-static unsigned create_rtcp_bye(pjmedia_stream *stream, pj_uint8_t *pkt,
-				unsigned max_len)
-{
-    pjmedia_rtcp_common hdr;
-
-    /* BYE header */
-    hdr.version = 2;
-    hdr.p = 0;
-    hdr.count = 1;
-    hdr.pt = 203;
-    hdr.length = 1;
-    if (max_len < (hdr.length << 2)) {
-	pj_assert(!"Not enough buffer for SDES packet");
-	return 0;
-    }
-    hdr.length = pj_htons((pj_uint16_t)hdr.length);
-    hdr.ssrc = stream->enc->rtp.out_hdr.ssrc;
-    pj_memcpy(pkt, &hdr, sizeof(hdr));
-
-    return sizeof(hdr);
 }
 
@@ -1832,40 +1845,12 @@
     /* Send RTCP RR and SDES after we receive some RTP packets */
     if (stream->rtcp.received >= 10 && !stream->initial_rr) {
-	void *sr_rr_pkt;
-	pj_uint8_t *pkt;
-	int len;
-
-	/* Build RR or SR */
-	pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
-
-        if (!stream->rtcp_sdes_bye_disabled) {
-            pkt = (pj_uint8_t*) stream->enc->out_pkt;
-            pj_memcpy(pkt, sr_rr_pkt, len);
-            pkt += len;
-
-            /* Append SDES */
-            len = create_rtcp_sdes(stream, (pj_uint8_t*)pkt, 
-                                   stream->enc->out_pkt_size - len);
-            if (len > 0) {
-                pkt += len;
-                len = ((pj_uint8_t*)pkt) - ((pj_uint8_t*)stream->enc->out_pkt);
-                status = pjmedia_transport_send_rtcp(stream->transport,
-                                                     stream->enc->out_pkt,
-                                                     len);
-                if (status != PJ_SUCCESS) {
-                    PJ_PERROR(4,(stream->port.info.name.ptr, status,
-                    		 "Error sending RTCP SDES"));
-                }
-            }
-        } else {
-            status = pjmedia_transport_send_rtcp(stream->transport,
-                                                 sr_rr_pkt, len);
-            if (status != PJ_SUCCESS) {
-                PJ_PERROR(4,(stream->port.info.name.ptr, status,
-                	     "Error sending initial RTCP RR"));
-            }
-        }
-
-	stream->initial_rr = PJ_TRUE;
+	status = send_rtcp(stream, !stream->rtcp_sdes_bye_disabled,
+			   PJ_FALSE, PJ_FALSE);
+        if (status != PJ_SUCCESS) {
+            PJ_PERROR(4,(stream->port.info.name.ptr, status,
+            	     "Error sending initial RTCP RR"));
+	} else {
+	    stream->initial_rr = PJ_TRUE;
+	}
     }
 }
@@ -2401,45 +2386,7 @@
     PJ_ASSERT_RETURN(stream != NULL, PJ_EINVAL);
 
-#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
-    /* Send RTCP XR on stream destroy */
-    if (stream->rtcp.xr_enabled) {
-	int i;
-	pjmedia_jb_state jb_state;
-	void *rtcp_pkt;
-	int len;
-
-	/* Update RTCP XR with current JB states */
-	pjmedia_jbuf_get_state(stream->jb, &jb_state);
-    	    
-	i = jb_state.avg_delay;
-	pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
-				    PJMEDIA_RTCP_XR_INFO_JB_NOM,
-				    i);
-
-	i = jb_state.max_delay;
-	pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session, 
-				    PJMEDIA_RTCP_XR_INFO_JB_MAX,
-				    i);
-
-	/* Build RTCP XR packet */
-	pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0, 
-				   &rtcp_pkt, &len);
-
-	/* Send the RTCP XR to remote address */
-	pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
-	
-	/* Send the RTCP XR to third-party destination if specified */
-	if (stream->rtcp_xr_dest_len) {
-	    pjmedia_transport_send_rtcp2(stream->transport, 
-					 &stream->rtcp_xr_dest,
-					 stream->rtcp_xr_dest_len, 
-					 rtcp_pkt, len);
-	}
-    }
-#endif
-
-    /* Send RTCP BYE */
+    /* Send RTCP BYE (also SDES & XR) */
     if (!stream->rtcp_sdes_bye_disabled) {
-        pjmedia_stream_send_rtcp_bye(stream);
+	send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_TRUE);
     }
 
@@ -2795,16 +2742,7 @@
 pjmedia_stream_send_rtcp_sdes( pjmedia_stream *stream )
 {
-    unsigned len;
-
     PJ_ASSERT_RETURN(stream, PJ_EINVAL);
 
-    len = create_rtcp_sdes(stream, (pj_uint8_t*)stream->enc->out_pkt,
-                           stream->enc->out_pkt_size);
-    if (len != 0) {
-        return pjmedia_transport_send_rtcp(stream->transport, 
-                                           stream->enc->out_pkt, len);
-    }
-
-    return PJ_SUCCESS;
+    return send_rtcp(stream, PJ_TRUE, PJ_FALSE, PJ_FALSE);
 }
 
@@ -2818,12 +2756,5 @@
 
     if (stream->enc && stream->transport) {
-        unsigned len;
-
-        len = create_rtcp_bye(stream, (pj_uint8_t*)stream->enc->out_pkt,
-                              stream->enc->out_pkt_size);
-        if (len != 0) {
-            return pjmedia_transport_send_rtcp(stream->transport, 
-                                               stream->enc->out_pkt, len);
-        }
+	return send_rtcp(stream, PJ_TRUE, PJ_TRUE, PJ_FALSE);
     }
 
