[vlc-commits] [Git][videolan/vlc][master] 2 commits: audio_output: stop resampling on flush
Jean-Baptiste Kempf (@jbk)
gitlab at videolan.org
Thu Oct 15 18:20:23 UTC 2026
Jean-Baptiste Kempf pushed to branch master at VideoLAN / VLC
Commits:
ddfc5e96 by Alexandre Janniaux at 2026-10-15T19:54:36+02:00
audio_output: stop resampling on flush
Even after stream_Reset(), the resampling state was not reset and thus
was kept after a flush: stream_Reset() flushed the filters but didn't
reset the resampling rate and up/down direction, so the resampling
started before a seek kept being applied to the audio played after it,
even though its drift no longer had anything to do with the new
data/clock time. The drift of the new timeline was then only corrected
once the old resampling had run its course, which is particularily
audible when using ugly resampler(s).
Reset the resampling rate and type when resetting the stream, so the
drift after a flush is recomputed and corrected from scratch. After this
commit, the resampling properly and audibly stops right after the seek.
- - - - -
05189ac4 by Alexandre Janniaux at 2026-10-15T19:54:36+02:00
test: audio_output: check resampling stops on flush
This reproduce the issue with resampling not properly stopping on seek
even though the audio output has been flushed.
- - - - -
4 changed files:
- src/audio_output/dec.c
- test/Makefile.am
- + test/src/audio_output/dec.c
- test/src/meson.build
Changes:
=====================================
src/audio_output/dec.c
=====================================
@@ -287,7 +287,11 @@ static void stream_Reset(vlc_aout_stream *stream)
vlc_audio_meter_Flush(&owner->meter);
if (stream->filters)
+ {
aout_FiltersFlush (stream->filters);
+ aout_FiltersAdjustResampling (stream->filters, 0);
+ }
+ stream->sync.resamp_type = AOUT_RESAMPLING_NONE;
vlc_clock_Lock(stream->sync.clock);
vlc_clock_Reset(stream->sync.clock);
=====================================
test/Makefile.am
=====================================
@@ -49,6 +49,7 @@ check_PROGRAMS = \
test_libvlc_media_discoverer \
test_libvlc_renderer_discoverer \
test_libvlc_slaves \
+ test_src_audio_output_dec \
test_src_config_chain \
test_src_clock_clock \
test_src_clock_start \
@@ -212,6 +213,21 @@ test_libvlc_slaves_LDADD = $(LIBVLCCORE) $(LIBVLC)
test_libvlc_meta_SOURCES = libvlc/meta.c
test_libvlc_meta_LDADD = $(LIBVLCCORE) $(LIBVLC)
+test_src_audio_output_dec_SOURCES = src/audio_output/dec.c \
+ ../src/audio_output/dec.c \
+ ../src/audio_output/output.c \
+ ../src/audio_output/common.c \
+ ../src/audio_output/filters.c \
+ ../src/audio_output/volume.c \
+ ../src/audio_output/meter.c \
+ ../src/input/replay_gain.c \
+ ../src/misc/objects.c \
+ ../src/misc/variables.c \
+ ../src/clock/clock.c \
+ ../src/clock/clock_internal.c
+test_src_audio_output_dec_CPPFLAGS = $(AM_CPPFLAGS) -I$(top_srcdir)/src
+test_src_audio_output_dec_LDADD = $(LIBVLCCORE) $(LIBVLC) $(LIBM)
+
test_src_clock_clock_SOURCES = src/clock/clock.c \
../src/clock/clock.c \
../src/clock/clock_internal.c
=====================================
test/src/audio_output/dec.c
=====================================
@@ -0,0 +1,257 @@
+/*****************************************************************************
+ * audio_output/dec.c: unit test for the audio output
+ *****************************************************************************
+ * Copyright (C) 2026 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <vlc_common.h>
+#include <vlc_tick.h>
+#include <vlc_es.h>
+#include <vlc_block.h>
+#include <vlc_fourcc.h>
+#include <vlc_aout.h>
+#include <vlc_filter.h>
+#include <vlc_modules.h>
+
+#include "../../../src/audio_output/aout_internal.h"
+#include "../../../src/clock/clock.h"
+
+#include <vlc/vlc.h>
+#include "../../libvlc/test.h"
+#include "../../../lib/libvlc_internal.h"
+
+#define MODULE_NAME test_audio_output_dec
+#undef VLC_DYNAMIC_PLUGIN
+#include <vlc_plugin.h>
+
+/* Define a builtin module for the mocked audio output. */
+const char vlc_module_name[] = MODULE_STRING;
+
+static void aout_Play(audio_output_t *aout, block_t *block, vlc_tick_t date)
+{
+ (void) aout; (void) date;
+ block_Release(block);
+}
+
+static void aout_Flush(audio_output_t *aout)
+{
+ (void) aout;
+}
+
+static int aout_Start(audio_output_t *aout, audio_sample_format_t *restrict fmt)
+{
+ (void) aout;
+ return AOUT_FMT_LINEAR(fmt) ? VLC_SUCCESS : VLC_EGENERIC;
+}
+
+static void aout_Stop(audio_output_t *aout)
+{
+ (void) aout;
+}
+
+static int aout_Open(vlc_object_t *obj)
+{
+ audio_output_t *aout = (audio_output_t *)obj;
+ aout->start = aout_Start;
+ aout->play = aout_Play;
+ aout->pause = NULL;
+ aout->flush = aout_Flush;
+ aout->stop = aout_Stop;
+ aout->drain = NULL;
+ aout->volume_set = NULL;
+ aout->mute_set = NULL;
+ aout->device_select = NULL;
+ return VLC_SUCCESS;
+}
+
+static int resampling;
+
+static block_t *resampler_Resample(filter_t *f, block_t *in)
+{
+ resampling = (int)f->fmt_in.audio.i_rate - (int)f->fmt_out.audio.i_rate;
+ return in;
+}
+
+static int resampler_Open(vlc_object_t *obj)
+{
+ static const struct vlc_filter_operations ops = {
+ .filter_audio = resampler_Resample,
+ };
+ ((filter_t *)obj)->ops = &ops;
+ return VLC_SUCCESS;
+}
+
+vlc_module_begin()
+ set_capability("audio output", 0)
+ set_callback(aout_Open)
+ add_submodule()
+ set_capability("audio resampler", 9999)
+ set_callback(resampler_Open)
+vlc_module_end()
+
+VLC_EXPORT const vlc_plugin_cb vlc_static_modules[] = {
+ VLC_SYMBOL(vlc_entry),
+ NULL
+};
+
+/* Stubs to remove vout from linking */
+void *vout_Create(void *obj);
+void *vout_Create(void *obj) { (void) obj; return NULL; }
+int vout_Request(const void *cfg, void *vctx, void *input);
+int vout_Request(const void *cfg, void *vctx, void *input)
+{ (void) cfg; (void) vctx; (void) input; return -1; }
+bool config_IsSafe(const char *name);
+bool config_IsSafe(const char *name) { (void) name; return false; }
+
+static vlc_tick_t mock_now = VLC_TICK_0;
+
+vlc_tick_t vlc_tick_now(void)
+{
+ return mock_now;
+}
+
+#define NB_SAMPLES 1024
+
+static block_t *make_frame(const audio_sample_format_t *fmt, vlc_tick_t pts)
+{
+ block_t *block = block_Alloc(NB_SAMPLES * fmt->i_bytes_per_frame);
+ assert(block != NULL);
+ block->i_nb_samples = NB_SAMPLES;
+ block->i_pts = block->i_dts = pts;
+ return block;
+}
+
+static void start_output(vlc_clock_t *clock, vlc_tick_t system_ts,
+ vlc_tick_t ts)
+{
+ vlc_clock_Lock(clock);
+ vlc_clock_Start(clock, system_ts, ts);
+ vlc_clock_Unlock(clock);
+}
+
+static void test_flush_stops_resampling(libvlc_int_t *vlc)
+{
+ struct vlc_logger *logger = vlc->obj.logger;
+
+ audio_sample_format_t fmt;
+ memset(&fmt, 0, sizeof(fmt));
+ fmt.i_format = VLC_CODEC_S16N;
+ fmt.i_rate = 48000;
+ fmt.i_physical_channels = AOUT_CHANS_STEREO;
+ aout_FormatPrepare(&fmt);
+
+ const vlc_tick_t frame_len = vlc_tick_from_samples(NB_SAMPLES, fmt.i_rate);
+
+ vlc_clock_main_t *clockbus = vlc_clock_main_New(logger, NULL);
+ assert(clockbus != NULL);
+
+ vlc_clock_main_Lock(clockbus);
+ vlc_clock_main_SetInputDejitter(clockbus, 0);
+ vlc_clock_main_SetDejitter(clockbus, 0);
+ vlc_clock_t *input = vlc_clock_main_CreateInputMaster(clockbus);
+ vlc_clock_t *aout_clock =
+ vlc_clock_main_CreateSlave(clockbus, "audio", AUDIO_ES, NULL, NULL);
+ assert(input != NULL && aout_clock != NULL);
+
+ const vlc_tick_t sys0 = VLC_TICK_0 + VLC_TICK_FROM_MS(1000);
+ const vlc_tick_t stream0 = VLC_TICK_0 + VLC_TICK_FROM_MS(1000);
+ mock_now = sys0;
+ vlc_clock_Start(input, sys0, stream0);
+ vlc_clock_main_Unlock(clockbus);
+
+ var_Create(vlc, "aout", VLC_VAR_STRING);
+ var_SetString(vlc, "aout", MODULE_STRING);
+
+ audio_output_t *aout = aout_New(vlc);
+ assert(aout != NULL);
+
+ const struct vlc_aout_stream_cfg cfg = {
+ .fmt = &fmt,
+ .profile = 0,
+ .clock = aout_clock,
+ .str_id = "audio",
+ .replay_gain = NULL,
+ };
+ vlc_aout_stream *stream = vlc_aout_stream_New(aout, &cfg);
+ assert(stream != NULL);
+
+ start_output(aout_clock, sys0, stream0);
+ resampling = 0;
+ vlc_aout_stream_Play(stream, make_frame(&fmt, stream0));
+ assert(resampling == 0);
+
+ /* output clock
+ * reports start
+ * playing time
+ * --------|-------------------|----------------------> time
+ * -100ms 0ms
+ *
+ * The output reports playing 100ms behind the clock, which will
+ * trigger resampling as it's less than the "too late" but more
+ * than the AOUT_MAX_PTS_DELAY. */
+
+ aout_TimingReport(aout, mock_now, stream0 - VLC_TICK_FROM_MS(100));
+ vlc_aout_stream_Play(stream, make_frame(&fmt, stream0 + frame_len));
+ vlc_aout_stream_Play(stream, make_frame(&fmt, stream0 + 2 * frame_len));
+ assert(resampling > 0);
+
+ /* In case of seek, the audio output is flushed, and we should then
+ * re-evaluate whether we still need to resample. */
+ vlc_aout_stream_Flush(stream);
+
+ const vlc_tick_t sys1 = sys0 + VLC_TICK_FROM_MS(500);
+ const vlc_tick_t stream1 = stream0 + VLC_TICK_FROM_SEC(30);
+ mock_now = sys1;
+ vlc_clock_main_Lock(clockbus);
+ vlc_clock_Reset(input);
+ vlc_clock_Start(input, sys1, stream1);
+ vlc_clock_main_Unlock(clockbus);
+ start_output(aout_clock, sys1, stream1);
+
+ vlc_aout_stream_Play(stream, make_frame(&fmt, stream1));
+ assert(resampling == 0);
+
+ vlc_aout_stream_Delete(stream);
+
+ aout_Release(aout);
+
+ vlc_clock_Delete(aout_clock);
+ vlc_clock_Delete(input);
+ vlc_clock_main_Delete(clockbus);
+}
+
+int main(void)
+{
+ test_init();
+
+ libvlc_instance_t *vlc = libvlc_new(0, NULL);
+ assert(vlc != NULL);
+
+ test_flush_stops_resampling(vlc->p_libvlc_int);
+
+ libvlc_release(vlc);
+ return 0;
+}
=====================================
test/src/meson.build
=====================================
@@ -45,6 +45,26 @@ vlc_tests += {
'link_with' : [libvlc, libvlccore],
}
+vlc_tests += {
+ 'name' : 'test_src_audio_output_dec',
+ 'sources' : files(
+ 'audio_output/dec.c',
+ '../../src/audio_output/dec.c',
+ '../../src/audio_output/output.c',
+ '../../src/audio_output/common.c',
+ '../../src/audio_output/filters.c',
+ '../../src/audio_output/volume.c',
+ '../../src/audio_output/meter.c',
+ '../../src/input/replay_gain.c',
+ '../../src/misc/objects.c',
+ '../../src/misc/variables.c',
+ '../../src/clock/clock.c',
+ '../../src/clock/clock_internal.c'),
+ 'suite' : ['src', 'test_src'],
+ 'link_with' : [libvlc, libvlccore],
+ 'include_directories' : include_directories('../../src'),
+}
+
vlc_tests += {
'name' : 'test_src_misc_variables',
'sources' : files('misc/variables.c'),
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/99733c5ddd42ec95a46f7e3fe106297524be825b...05189ac4372b40f4ccbc406e28a79410cd487590
--
View it on GitLab: https://code.videolan.org/videolan/vlc/-/compare/99733c5ddd42ec95a46f7e3fe106297524be825b...05189ac4372b40f4ccbc406e28a79410cd487590
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