1
/* $Id: rtcp_xr.c 3553 2011-05-05 06:14:19Z 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
21
#include <pjmedia/rtcp_xr.h>
22
#include <pjmedia/errno.h>
23
#include <pjmedia/rtcp.h>
24
#include <pj/assert.h>
28
#include <pj/string.h>
30
#if defined(PJMEDIA_HAS_RTCP_XR) && (PJMEDIA_HAS_RTCP_XR != 0)
32
#define THIS_FILE "rtcp_xr.c"
35
#if PJ_HAS_HIGH_RES_TIMER==0
36
# error "High resolution timer needs to be enabled"
40
/* RTCP XR payload type */
43
/* RTCP XR block types */
46
#define BT_RCPT_TIMES 3
50
#define BT_VOIP_METRICS 7
53
#define DEFAULT_GMIN 16
57
# define TRACE_(x) PJ_LOG(3,x)
62
void pjmedia_rtcp_xr_init( pjmedia_rtcp_xr_session *session,
63
struct pjmedia_rtcp_session *parent_session,
65
unsigned frames_per_packet)
67
pj_bzero(session, sizeof(pjmedia_rtcp_xr_session));
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;
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;
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;
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;
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;
103
void pjmedia_rtcp_xr_fini(pjmedia_rtcp_xr_session *session)
105
PJ_UNUSED_ARG(session);
108
PJ_DEF(void) pjmedia_rtcp_build_rtcp_xr( pjmedia_rtcp_xr_session *sess,
110
void **rtcp_pkt, int *len)
112
pj_uint16_t size = 0;
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)
119
pjmedia_rtcp_xr_rb_rr_time *r;
120
pjmedia_rtcp_ntp_rec ntp;
122
r = (pjmedia_rtcp_xr_rb_rr_time*) &sess->pkt.buf[size];
123
pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_rr_time));
125
/* Init block header */
126
r->header.bt = BT_RR_TIME;
127
r->header.specific = 0;
128
r->header.length = pj_htons(2);
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);
136
size += sizeof(pjmedia_rtcp_xr_rb_rr_time);
137
sess->rx_last_rr = sess->rtcp_session->stat.rx.pkt;
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)) &&
145
pjmedia_rtcp_xr_rb_dlrr *r;
146
pjmedia_rtcp_xr_rb_dlrr_item *dlrr_item;
149
r = (pjmedia_rtcp_xr_rb_dlrr*) &sess->pkt.buf[size];
150
pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_dlrr));
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);
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);
163
if (sess->rx_lrr != 0) {
164
pj_get_timestamp(&ts);
165
ts.u64 -= sess->rx_lrr_time.u64;
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);
175
size += sizeof(pjmedia_rtcp_xr_rb_dlrr);
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)
183
pjmedia_rtcp_xr_rb_stats *r;
184
pj_uint8_t specific = 0;
186
r = (pjmedia_rtcp_xr_rb_stats*) &sess->pkt.buf[size];
187
pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_stats));
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);
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);
207
if (sess->stat.rx.stat_sum.d) {
208
r->dup = pj_htonl(sess->stat.rx.stat_sum.dup);
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);
214
pj_htonl((unsigned)sess->stat.rx.stat_sum.jitter.mean);
216
pj_htonl(pj_math_stat_get_stddev(&sess->stat.rx.stat_sum.jitter));
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);
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;
231
size += sizeof(pjmedia_rtcp_xr_rb_stats);
232
pj_gettimeofday(&sess->stat.rx.stat_sum.update);
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)
240
pjmedia_rtcp_xr_rb_voip_mtc *r;
249
pj_uint32_t ctotal, m;
250
unsigned est_extra_delay;
252
r = (pjmedia_rtcp_xr_rb_voip_mtc*) &sess->pkt.buf[size];
253
pj_bzero(r, sizeof(pjmedia_rtcp_xr_rb_voip_mtc));
255
/* Init block header */
256
r->header.bt = BT_VOIP_METRICS;
257
r->header.specific = 0;
258
r->header.length = pj_htons(8);
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;
269
/* Calculate additional transition counts. */
272
ctotal = c11 + c14 + c13 + c22 + c23 + c31 + c32 + c33;
275
pj_uint32_t p32, p23;
278
//p32 = c32 / (c31 + c32 + c33);
279
if (c31 + c32 + c33 == 0)
282
p32 = (c32 << 16) / (c31 + c32 + c33);
285
//if ((c22 + c23) < 1) {
288
// p23 = 1 - c22 / (c22 + c23);
293
p23 = (c23 << 16) / (c22 + c23);
296
/* Calculate loss/discard densities, scaled of 0-256 */
298
sess->stat.rx.voip_mtc.gap_den = 0;
300
sess->stat.rx.voip_mtc.gap_den = (pj_uint8_t)
301
((c14 << 8) / (c11 + c14));
303
sess->stat.rx.voip_mtc.burst_den = 0;
305
sess->stat.rx.voip_mtc.burst_den = (pj_uint8_t)
306
((p23 << 8) / (p23 + p32));
308
/* Calculate (average) durations, in ms */
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);
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);
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;
333
/* Set round trip delay (in ms) to RTT calculated after receiving
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);
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.
350
est_extra_delay = 30;
352
#if PJMEDIA_SOUND_IMPLEMENTATION!=PJMEDIA_SOUND_NULL_SOUND
353
est_extra_delay += PJMEDIA_SND_DEFAULT_REC_LATENCY +
354
PJMEDIA_SND_DEFAULT_PLAY_LATENCY;
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 +
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);
393
size += sizeof(pjmedia_rtcp_xr_rb_voip_mtc);
394
pj_gettimeofday(&sess->stat.rx.voip_mtc.update);
397
/* Add RTCP XR header size */
398
size += sizeof(sess->pkt.common);
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));
403
/* Set the return values */
404
*rtcp_pkt = (void*) &sess->pkt;
409
void pjmedia_rtcp_xr_rx_rtcp_xr( pjmedia_rtcp_xr_session *sess,
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*)
420
unsigned pkt_len, rb_len;
422
if (rtcp_xr->common.pt != RTCP_XR)
425
pkt_len = pj_ntohs((pj_uint16_t)rtcp_xr->common.length);
427
if ((pkt_len + 1) > (size / 4))
430
/* Parse report rpt_types */
431
while ((pj_int32_t*)rb_hdr < (pj_int32_t*)pkt + pkt_len)
433
rb_len = pj_ntohs((pj_uint16_t)rb_hdr->length);
435
/* Just skip any block with length == 0 (no report content) */
437
switch (rb_hdr->bt) {
439
rb_rr_time = (pjmedia_rtcp_xr_rb_rr_time*) rb_hdr;
442
rb_dlrr = (pjmedia_rtcp_xr_rb_dlrr*) rb_hdr;
445
rb_stats = (pjmedia_rtcp_xr_rb_stats*) rb_hdr;
447
case BT_VOIP_METRICS:
448
rb_voip_mtc = (pjmedia_rtcp_xr_rb_voip_mtc*) rb_hdr;
454
rb_hdr = (pjmedia_rtcp_xr_rb_header*)
455
((pj_int32_t*)rb_hdr + rb_len + 1);
458
/* Receiving 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);
464
/* Calculate RR arrival time for DLRR */
465
pj_get_timestamp(&sess->rx_lrr_time);
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)));
474
pj_uint32_t lrr, now, dlrr;
476
pjmedia_rtcp_ntp_rec ntp;
478
/* LRR is the middle 32bit of NTP. It has 1/65536 second
481
lrr = pj_ntohl(rb_dlrr->item.lrr);
483
/* DLRR is delay since LRR, also in 1/65536 resolution */
484
dlrr = pj_ntohl(rb_dlrr->item.dlrr);
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);
490
/* End-to-end delay is (now-lrr-dlrr) */
491
eedelay = now - lrr - dlrr;
493
/* Convert end to end delay to usec (keeping the calculation in
495
* sess->ee_delay = (eedelay * 1000) / 65536;
497
if (eedelay < 4294) {
498
eedelay = (eedelay * 1000000) >> 16;
500
eedelay = (eedelay * 1000) >> 16;
504
TRACE_((sess->name, "Rx RTCP XR DLRR: lrr=%p, dlrr=%p (%d:%03dms), "
506
lrr, dlrr, dlrr/65536, (dlrr%65536)*1000/65536,
507
now, (pj_uint32_t)eedelay));
509
/* Only save calculation if "now" is greater than lrr, or
510
* otherwise rtt will be invalid
512
if (now-dlrr >= lrr) {
513
unsigned rtt = (pj_uint32_t)eedelay;
515
/* Check that eedelay value really makes sense.
516
* We allow up to 30 seconds RTT!
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
523
if (rtt>((unsigned)sess->stat.rtt.mean*3) && sess->stat.rtt.n!=0)
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",
532
TRACE_((sess->name, "RTCP RTT is set to %d usec", rtt));
533
pj_math_stat_update(&sess->stat.rtt, rtt);
536
PJ_LOG(5, (sess->name, "Internal RTCP NTP clock skew detected: "
537
"lrr=%p, now=%p, dlrr=%p (%d:%03dms), "
539
lrr, now, dlrr, dlrr/65536,
540
(dlrr%65536)*1000/65536,
545
/* Receiving Statistics Summary */
547
pj_uint8_t flags = rb_stats->header.specific;
549
pj_bzero(&sess->stat.tx.stat_sum, sizeof(sess->stat.tx.stat_sum));
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);
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;
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);
566
if (sess->stat.tx.stat_sum.d) {
567
sess->stat.tx.stat_sum.dup = pj_ntohl(rb_stats->dup);
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));
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));
586
pj_gettimeofday(&sess->stat.tx.stat_sum.update);
589
/* Receiving VoIP Metrics */
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);
621
pj_gettimeofday(&sess->stat.tx.voip_mtc.update);
625
/* Place seq into a 32-bit sequence number space based upon a
626
* heuristic for its most likely location.
628
static pj_uint32_t extend_seq(pjmedia_rtcp_xr_session *sess,
629
const pj_uint16_t seq)
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
638
sess->src_ref_seq = seq | 0x80000000u;
639
sess->uninitialized_src_ref_seq = PJ_FALSE;
640
extended_seq = sess->src_ref_seq;
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
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;
653
seq_b = seq_a + 0x00010000u;
654
diff_a = sess->src_ref_seq - seq_a;
655
diff_b = seq_b - sess->src_ref_seq;
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.
662
if(diff_a < diff_b) {
663
extended_seq = seq_a;
665
extended_seq = seq_b;
668
/* Set the reference sequence number to be this most
669
* recently-received sequence number.
671
sess->src_ref_seq = extended_seq;
674
/* Return our best guess for a 32-bit sequence number that
675
* corresponds to the 16-bit number we were given.
680
void pjmedia_rtcp_xr_rx_rtp( pjmedia_rtcp_xr_session *sess,
686
int toh, pj_bool_t toh_ipv4)
690
/* Get 32 bit version of sequence */
691
ext_seq = extend_seq(sess, (pj_uint16_t)seq);
693
/* Update statistics summary */
694
sess->stat.rx.stat_sum.count++;
696
if (sess->stat.rx.stat_sum.begin_seq == 0 ||
697
sess->stat.rx.stat_sum.begin_seq > ext_seq)
699
sess->stat.rx.stat_sum.begin_seq = ext_seq;
702
if (sess->stat.rx.stat_sum.end_seq == 0 ||
703
sess->stat.rx.stat_sum.end_seq < ext_seq)
705
sess->stat.rx.stat_sum.end_seq = ext_seq;
709
sess->stat.rx.stat_sum.l = PJ_TRUE;
711
sess->stat.rx.stat_sum.lost++;
715
sess->stat.rx.stat_sum.d = PJ_TRUE;
717
sess->stat.rx.stat_sum.dup++;
721
sess->stat.rx.stat_sum.j = PJ_TRUE;
722
pj_math_stat_update(&sess->stat.rx.stat_sum.jitter, jitter);
726
sess->stat.rx.stat_sum.t = toh_ipv4? 1 : 2;
727
pj_math_stat_update(&sess->stat.rx.stat_sum.toh, toh);
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.
735
if (lost >= 0 && discarded >= 0) {
737
sess->voip_mtc_stat.loss_count++;
740
sess->voip_mtc_stat.discard_count++;
742
if(!lost && !discarded) {
743
/* Number of good packets since last lost/discarded */
744
sess->voip_mtc_stat.pkt++;
747
if(sess->voip_mtc_stat.pkt >= sess->stat.rx.voip_mtc.gmin) {
749
if(sess->voip_mtc_stat.lost == 1) {
751
sess->voip_mtc_stat.c14++;
755
sess->voip_mtc_stat.c13++;
757
sess->voip_mtc_stat.lost = 1;
758
sess->voip_mtc_stat.c11 += sess->voip_mtc_stat.pkt;
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++;
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);
774
sess->voip_mtc_stat.pkt = 0;
779
void pjmedia_rtcp_xr_tx_rtp( pjmedia_rtcp_xr_session *session,
782
PJ_UNUSED_ARG(session);
783
PJ_UNUSED_ARG(ptsize);
786
PJ_DEF(pj_status_t) pjmedia_rtcp_xr_update_info(
787
pjmedia_rtcp_xr_session *sess,
794
case PJMEDIA_RTCP_XR_INFO_SIGNAL_LVL:
795
sess->stat.rx.voip_mtc.signal_lvl = (pj_int8_t) v;
798
case PJMEDIA_RTCP_XR_INFO_NOISE_LVL:
799
sess->stat.rx.voip_mtc.noise_lvl = (pj_int8_t) v;
802
case PJMEDIA_RTCP_XR_INFO_RERL:
803
sess->stat.rx.voip_mtc.rerl = (pj_uint8_t) v;
806
case PJMEDIA_RTCP_XR_INFO_R_FACTOR:
807
sess->stat.rx.voip_mtc.ext_r_factor = (pj_uint8_t) v;
810
case PJMEDIA_RTCP_XR_INFO_MOS_LQ:
811
sess->stat.rx.voip_mtc.mos_lq = (pj_uint8_t) v;
814
case PJMEDIA_RTCP_XR_INFO_MOS_CQ:
815
sess->stat.rx.voip_mtc.mos_cq = (pj_uint8_t) v;
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);
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);
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;
839
case PJMEDIA_RTCP_XR_INFO_JB_NOM:
840
sess->stat.rx.voip_mtc.jb_nom = (pj_uint16_t) v;
843
case PJMEDIA_RTCP_XR_INFO_JB_MAX:
844
sess->stat.rx.voip_mtc.jb_max = (pj_uint16_t) v;
847
case PJMEDIA_RTCP_XR_INFO_JB_ABS_MAX:
848
sess->stat.rx.voip_mtc.jb_abs_max = (pj_uint16_t) v;