Steve Lhomme pushed to branch master at VideoLAN / VLC


Commits:
f9d0c115 by Thomas Guillem at 2023-10-28T14:40:34+00:00
tracer: add vlc_tracer_vaTraceWithTs()

- - - - -
08950258 by Thomas Guillem at 2023-10-28T14:40:34+00:00
test: add clock test

Possible to run all tests (by default from the test suite):

vlc/build-linux/test $ ./test_src_clock_clock
[normal]: checking that normal update has a coeff of 1.0f
[lowprecision]: checking that low precision update has a coeff near 1.0f
[drift_72]: checking that a drift of 72ms in 2h is handled
[drift_-72]: checking that a drift of -72ms in 2h is handled
[drift_864]: checking that a drift of 864ms in 24h is handled
[drift_-864]: checking that a drift of -864ms in 24h is handled
[drift_sudden]: checking that a sudden drift is handled

Or run one test with traces:

./test_src_clock_clock lowprecision --tracer=json --json-tracer-file=foo.trace 
-vv

The test fake the timestamps that are sent to the default tracer to
behave like a live playback.

One possible future scenario would be to feed the clock with real points
taken from a real 24h live-streaming playback.

- - - - -


5 changed files:

- include/vlc_tracer.h
- src/libvlccore.sym
- src/misc/tracer.c
- test/Makefile.am
- + test/src/clock/clock.c


Changes:

=====================================
include/vlc_tracer.h
=====================================
@@ -136,10 +136,21 @@ VLC_API void vlc_tracer_Destroy(struct vlc_tracer 
*tracer);
 /**
  * Emit traces
  *
- * va-args are a list of key / value parameters.
+ * \param tracer tracer emitting the traces
+ * \param ts timestamp of the current trace
+ * \param entries  list of key / value parameters.
  * Key must be a not NULL string.
  * Value has to be defined with one of the type defined
  * in the \ref vlc_tracer_entry union.
+ */
+VLC_API void vlc_tracer_vaTraceWithTs(struct vlc_tracer *tracer, vlc_tick_t ts,
+                                      va_list entries);
+
+/**
+ * Emit traces
+ *
+ * cf. vlc_tracer_vaTraceWithTs()
+ *
  * \param tracer tracer emitting the traces
  * \param ts timestamp of the current trace
  */


=====================================
src/libvlccore.sym
=====================================
@@ -293,6 +293,7 @@ vlc_vaLog
 vlc_tracer_Create
 vlc_tracer_Destroy
 vlc_tracer_TraceWithTs
+vlc_tracer_vaTraceWithTs
 vlc_LogHeaderCreate
 vlc_LogDestroy
 vlc_strerror


=====================================
src/misc/tracer.c
=====================================
@@ -46,6 +46,19 @@ struct vlc_tracer_module {
     void *opaque;
 };
 
+void vlc_tracer_vaTraceWithTs(struct vlc_tracer *tracer, vlc_tick_t ts,
+                              va_list entries)
+{
+    assert(tracer->ops->trace != NULL);
+    struct vlc_tracer_module *module =
+            container_of(tracer, struct vlc_tracer_module, tracer);
+
+    va_list copy;
+    va_copy(copy, entries);
+    tracer->ops->trace(module->opaque, ts, copy);
+    va_end(copy);
+}
+
 void vlc_tracer_TraceWithTs(struct vlc_tracer *tracer, vlc_tick_t ts, ...)
 {
     assert(tracer->ops->trace != NULL);


=====================================
test/Makefile.am
=====================================
@@ -24,6 +24,7 @@ check_PROGRAMS = \
        test_libvlc_renderer_discoverer \
        test_libvlc_slaves \
        test_src_config_chain \
+       test_src_clock_clock \
        test_src_misc_ancillary \
        test_src_misc_variables \
        test_src_input_stream \
@@ -155,6 +156,10 @@ test_libvlc_slaves_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_libvlc_meta_SOURCES = libvlc/meta.c
 test_libvlc_meta_LDADD = $(LIBVLCCORE) $(LIBVLC)
 
+test_src_clock_clock_SOURCES = src/clock/clock.c \
+       ../src/clock/clock.c \
+       ../src/clock/clock_internal.c
+test_src_clock_clock_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_src_misc_ancillary_SOURCES = src/misc/ancillary.c
 test_src_misc_ancillary_LDADD = $(LIBVLCCORE) $(LIBVLC)
 test_src_misc_variables_SOURCES = src/misc/variables.c


=====================================
test/src/clock/clock.c
=====================================
@@ -0,0 +1,608 @@
+/*****************************************************************************
+ * clock/clock.c: test for the vlc clock
+ *****************************************************************************
+ * Copyright (C) 2023 VLC authors, VideoLAN and Videolabs SAS
+ *
+ * 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_tick.h>
+#include <vlc_es.h>
+#include <vlc_tracer.h>
+
+#include "../../../src/clock/clock.h"
+
+#include <vlc/vlc.h>
+#include "../../libvlc/test.h"
+#include "../../../lib/libvlc_internal.h"
+
+#define MODULE_NAME test_clock_clock
+#undef VLC_DYNAMIC_PLUGIN
+#include <vlc_plugin.h>
+#include <vlc_vector.h>
+
+/* Define a builtin module for mocked parts */
+const char vlc_module_name[] = MODULE_STRING;
+
+struct clock_ctx;
+
+struct clock_scenario
+{
+    const char *name;
+    const char *desc;
+    vlc_tick_t stream_start;
+    vlc_tick_t system_start; /* VLC_TICK_INVALID for vlc_tick_now() */
+    vlc_tick_t duration;
+    vlc_tick_t stream_increment; /* VLC_TICK_INVALID for manual increment */
+    vlc_tick_t total_drift_duration; /* VLC_TICK_INVALID for non-drift test */
+
+    void (*update)(const struct clock_ctx *ctx, size_t index,
+                   vlc_tick_t *system, vlc_tick_t stream);
+    void (*check)(const struct clock_ctx *ctx, size_t update_count,
+                  vlc_tick_t expected_system_end, vlc_tick_t stream_end);
+};
+
+struct clock_ctx
+{
+    vlc_clock_main_t *mainclk;
+    vlc_clock_t *master;
+    vlc_clock_t *slave;
+
+    const struct clock_scenario *scenario;
+};
+
+enum tracer_event_type {
+    TRACER_EVENT_TYPE_UPDATE,
+    TRACER_EVENT_TYPE_STATUS,
+};
+
+enum tracer_event_status  {
+    TRACER_EVENT_STATUS_RESET_USER,
+    TRACER_EVENT_STATUS_RESET_BADSOURCE,
+};
+
+struct tracer_event
+{
+    enum tracer_event_type type;
+
+    union {
+        struct {
+            double coeff;
+            vlc_tick_t offset;
+        } update;
+
+        enum tracer_event_status status;
+    };
+};
+
+struct tracer_ctx
+{
+    vlc_tick_t forced_ts;
+
+    struct VLC_VECTOR(struct tracer_event) events;
+};
+
+static struct tracer_ctx tracer_ctx;
+
+static void tracer_ctx_Reset(struct tracer_ctx *ctx)
+{
+    ctx->events.size = 0;
+    ctx->forced_ts = VLC_TICK_INVALID;
+}
+
+static void tracer_ctx_Init(struct tracer_ctx *ctx)
+{
+    tracer_ctx_Reset(ctx);
+    vlc_vector_init(&ctx->events);
+};
+
+static void tracer_ctx_Destroy(struct tracer_ctx *ctx)
+{
+    vlc_vector_destroy(&ctx->events);
+}
+
+static void tracer_ctx_PushUpdate(struct tracer_ctx *ctx,
+                                  double coeff, vlc_tick_t offset)
+{
+    struct tracer_event event = {
+        .type = TRACER_EVENT_TYPE_UPDATE,
+        .update = {
+            .coeff = coeff,
+            .offset = offset,
+        },
+    };
+    bool ret = vlc_vector_push(&ctx->events, event);
+    assert(ret);
+}
+
+static void tracer_ctx_PushStatus(struct tracer_ctx *ctx,
+                                  enum tracer_event_status status)
+{
+    struct tracer_event event = {
+        .type = TRACER_EVENT_TYPE_STATUS,
+        .status = status,
+    };
+    bool ret = vlc_vector_push(&ctx->events, event);
+    assert(ret);
+}
+
+static void TracerTrace(void *opaque, vlc_tick_t ts, va_list entries)
+{
+    (void) ts;
+    struct vlc_tracer *libvlc_tracer = opaque;
+    if (libvlc_tracer != NULL)
+    {
+        /* If the user specified a tracer, forward directly to it after faking
+         * the system ts */
+        assert(tracer_ctx.forced_ts != VLC_TICK_INVALID);
+        vlc_tracer_vaTraceWithTs(libvlc_tracer, tracer_ctx.forced_ts, entries);
+    }
+
+    struct vlc_tracer_entry entry = va_arg(entries, struct vlc_tracer_entry);
+
+    bool is_render = false, is_status = false;
+    unsigned nb_update = 0;
+    double coeff = 0.0f;
+    vlc_tick_t offset = VLC_TICK_INVALID;
+    enum tracer_event_status status = 0;
+
+    while (entry.key != NULL)
+    {
+        switch (entry.type)
+        {
+            case VLC_TRACER_INT:
+                if (!is_render)
+                    continue;
+                assert(!is_status);
+
+                if (strcmp(entry.key, "offset") == 0)
+                {
+                    nb_update++;
+                    offset = VLC_TICK_FROM_NS(entry.value.integer);
+                }
+                break;
+            case VLC_TRACER_DOUBLE:
+                if (!is_render)
+                    continue;
+                assert(!is_status);
+
+                if (strcmp(entry.key, "coeff") == 0)
+                {
+                    nb_update++;
+                    coeff = entry.value.double_;
+                }
+                break;
+            case VLC_TRACER_STRING:
+                if (strcmp(entry.key, "type") == 0)
+                {
+                    if (strcmp(entry.value.string, "RENDER") == 0)
+                        is_render = true;
+                }
+                if (!is_render)
+                    continue;
+                /* Assert that there is no "reset_bad_source" */
+                if (strcmp(entry.key, "event") == 0)
+                {
+                    is_status = true;
+                    if (strcmp(entry.value.string, "reset_bad_source") == 0)
+                        status = TRACER_EVENT_STATUS_RESET_BADSOURCE;
+                    else if (strcmp(entry.value.string, "reset_user") == 0)
+                        status = TRACER_EVENT_STATUS_RESET_USER;
+                    else
+                        vlc_assert_unreachable();
+                }
+
+                break;
+            default: vlc_assert_unreachable();
+        }
+        entry = va_arg(entries, struct vlc_tracer_entry);
+    }
+
+    if (!is_render)
+        return;
+
+    if (is_status)
+        tracer_ctx_PushStatus(&tracer_ctx, status);
+    else if (nb_update > 0)
+    {
+        assert(nb_update == 2);
+        tracer_ctx_PushUpdate(&tracer_ctx, coeff, offset);
+    }
+}
+
+/* Used to check for some trace value and hack the ts to the user tracer */
+static const struct vlc_tracer_operations *
+OpenTracer(vlc_object_t *obj, void **restrict sysp)
+{
+    static const struct vlc_tracer_operations ops =
+    {
+        .trace = TracerTrace,
+    };
+
+    *sysp = vlc_object_get_tracer(obj);
+
+    return &ops;
+}
+
+vlc_module_begin()
+    set_callback(OpenTracer)
+    set_capability("tracer", 0)
+vlc_module_end()
+
+VLC_EXPORT const vlc_plugin_cb vlc_static_modules[] = {
+    VLC_SYMBOL(vlc_entry),
+    NULL
+};
+
+static void play_scenario(libvlc_int_t *vlc, struct vlc_tracer *tracer,
+                          struct clock_scenario *scenario)
+{
+    fprintf(stderr, "[%s]: checking that %s\n", scenario->name, 
scenario->desc);
+
+    assert(scenario->update != NULL);
+
+    tracer_ctx_Reset(&tracer_ctx);
+
+    struct vlc_logger *logger = vlc->obj.logger;
+
+    vlc_clock_main_t *mainclk = vlc_clock_main_New(logger, tracer);
+    assert(mainclk != NULL);
+
+    vlc_clock_t *master = vlc_clock_main_CreateMaster(mainclk, scenario->name,
+                                                      NULL, NULL);
+    assert(master != NULL);
+
+    vlc_clock_t *slave = vlc_clock_main_CreateSlave(mainclk, NULL, VIDEO_ES,
+                                                    NULL, NULL);
+    assert(slave != NULL);
+
+    const struct clock_ctx ctx = {
+        .mainclk = mainclk,
+        .master = master,
+        .slave = slave,
+        .scenario = scenario,
+    };
+
+    vlc_tick_t stream_end = scenario->stream_start + scenario->duration;
+    vlc_tick_t stream = scenario->stream_start;
+    if (scenario->system_start == VLC_TICK_INVALID)
+        scenario->system_start = vlc_tick_now();
+    vlc_tick_t system = scenario->system_start;
+    vlc_tick_t expected_system = scenario->system_start;
+
+    tracer_ctx.forced_ts = expected_system;
+    size_t index = 0;
+
+    for(; stream < stream_end;
+        stream += scenario->stream_increment, ++index)
+    {
+        scenario->update(&ctx, index, &system, stream);
+        expected_system += scenario->stream_increment;
+
+        tracer_ctx.forced_ts = expected_system;
+    }
+
+    if (scenario->check != NULL)
+    {
+        assert(expected_system == scenario->system_start + scenario->duration);
+
+        scenario->check(&ctx, index, expected_system, stream_end);
+    }
+
+    if (master != NULL)
+        vlc_clock_Delete(master);
+    vlc_clock_Delete(slave);
+    vlc_clock_main_Delete(mainclk);
+}
+
+static void run_scenarios(int main_argc, const char *main_argv[],
+                          struct clock_scenario *scenarios, size_t count)
+{
+
+    int argc;
+    const char * const *argv;
+
+    const char *scenario_name = NULL;
+    if (main_argc > 1)
+    {
+        /* specific test run from the user with custom options */
+        scenario_name = main_argv[1];
+        argc = main_argc - 1;
+        argv = &main_argv[1];
+    }
+    else
+    {
+        argc = main_argc;
+        argv = main_argv;
+    }
+
+    libvlc_instance_t *vlc = libvlc_new(argc, argv);
+    assert(vlc != NULL);
+
+    tracer_ctx_Init(&tracer_ctx);
+
+    struct vlc_tracer *tracer = 
vlc_tracer_Create(VLC_OBJECT(vlc->p_libvlc_int),
+                                                  MODULE_STRING);
+    assert(tracer != NULL);
+
+    for (size_t i = 0; i < count; ++i)
+    {
+        if (scenario_name == NULL
+         || strcmp(scenario_name, scenarios[i].name) == 0)
+            play_scenario(vlc->p_libvlc_int, tracer, &scenarios[i]);
+    }
+
+    vlc_tracer_Destroy(tracer);
+
+    tracer_ctx_Destroy(&tracer_ctx);
+
+    libvlc_release(vlc);
+}
+
+static void normal_update(const struct clock_ctx *ctx, size_t index,
+                          vlc_tick_t *system, vlc_tick_t stream)
+{
+    (void) index;
+    const struct clock_scenario *scenario = ctx->scenario;
+
+    vlc_tick_t drift =
+        vlc_clock_Update(ctx->master, *system, stream, 1.0f);
+    /* The master can't drift */
+    assert(drift == VLC_TICK_INVALID);
+
+    /* Check the slave is drifting (only system is moving) */
+    drift = vlc_clock_Update(ctx->slave, *system,
+                             VLC_TICK_0, 1.0f);
+    assert(drift == - (stream - VLC_TICK_0));
+
+    *system += scenario->stream_increment;
+}
+
+static void check_no_event_error(size_t expected_update_count)
+{
+    /* assert that there is no error/status */
+    assert(tracer_ctx.events.size > 0);
+
+    size_t update_count = 0;
+    for (size_t i = 0; i < tracer_ctx.events.size; ++i)
+    {
+        struct tracer_event event = tracer_ctx.events.data[i];
+        switch (event.type)
+        {
+            case TRACER_EVENT_TYPE_UPDATE:
+                update_count++;
+                break;
+            case TRACER_EVENT_TYPE_STATUS:
+                switch (event.status)
+                {
+                    case TRACER_EVENT_STATUS_RESET_USER:
+                    case TRACER_EVENT_STATUS_RESET_BADSOURCE:
+                        assert("clock reset not expected" == NULL);
+                        break;
+                }
+                break;
+        }
+    }
+
+    assert(update_count == expected_update_count);
+}
+
+static void normal_check(const struct clock_ctx *ctx, size_t update_count,
+                         vlc_tick_t expected_system_end,
+                         vlc_tick_t stream_end)
+{
+    (void) expected_system_end; (void) stream_end;
+    const struct clock_scenario *scenario = ctx->scenario;
+
+    check_no_event_error(update_count);
+
+    for (size_t i = 0; i < tracer_ctx.events.size; ++i)
+    {
+        struct tracer_event event = tracer_ctx.events.data[i];
+        if (event.type == TRACER_EVENT_TYPE_UPDATE)
+        {
+            assert(event.update.coeff == 1.0f);
+            assert(event.update.offset ==
+                   scenario->system_start - scenario->stream_start);
+        }
+    }
+
+    vlc_tick_t converted =
+        vlc_clock_ConvertToSystem(ctx->slave, expected_system_end,
+                                  stream_end, 1.0f);
+    assert(converted == expected_system_end);
+}
+
+static void lowprecision_update(const struct clock_ctx *ctx, size_t index,
+                                vlc_tick_t *system, vlc_tick_t stream)
+{
+    (void) index;
+    const struct clock_scenario *scenario = ctx->scenario;
+
+    vlc_tick_t base_system = stream - scenario->stream_start
+                           + scenario->system_start;
+    /* random imprecision (seed based on stream) */
+    srand(stream);
+    vlc_tick_t imprecision = rand() % VLC_TICK_FROM_MS(5);
+    *system = base_system + imprecision;
+
+    vlc_tick_t drift =
+        vlc_clock_Update(ctx->master, *system, stream, 1.0f);
+    /* The master can't drift */
+    assert(drift == VLC_TICK_INVALID);
+}
+
+static void lowprecision_check(const struct clock_ctx *ctx, size_t 
update_count,
+                               vlc_tick_t expected_system_end,
+                               vlc_tick_t stream_end)
+{
+    (void) ctx; (void) expected_system_end; (void) stream_end;
+
+    check_no_event_error(update_count);
+
+    static const double epsilon = 0.005;
+
+    for (size_t i = 0; i < tracer_ctx.events.size; ++i)
+    {
+        struct tracer_event event = tracer_ctx.events.data[i];
+        if (event.type == TRACER_EVENT_TYPE_UPDATE)
+            assert(fabs(event.update.coeff - 1.0f) <= epsilon);
+
+    }
+}
+
+static void drift_update(const struct clock_ctx *ctx, size_t index,
+                         vlc_tick_t *system, vlc_tick_t stream)
+{
+    (void) index;
+    const struct clock_scenario *scenario = ctx->scenario;
+
+    vlc_tick_t drift =
+        vlc_clock_Update(ctx->master, *system, stream, 1.0f);
+    /* The master can't drift */
+    assert(drift == VLC_TICK_INVALID);
+
+    *system += scenario->stream_increment;
+
+    /* Simulate 1us drift every stream_increment */
+    if (scenario->total_drift_duration > 0)
+        *system += VLC_TICK_FROM_US(1);
+    else
+        *system -= VLC_TICK_FROM_US(1);
+}
+
+static void drift_check(const struct clock_ctx *ctx, size_t update_count,
+                        vlc_tick_t expected_system_end,
+                        vlc_tick_t stream_end)
+{
+    const struct clock_scenario *scenario = ctx->scenario;
+
+    check_no_event_error(update_count);
+
+    vlc_tick_t converted =
+        vlc_clock_ConvertToSystem(ctx->slave, expected_system_end,
+                                  stream_end, 1.0f);
+
+    assert(converted - expected_system_end == scenario->total_drift_duration);
+}
+
+static void drift_sudden_update(const struct clock_ctx *ctx, size_t index,
+                                vlc_tick_t *system, vlc_tick_t stream)
+{
+    (void) index;
+    const struct clock_scenario *scenario = ctx->scenario;
+
+    vlc_tick_t drift =
+        vlc_clock_Update(ctx->master, *system, stream, 1.0f);
+    /* The master can't drift */
+    assert(drift == VLC_TICK_INVALID);
+
+    *system += scenario->stream_increment;
+
+    if (stream - scenario->stream_start >= scenario->duration * 3 / 4)
+    {
+        /* Simulate a sudden high drift */
+        *system += VLC_TICK_FROM_US(4);
+    }
+}
+
+#define VLC_TICK_24H VLC_TICK_FROM_SEC(24 * 60 * 60)
+#define VLC_TICK_2H VLC_TICK_FROM_SEC(2 * 60 * 60)
+#define DEFAULT_STREAM_INCREMENT VLC_TICK_FROM_MS(100)
+
+#define INIT_SYSTEM_STREAM_TIMING(duration_, increment_) \
+    .stream_start = VLC_TICK_0 + VLC_TICK_FROM_MS(31000000), \
+    .system_start = VLC_TICK_INVALID, \
+    .duration = duration_, \
+    .stream_increment = increment_
+
+#define DRIFT_SCENARIO(name_, desc_, duration_, increment_, drift_) \
+    .name = name_, \
+    .desc = desc_, \
+    INIT_SYSTEM_STREAM_TIMING(duration_, increment_), \
+    .total_drift_duration = drift_, \
+    .update = drift_update, \
+    .check = drift_check,
+
+static struct clock_scenario clock_scenarios[] = {
+{
+    .name = "normal",
+    .desc = "normal update has a coeff of 1.0f",
+    INIT_SYSTEM_STREAM_TIMING(VLC_TICK_2H, DEFAULT_STREAM_INCREMENT),
+    .update = normal_update,
+    .check = normal_check,
+},
+{
+    .name = "lowprecision",
+    .desc = "low precision update has a coeff near 1.0f",
+    INIT_SYSTEM_STREAM_TIMING(VLC_TICK_2H, DEFAULT_STREAM_INCREMENT),
+    .update = lowprecision_update,
+    .check = lowprecision_check,
+},
+{
+    DRIFT_SCENARIO(
+        "drift_72",
+        "a drift of 72ms in 2h is handled",
+        VLC_TICK_2H,
+        DEFAULT_STREAM_INCREMENT,
+        VLC_TICK_FROM_MS(72)
+)},
+{
+    DRIFT_SCENARIO(
+        "drift_-72",
+        "a drift of -72ms in 2h is handled",
+        VLC_TICK_2H,
+        DEFAULT_STREAM_INCREMENT,
+        -VLC_TICK_FROM_MS(72)
+    )
+},
+{
+    DRIFT_SCENARIO(
+        "drift_864",
+        "a drift of 864ms in 24h is handled",
+        VLC_TICK_24H,
+        DEFAULT_STREAM_INCREMENT,
+        VLC_TICK_FROM_MS(864)
+    )
+},
+{
+    DRIFT_SCENARIO(
+        "drift_-864",
+        "a drift of -864ms in 24h is handled",
+        VLC_TICK_24H,
+        DEFAULT_STREAM_INCREMENT,
+        -VLC_TICK_FROM_MS(864)
+    )
+},
+{
+    .name = "drift_sudden",
+    .desc = "a sudden drift is handled",
+    INIT_SYSTEM_STREAM_TIMING(VLC_TICK_24H, DEFAULT_STREAM_INCREMENT),
+    .total_drift_duration = VLC_TICK_FROM_MS(864),
+    .update = drift_sudden_update,
+    .check = drift_check,
+},
+};
+
+int main(int argc, const char *argv[])
+{
+    test_init();
+    run_scenarios(argc, argv, clock_scenarios, ARRAY_SIZE(clock_scenarios));
+}



View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/d97fe9750e8eee3844327f075d9804a09ac5c106...08950258ef982204319ea48af93603ffef19bd5c

-- 
View it on GitLab: 
https://code.videolan.org/videolan/vlc/-/compare/d97fe9750e8eee3844327f075d9804a09ac5c106...08950258ef982204319ea48af93603ffef19bd5c
You're receiving this email because of your account on code.videolan.org.


VideoLAN code repository instance
_______________________________________________
vlc-commits mailing list
vlc-commits@videolan.org
https://mailman.videolan.org/listinfo/vlc-commits

Reply via email to