[vlc-commits] [Git][videolan/vlc][master] 6 commits: demux: add transient buffering status

François Cartegnie (@fcartegnie) gitlab at videolan.org
Thu Sep 24 11:53:43 UTC 2026



François Cartegnie pushed to branch master at VideoLAN / VLC


Commits:
655a2d7a by François Cartegnie at 2026-09-24T13:36:50+02:00
demux: add transient buffering status

- - - - -
7f022bc8 by François Cartegnie at 2026-09-24T13:36:50+02:00
input: preserve demux buffering status

- - - - -
d2af09ad by François Cartegnie at 2026-09-24T13:36:50+02:00
demux: adaptive: report transient buffering

- - - - -
ec61ad74 by François Cartegnie at 2026-09-24T13:36:50+02:00
input: pause input clock during transient buffering

- - - - -
be5cd7a7 by François Cartegnie at 2026-09-24T13:36:50+02:00
input: pause main clock on transient rebuffering

- - - - -
cd9717cb by François Cartegnie at 2026-09-24T13:36:50+02:00
demux: adaptive: refill before resuming playback

on underrun prevention, it should explicitely unpause playback
(get out of transient buffering)
only when the buffer is sufficient.

- - - - -


9 changed files:

- include/vlc_demux.h
- modules/demux/adaptive/PlaylistManager.cpp
- modules/demux/adaptive/PlaylistManager.h
- modules/demux/adaptive/Streams.cpp
- modules/demux/adaptive/Streams.hpp
- src/input/es_out.c
- src/input/es_out.h
- src/input/es_out_timeshift.c
- src/input/input.c


Changes:

=====================================
include/vlc_demux.h
=====================================
@@ -40,9 +40,13 @@
  */
 
 /* pf_demux return values */
-#define VLC_DEMUXER_EOF       0
-#define VLC_DEMUXER_EGENERIC -1
-#define VLC_DEMUXER_SUCCESS   1
+#define VLC_DEMUXER_EOF        0
+#define VLC_DEMUXER_EGENERIC  -1
+#define VLC_DEMUXER_SUCCESS    1
+/* Demuxer has temporary control of some buffering and
+ * recovering from an underrun. A paced input should
+ * pause the clock accordingly. Not an error */
+#define VLC_DEMUXER_BUFFERING  2
 
 /* DEMUX_TEST_AND_CLEAR flags */
 #define INPUT_UPDATE_TITLE      0x0010


=====================================
modules/demux/adaptive/PlaylistManager.cpp
=====================================
@@ -74,6 +74,7 @@ PlaylistManager::PlaylistManager( demux_t *p_demux_,
     b_preparsing = false;
     nextPlaylistupdate = 0;
     demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
+    demux.rebuffering = false;
     vlc_mutex_init(&demux.lock);
     vlc_cond_init(&demux.cond);
     vlc_mutex_init(&cached.lock);
@@ -440,6 +441,15 @@ vlc_tick_t PlaylistManager::getMinAheadTime() const
     return minbuffer;
 }
 
+bool PlaylistManager::isBufferingSufficient(Times from, vlc_tick_t minimum) const
+{
+    return std::all_of(streams.cbegin(), streams.cend(),
+        [from, minimum](const AbstractStream *st) {
+            return !st->isSelected() ||
+                   st->isBufferingSufficient(from, minimum);
+        });
+}
+
 bool PlaylistManager::reactivateStream(AbstractStream *stream)
 {
     return stream->reactivate(getResumePosition());
@@ -473,12 +483,26 @@ int PlaylistManager::doDemux(vlc_tick_t increment)
         return (b_dead || b_all_disabled) ? VLC_DEMUXER_EOF : VLC_DEMUXER_SUCCESS;
     }
 
-    Times barrier = demux.times;
-    barrier.offsetBy(increment);
+    const Times floor = demux.times;
+    const bool rebuffering = demux.rebuffering;
 
+    if(rebuffering)
+    {
+        const vlc_tick_t minimum = bufferingLogic->getMinBuffering(playlist);
+        if(!isBufferingSufficient(floor, minimum))
+        {
+            vlc_cond_timedwait(&demux.cond, &demux.lock, vlc_tick_now() + VLC_TICK_FROM_MS(50));
+            vlc_mutex_unlock(&demux.lock);
+            return VLC_DEMUXER_BUFFERING;
+        }
+        demux.rebuffering = false;
+    }
     vlc_mutex_unlock(&demux.lock);
 
-    AbstractStream::Status status = dequeue(demux.times, &barrier);
+    Times barrier = floor;
+    barrier.offsetBy(increment);
+
+    AbstractStream::Status status = dequeue(floor, &barrier);
 
     vlc_mutex_lock(&demux.lock);
     if(demux.firsttimes.continuous == VLC_TICK_INVALID && barrier.continuous != VLC_TICK_INVALID)
@@ -508,6 +532,7 @@ int PlaylistManager::doDemux(vlc_tick_t increment)
 
                 demux.times = Times();
                 demux.firsttimes = Times();
+                demux.rebuffering = false;
                 es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
 
                 setBufferingRunState(true);
@@ -516,13 +541,15 @@ int PlaylistManager::doDemux(vlc_tick_t increment)
         break;
     case AbstractStream::Status::Buffering:
         vlc_mutex_lock(&demux.lock);
+        demux.rebuffering = true;
         vlc_cond_timedwait(&demux.cond, &demux.lock, vlc_tick_now() + VLC_TICK_FROM_MS(50));
         vlc_mutex_unlock(&demux.lock);
-        break;
+        return VLC_DEMUXER_BUFFERING;
     case AbstractStream::Status::Discontinuity:
         vlc_mutex_lock(&demux.lock);
         demux.times = Times();
         demux.firsttimes = Times();
+        demux.rebuffering = false;
         demux.pcr_syncpoint = TimestampSynchronizationPoint::Discontinuity;
         es_out_Control(p_demux->out, ES_OUT_RESET_PCR);
         vlc_mutex_unlock(&demux.lock);
@@ -574,6 +601,7 @@ int PlaylistManager::doControl(int i_query, va_list args)
             {
                 vlc_tick_t now = vlc_tick_now();
                 demux.times = Times();
+                demux.rebuffering = false;
                 cached.lastupdate = 0;
                 if(b_pause)
                 {
@@ -656,6 +684,7 @@ int PlaylistManager::doControl(int i_query, va_list args)
             demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
             demux.times = Times();
             demux.firsttimes = Times();
+            demux.rebuffering = false;
             cached.lastupdate = 0;
             cached.i_normaltime = VLC_TICK_INVALID;
             cached.i_time = VLC_TICK_INVALID;
@@ -679,6 +708,7 @@ int PlaylistManager::doControl(int i_query, va_list args)
             demux.pcr_syncpoint = TimestampSynchronizationPoint::RandomAccess;
             demux.times = Times();
             demux.firsttimes = Times();
+            demux.rebuffering = false;
             cached.lastupdate = 0;
             cached.i_normaltime = VLC_TICK_INVALID;
             cached.i_time = VLC_TICK_INVALID;


=====================================
modules/demux/adaptive/PlaylistManager.h
=====================================
@@ -91,6 +91,7 @@ namespace adaptive
 
             Times getTimes(bool = false) const;
             vlc_tick_t getMinAheadTime() const;
+            bool isBufferingSufficient(Times, vlc_tick_t) const;
 
             virtual bool reactivateStream(AbstractStream *);
             bool setupPeriod();
@@ -125,6 +126,7 @@ namespace adaptive
             {
                 TimestampSynchronizationPoint pcr_syncpoint;
                 Times times, firsttimes;
+                bool rebuffering;
                 mutable vlc_mutex_t lock;
                 vlc_cond_t  cond;
             } demux;


=====================================
modules/demux/adaptive/Streams.cpp
=====================================
@@ -355,6 +355,16 @@ vlc_tick_t AbstractStream::getDemuxedAmount(Times from) const
     return i_demuxed;
 }
 
+bool AbstractStream::isBufferingSufficient(Times from, vlc_tick_t minimum) const
+{
+    vlc_mutex_locker locker(&lock);
+
+    if(!valid || disabled || fakeEsOut()->commandsQueue()->isEOF())
+        return true;
+
+    return getDemuxedAmount(from) >= minimum;
+}
+
 AbstractStream::BufferingStatus
 AbstractStream::getBufferAndStatus(const Times &deadline,
                                    vlc_tick_t i_min_buffering,


=====================================
modules/demux/adaptive/Streams.hpp
=====================================
@@ -89,6 +89,7 @@ namespace adaptive
                                   vlc_tick_t, bool = false);
         BufferingStatus getBufferAndStatus(const Times &, vlc_tick_t, vlc_tick_t, vlc_tick_t *);
         vlc_tick_t getDemuxedAmount(Times) const;
+        bool isBufferingSufficient(Times, vlc_tick_t) const;
         Status dequeue(Times, Times *);
 
         class StreamPosition


=====================================
src/input/es_out.c
=====================================
@@ -220,6 +220,7 @@ typedef struct
 
     /* */
     bool        b_paused;
+    bool        b_rebuffering;
     es_out_id_t *p_next_frame_es;
     vlc_tick_t  i_pause_date;
 
@@ -286,6 +287,16 @@ static es_out_sys_t *PRIV(es_out_t *out)
     return container_of(out, es_out_sys_t, out);
 }
 
+static bool EsOutIsPaused(const es_out_sys_t *p_sys)
+{
+    return p_sys->b_paused || p_sys->b_rebuffering;
+}
+
+static bool EsOutIsBuffering(const es_out_sys_t *p_sys)
+{
+    return p_sys->b_buffering || p_sys->b_rebuffering;
+}
+
 struct clock_source_mapping
 {
     char key[sizeof("monotonic")];
@@ -814,7 +825,7 @@ static vlc_tick_t EsOutGetWakeup(es_out_sys_t *p_sys)
      * to avoid too heavy buffering */
     if( !input_CanPaceControl(p_input) ||
         input_priv(p_input)->b_out_pace_control ||
-        p_sys->b_buffering )
+        EsOutIsBuffering(p_sys) )
         return 0;
 
     return input_clock_GetWakeup( p_sys->p_pgrm->p_input_clock );
@@ -1018,7 +1029,7 @@ static void EsOutResumeFromNextFrame(es_out_sys_t *p_sys)
     EsOutChangePosition(p_sys, EsOutStopNextFrame(p_sys));
 }
 
-static void EsOutChangePause(es_out_sys_t *p_sys, bool b_paused, vlc_tick_t i_date)
+static void EsOutChangeOutputPause(es_out_sys_t *p_sys, bool b_paused, vlc_tick_t i_date)
 {
     /* XXX the order is important */
     if( b_paused )
@@ -1056,10 +1067,37 @@ static void EsOutChangePause(es_out_sys_t *p_sys, bool b_paused, vlc_tick_t i_da
 
         EsOutProgramsChangeRate(p_sys);
     }
-    p_sys->b_paused = b_paused;
     p_sys->i_pause_date = i_date;
 }
 
+static void EsOutChangePause(es_out_sys_t *p_sys, bool b_paused, vlc_tick_t i_date)
+{
+    const bool b_was_paused = EsOutIsPaused(p_sys);
+
+    p_sys->b_paused = b_paused;
+    const bool b_is_paused = EsOutIsPaused(p_sys);
+
+    if( b_was_paused != b_is_paused )
+        EsOutChangeOutputPause(p_sys, b_is_paused, i_date);
+}
+
+static void EsOutChangeRebuffer(es_out_sys_t *p_sys, bool b_rebuffering, vlc_tick_t i_date)
+{
+    if( p_sys->b_rebuffering == b_rebuffering )
+        return;
+
+    const bool b_was_paused = EsOutIsPaused(p_sys);
+
+    p_sys->b_rebuffering = b_rebuffering;
+    const bool b_is_paused = EsOutIsPaused(p_sys);
+
+    if( b_was_paused != b_is_paused )
+        EsOutChangeOutputPause(p_sys, b_is_paused, i_date);
+
+    if( b_rebuffering || !p_sys->b_buffering )
+        input_SendEventCache(p_sys->p_input, b_rebuffering ? 0.0 : 1.0);
+}
+
 static void EsOutChangeRate(es_out_sys_t *p_sys, float rate)
 {
     es_out_id_t *es;
@@ -1216,7 +1254,7 @@ static void EsOutDecodersStopBuffering(es_out_sys_t *p_sys, bool b_forced)
     EsOutStopFreeVout(p_sys);
 
     /* */
-    const vlc_tick_t i_current_date = p_sys->b_paused ? p_sys->i_pause_date : vlc_tick_now();
+    const vlc_tick_t i_current_date = EsOutIsPaused(p_sys) ? p_sys->i_pause_date : vlc_tick_now();
 
     const vlc_tick_t update = i_current_date - i_buffering_duration;
 
@@ -1413,7 +1451,7 @@ static vlc_tick_t EsOutGetBuffering(es_out_sys_t *p_sys)
     }
     else
     {
-        if( p_sys->b_paused )
+        if( EsOutIsPaused(p_sys) )
         {
             i_system_duration = input_clock_GetSystemDuration(p_sys->p_pgrm->p_input_clock,
                                           p_sys->i_pause_date);
@@ -1734,8 +1772,8 @@ static es_out_pgrm_t *EsOutProgramAdd(es_out_sys_t *p_sys, input_source_t *sourc
         return NULL;
     }
 
-    if( p_sys->b_paused )
-        input_clock_ChangePause( p_pgrm->p_input_clock, p_sys->b_paused, p_sys->i_pause_date );
+    if( EsOutIsPaused(p_sys) )
+        input_clock_ChangePause( p_pgrm->p_input_clock, true, p_sys->i_pause_date );
     const vlc_tick_t pts_delay = p_sys->i_pts_delay + p_sys->i_pts_jitter
                                + p_sys->i_tracks_pts_delay;
     input_clock_SetJitter( p_pgrm->p_input_clock, pts_delay, p_sys->i_cr_average );
@@ -1748,6 +1786,8 @@ static es_out_pgrm_t *EsOutProgramAdd(es_out_sys_t *p_sys, input_source_t *sourc
      * than the visual quality if the user chose this option. */
     if (input_priv(p_input)->b_low_delay)
         vlc_clock_main_SetDejitter(p_pgrm->clocks.main, 0);
+    if( EsOutIsPaused(p_sys) )
+        vlc_clock_main_ChangePause(p_pgrm->clocks.main, p_sys->i_pause_date, true);
     vlc_clock_main_Unlock(p_pgrm->clocks.main);
 
     /* Append it */
@@ -2563,7 +2603,7 @@ static void EsOutCreateDecoder(es_out_sys_t *p_sys, es_out_id_t *p_es)
     {
         vlc_input_decoder_ChangeRate( dec, p_sys->rate );
 
-        if( unlikely( p_sys->b_paused ) ) /* Could happen during next-frame */
+        if( unlikely( EsOutIsPaused(p_sys) ) ) /* Could happen during next-frame */
             vlc_input_decoder_ChangePause( dec, true, p_sys->i_pause_date );
 
         if( p_sys->b_buffering )
@@ -2589,7 +2629,7 @@ static void EsOutCreateDecoder(es_out_sys_t *p_sys, es_out_id_t *p_es)
             {
                 if( p_sys->b_buffering )
                     vlc_input_decoder_StartWait( p_es->p_dec_record );
-                if( unlikely( p_sys->b_paused ) ) /* Could happen during next-frame */
+                if( unlikely( EsOutIsPaused(p_sys) ) ) /* Could happen during next-frame */
                     vlc_input_decoder_ChangePause( p_es->p_dec_record, true, p_sys->i_pause_date );
             }
         }
@@ -3631,12 +3671,16 @@ static int EsOutVaControlLocked(es_out_sys_t *p_sys, input_source_t *source,
         /* TODO do not use vlc_tick_now() but proper stream acquisition date */
         const bool b_low_delay = priv->b_low_delay;
         bool b_extra_buffering_allowed = !b_low_delay && EsOutIsExtraBufferingAllowed(p_sys);
-        bool buffering = p_sys->b_buffering || p_sys->p_next_frame_es != NULL;
+        bool buffering = EsOutIsBuffering(p_sys) || p_sys->p_next_frame_es != NULL;
+        /* Do not account the download stall as clock lateness. The input
+         * clock will shift this frozen system date when rebuffering ends. */
+        const vlc_tick_t system_now = p_sys->b_rebuffering
+                                    ? p_sys->i_pause_date : vlc_tick_now();
         vlc_tick_t i_late = input_clock_Update(
                             p_pgrm->p_input_clock,
                             input_CanPaceControl(p_sys->p_input), buffering,
                             b_extra_buffering_allowed,
-                            i_pcr, vlc_tick_now() );
+                            i_pcr, system_now );
 
         if (tracer != NULL)
         {
@@ -3657,6 +3701,9 @@ static int EsOutVaControlLocked(es_out_sys_t *p_sys, input_source_t *source,
             return VLC_SUCCESS;
         }
 
+        if( p_sys->b_rebuffering )
+            return VLC_SUCCESS;
+
         if (p_pgrm != p_sys->p_pgrm || p_sys->p_next_frame_es != NULL)
             return VLC_SUCCESS;
 
@@ -4116,6 +4163,14 @@ static int EsOutVaPrivControlLocked(es_out_sys_t *p_sys, input_source_t *source,
 
         return VLC_SUCCESS;
     }
+    case ES_OUT_PRIV_SET_REBUFFER_STATE:
+    {
+        const bool b_rebuffering = (bool)va_arg( args, int );
+        const vlc_tick_t i_date = va_arg( args, vlc_tick_t );
+
+        EsOutChangeRebuffer(p_sys, b_rebuffering, i_date);
+        return VLC_SUCCESS;
+    }
     case ES_OUT_PRIV_SET_RATE:
     {
         const float src_rate = va_arg( args, double );
@@ -4359,6 +4414,7 @@ input_EsOutNew(input_thread_t *p_input, input_source_t *main_source, float rate,
 
     p_sys->rate = rate;
     p_sys->b_paused = false;
+    p_sys->b_rebuffering = false;
 
     p_sys->b_buffering = true;
     p_sys->i_buffering_extra_initial = p_sys->i_buffering_extra_stream


=====================================
src/input/es_out.h
=====================================
@@ -79,6 +79,9 @@ enum es_out_query_private_e
     /* Set pause state */
     ES_OUT_PRIV_SET_PAUSE_STATE,                    /* arg1=bool b_source_paused, bool b_paused arg2=vlc_tick_t res=can fail */
 
+    /* Set transient demux rebuffering state */
+    ES_OUT_PRIV_SET_REBUFFER_STATE,                 /* arg1=bool b_rebuffering arg2=vlc_tick_t res=cannot fail */
+
     /* Set rate */
     ES_OUT_PRIV_SET_RATE,                           /* arg1=double source_rate arg2=double rate res=can fail */
 
@@ -262,6 +265,13 @@ es_out_SetPauseState(struct vlc_input_es_out *out,
     return es_out_PrivControl(out, ES_OUT_PRIV_SET_PAUSE_STATE, b_source_paused, b_paused, i_date);
 }
 
+static inline void
+es_out_SetRebufferState(struct vlc_input_es_out *out, bool b_rebuffering)
+{
+    int i_ret = es_out_PrivControl(out, ES_OUT_PRIV_SET_REBUFFER_STATE, b_rebuffering, vlc_tick_now());
+    assert( !i_ret );
+}
+
 static inline int
 es_out_SetRate(struct vlc_input_es_out *out, float source_rate, float rate)
 {


=====================================
src/input/es_out_timeshift.c
=====================================
@@ -798,6 +798,15 @@ static int PrivControlLocked(struct vlc_input_es_out *p_tsout,
 
         return ControlLockedSetPauseState(p_sys, in, b_source_paused, b_paused, i_date);
     }
+    case ES_OUT_PRIV_SET_REBUFFER_STATE:
+    {
+        const bool b_rebuffering = (bool)va_arg( args, int );
+        const vlc_tick_t i_date = va_arg( args, vlc_tick_t );
+
+        return es_out_in_PrivControl(p_sys->p_out, in,
+                                     ES_OUT_PRIV_SET_REBUFFER_STATE,
+                                     b_rebuffering, i_date);
+    }
     case ES_OUT_PRIV_SET_RATE:
     {
         const float src_rate = va_arg( args, double );


=====================================
src/input/input.c
=====================================
@@ -520,7 +520,16 @@ static void MainLoopDemux( input_thread_t *p_input, bool *pb_changed )
     if( i_ret == VLC_DEMUXER_SUCCESS )
         i_ret = demux_Demux( p_demux );
 
-    i_ret = i_ret > 0 ? VLC_DEMUXER_SUCCESS : ( i_ret < 0 ? VLC_DEMUXER_EGENERIC : VLC_DEMUXER_EOF);
+    if( i_ret != VLC_DEMUXER_BUFFERING )
+        i_ret = i_ret > 0 ? VLC_DEMUXER_SUCCESS : ( i_ret < 0 ? VLC_DEMUXER_EGENERIC : VLC_DEMUXER_EOF);
+
+    if( i_ret == VLC_DEMUXER_BUFFERING )
+    {
+        if( !es_out_GetBuffering( p_priv->p_es_out ) )
+            es_out_SetRebufferState( p_priv->p_es_out, true );
+    }
+    else
+        es_out_SetRebufferState( p_priv->p_es_out, false );
 
     if( i_ret == VLC_DEMUXER_SUCCESS )
     {



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7fc1ae989078a5df2a10acf0df7e498365703a0c...cd9717cb731ca8a677ddee89a3985c8c221d2a44

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/7fc1ae989078a5df2a10acf0df7e498365703a0c...cd9717cb731ca8a677ddee89a3985c8c221d2a44
You're receiving this email because of your account on code.videolan.org. Manage all notifications: https://code.videolan.org/-/profile/notifications | Help: https://code.videolan.org/help




More information about the vlc-commits mailing list