Skip to content

fix: vision mixer A/V sync and multiview latency#502

Merged
srperens merged 10 commits intomainfrom
fix/vision-mixer-av-sync-and-latency
Apr 17, 2026
Merged

fix: vision mixer A/V sync and multiview latency#502
srperens merged 10 commits intomainfrom
fix/vision-mixer-av-sync-and-latency

Conversation

@srperens
Copy link
Copy Markdown
Collaborator

Summary

  • audiomixer start-time-selection: Changed from first to zero to match compositor, fixing A/V offset when audio and video pass through separate aggregators
  • Configurable tsdemux latency: Added tsdemux_latency property (default 100ms) to MPEGTSSRT input block, down from GStreamer's 700ms default
  • Multiview compositor latency: Suppressed latency query stacking between distribution and multiview compositors, and fixed overlay appsrc stall by pushing at multiview framerate with proper timestamping

Test plan

  • cargo check — compiles cleanly
  • All 23 tests pass (unit, integration, openapi snapshot, pipeline lifecycle)
  • Manual: verify multiview output latency is noticeably reduced
  • Manual: verify A/V sync on PGM and multiview outputs
  • Manual: verify overlay labels/clock update correctly on multiview

🤖 Generated with Claude Code

Per Enstedt and others added 10 commits April 14, 2026 17:22
The audiomixer used start-time-selection=first while the vision mixer
compositor uses start-time-selection=zero. When audio and video pass
through separate aggregators (e.g. audio mixer + vision mixer in the
same flow), this mismatch causes a permanent A/V offset because the
two aggregators pick different time bases for their output.

Change audiomixer to start-time-selection=zero to match. This also
guards against the same GStreamer 1.26 race condition that motivated
the compositor change.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
GStreamer defaults tsdemux latency to 700ms for PCR synchronization.
This adds a tsdemux_latency property (default 100ms) to the MPEGTSSRT
input block, reducing end-to-end latency in live pipelines while keeping
enough margin for PCR-based A/V sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Two issues caused the multiview compositor (mv_comp) to add excessive
latency:

1. Latency query stacking: the PGM feed from the distribution
   compositor (mixer) is tee'd into mv_comp. The latency query from
   mv_comp traversed back through mixer, causing both compositors'
   latencies to stack. Fixed by suppressing the latency query on the
   queue_pgm_mv sink pad so mv_comp's peer latency is determined by
   its direct input paths instead.

2. Overlay stall: the overlay appsrc pushed at ~1fps (clock updates)
   with PTS=0. The aggregator saw the overlay pad as perpetually stale
   and waited up to its full deadline on every frame. Fixed by pushing
   at the multiview framerate (re-pushing the last sample when content
   hasn't changed) and using do-timestamp=true instead of PTS=0.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The tsdemux "latency" property only affects the reported latency in
GStreamer's latency query, not internal buffering. For live MPEG-TS
over SRT, 0ms is appropriate since SRT already handles jitter.

Add an "Ignore PCR" boolean property (default true) that sets
ignore-pcr on tsdemux. For live pipelines where SRT handles clock
recovery, PCR-based timestamp adjustments are unnecessary and can
interfere with timing.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Add a "TS Offset (ms)" property that applies a timestamp offset to all
clocksync elements inside whepserversink. A negative value makes the
output play out earlier than the pipeline latency dictates.

This enables multiview outputs to display with minimal delay (by setting
a negative offset that compensates for upstream pipeline latency) while
PGM outputs retain full pipeline latency for proper sync.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
The ts-offset approach on clocksync elements was ineffective because
webrtcsink's internal appsink has sync=true, which re-applies clock
synchronization downstream of clocksync — the buffer is released
earlier by clocksync but then waits in the appsink until the pipeline
latency elapses.

Replace with a "Low Latency" boolean property that sets sync=false on
the internal appsink elements via deep-element-added. This makes
buffers flow through immediately without waiting for the pipeline's
negotiated latency. A/V sync is maintained on the receiver side via
RTCP sender reports.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…anel

Replace the low_latency bool on WHEP output with a ts_offset_ms integer
for precise playout timing control. Add SessionThreadConfig to propagate
thread priority/affinity to dynamically created WebRTC session pipelines
via pad probes. Add a WebRTC stats overlay to the WHEP player (bitrate,
jitter buffer, packet loss, A/V sync offset). Clean up stale comments
and remove try_elevate_scheduling_privileges code.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Overlay repush was re-pushing the same GstSample, forcing BaseSrc to
copy the entire buffer (8 MB at 1080p) on every non-dirty frame due to
shared refcount. Store pixel data as Arc<[u8]> and wrap in a fresh
buffer per push — only an Arc refcount bump, no pixel copy.

Replace sleep(frame_interval) with deadline-based sleep so render/push
time doesn't accumulate as drift.

Clamp tsdemux_latency i64→i32 to GStreamer's [-1, i32::MAX] range and
use saturating_mul for WHEP ts-offset ns conversion.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…overlay timer spin

The is_active() guard on SessionThreadConfig was evaluated at block-build
time, before populate() is called in start(). The OnceLock was always empty
so session thread priority was never installed on WHEP session pipelines.
Move the check inside the consumer-pipeline-created callback where it runs
after populate().

Also clamp the overlay timer when it falls behind by more than one frame
to avoid a catch-up spin burst, and lower latency query log to trace level.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@srperens srperens merged commit 46c834e into main Apr 17, 2026
7 checks passed
@srperens srperens deleted the fix/vision-mixer-av-sync-and-latency branch April 17, 2026 08:35
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant