From 3e11ce43b9e83aff5d92e6209e38d268d564edcf Mon Sep 17 00:00:00 2001 From: Wim Taymans Date: Fri, 18 Apr 2014 11:11:14 +0200 Subject: [PATCH] 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 --- gst/rtpmanager/gstrtpjitterbuffer.c | 88 +++++++++++++++++------------ gst/rtpmanager/rtpjitterbuffer.c | 25 +++++++- gst/rtpmanager/rtpjitterbuffer.h | 4 ++ 3 files changed, 78 insertions(+), 39 deletions(-) diff --git a/gst/rtpmanager/gstrtpjitterbuffer.c b/gst/rtpmanager/gstrtpjitterbuffer.c index 7778005166..06ecaf2bef 100644 --- a/gst/rtpmanager/gstrtpjitterbuffer.c +++ b/gst/rtpmanager/gstrtpjitterbuffer.c @@ -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; diff --git a/gst/rtpmanager/rtpjitterbuffer.c b/gst/rtpmanager/rtpjitterbuffer.c index 1262f7d09a..d6cfc7d5bd 100644 --- a/gst/rtpmanager/rtpjitterbuffer.c +++ b/gst/rtpmanager/rtpjitterbuffer.c @@ -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); diff --git a/gst/rtpmanager/rtpjitterbuffer.h b/gst/rtpmanager/rtpjitterbuffer.h index 63eff02b04..7c8e5cbe47 100644 --- a/gst/rtpmanager/rtpjitterbuffer.h +++ b/gst/rtpmanager/rtpjitterbuffer.h @@ -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);