1
/* $Id: rtp.c 3553 2011-05-05 06:14:19Z nanang $ */
3
* Copyright (C) 2008-2011 Teluu Inc. (http://www.teluu.com)
4
* Copyright (C) 2003-2008 Benny Prijono <benny@prijono.org>
6
* This program is free software; you can redistribute it and/or modify
7
* it under the terms of the GNU General Public License as published by
8
* the Free Software Foundation; either version 2 of the License, or
9
* (at your option) any later version.
11
* This program is distributed in the hope that it will be useful,
12
* but WITHOUT ANY WARRANTY; without even the implied warranty of
13
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14
* GNU General Public License for more details.
16
* You should have received a copy of the GNU General Public License
17
* along with this program; if not, write to the Free Software
18
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
20
#include <pjmedia/rtp.h>
21
#include <pjmedia/errno.h>
23
#include <pj/sock.h> /* pj_htonx, pj_htonx */
24
#include <pj/assert.h>
26
#include <pj/string.h>
29
#define THIS_FILE "rtp.c"
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)
38
static void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *seq_ctrl,
42
PJ_DEF(pj_status_t) pjmedia_rtp_session_init( pjmedia_rtp_session *ses,
44
pj_uint32_t sender_ssrc )
47
"pjmedia_rtp_session_init: ses=%p, default_pt=%d, ssrc=0x%x",
48
ses, default_pt, sender_ssrc));
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;
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());
60
sender_ssrc = pj_htonl(sender_ssrc);
63
/* Initialize session. */
64
pj_bzero(ses, sizeof(*ses));
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;
71
/* Build default header for outgoing RTP packet. */
72
ses->out_hdr.v = RTP_VERSION;
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 );
80
ses->out_hdr.ssrc = sender_ssrc;
82
/* Keep some arguments as session defaults. */
83
ses->out_pt = (pj_uint16_t) default_pt;
88
PJ_DEF(pj_status_t) pjmedia_rtp_session_init2(
89
pjmedia_rtp_session *ses,
90
pjmedia_rtp_session_setting settings)
94
pj_uint32_t sender_ssrc = 0;
96
if (settings.flags & 1)
97
pt = settings.default_pt;
98
if (settings.flags & 2)
99
sender_ssrc = settings.sender_ssrc;
101
status = pjmedia_rtp_session_init(ses, pt, sender_ssrc);
102
if (status != PJ_SUCCESS)
105
if (settings.flags & 4) {
106
ses->out_extseq = settings.seq;
107
ses->out_hdr.seq = pj_htons((pj_uint16_t)ses->out_extseq);
109
if (settings.flags & 8)
110
ses->out_hdr.ts = pj_htonl(settings.ts);
116
PJ_DEF(pj_status_t) pjmedia_rtp_encode_rtp( pjmedia_rtp_session *ses,
118
int payload_len, int ts_len,
119
const void **rtphdr, int *hdrlen )
121
PJ_UNUSED_ARG(payload_len);
123
/* Update timestamp */
124
ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
126
/* If payload_len is zero, bail out.
127
* This is a clock frame; we're not really transmitting anything.
129
if (payload_len == 0)
132
/* Update session. */
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);
141
*rtphdr = &ses->out_hdr;
142
*hdrlen = sizeof(pjmedia_rtp_hdr);
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)
158
/* Assume RTP header at the start of packet. We'll verify this later. */
159
*hdr = (pjmedia_rtp_hdr*)pkt;
161
/* Check RTP header sanity. */
162
if ((*hdr)->v != RTP_VERSION) {
163
return PJMEDIA_RTP_EINVER;
166
/* Payload is located right after header plus CSRC */
167
offset = sizeof(pjmedia_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
169
/* Adjust offset if RTP extension is used. */
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));
176
/* Check that offset is less than packet size */
177
if (offset > pkt_len)
178
return PJMEDIA_RTP_EINLEN;
180
/* Find and set payload. */
181
*payload = ((pj_uint8_t*)pkt) + offset;
182
*payloadlen = pkt_len - offset;
184
/* Remove payload padding if any */
185
if ((*hdr)->p && *payloadlen > 0) {
188
pad_len = ((pj_uint8_t*)(*payload))[*payloadlen - 1];
189
if (pad_len <= *payloadlen)
190
*payloadlen -= pad_len;
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)
201
pjmedia_rtp_session_update2(ses, hdr, p_seq_st, PJ_TRUE);
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,
209
pjmedia_rtp_status seq_st;
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.
215
pj_assert(check_pt==PJ_TRUE || check_pt==PJ_FALSE);
218
seq_st.status.value = 0;
222
if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
224
if (pj_ntohl(hdr->ssrc) != ses->peer_ssrc) {
225
seq_st.status.flag.badssrc = 1;
226
ses->peer_ssrc = pj_ntohl(hdr->ssrc);
229
/* Check payload type. */
230
if (check_pt && hdr->pt != ses->out_pt) {
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;
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) );
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) {
248
} else if (!seq_st.status.flag.bad) {
253
p_seq_st->status.value = seq_st.status.value;
254
p_seq_st->diff = seq_st.diff;
260
void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
262
sess->base_seq = seq;
264
sess->bad_seq = RTP_SEQ_MOD + 1;
269
void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
271
pjmedia_rtp_seq_restart(sess, seq);
273
sess->max_seq = (pj_uint16_t) (seq - 1);
274
sess->probation = MIN_SEQUENTIAL;
278
void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *sess,
280
pjmedia_rtp_status *seq_status)
282
pj_uint16_t udelta = (pj_uint16_t) (seq - sess->max_seq);
283
pjmedia_rtp_status st;
290
* Source is not valid until MIN_SEQUENTIAL packets with
291
* sequential sequence numbers have been received.
293
if (sess->probation) {
295
st.status.flag.probation = 1;
297
if (seq == sess->max_seq+ 1) {
298
/* packet is in sequence */
302
if (sess->probation == 0) {
303
st.status.flag.probation = 0;
309
st.status.flag.bad = 1;
310
if (seq == sess->max_seq)
311
st.status.flag.dup = 1;
313
st.status.flag.outorder = 1;
315
sess->probation = MIN_SEQUENTIAL - 1;
320
} else if (udelta == 0) {
322
st.status.flag.dup = 1;
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;
334
} else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
335
/* the sequence number made a very large jump */
336
if (seq == sess->bad_seq) {
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).
342
pjmedia_rtp_seq_restart(sess, seq);
343
st.status.flag.restart = 1;
344
st.status.flag.probation = 1;
348
sess->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
349
st.status.flag.bad = 1;
350
st.status.flag.outorder = 1;
353
/* old duplicate or reordered packet.
354
* Not necessarily bad packet (?)
356
st.status.flag.outorder = 1;
361
seq_status->diff = st.diff;
362
seq_status->status.value = st.status.value;