jitterbuffer: improve EOS handling

Make a new method to disable the jitterbuffer buffering.
Rework the update_estimated_eos() method. Calculate how much time
there is left to play. If we have less than the delay of the
jitterbuffer, we disabled buffering because we might never be able to
fill the complete jitterbuffer again.
If we receive an EOS event, disable buffering. We will drain the
buffer and eventually push the EOS event out.
When we reach the estimated NPT timeout and we didn't receive an EOS
event, make one and queue it so that it can be pushed.

Fixes https://bugzilla.gnome.org/show_bug.cgi?id=728017
This commit is contained in:
Wim Taymans 2014-04-18 11:11:14 +02:00
parent 38a486b374
commit 3e11ce43b9
3 changed files with 78 additions and 39 deletions

View file

@ -1184,6 +1184,7 @@ gst_rtp_jitter_buffer_flush_stop (GstRtpJitterBuffer * jitterbuffer)
priv->last_rtptime = -1;
GST_DEBUG_OBJECT (jitterbuffer, "flush and reset jitterbuffer");
rtp_jitter_buffer_flush (priv->jbuf, (GFunc) free_item, NULL);
rtp_jitter_buffer_disable_buffering (priv->jbuf, FALSE);
rtp_jitter_buffer_reset_skew (priv->jbuf);
remove_all_timers (jitterbuffer);
JBUF_UNLOCK (priv);
@ -1378,6 +1379,7 @@ queue_event (GstRtpJitterBuffer * jitterbuffer, GstEvent * event)
break;
case GST_EVENT_EOS:
priv->eos = TRUE;
rtp_jitter_buffer_disable_buffering (priv->jbuf, TRUE);
break;
default:
break;
@ -1575,14 +1577,6 @@ check_buffering_percent (GstRtpJitterBuffer * jitterbuffer, gint percent)
if (percent == -1)
return NULL;
if (priv->eos || (priv->npt_stop != -1 &&
priv->npt_stop - priv->npt_start <=
rtp_jitter_buffer_get_delay (priv->jbuf))) {
GST_DEBUG_OBJECT (jitterbuffer, "short stream; faking full buffer");
rtp_jitter_buffer_set_buffering (priv->jbuf, FALSE);
percent = 100;
}
/* Post a buffering message */
if (priv->last_percent != percent) {
priv->last_percent = percent;
@ -2387,45 +2381,61 @@ static void
update_estimated_eos (GstRtpJitterBuffer * jitterbuffer,
RTPJitterBufferItem * item)
{
guint64 total, elapsed, left, estimated;
GstClockTime out_time;
GstRtpJitterBufferPrivate *priv = jitterbuffer->priv;
if (priv->npt_stop != -1 && priv->ext_timestamp != -1
&& priv->clock_base != -1 && priv->clock_rate > 0) {
guint64 elapsed, estimated;
if (priv->npt_stop == -1 || priv->ext_timestamp == -1
|| priv->clock_base == -1 || priv->clock_rate <= 0)
return;
elapsed = compute_elapsed (jitterbuffer, item);
/* compute the elapsed time */
elapsed = compute_elapsed (jitterbuffer, item);
if (elapsed > priv->last_elapsed || !priv->last_elapsed) {
guint64 left;
GstClockTime out_time;
/* do nothing if elapsed time doesn't increment */
if (priv->last_elapsed && elapsed <= priv->last_elapsed)
return;
priv->last_elapsed = elapsed;
priv->last_elapsed = elapsed;
left = priv->npt_stop - priv->npt_start;
GST_LOG_OBJECT (jitterbuffer, "left %" GST_TIME_FORMAT,
GST_TIME_ARGS (left));
/* this is the total time we need to play */
total = priv->npt_stop - priv->npt_start;
GST_LOG_OBJECT (jitterbuffer, "total %" GST_TIME_FORMAT,
GST_TIME_ARGS (total));
out_time = item->dts;
/* this is how much time there is left */
if (total > elapsed)
left = total - elapsed;
else
left = 0;
if (elapsed > 0)
estimated = gst_util_uint64_scale (out_time, left, elapsed);
else {
/* if there is almost nothing left,
* we may never advance enough to end up in the above case */
if (left < GST_SECOND)
estimated = GST_SECOND;
else
estimated = -1;
}
/* if we have less time left that the size of the buffer, we will not
* be able to keep it filled, disabled buffering then */
if (left < rtp_jitter_buffer_get_delay (priv->jbuf)) {
GST_DEBUG_OBJECT (jitterbuffer, "left %" GST_TIME_FORMAT
", disable buffering close to EOS", GST_TIME_ARGS (left));
rtp_jitter_buffer_disable_buffering (priv->jbuf, TRUE);
}
GST_LOG_OBJECT (jitterbuffer, "elapsed %" GST_TIME_FORMAT ", estimated %"
GST_TIME_FORMAT, GST_TIME_ARGS (elapsed), GST_TIME_ARGS (estimated));
/* this is the current time as running-time */
out_time = item->dts;
if (estimated != -1 && priv->estimated_eos != estimated) {
set_timer (jitterbuffer, TIMER_TYPE_EOS, -1, estimated);
priv->estimated_eos = estimated;
}
}
if (elapsed > 0)
estimated = gst_util_uint64_scale (out_time, total, elapsed);
else {
/* if there is almost nothing left,
* we may never advance enough to end up in the above case */
if (total < GST_SECOND)
estimated = GST_SECOND;
else
estimated = -1;
}
GST_LOG_OBJECT (jitterbuffer, "elapsed %" GST_TIME_FORMAT ", estimated %"
GST_TIME_FORMAT, GST_TIME_ARGS (elapsed), GST_TIME_ARGS (estimated));
if (estimated != -1 && priv->estimated_eos != estimated) {
set_timer (jitterbuffer, TIMER_TYPE_EOS, -1, estimated);
priv->estimated_eos = estimated;
}
}
@ -2787,6 +2797,10 @@ do_eos_timeout (GstRtpJitterBuffer * jitterbuffer, TimerData * timer,
GST_INFO_OBJECT (jitterbuffer, "got the NPT timeout");
remove_timer (jitterbuffer, timer);
if (!priv->eos) {
/* there was no EOS in the buffer, put one in there now */
queue_event (jitterbuffer, gst_event_new_eos ());
}
JBUF_SIGNAL_EVENT (priv);
return TRUE;

View file

@ -226,6 +226,19 @@ rtp_jitter_buffer_reset_skew (RTPJitterBuffer * jbuf)
GST_DEBUG ("reset skew correction");
}
/**
* rtp_jitter_buffer_disable_buffering:
* @jbuf: an #RTPJitterBuffer
* @disabled: the new state
*
* Enable or disable buffering on @jbuf.
*/
void
rtp_jitter_buffer_disable_buffering (RTPJitterBuffer * jbuf, gboolean disabled)
{
jbuf->buffering_disabled = disabled;
}
static void
rtp_jitter_buffer_resync (RTPJitterBuffer * jbuf, GstClockTime time,
GstClockTime gstrtptime, guint64 ext_rtptime, gboolean reset_skew)
@ -297,9 +310,14 @@ update_buffer_level (RTPJitterBuffer * jbuf, gint * percent)
level = get_buffer_level (jbuf);
GST_DEBUG ("buffer level %" GST_TIME_FORMAT, GST_TIME_ARGS (level));
if (jbuf->buffering_disabled) {
GST_DEBUG ("buffering is disabled");
level = jbuf->high_level;
}
if (jbuf->buffering) {
post = TRUE;
if (level > jbuf->high_level) {
if (level >= jbuf->high_level) {
GST_DEBUG ("buffering finished");
jbuf->buffering = FALSE;
}
@ -850,7 +868,7 @@ rtp_jitter_buffer_flush (RTPJitterBuffer * jbuf, GFunc free_func,
gboolean
rtp_jitter_buffer_is_buffering (RTPJitterBuffer * jbuf)
{
return jbuf->buffering;
return jbuf->buffering && !jbuf->buffering_disabled;
}
/**
@ -883,6 +901,9 @@ rtp_jitter_buffer_get_percent (RTPJitterBuffer * jbuf)
if (G_UNLIKELY (jbuf->high_level == 0))
return 100;
if (G_UNLIKELY (jbuf->buffering_disabled))
return 100;
level = get_buffer_level (jbuf);
percent = (level * 100 / jbuf->high_level);
percent = MIN (percent, 100);

View file

@ -100,6 +100,7 @@ struct _RTPJitterBuffer {
gint64 window_min;
gint64 skew;
gint64 prev_send_diff;
gboolean buffering_disabled;
};
struct _RTPJitterBufferClass {
@ -153,6 +154,9 @@ void rtp_jitter_buffer_reset_skew (RTPJitterBuffer *jbuf)
gboolean rtp_jitter_buffer_insert (RTPJitterBuffer *jbuf,
RTPJitterBufferItem *item,
gboolean *tail, gint *percent);
void rtp_jitter_buffer_disable_buffering (RTPJitterBuffer *jbuf, gboolean disabled);
RTPJitterBufferItem * rtp_jitter_buffer_peek (RTPJitterBuffer *jbuf);
RTPJitterBufferItem * rtp_jitter_buffer_pop (RTPJitterBuffer *jbuf, gint *percent);