From d1b40f5303d453dea4b76ef25539ae0d011ccc08 Mon Sep 17 00:00:00 2001 From: Wild Dragon Dev Date: Thu, 4 Jun 2026 00:51:14 +0000 Subject: [PATCH] fix(node-agent): pass correct FC_URL and Cmd to containerized decklink-bridge --- services/node-agent/index.js | 106 ++++------------------------------- 1 file changed, 11 insertions(+), 95 deletions(-) diff --git a/services/node-agent/index.js b/services/node-agent/index.js index 569e88f..18fc9de 100644 --- a/services/node-agent/index.js +++ b/services/node-agent/index.js @@ -77,7 +77,7 @@ const DC_BOARD = process.env.DELTACAST_BOARD || '0'; const FC_URL = process.env.FC_URL || 'http://framecache:7435'; // Node identity for framecache slot IDs (e.g. "decklink-zampp3-0"). // Set NODE_NAME in .env.worker so slot IDs are stable across restarts. -const FC_NODE_ID = process.env.NODE_NAME || process.env.HOSTNAME || 'local'; +const FC_NODE_ID = process.env.NODE_NAME || os.hostname(); let _dcBridge = null; // ChildProcess | null let _dcSidecarCount = 0; // active deltacast sidecars on this node @@ -205,104 +205,20 @@ async function startDecklinkBridge(deviceIndices) { if (await _dlBridgeRunning()) return; const devCsv = Array.isArray(deviceIndices) ? deviceIndices.join(',') : String(deviceIndices || '0'); - const DL_IMAGE = 'wild-dragon-capture:latest'; - const DL_BIN = '/usr/local/bin/decklink-bridge'; - - console.log(`[dl-bridge] spawning containerized bridge for devices: ${devCsv}`); + // Pass correct IP to containerized bridge. Default falls back to framecache:7435. + const _fcUrl = process.env.FRAMECACHE_IP ? `http://${process.env.FRAMECACHE_IP}:7435` : FC_URL; + const bridgeArgs = [ + '--devices', devCsv, + '--fc-url', _fcUrl, + '--audio-pipe-dir', DL_AUDIO_DIR, + ]; + const spec = { Image: DL_IMAGE, Entrypoint: [DL_BIN], - Cmd: ['--devices', devCsv, '--fc-url', FC_URL, '--audio-pipe-dir', DL_AUDIO_DIR], - Env: [`NODE_ID=${FC_NODE_ID}`, `FC_URL=${FC_URL}`], - HostConfig: { - NetworkMode: 'host', - Privileged: true, - Binds: ['/dev:/dev', '/dev/shm:/dev/shm'], - RestartPolicy: { Name: 'unless-stopped' }, - }, - }; - - try { - const createRes = await dockerApi('POST', '/containers/create?name=decklink-bridge', spec); - if (createRes.status !== 201 && createRes.status !== 409) { - console.error('[dl-bridge] create failed:', createRes.data); - return; - } - - const containerId = createRes.status === 409 ? 'decklink-bridge' : createRes.data.Id; - const startRes = await dockerApi('POST', `/containers/${containerId}/start`); - if (startRes.status !== 204 && startRes.status !== 304) { - console.error('[dl-bridge] start failed:', startRes.data); - return; - } - - _dlBridgeId = containerId; - _attachDlBridgeLogs(containerId); - console.log(`[dl-bridge] running in container ${containerId}`); - } catch (err) { - console.error(`[dl-bridge] spawn error: ${err.message}`); - } -} - -async function stopDecklinkBridge() { - if (!_dlBridgeId) return; - console.log('[dl-bridge] stopping container'); - try { - await dockerApi('POST', `/containers/${_dlBridgeId}/stop?t=5`); - await dockerApi('DELETE', `/containers/${_dlBridgeId}?force=true`); - } catch (err) { - console.error(`[dl-bridge] stop error: ${err.message}`); - } - _dlBridgeId = null; -} - -function _dcBridgeRunning() { - return _dcBridge !== null && _dcBridge.exitCode === null && _dcBridge.signalCode === null; -} - -// Check /proc on Linux to see if a deltacast-bridge process is alive. -// Used by startDeltacastBridge() to detect a bridge started outside node-agent -// (e.g. manually with sudo, or from a prior node-agent process). -function _dcBridgeProcessAlive() { - try { - for (const pid of fs.readdirSync('/proc')) { - if (!/^\d+$/.test(pid)) continue; - try { - // cmdline is NUL-delimited; read as binary-friendly string. - const cmdline = fs.readFileSync(`/proc/${pid}/cmdline`, 'latin1'); - if (cmdline.includes('deltacast-bridge')) return true; - } catch (_) { /* process may have exited mid-scan */ } - } - } catch (_) {} - return false; -} - -function startDeltacastBridge() { - if (_dcBridgeRunning()) return; // already up (we spawned it) - - try { fs.mkdirSync(DC_PIPE_DIR, { recursive: true }); } catch (_) {} - - // FIFOs may exist from a previous run. Only skip the spawn if a - // deltacast-bridge process is actually alive on the host — stale FIFOs with - // no live writer cause ffmpeg to block on open() indefinitely (no audio/video). - const _v0 = DC_PIPE_DIR + '/video-0.fifo'; - if (fs.existsSync(_v0)) { - if (_dcBridgeProcessAlive()) { - console.log('[dc-bridge] FIFOs exist and bridge process alive — skipping spawn'); - return; - } - console.log('[dc-bridge] FIFOs exist but bridge is NOT running — spawning fresh bridge'); - // Stale FIFOs are harmless: the bridge recreates them (mkfifo ignores EEXIST). - } - - const args = [ - '--device', DC_BOARD, - '--ports', DC_PORTS_CSV, - '--video-pipe-dir', DC_PIPE_DIR, - '--audio-pipe-dir', DC_PIPE_DIR, - '--fc-url', FC_URL, - ]; + Cmd: bridgeArgs, + Env: [`NODE_ID=${FC_NODE_ID}`, `FC_URL=${_fcUrl}`], console.log(`[dc-bridge] launching: ${DC_BRIDGE_BIN} ${args.join(' ')}`); const proc = spawn(DC_BRIDGE_BIN, args, {