diff options
author | Benny Prijono <bennylp@teluu.com> | 2012-03-30 07:10:13 +0000 |
---|---|---|
committer | Benny Prijono <bennylp@teluu.com> | 2012-03-30 07:10:13 +0000 |
commit | 6b4964727bffb379aca9601e1cf69051ccbf600c (patch) | |
tree | 1d9739ea8b3b5e0421f1d99b39e798b1514fb644 /pjmedia/src | |
parent | 85ac546acb235df62169c4ad317da74a62e56a88 (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.c | 24 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/endpoint.c | 46 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/rtcp.c | 293 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/sound_port.c | 1 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/stream.c | 385 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/transport_ice.c | 9 | ||||
-rw-r--r-- | pjmedia/src/pjmedia/transport_srtp.c | 45 |
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); } } |