summaryrefslogtreecommitdiffstats
path: root/gst
diff options
context:
space:
mode:
Diffstat (limited to 'gst')
-rw-r--r--gst/rtpmanager/gstrtpjitterbuffer.c146
1 files changed, 109 insertions, 37 deletions
diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c
index 9bea78f2..2c070c21 100644
--- a/gst/rtpmanager/gstrtpjitterbuffer.c
+++ b/gst/rtpmanager/gstrtpjitterbuffer.c
@@ -146,6 +146,8 @@ struct _GstRtpJitterBufferPrivate
guint32 last_popped_seqnum;
/* the next expected seqnum */
guint32 next_seqnum;
+ /* last output time */
+ GstClockTime last_out_time;
/* state */
gboolean eos;
@@ -219,6 +221,8 @@ static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad);
/* sinkpad overrides */
static gboolean gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps);
+static gboolean gst_rtp_jitter_buffer_src_event (GstPad * pad,
+ GstEvent * event);
static gboolean gst_rtp_jitter_buffer_sink_event (GstPad * pad,
GstEvent * event);
static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad,
@@ -349,6 +353,8 @@ gst_rtp_jitter_buffer_init (GstRtpJitterBuffer * jitterbuffer,
GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_query));
gst_pad_set_getcaps_function (priv->srcpad,
GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps));
+ gst_pad_set_event_function (priv->srcpad,
+ GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_src_event));
priv->sinkpad =
gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_sink_template,
@@ -541,6 +547,7 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
priv->srcresult = GST_FLOW_OK;
gst_segment_init (&priv->segment, GST_FORMAT_TIME);
priv->last_popped_seqnum = -1;
+ priv->last_out_time = -1;
priv->next_seqnum = -1;
priv->clock_rate = -1;
priv->eos = FALSE;
@@ -646,19 +653,26 @@ gst_rtp_jitter_buffer_change_state (GstElement * element,
return ret;
}
-/**
- * Performs comparison 'b - a' with check for overflows.
- */
-static inline gint
-priv_compare_rtp_seq_lt (guint16 a, guint16 b)
+static gboolean
+gst_rtp_jitter_buffer_src_event (GstPad * pad, GstEvent * event)
{
- /* check if diff more than half of the 16bit range */
- if (abs (b - a) > (1 << 15)) {
- /* one of a/b has wrapped */
- return a - b;
- } else {
- return b - a;
+ gboolean ret = TRUE;
+ GstRtpJitterBuffer *jitterbuffer;
+ GstRtpJitterBufferPrivate *priv;
+
+ jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
+ priv = jitterbuffer->priv;
+
+ GST_DEBUG_OBJECT (jitterbuffer, "received %s", GST_EVENT_TYPE_NAME (event));
+
+ switch (GST_EVENT_TYPE (event)) {
+ default:
+ ret = gst_pad_push_event (priv->sinkpad, event);
+ break;
}
+ gst_object_unref (jitterbuffer);
+
+ return ret;
}
static gboolean
@@ -856,7 +870,8 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstBuffer * buffer)
/* let's check if this buffer is too late, we cannot accept packets with
* bigger seqnum than the one we already pushed. */
if (priv->last_popped_seqnum != -1) {
- if (priv_compare_rtp_seq_lt (priv->last_popped_seqnum, seqnum) < 0)
+ /* FIXME. isn't this supposed to be <= ? */
+ if (gst_rtp_buffer_compare_seqnum (priv->last_popped_seqnum, seqnum) < 0)
goto too_late;
}
@@ -988,7 +1003,10 @@ gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
GstBuffer *outbuf;
GstFlowReturn result;
guint16 seqnum;
+ guint32 next_seqnum;
GstClockTime timestamp, out_time;
+ gboolean discont = FALSE;
+ gint gap;
priv = jitterbuffer->priv;
@@ -996,10 +1014,9 @@ gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
again:
GST_DEBUG_OBJECT (jitterbuffer, "Peeking item");
while (TRUE) {
-
/* always wait if we are blocked */
if (!priv->blocked) {
- /* if we have a packet, we can grab it */
+ /* if we have a packet, we can exit the loop and grab it */
if (rtp_jitter_buffer_num_packets (priv->jbuf) > 0)
break;
/* no packets but we are EOS, do eos logic */
@@ -1018,21 +1035,43 @@ again:
* new buffer is available. The peeked buffer is valid for as long as we hold
* the jitterbuffer lock. */
outbuf = rtp_jitter_buffer_peek (priv->jbuf);
+
+ /* get the seqnum and the next expected seqnum */
seqnum = gst_rtp_buffer_get_seq (outbuf);
+ next_seqnum = priv->next_seqnum;
/* get the timestamp, this is already corrected for clock skew by the
* jitterbuffer */
timestamp = GST_BUFFER_TIMESTAMP (outbuf);
GST_DEBUG_OBJECT (jitterbuffer,
- "Peeked buffer #%d, timestamp %" GST_TIME_FORMAT ", now %d left",
- seqnum, GST_TIME_ARGS (timestamp),
+ "Peeked buffer #%d, expect #%d, timestamp %" GST_TIME_FORMAT
+ ", now %d left", seqnum, next_seqnum, GST_TIME_ARGS (timestamp),
rtp_jitter_buffer_num_packets (priv->jbuf));
/* apply our timestamp offset to the incomming buffer, this will be our output
* timestamp. */
out_time = apply_offset (jitterbuffer, timestamp);
+ /* get the gap between this and the previous packet. If we don't know the
+ * previous packet seqnum assume no gap. */
+ if (next_seqnum != -1) {
+ gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum);
+
+ /* if we have a packet that we already pushed or considered dropped, pop it
+ * off and get the next packet */
+ if (gap < 0) {
+ GST_DEBUG_OBJECT (jitterbuffer, "Old packet #%d, next #%d dropping",
+ seqnum, next_seqnum);
+ outbuf = rtp_jitter_buffer_pop (priv->jbuf);
+ gst_buffer_unref (outbuf);
+ goto again;
+ }
+ } else {
+ GST_DEBUG_OBJECT (jitterbuffer, "no next seqnum known, first packet");
+ gap = -1;
+ }
+
/* If we don't know what the next seqnum should be (== -1) we have to wait
* because it might be possible that we are not receiving this buffer in-order,
* a buffer with a lower seqnum could arrive later and we want to push that
@@ -1041,18 +1080,32 @@ again:
* determine if we have missing a packet. If we have a missing packet (which
* must be before this packet) we can wait for it until the deadline for this
* packet expires. */
- if ((priv->next_seqnum == -1 || priv->next_seqnum != seqnum)
- && out_time != -1) {
+ if (gap != 0 && out_time != -1) {
GstClockID id;
GstClockTime sync_time;
GstClockReturn ret;
GstClock *clock;
+ GstClockTime duration = -1;
- if (priv->next_seqnum != -1) {
- /* we expected next_seqnum but received something else, that's a gap */
+ if (gap > 0) {
+ /* we have a gap */
GST_WARNING_OBJECT (jitterbuffer,
- "Sequence number GAP detected: expected %d instead of %d",
- priv->next_seqnum, seqnum);
+ "Sequence number GAP detected: expected %d instead of %d (%d missing)",
+ next_seqnum, seqnum, gap);
+
+ if (priv->last_out_time != -1) {
+ GST_DEBUG_OBJECT (jitterbuffer,
+ "out_time %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (out_time), GST_TIME_ARGS (priv->last_out_time));
+ /* interpollate between the current time and the last time based on
+ * number of packets we are missing, this is the estimated duration
+ * for the missing packet based on equidistant packet spacing. */
+ duration = (out_time - priv->last_out_time) / (gap + 1);
+ GST_DEBUG_OBJECT (jitterbuffer, "duration %" GST_TIME_FORMAT,
+ GST_TIME_ARGS (duration));
+ /* add this duration to the timestamp of the last packet we pushed */
+ out_time = (priv->last_out_time + duration);
+ }
} else {
/* we don't know what the next_seqnum should be, wait for the last
* possible moment to push this buffer, maybe we get an earlier seqnum
@@ -1104,27 +1157,45 @@ again:
"Wait got unscheduled, will retry to push with new buffer");
goto again;
}
- /* Get new timestamp, latency might have changed */
+
+ /* we now timed out, this means we lost a packet or finished synchronizing
+ * on the first buffer. */
+ if (gap > 0) {
+ GstEvent *event;
+
+ /* we had a gap and thus we lost a packet. Creat an event for this. */
+ GST_DEBUG_OBJECT (jitterbuffer, "Packet #%d lost", next_seqnum);
+ priv->num_late++;
+ discont = TRUE;
+
+ /* create paket lost event */
+ event = gst_event_new_custom (GST_EVENT_CUSTOM_DOWNSTREAM,
+ gst_structure_new ("GstRTPPacketLost",
+ "seqnum", G_TYPE_UINT, (guint) next_seqnum,
+ "timestamp", G_TYPE_UINT64, out_time,
+ "duration", G_TYPE_UINT64, duration, NULL));
+ gst_pad_push_event (priv->srcpad, event);
+
+ /* update our expected next packet */
+ priv->last_popped_seqnum = next_seqnum;
+ priv->last_out_time = out_time;
+ priv->next_seqnum = (next_seqnum + 1) & 0xffff;
+ /* look for next packet */
+ goto again;
+ }
+
+ /* there was no known gap,just the first packet, exit the loop and push */
+ GST_DEBUG_OBJECT (jitterbuffer, "First packet #%d synced", seqnum);
+
+ /* get new timestamp, latency might have changed */
out_time = apply_offset (jitterbuffer, timestamp);
}
push_buffer:
+
/* when we get here we are ready to pop and push the buffer */
outbuf = rtp_jitter_buffer_pop (priv->jbuf);
- /* check if we are pushing something unexpected */
- if (priv->next_seqnum != -1 && priv->next_seqnum != seqnum) {
- gint dropped;
-
- /* calc number of missing packets, careful for wraparounds */
- dropped = priv_compare_rtp_seq_lt (priv->next_seqnum, seqnum);
-
- GST_DEBUG_OBJECT (jitterbuffer,
- "Pushing DISCONT after dropping %d (%d to %d)", dropped,
- priv->next_seqnum, seqnum);
-
- /* update stats */
- priv->num_late += dropped;
-
+ if (discont) {
/* set DISCONT flag when we missed a packet. */
outbuf = gst_buffer_make_metadata_writable (outbuf);
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT);
@@ -1136,6 +1207,7 @@ push_buffer:
/* now we are ready to push the buffer. Save the seqnum and release the lock
* so the other end can push stuff in the queue again. */
priv->last_popped_seqnum = seqnum;
+ priv->last_out_time = out_time;
priv->next_seqnum = (seqnum + 1) & 0xffff;
JBUF_UNLOCK (priv);