~ubuntu-branches/ubuntu/vivid/sflphone/vivid

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject/pjmedia/src/pjmedia/rtcp_xr.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2013-06-30 11:40:56 UTC
  • mfrom: (4.1.18 saucy-proposed)
  • Revision ID: package-import@ubuntu.com-20130630114056-0np50jkyqo6vnmii
Tags: 1.2.3-2
* changeset_r92d62cfc54732bbbcfff2b1d36c096b120b981a5.diff 
  - fixes automatic endian detection 
* Update Vcs: fixes vcs-field-not-canonical

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: rtcp_xr.c 3553 2011-05-05 06:14:19Z nanang $ */
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
 
 
62
 
void 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
 
 
103
 
void pjmedia_rtcp_xr_fini(pjmedia_rtcp_xr_session *session)
104
 
{
105
 
    PJ_UNUSED_ARG(session);
106
 
}
107
 
 
108
 
PJ_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
 
 
409
 
void 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
 
 */
628
 
static 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
 
 
680
 
void 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
 
 
779
 
void 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
 
 
786
 
PJ_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