feat(capture): AUDIO_OFFSET_MS knob for fixed A/V alignment trim
The deltacast bridge captures audio and video on separate VHD streams; any constant capture-path latency difference shows as a fixed A/V offset (e.g. audio slightly ahead of video) even though stream lengths stay locked (no drift, verified ~1 frame over 461s). AUDIO_OFFSET_MS applies an -itsoffset on the SDI/Deltacast audio input only: positive DELAYS audio (audio-ahead case), negative advances it. Default 0 = no change, fully non-destructive, clamped to +/-1000ms. Lets an operator dial out residual offset with a lipsync loop without a code change.
This commit is contained in:
parent
e64281c9fd
commit
c40de38c45
1 changed files with 17 additions and 0 deletions
|
|
@ -197,6 +197,21 @@ const CAPTURE_GPU_INDEX = (() => {
|
||||||
// as an encoder option right after -c:v. Returns [] when no pin is configured.
|
// 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)] : []);
|
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) {
|
function hevcNvencArgs(framerate, growing) {
|
||||||
const base = ['-c:v', 'hevc_nvenc', ...nvencGpuSel(), '-preset', 'p4', '-rc', 'vbr', '-profile:v', 'main10'];
|
const base = ['-c:v', 'hevc_nvenc', ...nvencGpuSel(), '-preset', 'p4', '-rc', 'vbr', '-profile:v', 'main10'];
|
||||||
if (growing) {
|
if (growing) {
|
||||||
|
|
@ -821,6 +836,8 @@ class CaptureManager {
|
||||||
'-f', 's16le',
|
'-f', 's16le',
|
||||||
'-ar', '48000',
|
'-ar', '48000',
|
||||||
'-ac', '2',
|
'-ac', '2',
|
||||||
|
// Optional fixed A/V trim (env AUDIO_OFFSET_MS); default empty = no shift.
|
||||||
|
...audioOffsetArgs(),
|
||||||
'-i', audioFifoPath,
|
'-i', audioFifoPath,
|
||||||
],
|
],
|
||||||
isNetwork: false,
|
isNetwork: false,
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue