summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2006-06-29 14:45:17 +0000
committerBenny Prijono <bennylp@teluu.com>2006-06-29 14:45:17 +0000
commit44b3f0a399d641273df8a4b240514e710a905c31 (patch)
tree048ab55ba9d1856c6d6df1252aa4ad3a86b37862
parentf5ac454bfdeea60297c45f6bc46349db25ab2e29 (diff)
Improvements in PJMEDIA to support RFC 3605 (RTCP attribute in SDP) and other changes to improve RTCP communication behind NAT. Also fixed bug related to RTCP reporting changes in revision 565
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@568 74dad513-b988-da41-8d7b-12977e46ad98
-rw-r--r--pjmedia/include/pjmedia/config.h10
-rw-r--r--pjmedia/include/pjmedia/errno.h7
-rw-r--r--pjmedia/include/pjmedia/sdp.h26
-rw-r--r--pjmedia/include/pjmedia/stream.h3
-rw-r--r--pjmedia/include/pjmedia/transport.h9
-rw-r--r--pjmedia/src/pjmedia/endpoint.c18
-rw-r--r--pjmedia/src/pjmedia/errno.c1
-rw-r--r--pjmedia/src/pjmedia/sdp.c243
-rw-r--r--pjmedia/src/pjmedia/session.c38
-rw-r--r--pjmedia/src/pjmedia/stream.c2
-rw-r--r--pjmedia/src/pjmedia/transport_udp.c84
-rw-r--r--pjsip-apps/src/samples/siprtp.c1
-rw-r--r--pjsip-apps/src/samples/siprtp_report.c8
-rw-r--r--pjsip-apps/src/samples/streamutil.c8
-rw-r--r--pjsip/src/pjsua-lib/pjsua_call.c8
15 files changed, 320 insertions, 146 deletions
diff --git a/pjmedia/include/pjmedia/config.h b/pjmedia/include/pjmedia/config.h
index 4533c0f4..f4bfeae4 100644
--- a/pjmedia/include/pjmedia/config.h
+++ b/pjmedia/include/pjmedia/config.h
@@ -155,6 +155,16 @@
/**
+ * 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
+
+
+
+/**
* @}
*/
diff --git a/pjmedia/include/pjmedia/errno.h b/pjmedia/include/pjmedia/errno.h
index a78ed131..c9c6bae2 100644
--- a/pjmedia/include/pjmedia/errno.h
+++ b/pjmedia/include/pjmedia/errno.h
@@ -140,9 +140,14 @@ PJ_BEGIN_DECL
#define PJMEDIA_SDP_EINPT (PJMEDIA_ERRNO_START+33) /* 220033 */
/**
* @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 */
/************************************************************
diff --git a/pjmedia/include/pjmedia/sdp.h b/pjmedia/include/pjmedia/sdp.h
index 2bc43bb2..08a11203 100644
--- a/pjmedia/include/pjmedia/sdp.h
+++ b/pjmedia/include/pjmedia/sdp.h
@@ -293,6 +293,32 @@ 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);
+
+
+
/* **************************************************************************
* SDP CONNECTION INFO
****************************************************************************
diff --git a/pjmedia/include/pjmedia/stream.h b/pjmedia/include/pjmedia/stream.h
index c487d8c2..7a02b72a 100644
--- a/pjmedia/include/pjmedia/stream.h
+++ b/pjmedia/include/pjmedia/stream.h
@@ -91,6 +91,9 @@ struct pjmedia_stream_info
pjmedia_type type; /**< Media type (audio, video) */
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. */
unsigned tx_pt; /**< Outgoing codec paylaod type. */
diff --git a/pjmedia/include/pjmedia/transport.h b/pjmedia/include/pjmedia/transport.h
index f1d38a90..d50db4be 100644
--- a/pjmedia/include/pjmedia/transport.h
+++ b/pjmedia/include/pjmedia/transport.h
@@ -73,6 +73,7 @@ struct pjmedia_transport_op
pj_status_t (*attach)(pjmedia_transport *tp,
void *user_data,
const pj_sockaddr_t *rem_addr,
+ const pj_sockaddr_t *rem_rtcp,
unsigned addr_len,
void (*rtp_cb)(void *user_data,
const void *pkt,
@@ -154,6 +155,10 @@ struct pjmedia_transport
* @param user_data Arbitrary user data to be set when the callbacks are
* 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
* the transport.
@@ -165,6 +170,7 @@ struct pjmedia_transport
PJ_INLINE(pj_status_t) pjmedia_transport_attach(pjmedia_transport *tp,
void *user_data,
const pj_sockaddr_t *rem_addr,
+ const pj_sockaddr_t *rem_rtcp,
unsigned addr_len,
void (*rtp_cb)(void *user_data,
const void *pkt,
@@ -173,7 +179,8 @@ PJ_INLINE(pj_status_t) pjmedia_transport_attach(pjmedia_transport *tp,
const void*pkt,
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);
}
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index becdb164..24730f65 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -327,10 +327,26 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_sdp( pjmedia_endpt *endpt,
m->desc.port_count = 1;
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) {
pjmedia_codec_info *codec_info;
diff --git a/pjmedia/src/pjmedia/errno.c b/pjmedia/src/pjmedia/errno.c
index 4016fecb..3d62e067 100644
--- a/pjmedia/src/pjmedia/errno.c
+++ b/pjmedia/src/pjmedia/errno.c
@@ -55,6 +55,7 @@ static const struct
PJ_BUILD_ERR( PJMEDIA_SDP_ENOFMT, "No SDP payload format in the media line" ),
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. */
PJ_BUILD_ERR( PJMEDIA_SDPNEG_EINSTATE, "Invalid SDP negotiator state for operation" ),
diff --git a/pjmedia/src/pjmedia/sdp.c b/pjmedia/src/pjmedia/sdp.c
index 6a677b6c..48f091d1 100644
--- a/pjmedia/src/pjmedia/sdp.c
+++ b/pjmedia/src/pjmedia/sdp.c
@@ -59,7 +59,7 @@ static pjmedia_sdp_attr *parse_attr(pj_pool_t *pool, pj_scanner *scanner,
parse_context *ctx);
static void parse_media(pj_scanner *scanner, pjmedia_sdp_media *med,
parse_context *ctx);
-
+static void on_scanner_error(pj_scanner *scanner);
/*
* Scanner character specification.
@@ -241,79 +241,70 @@ PJ_DEF(pj_status_t) pjmedia_sdp_attr_remove( unsigned *count,
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_rtpmap( const pjmedia_sdp_attr *attr,
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:
* a=rtpmap:98 L16/16000/2.
*/
- /* 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;
+ /* Init */
+ rtpmap->pt.slen = rtpmap->param.slen = rtpmap->enc_name.slen = 0;
- rtpmap->pt = token;
+ /* Parse */
+ PJ_TRY {
+ /* Eat the first ':' */
+ if (pj_scan_get_char(&scanner) != ':') {
+ status = PJMEDIA_SDP_EINRTPMAP;
+ goto on_return;
+ }
- /* Expecting space after payload type. */
- if (*p != ' ') return PJMEDIA_SDP_EINRTPMAP;
- /* Get space. */
- ++p;
+ /* Get payload type. */
+ pj_scan_get(&scanner, &cs_token, &rtpmap->pt);
- /* 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 encoding name. */
+ pj_scan_get(&scanner, &cs_token, &rtpmap->enc_name);
- /* Get '/' */
- ++p;
+ /* Expecting '/' after encoding name. */
+ if (pj_scan_get_char(&scanner) != '/') {
+ status = PJMEDIA_SDP_EINRTPMAP;
+ goto on_return;
+ }
- /* 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);
+ /* Get the clock rate. */
+ pj_scan_get(&scanner, &cs_token, &token);
+ rtpmap->clock_rate = pj_strtoul(&token);
- /* Expecting either '/' or EOF */
- if (p != end && *p != '/')
- return PJMEDIA_SDP_EINRTPMAP;
+ /* 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;
+ }
- if (p != end) {
- ++p;
- token.ptr = (char*)p;
- token.slen = end-p;
- rtpmap->param = token;
- } else {
- rtpmap->param.ptr = NULL;
- rtpmap->param.slen = 0;
+ status = PJ_SUCCESS;
+ }
+ PJ_CATCH(SYNTAX_ERROR) {
+ status = PJMEDIA_SDP_EINRTPMAP;
}
+ PJ_END;
- return PJ_SUCCESS;
+on_return:
+ pj_scan_fini(&scanner);
+ return status;
}
PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
@@ -358,6 +349,70 @@ PJ_DEF(pj_status_t) pjmedia_sdp_attr_get_fmtp( const pjmedia_sdp_attr *attr,
return PJ_SUCCESS;
}
+
+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,
pjmedia_sdp_rtpmap **p_rtpmap)
@@ -376,8 +431,8 @@ PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
pjmedia_sdp_attr **p_attr)
{
pjmedia_sdp_attr *attr;
- char tempbuf[64], *p, *endbuf;
- int i;
+ char tempbuf[64];
+ int len;
/* Check arguments. */
PJ_ASSERT_RETURN(pool && rtpmap && p_attr, PJ_EINVAL);
@@ -386,12 +441,6 @@ PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
PJ_ASSERT_RETURN(rtpmap->enc_name.slen && rtpmap->clock_rate,
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));
PJ_ASSERT_RETURN(attr != NULL, PJ_ENOMEM);
@@ -399,37 +448,22 @@ PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
attr->name.ptr = "rtpmap";
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';
+ /* 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 = p-tempbuf;
+ attr->value.slen = len;
attr->value.ptr = pj_pool_alloc(pool, attr->value.slen);
pj_memcpy(attr->value.ptr, tempbuf, attr->value.slen);
@@ -440,27 +474,22 @@ PJ_DEF(pj_status_t) pjmedia_sdp_rtpmap_to_attr(pj_pool_t *pool,
static int print_connection_info( pjmedia_sdp_conn *c, char *buf, int len)
{
- char *p = buf;
+ int printed;
- if (len < 8+c->net_type.slen+c->addr_type.slen+c->addr.slen) {
+ 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,
const pjmedia_sdp_conn *rhs)
{
diff --git a/pjmedia/src/pjmedia/session.c b/pjmedia/src/pjmedia/session.c
index 9073bef1..fa8650e0 100644
--- a/pjmedia/src/pjmedia/session.c
+++ b/pjmedia/src/pjmedia/session.c
@@ -78,7 +78,6 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
unsigned i, pt;
pj_status_t status;
-
/* Validate arguments: */
PJ_ASSERT_RETURN(pool && si && local && remote, PJ_EINVAL);
@@ -155,15 +154,42 @@ PJ_DEF(pj_status_t) pjmedia_stream_info_from_sdp(
/* 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! */
if (!pj_isdigit(*local_m->desc.fmt[0].ptr) ||
!pj_isdigit(*rem_m->desc.fmt[0].ptr))
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index 9c8d3fbb..c65d6ce2 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -1067,7 +1067,7 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
/* 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);
if (status != PJ_SUCCESS)
diff --git a/pjmedia/src/pjmedia/transport_udp.c b/pjmedia/src/pjmedia/transport_udp.c
index b6b2e4f2..99d6d0aa 100644
--- a/pjmedia/src/pjmedia/transport_udp.c
+++ b/pjmedia/src/pjmedia/transport_udp.c
@@ -71,6 +71,8 @@ struct transport_udp
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 */
pj_ioqueue_op_key_t rtcp_write_op; /**< Pending write operation */
@@ -89,6 +91,7 @@ static void on_rx_rtcp(pj_ioqueue_key_t *key,
static pj_status_t transport_attach( pjmedia_transport *tp,
void *user_data,
const pj_sockaddr_t *rem_addr,
+ const pj_sockaddr_t *rem_rtcp,
unsigned addr_len,
void (*rtp_cb)(void*,
const void*,
@@ -275,8 +278,10 @@ PJ_DEF(pj_status_t) pjmedia_transport_udp_attach( pjmedia_endpt *endpt,
/* 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;
@@ -384,18 +389,9 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
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 */
udp->rtp_src_cnt = 0;
@@ -403,6 +399,28 @@ static void on_rx_rtp( pj_ioqueue_key_t *key,
"Remote RTP address switched to %s:%d",
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)));
+
+ }
}
}
}
@@ -440,9 +458,30 @@ static void on_rx_rtcp(pj_ioqueue_key_t *key,
if (udp->attached && cb)
(*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);
}
@@ -452,6 +491,7 @@ static void on_rx_rtcp(pj_ioqueue_key_t *key,
static pj_status_t transport_attach( pjmedia_transport *tp,
void *user_data,
const pj_sockaddr_t *rem_addr,
+ const pj_sockaddr_t *rem_rtcp,
unsigned addr_len,
void (*rtp_cb)(void*,
const void*,
@@ -461,6 +501,7 @@ static pj_status_t transport_attach( pjmedia_transport *tp,
pj_ssize_t))
{
struct transport_udp *udp = (struct transport_udp*) tp;
+ const pj_sockaddr_in *rtcp_addr;
/* Validate arguments */
PJ_ASSERT_RETURN(tp && rem_addr && addr_len, PJ_EINVAL);
@@ -473,10 +514,19 @@ static pj_status_t transport_attach( pjmedia_transport *tp,
/* Copy remote RTP address */
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 */
udp->rtp_cb = rtp_cb;
diff --git a/pjsip-apps/src/samples/siprtp.c b/pjsip-apps/src/samples/siprtp.c
index d8d48e11..8e761ba2 100644
--- a/pjsip-apps/src/samples/siprtp.c
+++ b/pjsip-apps/src/samples/siprtp.c
@@ -1284,6 +1284,7 @@ static void call_on_media_update( pjsip_inv_session *inv,
/* Attach media to transport */
status = pjmedia_transport_attach(audio->transport, audio,
&audio->si.rem_addr,
+ &audio->si.rem_rtcp,
sizeof(pj_sockaddr_in),
&on_rx_rtp,
&on_rx_rtcp);
diff --git a/pjsip-apps/src/samples/siprtp_report.c b/pjsip-apps/src/samples/siprtp_report.c
index 83c4f11d..eed5fc06 100644
--- a/pjsip-apps/src/samples/siprtp_report.c
+++ b/pjsip-apps/src/samples/siprtp_report.c
@@ -161,9 +161,9 @@ static void print_call(int call_index)
audio->rtcp.stat.rx.loss,
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,
audio->rtcp.stat.rx.loss_period.avg / 1000.0,
@@ -204,9 +204,9 @@ static void print_call(int call_index)
audio->rtcp.stat.tx.loss,
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,
audio->rtcp.stat.tx.loss_period.avg / 1000.0,
diff --git a/pjsip-apps/src/samples/streamutil.c b/pjsip-apps/src/samples/streamutil.c
index 20544276..5b46ff54 100644
--- a/pjsip-apps/src/samples/streamutil.c
+++ b/pjsip-apps/src/samples/streamutil.c
@@ -606,9 +606,9 @@ static void print_stream_stat(pjmedia_stream *stream)
stat.rx.loss,
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,
stat.rx.loss_period.avg / 1000.0,
@@ -649,9 +649,9 @@ static void print_stream_stat(pjmedia_stream *stream)
stat.tx.loss,
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,
stat.tx.loss_period.avg / 1000.0,
diff --git a/pjsip/src/pjsua-lib/pjsua_call.c b/pjsip/src/pjsua-lib/pjsua_call.c
index 9e411044..a40dceb9 100644
--- a/pjsip/src/pjsua-lib/pjsua_call.c
+++ b/pjsip/src/pjsua-lib/pjsua_call.c
@@ -1304,9 +1304,9 @@ static void dump_media_session(const char *indent,
stat.rx.loss,
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,
stat.rx.loss_period.avg / 1000.0,
@@ -1363,9 +1363,9 @@ static void dump_media_session(const char *indent,
stat.tx.loss,
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,
stat.tx.loss_period.min / 1000.0,