gst/rtpmanager/gstrtpjitterbuffer.c: Remove private version of a function that is in -base now.

Original commit message from CVS:
* gst/rtpmanager/gstrtpjitterbuffer.c:
(gst_rtp_jitter_buffer_init), (gst_rtp_jitter_buffer_flush_stop),
(gst_rtp_jitter_buffer_src_event), (gst_rtp_jitter_buffer_chain),
(gst_rtp_jitter_buffer_loop):
Remove private version of a function that is in -base now.
Add src event handler.
Rework the jitterbuffer pushing loop so that it can quickly react to
lost packets and instruct the depayloader of them. This can then be used
to implement error concealment data.
This commit is contained in:
Wim Taymans 2008-04-25 09:35:43 +00:00
parent 5fe12accc9
commit 120c55eb27
2 changed files with 121 additions and 37 deletions

View file

@ -1,3 +1,15 @@
2008-04-25 Wim Taymans <wim.taymans@collabora.co.uk>
* gst/rtpmanager/gstrtpjitterbuffer.c:
(gst_rtp_jitter_buffer_init), (gst_rtp_jitter_buffer_flush_stop),
(gst_rtp_jitter_buffer_src_event), (gst_rtp_jitter_buffer_chain),
(gst_rtp_jitter_buffer_loop):
Remove private version of a function that is in -base now.
Add src event handler.
Rework the jitterbuffer pushing loop so that it can quickly react to
lost packets and instruct the depayloader of them. This can then be used
to implement error concealment data.
2008-04-25 Wim Taymans <wim.taymans@collabora.co.uk> 2008-04-25 Wim Taymans <wim.taymans@collabora.co.uk>
* gst/rtpmanager/gstrtpsession.c: * gst/rtpmanager/gstrtpsession.c:

View file

@ -146,6 +146,8 @@ struct _GstRtpJitterBufferPrivate
guint32 last_popped_seqnum; guint32 last_popped_seqnum;
/* the next expected seqnum */ /* the next expected seqnum */
guint32 next_seqnum; guint32 next_seqnum;
/* last output time */
GstClockTime last_out_time;
/* state */ /* state */
gboolean eos; gboolean eos;
@ -219,6 +221,8 @@ static GstCaps *gst_rtp_jitter_buffer_getcaps (GstPad * pad);
/* sinkpad overrides */ /* sinkpad overrides */
static gboolean gst_jitter_buffer_sink_setcaps (GstPad * pad, GstCaps * caps); 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, static gboolean gst_rtp_jitter_buffer_sink_event (GstPad * pad,
GstEvent * event); GstEvent * event);
static GstFlowReturn gst_rtp_jitter_buffer_chain (GstPad * pad, 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_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_query));
gst_pad_set_getcaps_function (priv->srcpad, gst_pad_set_getcaps_function (priv->srcpad,
GST_DEBUG_FUNCPTR (gst_rtp_jitter_buffer_getcaps)); 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 = priv->sinkpad =
gst_pad_new_from_static_template (&gst_rtp_jitter_buffer_sink_template, 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; priv->srcresult = GST_FLOW_OK;
gst_segment_init (&priv->segment, GST_FORMAT_TIME); gst_segment_init (&priv->segment, GST_FORMAT_TIME);
priv->last_popped_seqnum = -1; priv->last_popped_seqnum = -1;
priv->last_out_time = -1;
priv->next_seqnum = -1; priv->next_seqnum = -1;
priv->clock_rate = -1; priv->clock_rate = -1;
priv->eos = FALSE; priv->eos = FALSE;
@ -646,19 +653,26 @@ gst_rtp_jitter_buffer_change_state (GstElement * element,
return ret; return ret;
} }
/** static gboolean
* Performs comparison 'b - a' with check for overflows. gst_rtp_jitter_buffer_src_event (GstPad * pad, GstEvent * event)
*/
static inline gint
priv_compare_rtp_seq_lt (guint16 a, guint16 b)
{ {
/* check if diff more than half of the 16bit range */ gboolean ret = TRUE;
if (abs (b - a) > (1 << 15)) { GstRtpJitterBuffer *jitterbuffer;
/* one of a/b has wrapped */ GstRtpJitterBufferPrivate *priv;
return a - b;
} else { jitterbuffer = GST_RTP_JITTER_BUFFER (gst_pad_get_parent (pad));
return b - a; 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 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 /* let's check if this buffer is too late, we cannot accept packets with
* bigger seqnum than the one we already pushed. */ * bigger seqnum than the one we already pushed. */
if (priv->last_popped_seqnum != -1) { 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; goto too_late;
} }
@ -988,7 +1003,10 @@ gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
GstBuffer *outbuf; GstBuffer *outbuf;
GstFlowReturn result; GstFlowReturn result;
guint16 seqnum; guint16 seqnum;
guint32 next_seqnum;
GstClockTime timestamp, out_time; GstClockTime timestamp, out_time;
gboolean discont = FALSE;
gint gap;
priv = jitterbuffer->priv; priv = jitterbuffer->priv;
@ -996,10 +1014,9 @@ gst_rtp_jitter_buffer_loop (GstRtpJitterBuffer * jitterbuffer)
again: again:
GST_DEBUG_OBJECT (jitterbuffer, "Peeking item"); GST_DEBUG_OBJECT (jitterbuffer, "Peeking item");
while (TRUE) { while (TRUE) {
/* always wait if we are blocked */ /* always wait if we are blocked */
if (!priv->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) if (rtp_jitter_buffer_num_packets (priv->jbuf) > 0)
break; break;
/* no packets but we are EOS, do eos logic */ /* 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 * new buffer is available. The peeked buffer is valid for as long as we hold
* the jitterbuffer lock. */ * the jitterbuffer lock. */
outbuf = rtp_jitter_buffer_peek (priv->jbuf); outbuf = rtp_jitter_buffer_peek (priv->jbuf);
/* get the seqnum and the next expected seqnum */
seqnum = gst_rtp_buffer_get_seq (outbuf); seqnum = gst_rtp_buffer_get_seq (outbuf);
next_seqnum = priv->next_seqnum;
/* get the timestamp, this is already corrected for clock skew by the /* get the timestamp, this is already corrected for clock skew by the
* jitterbuffer */ * jitterbuffer */
timestamp = GST_BUFFER_TIMESTAMP (outbuf); timestamp = GST_BUFFER_TIMESTAMP (outbuf);
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"Peeked buffer #%d, timestamp %" GST_TIME_FORMAT ", now %d left", "Peeked buffer #%d, expect #%d, timestamp %" GST_TIME_FORMAT
seqnum, GST_TIME_ARGS (timestamp), ", now %d left", seqnum, next_seqnum, GST_TIME_ARGS (timestamp),
rtp_jitter_buffer_num_packets (priv->jbuf)); rtp_jitter_buffer_num_packets (priv->jbuf));
/* apply our timestamp offset to the incomming buffer, this will be our output /* apply our timestamp offset to the incomming buffer, this will be our output
* timestamp. */ * timestamp. */
out_time = apply_offset (jitterbuffer, 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 /* 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, * 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 * 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 * 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 * must be before this packet) we can wait for it until the deadline for this
* packet expires. */ * packet expires. */
if ((priv->next_seqnum == -1 || priv->next_seqnum != seqnum) if (gap != 0 && out_time != -1) {
&& out_time != -1) {
GstClockID id; GstClockID id;
GstClockTime sync_time; GstClockTime sync_time;
GstClockReturn ret; GstClockReturn ret;
GstClock *clock; GstClock *clock;
GstClockTime duration = -1;
if (priv->next_seqnum != -1) { if (gap > 0) {
/* we expected next_seqnum but received something else, that's a gap */ /* we have a gap */
GST_WARNING_OBJECT (jitterbuffer, GST_WARNING_OBJECT (jitterbuffer,
"Sequence number GAP detected: expected %d instead of %d", "Sequence number GAP detected: expected %d instead of %d (%d missing)",
priv->next_seqnum, seqnum); 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 { } else {
/* we don't know what the next_seqnum should be, wait for the last /* 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 * 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"); "Wait got unscheduled, will retry to push with new buffer");
goto again; 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); out_time = apply_offset (jitterbuffer, timestamp);
} }
push_buffer: push_buffer:
/* when we get here we are ready to pop and push the buffer */ /* when we get here we are ready to pop and push the buffer */
outbuf = rtp_jitter_buffer_pop (priv->jbuf); outbuf = rtp_jitter_buffer_pop (priv->jbuf);
/* check if we are pushing something unexpected */ if (discont) {
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;
/* set DISCONT flag when we missed a packet. */ /* set DISCONT flag when we missed a packet. */
outbuf = gst_buffer_make_metadata_writable (outbuf); outbuf = gst_buffer_make_metadata_writable (outbuf);
GST_BUFFER_FLAG_SET (outbuf, GST_BUFFER_FLAG_DISCONT); 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 /* 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. */ * so the other end can push stuff in the queue again. */
priv->last_popped_seqnum = seqnum; priv->last_popped_seqnum = seqnum;
priv->last_out_time = out_time;
priv->next_seqnum = (seqnum + 1) & 0xffff; priv->next_seqnum = (seqnum + 1) & 0xffff;
JBUF_UNLOCK (priv); JBUF_UNLOCK (priv);