~noskcaj/ubuntu/saucy/sflphone/merge-1.2.3-2

« back to all changes in this revision

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

  • Committer: Jackson Doak
  • Date: 2013-07-10 21:04:46 UTC
  • mfrom: (20.1.3 sid)
  • Revision ID: noskcaj@ubuntu.com-20130710210446-y8f587vza807icr9
Properly merged from upstream.

Show diffs side-by-side

added added

removed removed

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