blob: ddd707810a7f3bdcbc08423b4268ca2a12ec5cea [file] [log] [blame]
Alexandre Lision8af73cb2013-12-10 14:11:20 -05001/* $Id$ */
2/*
3 * Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4 * Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
5 *
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.
10 *
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.
15 *
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
19 */
20#include <pjmedia/rtcp.h>
21#include <pjmedia/errno.h>
22#include <pj/assert.h>
23#include <pj/log.h>
24#include <pj/os.h>
25#include <pj/sock.h>
26#include <pj/string.h>
27
28#define THIS_FILE "rtcp.c"
29
30#define RTCP_SR 200
31#define RTCP_RR 201
32#define RTCP_SDES 202
33#define RTCP_BYE 203
34#define RTCP_XR 207
35
36enum {
37 RTCP_SDES_NULL = 0,
38 RTCP_SDES_CNAME = 1,
39 RTCP_SDES_NAME = 2,
40 RTCP_SDES_EMAIL = 3,
41 RTCP_SDES_PHONE = 4,
42 RTCP_SDES_LOC = 5,
43 RTCP_SDES_TOOL = 6,
44 RTCP_SDES_NOTE = 7
45};
46
47#if PJ_HAS_HIGH_RES_TIMER==0
48# error "High resolution timer needs to be enabled"
49#endif
50
51
52
53#if 0
54# define TRACE_(x) PJ_LOG(3,x)
55#else
56# define TRACE_(x) ;
57#endif
58
59
60/*
61 * Get NTP time.
62 */
63PJ_DEF(pj_status_t) pjmedia_rtcp_get_ntp_time(const pjmedia_rtcp_session *sess,
64 pjmedia_rtcp_ntp_rec *ntp)
65{
66/* Seconds between 1900-01-01 to 1970-01-01 */
67#define JAN_1970 (2208988800UL)
68 pj_timestamp ts;
69 pj_status_t status;
70
71 status = pj_get_timestamp(&ts);
72
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;
76
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;
81
82 /* Fill up the low 32bit part */
83 ntp->lo = ts.u32.lo;
84
85
86#if (defined(PJ_WIN32) && PJ_WIN32!=0) || \
87 (defined(PJ_WIN64) && PJ_WIN64!=0) || \
88 (defined(PJ_WIN32_WINCE) && PJ_WIN32_WINCE!=0)
89
90 /* On Win32, since we use QueryPerformanceCounter() as the backend
91 * timestamp API, we need to protect against this bug:
92 * Performance counter value may unexpectedly leap forward
93 * http://support.microsoft.com/default.aspx?scid=KB;EN-US;Q274323
94 */
95 {
96 /*
97 * Compare elapsed time reported by timestamp with actual elapsed
98 * time. If the difference is too excessive, then we use system
99 * time instead.
100 */
101
102 /* MIN_DIFF needs to be large enough so that "normal" diff caused
103 * by system activity or context switch doesn't trigger the time
104 * correction.
105 */
106 enum { MIN_DIFF = 400 };
107
108 pj_time_val ts_time, elapsed, diff;
109
110 pj_gettimeofday(&elapsed);
111
112 ts_time.sec = ntp->hi - sess->tv_base.sec - JAN_1970;
113 ts_time.msec = (long)(ntp->lo * 1000.0 / 0xFFFFFFFF);
114
115 PJ_TIME_VAL_SUB(elapsed, sess->tv_base);
116
117 if (PJ_TIME_VAL_LT(ts_time, elapsed)) {
118 diff = elapsed;
119 PJ_TIME_VAL_SUB(diff, ts_time);
120 } else {
121 diff = ts_time;
122 PJ_TIME_VAL_SUB(diff, elapsed);
123 }
124
125 if (PJ_TIME_VAL_MSEC(diff) >= MIN_DIFF) {
126
127 TRACE_((sess->name, "RTCP NTP timestamp corrected by %d ms",
128 PJ_TIME_VAL_MSEC(diff)));
129
130
131 ntp->hi = elapsed.sec + sess->tv_base.sec + JAN_1970;
132 ntp->lo = (elapsed.msec * 65536 / 1000) << 16;
133 }
134
135 }
136#endif
137
138 return status;
139}
140
141
142/*
143 * Initialize RTCP session setting.
144 */
145PJ_DEF(void) pjmedia_rtcp_session_setting_default(
146 pjmedia_rtcp_session_setting *settings)
147{
148 pj_bzero(settings, sizeof(*settings));
149}
150
151
152/*
153 * Initialize bidirectional RTCP statistics.
154 *
155 */
156PJ_DEF(void) pjmedia_rtcp_init_stat(pjmedia_rtcp_stat *stat)
157{
158 pj_time_val now;
159
160 pj_assert(stat);
161
162 pj_bzero(stat, sizeof(pjmedia_rtcp_stat));
163
164 pj_math_stat_init(&stat->rtt);
165 pj_math_stat_init(&stat->rx.loss_period);
166 pj_math_stat_init(&stat->rx.jitter);
167 pj_math_stat_init(&stat->tx.loss_period);
168 pj_math_stat_init(&stat->tx.jitter);
169
170#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
171 pj_math_stat_init(&stat->rx_ipdv);
172#endif
173
174#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
175 pj_math_stat_init(&stat->rx_raw_jitter);
176#endif
177
178 pj_gettimeofday(&now);
179 stat->start = now;
180}
181
182
183/*
184 * Initialize RTCP session.
185 */
186PJ_DEF(void) pjmedia_rtcp_init(pjmedia_rtcp_session *sess,
187 char *name,
188 unsigned clock_rate,
189 unsigned samples_per_frame,
190 pj_uint32_t ssrc)
191{
192 pjmedia_rtcp_session_setting settings;
193
194 pjmedia_rtcp_session_setting_default(&settings);
195 settings.name = name;
196 settings.clock_rate = clock_rate;
197 settings.samples_per_frame = samples_per_frame;
198 settings.ssrc = ssrc;
199
200 pjmedia_rtcp_init2(sess, &settings);
201}
202
203
204/*
205 * Initialize RTCP session.
206 */
207PJ_DEF(void) pjmedia_rtcp_init2( pjmedia_rtcp_session *sess,
208 const pjmedia_rtcp_session_setting *settings)
209{
210 pjmedia_rtcp_sr_pkt *sr_pkt = &sess->rtcp_sr_pkt;
211 pj_time_val now;
212
213 /* Memset everything */
214 pj_bzero(sess, sizeof(pjmedia_rtcp_session));
215
216 /* Last RX timestamp in RTP packet */
217 sess->rtp_last_ts = (unsigned)-1;
218
219 /* Name */
220 sess->name = settings->name ? settings->name : (char*)THIS_FILE;
221
222 /* Set clock rate */
223 sess->clock_rate = settings->clock_rate;
224 sess->pkt_size = settings->samples_per_frame;
225
226 /* Init common RTCP SR header */
227 sr_pkt->common.version = 2;
228 sr_pkt->common.count = 1;
229 sr_pkt->common.pt = RTCP_SR;
230 sr_pkt->common.length = pj_htons(12);
231 sr_pkt->common.ssrc = pj_htonl(settings->ssrc);
232
233 /* Copy to RTCP RR header */
234 pj_memcpy(&sess->rtcp_rr_pkt.common, &sr_pkt->common,
235 sizeof(pjmedia_rtcp_common));
236 sess->rtcp_rr_pkt.common.pt = RTCP_RR;
237 sess->rtcp_rr_pkt.common.length = pj_htons(7);
238
239 /* Get time and timestamp base and frequency */
240 pj_gettimeofday(&now);
241 sess->tv_base = now;
242 pj_get_timestamp(&sess->ts_base);
243 pj_get_timestamp_freq(&sess->ts_freq);
244 sess->rtp_ts_base = settings->rtp_ts_base;
245
246 /* Initialize statistics states */
247 pjmedia_rtcp_init_stat(&sess->stat);
248
249 /* RR will be initialized on receipt of the first RTP packet. */
250}
251
252
253PJ_DEF(void) pjmedia_rtcp_fini(pjmedia_rtcp_session *sess)
254{
255#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
256 pjmedia_rtcp_xr_fini(&sess->xr_session);
257#else
258 /* Nothing to do. */
259 PJ_UNUSED_ARG(sess);
260#endif
261}
262
263static void rtcp_init_seq(pjmedia_rtcp_session *sess)
264{
265 sess->received = 0;
266 sess->exp_prior = 0;
267 sess->rx_prior = 0;
268 sess->transit = 0;
269 sess->jitter = 0;
270}
271
272PJ_DEF(void) pjmedia_rtcp_rx_rtp( pjmedia_rtcp_session *sess,
273 unsigned seq,
274 unsigned rtp_ts,
275 unsigned payload)
276{
277 pjmedia_rtcp_rx_rtp2(sess, seq, rtp_ts, payload, PJ_FALSE);
278}
279
280PJ_DEF(void) pjmedia_rtcp_rx_rtp2(pjmedia_rtcp_session *sess,
281 unsigned seq,
282 unsigned rtp_ts,
283 unsigned payload,
284 pj_bool_t discarded)
285{
286 pj_timestamp ts;
287 pj_uint32_t arrival;
288 pj_int32_t transit;
289 pjmedia_rtp_status seq_st;
290 unsigned last_seq;
291
292#if !defined(PJMEDIA_HAS_RTCP_XR) || (PJMEDIA_HAS_RTCP_XR == 0)
293 PJ_UNUSED_ARG(discarded);
294#endif
295
296 if (sess->stat.rx.pkt == 0) {
297 /* Init sequence for the first time. */
298 pjmedia_rtp_seq_init(&sess->seq_ctrl, (pj_uint16_t)seq);
299 }
300
301 sess->stat.rx.pkt++;
302 sess->stat.rx.bytes += payload;
303
304 /* Process the RTP packet. */
305 last_seq = sess->seq_ctrl.max_seq;
306 pjmedia_rtp_seq_update(&sess->seq_ctrl, (pj_uint16_t)seq, &seq_st);
307
308 if (seq_st.status.flag.restart) {
309 rtcp_init_seq(sess);
310 }
311
312 if (seq_st.status.flag.dup) {
313 sess->stat.rx.dup++;
314 TRACE_((sess->name, "Duplicate packet detected"));
315 }
316
317 if (seq_st.status.flag.outorder && !seq_st.status.flag.probation) {
318 sess->stat.rx.reorder++;
319 TRACE_((sess->name, "Out-of-order packet detected"));
320 }
321
322 if (seq_st.status.flag.bad) {
323 sess->stat.rx.discard++;
324
325#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
326 pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq,
327 -1, /* lost */
328 (seq_st.status.flag.dup? 1:0), /* dup */
329 (!seq_st.status.flag.dup? 1:-1), /* discard */
330 -1, /* jitter */
331 -1, 0); /* toh */
332#endif
333
334 TRACE_((sess->name, "Bad packet discarded"));
335 return;
336 }
337
338 /* Only mark "good" packets */
339 ++sess->received;
340
341 /* Calculate loss periods. */
342 if (seq_st.diff > 1) {
343 unsigned count = seq_st.diff - 1;
344 unsigned period;
345
346 period = count * sess->pkt_size * 1000 / sess->clock_rate;
347 period *= 1000;
348
349 /* Update packet lost.
350 * The packet lost number will also be updated when we're sending
351 * outbound RTCP RR.
352 */
353 sess->stat.rx.loss += (seq_st.diff - 1);
354 TRACE_((sess->name, "%d packet(s) lost", seq_st.diff - 1));
355
356 /* Update loss period stat */
357 pj_math_stat_update(&sess->stat.rx.loss_period, period);
358 }
359
360
361 /*
362 * Calculate jitter only when sequence is good (see RFC 3550 section A.8),
363 * AND only when the timestamp is different than the last packet
364 * (see RTP FAQ).
365 */
366 if (seq_st.diff == 1 && rtp_ts != sess->rtp_last_ts) {
367 /* Get arrival time and convert timestamp to samples */
368 pj_get_timestamp(&ts);
369 ts.u64 = ts.u64 * sess->clock_rate / sess->ts_freq.u64;
370 arrival = ts.u32.lo;
371
372 transit = arrival - rtp_ts;
373
374 /* Ignore the first N packets as they normally have bad jitter
375 * due to other threads working to establish the call
376 */
377 if (sess->transit == 0 ||
378 sess->received < PJMEDIA_RTCP_IGNORE_FIRST_PACKETS)
379 {
380 sess->transit = transit;
381 sess->stat.rx.jitter.min = (unsigned)-1;
382 } else {
383 pj_int32_t d;
384 pj_uint32_t jitter;
385
386 d = transit - sess->transit;
387 if (d < 0)
388 d = -d;
389
390 sess->jitter += d - ((sess->jitter + 8) >> 4);
391
392 /* Update jitter stat */
393 jitter = sess->jitter >> 4;
394
395 /* Convert jitter unit from samples to usec */
396 if (jitter < 4294)
397 jitter = jitter * 1000000 / sess->clock_rate;
398 else {
399 jitter = jitter * 1000 / sess->clock_rate;
400 jitter *= 1000;
401 }
402 pj_math_stat_update(&sess->stat.rx.jitter, jitter);
403
404
405#if defined(PJMEDIA_RTCP_STAT_HAS_RAW_JITTER) && PJMEDIA_RTCP_STAT_HAS_RAW_JITTER!=0
406 {
407 pj_uint32_t raw_jitter;
408
409 /* Convert raw jitter unit from samples to usec */
410 if (d < 4294)
411 raw_jitter = d * 1000000 / sess->clock_rate;
412 else {
413 raw_jitter = d * 1000 / sess->clock_rate;
414 raw_jitter *= 1000;
415 }
416
417 /* Update jitter stat */
418 pj_math_stat_update(&sess->stat.rx_raw_jitter, raw_jitter);
419 }
420#endif
421
422
423#if defined(PJMEDIA_RTCP_STAT_HAS_IPDV) && PJMEDIA_RTCP_STAT_HAS_IPDV!=0
424 {
425 pj_int32_t ipdv;
426
427 ipdv = transit - sess->transit;
428 /* Convert IPDV unit from samples to usec */
429 if (ipdv > -2147 && ipdv < 2147)
430 ipdv = ipdv * 1000000 / (int)sess->clock_rate;
431 else {
432 ipdv = ipdv * 1000 / (int)sess->clock_rate;
433 ipdv *= 1000;
434 }
435
436 /* Update jitter stat */
437 pj_math_stat_update(&sess->stat.rx_ipdv, ipdv);
438 }
439#endif
440
441#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
442 pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq,
443 0, /* lost */
444 0, /* dup */
445 discarded, /* discard */
446 (sess->jitter >> 4), /* jitter */
447 -1, 0); /* toh */
448#endif
449
450 /* Update session transit */
451 sess->transit = transit;
452 }
453#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
454 } else if (seq_st.diff > 1) {
455 int i;
456
457 /* Report RTCP XR about packet losses */
458 for (i=seq_st.diff-1; i>0; --i) {
459 pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq - i,
460 1, /* lost */
461 0, /* dup */
462 0, /* discard */
463 -1, /* jitter */
464 -1, 0); /* toh */
465 }
466
467 /* Report RTCP XR this packet */
468 pjmedia_rtcp_xr_rx_rtp(&sess->xr_session, seq,
469 0, /* lost */
470 0, /* dup */
471 discarded, /* discard */
472 -1, /* jitter */
473 -1, 0); /* toh */
474#endif
475 }
476
477 /* Update timestamp of last RX RTP packet */
478 sess->rtp_last_ts = rtp_ts;
479}
480
481PJ_DEF(void) pjmedia_rtcp_tx_rtp(pjmedia_rtcp_session *sess,
482 unsigned bytes_payload_size)
483{
484 /* Update statistics */
485 sess->stat.tx.pkt++;
486 sess->stat.tx.bytes += bytes_payload_size;
487}
488
489
490static void parse_rtcp_report( pjmedia_rtcp_session *sess,
491 const void *pkt,
492 pj_size_t size)
493{
494 pjmedia_rtcp_common *common = (pjmedia_rtcp_common*) pkt;
495 const pjmedia_rtcp_rr *rr = NULL;
496 const pjmedia_rtcp_sr *sr = NULL;
497 pj_uint32_t last_loss, jitter_samp, jitter;
498
499 /* Parse RTCP */
500 if (common->pt == RTCP_SR) {
501 sr = (pjmedia_rtcp_sr*) (((char*)pkt) + sizeof(pjmedia_rtcp_common));
502 if (common->count > 0 && size >= (sizeof(pjmedia_rtcp_sr_pkt))) {
503 rr = (pjmedia_rtcp_rr*)(((char*)pkt) + (sizeof(pjmedia_rtcp_common)
504 + sizeof(pjmedia_rtcp_sr)));
505 }
506 } else if (common->pt == RTCP_RR && common->count > 0) {
507 rr = (pjmedia_rtcp_rr*)(((char*)pkt) + sizeof(pjmedia_rtcp_common));
508#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
509 } else if (common->pt == RTCP_XR) {
510 if (sess->xr_enabled)
511 pjmedia_rtcp_xr_rx_rtcp_xr(&sess->xr_session, pkt, size);
512
513 return;
514#endif
515 }
516
517
518 if (sr) {
519 /* Save LSR from NTP timestamp of RTCP packet */
520 sess->rx_lsr = ((pj_ntohl(sr->ntp_sec) & 0x0000FFFF) << 16) |
521 ((pj_ntohl(sr->ntp_frac) >> 16) & 0xFFFF);
522
523 /* Calculate SR arrival time for DLSR */
524 pj_get_timestamp(&sess->rx_lsr_time);
525
526 TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p",
527 sess->rx_lsr,
528 (pj_uint32_t)(sess->rx_lsr_time.u64*65536/sess->ts_freq.u64)));
529 }
530
531
532 /* Nothing more to do if there's no RR packet */
533 if (rr == NULL)
534 return;
535
536
537 last_loss = sess->stat.tx.loss;
538
539 /* Get packet loss */
540 sess->stat.tx.loss = (rr->total_lost_2 << 16) +
541 (rr->total_lost_1 << 8) +
542 rr->total_lost_0;
543
544 TRACE_((sess->name, "Rx RTCP RR: total_lost_2=%x, 1=%x, 0=%x, lost=%d",
545 (int)rr->total_lost_2,
546 (int)rr->total_lost_1,
547 (int)rr->total_lost_0,
548 sess->stat.tx.loss));
549
550 /* We can't calculate the exact loss period for TX, so just give the
551 * best estimation.
552 */
553 if (sess->stat.tx.loss > last_loss) {
554 unsigned period;
555
556 /* Loss period in msec */
557 period = (sess->stat.tx.loss - last_loss) * sess->pkt_size *
558 1000 / sess->clock_rate;
559
560 /* Loss period in usec */
561 period *= 1000;
562
563 /* Update loss period stat */
564 pj_math_stat_update(&sess->stat.tx.loss_period, period);
565 }
566
567 /* Get jitter value in usec */
568 jitter_samp = pj_ntohl(rr->jitter);
569 /* Calculate jitter in usec, avoiding overflows */
570 if (jitter_samp <= 4294)
571 jitter = jitter_samp * 1000000 / sess->clock_rate;
572 else {
573 jitter = jitter_samp * 1000 / sess->clock_rate;
574 jitter *= 1000;
575 }
576
577 /* Update jitter statistics */
578 pj_math_stat_update(&sess->stat.tx.jitter, jitter);
579
580 /* Can only calculate if LSR and DLSR is present in RR */
581 if (rr->lsr && rr->dlsr) {
582 pj_uint32_t lsr, now, dlsr;
583 pj_uint64_t eedelay;
584 pjmedia_rtcp_ntp_rec ntp;
585
586 /* LSR is the middle 32bit of NTP. It has 1/65536 second
587 * resolution
588 */
589 lsr = pj_ntohl(rr->lsr);
590
591 /* DLSR is delay since LSR, also in 1/65536 resolution */
592 dlsr = pj_ntohl(rr->dlsr);
593
594 /* Get current time, and convert to 1/65536 resolution */
595 pjmedia_rtcp_get_ntp_time(sess, &ntp);
596 now = ((ntp.hi & 0xFFFF) << 16) + (ntp.lo >> 16);
597
598 /* End-to-end delay is (now-lsr-dlsr) */
599 eedelay = now - lsr - dlsr;
600
601 /* Convert end to end delay to usec (keeping the calculation in
602 * 64bit space)::
603 * sess->ee_delay = (eedelay * 1000) / 65536;
604 */
605 if (eedelay < 4294) {
606 eedelay = (eedelay * 1000000) >> 16;
607 } else {
608 eedelay = (eedelay * 1000) >> 16;
609 eedelay *= 1000;
610 }
611
612 TRACE_((sess->name, "Rx RTCP RR: lsr=%p, dlsr=%p (%d:%03dms), "
613 "now=%p, rtt=%p",
614 lsr, dlsr, dlsr/65536, (dlsr%65536)*1000/65536,
615 now, (pj_uint32_t)eedelay));
616
617 /* Only save calculation if "now" is greater than lsr, or
618 * otherwise rtt will be invalid
619 */
620 if (now-dlsr >= lsr) {
621 unsigned rtt = (pj_uint32_t)eedelay;
622
623 /* Check that eedelay value really makes sense.
624 * We allow up to 30 seconds RTT!
625 */
626 if (eedelay > 30 * 1000 * 1000UL) {
627
628 TRACE_((sess->name, "RTT not making any sense, ignored.."));
629 goto end_rtt_calc;
630 }
631
632#if defined(PJMEDIA_RTCP_NORMALIZE_FACTOR) && PJMEDIA_RTCP_NORMALIZE_FACTOR!=0
633 /* "Normalize" rtt value that is exceptionally high. For such
634 * values, "normalize" the rtt to be PJMEDIA_RTCP_NORMALIZE_FACTOR
635 * times the average value.
636 */
637 if (rtt > ((unsigned)sess->stat.rtt.mean *
638 PJMEDIA_RTCP_NORMALIZE_FACTOR) && sess->stat.rtt.n!=0)
639 {
640 unsigned orig_rtt = rtt;
641 rtt = sess->stat.rtt.mean * PJMEDIA_RTCP_NORMALIZE_FACTOR;
642 PJ_LOG(5,(sess->name,
643 "RTT value %d usec is normalized to %d usec",
644 orig_rtt, rtt));
645 }
646#endif
647 TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt));
648
649 /* Update RTT stat */
650 pj_math_stat_update(&sess->stat.rtt, rtt);
651
652 } else {
653 PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: "
654 "lsr=%p, now=%p, dlsr=%p (%d:%03dms), "
655 "diff=%d",
656 lsr, now, dlsr, dlsr/65536,
657 (dlsr%65536)*1000/65536,
658 dlsr-(now-lsr)));
659 }
660 }
661
662end_rtt_calc:
663
664 pj_gettimeofday(&sess->stat.tx.update);
665 sess->stat.tx.update_cnt++;
666}
667
668
669static void parse_rtcp_sdes(pjmedia_rtcp_session *sess,
670 const void *pkt,
671 pj_size_t size)
672{
673 pjmedia_rtcp_sdes *sdes = &sess->stat.peer_sdes;
674 char *p, *p_end;
675 char *b, *b_end;
676
677 p = (char*)pkt + 8;
678 p_end = (char*)pkt + size;
679
680 pj_bzero(sdes, sizeof(*sdes));
681 b = sess->stat.peer_sdes_buf_;
682 b_end = b + sizeof(sess->stat.peer_sdes_buf_);
683
684 while (p < p_end) {
685 pj_uint8_t sdes_type, sdes_len;
686 pj_str_t sdes_value = {NULL, 0};
687
688 sdes_type = *p++;
689
690 /* Check for end of SDES item list */
691 if (sdes_type == RTCP_SDES_NULL || p == p_end)
692 break;
693
694 sdes_len = *p++;
695
696 /* Check for corrupted SDES packet */
697 if (p + sdes_len > p_end)
698 break;
699
700 /* Get SDES item */
701 if (b + sdes_len < b_end) {
702 pj_memcpy(b, p, sdes_len);
703 sdes_value.ptr = b;
704 sdes_value.slen = sdes_len;
705 b += sdes_len;
706 } else {
707 /* Insufficient SDES buffer */
708 PJ_LOG(5, (sess->name,
709 "Unsufficient buffer to save RTCP SDES type %d:%.*s",
710 sdes_type, sdes_len, p));
711 p += sdes_len;
712 continue;
713 }
714
715 switch (sdes_type) {
716 case RTCP_SDES_CNAME:
717 sdes->cname = sdes_value;
718 break;
719 case RTCP_SDES_NAME:
720 sdes->name = sdes_value;
721 break;
722 case RTCP_SDES_EMAIL:
723 sdes->email = sdes_value;
724 break;
725 case RTCP_SDES_PHONE:
726 sdes->phone = sdes_value;
727 break;
728 case RTCP_SDES_LOC:
729 sdes->loc = sdes_value;
730 break;
731 case RTCP_SDES_TOOL:
732 sdes->tool = sdes_value;
733 break;
734 case RTCP_SDES_NOTE:
735 sdes->note = sdes_value;
736 break;
737 default:
738 TRACE_((sess->name, "Received unknown RTCP SDES type %d:%.*s",
739 sdes_type, sdes_value.slen, sdes_value.ptr));
740 break;
741 }
742
743 p += sdes_len;
744 }
745}
746
747
748static void parse_rtcp_bye(pjmedia_rtcp_session *sess,
749 const void *pkt,
750 pj_size_t size)
751{
752 pj_str_t reason = {"-", 1};
753
754 /* Check and get BYE reason */
755 if (size > 8) {
756 reason.slen = PJ_MIN(sizeof(sess->stat.peer_sdes_buf_),
757 *((pj_uint8_t*)pkt+8));
758 pj_memcpy(sess->stat.peer_sdes_buf_, ((pj_uint8_t*)pkt+9),
759 reason.slen);
760 reason.ptr = sess->stat.peer_sdes_buf_;
761 }
762
763 /* Just print RTCP BYE log */
764 PJ_LOG(5, (sess->name, "Received RTCP BYE, reason: %.*s",
765 reason.slen, reason.ptr));
766}
767
768
769PJ_DEF(void) pjmedia_rtcp_rx_rtcp( pjmedia_rtcp_session *sess,
770 const void *pkt,
771 pj_size_t size)
772{
773 pj_uint8_t *p, *p_end;
774
775 p = (pj_uint8_t*)pkt;
776 p_end = p + size;
777 while (p < p_end) {
778 pjmedia_rtcp_common *common = (pjmedia_rtcp_common*)p;
779 unsigned len;
780
781 len = (pj_ntohs((pj_uint16_t)common->length)+1) * 4;
782 switch(common->pt) {
783 case RTCP_SR:
784 case RTCP_RR:
785 case RTCP_XR:
786 parse_rtcp_report(sess, p, len);
787 break;
788 case RTCP_SDES:
789 parse_rtcp_sdes(sess, p, len);
790 break;
791 case RTCP_BYE:
792 parse_rtcp_bye(sess, p, len);
793 break;
794 default:
795 /* Ignore unknown RTCP */
796 TRACE_((sess->name, "Received unknown RTCP packet type=%d",
797 common->pt));
798 break;
799 }
800
801 p += len;
802 }
803}
804
805
806PJ_DEF(void) pjmedia_rtcp_build_rtcp(pjmedia_rtcp_session *sess,
807 void **ret_p_pkt, int *len)
808{
809 pj_uint32_t expected, expected_interval, received_interval, lost_interval;
810 pjmedia_rtcp_common *common;
811 pjmedia_rtcp_sr *sr;
812 pjmedia_rtcp_rr *rr;
813 pj_timestamp ts_now;
814 pjmedia_rtcp_ntp_rec ntp;
815
816 /* Get current NTP time. */
817 pj_get_timestamp(&ts_now);
818 pjmedia_rtcp_get_ntp_time(sess, &ntp);
819
820
821 /* See if we have transmitted RTP packets since last time we
822 * sent RTCP SR.
823 */
824 if (sess->stat.tx.pkt != pj_ntohl(sess->rtcp_sr_pkt.sr.sender_pcount)) {
825 pj_time_val ts_time;
826 pj_uint32_t rtp_ts;
827
828 /* So we should send RTCP SR */
829 *ret_p_pkt = (void*) &sess->rtcp_sr_pkt;
830 *len = sizeof(pjmedia_rtcp_sr_pkt);
831 common = &sess->rtcp_sr_pkt.common;
832 rr = &sess->rtcp_sr_pkt.rr;
833 sr = &sess->rtcp_sr_pkt.sr;
834
835 /* Update packet count */
836 sr->sender_pcount = pj_htonl(sess->stat.tx.pkt);
837
838 /* Update octets count */
839 sr->sender_bcount = pj_htonl(sess->stat.tx.bytes);
840
841 /* Fill in NTP timestamp in SR. */
842 sr->ntp_sec = pj_htonl(ntp.hi);
843 sr->ntp_frac = pj_htonl(ntp.lo);
844
845 /* Fill in RTP timestamp (corresponds to NTP timestamp) in SR. */
846 ts_time.sec = ntp.hi - sess->tv_base.sec - JAN_1970;
847 ts_time.msec = (long)(ntp.lo * 1000.0 / 0xFFFFFFFF);
848 rtp_ts = sess->rtp_ts_base +
849 (pj_uint32_t)(sess->clock_rate*ts_time.sec) +
850 (pj_uint32_t)(sess->clock_rate*ts_time.msec/1000);
851 sr->rtp_ts = pj_htonl(rtp_ts);
852
853 TRACE_((sess->name, "TX RTCP SR: ntp_ts=%p",
854 ((ntp.hi & 0xFFFF) << 16) + ((ntp.lo & 0xFFFF0000)
855 >> 16)));
856
857
858 } else {
859 /* We should send RTCP RR then */
860 *ret_p_pkt = (void*) &sess->rtcp_rr_pkt;
861 *len = sizeof(pjmedia_rtcp_rr_pkt);
862 common = &sess->rtcp_rr_pkt.common;
863 rr = &sess->rtcp_rr_pkt.rr;
864 sr = NULL;
865 }
866
867 /* SSRC and last_seq */
868 rr->ssrc = pj_htonl(sess->peer_ssrc);
869 rr->last_seq = (sess->seq_ctrl.cycles & 0xFFFF0000L);
870 /* Since this is an "+=" operation, make sure we update last_seq on
871 * both RR and SR.
872 */
873 sess->rtcp_sr_pkt.rr.last_seq += sess->seq_ctrl.max_seq;
874 sess->rtcp_rr_pkt.rr.last_seq += sess->seq_ctrl.max_seq;
875 rr->last_seq = pj_htonl(rr->last_seq);
876
877
878 /* Jitter */
879 rr->jitter = pj_htonl(sess->jitter >> 4);
880
881
882 /* Total lost. */
883 expected = pj_ntohl(rr->last_seq) - sess->seq_ctrl.base_seq;
884
885 /* This is bug: total lost already calculated on each incoming RTP!
886 if (expected >= sess->received)
887 sess->stat.rx.loss = expected - sess->received;
888 else
889 sess->stat.rx.loss = 0;
890 */
891
892 rr->total_lost_2 = (sess->stat.rx.loss >> 16) & 0xFF;
893 rr->total_lost_1 = (sess->stat.rx.loss >> 8) & 0xFF;
894 rr->total_lost_0 = (sess->stat.rx.loss & 0xFF);
895
896 /* Fraction lost calculation */
897 expected_interval = expected - sess->exp_prior;
898 sess->exp_prior = expected;
899
900 received_interval = sess->received - sess->rx_prior;
901 sess->rx_prior = sess->received;
902
903 if (expected_interval >= received_interval)
904 lost_interval = expected_interval - received_interval;
905 else
906 lost_interval = 0;
907
908 if (expected_interval==0 || lost_interval == 0) {
909 rr->fract_lost = 0;
910 } else {
911 rr->fract_lost = (lost_interval << 8) / expected_interval;
912 }
913
914 if (sess->rx_lsr_time.u64 == 0 || sess->rx_lsr == 0) {
915 rr->lsr = 0;
916 rr->dlsr = 0;
917 } else {
918 pj_timestamp ts;
919 pj_uint32_t lsr = sess->rx_lsr;
920 pj_uint64_t lsr_time = sess->rx_lsr_time.u64;
921 pj_uint32_t dlsr;
922
923 /* Convert LSR time to 1/65536 seconds resolution */
924 lsr_time = (lsr_time << 16) / sess->ts_freq.u64;
925
926 /* Fill in LSR.
927 LSR is the middle 32bit of the last SR NTP time received.
928 */
929 rr->lsr = pj_htonl(lsr);
930
931 /* Fill in DLSR.
932 DLSR is Delay since Last SR, in 1/65536 seconds.
933 */
934 ts.u64 = ts_now.u64;
935
936 /* Convert interval to 1/65536 seconds value */
937 ts.u64 = (ts.u64 << 16) / sess->ts_freq.u64;
938
939 /* Get DLSR */
940 dlsr = (pj_uint32_t)(ts.u64 - lsr_time);
941 rr->dlsr = pj_htonl(dlsr);
942
943 TRACE_((sess->name,"Tx RTCP RR: lsr=%p, lsr_time=%p, now=%p, dlsr=%p"
944 "(%ds:%03dms)",
945 lsr,
946 (pj_uint32_t)lsr_time,
947 (pj_uint32_t)ts.u64,
948 dlsr,
949 dlsr/65536,
950 (dlsr%65536)*1000/65536 ));
951 }
952
953 /* Update counter */
954 pj_gettimeofday(&sess->stat.rx.update);
955 sess->stat.rx.update_cnt++;
956}
957
958
959PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_sdes(
960 pjmedia_rtcp_session *session,
961 void *buf,
962 pj_size_t *length,
963 const pjmedia_rtcp_sdes *sdes)
964{
965 pjmedia_rtcp_common *hdr;
966 pj_uint8_t *p;
967 pj_size_t len;
968
969 PJ_ASSERT_RETURN(session && buf && length && sdes, PJ_EINVAL);
970
971 /* Verify SDES item length */
972 if (sdes->cname.slen > 255 || sdes->name.slen > 255 ||
973 sdes->email.slen > 255 || sdes->phone.slen > 255 ||
974 sdes->loc.slen > 255 || sdes->tool.slen > 255 ||
975 sdes->note.slen > 255)
976 {
977 return PJ_EINVAL;
978 }
979
980 /* Verify buffer length */
981 len = sizeof(*hdr);
982 if (sdes->cname.slen) len += sdes->cname.slen + 2;
983 if (sdes->name.slen) len += sdes->name.slen + 2;
984 if (sdes->email.slen) len += sdes->email.slen + 2;
985 if (sdes->phone.slen) len += sdes->phone.slen + 2;
986 if (sdes->loc.slen) len += sdes->loc.slen + 2;
987 if (sdes->tool.slen) len += sdes->tool.slen + 2;
988 if (sdes->note.slen) len += sdes->note.slen + 2;
989 len++; /* null termination */
990 len = ((len+3)/4) * 4;
991 if (len > *length)
992 return PJ_ETOOSMALL;
993
994 /* Build RTCP SDES header */
995 hdr = (pjmedia_rtcp_common*)buf;
996 pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr));
997 hdr->pt = RTCP_SDES;
998 hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
999
1000 /* Build RTCP SDES items */
1001 p = (pj_uint8_t*)hdr + sizeof(*hdr);
1002#define BUILD_SDES_ITEM(SDES_NAME, SDES_TYPE) \
1003 if (sdes->SDES_NAME.slen) { \
1004 *p++ = SDES_TYPE; \
1005 *p++ = (pj_uint8_t)sdes->SDES_NAME.slen; \
1006 pj_memcpy(p, sdes->SDES_NAME.ptr, sdes->SDES_NAME.slen); \
1007 p += sdes->SDES_NAME.slen; \
1008 }
1009 BUILD_SDES_ITEM(cname, RTCP_SDES_CNAME);
1010 BUILD_SDES_ITEM(name, RTCP_SDES_NAME);
1011 BUILD_SDES_ITEM(email, RTCP_SDES_EMAIL);
1012 BUILD_SDES_ITEM(phone, RTCP_SDES_PHONE);
1013 BUILD_SDES_ITEM(loc, RTCP_SDES_LOC);
1014 BUILD_SDES_ITEM(tool, RTCP_SDES_TOOL);
1015 BUILD_SDES_ITEM(note, RTCP_SDES_NOTE);
1016#undef BUILD_SDES_ITEM
1017
1018 /* Null termination */
1019 *p++ = 0;
1020
1021 /* Pad to 32bit */
1022 while ((p-(pj_uint8_t*)buf) % 4)
1023 *p++ = 0;
1024
1025 /* Finally */
1026 pj_assert((int)len == p-(pj_uint8_t*)buf);
1027 *length = len;
1028
1029 return PJ_SUCCESS;
1030}
1031
1032
1033PJ_DEF(pj_status_t) pjmedia_rtcp_build_rtcp_bye(pjmedia_rtcp_session *session,
1034 void *buf,
1035 pj_size_t *length,
1036 const pj_str_t *reason)
1037{
1038 pjmedia_rtcp_common *hdr;
1039 pj_uint8_t *p;
1040 pj_size_t len;
1041
1042 PJ_ASSERT_RETURN(session && buf && length, PJ_EINVAL);
1043
1044 /* Verify BYE reason length */
1045 if (reason && reason->slen > 255)
1046 return PJ_EINVAL;
1047
1048 /* Verify buffer length */
1049 len = sizeof(*hdr);
1050 if (reason && reason->slen) len += reason->slen + 1;
1051 len = ((len+3)/4) * 4;
1052 if (len > *length)
1053 return PJ_ETOOSMALL;
1054
1055 /* Build RTCP BYE header */
1056 hdr = (pjmedia_rtcp_common*)buf;
1057 pj_memcpy(hdr, &session->rtcp_sr_pkt.common, sizeof(*hdr));
1058 hdr->pt = RTCP_BYE;
1059 hdr->length = pj_htons((pj_uint16_t)(len/4 - 1));
1060
1061 /* Write RTCP BYE reason */
1062 p = (pj_uint8_t*)hdr + sizeof(*hdr);
1063 if (reason && reason->slen) {
1064 *p++ = (pj_uint8_t)reason->slen;
1065 pj_memcpy(p, reason->ptr, reason->slen);
1066 p += reason->slen;
1067 }
1068
1069 /* Pad to 32bit */
1070 while ((p-(pj_uint8_t*)buf) % 4)
1071 *p++ = 0;
1072
1073 pj_assert((int)len == p-(pj_uint8_t*)buf);
1074 *length = len;
1075
1076 return PJ_SUCCESS;
1077}
1078
1079
1080PJ_DEF(pj_status_t) pjmedia_rtcp_enable_xr( pjmedia_rtcp_session *sess,
1081 pj_bool_t enable)
1082{
1083#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
1084
1085 /* Check if request won't change anything */
1086 if (!(enable ^ sess->xr_enabled))
1087 return PJ_SUCCESS;
1088
1089 if (!enable) {
1090 sess->xr_enabled = PJ_FALSE;
1091 return PJ_SUCCESS;
1092 }
1093
1094 pjmedia_rtcp_xr_init(&sess->xr_session, sess, 0, 1);
1095 sess->xr_enabled = PJ_TRUE;
1096
1097 return PJ_SUCCESS;
1098
1099#else
1100
1101 PJ_UNUSED_ARG(sess);
1102 PJ_UNUSED_ARG(enable);
1103 return PJ_ENOTSUP;
1104
1105#endif
1106}