diff --git a/services/capture/src/capture-manager.js b/services/capture/src/capture-manager.js index e12b2a2..01ada3b 100644 --- a/services/capture/src/capture-manager.js +++ b/services/capture/src/capture-manager.js @@ -197,6 +197,21 @@ const CAPTURE_GPU_INDEX = (() => { // as an encoder option right after -c:v. Returns [] when no pin is configured. const nvencGpuSel = () => (CAPTURE_GPU_INDEX != null ? ['-gpu', String(CAPTURE_GPU_INDEX)] : []); +// Optional fixed A/V alignment trim for the SDI/Deltacast audio input. The +// deltacast bridge captures audio and video on separate VHD streams; any +// constant capture-path latency difference between them shows as a fixed A/V +// offset (e.g. audio slightly ahead of video) even though stream LENGTHS stay +// locked (no drift). AUDIO_OFFSET_MS lets an operator dial that out without a +// rebuild: POSITIVE value DELAYS audio (use when audio is AHEAD of video), +// NEGATIVE advances it. Applied as ffmpeg `-itsoffset` on the audio input only. +// Default 0 = no change (fully non-destructive). Range-clamped to ±1000 ms. +const audioOffsetArgs = () => { + const raw = parseFloat(process.env.AUDIO_OFFSET_MS || '0'); + if (!Number.isFinite(raw) || raw === 0) return []; + const ms = Math.max(-1000, Math.min(1000, raw)); + return ['-itsoffset', (ms / 1000).toFixed(4)]; +}; + function hevcNvencArgs(framerate, growing) { const base = ['-c:v', 'hevc_nvenc', ...nvencGpuSel(), '-preset', 'p4', '-rc', 'vbr', '-profile:v', 'main10']; if (growing) { @@ -821,6 +836,8 @@ class CaptureManager { '-f', 's16le', '-ar', '48000', '-ac', '2', + // Optional fixed A/V trim (env AUDIO_OFFSET_MS); default empty = no shift. + ...audioOffsetArgs(), '-i', audioFifoPath, ], isNetwork: false,