1
/* $Id: rtcp.c 3960 2012-02-27 14:41:21Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia/rtcp.h>
21
#include <pjmedia/errno.h>
22
#include <pj/assert.h>
26
#include <pj/string.h>
28
#define THIS_FILE "rtcp.c"
47
#if PJ_HAS_HIGH_RES_TIMER==0
48
# error "High resolution timer needs to be enabled"
54
# define TRACE_(x) PJ_LOG(3,x)
63
PJ_DEF(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess,
64
pjmedia_rtcp_ntp_rec *ntp)
66
/* Seconds between 1900-01-01 to 1970-01-01 */
67
#define JAN_1970 (2208988800UL)
71
status = pj_get_timestamp(&ts);
73
/* Fill up the high 32bit part */
74
ntp->hi = (pj_uint32_t)((ts.u64 - sess->ts_base.u64) / sess->ts_freq.u64)
75
+ sess->tv_base.sec + JAN_1970;
77
/* Calculate seconds fractions */
78
ts.u64 = (ts.u64 - sess->ts_base.u64) % sess->ts_freq.u64;
79
pj_assert(ts.u64 < sess->ts_freq.u64);
80
ts.u64 = (ts.u64 << 32) / sess->ts_freq.u64;
82
/* Fill up the low 32bit part */
86
#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
87
(defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0)
89
/* On Win32, since we use QueryPerformanceCounter() as the backend
90
* timestamp API, we need to protect against this bug:
91
* Performance counter value may unexpectedly leap forward
92
* http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
96
* Compare elapsed time reported by timestamp with actual elapsed
97
* time. If the difference is too excessive, then we use system
101
/* MIN_DIFF needs to be large enough so that "normal" diff caused
102
* by system activity or context switch doesn't trigger the time
105
enum { MIN_DIFF = 400 };
107
pj_time_val ts_time, elapsed, diff;
109
pj_gettimeofday(&elapsed);
111
ts_time.sec = ntp->hi - sess->tv_base.sec - JAN_1970;
112
ts_time.msec = (long)(ntp->lo * 1000.0 / 0xFFFFFFFF);
114
PJ_TIME_VAL_SUB(elapsed, sess->tv_base);
116
if (PJ_TIME_VAL_LT(ts_time, elapsed)) {
118
PJ_TIME_VAL_SUB(diff, ts_time);
121
PJ_TIME_VAL_SUB(diff, elapsed);
124
if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) {
126
TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms",
127
PJ_TIME_VAL_MSEC(diff)));
130
ntp->hi = elapsed.sec + sess->tv_base.sec + JAN_1970;
131
ntp->lo = (elapsed.msec * 65536 / 1000) << 16;
142
* Initialize RTCP session setting.
144
PJ_DEF(void) pjmedia_rtcp_session_setting_default(
145
pjmedia_rtcp_session_setting *settings)
147
pj_bzero(settings, sizeof(*settings));
152
* Initialize bidirectional RTCP statistics.
155
PJ_DEF(void) pjmedia_rtcp_init_stat(pjmedia_rtcp_stat *stat)
161
pj_bzero(stat, sizeof(pjmedia_rtcp_stat));
163
pj_math_stat_init(&stat->rtt);
164
pj_math_stat_init(&stat->rx.loss_period);
165
pj_math_stat_init(&stat->rx.jitter);
166
pj_math_stat_init(&stat->tx.loss_period);
167
pj_math_stat_init(&stat->tx.jitter);
169
#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
170
pj_math_stat_init(&stat->rx_ipdv);
173
#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
174
pj_math_stat_init(&stat->rx_raw_jitter);
177
pj_gettimeofday(&now);
183
* Initialize RTCP session.
185
PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *sess,
188
unsigned samples_per_frame,
191
pjmedia_rtcp_session_setting settings;
193
pjmedia_rtcp_session_setting_default(&settings);
194
settings.name = name;
195
settings.clock_rate = clock_rate;
196
settings.samples_per_frame = samples_per_frame;
197
settings.ssrc = ssrc;
199
pjmedia_rtcp_init2(sess, &settings);
204
* Initialize RTCP session.
206
PJ_DEF(void) pjmedia_rtcp_init2( pjmedia_rtcp_session *sess,
207
const pjmedia_rtcp_session_setting *settings)
209
pjmedia_rtcp_sr_pkt *sr_pkt = &sess->rtcp_sr_pkt;
212
/* Memset everything */
213
pj_bzero(sess, sizeof(pjmedia_rtcp_session));
215
/* Last RX timestamp in RTP packet */
216
sess->rtp_last_ts = (unsigned)-1;
219
sess->name = settings->name ? settings->name : (char*)THIS_FILE;
222
sess->clock_rate = settings->clock_rate;
223
sess->pkt_size = settings->samples_per_frame;
225
/* Init common RTCP SR header */
226
sr_pkt->common.version = 2;
227
sr_pkt->common.count = 1;
228
sr_pkt->common.pt = RTCP_SR;
229
sr_pkt->common.length = pj_htons(12);
230
sr_pkt->common.ssrc = pj_htonl(settings->ssrc);
232
/* Copy to RTCP RR header */
233
pj_memcpy(&sess->rtcp_rr_pkt.common, &sr_pkt->common,
234
sizeof(pjmedia_rtcp_common));
235
sess->rtcp_rr_pkt.common.pt = RTCP_RR;
236
sess->rtcp_rr_pkt.common.length = pj_htons(7);
238
/* Get time and timestamp base and frequency */
239
pj_gettimeofday(&now);
241
pj_get_timestamp(&sess->ts_base);
242
pj_get_timestamp_freq(&sess->ts_freq);
243
sess->rtp_ts_base = settings->rtp_ts_base;
245
/* Initialize statistics states */
246
pjmedia_rtcp_init_stat(&sess->stat);
248
/* RR will be initialized on receipt of the first RTP packet. */
252
PJ_DEF(void) pjmedia_rtcp_fini(pjmedia_rtcp_session *sess)
254
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
255
pjmedia_rtcp_xr_fini(&sess->xr_session);
262
static void rtcp_init_seq(pjmedia_rtcp_session *sess)
271
PJ_DEF(void) pjmedia_rtcp_rx_rtp( pjmedia_rtcp_session *sess,
276
pjmedia_rtcp_rx_rtp2(sess, seq, rtp_ts, payload, PJ_FALSE);
279
PJ_DEF(void) pjmedia_rtcp_rx_rtp2(pjmedia_rtcp_session *sess,
288
pjmedia_rtp_status seq_st;
291
#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0)
292
PJ_UNUSED_ARG(discarded);
295
if (sess->stat.rx.pkt == 0) {
296
/* Init sequence for the first time. */
297
pjmedia_rtp_seq_init(&sess->seq_ctrl, (pj_uint16_t)seq);
301
sess->stat.rx.bytes += payload;
303
/* Process the RTP packet. */
304
last_seq = sess->seq_ctrl.max_seq;
305
pjmedia_rtp_seq_update(&sess->seq_ctrl, (pj_uint16_t)seq, &seq_st);
307
if (seq_st.status.flag.restart) {
311
if (seq_st.status.flag.dup) {
313
TRACE_((sess->name, "Duplicate packet detected"));
316
if (seq_st.status.flag.outorder && !seq_st.status.flag.probation) {
317
sess->stat.rx.reorder++;
318
TRACE_((sess->name, "Out-of-order packet detected"));
321
if (seq_st.status.flag.bad) {
322
sess->stat.rx.discard++;
324
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
325
pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq,
327
(seq_st.status.flag.dup? 1:0), /* dup */
328
(!seq_st.status.flag.dup? 1:-1), /* discard */
333
TRACE_((sess->name, "Bad packet discarded"));
337
/* Only mark "good" packets */
340
/* Calculate loss periods. */
341
if (seq_st.diff > 1) {
342
unsigned count = seq_st.diff - 1;
345
period = count * sess->pkt_size * 1000 / sess->clock_rate;
348
/* Update packet lost.
349
* The packet lost number will also be updated when we're sending
352
sess->stat.rx.loss += (seq_st.diff - 1);
353
TRACE_((sess->name, "%d packet(s) lost", seq_st.diff - 1));
355
/* Update loss period stat */
356
pj_math_stat_update(&sess->stat.rx.loss_period, period);
361
* Calculate jitter only when sequence is good (see RFC 3550 section A.8),
362
* AND only when the timestamp is different than the last packet
365
if (seq_st.diff == 1 && rtp_ts != sess->rtp_last_ts) {
366
/* Get arrival time and convert timestamp to samples */
367
pj_get_timestamp(&ts);
368
ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64;
371
transit = arrival - rtp_ts;
373
/* Ignore the first N packets as they normally have bad jitter
374
* due to other threads working to establish the call
376
if (sess->transit == 0 ||
377
sess->received < PJMEDIA_RTCP_IGNORE_FIRST_PACKETS)
379
sess->transit = transit;
380
sess->stat.rx.jitter.min = (unsigned)-1;
385
d = transit - sess->transit;
389
sess->jitter += d - ((sess->jitter + 8) >> 4);
391
/* Update jitter stat */
392
jitter = sess->jitter >> 4;
394
/* Convert jitter unit from samples to usec */
396
jitter = jitter * 1000000 / sess->clock_rate;
398
jitter = jitter * 1000 / sess->clock_rate;
401
pj_math_stat_update(&sess->stat.rx.jitter, jitter);
404
#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
406
pj_uint32_t raw_jitter;
408
/* Convert raw jitter unit from samples to usec */
410
raw_jitter = d * 1000000 / sess->clock_rate;
412
raw_jitter = d * 1000 / sess->clock_rate;
416
/* Update jitter stat */
417
pj_math_stat_update(&sess->stat.rx_raw_jitter, raw_jitter);
422
#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
426
ipdv = transit - sess->transit;
427
/* Convert IPDV unit from samples to usec */
428
if (ipdv > -2147 && ipdv < 2147)
429
ipdv = ipdv * 1000000 / (int)sess->clock_rate;
431
ipdv = ipdv * 1000 / (int)sess->clock_rate;
435
/* Update jitter stat */
436
pj_math_stat_update(&sess->stat.rx_ipdv, ipdv);
440
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
441
pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq,
444
discarded, /* discard */
445
(sess->jitter >> 4), /* jitter */
449
/* Update session transit */
450
sess->transit = transit;
452
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
453
} else if (seq_st.diff > 1) {
456
/* Report RTCP XR about packet losses */
457
for (i=seq_st.diff-1; i>0; --i) {
458
pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq - i,
466
/* Report RTCP XR this packet */
467
pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq,
470
discarded, /* discard */
476
/* Update timestamp of last RX RTP packet */
477
sess->rtp_last_ts = rtp_ts;
480
PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess,
481
unsigned bytes_payload_size)
483
/* Update statistics */
485
sess->stat.tx.bytes += bytes_payload_size;
489
static void parse_rtcp_report( pjmedia_rtcp_session *sess,
493
pjmedia_rtcp_common *common = (pjmedia_rtcp_common*) pkt;
494
const pjmedia_rtcp_rr *rr = NULL;
495
const pjmedia_rtcp_sr *sr = NULL;
496
pj_uint32_t last_loss, jitter_samp, jitter;
499
if (common->pt == RTCP_SR) {
500
sr = (pjmedia_rtcp_sr*) (((char*)pkt) + sizeof(pjmedia_rtcp_common));
501
if (common->count > 0 && size >= (sizeof(pjmedia_rtcp_sr_pkt))) {
502
rr = (pjmedia_rtcp_rr*)(((char*)pkt) + (sizeof(pjmedia_rtcp_common)
503
+ sizeof(pjmedia_rtcp_sr)));
505
} else if (common->pt == RTCP_RR && common->count > 0) {
506
rr = (pjmedia_rtcp_rr*)(((char*)pkt) + sizeof(pjmedia_rtcp_common));
507
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
508
} else if (common->pt == RTCP_XR) {
509
if (sess->xr_enabled)
510
pjmedia_rtcp_xr_rx_rtcp_xr(&sess->xr_session, pkt, size);
518
/* Save LSR from NTP timestamp of RTCP packet */
519
sess->rx_lsr = ((pj_ntohl(sr->ntp_sec) & 0x0000FFFF) << 16) |
520
((pj_ntohl(sr->ntp_frac) >> 16) & 0xFFFF);
522
/* Calculate SR arrival time for DLSR */
523
pj_get_timestamp(&sess->rx_lsr_time);
525
TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p",
527
(pj_uint32_t)(sess->rx_lsr_time.u64*65536/sess->ts_freq.u64)));
531
/* Nothing more to do if there's no RR packet */
536
last_loss = sess->stat.tx.loss;
538
/* Get packet loss */
539
sess->stat.tx.loss = (rr->total_lost_2 << 16) +
540
(rr->total_lost_1 << 8) +
543
TRACE_((sess->name, "Rx RTCP RR: total_lost_2=%x, 1=%x, 0=%x, lost=%d",
544
(int)rr->total_lost_2,
545
(int)rr->total_lost_1,
546
(int)rr->total_lost_0,
547
sess->stat.tx.loss));
549
/* We can't calculate the exact loss period for TX, so just give the
552
if (sess->stat.tx.loss > last_loss) {
555
/* Loss period in msec */
556
period = (sess->stat.tx.loss - last_loss) * sess->pkt_size *
557
1000 / sess->clock_rate;
559
/* Loss period in usec */
562
/* Update loss period stat */
563
pj_math_stat_update(&sess->stat.tx.loss_period, period);
566
/* Get jitter value in usec */
567
jitter_samp = pj_ntohl(rr->jitter);
568
/* Calculate jitter in usec, avoiding overflows */
569
if (jitter_samp <= 4294)
570
jitter = jitter_samp * 1000000 / sess->clock_rate;
572
jitter = jitter_samp * 1000 / sess->clock_rate;
576
/* Update jitter statistics */
577
pj_math_stat_update(&sess->stat.tx.jitter, jitter);
579
/* Can only calculate if LSR and DLSR is present in RR */
580
if (rr->lsr && rr->dlsr) {
581
pj_uint32_t lsr, now, dlsr;
583
pjmedia_rtcp_ntp_rec ntp;
585
/* LSR is the middle 32bit of NTP. It has 1/65536 second
588
lsr = pj_ntohl(rr->lsr);
590
/* DLSR is delay since LSR, also in 1/65536 resolution */
591
dlsr = pj_ntohl(rr->dlsr);
593
/* Get current time, and convert to 1/65536 resolution */
594
pjmedia_rtcp_get_ntp_time(sess, &ntp);
595
now = ((ntp.hi & 0xFFFF) << 16) + (ntp.lo >> 16);
597
/* End-to-end delay is (now-lsr-dlsr) */
598
eedelay = now - lsr - dlsr;
600
/* Convert end to end delay to usec (keeping the calculation in
602
* sess->ee_delay = (eedelay * 1000) / 65536;
604
if (eedelay < 4294) {
605
eedelay = (eedelay * 1000000) >> 16;
607
eedelay = (eedelay * 1000) >> 16;
611
TRACE_((sess->name, "Rx RTCP RR: lsr=%p, dlsr=%p (%d:%03dms), "
613
lsr, dlsr, dlsr/65536, (dlsr%65536)*1000/65536,
614
now, (pj_uint32_t)eedelay));
616
/* Only save calculation if "now" is greater than lsr, or
617
* otherwise rtt will be invalid
619
if (now-dlsr >= lsr) {
620
unsigned rtt = (pj_uint32_t)eedelay;
622
/* Check that eedelay value really makes sense.
623
* We allow up to 30 seconds RTT!
625
if (eedelay > 30 * 1000 * 1000UL) {
627
TRACE_((sess->name, "RTT not making any sense, ignored.."));
631
#if defined(PJMEDIA_RTCP_NORMALIZE_FACTOR) && PJMEDIA_RTCP_NORMALIZE_FACTOR!=0
632
/* "Normalize" rtt value that is exceptionally high. For such
633
* values, "normalize" the rtt to be PJMEDIA_RTCP_NORMALIZE_FACTOR
634
* times the average value.
636
if (rtt > ((unsigned)sess->stat.rtt.mean *
637
PJMEDIA_RTCP_NORMALIZE_FACTOR) && sess->stat.rtt.n!=0)
639
unsigned orig_rtt = rtt;
640
rtt = sess->stat.rtt.mean * PJMEDIA_RTCP_NORMALIZE_FACTOR;
641
PJ_LOG(5,(sess->name,
642
"RTT value %d usec is normalized to %d usec",
646
TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt));
648
/* Update RTT stat */
649
pj_math_stat_update(&sess->stat.rtt, rtt);
652
PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: "
653
"lsr=%p, now=%p, dlsr=%p (%d:%03dms), "
655
lsr, now, dlsr, dlsr/65536,
656
(dlsr%65536)*1000/65536,
663
pj_gettimeofday(&sess->stat.tx.update);
664
sess->stat.tx.update_cnt++;
668
static void parse_rtcp_sdes(pjmedia_rtcp_session *sess,
672
pjmedia_rtcp_sdes *sdes = &sess->stat.peer_sdes;
677
p_end = (char*)pkt + size;
679
pj_bzero(sdes, sizeof(*sdes));
680
b = sess->stat.peer_sdes_buf_;
681
b_end = b + sizeof(sess->stat.peer_sdes_buf_);
684
pj_uint8_t sdes_type, sdes_len;
685
pj_str_t sdes_value = {NULL, 0};
689
/* Check for end of SDES item list */
690
if (sdes_type == RTCP_SDES_NULL || p == p_end)
695
/* Check for corrupted SDES packet */
696
if (p + sdes_len > p_end)
700
if (b + sdes_len < b_end) {
701
pj_memcpy(b, p, sdes_len);
703
sdes_value.slen = sdes_len;
706
/* Insufficient SDES buffer */
707
PJ_LOG(5, (sess->name,
708
"Unsufficient buffer to save RTCP SDES type %d:%.*s",
709
sdes_type, sdes_len, p));
715
case RTCP_SDES_CNAME:
716
sdes->cname = sdes_value;
719
sdes->name = sdes_value;
721
case RTCP_SDES_EMAIL:
722
sdes->email = sdes_value;
724
case RTCP_SDES_PHONE:
725
sdes->phone = sdes_value;
728
sdes->loc = sdes_value;
731
sdes->tool = sdes_value;
734
sdes->note = sdes_value;
737
TRACE_((sess->name, "Received unknown RTCP SDES type %d:%.*s",
738
sdes_type, sdes_value.slen, sdes_value.ptr));
747
static void parse_rtcp_bye(pjmedia_rtcp_session *sess,
751
pj_str_t reason = {"-", 1};
753
/* Check and get BYE reason */
755
reason.slen = *((pj_uint8_t*)pkt+8);
756
pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9),
758
reason.ptr = sess->stat.peer_sdes_buf_;
761
/* Just print RTCP BYE log */
762
PJ_LOG(5, (sess->name, "Received RTCP BYE, reason: %.*s",
763
reason.slen, reason.ptr));
767
PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess,
771
pj_uint8_t *p, *p_end;
773
p = (pj_uint8_t*)pkt;
776
pjmedia_rtcp_common *common = (pjmedia_rtcp_common*)p;
779
len = (pj_ntohs((pj_uint16_t)common->length)+1) * 4;
784
parse_rtcp_report(sess, p, len);
787
parse_rtcp_sdes(sess, p, len);
790
parse_rtcp_bye(sess, p, len);
793
/* Ignore unknown RTCP */
794
TRACE_((sess->name, "Received unknown RTCP packet type=%d",
804
PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess,
805
void **ret_p_pkt, int *len)
807
pj_uint32_t expected, expected_interval, received_interval, lost_interval;
808
pjmedia_rtcp_common *common;
812
pjmedia_rtcp_ntp_rec ntp;
814
/* Get current NTP time. */
815
pj_get_timestamp(&ts_now);
816
pjmedia_rtcp_get_ntp_time(sess, &ntp);
819
/* See if we have transmitted RTP packets since last time we
822
if (sess->stat.tx.pkt != pj_ntohl(sess->rtcp_sr_pkt.sr.sender_pcount)) {
826
/* So we should send RTCP SR */
827
*ret_p_pkt = (void*) &sess->rtcp_sr_pkt;
828
*len = sizeof(pjmedia_rtcp_sr_pkt);
829
common = &sess->rtcp_sr_pkt.common;
830
rr = &sess->rtcp_sr_pkt.rr;
831
sr = &sess->rtcp_sr_pkt.sr;
833
/* Update packet count */
834
sr->sender_pcount = pj_htonl(sess->stat.tx.pkt);
836
/* Update octets count */
837
sr->sender_bcount = pj_htonl(sess->stat.tx.bytes);
839
/* Fill in NTP timestamp in SR. */
840
sr->ntp_sec = pj_htonl(ntp.hi);
841
sr->ntp_frac = pj_htonl(ntp.lo);
843
/* Fill in RTP timestamp (corresponds to NTP timestamp) in SR. */
844
ts_time.sec = ntp.hi - sess->tv_base.sec - JAN_1970;
845
ts_time.msec = (long)(ntp.lo * 1000.0 / 0xFFFFFFFF);
846
rtp_ts = sess->rtp_ts_base +
847
(pj_uint32_t)(sess->clock_rate*ts_time.sec) +
848
(pj_uint32_t)(sess->clock_rate*ts_time.msec/1000);
849
sr->rtp_ts = pj_htonl(rtp_ts);
851
TRACE_((sess->name, "TX RTCP SR: ntp_ts=%p",
852
((ntp.hi & 0xFFFF) << 16) + ((ntp.lo & 0xFFFF0000)
857
/* We should send RTCP RR then */
858
*ret_p_pkt = (void*) &sess->rtcp_rr_pkt;
859
*len = sizeof(pjmedia_rtcp_rr_pkt);
860
common = &sess->rtcp_rr_pkt.common;
861
rr = &sess->rtcp_rr_pkt.rr;
865
/* SSRC and last_seq */
866
rr->ssrc = pj_htonl(sess->peer_ssrc);
867
rr->last_seq = (sess->seq_ctrl.cycles & 0xFFFF0000L);
868
/* Since this is an "+=" operation, make sure we update last_seq on
871
sess->rtcp_sr_pkt.rr.last_seq += sess->seq_ctrl.max_seq;
872
sess->rtcp_rr_pkt.rr.last_seq += sess->seq_ctrl.max_seq;
873
rr->last_seq = pj_htonl(rr->last_seq);
877
rr->jitter = pj_htonl(sess->jitter >> 4);
881
expected = pj_ntohl(rr->last_seq) - sess->seq_ctrl.base_seq;
883
/* This is bug: total lost already calculated on each incoming RTP!
884
if (expected >= sess->received)
885
sess->stat.rx.loss = expected - sess->received;
887
sess->stat.rx.loss = 0;
890
rr->total_lost_2 = (sess->stat.rx.loss >> 16) & 0xFF;
891
rr->total_lost_1 = (sess->stat.rx.loss >> 8) & 0xFF;
892
rr->total_lost_0 = (sess->stat.rx.loss & 0xFF);
894
/* Fraction lost calculation */
895
expected_interval = expected - sess->exp_prior;
896
sess->exp_prior = expected;
898
received_interval = sess->received - sess->rx_prior;
899
sess->rx_prior = sess->received;
901
if (expected_interval >= received_interval)
902
lost_interval = expected_interval - received_interval;
906
if (expected_interval==0 || lost_interval == 0) {
909
rr->fract_lost = (lost_interval << 8) / expected_interval;
912
if (sess->rx_lsr_time.u64 == 0 || sess->rx_lsr == 0) {
917
pj_uint32_t lsr = sess->rx_lsr;
918
pj_uint64_t lsr_time = sess->rx_lsr_time.u64;
921
/* Convert LSR time to 1/65536 seconds resolution */
922
lsr_time = (lsr_time << 16) / sess->ts_freq.u64;
925
LSR is the middle 32bit of the last SR NTP time received.
927
rr->lsr = pj_htonl(lsr);
930
DLSR is Delay since Last SR, in 1/65536 seconds.
934
/* Convert interval to 1/65536 seconds value */
935
ts.u64 = (ts.u64 << 16) / sess->ts_freq.u64;
938
dlsr = (pj_uint32_t)(ts.u64 - lsr_time);
939
rr->dlsr = pj_htonl(dlsr);
941
TRACE_((sess->name,"Tx RTCP RR: lsr=%p, lsr_time=%p, now=%p, dlsr=%p"
944
(pj_uint32_t)lsr_time,
948
(dlsr%65536)*1000/65536 ));
952
pj_gettimeofday(&sess->stat.rx.update);
953
sess->stat.rx.update_cnt++;
957
PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_sdes(
958
pjmedia_rtcp_session *session,
961
const pjmedia_rtcp_sdes *sdes)
963
pjmedia_rtcp_common *hdr;
967
PJ_ASSERT_RETURN(session && buf && length && sdes, PJ_EINVAL);
969
/* Verify SDES item length */
970
if (sdes->cname.slen > 255 || sdes->name.slen > 255 ||
971
sdes->email.slen > 255 || sdes->phone.slen > 255 ||
972
sdes->loc.slen > 255 || sdes->tool.slen > 255 ||
973
sdes->note.slen > 255)
978
/* Verify buffer length */
980
if (sdes->cname.slen) len += sdes->cname.slen + 2;
981
if (sdes->name.slen) len += sdes->name.slen + 2;
982
if (sdes->email.slen) len += sdes->email.slen + 2;
983
if (sdes->phone.slen) len += sdes->phone.slen + 2;
984
if (sdes->loc.slen) len += sdes->loc.slen + 2;
985
if (sdes->tool.slen) len += sdes->tool.slen + 2;
986
if (sdes->note.slen) len += sdes->note.slen + 2;
987
len++; /* null termination */
988
len = ((len+3)/4) * 4;
992
/* Build RTCP SDES header */
993
hdr = (pjmedia_rtcp_common*)buf;
994
pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr));
996
hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
998
/* Build RTCP SDES items */
999
p = (pj_uint8_t*)hdr + sizeof(*hdr);
1000
#define BUILD_SDES_ITEM(SDES_NAME, SDES_TYPE) \
1001
if (sdes->SDES_NAME.slen) { \
1003
*p++ = (pj_uint8_t)sdes->SDES_NAME.slen; \
1004
pj_memcpy(p, sdes->SDES_NAME.ptr, sdes->SDES_NAME.slen); \
1005
p += sdes->SDES_NAME.slen; \
1007
BUILD_SDES_ITEM(cname, RTCP_SDES_CNAME);
1008
BUILD_SDES_ITEM(name, RTCP_SDES_NAME);
1009
BUILD_SDES_ITEM(email, RTCP_SDES_EMAIL);
1010
BUILD_SDES_ITEM(phone, RTCP_SDES_PHONE);
1011
BUILD_SDES_ITEM(loc, RTCP_SDES_LOC);
1012
BUILD_SDES_ITEM(tool, RTCP_SDES_TOOL);
1013
BUILD_SDES_ITEM(note, RTCP_SDES_NOTE);
1014
#undef BUILD_SDES_ITEM
1016
/* Null termination */
1020
while ((p-(pj_uint8_t*)buf) % 4)
1024
pj_assert((int)len == p-(pj_uint8_t*)buf);
1031
PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_bye(pjmedia_rtcp_session *session,
1034
const pj_str_t *reason)
1036
pjmedia_rtcp_common *hdr;
1040
PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL);
1042
/* Verify BYE reason length */
1043
if (reason && reason->slen > 255)
1046
/* Verify buffer length */
1048
if (reason && reason->slen) len += reason->slen + 1;
1049
len = ((len+3)/4) * 4;
1051
return PJ_ETOOSMALL;
1053
/* Build RTCP BYE header */
1054
hdr = (pjmedia_rtcp_common*)buf;
1055
pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr));
1057
hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
1059
/* Write RTCP BYE reason */
1060
p = (pj_uint8_t*)hdr + sizeof(*hdr);
1061
if (reason && reason->slen) {
1062
*p++ = (pj_uint8_t)reason->slen;
1063
pj_memcpy(p, reason->ptr, reason->slen);
1068
while ((p-(pj_uint8_t*)buf) % 4)
1071
pj_assert((int)len == p-(pj_uint8_t*)buf);
1078
PJ_DEF(pj_status_t) pjmedia_rtcp_enable_xr( pjmedia_rtcp_session *sess,
1081
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
1083
/* Check if request won't change anything */
1084
if (!(enable ^ sess->xr_enabled))
1088
sess->xr_enabled = PJ_FALSE;
1092
pjmedia_rtcp_xr_init(&sess->xr_session, sess, 0, 1);
1093
sess->xr_enabled = PJ_TRUE;
1099
PJ_UNUSED_ARG(sess);
1100
PJ_UNUSED_ARG(enable);