Commit message: Merge pull request from GHSA-vhxv-phmx-g52q

* Prevent OOB read/write when parsing RTCP FB RPSI

* Add log information

* Modification based on comments.
CVE: CVE-2022-24786
CWE: CWE-200
Vulnerability description: PJSIP is a free and open source multimedia communication library written in C. PJSIP versions 2.12 and prior do not parse incoming RTCP feedback RPSI (Reference Picture Selection Indication) packet, but any app that directly uses pjmedia_rtcp_fb_parse_rpsi() will be affected. A patch is available in the `master` branch of the `pjsip/pjproject` GitHub repository. There are currently no known workarounds.

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_init_stat(pjmedia_rtcp_stat *stat)
{
    pj_time_val now;

    pj_assert(stat);

    pj_bzero(stat, sizeof(pjmedia_rtcp_stat));

    pj_math_stat_init(&stat->rtt);
    pj_math_stat_init(&stat->rx.loss_period);
    pj_math_stat_init(&stat->rx.jitter);
    pj_math_stat_init(&stat->tx.loss_period);
    pj_math_stat_init(&stat->tx.jitter);

#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
    pj_math_stat_init(&stat->rx_ipdv);
#endif

#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
    pj_math_stat_init(&stat->rx_raw_jitter);
#endif

    pj_gettimeofday(&now);
    stat->start = now;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_sli(
					const void *buf,
					pj_size_t length,
					unsigned *sli_cnt,
					pjmedia_rtcp_fb_sli sli[])
{
    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;
    pj_uint8_t *p;
    unsigned cnt, i;

    PJ_ASSERT_RETURN(buf && sli_cnt && sli, PJ_EINVAL);
    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_fb_common), PJ_ETOOSMALL);

    /* PLI uses pt==RTCP_PSFB and FMT==2 */
    if (hdr->rtcp_common.pt != RTCP_PSFB || hdr->rtcp_common.count != 2)
	return PJ_ENOTFOUND;

    cnt = pj_ntohs((pj_uint16_t)hdr->rtcp_common.length) - 2;
    if (length < (cnt+3)*4)
	return PJ_ETOOSMALL;

    *sli_cnt = PJ_MIN(*sli_cnt, cnt);

    p = (pj_uint8_t*)hdr + sizeof(*hdr);
    for (i = 0; i < *sli_cnt; ++i) {
	/* 'first' takes 13 bit */
	sli[i].first = (p[0] << 5) + ((p[1] & 0xF8) >> 3);
	/* 'number' takes 13 bit */
	sli[i].number = ((p[1] & 0x07) << 10) +
			(p[2] << 2) +
			((p[3] & 0xC0) >> 6);
	/* 'pict_id' takes 6 bit */
	sli[i].pict_id = (p[3] & 0x3F);
	p += 4;
    }

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp.c

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;
    }
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess, 
				     void **ret_p_pkt, int *len)
{
    pj_uint32_t expected, expected_interval, received_interval, lost_interval;
    pjmedia_rtcp_sr *sr;
    pjmedia_rtcp_rr *rr;
    pj_timestamp ts_now;
    pjmedia_rtcp_ntp_rec ntp;

    /* Get current NTP time. */
    pj_get_timestamp(&ts_now);
    pjmedia_rtcp_get_ntp_time(sess, &ntp);


    /* See if we have transmitted RTP packets since last time we
     * sent RTCP SR.
     */
    if (sess->stat.tx.pkt != pj_ntohl(sess->rtcp_sr_pkt.sr.sender_pcount)) {
	pj_time_val ts_time;
	pj_uint32_t rtp_ts;

	/* So we should send RTCP SR */
	*ret_p_pkt = (void*) &sess->rtcp_sr_pkt;
	*len = sizeof(pjmedia_rtcp_sr_pkt);
	rr = &sess->rtcp_sr_pkt.rr;
	sr = &sess->rtcp_sr_pkt.sr;

	/* Update packet count */
	sr->sender_pcount = pj_htonl(sess->stat.tx.pkt);

	/* Update octets count */
	sr->sender_bcount = pj_htonl(sess->stat.tx.bytes);

	/* Fill in NTP timestamp in SR. */
	sr->ntp_sec = pj_htonl(ntp.hi);
	sr->ntp_frac = pj_htonl(ntp.lo);

	/* Fill in RTP timestamp (corresponds to NTP timestamp) in SR. */
	ts_time.sec = ntp.hi - sess->tv_base.sec - JAN_1970;
	ts_time.msec = (long)(ntp.lo * 1000.0 / 0xFFFFFFFF);
	rtp_ts = sess->rtp_ts_base +
		 (pj_uint32_t)(sess->clock_rate*ts_time.sec) +
		 (pj_uint32_t)(sess->clock_rate*ts_time.msec/1000);
	sr->rtp_ts = pj_htonl(rtp_ts);

	TRACE_((sess->name, "TX RTCP SR: ntp_ts=%p", 
			   ((ntp.hi & 0xFFFF) << 16) + ((ntp.lo & 0xFFFF0000) 
				>> 16)));


    } else {
	/* We should send RTCP RR then */
	*ret_p_pkt = (void*) &sess->rtcp_rr_pkt;
	*len = sizeof(pjmedia_rtcp_rr_pkt);
	rr = &sess->rtcp_rr_pkt.rr;
	sr = NULL;
    }
    
    /* SSRC and last_seq */
    rr->ssrc = pj_htonl(sess->peer_ssrc);
    rr->last_seq = (sess->seq_ctrl.cycles & 0xFFFF0000L);
    /* Since this is an "+=" operation, make sure we update last_seq on
     * both RR and SR.
     */
    sess->rtcp_sr_pkt.rr.last_seq += sess->seq_ctrl.max_seq;
    sess->rtcp_rr_pkt.rr.last_seq += sess->seq_ctrl.max_seq;
    rr->last_seq = pj_htonl(rr->last_seq);


    /* Jitter */
    rr->jitter = pj_htonl(sess->jitter >> 4);
    
    
    /* Total lost. */
    expected = pj_ntohl(rr->last_seq) - sess->seq_ctrl.base_seq;

    /* This is bug: total lost already calculated on each incoming RTP!
    if (expected >= sess->received)
	sess->stat.rx.loss = expected - sess->received;
    else
	sess->stat.rx.loss = 0;
    */

    rr->total_lost_2 = (sess->stat.rx.loss >> 16) & 0xFF;
    rr->total_lost_1 = (sess->stat.rx.loss >> 8) & 0xFF;
    rr->total_lost_0 = (sess->stat.rx.loss & 0xFF);

    /* Fraction lost calculation */
    expected_interval = expected - sess->exp_prior;
    sess->exp_prior = expected;
    
    received_interval = sess->received - sess->rx_prior;
    sess->rx_prior = sess->received;
    
    if (expected_interval >= received_interval)
	lost_interval = expected_interval - received_interval;
    else
	lost_interval = 0;
    
    if (expected_interval==0 || lost_interval == 0) {
	rr->fract_lost = 0;
    } else {
	rr->fract_lost = (lost_interval << 8) / expected_interval;
    }
    
    if (sess->rx_lsr_time.u64 == 0 || sess->rx_lsr == 0) {
	rr->lsr = 0;
	rr->dlsr = 0;
    } else {
	pj_timestamp ts;
	pj_uint32_t lsr = sess->rx_lsr;
	pj_uint64_t lsr_time = sess->rx_lsr_time.u64;
	pj_uint32_t dlsr;
	
	/* Convert LSR time to 1/65536 seconds resolution */
	lsr_time = (lsr_time << 16) / sess->ts_freq.u64;

	/* Fill in LSR.
	   LSR is the middle 32bit of the last SR NTP time received.
	 */
	rr->lsr = pj_htonl(lsr);
	
	/* Fill in DLSR.
	   DLSR is Delay since Last SR, in 1/65536 seconds.
	 */
	ts.u64 = ts_now.u64;

	/* Convert interval to 1/65536 seconds value */
	ts.u64 = (ts.u64 << 16) / sess->ts_freq.u64;

	/* Get DLSR */
	dlsr = (pj_uint32_t)(ts.u64 - lsr_time);
	rr->dlsr = pj_htonl(dlsr);

	TRACE_((sess->name,"Tx RTCP RR: lsr=%p, lsr_time=%p, now=%p, dlsr=%p"
			   "(%ds:%03dms)",
			   lsr, 
			   (pj_uint32_t)lsr_time,
			   (pj_uint32_t)ts.u64, 
			   dlsr,
			   dlsr/65536,
			   (dlsr%65536)*1000/65536 ));
    }
    
    /* Update counter */
    pj_gettimeofday(&sess->stat.rx.update);
    sess->stat.rx.update_cnt++;
}

Project: pjproject
File: rtcp.c

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) {
    	/* Make sure the BYE reason does not exceed:
    	 * - the size of the available buffer
    	 * - the declared reason's length
    	 * - the actual packet size
    	 */
	reason.slen = PJ_MIN(sizeof(sess->stat.peer_sdes_buf_),
                             *((pj_uint8_t*)pkt+8));
        reason.slen = PJ_MIN(reason.slen, (pj_ssize_t)(size-9));

	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));
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_encode_sdp(
				    pj_pool_t *pool,
				    pjmedia_endpt *endpt,
				    const pjmedia_rtcp_fb_setting *opt,
				    pjmedia_sdp_session *sdp_local,
				    unsigned med_idx,
				    const pjmedia_sdp_session *sdp_remote)
{
    pjmedia_sdp_media *m = sdp_local->media[med_idx];
    unsigned i;
    unsigned sci_cnt = 0;
    sdp_codec_info_t sci[PJMEDIA_MAX_SDP_FMT];
    pj_status_t status;

    PJ_UNUSED_ARG(sdp_remote);

    PJ_ASSERT_RETURN(pool && endpt && opt && sdp_local, PJ_EINVAL);
    PJ_ASSERT_RETURN(med_idx < sdp_local->media_count, PJ_EINVAL);

    /* Add RTCP Feedback profile (AVPF), if configured to */
    if (!opt->dont_use_avpf) {
	unsigned proto = pjmedia_sdp_transport_get_proto(&m->desc.transport);
	if (!PJMEDIA_TP_PROTO_HAS_FLAG(proto, PJMEDIA_TP_PROFILE_RTCP_FB)) {
	    pj_str_t new_tp;
	    pj_strdup_with_null(pool, &new_tp, &m->desc.transport);
	    new_tp.ptr[new_tp.slen++] = 'F';
	    m->desc.transport = new_tp;
	}
    }

    /* Add RTCP Feedback capability to SDP */
    for (i = 0; i < opt->cap_count; ++i) {
	unsigned j;

	/* All codecs */
	if (pj_strcmp2(&opt->caps[i].codec_id, "*") == 0) {
	    status = add_sdp_attr_rtcp_fb(pool, "*", &opt->caps[i], m);
	    if (status != PJ_SUCCESS) {
		PJ_PERROR(3, (THIS_FILE, status,
			  "Failed generating SDP a=rtcp-fb:*"));
	    }
	    continue;
	}

	/* Specific codec */
	if (sci_cnt == 0) {
	    sci_cnt = PJ_ARRAY_SIZE(sci);
	    status = get_codec_info_from_sdp(endpt, m, &sci_cnt, sci);
	    if (status != PJ_SUCCESS) {
		PJ_PERROR(3, (THIS_FILE, status,
			  "Failed populating codec info from SDP"));
		return status;
	    }
	}

	for (j = 0; j < sci_cnt; ++j) {
	    if (pj_strnicmp2(&opt->caps[i].codec_id, sci[j].id,
			     opt->caps[i].codec_id.slen) == 0)
	    {
		char tmp[4];
		snprintf(tmp, sizeof(tmp), "%d", sci[j].pt);
		status = add_sdp_attr_rtcp_fb(pool, tmp, &opt->caps[i], m);
		if (status != PJ_SUCCESS) {
		    PJ_PERROR(3, (THIS_FILE, status,
			      "Failed generating SDP a=rtcp-fb:%d (%s)",
			      sci[j].pt, opt->caps[i].codec_id.ptr));
		}
		break;
	    }
	}
	if (j == sci_cnt) {
	    /* Codec ID not found in SDP (perhaps better ignore this error
	     * as app may configure audio and video in single setting).
	     */
	    PJ_PERROR(6, (THIS_FILE, PJ_ENOTFOUND,
		      "Failed generating SDP a=rtcp-fb for %s",
		      opt->caps[i].codec_id.ptr));
	}
    }

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_init2( pjmedia_rtcp_session *sess,
				 const pjmedia_rtcp_session_setting *settings)
{
    pjmedia_rtcp_sr_pkt *sr_pkt = &sess->rtcp_sr_pkt;
    pj_time_val now;
    
    /* Memset everything */
    pj_bzero(sess, sizeof(pjmedia_rtcp_session));

    /* Last RX timestamp in RTP packet */
    sess->rtp_last_ts = (unsigned)-1;

    /* Name */
    sess->name = settings->name ? settings->name : (char*)THIS_FILE;

    /* Set clock rate */
    sess->clock_rate = settings->clock_rate;
    sess->pkt_size = settings->samples_per_frame;

    /* Init common RTCP SR header */
    sr_pkt->common.version = 2;
    sr_pkt->common.count = 1;
    sr_pkt->common.pt = RTCP_SR;
    sr_pkt->common.length = pj_htons(12);
    sr_pkt->common.ssrc = pj_htonl(settings->ssrc);
    
    /* Copy to RTCP RR header */
    pj_memcpy(&sess->rtcp_rr_pkt.common, &sr_pkt->common, 
	      sizeof(pjmedia_rtcp_common));
    sess->rtcp_rr_pkt.common.pt = RTCP_RR;
    sess->rtcp_rr_pkt.common.length = pj_htons(7);

    /* Copy to RTCP FB common header */
    pj_memcpy(&sess->rtcp_fb_com, &sr_pkt->common, 
	      sizeof(pjmedia_rtcp_common));
    sess->rtcp_fb_com.ssrc_src = 0;

    /* Get time and timestamp base and frequency */
    pj_gettimeofday(&now);
    sess->tv_base = now;
    pj_get_timestamp(&sess->ts_base);
    pj_get_timestamp_freq(&sess->ts_freq);
    sess->rtp_ts_base = settings->rtp_ts_base;

    /* Initialize statistics states */
    pjmedia_rtcp_init_stat(&sess->stat);

    /* RR will be initialized on receipt of the first RTP packet. */
}

Project: pjproject
File: rtcp.c

static void parse_rtcp_fb(pjmedia_rtcp_session *sess,
			  const void *pkt,
			  pj_size_t size)
{
    unsigned cnt = 1;
    pjmedia_rtcp_fb_nack nack[1];
    //pjmedia_rtcp_fb_sli sli[1];
    //pjmedia_rtcp_fb_rpsi rpsi;
    pjmedia_event ev;
    pj_timestamp ts_now;

    pj_get_timestamp(&ts_now);

    if (pjmedia_rtcp_fb_parse_nack(pkt, size, &cnt, nack)==PJ_SUCCESS)
    {
	pjmedia_event_init(&ev, PJMEDIA_EVENT_RX_RTCP_FB, &ts_now, sess);
	ev.data.rx_rtcp_fb.cap.type = PJMEDIA_RTCP_FB_NACK;
	ev.data.rx_rtcp_fb.msg.nack = nack[0];
	pjmedia_event_publish(NULL, sess, &ev, 0);

    } else if (pjmedia_rtcp_fb_parse_pli(pkt, size)==PJ_SUCCESS)
    {
	pjmedia_event_init(&ev, PJMEDIA_EVENT_RX_RTCP_FB, &ts_now, sess);
	ev.data.rx_rtcp_fb.cap.type = PJMEDIA_RTCP_FB_NACK;
	pj_strset2(&ev.data.rx_rtcp_fb.cap.param, (char*)"pli");
	pjmedia_event_publish(NULL, sess, &ev, 0);

	/*  For other FB type implementations later
    } else if (pjmedia_rtcp_fb_parse_sli(pkt, size, &cnt, sli)==PJ_SUCCESS)
    {
    } else if (pjmedia_rtcp_fb_parse_rpsi(pkt, size, &rpsi)==PJ_SUCCESS)
    {
	*/
    } else {
	/* Ignore unknown RTCP Feedback */
	TRACE_((sess->name, "Received unknown RTCP feedback"));
    }
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_rx_rtp2(pjmedia_rtcp_session *sess, 
				  unsigned seq, 
				  unsigned rtp_ts,
				  unsigned payload,
				  pj_bool_t discarded)
{   
    pj_timestamp ts;
    pj_uint32_t arrival;
    pj_int32_t transit;
    pjmedia_rtp_status seq_st;

#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0)
    PJ_UNUSED_ARG(discarded);
#endif

    if (sess->stat.rx.pkt == 0) {
	/* Init sequence for the first time. */
	pjmedia_rtp_seq_init(&sess->seq_ctrl, (pj_uint16_t)seq);
    } 

    sess->stat.rx.pkt++;
    sess->stat.rx.bytes += payload;

    /* Process the RTP packet. */
    pjmedia_rtp_seq_update(&sess->seq_ctrl, (pj_uint16_t)seq, &seq_st);

    if (seq_st.status.flag.restart) {
	rtcp_init_seq(sess);
    }
    
    if (seq_st.status.flag.dup) {
	sess->stat.rx.dup++;
	TRACE_((sess->name, "Duplicate packet detected"));
    }

    if (seq_st.status.flag.outorder && !seq_st.status.flag.probation) {
	sess->stat.rx.reorder++;
	TRACE_((sess->name, "Out-of-order packet detected"));
    }

    if (seq_st.status.flag.bad) {
	sess->stat.rx.discard++;

#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
	pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, 
			       -1,				 /* lost    */
			       (seq_st.status.flag.dup? 1:0),	 /* dup     */
			       (!seq_st.status.flag.dup? 1:-1),  /* discard */
			       -1,				 /* jitter  */
			       -1, 0);				 /* toh	    */
#endif

	TRACE_((sess->name, "Bad packet discarded"));
	return;
    }

    /* Only mark "good" packets */
    ++sess->received;

    /* Calculate loss periods. */
    if (seq_st.diff > 1) {
	unsigned count = seq_st.diff - 1;
	unsigned period;

	period = count * sess->pkt_size * 1000 / sess->clock_rate;
	period *= 1000;

	/* Update packet lost. 
	 * The packet lost number will also be updated when we're sending
	 * outbound RTCP RR.
	 */
	sess->stat.rx.loss += (seq_st.diff - 1);
	TRACE_((sess->name, "%d packet(s) lost", seq_st.diff - 1));

	/* Update loss period stat */
	pj_math_stat_update(&sess->stat.rx.loss_period, period);
    }


    /*
     * Calculate jitter only when sequence is good (see RFC 3550 section A.8),
     * AND only when the timestamp is different than the last packet
     * (see RTP FAQ).
     */
    if (seq_st.diff == 1 && rtp_ts != sess->rtp_last_ts) {
	/* Get arrival time and convert timestamp to samples */
	pj_get_timestamp(&ts);
	ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64;
	arrival = ts.u32.lo;

	transit = arrival - rtp_ts;
    
	/* Ignore the first N packets as they normally have bad jitter
	 * due to other threads working to establish the call
	 */
	if (sess->transit == 0 || 
	    sess->received < PJMEDIA_RTCP_IGNORE_FIRST_PACKETS) 
	{
	    sess->transit = transit;
	    sess->stat.rx.jitter.min = (unsigned)-1;
	} else {
	    pj_int32_t d;
	    pj_uint32_t jitter;

	    d = transit - sess->transit;
	    if (d < 0) 
		d = -d;
	    
	    sess->jitter += d - ((sess->jitter + 8) >> 4);

	    /* Update jitter stat */
	    jitter = sess->jitter >> 4;
	    
	    /* Convert jitter unit from samples to usec */
	    if (jitter < 4294)
		jitter = jitter * 1000000 / sess->clock_rate;
	    else {
		jitter = jitter * 1000 / sess->clock_rate;
		jitter *= 1000;
	    }
	    pj_math_stat_update(&sess->stat.rx.jitter, jitter);


#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
	    {
		pj_uint32_t raw_jitter;

		/* Convert raw jitter unit from samples to usec */
		if (d < 4294)
		    raw_jitter = d * 1000000 / sess->clock_rate;
		else {
		    raw_jitter = d * 1000 / sess->clock_rate;
		    raw_jitter *= 1000;
		}
		
		/* Update jitter stat */
		pj_math_stat_update(&sess->stat.rx_raw_jitter, raw_jitter);
	    }
#endif


#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
	    {
		pj_int32_t ipdv;

		ipdv = transit - sess->transit;
		/* Convert IPDV unit from samples to usec */
		if (ipdv > -2147 && ipdv < 2147)
		    ipdv = ipdv * 1000000 / (int)sess->clock_rate;
		else {
		    ipdv = ipdv * 1000 / (int)sess->clock_rate;
		    ipdv *= 1000;
		}
		
		/* Update jitter stat */
		pj_math_stat_update(&sess->stat.rx_ipdv, ipdv);
	    }
#endif

#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
	    pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, 
				   0,			    /* lost    */
				   0,			    /* dup     */
				   discarded,		    /* discard */
				   (sess->jitter >> 4),	    /* jitter  */
				   -1, 0);		    /* toh     */
#endif

	    /* Update session transit */
	    sess->transit = transit;
	}
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
    } else if (seq_st.diff > 1) {
	int i;

	/* Report RTCP XR about packet losses */
	for (i=seq_st.diff-1; i>0; --i) {
	    pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq - i, 
				   1,			    /* lost    */
				   0,			    /* dup     */
				   0,			    /* discard */
				   -1,			    /* jitter  */
				   -1, 0);		    /* toh     */
	}

	/* Report RTCP XR this packet */
	pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq, 
			       0,			    /* lost    */
			       0,			    /* dup     */
			       discarded,		    /* discard */
			       -1,			    /* jitter  */
			       -1, 0);			    /* toh     */
#endif
    }

    /* Update timestamp of last RX RTP packet */
    sess->rtp_last_ts = rtp_ts;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_sli(
					pjmedia_rtcp_session *session, 
					void *buf,
					pj_size_t *length,
					unsigned sli_cnt,
					const pjmedia_rtcp_fb_sli sli[])
{
    pjmedia_rtcp_fb_common *hdr;
    pj_uint8_t *p;
    unsigned len, i;

    PJ_ASSERT_RETURN(session && buf && length && sli_cnt && sli, PJ_EINVAL);

    len = (3 + sli_cnt) * 4;
    if (len > *length)
	return PJ_ETOOSMALL;

    /* Build RTCP-FB SLI header */
    hdr = (pjmedia_rtcp_fb_common*)buf;
    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
    hdr->rtcp_common.pt = RTCP_PSFB;
    hdr->rtcp_common.count = 2; /* FMT = 2 */
    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));

    /* Build RTCP-FB SLI FCI */
    p = (pj_uint8_t*)hdr + sizeof(*hdr);
    for (i = 0; i < sli_cnt; ++i) {
	/* 'first' takes 13 bit */
	*p++  = (pj_uint8_t)((sli[i].first >> 5) & 0xFF);   /* 8 MSB bits */
	*p    = (pj_uint8_t)((sli[i].first & 31) << 3);	    /* 5 LSB bits */
	/* 'number' takes 13 bit */
	*p++ |= (pj_uint8_t)((sli[i].number >> 10) & 7);    /* 3 MSB bits */
	*p++  = (pj_uint8_t)((sli[i].number >> 2) & 0xFF);  /* 8 mid bits */
	*p    = (pj_uint8_t)((sli[i].number & 3) << 6);	    /* 2 LSB bits */
	/* 'pict_id' takes 6 bit */
	*p++ |= (sli[i].pict_id & 63);
    }

    /* Finally */
    *length = len;

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_nack(
					pjmedia_rtcp_session *session,
					void *buf,
					pj_size_t *length,
					unsigned nack_cnt,
					const pjmedia_rtcp_fb_nack nack[])
{
    pjmedia_rtcp_fb_common *hdr;
    pj_uint8_t *p;
    unsigned len, i;

    PJ_ASSERT_RETURN(session && buf && length && nack_cnt && nack, PJ_EINVAL);

    len = (3 + nack_cnt) * 4;
    if (len > *length)
	return PJ_ETOOSMALL;

    /* Build RTCP-FB NACK header */
    hdr = (pjmedia_rtcp_fb_common*)buf;
    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
    hdr->rtcp_common.pt = RTCP_RTPFB;
    hdr->rtcp_common.count = 1; /* FMT = 1 */
    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));

    /* Build RTCP-FB NACK FCI */
    p = (pj_uint8_t*)hdr + sizeof(*hdr);
    for (i = 0; i < nack_cnt; ++i) {
	pj_uint16_t val;
	val = pj_htons((pj_uint16_t)nack[i].pid);
	pj_memcpy(p, &val, 2);
	val = pj_htons(nack[i].blp);
	pj_memcpy(p+2, &val, 2);
	p += 4;
    }

    /* Finally */
    *length = len;

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(void) pjmedia_rtcp_fb_info_dup( pj_pool_t *pool,
				       pjmedia_rtcp_fb_info *dst,
				       const pjmedia_rtcp_fb_info *src)
{
    unsigned i;

    pj_assert(pool && dst && src);

    pj_memcpy(dst, src, sizeof(pjmedia_rtcp_fb_info));
    for (i = 0; i < src->cap_count; ++i) {
	pjmedia_rtcp_fb_cap_dup(pool, &dst->caps[i], &src->caps[i]);
    }
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(void) pjmedia_rtcp_fb_setting_dup( pj_pool_t *pool,
					  pjmedia_rtcp_fb_setting *dst,
					  const pjmedia_rtcp_fb_setting *src)
{
    unsigned i;

    pj_assert(pool && dst && src);

    pj_memcpy(dst, src, sizeof(pjmedia_rtcp_fb_setting));
    for (i = 0; i < src->cap_count; ++i) {
	pjmedia_rtcp_fb_cap_dup(pool, &dst->caps[i], &src->caps[i]);
    }
}

Project: pjproject
File: rtcp.c

static void rtcp_init_seq(pjmedia_rtcp_session *sess)
{
    sess->received = 0;
    sess->exp_prior = 0;
    sess->rx_prior = 0;
    sess->transit = 0;
    sess->jitter = 0;
}

Project: pjproject
File: rtcp.c

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;
	unsigned len;

	if (p + sizeof(pjmedia_rtcp_common) > p_end) {
	    TRACE_((sess->name, "Receiving truncated RTCP packet (1)"));
	    break;
	}
	common = (pjmedia_rtcp_common*)p;

	len = (pj_ntohs((pj_uint16_t)common->length)+1) * 4;
	if (p + len > p_end) {
	    TRACE_((sess->name, "Receiving truncated RTCP packet (2)"));
	    break;
	}

	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;
	case RTCP_RTPFB:
	case RTCP_PSFB:
	    parse_rtcp_fb(sess, p, len);
	    break;
	default:
	    /* Ignore unknown RTCP */
	    TRACE_((sess->name, "Received unknown RTCP packet type=%d",
		    common->pt));
	    break;
	}

	p += len;
    }
}

Project: pjproject
File: rtcp.c

PJ_DEF(pj_status_t) pjmedia_rtcp_enable_xr( pjmedia_rtcp_session *sess, 
					    pj_bool_t enable)
{
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)

    /* Check if request won't change anything */
    if (!(enable ^ sess->xr_enabled))
	return PJ_SUCCESS;

    if (!enable) {
	sess->xr_enabled = PJ_FALSE;
	return PJ_SUCCESS;
    }

    pjmedia_rtcp_xr_init(&sess->xr_session, sess, 0, 1);
    sess->xr_enabled = PJ_TRUE;

    return PJ_SUCCESS;

#else

    PJ_UNUSED_ARG(sess);
    PJ_UNUSED_ARG(enable);
    return PJ_ENOTSUP;

#endif
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_nack(
					const void *buf,
					pj_size_t length,
					unsigned *nack_cnt,
					pjmedia_rtcp_fb_nack nack[])
{
    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;
    pj_uint8_t *p;
    unsigned cnt, i;

    PJ_ASSERT_RETURN(buf && nack_cnt && nack, PJ_EINVAL);
    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_fb_common), PJ_ETOOSMALL);

    /* Generic NACK uses pt==RTCP_RTPFB and FMT==1 */
    if (hdr->rtcp_common.pt != RTCP_RTPFB || hdr->rtcp_common.count != 1)
	return PJ_ENOTFOUND;

    cnt = pj_ntohs((pj_uint16_t)hdr->rtcp_common.length);
    if (cnt > 2) cnt -= 2; else cnt = 0;
    if (length < (cnt+3)*4)
	return PJ_ETOOSMALL;

    *nack_cnt = PJ_MIN(*nack_cnt, cnt);

    p = (pj_uint8_t*)hdr + sizeof(*hdr);
    for (i = 0; i < *nack_cnt; ++i) {
	pj_uint16_t val;

	pj_memcpy(&val, p, 2);
	nack[i].pid = pj_ntohs(val);
	pj_memcpy(&val, p+2, 2);
	nack[i].blp = pj_ntohs(val);
	p += 4;
    }

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp_fb.c

static void pjmedia_rtcp_fb_cap_dup(pj_pool_t *pool,
				    pjmedia_rtcp_fb_cap *dst,
				    const pjmedia_rtcp_fb_cap *src)
{
    pj_strdup(pool, &dst->codec_id, &src->codec_id);
    dst->type = src->type;
    pj_strdup(pool, &dst->type_name, &src->type_name);
    pj_strdup(pool, &dst->param, &src->param);
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_pli(
					pjmedia_rtcp_session *session, 
					void *buf,
					pj_size_t *length)
{
    pjmedia_rtcp_fb_common *hdr;
    unsigned len;

    PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL);

    len = 12;
    if (len > *length)
	return PJ_ETOOSMALL;

    /* Build RTCP-FB PLI header */
    hdr = (pjmedia_rtcp_fb_common*)buf;
    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
    hdr->rtcp_common.pt = RTCP_PSFB;
    hdr->rtcp_common.count = 1; /* FMT = 1 */
    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));

    /* Finally */
    *length = len;

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_session_setting_default(
				    pjmedia_rtcp_session_setting *settings)
{
    pj_bzero(settings, sizeof(*settings));
}

Project: pjproject
File: rtcp.c

PJ_DEF(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess,
					      pjmedia_rtcp_ntp_rec *ntp)
{
/* Seconds between 1900-01-01 to 1970-01-01 */
#define JAN_1970  (2208988800UL)
    pj_timestamp ts;
    pj_status_t status;

    status = pj_get_timestamp(&ts);

    /* Fill up the high 32bit part */
    ntp->hi = (pj_uint32_t)((ts.u64 - sess->ts_base.u64) / sess->ts_freq.u64)
	      + sess->tv_base.sec + JAN_1970;

    /* Calculate seconds fractions */
    ts.u64 = (ts.u64 - sess->ts_base.u64) % sess->ts_freq.u64;
    pj_assert(ts.u64 < sess->ts_freq.u64);
    ts.u64 = (ts.u64 << 32) / sess->ts_freq.u64;

    /* Fill up the low 32bit part */
    ntp->lo = ts.u32.lo;


#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
    (defined(PJ_WIN64) && PJ_WIN64!=0) || \
    (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0)

    /* On Win32, since we use QueryPerformanceCounter() as the backend
     * timestamp API, we need to protect against this bug:
     *   Performance counter value may unexpectedly leap forward
     *   http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
     */
    {
	/*
	 * Compare elapsed time reported by timestamp with actual elapsed 
	 * time. If the difference is too excessive, then we use system
	 * time instead.
	 */

	/* MIN_DIFF needs to be large enough so that "normal" diff caused
	 * by system activity or context switch doesn't trigger the time
	 * correction.
	 */
	enum { MIN_DIFF = 400 };

	pj_time_val ts_time, elapsed, diff;

	pj_gettimeofday(&elapsed);

	ts_time.sec = ntp->hi - sess->tv_base.sec - JAN_1970;
	ts_time.msec = (long)(ntp->lo * 1000.0 / 0xFFFFFFFF);

	PJ_TIME_VAL_SUB(elapsed, sess->tv_base);

	if (PJ_TIME_VAL_LT(ts_time, elapsed)) {
	    diff = elapsed;
	    PJ_TIME_VAL_SUB(diff, ts_time);
	} else {
	    diff = ts_time;
	    PJ_TIME_VAL_SUB(diff, elapsed);
	}

	if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) {

	    TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms",
		    PJ_TIME_VAL_MSEC(diff)));


	    ntp->hi = elapsed.sec + sess->tv_base.sec + JAN_1970;
	    ntp->lo = (elapsed.msec * 65536 / 1000) << 16;
	}

    }
#endif

    return status;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_decode_sdp2(
				    pj_pool_t *pool,
				    pjmedia_endpt *endpt,
				    const void *opt,
				    const pjmedia_sdp_session *sdp,
				    unsigned med_idx,
				    int pt,
				    pjmedia_rtcp_fb_info *info)
{
    unsigned sci_cnt = PJMEDIA_MAX_SDP_FMT;
    sdp_codec_info_t sci[PJMEDIA_MAX_SDP_FMT];
    const pjmedia_sdp_media *m;
    pj_status_t status;
    unsigned i;

    PJ_UNUSED_ARG(opt);

    PJ_ASSERT_RETURN(pool && endpt && opt==NULL && sdp, PJ_EINVAL);
    PJ_ASSERT_RETURN(med_idx < sdp->media_count, PJ_EINVAL);
    PJ_ASSERT_RETURN(pt <= 127, PJ_EINVAL);

    m = sdp->media[med_idx];
    status = get_codec_info_from_sdp(endpt, m, &sci_cnt, sci);
    if (status != PJ_SUCCESS)
	return status;

    pj_bzero(info, sizeof(*info));

    /* Iterate all SDP attribute a=rtcp-fb in the SDP media */
    for (i = 0; i < m->attr_count; ++i) {
	const pjmedia_sdp_attr *a = m->attr[i];
	pj_str_t token;
	pj_ssize_t tok_idx;
	unsigned j;
	const char *codec_id = NULL;
	pj_str_t type_name = {0};
	pjmedia_rtcp_fb_type type = PJMEDIA_RTCP_FB_OTHER;

	/* Skip non a=rtcp-fb */
	if (pj_strcmp2(&a->name, "rtcp-fb") != 0)
	    continue;

	/* Get PT */
	tok_idx = pj_strtok2(&a->value, " \t", &token, 0);
	if (tok_idx == a->value.slen)
	    continue;

	if (pj_strcmp2(&token, "*") == 0) {
	    /* All codecs */
	    codec_id = "*";
	} else {
	    /* Specific PT/codec */
	    unsigned pt_ = (unsigned) pj_strtoul2(&token, NULL, 10);
	    for (j = 0; j < sci_cnt; ++j) {
		/* Check if payload type is valid and requested */
		if (pt_ == sci[j].pt && (pt < 0 || pt == (int)pt_)) {
		    codec_id = sci[j].id;
		    break;
		}
	    }
	}

	/* Skip this a=rtcp-fb if PT is not recognized or not requested */
	if (!codec_id)
	    continue;

	/* Get RTCP-FB type */
	tok_idx = pj_strtok2(&a->value, " \t", &token, tok_idx + token.slen);
	if (tok_idx == a->value.slen)
	    continue;

	for (j = 0; j < PJ_ARRAY_SIZE(rtcp_fb_type_name); ++j) {
	    if (pj_strcmp2(&token, rtcp_fb_type_name[j].name) == 0) {
		type = rtcp_fb_type_name[j].type;
		break;
	    }
	}
	if (type == PJMEDIA_RTCP_FB_OTHER)
	    type_name = token;

	/* Got all the mandatory fields, let's initialize RTCP-FB cap */
	pj_strdup2(pool, &info->caps[info->cap_count].codec_id, codec_id);
	info->caps[info->cap_count].type = type;
	if (type == PJMEDIA_RTCP_FB_OTHER)
	    pj_strdup(pool, &info->caps[info->cap_count].type_name, &type_name);

	/* Get RTCP-FB param */
	tok_idx = pj_strtok2(&a->value, " \t", &token, tok_idx + token.slen);
	if (tok_idx != a->value.slen)
	    pj_strdup(pool, &info->caps[info->cap_count].param, &token);

	/* Next */
	if (++info->cap_count == PJMEDIA_RTCP_FB_MAX_CAP)
	    break;
    }

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_build_rpsi(
					    pjmedia_rtcp_session *session, 
					    void *buf,
					    pj_size_t *length,
					    const pjmedia_rtcp_fb_rpsi *rpsi)
{
    pjmedia_rtcp_fb_common *hdr;
    pj_uint8_t *p;
    unsigned bitlen, padlen, len;

    PJ_ASSERT_RETURN(session && buf && length && rpsi, PJ_EINVAL);

    bitlen = (unsigned)rpsi->rpsi_bit_len + 16;
    padlen = (32 - (bitlen % 32)) % 32;
    len = (3 + (bitlen+padlen)/32) * 4;
    if (len > *length)
	return PJ_ETOOSMALL;

    /* Build RTCP-FB RPSI header */
    hdr = (pjmedia_rtcp_fb_common*)buf;
    pj_memcpy(hdr, &session->rtcp_fb_com, sizeof(*hdr));
    hdr->rtcp_common.pt = RTCP_PSFB;
    hdr->rtcp_common.count = 3; /* FMT = 3 */
    hdr->rtcp_common.length = pj_htons((pj_uint16_t)(len/4 - 1));

    /* Build RTCP-FB RPSI FCI */
    p = (pj_uint8_t*)hdr + sizeof(*hdr);
    /* PB (number of padding bits) */
    *p++ = (pj_uint8_t)padlen;
    /* Payload type */
    *p++ = rpsi->pt & 0x7F;
    /* RPSI bit string */
    pj_memcpy(p, rpsi->rpsi.ptr, rpsi->rpsi_bit_len/8);
    p += rpsi->rpsi_bit_len/8;
    if (rpsi->rpsi_bit_len % 8) {
	*p++ = *(rpsi->rpsi.ptr + rpsi->rpsi_bit_len/8);
    }
    /* Zero padding */
    if (padlen >= 8)
	pj_bzero(p, padlen/8);

    /* Finally */
    *length = len;

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *sess, 
			       char *name,
			       unsigned clock_rate,
			       unsigned samples_per_frame,
			       pj_uint32_t ssrc)
{
    pjmedia_rtcp_session_setting settings;

    pjmedia_rtcp_session_setting_default(&settings);
    settings.name = name;
    settings.clock_rate = clock_rate;
    settings.samples_per_frame = samples_per_frame;
    settings.ssrc = ssrc;

    pjmedia_rtcp_init2(sess, &settings);
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_decode_sdp(
				    pj_pool_t *pool,
				    pjmedia_endpt *endpt,
				    const void *opt,
				    const pjmedia_sdp_session *sdp,
				    unsigned med_idx,
				    pjmedia_rtcp_fb_info *info)
{
    return pjmedia_rtcp_fb_decode_sdp2(pool, endpt, opt, sdp, med_idx, -1,
				       info);
}

Project: pjproject
File: rtcp_fb.c

static pj_status_t get_codec_info_from_sdp(pjmedia_endpt *endpt,
					   const pjmedia_sdp_media *m,
					   unsigned *sci_cnt,
					   sdp_codec_info_t sci[])
{
    pjmedia_codec_mgr *codec_mgr;
    unsigned j, cnt = 0;
    pjmedia_type type = PJMEDIA_TYPE_UNKNOWN;
    pj_status_t status;

    type = pjmedia_get_type(&m->desc.media);
    if (type != PJMEDIA_TYPE_AUDIO && type != PJMEDIA_TYPE_VIDEO)
	return PJMEDIA_EUNSUPMEDIATYPE;

    codec_mgr = pjmedia_endpt_get_codec_mgr(endpt);
    for (j = 0; j < m->desc.fmt_count && cnt < *sci_cnt; ++j) {
	unsigned pt = 0;
	pt = pj_strtoul(&m->desc.fmt[j]);
	if (pt < 96) {
	    if (type == PJMEDIA_TYPE_AUDIO) {
		const pjmedia_codec_info *ci;
		status = pjmedia_codec_mgr_get_codec_info(codec_mgr, pt, &ci);
		if (status != PJ_SUCCESS)
		    continue;

		pjmedia_codec_info_to_id(ci, sci[cnt].id, sizeof(sci[0].id));
	    } else {
#if defined(PJMEDIA_HAS_VIDEO) && (PJMEDIA_HAS_VIDEO != 0)
		const pjmedia_vid_codec_info *ci;
		status = pjmedia_vid_codec_mgr_get_codec_info(NULL, pt, &ci);
		if (status != PJ_SUCCESS)
		    continue;

		pjmedia_vid_codec_info_to_id(ci, sci[cnt].id,
					     sizeof(sci[0].id));
#else
		continue;
#endif
	    }
	} else {
	    pjmedia_sdp_attr *a;
	    pjmedia_sdp_rtpmap r;
	    a = pjmedia_sdp_media_find_attr2(m, "rtpmap",
					     &m->desc.fmt[j]);
	    if (a == NULL)
		continue;
	    status = pjmedia_sdp_attr_get_rtpmap(a, &r);
	    if (status != PJ_SUCCESS)
		continue;

	    if (type == PJMEDIA_TYPE_AUDIO) {
		/* Audio codec id format: "name/clock-rate/channel-count" */
		if (r.param.slen) {
		    pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
				     "%.*s/%d/%.*s",
				     (int)r.enc_name.slen, r.enc_name.ptr,
				     r.clock_rate,
				     (int)r.param.slen, r.param.ptr);
		} else {
		    pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
				     "%.*s/%d/1",
				     (int)r.enc_name.slen, r.enc_name.ptr,
				     r.clock_rate);
		}
	    } else {
		/* Video codec id format: "name/payload-type" */
		pj_ansi_snprintf(sci[cnt].id, sizeof(sci[0].id),
				 "%.*s/%d",
				 (int)r.enc_name.slen, r.enc_name.ptr, pt);
	    }
	}
	sci[cnt++].pt = pt;
    }
    *sci_cnt = cnt;
    
    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess, 
				 unsigned bytes_payload_size)
{
    /* Update statistics */
    sess->stat.tx.pkt++;
    sess->stat.tx.bytes += bytes_payload_size;
}

Project: pjproject
File: rtcp.c

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;
    pj_size_t 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;
}

Project: pjproject
File: rtcp_fb.c

static pj_status_t add_sdp_attr_rtcp_fb( pj_pool_t *pool,
					 const char *pt,
					 const pjmedia_rtcp_fb_cap *cap,
					 pjmedia_sdp_media *m)
{
    pjmedia_sdp_attr *a;
    char tmp[128];
    pj_str_t val;
    pj_str_t type_name = {0};

    if (cap->type < PJMEDIA_RTCP_FB_OTHER)
	pj_cstr(&type_name, rtcp_fb_type_name[cap->type].name);
    else if (cap->type == PJMEDIA_RTCP_FB_OTHER)
	type_name = cap->type_name;

    if (type_name.slen == 0)
	return PJ_EINVAL;

    /* Generate RTCP FB param */
    if (cap->param.slen) {
	pj_ansi_snprintf(tmp, sizeof(tmp), "%s %.*s %.*s", pt,
			 (int)type_name.slen, type_name.ptr,
			 (int)cap->param.slen, cap->param.ptr);
    } else {
	pj_ansi_snprintf(tmp, sizeof(tmp), "%s %.*s", pt,
			 (int)type_name.slen, type_name.ptr);
    }
    pj_strset2(&val, tmp);

    /* Generate and add SDP attribute a=rtcp-fb */
    a = pjmedia_sdp_attr_create(pool, "rtcp-fb", &val);
    m->attr[m->attr_count++] = a;

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp.c

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;
    const pjmedia_rtcp_sr *sr = NULL;
    pj_uint32_t last_loss, jitter_samp, jitter;

    /* Parse RTCP */
    if (common->pt == RTCP_SR) {
        if (sizeof (pjmedia_rtcp_common) + sizeof (pjmedia_rtcp_sr) > size) {
	    TRACE_((sess->name, "Discarding RTCP SR due to truncated size "
	    			"%d bytes", size));
            return;
        }
	sr = (pjmedia_rtcp_sr*) (((char*)pkt) + sizeof(pjmedia_rtcp_common));
	if (common->count > 0 && size >= (sizeof(pjmedia_rtcp_sr_pkt))) {
	    rr = (pjmedia_rtcp_rr*)(((char*)pkt) + (sizeof(pjmedia_rtcp_common)
				    + sizeof(pjmedia_rtcp_sr)));
	}
    } else if (common->pt == RTCP_RR && common->count > 0) {
	if (sizeof (pjmedia_rtcp_common) + sizeof (pjmedia_rtcp_rr) > size) {
	    TRACE_((sess->name, "Discarding RTCP RR due to truncated size "
	    			"%d bytes", size));
	    return;
	}
	rr = (pjmedia_rtcp_rr*)(((char*)pkt) + sizeof(pjmedia_rtcp_common));
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
    } else if (common->pt == RTCP_XR) {
	if (sess->xr_enabled)
	    pjmedia_rtcp_xr_rx_rtcp_xr(&sess->xr_session, pkt, size);

	return;
#endif
    }


    if (sr) {
	/* Save LSR from NTP timestamp of RTCP packet */
	sess->rx_lsr = ((pj_ntohl(sr->ntp_sec) & 0x0000FFFF) << 16) | 
		       ((pj_ntohl(sr->ntp_frac) >> 16) & 0xFFFF);

	/* Calculate SR arrival time for DLSR */
	pj_get_timestamp(&sess->rx_lsr_time);

	TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p", 
		sess->rx_lsr,
		(pj_uint32_t)(sess->rx_lsr_time.u64*65536/sess->ts_freq.u64)));
    }


    /* Nothing more to do if there's no RR packet */
    if (rr == NULL)
	return;


    last_loss = sess->stat.tx.loss;

    /* Get packet loss */
    sess->stat.tx.loss = (rr->total_lost_2 << 16) +
			 (rr->total_lost_1 << 8) +
			  rr->total_lost_0;

    TRACE_((sess->name, "Rx RTCP RR: total_lost_2=%x, 1=%x, 0=%x, lost=%d", 
	    (int)rr->total_lost_2,
	    (int)rr->total_lost_1,
	    (int)rr->total_lost_0,
	    sess->stat.tx.loss));
    
    /* We can't calculate the exact loss period for TX, so just give the
     * best estimation.
     */
    if (sess->stat.tx.loss > last_loss) {
	unsigned period;

	/* Loss period in msec */
	period = (sess->stat.tx.loss - last_loss) * sess->pkt_size *
		 1000 / sess->clock_rate;

	/* Loss period in usec */
	period *= 1000;

	/* Update loss period stat */
	pj_math_stat_update(&sess->stat.tx.loss_period, period);
    }

    /* Get jitter value in usec */
    jitter_samp = pj_ntohl(rr->jitter);
    /* Calculate jitter in usec, avoiding overflows */
    if (jitter_samp <= 4294)
	jitter = jitter_samp * 1000000 / sess->clock_rate;
    else {
	jitter = jitter_samp * 1000 / sess->clock_rate;
	jitter *= 1000;
    }

    /* Update jitter statistics */
    pj_math_stat_update(&sess->stat.tx.jitter, jitter);

    /* Can only calculate if LSR and DLSR is present in RR */
    if (rr->lsr && rr->dlsr) {
	pj_uint32_t lsr, now, dlsr;
	pj_uint64_t eedelay;
	pjmedia_rtcp_ntp_rec ntp;

	/* LSR is the middle 32bit of NTP. It has 1/65536 second 
	 * resolution 
	 */
	lsr = pj_ntohl(rr->lsr);

	/* DLSR is delay since LSR, also in 1/65536 resolution */
	dlsr = pj_ntohl(rr->dlsr);

	/* Get current time, and convert to 1/65536 resolution */
	pjmedia_rtcp_get_ntp_time(sess, &ntp);
	now = ((ntp.hi & 0xFFFF) << 16) + (ntp.lo >> 16);

	/* End-to-end delay is (now-lsr-dlsr) */
	eedelay = now - lsr - dlsr;

	/* Convert end to end delay to usec (keeping the calculation in
         * 64bit space)::
	 *   sess->ee_delay = (eedelay * 1000) / 65536;
	 */
	if (eedelay < 4294) {
	    eedelay = (eedelay * 1000000) >> 16;
	} else {
	    eedelay = (eedelay * 1000) >> 16;
	    eedelay *= 1000;
	}

	TRACE_((sess->name, "Rx RTCP RR: lsr=%p, dlsr=%p (%d:%03dms), "
			   "now=%p, rtt=%p",
		lsr, dlsr, dlsr/65536, (dlsr%65536)*1000/65536,
		now, (pj_uint32_t)eedelay));
	
	/* Only save calculation if "now" is greater than lsr, or
	 * otherwise rtt will be invalid 
	 */
	if (now-dlsr >= lsr) {
	    unsigned rtt = (pj_uint32_t)eedelay;
	    
	    /* Check that eedelay value really makes sense. 
	     * We allow up to 30 seconds RTT!
	     */
	    if (eedelay > 30 * 1000 * 1000UL) {

		TRACE_((sess->name, "RTT not making any sense, ignored.."));
		goto end_rtt_calc;
	    }

#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 *
		       PJMEDIA_RTCP_NORMALIZE_FACTOR) && sess->stat.rtt.n!=0)
	    {
		unsigned orig_rtt = rtt;
		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 */
	    pj_math_stat_update(&sess->stat.rtt, rtt);

	} else {
	    PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: "
				   "lsr=%p, now=%p, dlsr=%p (%d:%03dms), "
				   "diff=%d",
				   lsr, now, dlsr, dlsr/65536,
				   (dlsr%65536)*1000/65536,
				   dlsr-(now-lsr)));
	}
    }

end_rtt_calc:

    pj_gettimeofday(&sess->stat.tx.update);
    sess->stat.tx.update_cnt++;
}

Project: pjproject
File: rtcp.c

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;
    pj_size_t 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;
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_fini(pjmedia_rtcp_session *sess)
{
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
    pjmedia_rtcp_xr_fini(&sess->xr_session);
#else
    /* Nothing to do. */
    PJ_UNUSED_ARG(sess);
#endif
}

Project: pjproject
File: rtcp.c

PJ_DEF(void) pjmedia_rtcp_rx_rtp( pjmedia_rtcp_session *sess, 
				  unsigned seq, 
				  unsigned rtp_ts,
				  unsigned payload)
{
    pjmedia_rtcp_rx_rtp2(sess, seq, rtp_ts, payload, PJ_FALSE);
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_pli(
					const void *buf,
					pj_size_t length)
{
    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;

    PJ_ASSERT_RETURN(buf, PJ_EINVAL);

    if (length < 12)
    	return PJ_ETOOSMALL;

    /* PLI uses pt==RTCP_PSFB and FMT==1 */
    if (hdr->rtcp_common.pt != RTCP_PSFB || hdr->rtcp_common.count != 1)
	return PJ_ENOTFOUND;

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_setting_default(
					pjmedia_rtcp_fb_setting *opt)
{
    pj_bzero(opt, sizeof(*opt));
    opt->dont_use_avpf = PJ_TRUE;

    return PJ_SUCCESS;
}

Project: pjproject
File: rtcp_fb.c

PJ_DEF(pj_status_t) pjmedia_rtcp_fb_parse_rpsi(
					const void *buf,
					pj_size_t length,
					pjmedia_rtcp_fb_rpsi *rpsi)
{
    pjmedia_rtcp_fb_common *hdr = (pjmedia_rtcp_fb_common*) buf;
    pj_uint8_t *p;
    pj_uint8_t padlen;
    pj_size_t rpsi_len;

    PJ_ASSERT_RETURN(buf && rpsi, PJ_EINVAL);
    PJ_ASSERT_RETURN(length >= sizeof(pjmedia_rtcp_fb_common), PJ_ETOOSMALL);

    /* RPSI uses pt==RTCP_PSFB and FMT==3 */
    if (hdr->rtcp_common.pt != RTCP_PSFB || hdr->rtcp_common.count != 3)
	return PJ_ENOTFOUND;

    if (hdr->rtcp_common.length < 3) {    
        PJ_PERROR(3, (THIS_FILE, PJ_ETOOSMALL,
                      "Failed parsing FB RPSI, invalid header length"));
	return PJ_ETOOSMALL;
    }

    rpsi_len = (pj_ntohs((pj_uint16_t)hdr->rtcp_common.length)-2) * 4;
    if (length < rpsi_len + 12)
	return PJ_ETOOSMALL;

    p = (pj_uint8_t*)hdr + sizeof(*hdr);
    padlen = *p++;

    if (padlen >= 32) {
        PJ_PERROR(3, (THIS_FILE, PJ_ETOOBIG,
                      "Failed parsing FB RPSI, invalid RPSI padding len"));
	return PJ_ETOOBIG;
    }

    if ((rpsi_len * 8) < (unsigned)(16 + padlen)) {
        PJ_PERROR(3, (THIS_FILE, PJ_ETOOSMALL,
                      "Failed parsing FB RPSI, invalid RPSI bit len"));
	return PJ_ETOOSMALL;
    }

    rpsi->pt = (*p++ & 0x7F);
    rpsi->rpsi_bit_len = rpsi_len*8 - 16 - padlen;
    pj_strset(&rpsi->rpsi, (char*)p, (rpsi->rpsi_bit_len + 7)/8);

    return PJ_SUCCESS;
}