summaryrefslogtreecommitdiff
path: root/pjmedia/src
diff options
context:
space:
mode:
authorBenny Prijono <bennylp@teluu.com>2012-03-30 07:10:13 +0000
committerBenny Prijono <bennylp@teluu.com>2012-03-30 07:10:13 +0000
commit6b4964727bffb379aca9601e1cf69051ccbf600c (patch)
tree1d9739ea8b3b5e0421f1d99b39e798b1514fb644 /pjmedia/src
parent85ac546acb235df62169c4ad317da74a62e56a88 (diff)
Re #1474: Merged all changes from 1.12 - HEAD (from the 1.x branch)
git-svn-id: http://svn.pjsip.org/repos/pjproject/trunk@3999 74dad513-b988-da41-8d7b-12977e46ad98
Diffstat (limited to 'pjmedia/src')
-rw-r--r--pjmedia/src/pjmedia/conf_switch.c24
-rw-r--r--pjmedia/src/pjmedia/endpoint.c46
-rw-r--r--pjmedia/src/pjmedia/rtcp.c293
-rw-r--r--pjmedia/src/pjmedia/sound_port.c1
-rw-r--r--pjmedia/src/pjmedia/stream.c385
-rw-r--r--pjmedia/src/pjmedia/transport_ice.c9
-rw-r--r--pjmedia/src/pjmedia/transport_srtp.c45
7 files changed, 555 insertions, 248 deletions
diff --git a/pjmedia/src/pjmedia/conf_switch.c b/pjmedia/src/pjmedia/conf_switch.c
index 1e13e738..269405da 100644
--- a/pjmedia/src/pjmedia/conf_switch.c
+++ b/pjmedia/src/pjmedia/conf_switch.c
@@ -1086,11 +1086,14 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
while (f_start < f_end) {
unsigned nsamples_to_copy, nsamples_req;
- /* Copy frame to listener's TX buffer. */
+ /* Copy frame to listener's TX buffer.
+ * Note that if the destination is port 0, just copy the whole
+ * available samples.
+ */
nsamples_to_copy = f_end - f_start;
nsamples_req = cport_dst->samples_per_frame -
(frm_dst->size>>1);
- if (nsamples_to_copy > nsamples_req)
+ if (cport_dst->slot && nsamples_to_copy > nsamples_req)
nsamples_to_copy = nsamples_req;
/* Adjust TX level. */
@@ -1123,16 +1126,19 @@ static pj_status_t write_frame(struct conf_port *cport_dst,
/* Check if it's time to deliver the TX buffer to listener,
* i.e: samples count in TX buffer equal to listener's
- * samples per frame.
+ * samples per frame. Note that for destination port 0 this
+ * function will just populate all samples in the TX buffer.
*/
- if ((frm_dst->size >> 1) == cport_dst->samples_per_frame)
+ if (cport_dst->slot == 0) {
+ /* Update TX timestamp. */
+ pj_add_timestamp32(&cport_dst->ts_tx, nsamples_to_copy);
+ } else if ((frm_dst->size >> 1) ==
+ cport_dst->samples_per_frame)
{
- if (cport_dst->slot) {
- pjmedia_port_put_frame(cport_dst->port, frm_dst);
+ pjmedia_port_put_frame(cport_dst->port, frm_dst);
- /* Reset TX buffer. */
- frm_dst->size = 0;
- }
+ /* Reset TX buffer. */
+ frm_dst->size = 0;
/* Update TX timestamp. */
pj_add_timestamp32(&cport_dst->ts_tx,
diff --git a/pjmedia/src/pjmedia/endpoint.c b/pjmedia/src/pjmedia/endpoint.c
index b9ebe69a..4f592b6d 100644
--- a/pjmedia/src/pjmedia/endpoint.c
+++ b/pjmedia/src/pjmedia/endpoint.c
@@ -24,6 +24,7 @@
#include <pjmedia-audiodev/audiodev.h>
#include <pj/assert.h>
#include <pj/ioqueue.h>
+#include <pj/lock.h>
#include <pj/log.h>
#include <pj/os.h>
#include <pj/pool.h>
@@ -57,6 +58,14 @@ static int PJ_THREAD_FUNC worker_proc(void*);
#define MAX_THREADS 16
+/* List of media endpoint exit callback. */
+typedef struct exit_cb
+{
+ PJ_DECL_LIST_MEMBER (struct exit_cb);
+ pjmedia_endpt_exit_callback func;
+} exit_cb;
+
+
/** Concrete declaration of media endpoint. */
struct pjmedia_endpt
{
@@ -86,6 +95,9 @@ struct pjmedia_endpt
/** Is telephone-event enable */
pj_bool_t has_telephone_event;
+
+ /** List of exit callback. */
+ exit_cb exit_cb_list;
};
/**
@@ -129,6 +141,9 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create(pj_pool_factory *pf,
if (status != PJ_SUCCESS)
goto on_error;
+ /* Initialize exit callback list. */
+ pj_list_init(&endpt->exit_cb_list);
+
/* Create ioqueue if none is specified. */
if (endpt->ioqueue == NULL) {
@@ -189,6 +204,7 @@ PJ_DEF(pjmedia_codec_mgr*) pjmedia_endpt_get_codec_mgr(pjmedia_endpt *endpt)
*/
PJ_DEF(pj_status_t) pjmedia_endpt_destroy (pjmedia_endpt *endpt)
{
+ exit_cb *ecb;
unsigned i;
PJ_ASSERT_RETURN(endpt, PJ_EINVAL);
@@ -214,6 +230,14 @@ PJ_DEF(pj_status_t) pjmedia_endpt_destroy (pjmedia_endpt *endpt)
pjmedia_codec_mgr_destroy(&endpt->codec_mgr);
pjmedia_aud_subsys_shutdown();
+
+ /* Call all registered exit callbacks */
+ ecb = endpt->exit_cb_list.next;
+ while (ecb != &endpt->exit_cb_list) {
+ (*ecb->func)(endpt);
+ ecb = ecb->next;
+ }
+
pj_pool_release (endpt->pool);
return PJ_SUCCESS;
@@ -434,7 +458,7 @@ PJ_DEF(pj_status_t) pjmedia_endpt_create_audio_sdp(pjmedia_endpt *endpt,
rtpmap.param.slen = 1;
} else {
- rtpmap.param.ptr = NULL;
+ rtpmap.param.ptr = "";
rtpmap.param.slen = 0;
}
@@ -896,3 +920,23 @@ PJ_DEF(pj_status_t) pjmedia_endpt_dump(pjmedia_endpt *endpt)
return PJ_SUCCESS;
}
+
+PJ_DEF(pj_status_t) pjmedia_endpt_atexit( pjmedia_endpt *endpt,
+ pjmedia_endpt_exit_callback func)
+{
+ exit_cb *new_cb;
+
+ PJ_ASSERT_RETURN(endpt && func, PJ_EINVAL);
+
+ if (endpt->quit_flag)
+ return PJ_EINVALIDOP;
+
+ new_cb = PJ_POOL_ZALLOC_T(endpt->pool, exit_cb);
+ new_cb->func = func;
+
+ pj_enter_critical_section();
+ pj_list_push_back(&endpt->exit_cb_list, new_cb);
+ pj_leave_critical_section();
+
+ return PJ_SUCCESS;
+}
diff --git a/pjmedia/src/pjmedia/rtcp.c b/pjmedia/src/pjmedia/rtcp.c
index cb048302..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;
@@ -615,19 +628,21 @@ PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess,
goto end_rtt_calc;
}
- /* "Normalize" rtt value that is exceptionally high.
- * For such values, "normalize" the rtt to be three times
- * the average value.
+#if defined(PJMEDIA_RTCP_NORMALIZE_FACTOR) && PJMEDIA_RTCP_NORMALIZE_FACTOR!=0
+ /* "Normalize" rtt value that is exceptionally high. For such
+ * values, "normalize" the rtt to be PJMEDIA_RTCP_NORMALIZE_FACTOR
+ * times the average value.
*/
- if (rtt > ((unsigned)sess->stat.rtt.mean*3) && sess->stat.rtt.n!=0)
+ if (rtt > ((unsigned)sess->stat.rtt.mean *
+ PJMEDIA_RTCP_NORMALIZE_FACTOR) && sess->stat.rtt.n!=0)
{
unsigned orig_rtt = rtt;
- rtt = sess->stat.rtt.mean*3;
- PJ_LOG(5,(sess->name,
+ rtt = sess->stat.rtt.mean * PJMEDIA_RTCP_NORMALIZE_FACTOR;
+ PJ_LOG(5,(sess->name,
"RTT value %d usec is normalized to %d usec",
orig_rtt, rtt));
}
-
+#endif
TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt));
/* Update RTT stat */
@@ -650,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)
{
@@ -802,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/sound_port.c b/pjmedia/src/pjmedia/sound_port.c
index e377a1ed..d99d3049 100644
--- a/pjmedia/src/pjmedia/sound_port.c
+++ b/pjmedia/src/pjmedia/sound_port.c
@@ -441,7 +441,6 @@ PJ_DEF(pj_status_t) pjmedia_snd_port_create2(pj_pool_t *pool,
snd_port->dir = prm->base.dir;
snd_port->rec_id = prm->base.rec_id;
snd_port->play_id = prm->base.play_id;
- snd_port->dir = PJMEDIA_DIR_CAPTURE_PLAYBACK;
snd_port->clock_rate = prm->base.clock_rate;
snd_port->channel_count = prm->base.channel_count;
snd_port->samples_per_frame = prm->base.samples_per_frame;
diff --git a/pjmedia/src/pjmedia/stream.c b/pjmedia/src/pjmedia/stream.c
index b5354cd1..983f0445 100644
--- a/pjmedia/src/pjmedia/stream.c
+++ b/pjmedia/src/pjmedia/stream.c
@@ -155,6 +155,9 @@ struct pjmedia_stream
pj_uint32_t rtcp_interval; /**< Interval, in timestamp. */
pj_bool_t initial_rr; /**< Initial RTCP RR sent */
pj_bool_t rtcp_sdes_bye_disabled;/**< Send RTCP SDES/BYE?*/
+ void *out_rtcp_pkt; /**< Outgoing RTCP packet. */
+ unsigned out_rtcp_pkt_size;
+ /**< Outgoing RTCP packet size. */
/* RFC 2833 DTMF transmission queue: */
int tx_event_pt; /**< Outgoing pt for dtmf. */
@@ -245,6 +248,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)
@@ -425,8 +434,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
@@ -913,146 +921,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;
- if (stream->rtcp_last_tx == 0) {
-
- stream->rtcp_last_tx = timestamp;
+ /* Build RTCP RR/SR packet */
+ pjmedia_rtcp_build_rtcp(&stream->rtcp, &sr_rr_pkt, &len);
- } else if (timestamp - stream->rtcp_last_tx >= stream->rtcp_interval) {
-
- void *rtcp_pkt;
- int len;
+#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0)
+ with_xr = PJ_FALSE;
+#endif
- pjmedia_rtcp_build_rtcp(&stream->rtcp, &rtcp_pkt, &len);
+ if (with_sdes || with_bye || with_xr) {
+ pkt = (pj_uint8_t*) stream->out_rtcp_pkt;
+ pj_memcpy(pkt, sr_rr_pkt, len);
+ max_len = stream->out_rtcp_pkt_size;
+ } else {
+ pkt = sr_rr_pkt;
+ max_len = len;
+ }
- pjmedia_transport_send_rtcp(stream->transport, rtcp_pkt, len);
+ /* Build RTCP SDES packet */
+ if (with_sdes) {
+ pjmedia_rtcp_sdes sdes;
+ pj_size_t sdes_len;
- stream->rtcp_last_tx = timestamp;
+ 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 (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) {
+ pj_size_t 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;
+ }
}
@@ -1348,8 +1369,13 @@ static pj_status_t put_frame_imp( pjmedia_port *port,
stream->is_streaming = PJ_TRUE;
/* Send the RTP packet to the transport. */
- pjmedia_transport_send_rtp(stream->transport, channel->out_pkt,
- frame_out.size + sizeof(pjmedia_rtp_hdr));
+ status = pjmedia_transport_send_rtp(stream->transport, channel->out_pkt,
+ frame_out.size +
+ sizeof(pjmedia_rtp_hdr));
+ if (status != PJ_SUCCESS) {
+ PJ_PERROR(4,(stream->port.info.name.ptr, status,
+ "Error sending RTP"));
+ }
/* Update stat */
pjmedia_rtcp_tx_rtp(&stream->rtcp, frame_out.size);
@@ -1819,32 +1845,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);
- pjmedia_transport_send_rtcp(stream->transport,
- stream->enc->out_pkt, len);
- }
- } else {
- pjmedia_transport_send_rtcp(stream->transport, sr_rr_pkt, len);
- }
-
- 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;
+ }
}
}
@@ -1882,7 +1890,6 @@ static pj_status_t create_channel( pj_pool_t *pool,
{
pjmedia_channel *channel;
pj_status_t status;
- unsigned min_out_pkt_size;
/* Allocate memory for channel descriptor */
@@ -1914,15 +1921,6 @@ static pj_status_t create_channel( pj_pool_t *pool,
return PJ_ENOTSUP;
}
- /* It should big enough to hold (minimally) RTCP SR with an SDES. */
- min_out_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) +
- sizeof(pjmedia_rtcp_common) +
- (4 + stream->cname.slen) +
- 32;
-
- if (channel->out_pkt_size < min_out_pkt_size)
- channel->out_pkt_size = min_out_pkt_size;
-
channel->out_pkt = pj_pool_alloc(pool, channel->out_pkt_size);
PJ_ASSERT_RETURN(channel->out_pkt != NULL, PJ_ENOMEM);
@@ -2265,7 +2263,30 @@ PJ_DEF(pj_status_t) pjmedia_stream_create( pjmedia_endpt *endpt,
#endif
pjmedia_rtcp_init2(&stream->rtcp, &rtcp_setting);
+
+ if (info->rtp_seq_ts_set) {
+ stream->rtcp.stat.rtp_tx_last_seq = info->rtp_seq;
+ stream->rtcp.stat.rtp_tx_last_ts = info->rtp_ts;
+ }
+ }
+
+ /* Allocate outgoing RTCP buffer, should be enough to hold SR/RR, SDES,
+ * BYE, and XR.
+ */
+ stream->out_rtcp_pkt_size = sizeof(pjmedia_rtcp_sr_pkt) +
+ sizeof(pjmedia_rtcp_common) +
+ (4 + stream->cname.slen) +
+ 32;
+#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
+ if (info->rtcp_xr_enabled) {
+ stream->out_rtcp_pkt_size += sizeof(pjmedia_rtcp_xr_pkt);
}
+#endif
+
+ if (stream->out_rtcp_pkt_size > PJMEDIA_MAX_MTU)
+ stream->out_rtcp_pkt_size = PJMEDIA_MAX_MTU;
+
+ stream->out_rtcp_pkt = pj_pool_alloc(pool, stream->out_rtcp_pkt_size);
/* Only attach transport when stream is ready. */
status = pjmedia_transport_attach(tp, stream, &info->rem_addr,
@@ -2391,47 +2412,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
@@ -2799,18 +2782,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);
}
/*
@@ -2822,14 +2796,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;
diff --git a/pjmedia/src/pjmedia/transport_ice.c b/pjmedia/src/pjmedia/transport_ice.c
index bf7988e5..2059b5c2 100644
--- a/pjmedia/src/pjmedia/transport_ice.c
+++ b/pjmedia/src/pjmedia/transport_ice.c
@@ -419,7 +419,9 @@ static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice,
* the session, in this case we will answer with full ICE SDP and
* new ufrag/pwd pair.
*/
- if (!restart_session && pj_ice_strans_sess_is_complete(tp_ice->ice_st)) {
+ if (!restart_session && pj_ice_strans_sess_is_complete(tp_ice->ice_st) &&
+ pj_ice_strans_get_state(tp_ice->ice_st) != PJ_ICE_STRANS_STATE_FAILED)
+ {
const pj_ice_sess_check *check;
char *attr_buf;
pjmedia_sdp_conn *conn;
@@ -549,7 +551,10 @@ static pj_status_t encode_session_in_sdp(struct transport_ice *tp_ice,
pjmedia_sdp_attr_add(&m->attr_count, m->attr, attr);
}
- } else if (pj_ice_strans_has_sess(tp_ice->ice_st)) {
+ } else if (pj_ice_strans_has_sess(tp_ice->ice_st) &&
+ pj_ice_strans_get_state(tp_ice->ice_st) !=
+ PJ_ICE_STRANS_STATE_FAILED)
+ {
/* Encode all candidates to SDP media */
char *attr_buf;
unsigned comp;
diff --git a/pjmedia/src/pjmedia/transport_srtp.c b/pjmedia/src/pjmedia/transport_srtp.c
index 85ee70e9..76bd1518 100644
--- a/pjmedia/src/pjmedia/transport_srtp.c
+++ b/pjmedia/src/pjmedia/transport_srtp.c
@@ -270,9 +270,9 @@ const char* get_libsrtp_errstr(int err)
}
static pj_bool_t libsrtp_initialized;
-static void pjmedia_srtp_deinit_lib(void);
+static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt);
-PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(void)
+PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(pjmedia_endpt *endpt)
{
if (libsrtp_initialized == PJ_FALSE) {
err_status_t err;
@@ -284,7 +284,8 @@ PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(void)
return PJMEDIA_ERRNO_FROM_LIBSRTP(err);
}
- if (pj_atexit(pjmedia_srtp_deinit_lib) != PJ_SUCCESS) {
+ if (pjmedia_endpt_atexit(endpt, pjmedia_srtp_deinit_lib) != PJ_SUCCESS)
+ {
/* There will be memory leak when it fails to schedule libsrtp
* deinitialization, however the memory leak could be harmless,
* since in modern OS's memory used by an application is released
@@ -299,10 +300,19 @@ PJ_DEF(pj_status_t) pjmedia_srtp_init_lib(void)
return PJ_SUCCESS;
}
-static void pjmedia_srtp_deinit_lib(void)
+static void pjmedia_srtp_deinit_lib(pjmedia_endpt *endpt)
{
err_status_t err;
+ /* Note that currently this SRTP init/deinit is not equipped with
+ * reference counter, it should be safe as normally there is only
+ * one single instance of media endpoint and even if it isn't, the
+ * pjmedia_transport_srtp_create() will invoke SRTP init (the only
+ * drawback should be the delay described by #788).
+ */
+
+ PJ_UNUSED_ARG(endpt);
+
err = srtp_deinit();
if (err != err_status_ok) {
PJ_LOG(4, (THIS_FILE, "Failed to deinitialize libsrtp: %s",
@@ -410,7 +420,7 @@ PJ_DEF(pj_status_t) pjmedia_transport_srtp_create(
}
/* Init libsrtp. */
- status = pjmedia_srtp_init_lib();
+ status = pjmedia_srtp_init_lib(endpt);
if (status != PJ_SUCCESS)
return status;
@@ -907,19 +917,22 @@ static void srtp_rtp_cb( void *user_data, void *pkt, pj_ssize_t size)
(err == err_status_replay_old || err == err_status_replay_fail))
{
/* Handle such condition that stream is updated (RTP seq is reinited
- * & SRTP is restarted), but some old packets are still coming
- * so SRTP is learning wrong RTP seq. While the newly inited RTP seq
- * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect()
- * will returning err_status_replay_*. Restarting SRTP can resolve
- * this.
- */
- if (pjmedia_transport_srtp_start((pjmedia_transport*)srtp,
- &srtp->tx_policy, &srtp->rx_policy)
- != PJ_SUCCESS)
- {
+ * & SRTP is restarted), but some old packets are still coming
+ * so SRTP is learning wrong RTP seq. While the newly inited RTP seq
+ * comes, SRTP thinks the RTP seq is replayed, so srtp_unprotect()
+ * will return err_status_replay_*. Restarting SRTP can resolve this.
+ */
+ pjmedia_srtp_crypto tx, rx;
+ pj_status_t status;
+
+ tx = srtp->tx_policy;
+ rx = srtp->rx_policy;
+ status = pjmedia_transport_srtp_start((pjmedia_transport*)srtp,
+ &tx, &rx);
+ if (status != PJ_SUCCESS) {
PJ_LOG(5,(srtp->pool->obj_name, "Failed to restart SRTP, err=%s",
get_libsrtp_errstr(err)));
- } else {
+ } else if (!srtp->bypass_srtp) {
err = srtp_unprotect(srtp->srtp_rx_ctx, (pj_uint8_t*)pkt, &len);
}
}