[vlc-commits] [Git][videolan/vlc][master] 7 commits: input_clock: add new function to handle buffering

Alexandre Janniaux (@alexandre-janniaux) gitlab at videolan.org
Thu Mar 20 10:07:46 UTC 2025



Alexandre Janniaux pushed to branch master at VideoLAN / VLC


Commits:
8cb7982b by Alexandre Janniaux at 2025-03-20T09:53:20+00:00
input_clock: add new function to handle buffering

Those functions are currently specific interpretation of the values that
are already returned by input_clock_GetState(), ie. computed from
system start, stream start, system buffering duration and stream
buffering duration as well as the clock pause date or the current date.

The rationale for separating the computation in those functions instead
of input_clock_GetState is that the latter exposes an internal state of
the input_clock buffering code, which prevents modifying it in a
different way -- like exposing multiple buffering segments for instance.
Exposing the computations by "intent" instead of by "current state"
enables abstracting away the buffering from the es_out.

- - - - -
854340ae by Alexandre Janniaux at 2025-03-20T09:53:20+00:00
es_out: use new input_clock buffering function

- - - - -
0bfa39f7 by Alexandre Janniaux at 2025-03-20T09:53:20+00:00
vlc_messages: expose struct vlc_logger definition

The structure is already used in the Rust bindings and has as low
dependencies as possible to anything. Exposing it allows creating logger
that will work in a minimal test environment.

- - - - -
6f0b0699 by Alexandre Janniaux at 2025-03-20T09:53:20+00:00
input_clock: add logger from input_clock_New()

The logger is used in the input_clock_Update() function, but it's more
suitable to define the logger at the object creation rather than being
able to "switch" logger instance at call site.

- - - - -
3e573be9 by Alexandre Janniaux at 2025-03-20T09:53:20+00:00
input_clock: remove logger from input_clock_Update

We can use the internal logger reference now.

- - - - -
bfee935b by Alexandre Janniaux at 2025-03-20T09:53:20+00:00
input_clock: remove DVD CR gap check

The check was added back then for a special case with DVDs, but it
should be moved to the DVD demuxer instead if it is really needed.
However, it actually request the system time to have a min value which
is almost always the case now, so it's probably not needed anymore.

- - - - -
102df8e8 by Alexandre Janniaux at 2025-03-20T09:53:20+00:00
test: add test for input clock

Add a unit test checking the behaviour of the input clock. For now, the
test mainly setup the infrastructure for testing the input clock and
checks some basic fundamentals.

Note that the test doesn't pass without the previous commit removing the
CR_PCR_GAP hack for DVD, so the test actually still check something that
was failing before.

- - - - -


9 changed files:

- include/vlc_messages.h
- src/Makefile.am
- src/clock/input_clock.c
- src/clock/input_clock.h
- + src/clock/test/input_clock.c
- src/input/es_out.c
- src/input/test/es_out.c
- src/meson.build
- src/misc/messages.c


Changes:

=====================================
include/vlc_messages.h
=====================================
@@ -121,7 +121,9 @@ VLC_API const char *vlc_strerror_c(int);
  * @{
  */
 
-struct vlc_logger;
+struct vlc_logger {
+    const struct vlc_logger_operations *ops;
+};
 
 VLC_API void vlc_Log(struct vlc_logger *const *logger, int prio,
                      const char *type, const char *module,


=====================================
src/Makefile.am
=====================================
@@ -722,6 +722,12 @@ test_input_es_out_SOURCES = input/test/es_out.c \
 	clock/input_clock.c clock/input_clock.h
 check_PROGRAMS += test_input_es_out
 
+test_input_clock_SOURCES = clock/test/input_clock.c \
+	clock/input_clock.c clock/input_clock.h \
+	clock/clock_internal.c clock/clock_internal.h \
+	clock/clock.c clock/clock.h
+check_PROGRAMS += test_input_clock
+
 LDADD = libvlccore.la \
 	../compat/libcompat.la
 


=====================================
src/clock/input_clock.c
=====================================
@@ -3,6 +3,7 @@
  *****************************************************************************
  * Copyright (C) 1999-2018 VLC authors and VideoLAN
  * Copyright (C) 2008 Laurent Aimar
+ * Copyright (C) 2023-2025 Alexandre Janniaux <ajanni at videolabs.io>
  *
  * Authors: Christophe Massiot <massiot at via.ecp.fr>
  *          Laurent Aimar < fenrir _AT_ videolan _DOT_ org >
@@ -103,6 +104,7 @@
 /* */
 struct input_clock_t
 {
+    struct vlc_logger *logger;
     struct {
         const struct vlc_input_clock_cbs *cbs;
         void *opaque;
@@ -162,11 +164,12 @@ static void UpdateListener( input_clock_t *cl, bool discontinuity )
 /*****************************************************************************
  * input_clock_New: create a new clock
  *****************************************************************************/
-input_clock_t *input_clock_New( float rate )
+input_clock_t *input_clock_New(struct vlc_logger *logger, float rate)
 {
     input_clock_t *cl = malloc( sizeof(*cl) );
     if( !cl )
         return NULL;
+    cl->logger = logger;
     cl->listener.cbs = NULL;
     cl->listener.opaque = NULL;
 
@@ -219,7 +222,7 @@ void input_clock_AttachListener(input_clock_t *cl,
  *  i_ck_stream: date in stream clock
  *  i_ck_system: date in system clock
  *****************************************************************************/
-vlc_tick_t input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
+vlc_tick_t input_clock_Update( input_clock_t *cl,
                          bool b_can_pace_control, bool b_buffering,
                          bool b_extra_buffering_allowed,
                          vlc_tick_t i_ck_stream, vlc_tick_t i_ck_system )
@@ -254,12 +257,12 @@ vlc_tick_t input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
             /* Stream discontinuity, for which we haven't received a
              * warning from the stream control facilities (dd-edited
              * stream ?). */
-            msg_Warn(p_log, "clock gap, unexpected stream discontinuity: "
+            vlc_warning(cl->logger, "clock gap, unexpected stream discontinuity: "
                      "system_diff: %"PRId64" stream_diff: %"PRId64,
                      system_diff, stream_diff);
 
             /* */
-            msg_Warn(p_log, "feeding synchro with a new reference point trying"
+            vlc_warning(cl->logger, "feeding synchro with a new reference point trying"
                      " to recover from clock gap");
             b_reset_reference= true;
             discontinuity = true;
@@ -275,8 +278,7 @@ vlc_tick_t input_clock_Update( input_clock_t *cl, vlc_object_t *p_log,
 
         /* Feed synchro with a new reference point. */
         cl->b_has_reference = true;
-        cl->ref = clock_point_Create( __MAX( CR_MEAN_PTS_GAP, i_ck_system ),
-                                      i_ck_stream );
+        cl->ref = clock_point_Create(i_ck_system, i_ck_stream);
     }
 
     /* Compute the drift between the stream clock and the system clock
@@ -420,6 +422,31 @@ int input_clock_GetState( input_clock_t *cl,
     return VLC_SUCCESS;
 }
 
+int input_clock_GetBufferingDuration(
+    const input_clock_t *clock,
+    vlc_tick_t *stream_duration,
+    vlc_tick_t *system_duration
+){
+    if (!clock->b_has_reference)
+        return VLC_EGENERIC;
+
+    *stream_duration = clock->last.stream - clock->ref.stream;
+    *system_duration = clock->last.system - clock->ref.system;
+
+    return VLC_SUCCESS;
+}
+
+vlc_tick_t input_clock_GetSystemDuration(
+    const input_clock_t *clock,
+    vlc_tick_t system_reference
+){
+    if (!clock->b_has_reference)
+        return 0;
+
+    vlc_tick_t system_start = clock->ref.system;
+    return system_reference - system_start;
+}
+
 void input_clock_ChangeSystemOrigin( input_clock_t *cl, vlc_tick_t i_system )
 {
     assert( cl->b_has_reference );


=====================================
src/clock/input_clock.h
=====================================
@@ -3,6 +3,7 @@
  *****************************************************************************
  * Copyright (C) 2008-2018 VLC authors and VideoLAN
  * Copyright (C) 2008 Laurent Aimar
+ * Copyright (C) 2023-2025 Alexandre Janniaux <ajanni at videolabs.io>
  *
  * Authors: Laurent Aimar < fenrir _AT_ videolan _DOT_ org >
  *
@@ -71,7 +72,7 @@ struct vlc_input_clock_cbs {
  *
  * You must use input_clock_Delete to delete it once unused.
  */
-input_clock_t *input_clock_New( float rate );
+input_clock_t *input_clock_New(struct vlc_logger *logger, float rate);
 
 /**
  * This function attach a clock listener to the input clock
@@ -98,7 +99,6 @@ void input_clock_Delete(input_clock_t *);
  * It will also tell if the clock point is late regarding our buffering.
  *
  * \param clock the input clock object to update with the new point
- * \param p_log the logger object to use
  * \param b_can_pace_control whether the input can control the speed of playback
  * \param b_buffering whether the input is buffering
  * \param b_extra_buffering_allowed tells if we are allowed to bufferize more
@@ -108,7 +108,7 @@ void input_clock_Delete(input_clock_t *);
  *
  * \return clock update delay
  */
-vlc_tick_t input_clock_Update( input_clock_t *clock, vlc_object_t *p_log,
+vlc_tick_t input_clock_Update(input_clock_t *clock,
                             bool b_can_pace_control, bool b_buffering,
                             bool b_extra_buffering_allowed,
                             vlc_tick_t i_clock, vlc_tick_t i_system );
@@ -154,6 +154,27 @@ int input_clock_GetState( input_clock_t *,
                           vlc_tick_t *pi_stream_start, vlc_tick_t *pi_system_start,
                           vlc_tick_t *pi_stream_duration, vlc_tick_t *pi_system_duration );
 
+/**
+ * Return the duration of stream buffered as well as for how long the
+ * buffering has been started.
+ *
+ * @param clock An input clock with valid references
+ * @param stream_duration the available buffering duration
+ * @param system_duration the time spent in buffering
+ */
+int input_clock_GetBufferingDuration(
+    const input_clock_t *clock,
+    vlc_tick_t *stream_duration,
+    vlc_tick_t *system_duration);
+
+/**
+ * Return for how long the buffering would be running up to the system
+ * reference given.
+ */
+vlc_tick_t input_clock_GetSystemDuration(
+    const input_clock_t *clock,
+    vlc_tick_t system_reference);
+
 /**
  * This function allows the set the minimal configuration for the jitter estimation algo.
  */


=====================================
src/clock/test/input_clock.c
=====================================
@@ -0,0 +1,151 @@
+/*****************************************************************************
+ * input_clock.c: tests for the input buffering code
+ *****************************************************************************
+ * Copyright (C) 2023-2025 Alexandre Janniaux <ajanni at videolabs.io>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU Lesser General Public License as published by
+ * the Free Software Foundation; either version 2.1 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU Lesser General Public License for more details.
+ *
+ * You should have received a copy of the GNU Lesser General Public License
+ * along with this program; if not, write to the Free Software Foundation,
+ * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
+ *****************************************************************************/
+
+#ifdef HAVE_CONFIG_H
+# include "config.h"
+#endif
+
+#include <vlc_common.h>
+#include <vlc_modules.h>
+#include <vlc_messages.h>
+#include <vlc_tick.h>
+
+#include <vlc/libvlc.h>
+
+#include "../../../src/clock/input_clock.h"
+#include "../../../src/clock/clock.h"
+#include "../../../src/clock/clock_internal.h"
+
+const char vlc_module_name[] = "test_input_clock";
+
+static void LoggerLog(void *data, int type, const vlc_log_t *item,
+        const char *fmt, va_list args)
+{
+    (void)data; (void)type; (void)item;
+    vfprintf(stderr, fmt, args);
+}
+
+static const struct vlc_logger_operations logger_ops =
+{
+    .log = LoggerLog,
+};
+static struct vlc_logger logger = { &logger_ops };
+
+static void test_clock_update(void)
+{
+    input_clock_t *clock = input_clock_New(&logger, 1.f);
+    assert(clock != NULL);
+
+    {
+        vlc_tick_t system_now = VLC_TICK_0;
+        vlc_tick_t system_duration = input_clock_GetSystemDuration(clock, system_now);
+        assert(system_duration == 0);
+    }
+
+    bool can_pace_control = true;
+    bool buffering_allowed = true;
+    vlc_tick_t date_stream = VLC_TICK_0;
+    vlc_tick_t date_system = VLC_TICK_0;
+    input_clock_Update(clock, can_pace_control, buffering_allowed, false, date_stream, date_system);
+
+    vlc_tick_t stream_duration, system_duration;
+    input_clock_GetBufferingDuration(clock, &stream_duration, &system_duration);
+    fprintf(stderr, "system_duration=%" PRId64 " stream_duration=%" PRId64 "\n",
+            system_duration, stream_duration);
+    assert(stream_duration == 0);
+    assert(system_duration == 0);
+
+    vlc_tick_t system_now = VLC_TICK_0;
+    system_duration = input_clock_GetSystemDuration(clock, system_now);
+    assert(system_duration == 0);
+
+    /* Reset drift */
+    input_clock_Reset(clock);
+    input_clock_Delete(clock);
+}
+
+static void clock_update_abort(
+    vlc_tick_t system_ts, vlc_tick_t ts, double rate,
+    unsigned frame_rate, unsigned frame_rate_base, void *data)
+{
+    (void)system_ts; (void)ts; (void)rate;
+    (void)frame_rate; (void)frame_rate_base;
+    (void)data;
+    assert(!"No clock update should happen");
+}
+
+static const struct vlc_clock_cbs clock_abort_ops =
+{
+    .on_update = clock_update_abort,
+};
+
+struct clock_validate {
+    bool validate;
+    struct {
+        vlc_tick_t system;
+        vlc_tick_t stream;
+    } expected;
+};
+
+static void clock_update_validate(
+    vlc_tick_t system_ts, vlc_tick_t ts, double rate,
+    unsigned frame_rate, unsigned frame_rate_base, void *data)
+{
+    (void)system_ts; (void)ts; (void)rate;
+    (void)frame_rate; (void)frame_rate_base;
+
+    struct clock_validate *validate = data;
+    fprintf(stderr, "%s: system=%" PRId64 " stream=%" PRId64
+            ", checking against system=%"PRId64 " stream=%" PRId64 "\n",
+            __func__, system_ts, ts, validate->expected.system, validate->expected.stream);
+    validate->validate = true;
+    assert(validate->expected.system == system_ts);
+    assert(validate->expected.stream == ts);
+}
+static const struct vlc_clock_cbs clock_validate_ops =
+{
+    .on_update = clock_update_validate,
+};
+
+struct clock_listener_switch {
+    const struct vlc_clock_cbs *cbs;
+    void *opaque;
+};
+
+static void clock_update_switch(
+    vlc_tick_t system_ts, vlc_tick_t ts, double rate,
+    unsigned frame_rate, unsigned frame_rate_base, void *data)
+{
+    struct clock_listener_switch *listener = data;
+    listener->cbs->on_update(system_ts, ts, rate, frame_rate, frame_rate_base, listener->opaque);
+}
+
+static const struct vlc_clock_cbs clock_switch_ops = {
+    .on_update = clock_update_switch,
+};
+
+
+
+int main(void)
+{
+    fprintf(stderr, "test_clock_update:\n");
+    test_clock_update();
+    return 0;
+}


=====================================
src/input/es_out.c
=====================================
@@ -987,8 +987,16 @@ static void EsOutDecodersStopBuffering(es_out_sys_t *p_sys, bool b_forced)
     vlc_tick_t i_system_start;
     vlc_tick_t i_stream_duration;
     vlc_tick_t i_system_duration;
-    if (input_clock_GetState( p_sys->p_pgrm->p_input_clock,
-                                  &i_stream_start, &i_system_start,
+
+    /* TODO: We still need input_clock_GetState for the preroll computation
+     *       for now. */
+    if (input_clock_GetState(p_sys->p_pgrm->p_input_clock,
+                             &i_stream_start, &i_system_start,
+                             &i_stream_duration, &i_system_duration))
+        return;
+    (void)i_system_start;
+
+    if (input_clock_GetBufferingDuration( p_sys->p_pgrm->p_input_clock,
                                   &i_stream_duration, &i_system_duration ))
         return;
 
@@ -1217,18 +1225,15 @@ static void EsOutFrameNext(es_out_sys_t *p_sys)
 }
 static vlc_tick_t EsOutGetBuffering(es_out_sys_t *p_sys)
 {
-    vlc_tick_t i_stream_duration, i_system_start;
-
     if( !p_sys->p_pgrm )
         return 0;
 
-    vlc_tick_t i_stream_start, i_system_duration;
+    vlc_tick_t i_stream_duration, i_system_duration;
 
     /* If the input_clock is not ready, we don't have a reference point
      * which means that buffering has not started, continue waiting. */
-    if (input_clock_GetState(p_sys->p_pgrm->p_input_clock,
-                             &i_stream_start, &i_system_start,
-                             &i_stream_duration, &i_system_duration))
+    if (input_clock_GetBufferingDuration(p_sys->p_pgrm->p_input_clock,
+                                         &i_stream_duration, &i_system_duration))
         return 0;
 
     vlc_tick_t i_delay;
@@ -1241,13 +1246,16 @@ static vlc_tick_t EsOutGetBuffering(es_out_sys_t *p_sys)
     {
         if( p_sys->b_paused )
         {
-            i_system_duration = p_sys->i_pause_date  - i_system_start;
+            i_system_duration = input_clock_GetSystemDuration(p_sys->p_pgrm->p_input_clock,
+                                          p_sys->i_pause_date);
             if( p_sys->i_buffering_extra_initial > 0 )
                 i_system_duration += p_sys->i_buffering_extra_system - p_sys->i_buffering_extra_initial;
         }
         else
         {
-            i_system_duration = vlc_tick_now() - i_system_start;
+            i_system_duration = input_clock_GetSystemDuration(p_sys->p_pgrm->p_input_clock,
+                                          vlc_tick_now());
+
         }
 
         const vlc_tick_t i_consumed = i_system_duration * p_sys->rate - i_stream_duration;
@@ -1547,7 +1555,7 @@ static es_out_pgrm_t *EsOutProgramAdd(es_out_sys_t *p_sys, input_source_t *sourc
         return NULL;
     }
 
-    p_pgrm->p_input_clock = input_clock_New( p_sys->rate );
+    p_pgrm->p_input_clock = input_clock_New(vlc_object_logger(p_input), p_sys->rate);
     if( !p_pgrm->p_input_clock )
     {
         vlc_clock_main_Delete(p_pgrm->clocks.main);
@@ -3399,7 +3407,7 @@ static int EsOutVaControlLocked(es_out_sys_t *p_sys, input_source_t *source,
         const bool b_low_delay = priv->b_low_delay;
         bool b_extra_buffering_allowed = !b_low_delay && EsOutIsExtraBufferingAllowed(p_sys);
         vlc_tick_t i_late = input_clock_Update(
-                            p_pgrm->p_input_clock, VLC_OBJECT(p_sys->p_input),
+                            p_pgrm->p_input_clock,
                             input_CanPaceControl(p_sys->p_input), p_sys->b_buffering,
                             b_extra_buffering_allowed,
                             i_pcr, vlc_tick_now() );


=====================================
src/input/test/es_out.c
=====================================
@@ -329,10 +329,6 @@ static const struct vlc_logger_operations test_logger_operations = {
     .log = LogText,
 };
 
-struct vlc_logger {
-    const struct vlc_logger_operations *ops;
-};
-
 static void test_playback(void)
 {
     vlc_list_init(&opened_decoders);


=====================================
src/meson.build
=====================================
@@ -472,3 +472,18 @@ vlc_tests += {
   'link_with' : [libvlccore],
   'include_directories' : [include_directories('.')],
 }
+
+vlc_tests += {
+    'name' : 'input_clock',
+    'sources' : files('clock/test/input_clock.c',
+        'clock/input_clock.c',
+        'clock/input_clock.h',
+        'clock/clock_internal.c',
+        'clock/clock_internal.h',
+        'clock/clock.c',
+        'clock/clock.h',
+    ),
+    'suite' : ['src'],
+    'link_with' : [libvlccore],
+}
+


=====================================
src/misc/messages.c
=====================================
@@ -82,10 +82,6 @@ static void vlc_LogSpam(vlc_object_t *obj)
     msg_Dbg(obj, "configured with %s", CONFIGURE_LINE);
 }
 
-struct vlc_logger {
-    const struct vlc_logger_operations *ops;
-};
-
 static void vlc_vaLogCallback(vlc_logger_t *logger, int type,
                               const vlc_log_t *item, const char *format,
                               va_list ap)



View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/9b6f559d8ae55fb21afb194db79ad1a8ef51d41f...102df8e8a513915e643926870c561599e12995dd

-- 
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/9b6f559d8ae55fb21afb194db79ad1a8ef51d41f...102df8e8a513915e643926870c561599e12995dd
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance


More information about the vlc-commits mailing list