From 8ceb671cc8788c2cf7945ae084b758d88589d045 Mon Sep 17 00:00:00 2001 From: thewrz Date: Fri, 27 Feb 2026 21:54:22 -0800 Subject: [PATCH] module-vban: derive write position from frame counter Instead of writing packets sequentially and losing sync on any frame gap, compute the write position from the VBAN header's n_frames field. Out-of-order packets land at the correct ringbuffer offset, matching how module-rtp handles this. Only advance the writeindex when a packet extends the frontier so that late arrivals fill gaps without moving the pointer backwards. Fixes: https://gitlab.freedesktop.org/pipewire/pipewire/-/issues/5145 --- src/modules/module-vban/audio.c | 39 ++++++++++++++++++++------------ src/modules/module-vban/stream.c | 1 + 2 files changed, 25 insertions(+), 15 deletions(-) diff --git a/src/modules/module-vban/audio.c b/src/modules/module-vban/audio.c index 099246895..40a6415dc 100644 --- a/src/modules/module-vban/audio.c +++ b/src/modules/module-vban/audio.c @@ -98,38 +98,43 @@ static int vban_audio_receive(struct impl *impl, uint8_t *buffer, ssize_t len) samples = SPA_MIN(hdr->format_nbs+1, plen / stride); n_frames = hdr->n_frames; - if (impl->have_sync && impl->n_frames != n_frames) { - pw_log_info("unexpected frame (%d != %d)", - n_frames, impl->n_frames); + + if (impl->samples_per_frame == 0) { + impl->samples_per_frame = samples; + } else if (samples != impl->samples_per_frame) { + pw_log_warn("samples_per_frame changed (%u != %u)", + samples, impl->samples_per_frame); + impl->samples_per_frame = samples; impl->have_sync = false; } + + if (impl->have_sync && impl->n_frames != n_frames) { + pw_log_info("unexpected frame (%u != %u)", + n_frames, impl->n_frames); + } impl->n_frames = n_frames + 1; - timestamp = impl->timestamp; - impl->timestamp += samples; + /* derive write position from frame counter, like module-rtp */ + timestamp = n_frames * impl->samples_per_frame; + write = timestamp + impl->target_buffer; filled = spa_ringbuffer_get_write_index(&impl->ring, &expected_write); - /* we always write to timestamp + delay */ - write = timestamp + impl->target_buffer; - if (!impl->have_sync) { - pw_log_info("sync to timestamp:%u target:%u", - timestamp, impl->target_buffer); + pw_log_info("sync to n_frames:%u timestamp:%u target:%u", + n_frames, timestamp, impl->target_buffer); /* we read from timestamp, keeping target_buffer of data * in the ringbuffer. */ impl->ring.readindex = timestamp; impl->ring.writeindex = write; filled = impl->target_buffer; + expected_write = write; spa_dll_init(&impl->dll); spa_dll_set_bw(&impl->dll, SPA_DLL_BW_MAX, 128, impl->rate); memset(impl->buffer, 0, BUFFER_SIZE); impl->have_sync = true; - } else if (expected_write != write) { - pw_log_debug("unexpected write (%u != %u)", - write, expected_write); } if (filled + samples > BUFFER_SIZE / stride) { @@ -137,14 +142,17 @@ static int vban_audio_receive(struct impl *impl, uint8_t *buffer, ssize_t len) BUFFER_SIZE / stride); impl->have_sync = false; } else { - pw_log_trace("got samples:%u", samples); + pw_log_trace("got n_frames:%u samples:%u", n_frames, samples); spa_ringbuffer_write_data(&impl->ring, impl->buffer, BUFFER_SIZE, (write * stride) & BUFFER_MASK, &buffer[hlen], (samples * stride)); + + /* only advance writeindex if this extends the frontier */ write += samples; - spa_ringbuffer_write_update(&impl->ring, write); + if ((int32_t)(write - expected_write) > 0) + spa_ringbuffer_write_update(&impl->ring, write); } return 0; } @@ -262,5 +270,6 @@ static int vban_audio_init(struct impl *impl, enum spa_direction direction) else impl->stream_events.process = vban_audio_process_playback; impl->receive_vban = vban_audio_receive; + impl->samples_per_frame = 0; return 0; } diff --git a/src/modules/module-vban/stream.c b/src/modules/module-vban/stream.c index da3469583..168ecc837 100644 --- a/src/modules/module-vban/stream.c +++ b/src/modules/module-vban/stream.c @@ -61,6 +61,7 @@ struct impl { struct vban_header header; uint32_t timestamp; uint32_t n_frames; + uint32_t samples_per_frame; struct spa_ringbuffer ring; uint8_t buffer[BUFFER_SIZE];