summaryrefslogtreecommitdiff
path: root/pjmedia
diff options
context:
space:
mode:
authorNanang Izzuddin <nanang@teluu.com>2012-02-27 14:41:21 +0000
committerNanang Izzuddin <nanang@teluu.com>2012-02-27 14:41:21 +0000
commit43a425f06f2e69ea5b6c5c27da7b24cdc1036a10 (patch)
tree1402abcddca75382ac71ebb4d3a4e4da783a6d7c /pjmedia
parent45a544797fe4ad613385965bd002973cdd80be2c (diff)
Fix #1440: Send and parse RTCP compound packet, containing report (RR/SR/XR), SDES, and BYE.
git-svn-id: http://svn.pjsip.org/repos/pjproject/branches/1.x@3960 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia')
-rw-r--r--pjmedia/include/pjmedia/config.h15
-rw-r--r--pjmedia/include/pjmedia/rtcp.h121
-rw-r--r--pjmedia/src/pjmedia/rtcp.c277
-rw-r--r--pjmedia/src/pjmedia/stream.c349
4 files changed, 501 insertions, 261 deletions
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 31ae2bfa..c5524623 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -489,7 +489,7 @@
* if it is enabled on run-time on per stream basis. See
* PJMEDIA_STREAM_ENABLE_XR setting for more info.
*
- * Default: 1 (yes).
+ * Default: 0 (no).
*/
#ifndef PJMEDIA_HAS_RTCP_XR
# define PJMEDIA_HAS_RTCP_XR 0
@@ -507,6 +507,19 @@
# 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
+
+
/**
* Specify how long (in miliseconds) the stream should suspend the
* silence detector/voice activity detector (VAD) during the initial
diff --git a/pjmedia/include/pjmedia/rtcp.h b/pjmedia/include/pjmedia/rtcp.h
index 259d3483..9fc53657 100644
--- a/pjmedia/include/pjmedia/rtcp.h
+++ b/pjmedia/include/pjmedia/rtcp.h
@@ -54,29 +54,26 @@ PJ_BEGIN_DECL
* #pjmedia_stream_get_stat() function.
*/
+
#pragma pack(1)
/**
* RTCP sender report.
*/
-struct pjmedia_rtcp_sr
+typedef struct pjmedia_rtcp_sr
{
pj_uint32_t ntp_sec; /**< NTP time, seconds part. */
pj_uint32_t ntp_frac; /**< NTP time, fractions part. */
pj_uint32_t rtp_ts; /**< RTP timestamp. */
pj_uint32_t sender_pcount; /**< Sender packet cound. */
pj_uint32_t sender_bcount; /**< Sender octet/bytes count. */
-};
+} pjmedia_rtcp_sr;
-/**
- * @see pjmedia_rtcp_sr
- */
-typedef struct pjmedia_rtcp_sr pjmedia_rtcp_sr;
/**
* RTCP receiver report.
*/
-struct pjmedia_rtcp_rr
+typedef struct pjmedia_rtcp_rr
{
pj_uint32_t ssrc; /**< SSRC identification. */
#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
@@ -94,18 +91,13 @@ struct pjmedia_rtcp_rr
pj_uint32_t jitter; /**< Jitter. */
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;
/**
* RTCP common header.
*/
-struct pjmedia_rtcp_common
+typedef struct pjmedia_rtcp_common
{
#if defined(PJ_IS_BIG_ENDIAN) && PJ_IS_BIG_ENDIAN!=0
unsigned version:2; /**< packet type */
@@ -120,12 +112,8 @@ struct pjmedia_rtcp_common
#endif
unsigned length:16; /**< packet length */
pj_uint32_t ssrc; /**< SSRC identification */
-};
+} pjmedia_rtcp_common;
-/**
- * @see pjmedia_rtcp_common
- */
-typedef struct pjmedia_rtcp_common pjmedia_rtcp_common;
/**
* This structure declares default RTCP packet (SR) that is sent by pjmedia.
@@ -153,25 +141,34 @@ typedef struct pjmedia_rtcp_rr_pkt
/**
- * NTP time representation.
+ * RTCP SDES structure.
*/
-struct pjmedia_rtcp_ntp_rec
+typedef struct pjmedia_rtcp_sdes
{
- pj_uint32_t hi; /**< High order 32-bit part. */
- pj_uint32_t lo; /**< Lo order 32-bit part. */
-};
+ 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;
+
/**
- * @see pjmedia_rtcp_ntp_rec
+ * NTP time representation.
*/
-typedef struct pjmedia_rtcp_ntp_rec 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. */
+} pjmedia_rtcp_ntp_rec;
/**
* Unidirectional RTP stream statistics.
*/
-struct pjmedia_rtcp_stream_stat
+typedef struct pjmedia_rtcp_stream_stat
{
pj_time_val update; /**< Time of last update. */
unsigned update_cnt; /**< Number of updates (to calculate avg) */
@@ -190,20 +187,14 @@ struct pjmedia_rtcp_stream_stat
} loss_type; /**< Types of loss detected. */
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;
/**
* Bidirectional RTP stream statistics.
*/
-struct pjmedia_rtcp_stat
+typedef struct pjmedia_rtcp_stat
{
pj_time_val start; /**< Time when session was created */
@@ -226,20 +217,19 @@ struct pjmedia_rtcp_stat
receiving direction
(in usec). */
#endif
-};
+ pjmedia_rtcp_sdes peer_sdes; /**< Peer SDES. */
+ char peer_sdes_buf_[PJMEDIA_RTCP_RX_SDES_BUF_LEN];
+ /**< Peer SDES buffer. */
-/**
- * @see pjmedia_rtcp_stat
- */
-typedef struct pjmedia_rtcp_stat pjmedia_rtcp_stat;
+} pjmedia_rtcp_stat;
/**
* RTCP session is used to monitor the RTP session of one endpoint. There
* should only be one RTCP session for a bidirectional RTP streams.
*/
-struct pjmedia_rtcp_session
+typedef struct pjmedia_rtcp_session
{
char *name; /**< Name identification. */
pjmedia_rtcp_sr_pkt rtcp_sr_pkt;/**< Cached RTCP SR packet. */
@@ -278,12 +268,7 @@ struct pjmedia_rtcp_session
*/
pjmedia_rtcp_xr_session xr_session;
#endif
-};
-
-/**
- * @see pjmedia_rtcp_session
- */
-typedef struct pjmedia_rtcp_session pjmedia_rtcp_session;
+} pjmedia_rtcp_session;
/**
@@ -439,6 +424,46 @@ PJ_DECL(void) pjmedia_rtcp_build_rtcp( pjmedia_rtcp_session *session,
/**
+ * 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.
*
diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c
index 7832771c..52274155 100644
--- a/pjmedia/src/pjmedia/rtcp.c
+++ b/pjmedia/src/pjmedia/rtcp.c
@@ -29,8 +29,21 @@
#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
# error "High resolution timer needs to be enabled"
#endif
@@ -473,9 +486,9 @@ PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess,
}
-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;
const pjmedia_rtcp_rr *rr = NULL;
@@ -652,6 +665,142 @@ end_rtt_calc:
}
+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)
{
@@ -804,6 +953,128 @@ PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess,
sess->stat.rx.update_cnt++;
}
+
+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)
{
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 94d8e0f8..a6301315 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -234,6 +234,12 @@ static void stream_perror(const char *sender, const char *title,
}
+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
PJ_INLINE(int) trace_jb_print_timestamp(char **buf, pj_ssize_t len)
@@ -414,8 +420,7 @@ static void send_keep_alive_packet(pjmedia_stream *stream)
pkt_len);
/* 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
@@ -903,151 +908,159 @@ static void create_dtmf_payload(pjmedia_stream *stream,
}
-/**
- * check_tx_rtcp()
- *
- * This function is can be called by either put_frame() or get_frame(),
- * to transmit periodic RTCP SR/RR report.
- */
-static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp)
+static pj_status_t send_rtcp(pjmedia_stream *stream,
+ pj_bool_t with_sdes,
+ pj_bool_t with_bye,
+ pj_bool_t with_xr)
{
- /* Note that timestamp may represent local or remote timestamp,
- * depending on whether this function is called from put_frame()
- * or get_frame().
- */
+ 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 (stream->rtcp_last_tx == 0) {
-
- stream->rtcp_last_tx = timestamp;
+#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0)
+ with_xr = PJ_FALSE;
+#endif
- } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
-
- void *rtcp_pkt;
- int len;
- pj_status_t status;
+ 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;
+ }
- pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
+ /* Build RTCP SDES packet */
+ if (with_sdes) {
+ pjmedia_rtcp_sdes sdes;
+ unsigned sdes_len;
- status=pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, 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 sending RTCP"));
+ "Error generating RTCP SDES"));
+ } else {
+ len += sdes_len;
}
-
- stream->rtcp_last_tx = timestamp;
}
+ /* Build RTCP XR packet */
#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;
+ 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);
+ /* 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.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;
- pjmedia_rtcp_xr_update_info(&stream->rtcp.xr_session,
- PJMEDIA_RTCP_XR_INFO_JB_MAX,
- i);
+ 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);
- /* Build RTCP XR packet */
- pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0,
- &rtcp_pkt, &len);
+ pjmedia_rtcp_build_rtcp_xr(&stream->rtcp.xr_session, 0,
+ &xr_pkt, &xr_len);
- /* Send the RTCP XR to remote address */
- pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, 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,
- rtcp_pkt, len);
+ xr_pkt, xr_len);
}
- /* Update last tx RTCP XR */
- stream->rtcp_xr_last_tx = timestamp;
+ } else {
+ PJ_PERROR(4,(stream->port.info.name.ptr, PJ_ETOOBIG,
+ "Error generating RTCP-XR"));
}
}
#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 */
+ 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;
}
-/* Build RTCP BYE packet */
-static unsigned create_rtcp_bye(pjmedia_stream *stream, pj_uint8_t *pkt,
- unsigned max_len)
+/**
+ * check_tx_rtcp()
+ *
+ * This function is can be called by either put_frame() or get_frame(),
+ * to transmit periodic RTCP SR/RR report.
+ */
+static void check_tx_rtcp(pjmedia_stream *stream, pj_uint32_t timestamp)
{
- 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);
+ /* Note that timestamp may represent local or remote timestamp,
+ * depending on whether this function is called from put_frame()
+ * or get_frame().
+ */
+
+ if (stream->rtcp_last_tx == 0) {
+
+ stream->rtcp_last_tx = timestamp;
+
+ } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
+ pj_bool_t with_xr = PJ_FALSE;
+ pj_status_t status;
+
+#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"));
+ }
+
+ stream->rtcp_last_tx = timestamp;
+ }
}
@@ -1831,42 +1844,14 @@ on_return:
/* 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;
+ }
}
}
@@ -2400,47 +2385,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_destroy( pjmedia_stream *stream )
{
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);
}
/* Detach from transport
@@ -2794,18 +2741,9 @@ PJ_DEF(pj_status_t) pjmedia_stream_set_dtmf_callback(pjmedia_stream *stream,
PJ_DEF(pj_status_t)
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);
}
/*
@@ -2817,14 +2755,7 @@ pjmedia_stream_send_rtcp_bye( pjmedia_stream *stream )
PJ_ASSERT_RETURN(stream, PJ_EINVAL);
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);
}
return PJ_SUCCESS;