jitterbuffer: operate on DTS

Make the jitterbuffer schedule the timeouts based on the DTS instead
of the PTS. This makes it all smoother with reordered frames and gives
the decoder time to reorder the frames in time.
This commit is contained in:
Wim Taymans 2013-08-01 11:29:32 +02:00
parent 80c5934290
commit f864131227
2 changed files with 52 additions and 40 deletions

View file

@ -148,6 +148,7 @@ struct _GstRtpJitterBufferPrivate
guint32 next_seqnum; guint32 next_seqnum;
/* last output time */ /* last output time */
GstClockTime last_out_time; GstClockTime last_out_time;
GstClockTime last_out_dts;
GstClockTime last_out_pts; GstClockTime last_out_pts;
/* the next expected seqnum we receive */ /* the next expected seqnum we receive */
guint32 next_in_seqnum; guint32 next_in_seqnum;
@ -731,7 +732,7 @@ gst_rtp_jitter_buffer_set_active (GstRtpJitterBuffer * jbuf, gboolean active,
} }
if ((head = rtp_jitter_buffer_peek (priv->jbuf))) { if ((head = rtp_jitter_buffer_peek (priv->jbuf))) {
/* head buffer timestamp and offset gives our output time */ /* head buffer timestamp and offset gives our output time */
last_out = GST_BUFFER_TIMESTAMP (head) + priv->ts_offset; last_out = GST_BUFFER_DTS (head) + priv->ts_offset;
} else { } else {
/* use last known time when the buffer is empty */ /* use last known time when the buffer is empty */
last_out = priv->last_out_time; last_out = priv->last_out_time;
@ -897,6 +898,7 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
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->last_out_time = -1;
priv->last_out_dts = -1;
priv->last_out_pts = -1; priv->last_out_pts = -1;
priv->next_seqnum = -1; priv->next_seqnum = -1;
priv->next_in_seqnum = -1; priv->next_in_seqnum = -1;
@ -1276,7 +1278,7 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
GstRtpJitterBufferPrivate *priv; GstRtpJitterBufferPrivate *priv;
guint16 seqnum; guint16 seqnum;
GstFlowReturn ret = GST_FLOW_OK; GstFlowReturn ret = GST_FLOW_OK;
GstClockTime timestamp; GstClockTime dts, pts;
guint64 latency_ts; guint64 latency_ts;
gboolean tail; gboolean tail;
gint percent = -1; gint percent = -1;
@ -1294,18 +1296,24 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
seqnum = gst_rtp_buffer_get_seq (&rtp); seqnum = gst_rtp_buffer_get_seq (&rtp);
gst_rtp_buffer_unmap (&rtp); gst_rtp_buffer_unmap (&rtp);
/* take the timestamp of the buffer. This is the time when the packet was /* make sure we have PTS and DTS set */
pts = GST_BUFFER_PTS (buffer);
dts = GST_BUFFER_DTS (buffer);
if (dts == -1)
dts = pts;
else if (pts == -1)
pts = dts;
/* take the DTS of the buffer. This is the time when the packet was
* received and is used to calculate jitter and clock skew. We will adjust * received and is used to calculate jitter and clock skew. We will adjust
* this timestamp with the smoothed value after processing it in the * this PTS with the smoothed value after processing it in the
* jitterbuffer. */ * jitterbuffer and assign it as the PTS. */
timestamp = GST_BUFFER_TIMESTAMP (buffer);
/* bring to running time */ /* bring to running time */
timestamp = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME, dts = gst_segment_to_running_time (&priv->segment, GST_FORMAT_TIME, dts);
timestamp);
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"Received packet #%d at time %" GST_TIME_FORMAT, seqnum, "Received packet #%d at time %" GST_TIME_FORMAT, seqnum,
GST_TIME_ARGS (timestamp)); GST_TIME_ARGS (dts));
JBUF_LOCK_CHECK (priv, out_flushing); JBUF_LOCK_CHECK (priv, out_flushing);
@ -1409,13 +1417,15 @@ gst_rtp_jitter_buffer_chain (GstPad * pad, GstObject * parent,
} }
/* we need to make the metadata writable before pushing it in the jitterbuffer /* we need to make the metadata writable before pushing it in the jitterbuffer
* because the jitterbuffer will update the timestamp */ * because the jitterbuffer will update the PTS */
buffer = gst_buffer_make_writable (buffer); buffer = gst_buffer_make_writable (buffer);
GST_BUFFER_DTS (buffer) = dts;
GST_BUFFER_PTS (buffer) = pts;
/* now insert the packet into the queue in sorted order. This function returns /* now insert the packet into the queue in sorted order. This function returns
* FALSE if a packet with the same seqnum was already in the queue, meaning we * FALSE if a packet with the same seqnum was already in the queue, meaning we
* have a duplicate. */ * have a duplicate. */
if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, buffer, timestamp, if (G_UNLIKELY (!rtp_jitter_buffer_insert (priv->jbuf, buffer, dts,
priv->clock_rate, &tail, &percent))) priv->clock_rate, &tail, &percent)))
goto duplicate; goto duplicate;
@ -1636,7 +1646,7 @@ update_estimated_eos (GstRtpJitterBuffer * jitterbuffer, GstBuffer * outbuf)
GST_LOG_OBJECT (jitterbuffer, "left %" GST_TIME_FORMAT, GST_LOG_OBJECT (jitterbuffer, "left %" GST_TIME_FORMAT,
GST_TIME_ARGS (left)); GST_TIME_ARGS (left));
out_time = GST_BUFFER_PTS (outbuf); out_time = GST_BUFFER_DTS (outbuf);
if (elapsed > 0) if (elapsed > 0)
estimated = gst_util_uint64_scale (out_time, left, elapsed); estimated = gst_util_uint64_scale (out_time, left, elapsed);
@ -1662,14 +1672,13 @@ update_estimated_eos (GstRtpJitterBuffer * jitterbuffer, GstBuffer * outbuf)
/* take a buffer from the queue and push it */ /* take a buffer from the queue and push it */
static GstFlowReturn static GstFlowReturn
pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum, pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum)
GstClockTime pts)
{ {
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
GstFlowReturn result; GstFlowReturn result;
GstBuffer *outbuf; GstBuffer *outbuf;
GstClockTime dts, pts;
gint percent = -1; gint percent = -1;
GstClockTime out_time;
/* 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, &percent); outbuf = rtp_jitter_buffer_pop (priv->jbuf, &percent);
@ -1687,10 +1696,12 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
priv->ts_discont = FALSE; priv->ts_discont = FALSE;
} }
dts = GST_BUFFER_DTS (outbuf);
pts = GST_BUFFER_PTS (outbuf);
/* apply timestamp with offset to buffer now */ /* apply timestamp with offset to buffer now */
out_time = apply_offset (jitterbuffer, pts); GST_BUFFER_DTS (outbuf) = apply_offset (jitterbuffer, dts);
GST_BUFFER_PTS (outbuf) = out_time; GST_BUFFER_PTS (outbuf) = apply_offset (jitterbuffer, pts);
GST_BUFFER_DTS (outbuf) = out_time;
/* update the elapsed time when we need to check against the npt stop time. */ /* update the elapsed time when we need to check against the npt stop time. */
update_estimated_eos (jitterbuffer, outbuf); update_estimated_eos (jitterbuffer, outbuf);
@ -1698,7 +1709,8 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
/* 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->last_out_time = GST_BUFFER_PTS (outbuf);
priv->last_out_dts = dts;
priv->last_out_pts = pts; priv->last_out_pts = pts;
priv->next_seqnum = (seqnum + 1) & 0xffff; priv->next_seqnum = (seqnum + 1) & 0xffff;
JBUF_UNLOCK (priv); JBUF_UNLOCK (priv);
@ -1708,8 +1720,10 @@ pop_and_push_next (GstRtpJitterBuffer * jitterbuffer, guint16 seqnum,
/* push buffer */ /* push buffer */
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"Pushing buffer %d, timestamp %" GST_TIME_FORMAT, seqnum, "Pushing buffer %d, dts %" GST_TIME_FORMAT ", pts %" GST_TIME_FORMAT,
GST_TIME_ARGS (out_time)); seqnum, GST_TIME_ARGS (GST_BUFFER_DTS (outbuf)),
GST_TIME_ARGS (GST_BUFFER_PTS (outbuf)));
result = gst_pad_push (priv->srcpad, outbuf); result = gst_pad_push (priv->srcpad, outbuf);
JBUF_LOCK_CHECK (priv, out_flushing); JBUF_LOCK_CHECK (priv, out_flushing);
@ -1724,24 +1738,24 @@ out_flushing:
} }
static GstClockTime static GstClockTime
estimate_pts (GstRtpJitterBuffer * jitterbuffer, GstClockTime pts, gint gap) estimate_dts (GstRtpJitterBuffer * jitterbuffer, GstClockTime dts, gint gap)
{ {
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv; GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
GstClockTime duration; GstClockTime duration;
if (pts == -1 || priv->last_out_pts == -1) if (dts == -1 || priv->last_out_dts == -1)
return pts; return dts;
GST_DEBUG_OBJECT (jitterbuffer, GST_DEBUG_OBJECT (jitterbuffer,
"pts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT, "dts %" GST_TIME_FORMAT ", last %" GST_TIME_FORMAT,
GST_TIME_ARGS (pts), GST_TIME_ARGS (priv->last_out_pts)); GST_TIME_ARGS (dts), GST_TIME_ARGS (priv->last_out_dts));
/* interpolate between the current time and the last time based on /* interpolate between the current time and the last time based on
* number of packets we are missing, this is the estimated duration * number of packets we are missing, this is the estimated duration
* for the missing packet based on equidistant packet spacing. Also make * for the missing packet based on equidistant packet spacing. Also make
* sure we never go negative. */ * sure we never go negative. */
if (pts >= priv->last_out_pts) if (dts >= priv->last_out_dts)
duration = (pts - priv->last_out_pts) / (gap + 1); duration = (dts - priv->last_out_dts) / (gap + 1);
else else
/* packet already lost, timer will timeout quickly */ /* packet already lost, timer will timeout quickly */
duration = 0; duration = 0;
@ -1750,9 +1764,9 @@ estimate_pts (GstRtpJitterBuffer * jitterbuffer, GstClockTime pts, gint gap)
GST_TIME_ARGS (duration)); GST_TIME_ARGS (duration));
/* add this duration to the timestamp of the last packet we pushed */ /* add this duration to the timestamp of the last packet we pushed */
pts = (priv->last_out_pts + duration); dts = (priv->last_out_dts + duration);
return pts; return dts;
} }
/* Peek a buffer and compare the seqnum to the expected seqnum. /* Peek a buffer and compare the seqnum to the expected seqnum.
@ -1772,7 +1786,7 @@ handle_next_buffer (GstRtpJitterBuffer * jitterbuffer)
GstFlowReturn result = GST_FLOW_OK; GstFlowReturn result = GST_FLOW_OK;
GstBuffer *outbuf; GstBuffer *outbuf;
guint16 seqnum; guint16 seqnum;
GstClockTime pts; GstClockTime dts;
guint32 next_seqnum; guint32 next_seqnum;
gint gap; gint gap;
GstRTPBuffer rtp = { NULL, }; GstRTPBuffer rtp = { NULL, };
@ -1785,7 +1799,7 @@ handle_next_buffer (GstRtpJitterBuffer * jitterbuffer)
again: again:
/* peek a buffer, we're just looking at the sequence number. /* peek a buffer, we're just looking at the sequence number.
* If all is fine, we'll pop and push it. If the sequence number is wrong we * If all is fine, we'll pop and push it. If the sequence number is wrong we
* wait on the timestamp. In the chain function we will unlock the wait when a * wait on the DTS. In the chain function we will unlock the wait when a
* 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);
@ -1799,9 +1813,7 @@ again:
next_seqnum = priv->next_seqnum; next_seqnum = priv->next_seqnum;
/* get the timestamp, this is already corrected for clock skew by the dts = GST_BUFFER_DTS (outbuf);
* jitterbuffer */
pts = GST_BUFFER_PTS (outbuf);
/* get the gap between this and the previous packet. If we don't know the /* get the gap between this and the previous packet. If we don't know the
* previous packet seqnum assume no gap. */ * previous packet seqnum assume no gap. */
@ -1810,14 +1822,14 @@ again:
/* 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
* while we wait */ * while we wait */
result = set_timer (jitterbuffer, TIMER_TYPE_DEADLINE, seqnum, pts); result = set_timer (jitterbuffer, TIMER_TYPE_DEADLINE, seqnum, dts);
} else { } else {
/* else calculate GAP */ /* else calculate GAP */
gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum); gap = gst_rtp_buffer_compare_seqnum (next_seqnum, seqnum);
if (G_LIKELY (gap == 0)) { if (G_LIKELY (gap == 0)) {
/* no missing packet, pop and push */ /* no missing packet, pop and push */
result = pop_and_push_next (jitterbuffer, seqnum, pts); result = pop_and_push_next (jitterbuffer, seqnum);
} else if (G_UNLIKELY (gap < 0)) { } else if (G_UNLIKELY (gap < 0)) {
/* if we have a packet that we already pushed or considered dropped, pop it /* if we have a packet that we already pushed or considered dropped, pop it
* off and get the next packet */ * off and get the next packet */
@ -1831,9 +1843,9 @@ again:
"Sequence number GAP detected: expected %d instead of %d (%d missing)", "Sequence number GAP detected: expected %d instead of %d (%d missing)",
next_seqnum, seqnum, gap); next_seqnum, seqnum, gap);
/* packet missing, estimate when we should ultimately push this packet */ /* packet missing, estimate when we should ultimately push this packet */
pts = estimate_pts (jitterbuffer, pts, gap); dts = estimate_dts (jitterbuffer, dts, gap);
/* and set a timer for it */ /* and set a timer for it */
result = set_timer (jitterbuffer, TIMER_TYPE_LOST, next_seqnum, pts); result = set_timer (jitterbuffer, TIMER_TYPE_LOST, next_seqnum, dts);
} }
} }
return result; return result;
@ -1895,6 +1907,7 @@ do_lost_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
/* update our expected next packet */ /* update our expected next packet */
priv->last_popped_seqnum = timer->seqnum; priv->last_popped_seqnum = timer->seqnum;
priv->last_out_time = apply_offset (jitterbuffer, timer->timeout); priv->last_out_time = apply_offset (jitterbuffer, timer->timeout);
priv->last_out_dts = timer->timeout;
priv->last_out_pts = timer->timeout; priv->last_out_pts = timer->timeout;
priv->next_seqnum = (timer->seqnum + lost_packets) & 0xffff; priv->next_seqnum = (timer->seqnum + lost_packets) & 0xffff;
/* remove timer now */ /* remove timer now */

View file

@ -684,7 +684,6 @@ rtp_jitter_buffer_insert (RTPJitterBuffer * jbuf, GstBuffer * buf,
* running time. */ * running time. */
time = calculate_skew (jbuf, rtptime, time, clock_rate); time = calculate_skew (jbuf, rtptime, time, clock_rate);
GST_BUFFER_PTS (buf) = time; GST_BUFFER_PTS (buf) = time;
GST_BUFFER_DTS (buf) = time;
/* It's more likely that the packet was inserted in the front of the buffer */ /* It's more likely that the packet was inserted in the front of the buffer */
if (G_LIKELY (list)) if (G_LIKELY (list))