[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