/** State for an RTP source */
struct rtp_source_t
{
- mtime_t expiry; /* inactivation date */
uint32_t ssrc;
+ uint32_t jitter; /* interarrival delay jitter estimate */
+ mtime_t last_rx; /* last received packet local timestamp */
+ uint32_t last_ts; /* last received packet RTP timestamp */
+
uint16_t bad_seq; /* tentatively next expected sequence for resync */
uint16_t max_seq; /* next expected sequence */
return NULL;
source->ssrc = ssrc;
+ source->jitter = 0;
source->max_seq = source->bad_seq = init_seq;
source->last_seq = init_seq - 1;
source->blocks = NULL;
free (source);
}
+static inline uint8_t rtp_ptype (const block_t *block)
+{
+ return block->p_buffer[1] & 0x7F;
+}
static inline uint16_t rtp_seq (const block_t *block)
{
return GetWBE (block->p_buffer + 2);
}
+static inline uint32_t rtp_timestamp (const block_t *block)
+{
+ assert (block->i_buffer >= 12);
+ return GetDWBE (block->p_buffer + 4);
+}
+
+static const struct rtp_pt_t *
+rtp_find_ptype (const rtp_session_t *session, rtp_source_t *source,
+ const block_t *block, void **pt_data)
+{
+ uint8_t ptype = rtp_ptype (block);
+
+ for (unsigned i = 0; i < session->ptc; i++)
+ {
+ if (session->ptv[i].number == ptype)
+ {
+ if (pt_data != NULL)
+ *pt_data = source->opaque[i];
+ return &session->ptv[i];
+ }
+ }
+ return NULL;
+}
+
/**
* Receives an RTP packet and queues it.
* @param demux VLC demux object
}
/* RTP source garbage collection */
- if (tmp->expiry < now)
+ if ((tmp->last_rx + (p_sys->timeout * CLOCK_FREQ)) < now)
{
rtp_source_destroy (demux, session, tmp);
if (--session->srcc > 0)
goto drop;
tab[session->srcc++] = src;
+ /* Cannot compute jitter yet */
}
+ else
+ {
+ const rtp_pt_t *pt = rtp_find_ptype (session, src, block, NULL);
+
+ if (pt != NULL)
+ {
+ /* Recompute jitter estimate.
+ * That is computed from the RTP timestamps and the system clock.
+ * It is independent of RTP sequence. */
+ uint32_t freq = pt->frequency;
+ uint32_t ts = rtp_timestamp (block);
+ int64_t d = ((now - src->last_rx) * freq) / CLOCK_FREQ;
+ d -= ts - src->last_ts;
+ if (d < 0) d = -d;
+ src->jitter += ((d - src->jitter) + 8) >> 4;
+ }
+ }
+ src->last_rx = now;
+ src->last_ts = rtp_timestamp (block);
+
+ /* Be optimistic for the first packet. Certain codec, such as Vorbis
+ * do not like loosing the first packet(s), so we cannot just wait
+ * for proper sequence synchronization. And we don't want to assume that
+ * the sender starts at seq=0 either. */
+ if (src->blocks == NULL)
+ src->max_seq = seq - p_sys->max_dropout;
/* Check sequence number */
/* NOTE: the sequence number is per-source,
block_t **pp = &src->blocks;
for (block_t *prev = *pp; prev != NULL; prev = *pp)
{
- if ((int16_t)(seq - rtp_seq (*pp)) < 0)
+ int16_t delta_seq = seq - rtp_seq (prev);
+ if (delta_seq < 0)
break;
+ if (delta_seq == 0)
+ goto drop; /* duplicate */
pp = &prev->p_next;
}
block->p_next = *pp;
src->last_seq = rtp_seq (block);
/* Match the payload type */
- const rtp_pt_t *pt = NULL;
- void *pt_data = NULL;
- const uint8_t ptype = block->p_buffer[1] & 0x7F;
-
- for (unsigned i = 0; i < session->ptc; i++)
- {
- if (session->ptv[i].number == ptype)
- {
- pt = &session->ptv[i];
- pt_data = src->opaque[i];
- break;
- }
- }
-
+ void *pt_data;
+ const rtp_pt_t *pt = rtp_find_ptype (session, src, block, &pt_data);
if (pt == NULL)
{
- msg_Dbg (demux, "ignoring unknown payload (%"PRIu8")", ptype);
+ msg_Dbg (demux, "ignoring unknown payload (%"PRIu8")",
+ rtp_ptype (block));
goto drop;
}
* Otherwise it would be impossible to compute consistent timestamps. */
/* FIXME: handle timestamp wrap properly */
/* TODO: sync multiple sources sanely... */
- const uint32_t timestamp = GetDWBE (block->p_buffer + 4);
+ const uint32_t timestamp = rtp_timestamp (block);
block->i_pts = UINT64_C(1) * CLOCK_FREQ * timestamp / pt->frequency;
/* CSRC count */