From 967547ae97640da9971624c458442018ce3176cc Mon Sep 17 00:00:00 2001 From: ZGaetano Date: Thu, 4 Jun 2026 19:31:48 +0000 Subject: [PATCH] =?UTF-8?q?fix(growing):=20drop=20FIFO=20FD-priming=20dead?= =?UTF-8?q?lock=20=E2=80=94=20ffmpeg-first=20ordering=20feeds=20raw2bmx=20?= =?UTF-8?q?frames?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Root cause of frozen 22KB growing MXF: the parent held a stray read-write priming writer (FD 7) on the video FIFO while raw2bmx opened it, so raw2bmx read a malformed/empty stream start and exited after the header (Duration:0). Proven live on zampp3: removing the FD-7/8 priming + fd-watch loop and simply starting ffmpeg before raw2bmx lets the blocking FIFO opens pair up naturally (ffmpeg sole writer). True-1080p59.94 AVC-Intra 100 then grows monotonically on disk and a mid-write snapshot decodes to its last frame (481 frames @ 8s). --- services/capture/src/capture-manager.js | 36 +++++-------------------- 1 file changed, 6 insertions(+), 30 deletions(-) diff --git a/services/capture/src/capture-manager.js b/services/capture/src/capture-manager.js index 76338f0..1a14bd1 100644 --- a/services/capture/src/capture-manager.js +++ b/services/capture/src/capture-manager.js @@ -937,50 +937,26 @@ const bmx = [ // overwrites them in-place. It is killed by the cleanup trap on exit. const script = ` set -u -# Save the ORIGINAL stdin (the fc_pipe video stream Node pipes in) to FD 9 -# BEFORE any FIFO priming touches fd 0. ffmpeg later reads from FD 9 explicitly, -# guaranteeing it is the sole reader of the raw video — running ffmpeg as a -# backgrounded subshell that merely inherited fd 0 starved it of bytes when the -# input was a pipe (the "No filtered frames / empty output" growing failure). exec 9<&0 VF=$(mktemp -u /tmp/grow_v.XXXXXX); AF=$(mktemp -u /tmp/grow_a.XXXXXX) OUT=${sh(outPath)} mkfifo "$VF" "$AF" -PATCHPID= -cleanup() { rm -f "$VF" "$AF"; [ -n "$PATCHPID" ] && kill "$PATCHPID" 2>/dev/null; } - trap cleanup EXIT -# Prime both FIFOs read-write (non-blocking) to break the open-order deadlock. -exec 7<>"$VF" 8<>"$AF" -# raw2bmx: close priming FDs (no stray writer) + read stdin from /dev/null before -# exec so it sees real EOF and never competes for the video pipe. -( exec 7>&- 8>&- 9>&- 0/tmp/raw2bmx.log 2>&1 ) & -BMXPID=$! -# ffmpeg reads the raw video from FD 9 (the saved original stdin). pipe:0 in its -# arg list maps to fd 0, so we point fd 0 at FD 9 for the child. -( exec 7>&- 8>&- 0<&9 9<&-; exec ${ffLine} ) & +cleanup() { rm -f "$VF" "$AF"; } +trap cleanup EXIT +( exec 0<&9 9<&-; exec ${ffLine} ) & FFPID=$! -# Parent no longer needs the saved stdin — drop it so ffmpeg is the sole reader. exec 9<&- -# Forward a clean stop to ffmpeg; raw2bmx then gets EOF and finalizes the footer. +exec ${bmxLine} >/tmp/raw2bmx.log 2>&1 & +BMXPID=$! stop() { kill -INT "$FFPID" 2>/dev/null; } trap stop INT TERM -# Drop the parent priming FDs once raw2bmx has opened BOTH FIFOs, so ffmpeg is -# the sole writer (its EOF reaches raw2bmx). If raw2bmx dies early, bail. -for i in $(seq 1 200); do - kill -0 "$BMXPID" 2>/dev/null || break - n=$(ls -l /proc/$BMXPID/fd 2>/dev/null | grep -c -- "$VF\\|$AF") - [ "\${n:-0}" -ge 2 ] && break - sleep 0.1 -done -exec 7>&- 8>&- -PATCHPID= -# Wait for ffmpeg (source end), then for raw2bmx to finalize the footer. wait "$FFPID"; FFRC=$? wait "$BMXPID"; BMXRC=$? echo "[grow] ffmpeg rc=$FFRC raw2bmx rc=$BMXRC out=$OUT" >&2 exit "$BMXRC" `; return ['-c', script]; + return ['-c', script]; } /**