Re-architect the Deltacast bridge from two independently-buffered VHD
streams (DISJOINED_VIDEO + a separate DISJOINED_ANC audio stream) to a
single VHD_SDI_STPROC_JOINED RX stream per port. Each locked slot now
carries both the video frame and that frame's SDI-embedded audio, so
audio is extracted (VHD_SlotExtractAudio) from the SAME slot the video
came from — eliminating the constant "audio ahead of video" offset at
its root instead of papering over it with --audio-delay-ms.
This mirrors Deltacast's own FFmpeg fork (libavdevice/videomaster_common.c):
open JOINED, then per frame LockSlot -> GetSlotBuffer(VIDEO) ->
SlotExtractAudio(same slot) -> Unlock.
Changes (main.c):
- main(): RX stream opened with VHD_SDI_STPROC_JOINED (was
VHD_SDI_STPROC_DISJOINED_VIDEO). SDI interface still propagated so
audio extraction yields real samples.
- video_thread(): becomes the single per-frame consumer. After locking
each JOINED slot it extracts the embedded audio (audio_extract_slot)
and pushes de-interleaved s16le stereo PCM into a new lock-free SPSC
ring (ApcmRing), then writes the video frame to the framecache ring
(or legacy video FIFO). Audio is extracted on BOTH the framecache and
legacy-FIFO paths, before the video packing check so it is never
dropped on a rare packing mismatch.
- audio_thread(): no longer opens any VHD stream. It is now purely the
audio-FIFO sink: drains the ApcmRing into the named audio FIFO
(ffmpeg input 1), flushes the ring to the live edge on reader attach,
survives ffmpeg restarts (EPIPE -> reopen), and emits wall-clock-paced
silence when the ring is empty (preserves the silence-fallback when the
signal carries no embedded audio).
- Audio stays 48 kHz stereo s16le to match ffmpeg's expectations.
- --audio-delay-ms / FC_AUDIO_DELAY_MS retained but now unnecessary
(should be left at 0); kept for compatibility / emergency tuning.
Compiles clean (only pre-existing %lu/ULONG format warnings in main()).
Not installed, not deployed — branch only.