feat(capture): replace deltacast _buildInputArgs stub with real bridge spawn

This commit is contained in:
Zac Gaetano 2026-06-01 07:54:17 -04:00
parent 6fec41aaf9
commit 4da7bf8b41

View file

@ -195,41 +195,50 @@ class CaptureManager {
return { inputArgs: ['-probesize','32M','-analyzeduration','10M','-fflags','+genpts','-i', sourceUrl], isNetwork: true }; return { inputArgs: ['-probesize','32M','-analyzeduration','10M','-fflags','+genpts','-i', sourceUrl], isNetwork: true };
} }
// Deltacast SDI via VideoMaster SDK FFmpeg plugin.
// FFmpeg input format is 'deltacast', device address is 'deltacast://<index>'.
// When the physical device is absent (/dev/deltacast<N> missing), fall back
// to a lavfi test card so development and integration testing work without hardware.
if (sourceType === 'deltacast') { if (sourceType === 'deltacast') {
const idx = (typeof device === 'number' || /^\d+$/.test(String(device))) const idx = (typeof device === 'number' || /^\d+$/.test(String(device)))
? parseInt(device, 10) ? parseInt(device, 10) : 0;
: 0; const audioFifo = `/tmp/dc-audio-${this._sessionIdForBridge}`;
const { existsSync } = await import('node:fs');
const deviceNode = `/dev/deltacast${idx}`; // Create the audio FIFO before spawning the bridge.
if (existsSync(deviceNode)) { const { execSync: _exec } = await import('child_process');
console.log(`[capture] Deltacast index ${idx}${deviceNode} (hardware)`); try { _exec(`mkfifo ${audioFifo}`); } catch (_) { /* may already exist */ }
return {
inputArgs: ['-f', 'deltacast', '-i', `deltacast://${idx}`], const bridge = spawn('deltacast-capture', [
isNetwork: false, '--device', String(idx),
}; '--port', String(idx),
} else { '--audio-pipe', audioFifo,
// No hardware — lavfi test card with port label + timecode burn-in. '--signal-timeout', '30',
// Matches the deltacast-sdi-recorder standalone app fallback exactly so ], { stdio: ['ignore', 'pipe', 'pipe'] });
// recorded files look right in the MAM library during dev.
console.warn(`[capture] Deltacast device ${deviceNode} not found — using lavfi test card for port ${idx}`); // Log bridge stderr after the first line (non-JSON diagnostic output)
const testSrc = [ let firstLineDone = false;
`testsrc2=size=1920x1080:rate=30`, bridge.stderr.on('data', (d) => {
`drawtext=text='DELTACAST PORT ${idx} — TEST MODE':fontsize=48:fontcolor=white:x=(w-text_w)/2:y=(h-text_h)/2`, if (firstLineDone) console.error(`[deltacast-bridge] ${d}`);
`drawtext=text='%{localtime\\:%H\\:%M\\:%S}':fontsize=32:fontcolor=yellow:x=10:y=10`, else if (d.toString().includes('\n')) firstLineDone = true;
].join(','); });
return {
inputArgs: [ const fmt = await readFirstStderrLine(bridge, 35_000);
'-f', 'lavfi', '-i', testSrc, // fmt: { width, height, fps_num, fps_den, interlaced, pix_fmt,
'-f', 'lavfi', '-i', 'sine=frequency=1000:sample_rate=48000', // audio_channels, audio_rate, device, port }
'-map', '0:v:0', '-map', '1:a:0',
], return {
isNetwork: false, inputArgs: [
}; '-f', 'rawvideo',
} '-pix_fmt', fmt.pix_fmt,
'-video_size', `${fmt.width}x${fmt.height}`,
'-framerate', `${fmt.fps_num}/${fmt.fps_den}`,
'-i', 'pipe:0',
'-f', 's16le',
'-ar', String(fmt.audio_rate),
'-ac', String(fmt.audio_channels),
'-i', audioFifo,
],
isNetwork: false,
bridgeProcess: bridge,
audioFifo,
interlaced: !!fmt.interlaced,
};
} }
// Default: SDI via DeckLink // Default: SDI via DeckLink