3 * @brief RTP session handling
5 /*****************************************************************************
6 * Copyright © 2008 Rémi Denis-Courmont
8 * This library is free software; you can redistribute it and/or
9 * modify it under the terms of the GNU General Public License
10 * as published by the Free Software Foundation; either version 2.0
11 * of the License, or (at your option) any later version.
13 * This library is distributed in the hope that it will be useful,
14 * but WITHOUT ANY WARRANTY; without even the implied warranty of
15 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
16 * GNU General Public License for more details.
18 * You should have received a copy of the GNU Lesser General Public
19 * License along with this library; if not, write to the Free Software
20 * Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
21 ****************************************************************************/
32 #include <vlc_demux.h>
36 typedef struct rtp_source_t rtp_source_t;
38 /** State for a RTP session: */
48 rtp_source_create (demux_t *, const rtp_session_t *, uint32_t, uint16_t);
50 rtp_source_destroy (demux_t *, const rtp_session_t *, rtp_source_t *);
52 static void rtp_decode (demux_t *, const rtp_session_t *, rtp_source_t *);
55 * Creates a new RTP session.
58 rtp_session_create (demux_t *demux)
60 rtp_session_t *session = malloc (sizeof (*session));
75 * Destroys an RTP session.
77 void rtp_session_destroy (demux_t *demux, rtp_session_t *session)
79 for (unsigned i = 0; i < session->srcc; i++)
80 rtp_source_destroy (demux, session, session->srcv[i]);
88 static void *no_init (demux_t *demux)
93 static void no_destroy (demux_t *demux, void *opaque)
95 (void)demux; (void)opaque;
98 static void no_decode (demux_t *demux, void *opaque, block_t *block)
100 (void)demux; (void)opaque;
101 block_Release (block);
105 * Adds a payload type to an RTP session.
107 int rtp_add_type (demux_t *demux, rtp_session_t *ses, const rtp_pt_t *pt)
111 msg_Err (demux, "cannot change RTP payload formats during session");
115 rtp_pt_t *ppt = realloc (ses->ptv, (ses->ptc + 1) * sizeof (rtp_pt_t));
122 ppt->init = pt->init ? pt->init : no_init;
123 ppt->destroy = pt->destroy ? pt->destroy : no_destroy;
124 ppt->decode = pt->decode ? pt->decode : no_decode;
125 ppt->frequency = pt->frequency;
126 ppt->number = pt->number;
128 assert (ppt->frequency > 0); /* SIGFPE! */
133 /** State for an RTP source */
136 mtime_t expiry; /* inactivation date */
138 uint16_t bad_seq; /* tentatively next expected sequence for resync */
139 uint16_t max_seq; /* next expected sequence */
141 block_t *blocks; /* re-ordered blocks queue */
142 void *opaque[0]; /* Per-source prviate payload data */
146 * Initializes a new RTP source within an RTP session.
148 static rtp_source_t *
149 rtp_source_create (demux_t *demux, const rtp_session_t *session,
150 uint32_t ssrc, uint16_t init_seq)
152 rtp_source_t *source;
154 source = malloc (sizeof (*source) + (sizeof (void *) * session->ptc));
159 source->max_seq = source->bad_seq = init_seq;
160 source->blocks = NULL;
162 /* Initializes all payload */
163 for (unsigned i = 0; i < session->ptc; i++)
164 source->opaque[i] = session->ptv[i].init (demux);
166 msg_Dbg (demux, "added RTP source (%08x)", ssrc);
172 * Destroys an RTP source and its associated streams.
175 rtp_source_destroy (demux_t *demux, const rtp_session_t *session,
176 rtp_source_t *source)
178 msg_Dbg (demux, "removing RTP source (%08x)", source->ssrc);
180 for (unsigned i = 0; i < session->ptc; i++)
181 session->ptv[i].destroy (demux, source->opaque[i]);
182 block_ChainRelease (source->blocks);
187 static inline uint16_t rtp_seq (const block_t *block)
189 assert (block->i_buffer >= 4);
190 return GetWBE (block->p_buffer + 2);
194 * Receives an RTP packet and queues it.
195 * @param demux VLC demux object
196 * @param session RTP session receiving the packet
197 * @param block RTP packet including the RTP header
200 rtp_receive (demux_t *demux, rtp_session_t *session, block_t *block)
202 demux_sys_t *p_sys = demux->p_sys;
204 /* RTP header sanity checks (see RFC 3550) */
205 if (block->i_buffer < 12)
207 if ((block->p_buffer[0] >> 6 ) != 2) /* RTP version number */
210 /* Remove padding if present */
211 if (block->p_buffer[0] & 0x20)
213 uint8_t padding = block->p_buffer[block->i_buffer - 1];
214 if ((padding == 0) || (block->i_buffer < (12u + padding)))
215 goto drop; /* illegal value */
217 block->i_buffer -= padding;
220 mtime_t now = mdate ();
221 rtp_source_t *src = NULL;
222 const uint16_t seq = GetWBE (block->p_buffer + 2);
223 const uint32_t ssrc = GetDWBE (block->p_buffer + 8);
225 /* In most case, we know this source already */
226 for (unsigned i = 0, max = session->srcc; i < max; i++)
228 rtp_source_t *tmp = session->srcv[i];
229 if (tmp->ssrc == ssrc)
235 /* RTP source garbage collection */
236 if (tmp->expiry < now)
238 rtp_source_destroy (demux, session, tmp);
239 if (--session->srcc > 0)
240 session->srcv[i] = session->srcv[session->srcc - 1];
247 if (session->srcc >= p_sys->max_src)
249 msg_Warn (demux, "too many RTP sessions");
254 tab = realloc (session->srcv, (session->srcc + 1) * sizeof (*tab));
259 src = rtp_source_create (demux, session, ssrc, seq);
263 tab[session->srcc++] = src;
266 /* Check sequence number */
267 /* NOTE: the sequence number is per-source,
268 * but is independent from the payload type. */
269 uint16_t delta_seq = seq - (src->max_seq + 1);
270 if ((delta_seq < 0x8000) ? (delta_seq > p_sys->max_dropout)
271 : ((65535 - delta_seq) > p_sys->max_misorder))
273 msg_Dbg (demux, "sequence discontinuity (got: %u, expected: %u)",
274 seq, (src->max_seq + 1) & 0xffff);
275 if (seq == ((src->bad_seq + 1) & 0xffff))
277 src->max_seq = src->bad_seq = seq;
278 msg_Warn (demux, "sequence resynchronized");
279 block_ChainRelease (src->blocks);
289 /* Queues the block in sequence order,
290 * hence there is a single queue for all payload types. */
291 block_t **pp = &src->blocks;
292 for (block_t *prev = *pp; prev != NULL; prev = *pp)
294 if ((int16_t)(seq - rtp_seq (*pp)) >= 0)
301 rtp_decode (demux, session, src);
305 block_Release (block);
310 rtp_decode (demux_t *demux, const rtp_session_t *session, rtp_source_t *src)
312 block_t *block = src->blocks;
314 /* Buffer underflow? */
315 if (!block || !block->p_next || !block->p_next->p_next)
317 /* TODO: use time rather than packet counts for buffer measurement */
318 src->blocks = block->p_next;
319 block->p_next = NULL;
321 /* Match the payload type */
322 const rtp_pt_t *pt = NULL;
323 void *pt_data = NULL;
324 const uint8_t ptype = block->p_buffer[1] & 0x7F;
326 for (unsigned i = 0; i < session->ptc; i++)
328 if (session->ptv[i].number == ptype)
330 pt = &session->ptv[i];
331 pt_data = src->opaque[i];
338 msg_Dbg (demux, "ignoring unknown payload (%"PRIu8")", ptype);
342 /* Computes the PTS from the RTP timestamp and payload RTP frequency.
343 * DTS is unknown. Also, while the clock frequency depends on the payload
344 * format, a single source MUST only use payloads of a chosen frequency.
345 * Otherwise it would be impossible to compute consistent timestamps. */
346 /* FIXME: handle timestamp wrap properly */
347 /* TODO: sync multiple sources sanely... */
348 const uint32_t timestamp = GetDWBE (block->p_buffer + 4);
349 block->i_pts = UINT64_C(1) * CLOCK_FREQ * timestamp / pt->frequency;
350 //msg_Dbg (demux, "pts = %"PRIu64, block->i_pts);
353 size_t skip = 12u + (block->p_buffer[0] & 0x0F) * 4;
355 /* Extension header (ignored for now) */
356 if (block->p_buffer[0] & 0x10)
359 if (block->i_buffer < skip)
362 skip += 4 * GetWBE (block->p_buffer + skip - 2);
365 if (block->i_buffer < skip)
368 block->p_buffer += skip;
369 block->i_buffer -= skip;
371 pt->decode (demux, pt_data, block);
375 block_Release (block);