fix(capture): add 5s pre-roll delay to stabilize SDI, remove framerate display
- services/capture/src/capture-manager.js: - Added 5s pre-roll delay for Deltacast, Blackmagic, and SDI capture paths. - Activates when is present. - Spawns the process immediately, drains/discards the unstable frames for 5 seconds, and then pipes the stdout of the to the actual process. - Keeps the master output perfectly clean from frame 0. - services/web-ui/public/screens-ingest.jsx: - Removed currentFps framerate display from both recording and idle status states.
This commit is contained in:
parent
9dc86aa3b6
commit
b6545e61a9
2 changed files with 15 additions and 19 deletions
|
|
@ -7,6 +7,8 @@ import { createUploadStream } from './s3/client.js';
|
||||||
|
|
||||||
|
|
||||||
const S3_BUCKET = process.env.S3_BUCKET || 'wild-dragon';
|
const S3_BUCKET = process.env.S3_BUCKET || 'wild-dragon';
|
||||||
|
const PRE_ROLL_SECONDS = parseInt(process.env.PRE_ROLL_SECONDS || '5', 10);
|
||||||
|
|
||||||
|
|
||||||
// Growing-files mode: writes the master to a local SMB-backed share that the
|
// Growing-files mode: writes the master to a local SMB-backed share that the
|
||||||
// editor can mount, instead of streaming to S3 in real time. The promotion
|
// editor can mount, instead of streaming to S3 in real time. The promotion
|
||||||
|
|
@ -1051,13 +1053,24 @@ exit "$BMXRC"
|
||||||
// The stop handler sets needsProxy=true so the worker picks it up.
|
// The stop handler sets needsProxy=true so the worker picks it up.
|
||||||
const proxyKey = null;
|
const proxyKey = null;
|
||||||
|
|
||||||
const startedAt = new Date().toISOString();
|
|
||||||
|
|
||||||
this._sessionIdForBridge = sessionId;
|
this._sessionIdForBridge = sessionId;
|
||||||
const { inputArgs, isNetwork, bridgeProcess = null, audioFifo = null, interlaced = false, audioInputIndex = 0 } = await this._buildInputArgs({
|
const { inputArgs, isNetwork, bridgeProcess = null, audioFifo = null, interlaced = false, audioInputIndex = 0 } = await this._buildInputArgs({
|
||||||
sourceType, sourceBackend, device, port, board, sourceUrl, listen, listenPort, streamKey,
|
sourceType, sourceBackend, device, port, board, sourceUrl, listen, listenPort, streamKey,
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// ── Pre-roll: discard initial unstable frames ────────────────────────────
|
||||||
|
if (bridgeProcess && (sourceType === 'deltacast' || sourceType === 'blackmagic' || sourceType === 'sdi')) {
|
||||||
|
console.log(`[capture] pre-rolling: discarding ${PRE_ROLL_SECONDS}s of frames`);
|
||||||
|
// Attach temporary drain listener.
|
||||||
|
bridgeProcess.stdout.on('data', () => {});
|
||||||
|
await new Promise(r => setTimeout(r, PRE_ROLL_SECONDS * 1000));
|
||||||
|
bridgeProcess.stdout.removeAllListeners('data');
|
||||||
|
console.log(`[capture] pre-roll complete.`);
|
||||||
|
}
|
||||||
|
|
||||||
|
const startedAt = new Date().toISOString();
|
||||||
|
const recordingStartedAt = Date.now();
|
||||||
|
|
||||||
// Audio input index is returned EXPLICITLY by _buildInputArgs (audioInputIndex)
|
// Audio input index is returned EXPLICITLY by _buildInputArgs (audioInputIndex)
|
||||||
// rather than guessed from sourceType/FC_SLOT_ID — that guess was wrong for
|
// rather than guessed from sourceType/FC_SLOT_ID — that guess was wrong for
|
||||||
// the legacy deltacast FIFO path (which has audio at input 1 but no FC_SLOT_ID),
|
// the legacy deltacast FIFO path (which has audio at input 1 but no FC_SLOT_ID),
|
||||||
|
|
|
||||||
|
|
@ -759,14 +759,6 @@ function RecorderRow({ recorder: initialRecorder, onRefresh }) {
|
||||||
String(d % 60).padStart(2, '0');
|
String(d % 60).padStart(2, '0');
|
||||||
}, [isRec, elapsedSecs]);
|
}, [isRec, elapsedSecs]);
|
||||||
|
|
||||||
// Show live fps when recording and signal is healthy; fall back to configured value.
|
|
||||||
const displayFramerate = React.useMemo(() => {
|
|
||||||
if (isRec && liveStatus && liveStatus.currentFps != null && liveStatus.currentFps > 0) {
|
|
||||||
return Number(liveStatus.currentFps).toFixed(2) + ' fps';
|
|
||||||
}
|
|
||||||
return recorder.framerate || 'native';
|
|
||||||
}, [isRec, liveStatus, recorder.framerate]);
|
|
||||||
|
|
||||||
const displaySignal = liveStatus
|
const displaySignal = liveStatus
|
||||||
? (liveStatus.signal || '·')
|
? (liveStatus.signal || '·')
|
||||||
: (isRec ? 'connecting…' : '·');
|
: (isRec ? 'connecting…' : '·');
|
||||||
|
|
@ -861,10 +853,6 @@ function RecorderRow({ recorder: initialRecorder, onRefresh }) {
|
||||||
{displaySignal}
|
{displaySignal}
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<div className="recorder-stat">
|
|
||||||
<div className="stat-label">Framerate</div>
|
|
||||||
<div className="stat-val mono">{displayFramerate}</div>
|
|
||||||
</div>
|
|
||||||
</div>
|
</div>
|
||||||
<div className="recorder-actions">
|
<div className="recorder-actions">
|
||||||
{!isRec && (
|
{!isRec && (
|
||||||
|
|
@ -958,11 +946,6 @@ function CapturePortChip({ port, sigEntry }) {
|
||||||
<span style={{ fontSize: 9.5, fontWeight: 700, letterSpacing: '0.05em', color }}>
|
<span style={{ fontSize: 9.5, fontWeight: 700, letterSpacing: '0.05em', color }}>
|
||||||
{label}
|
{label}
|
||||||
</span>
|
</span>
|
||||||
{sigEntry && sigEntry.currentFps != null && (
|
|
||||||
<span style={{ fontSize: 9.5, color: 'var(--text-4)', fontFamily: 'var(--font-mono)' }}>
|
|
||||||
{Number(sigEntry.currentFps).toFixed(1)} fps
|
|
||||||
</span>
|
|
||||||
)}
|
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue