*
* This library is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
- * as published by the Free Software Foundation; either version 2.0
+ * as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
*
* This library is distributed in the hope that it will be useful,
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
- * You should have received a copy of the GNU Lesser General Public
+ * You should have received a copy of the GNU General Public
* License along with this library; if not, write to the Free Software
* Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA
****************************************************************************/
#include <assert.h>
#include <errno.h>
-#include <vlc/vlc.h>
+#include <vlc_common.h>
#include <vlc_demux.h>
#include "rtp.h"
mtime_t last_rx; /* last received packet local timestamp */
uint32_t last_ts; /* last received packet RTP timestamp */
+ uint32_t ref_rtp; /* sender RTP timestamp reference */
+ mtime_t ref_ntp; /* sender NTP timestamp reference */
+
uint16_t bad_seq; /* tentatively next expected sequence for resync */
uint16_t max_seq; /* next expected sequence */
source->ssrc = ssrc;
source->jitter = 0;
+ source->ref_rtp = 0;
+ /* TODO: use VLC_TS_0, but VLC does not like negative PTS at the moment */
+ source->ref_ntp = UINT64_C (1) << 62;
source->max_seq = source->bad_seq = init_seq;
source->last_seq = init_seq - 1;
source->blocks = NULL;
}
/* RTP source garbage collection */
- if ((tmp->last_rx + (p_sys->timeout * CLOCK_FREQ)) < now)
+ if ((tmp->last_rx + p_sys->timeout) < now)
{
rtp_source_destroy (demux, session, tmp);
if (--session->srcc > 0)
}
}
src->last_rx = now;
+ block->i_pts = now; /* store reception time until dequeued */
src->last_ts = rtp_timestamp (block);
/* Check sequence number */
/* NOTE: the sequence number is per-source,
* but is independent from the payload type. */
- int delta_seq = seq - src->max_seq;
+ int16_t delta_seq = seq - src->max_seq;
if ((delta_seq > 0) ? (delta_seq > p_sys->max_dropout)
: (-delta_seq > p_sys->max_misorder))
{
block_t **pp = &src->blocks;
for (block_t *prev = *pp; prev != NULL; prev = *pp)
{
- int delta_seq = seq - rtp_seq (prev);
+ int16_t delta_seq = seq - rtp_seq (prev);
if (delta_seq < 0)
break;
if (delta_seq == 0)
{
- msg_Warn (demux, "duplicate packet (sequence: %"PRIu16")",
- seq);
+ msg_Dbg (demux, "duplicate packet (sequence: %"PRIu16")", seq);
goto drop; /* duplicate */
}
pp = &prev->p_next;
}
+static void rtp_decode (demux_t *, const rtp_session_t *, rtp_source_t *);
+
+/**
+ * Dequeues RTP packets and pass them to decoder. Not cancellation-safe(?).
+ * A packet is decoded if it is the next in sequence order, or if we have
+ * given up waiting on the missing packets (time out) from the last one
+ * already decoded.
+ *
+ * @param demux VLC demux object
+ * @param session RTP session receiving the packet
+ * @param deadlinep pointer to deadline to call rtp_dequeue() again
+ * @return true if the buffer is not empty, false otherwise.
+ * In the later case, *deadlinep is undefined.
+ */
+bool rtp_dequeue (demux_t *demux, const rtp_session_t *session,
+ mtime_t *restrict deadlinep)
+{
+ mtime_t now = mdate ();
+ bool pending = false;
+
+ *deadlinep = INT64_MAX;
+
+ for (unsigned i = 0, max = session->srcc; i < max; i++)
+ {
+ rtp_source_t *src = session->srcv[i];
+ block_t *block;
+
+ /* Because of IP packet delay variation (IPDV), we need to guesstimate
+ * how long to wait for a missing packet in the RTP sequence
+ * (see RFC3393 for background on IPDV).
+ *
+ * This situation occurs if a packet got lost, or if the network has
+ * re-ordered packets. Unfortunately, the MSL is 2 minutes, orders of
+ * magnitude too long for multimedia. We need a trade-off.
+ * If we underestimated IPDV, we may have to discard valid but late
+ * packets. If we overestimate it, we will either cause too much
+ * delay, or worse, underflow our downstream buffers, as we wait for
+ * definitely a lost packets.
+ *
+ * The rest of the "de-jitter buffer" work is done by the internal
+ * LibVLC E/S-out clock synchronization. Here, we need to bother about
+ * re-ordering packets, as decoders can't cope with mis-ordered data.
+ */
+ while (((block = src->blocks)) != NULL)
+ {
+ if ((int16_t)(rtp_seq (block) - (src->last_seq + 1)) <= 0)
+ { /* Next (or earlier) block ready, no need to wait */
+ rtp_decode (demux, session, src);
+ continue;
+ }
+
+ /* Wait for 3 times the inter-arrival delay variance (about 99.7%
+ * match for random gaussian jitter).
+ */
+ mtime_t deadline;
+ const rtp_pt_t *pt = rtp_find_ptype (session, src, block, NULL);
+ if (pt)
+ deadline = CLOCK_FREQ * 3 * src->jitter / pt->frequency;
+ else
+ deadline = 0; /* no jitter estimate with no frequency :( */
+
+ /* Make sure we wait at least for 25 msec */
+ if (deadline < (CLOCK_FREQ / 40))
+ deadline = CLOCK_FREQ / 40;
+
+ /* Additionnaly, we implicitly wait for the packetization time
+ * multiplied by the number of missing packets. block is the first
+ * non-missing packet (lowest sequence number). We have no better
+ * estimated time of arrival, as we do not know the RTP timestamp
+ * of not yet received packets. */
+ deadline += block->i_pts;
+ if (now >= deadline)
+ {
+ rtp_decode (demux, session, src);
+ continue;
+ }
+ if (*deadlinep > deadline)
+ *deadlinep = deadline;
+ pending = true; /* packet pending in buffer */
+ break;
+ }
+ }
+ return pending;
+}
+
+/**
+ * Dequeues all RTP packets and pass them to decoder. Not cancellation-safe(?).
+ * This function can be used when the packet source is known not to reorder.
+ */
+void rtp_dequeue_force (demux_t *demux, const rtp_session_t *session)
+{
+ for (unsigned i = 0, max = session->srcc; i < max; i++)
+ {
+ rtp_source_t *src = session->srcv[i];
+ block_t *block;
+
+ while (((block = src->blocks)) != NULL)
+ rtp_decode (demux, session, src);
+ }
+}
+
+/**
+ * Decodes one RTP packet.
+ */
static void
rtp_decode (demux_t *demux, const rtp_session_t *session, rtp_source_t *src)
{
if (delta_seq != 0)
{
if (delta_seq >= 0x8000)
- { /* Unrecoverable if later packets have already been dequeued */
- msg_Warn (demux, "ignoring late packet (sequence: %u)",
+ { /* Trash too late packets (and PIM Assert duplicates) */
+ msg_Dbg (demux, "ignoring late packet (sequence: %"PRIu16")",
rtp_seq (block));
goto drop;
}
+ msg_Warn (demux, "%"PRIu16" packet(s) lost", delta_seq);
block->i_flags |= BLOCK_FLAG_DISCONTINUITY;
}
src->last_seq = rtp_seq (block);
* DTS is unknown. Also, while the clock frequency depends on the payload
* format, a single source MUST only use payloads of a chosen frequency.
* Otherwise it would be impossible to compute consistent timestamps. */
- /* FIXME: handle timestamp wrap properly */
- /* TODO: inter-medias/sessions sync (using RTCP-SR) */
const uint32_t timestamp = rtp_timestamp (block);
- block->i_pts = UINT64_C(1) * CLOCK_FREQ * timestamp / pt->frequency;
+ block->i_pts = src->ref_ntp
+ + CLOCK_FREQ * (int32_t)(timestamp - src->ref_rtp) / pt->frequency;
+ /* TODO: proper inter-medias/sessions sync (using RTCP-SR) */
+ src->ref_ntp = block->i_pts;
+ src->ref_rtp = timestamp;
/* CSRC count */
size_t skip = 12u + (block->p_buffer[0] & 0x0F) * 4;
drop:
block_Release (block);
}
-
-
-/**
- * Dequeues an RTP packet and pass it to decoder. Not cancellation-safe(?).
- *
- * @param demux VLC demux object
- * @param session RTP session receiving the packet
- * @param deadlinep pointer to deadline to call rtp_dequeue() again
- * @return true if the buffer is not empty, false otherwise.
- * In the later case, *deadlinep is undefined.
- */
-bool rtp_dequeue (demux_t *demux, const rtp_session_t *session,
- mtime_t *restrict deadlinep)
-{
- mtime_t now = mdate ();
- bool pending = false;
-
- for (unsigned i = 0, max = session->srcc; i < max; i++)
- {
- rtp_source_t *src = session->srcv[i];
- block_t *block;
-
- /* Because of IP packet delay variation (IPDV), we need to guesstimate
- * how long to wait for a missing packet in the RTP sequence
- * (see RFC3393 for background on IPDV).
- *
- * This situation occurs if a packet got lost, or if the network has
- * re-ordered packets. Unfortunately, the MSL is 2 minutes, orders of
- * magnitude too long for multimedia. We need a tradeoff.
- * If we underestimated IPDV, we may have to discard valid but late
- * packets. If we overestimate it, we will either cause too much
- * delay, or worse, underflow our downstream buffers, as we wait for
- * definitely a lost packets.
- *
- * The rest of the "de-jitter buffer" work is done by the interval
- * LibVLC E/S-out clock synchronization. Here, we need to bother about
- * re-ordering packets, as decoders can't cope with mis-ordered data.
- */
- while (((block = src->blocks)) != NULL)
- {
- if ((int16_t)(rtp_seq (block) - (src->last_seq + 1)) <= 0)
- { /* Next (or earlier) block ready, no need to wait */
- rtp_decode (demux, session, src);
- continue;
- }
-
- /* Wait for 3 times the inter-arrival delay variance (about 99.7%
- * match for random gaussian jitter). Additionnaly, we implicitly
- * wait for misordering times the packetization time.
- */
- mtime_t deadline = src->last_rx;
- const rtp_pt_t *pt = rtp_find_ptype (session, src, block, NULL);
- if (pt)
- deadline += UINT64_C(3) * CLOCK_FREQ * src->jitter
- / pt->frequency;
-
- if (now >= deadline)
- {
- rtp_decode (demux, session, src);
- continue;
- }
- if (*deadlinep > deadline)
- *deadlinep = deadline;
- pending = true; /* packet pending in buffer */
- break;
- }
- }
- return pending;
-}