blob: 5a19c37e4e2762f0597bda4b83b33f65d4fa7862 [file] [log] [blame]
Tristan Matthews0a329cc2013-07-17 13:20:14 -04001/* $Id$ */
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
38static void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *seq_ctrl,
39 pj_uint16_t seq);
40
41
42PJ_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
88PJ_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
116PJ_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 /* Update timestamp */
122 ses->out_hdr.ts = pj_htonl(pj_ntohl(ses->out_hdr.ts)+ts_len);
123
124 /* If payload_len is zero, bail out.
125 * This is a clock frame; we're not really transmitting anything.
126 */
127 if (payload_len == 0)
128 return PJ_SUCCESS;
129
130 /* Update session. */
131 ses->out_extseq++;
132
133 /* Create outgoing header. */
134 ses->out_hdr.pt = (pj_uint8_t) ((pt == -1) ? ses->out_pt : pt);
135 ses->out_hdr.m = (pj_uint16_t) m;
136 ses->out_hdr.seq = pj_htons( (pj_uint16_t) ses->out_extseq);
137
138 /* Return values */
139 *rtphdr = &ses->out_hdr;
140 *hdrlen = sizeof(pjmedia_rtp_hdr);
141
142 return PJ_SUCCESS;
143}
144
145
146PJ_DEF(pj_status_t) pjmedia_rtp_decode_rtp( pjmedia_rtp_session *ses,
147 const void *pkt, int pkt_len,
148 const pjmedia_rtp_hdr **hdr,
149 const void **payload,
150 unsigned *payloadlen)
151{
152 int offset;
153
154 PJ_UNUSED_ARG(ses);
155
156 /* Assume RTP header at the start of packet. We'll verify this later. */
157 *hdr = (pjmedia_rtp_hdr*)pkt;
158
159 /* Check RTP header sanity. */
160 if ((*hdr)->v != RTP_VERSION) {
161 return PJMEDIA_RTP_EINVER;
162 }
163
164 /* Payload is located right after header plus CSRC */
165 offset = sizeof(pjmedia_rtp_hdr) + ((*hdr)->cc * sizeof(pj_uint32_t));
166
167 /* Adjust offset if RTP extension is used. */
168 if ((*hdr)->x) {
169 pjmedia_rtp_ext_hdr *ext = (pjmedia_rtp_ext_hdr*)
170 (((pj_uint8_t*)pkt) + offset);
171 offset += ((pj_ntohs(ext->length)+1) * sizeof(pj_uint32_t));
172 }
173
174 /* Check that offset is less than packet size */
175 if (offset > pkt_len)
176 return PJMEDIA_RTP_EINLEN;
177
178 /* Find and set payload. */
179 *payload = ((pj_uint8_t*)pkt) + offset;
180 *payloadlen = pkt_len - offset;
181
182 /* Remove payload padding if any */
183 if ((*hdr)->p && *payloadlen > 0) {
184 pj_uint8_t pad_len;
185
186 pad_len = ((pj_uint8_t*)(*payload))[*payloadlen - 1];
187 if (pad_len <= *payloadlen)
188 *payloadlen -= pad_len;
189 }
190
191 return PJ_SUCCESS;
192}
193
194
195PJ_DEF(void) pjmedia_rtp_session_update( pjmedia_rtp_session *ses,
196 const pjmedia_rtp_hdr *hdr,
197 pjmedia_rtp_status *p_seq_st)
198{
199 pjmedia_rtp_session_update2(ses, hdr, p_seq_st, PJ_TRUE);
200}
201
202PJ_DEF(void) pjmedia_rtp_session_update2( pjmedia_rtp_session *ses,
203 const pjmedia_rtp_hdr *hdr,
204 pjmedia_rtp_status *p_seq_st,
205 pj_bool_t check_pt)
206{
207 pjmedia_rtp_status seq_st;
208
209 /* for now check_pt MUST be either PJ_TRUE or PJ_FALSE.
210 * In the future we might change check_pt from boolean to
211 * unsigned integer to accommodate more flags.
212 */
213 pj_assert(check_pt==PJ_TRUE || check_pt==PJ_FALSE);
214
215 /* Init status */
216 seq_st.status.value = 0;
217 seq_st.diff = 0;
218
219 /* Check SSRC. */
220 if (ses->peer_ssrc == 0) ses->peer_ssrc = pj_ntohl(hdr->ssrc);
221
222 if (pj_ntohl(hdr->ssrc) != ses->peer_ssrc) {
223 seq_st.status.flag.badssrc = 1;
224 ses->peer_ssrc = pj_ntohl(hdr->ssrc);
225 }
226
227 /* Check payload type. */
228 if (check_pt && hdr->pt != ses->out_pt) {
229 if (p_seq_st) {
230 p_seq_st->status.value = seq_st.status.value;
231 p_seq_st->status.flag.bad = 1;
232 p_seq_st->status.flag.badpt = 1;
233 }
234 return;
235 }
236
237 /* Initialize sequence number on first packet received. */
238 if (ses->received == 0)
239 pjmedia_rtp_seq_init( &ses->seq_ctrl, pj_ntohs(hdr->seq) );
240
241 /* Check sequence number to see if remote session has been restarted. */
242 pjmedia_rtp_seq_update( &ses->seq_ctrl, pj_ntohs(hdr->seq), &seq_st);
243 if (seq_st.status.flag.restart) {
244 ++ses->received;
245
246 } else if (!seq_st.status.flag.bad) {
247 ++ses->received;
248 }
249
250 if (p_seq_st) {
251 p_seq_st->status.value = seq_st.status.value;
252 p_seq_st->diff = seq_st.diff;
253 }
254}
255
256
257
258void pjmedia_rtp_seq_restart(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
259{
260 sess->base_seq = seq;
261 sess->max_seq = seq;
262 sess->bad_seq = RTP_SEQ_MOD + 1;
263 sess->cycles = 0;
264}
265
266
267void pjmedia_rtp_seq_init(pjmedia_rtp_seq_session *sess, pj_uint16_t seq)
268{
269 pjmedia_rtp_seq_restart(sess, seq);
270
271 sess->max_seq = (pj_uint16_t) (seq - 1);
272 sess->probation = MIN_SEQUENTIAL;
273}
274
275
276void pjmedia_rtp_seq_update( pjmedia_rtp_seq_session *sess,
277 pj_uint16_t seq,
278 pjmedia_rtp_status *seq_status)
279{
280 pj_uint16_t udelta = (pj_uint16_t) (seq - sess->max_seq);
281 pjmedia_rtp_status st;
282
283 /* Init status */
284 st.status.value = 0;
285 st.diff = 0;
286
287 /*
288 * Source is not valid until MIN_SEQUENTIAL packets with
289 * sequential sequence numbers have been received.
290 */
291 if (sess->probation) {
292
293 st.status.flag.probation = 1;
294
295 if (seq == sess->max_seq+ 1) {
296 /* packet is in sequence */
297 st.diff = 1;
298 sess->probation--;
299 sess->max_seq = seq;
300 if (sess->probation == 0) {
301 st.status.flag.probation = 0;
302 }
303 } else {
304
305 st.diff = 0;
306
307 st.status.flag.bad = 1;
308 if (seq == sess->max_seq)
309 st.status.flag.dup = 1;
310 else
311 st.status.flag.outorder = 1;
312
313 sess->probation = MIN_SEQUENTIAL - 1;
314 sess->max_seq = seq;
315 }
316
317
318 } else if (udelta == 0) {
319
320 st.status.flag.dup = 1;
321
322 } else if (udelta < MAX_DROPOUT) {
323 /* in order, with permissible gap */
324 if (seq < sess->max_seq) {
325 /* Sequence number wrapped - count another 64K cycle. */
326 sess->cycles += RTP_SEQ_MOD;
327 }
328 sess->max_seq = seq;
329
330 st.diff = udelta;
331
332 } else if (udelta <= (RTP_SEQ_MOD - MAX_MISORDER)) {
333 /* the sequence number made a very large jump */
334 if (seq == sess->bad_seq) {
335 /*
336 * Two sequential packets -- assume that the other side
337 * restarted without telling us so just re-sync
338 * (i.e., pretend this was the first packet).
339 */
340 pjmedia_rtp_seq_restart(sess, seq);
341 st.status.flag.restart = 1;
342 st.status.flag.probation = 1;
343 st.diff = 1;
344 }
345 else {
346 sess->bad_seq = (seq + 1) & (RTP_SEQ_MOD-1);
347 st.status.flag.bad = 1;
348 st.status.flag.outorder = 1;
349 }
350 } else {
351 /* old duplicate or reordered packet.
352 * Not necessarily bad packet (?)
353 */
354 st.status.flag.outorder = 1;
355 }
356
357
358 if (seq_status) {
359 seq_status->diff = st.diff;
360 seq_status->status.value = st.status.value;
361 }
362}
363
364