blob: 173be9a9162a6e8868204d5cd41d4ff1b980e976 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $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
21#include <pjmedia/rtcp_xr.h>
22#include <pjmedia/errno.h>
23#include <pjmedia/rtcp.h>
24#include <pj/assert.h>
25#include <pj/log.h>
26#include <pj/os.h>
27#include <pj/sock.h>
28#include <pj/string.h>
29
30#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
31
32#define THIS_FILE "rtcp_xr.c"
33
34
35#if PJ_HAS_HIGH_RES_TIMER==0
36# error "High resolution timer needs to be enabled"
37#endif
38
39
40/* RTCP XR payload type */
41#define RTCP_XR 207
42
43/* RTCP XR block types */
44#define BT_LOSS_RLE 1
45#define BT_DUP_RLE 2
46#define BT_RCPT_TIMES 3
47#define BT_RR_TIME 4
48#define BT_DLRR 5
49#define BT_STATS 6
50#define BT_VOIP_METRICS 7
51
52
53#define DEFAULT_GMIN 16
54
55
56#if 0
57# define TRACE_(x) PJ_LOG(3,x)
58#else
59# define TRACE_(x) ;
60#endif
61
62void pjmedia_rtcp_xr_init( pjmedia_rtcp_xr_session *session,
63 struct pjmedia_rtcp_session *parent_session,
64 pj_uint8_t gmin,
65 unsigned frames_per_packet)
66{
67 pj_bzero(session, sizeof(pjmedia_rtcp_xr_session));
68
69 session->name = parent_session->name;
70 session->rtcp_session = parent_session;
71 pj_memcpy(&session->pkt.common, &session->rtcp_session->rtcp_sr_pkt.common,
72 sizeof(pjmedia_rtcp_common));
73 session->pkt.common.pt = RTCP_XR;
74
75 /* Init config */
76 session->stat.rx.voip_mtc.gmin = (pj_uint8_t)(gmin? gmin : DEFAULT_GMIN);
77 session->ptime = session->rtcp_session->pkt_size * 1000 /
78 session->rtcp_session->clock_rate;
79 session->frames_per_packet = frames_per_packet;
80
81 /* Init Statistics Summary fields which have non-zero default */
82 session->stat.rx.stat_sum.jitter.min = (unsigned) -1;
83 session->stat.rx.stat_sum.toh.min = (unsigned) -1;
84
85 /* Init VoIP Metrics fields which have non-zero default */
86 session->stat.rx.voip_mtc.signal_lvl = 127;
87 session->stat.rx.voip_mtc.noise_lvl = 127;
88 session->stat.rx.voip_mtc.rerl = 127;
89 session->stat.rx.voip_mtc.r_factor = 127;
90 session->stat.rx.voip_mtc.ext_r_factor = 127;
91 session->stat.rx.voip_mtc.mos_lq = 127;
92 session->stat.rx.voip_mtc.mos_cq = 127;
93
94 session->stat.tx.voip_mtc.signal_lvl = 127;
95 session->stat.tx.voip_mtc.noise_lvl = 127;
96 session->stat.tx.voip_mtc.rerl = 127;
97 session->stat.tx.voip_mtc.r_factor = 127;
98 session->stat.tx.voip_mtc.ext_r_factor = 127;
99 session->stat.tx.voip_mtc.mos_lq = 127;
100 session->stat.tx.voip_mtc.mos_cq = 127;
101}
102
103void pjmedia_rtcp_xr_fini(pjmedia_rtcp_xr_session *session)
104{
105 PJ_UNUSED_ARG(session);
106}
107
108PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess,
109 unsigned rpt_types,
110 void **rtcp_pkt, int *len)
111{
112 pj_uint16_t size = 0;
113
114 /* Receiver Reference Time Report Block */
115 /* Build this block if we have received packets since last build */
116 if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_RR_TIME)) &&
117 sess->rx_last_rr != sess->rtcp_session->stat.rx.pkt)
118 {
119 pjmedia_rtcp_xr_rb_rr_time *r;
120 pjmedia_rtcp_ntp_rec ntp;
121
122 r = (pjmedia_rtcp_xr_rb_rr_time*) &sess->pkt.buf[size];
123 pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_rr_time));
124
125 /* Init block header */
126 r->header.bt = BT_RR_TIME;
127 r->header.specific = 0;
128 r->header.length = pj_htons(2);
129
130 /* Generate block contents */
131 pjmedia_rtcp_get_ntp_time(sess->rtcp_session, &ntp);
132 r->ntp_sec = pj_htonl(ntp.hi);
133 r->ntp_frac = pj_htonl(ntp.lo);
134
135 /* Finally */
136 size += sizeof(pjmedia_rtcp_xr_rb_rr_time);
137 sess->rx_last_rr = sess->rtcp_session->stat.rx.pkt;
138 }
139
140 /* DLRR Report Block */
141 /* Build this block if we have received RR NTP (rx_lrr) before */
142 if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_DLRR)) &&
143 sess->rx_lrr)
144 {
145 pjmedia_rtcp_xr_rb_dlrr *r;
146 pjmedia_rtcp_xr_rb_dlrr_item *dlrr_item;
147 pj_timestamp ts;
148
149 r = (pjmedia_rtcp_xr_rb_dlrr*) &sess->pkt.buf[size];
150 pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_dlrr));
151
152 /* Init block header */
153 r->header.bt = BT_DLRR;
154 r->header.specific = 0;
155 r->header.length = pj_htons(sizeof(pjmedia_rtcp_xr_rb_dlrr)/4 - 1);
156
157 /* Generate block contents */
158 dlrr_item = &r->item;
159 dlrr_item->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc);
160 dlrr_item->lrr = pj_htonl(sess->rx_lrr);
161
162 /* Calculate DLRR */
163 if (sess->rx_lrr != 0) {
164 pj_get_timestamp(&ts);
165 ts.u64 -= sess->rx_lrr_time.u64;
166
167 /* Convert DLRR time to 1/65536 seconds resolution */
168 ts.u64 = (ts.u64 << 16) / sess->rtcp_session->ts_freq.u64;
169 dlrr_item->dlrr = pj_htonl(ts.u32.lo);
170 } else {
171 dlrr_item->dlrr = 0;
172 }
173
174 /* Finally */
175 size += sizeof(pjmedia_rtcp_xr_rb_dlrr);
176 }
177
178 /* Statistics Summary Block */
179 /* Build this block if we have received packets since last build */
180 if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_STATS)) &&
181 sess->stat.rx.stat_sum.count > 0)
182 {
183 pjmedia_rtcp_xr_rb_stats *r;
184 pj_uint8_t specific = 0;
185
186 r = (pjmedia_rtcp_xr_rb_stats*) &sess->pkt.buf[size];
187 pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_stats));
188
189 /* Init block header */
190 specific |= sess->stat.rx.stat_sum.l ? (1 << 7) : 0;
191 specific |= sess->stat.rx.stat_sum.d ? (1 << 6) : 0;
192 specific |= sess->stat.rx.stat_sum.j ? (1 << 5) : 0;
193 specific |= (sess->stat.rx.stat_sum.t & 3) << 3;
194 r->header.bt = BT_STATS;
195 r->header.specific = specific;
196 r->header.length = pj_htons(9);
197
198 /* Generate block contents */
199 r->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc);
200 r->begin_seq = pj_htons((pj_uint16_t)
201 (sess->stat.rx.stat_sum.begin_seq & 0xFFFF));
202 r->end_seq = pj_htons((pj_uint16_t)
203 (sess->stat.rx.stat_sum.end_seq & 0xFFFF));
204 if (sess->stat.rx.stat_sum.l) {
205 r->lost = pj_htonl(sess->stat.rx.stat_sum.lost);
206 }
207 if (sess->stat.rx.stat_sum.d) {
208 r->dup = pj_htonl(sess->stat.rx.stat_sum.dup);
209 }
210 if (sess->stat.rx.stat_sum.j) {
211 r->jitter_min = pj_htonl(sess->stat.rx.stat_sum.jitter.min);
212 r->jitter_max = pj_htonl(sess->stat.rx.stat_sum.jitter.max);
213 r->jitter_mean =
214 pj_htonl((unsigned)sess->stat.rx.stat_sum.jitter.mean);
215 r->jitter_dev =
216 pj_htonl(pj_math_stat_get_stddev(&sess->stat.rx.stat_sum.jitter));
217 }
218 if (sess->stat.rx.stat_sum.t) {
219 r->toh_min = sess->stat.rx.stat_sum.toh.min;
220 r->toh_max = sess->stat.rx.stat_sum.toh.max;
221 r->toh_mean = (unsigned) sess->stat.rx.stat_sum.toh.mean;
222 r->toh_dev = pj_math_stat_get_stddev(&sess->stat.rx.stat_sum.toh);
223 }
224
225 /* Reset TX statistics summary each time built */
226 pj_bzero(&sess->stat.rx.stat_sum, sizeof(sess->stat.rx.stat_sum));
227 sess->stat.rx.stat_sum.jitter.min = (unsigned) -1;
228 sess->stat.rx.stat_sum.toh.min = (unsigned) -1;
229
230 /* Finally */
231 size += sizeof(pjmedia_rtcp_xr_rb_stats);
232 pj_gettimeofday(&sess->stat.rx.stat_sum.update);
233 }
234
235 /* Voip Metrics Block */
236 /* Build this block if we have received packets */
237 if ((rpt_types == 0 || (rpt_types & PJMEDIA_RTCP_XR_VOIP_METRICS)) &&
238 sess->rtcp_session->stat.rx.pkt)
239 {
240 pjmedia_rtcp_xr_rb_voip_mtc *r;
241 pj_uint32_t c11;
242 pj_uint32_t c13;
243 pj_uint32_t c14;
244 pj_uint32_t c22;
245 pj_uint32_t c23;
246 pj_uint32_t c31;
247 pj_uint32_t c32;
248 pj_uint32_t c33;
249 pj_uint32_t ctotal, m;
250 unsigned est_extra_delay;
251
252 r = (pjmedia_rtcp_xr_rb_voip_mtc*) &sess->pkt.buf[size];
253 pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_voip_mtc));
254
255 /* Init block header */
256 r->header.bt = BT_VOIP_METRICS;
257 r->header.specific = 0;
258 r->header.length = pj_htons(8);
259
260 /* Use temp vars for easiness. */
261 c11 = sess->voip_mtc_stat.c11;
262 c13 = sess->voip_mtc_stat.c13;
263 c14 = sess->voip_mtc_stat.c14;
264 c22 = sess->voip_mtc_stat.c22;
265 c23 = sess->voip_mtc_stat.c23;
266 c33 = sess->voip_mtc_stat.c33;
267 m = sess->ptime * sess->frames_per_packet;
268
269 /* Calculate additional transition counts. */
270 c31 = c13;
271 c32 = c23;
272 ctotal = c11 + c14 + c13 + c22 + c23 + c31 + c32 + c33;
273
274 if (ctotal) {
275 pj_uint32_t p32, p23;
276
277 //original version:
278 //p32 = c32 / (c31 + c32 + c33);
279 if (c31 + c32 + c33 == 0)
280 p32 = 0;
281 else
282 p32 = (c32 << 16) / (c31 + c32 + c33);
283
284 //original version:
285 //if ((c22 + c23) < 1) {
286 // p23 = 1;
287 //} else {
288 // p23 = 1 - c22 / (c22 + c23);
289 //}
290 if (c23 == 0) {
291 p23 = 0;
292 } else {
293 p23 = (c23 << 16) / (c22 + c23);
294 }
295
296 /* Calculate loss/discard densities, scaled of 0-256 */
297 if (c11 == 0)
298 sess->stat.rx.voip_mtc.gap_den = 0;
299 else
300 sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)
301 ((c14 << 8) / (c11 + c14));
302 if (p23 == 0)
303 sess->stat.rx.voip_mtc.burst_den = 0;
304 else
305 sess->stat.rx.voip_mtc.burst_den = (pj_uint8_t)
306 ((p23 << 8) / (p23 + p32));
307
308 /* Calculate (average) durations, in ms */
309 if (c13 == 0) {
310 c13 = 1;
311 ctotal += 1;
312 }
313 sess->stat.rx.voip_mtc.gap_dur = (pj_uint16_t)
314 ((c11+c14+c13) * m / c13);
315 sess->stat.rx.voip_mtc.burst_dur = (pj_uint16_t)
316 ((ctotal - (c11+c14+c13)) * m / c13);
317
318 /* Callculate loss/discard rates, scaled 0-256 */
319 sess->stat.rx.voip_mtc.loss_rate = (pj_uint8_t)
320 ((sess->voip_mtc_stat.loss_count << 8) / ctotal);
321 sess->stat.rx.voip_mtc.discard_rate = (pj_uint8_t)
322 ((sess->voip_mtc_stat.discard_count << 8) / ctotal);
323 } else {
324 /* No lost/discarded packet yet. */
325 sess->stat.rx.voip_mtc.gap_den = 0;
326 sess->stat.rx.voip_mtc.burst_den = 0;
327 sess->stat.rx.voip_mtc.gap_dur = 0;
328 sess->stat.rx.voip_mtc.burst_dur = 0;
329 sess->stat.rx.voip_mtc.loss_rate = 0;
330 sess->stat.rx.voip_mtc.discard_rate = 0;
331 }
332
333 /* Set round trip delay (in ms) to RTT calculated after receiving
334 * DLRR or DLSR.
335 */
336 if (sess->stat.rtt.last)
337 sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
338 (sess->stat.rtt.last / 1000);
339 else if (sess->rtcp_session->stat.rtt.last)
340 sess->stat.rx.voip_mtc.rnd_trip_delay = (pj_uint16_t)
341 (sess->rtcp_session->stat.rtt.last / 1000);
342
343 /* End system delay = RTT/2 + current jitter buffer size +
344 * EXTRA (estimated extra delay)
345 * EXTRA will cover additional delay introduced by other components of
346 * audio engine, e.g: sound device, codec, AEC, PLC, WSOLA.
347 * Since it is difficult to get the exact value of EXTRA, estimation
348 * is taken to be totally around 30ms + sound device latency.
349 */
350 est_extra_delay = 30;
351
352#if PJMEDIA_SOUND_IMPLEMENTATION!=PJMEDIA_SOUND_NULL_SOUND
353 est_extra_delay += PJMEDIA_SND_DEFAULT_REC_LATENCY +
354 PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
355#endif
356
357 sess->stat.rx.voip_mtc.end_sys_delay = (pj_uint16_t)
358 (sess->stat.rx.voip_mtc.rnd_trip_delay / 2 +
359 sess->stat.rx.voip_mtc.jb_nom +
360 est_extra_delay);
361
362 /* Generate block contents */
363 r->ssrc = pj_htonl(sess->rtcp_session->peer_ssrc);
364 r->loss_rate = sess->stat.rx.voip_mtc.loss_rate;
365 r->discard_rate = sess->stat.rx.voip_mtc.discard_rate;
366 r->burst_den = sess->stat.rx.voip_mtc.burst_den;
367 r->gap_den = sess->stat.rx.voip_mtc.gap_den;
368 r->burst_dur = pj_htons(sess->stat.rx.voip_mtc.burst_dur);
369 r->gap_dur = pj_htons(sess->stat.rx.voip_mtc.gap_dur);
370 r->rnd_trip_delay = pj_htons(sess->stat.rx.voip_mtc.rnd_trip_delay);
371 r->end_sys_delay = pj_htons(sess->stat.rx.voip_mtc.end_sys_delay);
372 /* signal & noise level encoded in two's complement form */
373 r->signal_lvl = (pj_uint8_t)
374 ((sess->stat.rx.voip_mtc.signal_lvl >= 0)?
375 sess->stat.rx.voip_mtc.signal_lvl :
376 (sess->stat.rx.voip_mtc.signal_lvl + 256));
377 r->noise_lvl = (pj_uint8_t)
378 ((sess->stat.rx.voip_mtc.noise_lvl >= 0)?
379 sess->stat.rx.voip_mtc.noise_lvl :
380 (sess->stat.rx.voip_mtc.noise_lvl + 256));
381 r->rerl = sess->stat.rx.voip_mtc.rerl;
382 r->gmin = sess->stat.rx.voip_mtc.gmin;
383 r->r_factor = sess->stat.rx.voip_mtc.r_factor;
384 r->ext_r_factor = sess->stat.rx.voip_mtc.ext_r_factor;
385 r->mos_lq = sess->stat.rx.voip_mtc.mos_lq;
386 r->mos_cq = sess->stat.rx.voip_mtc.mos_cq;
387 r->rx_config = sess->stat.rx.voip_mtc.rx_config;
388 r->jb_nom = pj_htons(sess->stat.rx.voip_mtc.jb_nom);
389 r->jb_max = pj_htons(sess->stat.rx.voip_mtc.jb_max);
390 r->jb_abs_max = pj_htons(sess->stat.rx.voip_mtc.jb_abs_max);
391
392 /* Finally */
393 size += sizeof(pjmedia_rtcp_xr_rb_voip_mtc);
394 pj_gettimeofday(&sess->stat.rx.voip_mtc.update);
395 }
396
397 /* Add RTCP XR header size */
398 size += sizeof(sess->pkt.common);
399
400 /* Set RTCP XR header 'length' to packet size in 32-bit unit minus one */
401 sess->pkt.common.length = pj_htons((pj_uint16_t)(size/4 - 1));
402
403 /* Set the return values */
404 *rtcp_pkt = (void*) &sess->pkt;
405 *len = size;
406}
407
408
409void pjmedia_rtcp_xr_rx_rtcp_xr( pjmedia_rtcp_xr_session *sess,
410 const void *pkt,
411 pj_size_t size)
412{
413 const pjmedia_rtcp_xr_pkt *rtcp_xr = (pjmedia_rtcp_xr_pkt*) pkt;
414 const pjmedia_rtcp_xr_rb_rr_time *rb_rr_time = NULL;
415 const pjmedia_rtcp_xr_rb_dlrr *rb_dlrr = NULL;
416 const pjmedia_rtcp_xr_rb_stats *rb_stats = NULL;
417 const pjmedia_rtcp_xr_rb_voip_mtc *rb_voip_mtc = NULL;
418 const pjmedia_rtcp_xr_rb_header *rb_hdr = (pjmedia_rtcp_xr_rb_header*)
419 rtcp_xr->buf;
420 unsigned pkt_len, rb_len;
421
422 if (rtcp_xr->common.pt != RTCP_XR)
423 return;
424
425 pkt_len = pj_ntohs((pj_uint16_t)rtcp_xr->common.length);
426
427 if ((pkt_len + 1) > (size / 4))
428 return;
429
430 /* Parse report rpt_types */
431 while ((pj_int32_t*)rb_hdr < (pj_int32_t*)pkt + pkt_len)
432 {
433 rb_len = pj_ntohs((pj_uint16_t)rb_hdr->length);
434
435 /* Just skip any block with length == 0 (no report content) */
436 if (rb_len) {
437 switch (rb_hdr->bt) {
438 case BT_RR_TIME:
439 rb_rr_time = (pjmedia_rtcp_xr_rb_rr_time*) rb_hdr;
440 break;
441 case BT_DLRR:
442 rb_dlrr = (pjmedia_rtcp_xr_rb_dlrr*) rb_hdr;
443 break;
444 case BT_STATS:
445 rb_stats = (pjmedia_rtcp_xr_rb_stats*) rb_hdr;
446 break;
447 case BT_VOIP_METRICS:
448 rb_voip_mtc = (pjmedia_rtcp_xr_rb_voip_mtc*) rb_hdr;
449 break;
450 default:
451 break;
452 }
453 }
454 rb_hdr = (pjmedia_rtcp_xr_rb_header*)
455 ((pj_int32_t*)rb_hdr + rb_len + 1);
456 }
457
458 /* Receiving RR Time */
459 if (rb_rr_time) {
460 /* Save LRR from NTP timestamp of the RR time block report */
461 sess->rx_lrr = ((pj_ntohl(rb_rr_time->ntp_sec) & 0x0000FFFF) << 16) |
462 ((pj_ntohl(rb_rr_time->ntp_frac) >> 16) & 0xFFFF);
463
464 /* Calculate RR arrival time for DLRR */
465 pj_get_timestamp(&sess->rx_lrr_time);
466
467 TRACE_((sess->name, "Rx RTCP SR: ntp_ts=%p", sess->rx_lrr,
468 (pj_uint32_t)(sess->rx_lrr_time.u64*65536/
469 sess->rtcp_session->ts_freq.u64)));
470 }
471
472 /* Receiving DLRR */
473 if (rb_dlrr) {
474 pj_uint32_t lrr, now, dlrr;
475 pj_uint64_t eedelay;
476 pjmedia_rtcp_ntp_rec ntp;
477
478 /* LRR is the middle 32bit of NTP. It has 1/65536 second
479 * resolution
480 */
481 lrr = pj_ntohl(rb_dlrr->item.lrr);
482
483 /* DLRR is delay since LRR, also in 1/65536 resolution */
484 dlrr = pj_ntohl(rb_dlrr->item.dlrr);
485
486 /* Get current time, and convert to 1/65536 resolution */
487 pjmedia_rtcp_get_ntp_time(sess->rtcp_session, &ntp);
488 now = ((ntp.hi & 0xFFFF) << 16) + (ntp.lo >> 16);
489
490 /* End-to-end delay is (now-lrr-dlrr) */
491 eedelay = now - lrr - dlrr;
492
493 /* Convert end to end delay to usec (keeping the calculation in
494 * 64bit space)::
495 * sess->ee_delay = (eedelay * 1000) / 65536;
496 */
497 if (eedelay < 4294) {
498 eedelay = (eedelay * 1000000) >> 16;
499 } else {
500 eedelay = (eedelay * 1000) >> 16;
501 eedelay *= 1000;
502 }
503
504 TRACE_((sess->name, "Rx RTCP XR DLRR: lrr=%p, dlrr=%p (%d:%03dms), "
505 "now=%p, rtt=%p",
506 lrr, dlrr, dlrr/65536, (dlrr%65536)*1000/65536,
507 now, (pj_uint32_t)eedelay));
508
509 /* Only save calculation if "now" is greater than lrr, or
510 * otherwise rtt will be invalid
511 */
512 if (now-dlrr >= lrr) {
513 unsigned rtt = (pj_uint32_t)eedelay;
514
515 /* Check that eedelay value really makes sense.
516 * We allow up to 30 seconds RTT!
517 */
518 if (eedelay <= 30 * 1000 * 1000UL) {
519 /* "Normalize" rtt value that is exceptionally high.
520 * For such values, "normalize" the rtt to be three times
521 * the average value.
522 */
523 if (rtt>((unsigned)sess->stat.rtt.mean*3) && sess->stat.rtt.n!=0)
524 {
525 unsigned orig_rtt = rtt;
526 rtt = (unsigned)sess->stat.rtt.mean*3;
527 PJ_LOG(5,(sess->name,
528 "RTT value %d usec is normalized to %d usec",
529 orig_rtt, rtt));
530 }
531
532 TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt));
533 pj_math_stat_update(&sess->stat.rtt, rtt);
534 }
535 } else {
536 PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: "
537 "lrr=%p, now=%p, dlrr=%p (%d:%03dms), "
538 "diff=%d",
539 lrr, now, dlrr, dlrr/65536,
540 (dlrr%65536)*1000/65536,
541 dlrr-(now-lrr)));
542 }
543 }
544
545 /* Receiving Statistics Summary */
546 if (rb_stats) {
547 pj_uint8_t flags = rb_stats->header.specific;
548
549 pj_bzero(&sess->stat.tx.stat_sum, sizeof(sess->stat.tx.stat_sum));
550
551 /* Range of packets sequence reported in this blocks */
552 sess->stat.tx.stat_sum.begin_seq = pj_ntohs(rb_stats->begin_seq);
553 sess->stat.tx.stat_sum.end_seq = pj_ntohs(rb_stats->end_seq);
554
555 /* Get flags of valid fields */
556 sess->stat.tx.stat_sum.l = (flags & (1 << 7)) != 0;
557 sess->stat.tx.stat_sum.d = (flags & (1 << 6)) != 0;
558 sess->stat.tx.stat_sum.j = (flags & (1 << 5)) != 0;
559 sess->stat.tx.stat_sum.t = (flags & (3 << 3)) != 0;
560
561 /* Fetch the reports info */
562 if (sess->stat.tx.stat_sum.l) {
563 sess->stat.tx.stat_sum.lost = pj_ntohl(rb_stats->lost);
564 }
565
566 if (sess->stat.tx.stat_sum.d) {
567 sess->stat.tx.stat_sum.dup = pj_ntohl(rb_stats->dup);
568 }
569
570 if (sess->stat.tx.stat_sum.j) {
571 sess->stat.tx.stat_sum.jitter.min = pj_ntohl(rb_stats->jitter_min);
572 sess->stat.tx.stat_sum.jitter.max = pj_ntohl(rb_stats->jitter_max);
573 sess->stat.tx.stat_sum.jitter.mean= pj_ntohl(rb_stats->jitter_mean);
574 pj_math_stat_set_stddev(&sess->stat.tx.stat_sum.jitter,
575 pj_ntohl(rb_stats->jitter_dev));
576 }
577
578 if (sess->stat.tx.stat_sum.t) {
579 sess->stat.tx.stat_sum.toh.min = rb_stats->toh_min;
580 sess->stat.tx.stat_sum.toh.max = rb_stats->toh_max;
581 sess->stat.tx.stat_sum.toh.mean= rb_stats->toh_mean;
582 pj_math_stat_set_stddev(&sess->stat.tx.stat_sum.toh,
583 pj_ntohl(rb_stats->toh_dev));
584 }
585
586 pj_gettimeofday(&sess->stat.tx.stat_sum.update);
587 }
588
589 /* Receiving VoIP Metrics */
590 if (rb_voip_mtc) {
591 sess->stat.tx.voip_mtc.loss_rate = rb_voip_mtc->loss_rate;
592 sess->stat.tx.voip_mtc.discard_rate = rb_voip_mtc->discard_rate;
593 sess->stat.tx.voip_mtc.burst_den = rb_voip_mtc->burst_den;
594 sess->stat.tx.voip_mtc.gap_den = rb_voip_mtc->gap_den;
595 sess->stat.tx.voip_mtc.burst_dur = pj_ntohs(rb_voip_mtc->burst_dur);
596 sess->stat.tx.voip_mtc.gap_dur = pj_ntohs(rb_voip_mtc->gap_dur);
597 sess->stat.tx.voip_mtc.rnd_trip_delay =
598 pj_ntohs(rb_voip_mtc->rnd_trip_delay);
599 sess->stat.tx.voip_mtc.end_sys_delay =
600 pj_ntohs(rb_voip_mtc->end_sys_delay);
601 /* signal & noise level encoded in two's complement form */
602 sess->stat.tx.voip_mtc.signal_lvl = (pj_int8_t)
603 ((rb_voip_mtc->signal_lvl > 127)?
604 ((int)rb_voip_mtc->signal_lvl - 256) :
605 rb_voip_mtc->signal_lvl);
606 sess->stat.tx.voip_mtc.noise_lvl = (pj_int8_t)
607 ((rb_voip_mtc->noise_lvl > 127)?
608 ((int)rb_voip_mtc->noise_lvl - 256) :
609 rb_voip_mtc->noise_lvl);
610 sess->stat.tx.voip_mtc.rerl = rb_voip_mtc->rerl;
611 sess->stat.tx.voip_mtc.gmin = rb_voip_mtc->gmin;
612 sess->stat.tx.voip_mtc.r_factor = rb_voip_mtc->r_factor;
613 sess->stat.tx.voip_mtc.ext_r_factor = rb_voip_mtc->ext_r_factor;
614 sess->stat.tx.voip_mtc.mos_lq = rb_voip_mtc->mos_lq;
615 sess->stat.tx.voip_mtc.mos_cq = rb_voip_mtc->mos_cq;
616 sess->stat.tx.voip_mtc.rx_config = rb_voip_mtc->rx_config;
617 sess->stat.tx.voip_mtc.jb_nom = pj_ntohs(rb_voip_mtc->jb_nom);
618 sess->stat.tx.voip_mtc.jb_max = pj_ntohs(rb_voip_mtc->jb_max);
619 sess->stat.tx.voip_mtc.jb_abs_max = pj_ntohs(rb_voip_mtc->jb_abs_max);
620
621 pj_gettimeofday(&sess->stat.tx.voip_mtc.update);
622 }
623}
624
625/* Place seq into a 32-bit sequence number space based upon a
626 * heuristic for its most likely location.
627 */
628static pj_uint32_t extend_seq(pjmedia_rtcp_xr_session *sess,
629 const pj_uint16_t seq)
630{
631
632 pj_uint32_t extended_seq, seq_a, seq_b, diff_a, diff_b;
633 if(sess->uninitialized_src_ref_seq) {
634 /* This is the first sequence number received. Place
635 * it in the middle of the extended sequence number
636 * space.
637 */
638 sess->src_ref_seq = seq | 0x80000000u;
639 sess->uninitialized_src_ref_seq = PJ_FALSE;
640 extended_seq = sess->src_ref_seq;
641 } else {
642 /* Prior sequence numbers have been received.
643 * Propose two candidates for the extended sequence
644 * number: seq_a is without wraparound, seq_b with
645 * wraparound.
646 */
647 seq_a = seq | (sess->src_ref_seq & 0xFFFF0000u);
648 if(sess->src_ref_seq < seq_a) {
649 seq_b = seq_a - 0x00010000u;
650 diff_a = seq_a - sess->src_ref_seq;
651 diff_b = sess->src_ref_seq - seq_b;
652 } else {
653 seq_b = seq_a + 0x00010000u;
654 diff_a = sess->src_ref_seq - seq_a;
655 diff_b = seq_b - sess->src_ref_seq;
656 }
657
658 /* Choose the closer candidate. If they are equally
659 * close, the choice is somewhat arbitrary: we choose
660 * the candidate for which no rollover is necessary.
661 */
662 if(diff_a < diff_b) {
663 extended_seq = seq_a;
664 } else {
665 extended_seq = seq_b;
666 }
667
668 /* Set the reference sequence number to be this most
669 * recently-received sequence number.
670 */
671 sess->src_ref_seq = extended_seq;
672 }
673
674 /* Return our best guess for a 32-bit sequence number that
675 * corresponds to the 16-bit number we were given.
676 */
677 return extended_seq;
678}
679
680void pjmedia_rtcp_xr_rx_rtp( pjmedia_rtcp_xr_session *sess,
681 unsigned seq,
682 int lost,
683 int dup,
684 int discarded,
685 int jitter,
686 int toh, pj_bool_t toh_ipv4)
687{
688 pj_uint32_t ext_seq;
689
690 /* Get 32 bit version of sequence */
691 ext_seq = extend_seq(sess, (pj_uint16_t)seq);
692
693 /* Update statistics summary */
694 sess->stat.rx.stat_sum.count++;
695
696 if (sess->stat.rx.stat_sum.begin_seq == 0 ||
697 sess->stat.rx.stat_sum.begin_seq > ext_seq)
698 {
699 sess->stat.rx.stat_sum.begin_seq = ext_seq;
700 }
701
702 if (sess->stat.rx.stat_sum.end_seq == 0 ||
703 sess->stat.rx.stat_sum.end_seq < ext_seq)
704 {
705 sess->stat.rx.stat_sum.end_seq = ext_seq;
706 }
707
708 if (lost >= 0) {
709 sess->stat.rx.stat_sum.l = PJ_TRUE;
710 if (lost > 0)
711 sess->stat.rx.stat_sum.lost++;
712 }
713
714 if (dup >= 0) {
715 sess->stat.rx.stat_sum.d = PJ_TRUE;
716 if (dup > 0)
717 sess->stat.rx.stat_sum.dup++;
718 }
719
720 if (jitter >= 0) {
721 sess->stat.rx.stat_sum.j = PJ_TRUE;
722 pj_math_stat_update(&sess->stat.rx.stat_sum.jitter, jitter);
723 }
724
725 if (toh >= 0) {
726 sess->stat.rx.stat_sum.t = toh_ipv4? 1 : 2;
727 pj_math_stat_update(&sess->stat.rx.stat_sum.toh, toh);
728 }
729
730 /* Update burst metrics.
731 * There are two terms introduced in the RFC 3611: gap & burst.
732 * Gap represents good stream condition, lost+discard rate <= 1/Gmin.
733 * Burst represents the opposite, lost+discard rate > 1/Gmin.
734 */
735 if (lost >= 0 && discarded >= 0) {
736 if(lost > 0) {
737 sess->voip_mtc_stat.loss_count++;
738 }
739 if(discarded > 0) {
740 sess->voip_mtc_stat.discard_count++;
741 }
742 if(!lost && !discarded) {
743 /* Number of good packets since last lost/discarded */
744 sess->voip_mtc_stat.pkt++;
745 }
746 else {
747 if(sess->voip_mtc_stat.pkt >= sess->stat.rx.voip_mtc.gmin) {
748 /* Gap condition */
749 if(sess->voip_mtc_stat.lost == 1) {
750 /* Gap -> Gap */
751 sess->voip_mtc_stat.c14++;
752 }
753 else {
754 /* Burst -> Gap */
755 sess->voip_mtc_stat.c13++;
756 }
757 sess->voip_mtc_stat.lost = 1;
758 sess->voip_mtc_stat.c11 += sess->voip_mtc_stat.pkt;
759 }
760 else {
761 /* Burst condition */
762 sess->voip_mtc_stat.lost++;
763 if(sess->voip_mtc_stat.pkt == 0) {
764 /* Consecutive losts */
765 sess->voip_mtc_stat.c33++;
766 }
767 else {
768 /* Any good packets, but still bursting */
769 sess->voip_mtc_stat.c23++;
770 sess->voip_mtc_stat.c22 += (sess->voip_mtc_stat.pkt - 1);
771 }
772 }
773
774 sess->voip_mtc_stat.pkt = 0;
775 }
776 }
777}
778
779void pjmedia_rtcp_xr_tx_rtp( pjmedia_rtcp_xr_session *session,
780 unsigned ptsize )
781{
782 PJ_UNUSED_ARG(session);
783 PJ_UNUSED_ARG(ptsize);
784}
785
786PJ_DEF(pj_status_t) pjmedia_rtcp_xr_update_info(
787 pjmedia_rtcp_xr_session *sess,
788 unsigned info,
789 pj_int32_t val)
790{
791 int v = val;
792
793 switch(info) {
794 case PJMEDIA_RTCP_XR_INFO_SIGNAL_LVL:
795 sess->stat.rx.voip_mtc.signal_lvl = (pj_int8_t) v;
796 break;
797
798 case PJMEDIA_RTCP_XR_INFO_NOISE_LVL:
799 sess->stat.rx.voip_mtc.noise_lvl = (pj_int8_t) v;
800 break;
801
802 case PJMEDIA_RTCP_XR_INFO_RERL:
803 sess->stat.rx.voip_mtc.rerl = (pj_uint8_t) v;
804 break;
805
806 case PJMEDIA_RTCP_XR_INFO_R_FACTOR:
807 sess->stat.rx.voip_mtc.ext_r_factor = (pj_uint8_t) v;
808 break;
809
810 case PJMEDIA_RTCP_XR_INFO_MOS_LQ:
811 sess->stat.rx.voip_mtc.mos_lq = (pj_uint8_t) v;
812 break;
813
814 case PJMEDIA_RTCP_XR_INFO_MOS_CQ:
815 sess->stat.rx.voip_mtc.mos_cq = (pj_uint8_t) v;
816 break;
817
818 case PJMEDIA_RTCP_XR_INFO_CONF_PLC:
819 if (v >= 0 && v <= 3) {
820 sess->stat.rx.voip_mtc.rx_config &= 0x3F;
821 sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 6);
822 }
823 break;
824
825 case PJMEDIA_RTCP_XR_INFO_CONF_JBA:
826 if (v >= 0 && v <= 3) {
827 sess->stat.rx.voip_mtc.rx_config &= 0xCF;
828 sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) (v << 4);
829 }
830 break;
831
832 case PJMEDIA_RTCP_XR_INFO_CONF_JBR:
833 if (v >= 0 && v <= 15) {
834 sess->stat.rx.voip_mtc.rx_config &= 0xF0;
835 sess->stat.rx.voip_mtc.rx_config |= (pj_uint8_t) v;
836 }
837 break;
838
839 case PJMEDIA_RTCP_XR_INFO_JB_NOM:
840 sess->stat.rx.voip_mtc.jb_nom = (pj_uint16_t) v;
841 break;
842
843 case PJMEDIA_RTCP_XR_INFO_JB_MAX:
844 sess->stat.rx.voip_mtc.jb_max = (pj_uint16_t) v;
845 break;
846
847 case PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX:
848 sess->stat.rx.voip_mtc.jb_abs_max = (pj_uint16_t) v;
849 break;
850
851 default:
852 return PJ_EINVAL;
853 }
854
855 return PJ_SUCCESS;
856}
857
858#endif