~ubuntu-branches/ubuntu/trusty/sflphone/trusty

« back to all changes in this revision

Viewing changes to daemon/libs/pjproject-2.0.1/pjmedia/src/pjmedia/rtp.c

  • Committer: Package Import Robot
  • Author(s): Mark Purcell
  • Date: 2014-01-28 18:23:36 UTC
  • mfrom: (4.3.4 sid)
  • Revision ID: package-import@ubuntu.com-20140128182336-jrsv0k9u6cawc068
Tags: 1.3.0-1
* New upstream release 
  - Fixes "New Upstream Release" (Closes: #735846)
  - Fixes "Ringtone does not stop" (Closes: #727164)
  - Fixes "[sflphone-kde] crash on startup" (Closes: #718178)
  - Fixes "sflphone GUI crashes when call is hung up" (Closes: #736583)
* Build-Depends: ensure GnuTLS 2.6
  - libucommon-dev (>= 6.0.7-1.1), libccrtp-dev (>= 2.0.6-3)
  - Fixes "FTBFS Build-Depends libgnutls{26,28}-dev" (Closes: #722040)
* Fix "boost 1.49 is going away" unversioned Build-Depends: (Closes: #736746)
* Add Build-Depends: libsndfile-dev, nepomuk-core-dev

Show diffs side-by-side

added added

removed removed

Lines of Context:
1
 
/* $Id: rtp.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
 
#include <pjmedia/rtp.h>
21
 
#include <pjmedia/errno.h>
22
 
#include <pj/log.h>
23
 
#include <pj/sock.h>    /* pj_htonx, pj_htonx */
24
 
#include <pj/assert.h>
25
 
#include <pj/rand.h>
26
 
#include <pj/string.h>
27
 
 
28
 
 
29
 
#define THIS_FILE   "rtp.c"
30
 
 
31
 
#define RTP_VERSION     2
32
 
 
33
 
#define RTP_SEQ_MOD     (1 << 16)
34
 
#define MAX_DROPOUT     ((pj_int16_t)3000)
35
 
#define MAX_MISORDER    ((pj_int16_t)100)
36
 
#define MIN_SEQUENTIAL  ((pj_int16_t)2)
37
 
 
38
 
static void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *seq_ctrl,
39
 
                                    pj_uint16_t seq);
40
 
 
41
 
 
42
 
PJ_DEF(pj_status_t) pjmedia_rtp_session_init( pjmedia_rtp_session *ses,
43
 
                                              int default_pt,
44
 
                                              pj_uint32_t sender_ssrc )
45
 
{
46
 
    PJ_LOG(5, (THIS_FILE,
47
 
               "pjmedia_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
48
 
               ses, default_pt, sender_ssrc));
49
 
 
50
 
    /* Check RTP header packing. */
51
 
    if (sizeof(struct pjmedia_rtp_hdr) != 12) {
52
 
        pj_assert(!"Wrong RTP header packing!");
53
 
        return PJMEDIA_RTP_EINPACK;
54
 
    }
55
 
 
56
 
    /* If sender_ssrc is not specified, create from random value. */
57
 
    if (sender_ssrc == 0 || sender_ssrc == (pj_uint32_t)-1) {
58
 
        sender_ssrc = pj_htonl(pj_rand());
59
 
    } else {
60
 
        sender_ssrc = pj_htonl(sender_ssrc);
61
 
    }
62
 
 
63
 
    /* Initialize session. */
64
 
    pj_bzero(ses, sizeof(*ses));
65
 
 
66
 
    /* Initial sequence number SHOULD be random, according to RFC 3550. */
67
 
    /* According to RFC 3711, it should be random within 2^15 bit */
68
 
    ses->out_extseq = pj_rand() & 0x7FFF;
69
 
    ses->peer_ssrc = 0;
70
 
 
71
 
    /* Build default header for outgoing RTP packet. */
72
 
    ses->out_hdr.v = RTP_VERSION;
73
 
    ses->out_hdr.p = 0;
74
 
    ses->out_hdr.x = 0;
75
 
    ses->out_hdr.cc = 0;
76
 
    ses->out_hdr.m = 0;
77
 
    ses->out_hdr.pt = (pj_uint8_t) default_pt;
78
 
    ses->out_hdr.seq = (pj_uint16_t) pj_htons( (pj_uint16_t)ses->out_extseq );
79
 
    ses->out_hdr.ts = 0;
80
 
    ses->out_hdr.ssrc = sender_ssrc;
81
 
 
82
 
    /* Keep some arguments as session defaults. */
83
 
    ses->out_pt = (pj_uint16_t) default_pt;
84
 
 
85
 
    return PJ_SUCCESS;
86
 
}
87
 
 
88
 
PJ_DEF(pj_status_t) pjmedia_rtp_session_init2(
89
 
                                    pjmedia_rtp_session *ses,
90
 
                                    pjmedia_rtp_session_setting settings)
91
 
{
92
 
    pj_status_t status;
93
 
    int          pt = 0;
94
 
    pj_uint32_t  sender_ssrc = 0;
95
 
 
96
 
    if (settings.flags & 1)
97
 
        pt = settings.default_pt;
98
 
    if (settings.flags & 2)
99
 
        sender_ssrc = settings.sender_ssrc;
100
 
 
101
 
    status = pjmedia_rtp_session_init(ses, pt, sender_ssrc);
102
 
    if (status != PJ_SUCCESS)
103
 
        return status;
104
 
 
105
 
    if (settings.flags & 4) {
106
 
        ses->out_extseq = settings.seq;
107
 
        ses->out_hdr.seq = pj_htons((pj_uint16_t)ses->out_extseq);
108
 
    }
109
 
    if (settings.flags & 8)
110
 
        ses->out_hdr.ts = pj_htonl(settings.ts);
111
 
 
112
 
    return PJ_SUCCESS;
113
 
}
114
 
 
115
 
 
116
 
PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses,
117
 
                                            int pt, int m,
118
 
                                            int payload_len, int ts_len,
119
 
                                            const void **rtphdr, int *hdrlen )
120
 
{
121
 
    PJ_UNUSED_ARG(payload_len);
122
 
 
123
 
    /* Update timestamp */
124
 
    ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
125
 
 
126
 
    /* If payload_len is zero, bail out.
127
 
     * This is a clock frame; we're not really transmitting anything.
128
 
     */
129
 
    if (payload_len == 0)
130
 
        return PJ_SUCCESS;
131
 
 
132
 
    /* Update session. */
133
 
    ses->out_extseq++;
134
 
 
135
 
    /* Create outgoing header. */
136
 
    ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
137
 
    ses->out_hdr.m = (pj_uint16_t) m;
138
 
    ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
139
 
 
140
 
    /* Return values */
141
 
    *rtphdr = &ses->out_hdr;
142
 
    *hdrlen = sizeof(pjmedia_rtp_hdr);
143
 
 
144
 
    return PJ_SUCCESS;
145
 
}
146
 
 
147
 
 
148
 
PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses,
149
 
                                            const void *pkt, int pkt_len,
150
 
                                            const pjmedia_rtp_hdr **hdr,
151
 
                                            const void **payload,
152
 
                                            unsigned *payloadlen)
153
 
{
154
 
    int offset;
155
 
 
156
 
    PJ_UNUSED_ARG(ses);
157
 
 
158
 
    /* Assume RTP header at the start of packet. We'll verify this later. */
159
 
    *hdr = (pjmedia_rtp_hdr*)pkt;
160
 
 
161
 
    /* Check RTP header sanity. */
162
 
    if ((*hdr)->v != RTP_VERSION) {
163
 
        return PJMEDIA_RTP_EINVER;
164
 
    }
165
 
 
166
 
    /* Payload is located right after header plus CSRC */
167
 
    offset = sizeof(pjmedia_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
168
 
 
169
 
    /* Adjust offset if RTP extension is used. */
170
 
    if ((*hdr)->x) {
171
 
        pjmedia_rtp_ext_hdr *ext = (pjmedia_rtp_ext_hdr*)
172
 
                                    (((pj_uint8_t*)pkt) + offset);
173
 
        offset += ((pj_ntohs(ext->length)+1) * sizeof(pj_uint32_t));
174
 
    }
175
 
 
176
 
    /* Check that offset is less than packet size */
177
 
    if (offset > pkt_len)
178
 
        return PJMEDIA_RTP_EINLEN;
179
 
 
180
 
    /* Find and set payload. */
181
 
    *payload = ((pj_uint8_t*)pkt) + offset;
182
 
    *payloadlen = pkt_len - offset;
183
 
 
184
 
    /* Remove payload padding if any */
185
 
    if ((*hdr)->p && *payloadlen > 0) {
186
 
        pj_uint8_t pad_len;
187
 
 
188
 
        pad_len = ((pj_uint8_t*)(*payload))[*payloadlen - 1];
189
 
        if (pad_len <= *payloadlen)
190
 
            *payloadlen -= pad_len;
191
 
    }
192
 
 
193
 
    return PJ_SUCCESS;
194
 
}
195
 
 
196
 
 
197
 
PJ_DEF(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses,
198
 
                                         const pjmedia_rtp_hdr *hdr,
199
 
                                         pjmedia_rtp_status *p_seq_st)
200
 
{
201
 
    pjmedia_rtp_session_update2(ses, hdr, p_seq_st, PJ_TRUE);
202
 
}
203
 
 
204
 
PJ_DEF(void) pjmedia_rtp_session_update2( pjmedia_rtp_session *ses,
205
 
                                          const pjmedia_rtp_hdr *hdr,
206
 
                                          pjmedia_rtp_status *p_seq_st,
207
 
                                          pj_bool_t check_pt)
208
 
{
209
 
    pjmedia_rtp_status seq_st;
210
 
 
211
 
    /* for now check_pt MUST be either PJ_TRUE or PJ_FALSE.
212
 
     * In the future we might change check_pt from boolean to
213
 
     * unsigned integer to accommodate more flags.
214
 
     */
215
 
    pj_assert(check_pt==PJ_TRUE || check_pt==PJ_FALSE);
216
 
 
217
 
    /* Init status */
218
 
    seq_st.status.value = 0;
219
 
    seq_st.diff = 0;
220
 
 
221
 
    /* Check SSRC. */
222
 
    if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
223
 
 
224
 
    if (pj_ntohl(hdr->ssrc) != ses->peer_ssrc) {
225
 
        seq_st.status.flag.badssrc = 1;
226
 
        ses->peer_ssrc = pj_ntohl(hdr->ssrc);
227
 
    }
228
 
 
229
 
    /* Check payload type. */
230
 
    if (check_pt && hdr->pt != ses->out_pt) {
231
 
        if (p_seq_st) {
232
 
            p_seq_st->status.value = seq_st.status.value;
233
 
            p_seq_st->status.flag.bad = 1;
234
 
            p_seq_st->status.flag.badpt = 1;
235
 
        }
236
 
        return;
237
 
    }
238
 
 
239
 
    /* Initialize sequence number on first packet received. */
240
 
    if (ses->received == 0)
241
 
        pjmedia_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
242
 
 
243
 
    /* Check sequence number to see if remote session has been restarted. */
244
 
    pjmedia_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq), &seq_st);
245
 
    if (seq_st.status.flag.restart) {
246
 
        ++ses->received;
247
 
 
248
 
    } else if (!seq_st.status.flag.bad) {
249
 
        ++ses->received;
250
 
    }
251
 
 
252
 
    if (p_seq_st) {
253
 
        p_seq_st->status.value = seq_st.status.value;
254
 
        p_seq_st->diff = seq_st.diff;
255
 
    }
256
 
}
257
 
 
258
 
 
259
 
 
260
 
void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
261
 
{
262
 
    sess->base_seq = seq;
263
 
    sess->max_seq = seq;
264
 
    sess->bad_seq = RTP_SEQ_MOD + 1;
265
 
    sess->cycles = 0;
266
 
}
267
 
 
268
 
 
269
 
void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
270
 
{
271
 
    pjmedia_rtp_seq_restart(sess, seq);
272
 
 
273
 
    sess->max_seq = (pj_uint16_t) (seq - 1);
274
 
    sess->probation = MIN_SEQUENTIAL;
275
 
}
276
 
 
277
 
 
278
 
void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *sess,
279
 
                             pj_uint16_t seq,
280
 
                             pjmedia_rtp_status *seq_status)
281
 
{
282
 
    pj_uint16_t udelta = (pj_uint16_t) (seq - sess->max_seq);
283
 
    pjmedia_rtp_status st;
284
 
 
285
 
    /* Init status */
286
 
    st.status.value = 0;
287
 
    st.diff = 0;
288
 
 
289
 
    /*
290
 
     * Source is not valid until MIN_SEQUENTIAL packets with
291
 
     * sequential sequence numbers have been received.
292
 
     */
293
 
    if (sess->probation) {
294
 
 
295
 
        st.status.flag.probation = 1;
296
 
 
297
 
        if (seq == sess->max_seq+ 1) {
298
 
            /* packet is in sequence */
299
 
            st.diff = 1;
300
 
            sess->probation--;
301
 
            sess->max_seq = seq;
302
 
            if (sess->probation == 0) {
303
 
                st.status.flag.probation = 0;
304
 
            }
305
 
        } else {
306
 
 
307
 
            st.diff = 0;
308
 
 
309
 
            st.status.flag.bad = 1;
310
 
            if (seq == sess->max_seq)
311
 
                st.status.flag.dup = 1;
312
 
            else
313
 
                st.status.flag.outorder = 1;
314
 
 
315
 
            sess->probation = MIN_SEQUENTIAL - 1;
316
 
            sess->max_seq = seq;
317
 
        }
318
 
 
319
 
 
320
 
    } else if (udelta == 0) {
321
 
 
322
 
        st.status.flag.dup = 1;
323
 
 
324
 
    } else if (udelta < MAX_DROPOUT) {
325
 
        /* in order, with permissible gap */
326
 
        if (seq < sess->max_seq) {
327
 
            /* Sequence number wrapped - count another 64K cycle. */
328
 
            sess->cycles += RTP_SEQ_MOD;
329
 
        }
330
 
        sess->max_seq = seq;
331
 
 
332
 
        st.diff = udelta;
333
 
 
334
 
    } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
335
 
        /* the sequence number made a very large jump */
336
 
        if (seq == sess->bad_seq) {
337
 
            /*
338
 
             * Two sequential packets -- assume that the other side
339
 
             * restarted without telling us so just re-sync
340
 
             * (i.e., pretend this was the first packet).
341
 
             */
342
 
            pjmedia_rtp_seq_restart(sess, seq);
343
 
            st.status.flag.restart = 1;
344
 
            st.status.flag.probation = 1;
345
 
            st.diff = 1;
346
 
        }
347
 
        else {
348
 
            sess->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
349
 
            st.status.flag.bad = 1;
350
 
            st.status.flag.outorder = 1;
351
 
        }
352
 
    } else {
353
 
        /* old duplicate or reordered packet.
354
 
         * Not necessarily bad packet (?)
355
 
         */
356
 
        st.status.flag.outorder = 1;
357
 
    }
358
 
 
359
 
 
360
 
    if (seq_status) {
361
 
        seq_status->diff = st.diff;
362
 
        seq_status->status.value = st.status.value;
363
 
    }
364
 
}