diff --git a/services/capture/src/capture-manager.js b/services/capture/src/capture-manager.js index 09df4e0..a3b46e6 100644 --- a/services/capture/src/capture-manager.js +++ b/services/capture/src/capture-manager.js @@ -242,8 +242,22 @@ const CONTAINER_EXT = { // // Audio: AAC (a TS-native codec Premiere imports). PCM-in-TS is tagged as // SMPTE-302M `bin_data`, which Premiere does not reliably import as audio. +// +// THIRD-ATTEMPT FIX — profile/pixel format. The previous attempt used +// `-profile:v high422 -pix_fmt yuv422p`, which produces an H.264 "High 4:2:2 +// Intra" stream. ffmpeg/ffprobe/VLC decode that fine (which is why prior static +// checks passed), but Adobe Premiere's H.264 importer ONLY supports 8-bit 4:2:0 +// (Baseline/Main/High); it silently refuses High 4:2:2 (Intra) — the exact +// "ffprobe opens it but Premiere won't import" symptom the user reported. +// Verified live on zampp2 against the deployed capture image: +// high422/yuv422p -> profile "High 4:2:2 Intra" (decodes in ffmpeg, rejected by Premiere) +// high /yuv420p -> profile "High" (decodes mid-write, Premiere-importable) +// 4:2:0 is the only chroma subsampling Premiere ingests for H.264; the +// 4:2:2 mezzanine path is the (non-growing) ProRes/DNxHR master, not this +// growing TS. `-g 1` still makes every frame an IDR so a partially-written file +// decodes to its last complete frame (edit-while-record). const GROWING_VIDEO_ARGS = [ - '-c:v', 'libx264', '-profile:v', 'high422', '-pix_fmt', 'yuv422p', + '-c:v', 'libx264', '-profile:v', 'high', '-pix_fmt', 'yuv420p', '-preset', 'veryfast', '-g', '1', ]; const GROWING_EXT = 'ts'; @@ -336,15 +350,24 @@ function buildEncodeArgs({ container, isNetwork, isProxy = false, growing = false, }) { - // ── Growing master: force MPEG-TS + H.264 all-intra, ignoring the configured - // MOV/ProRes container/codec. This is a format VLC and Premiere both open - // WHILE GROWING (no footer/moov to wait on — see GROWING_VIDEO_ARGS above). + // ── Growing master: MPEG-TS + H.264 all-intra. The CODEC and CONTAINER are + // necessarily fixed here (H.264-in-TS is the only all-intra format Premiere + // opens WHILE growing — no footer/moov to wait on; ProRes/DNxHR/PCM are not + // valid TS payloads), so the operator's codec/container selections cannot be + // honoured for a growing recorder. The operator's TARGET BITRATE, framerate, + // and audio-channel count CAN and now ARE honoured — previously the + // configured bitrate was dropped entirely, so "master recording settings + // don't do anything" was literally true for a growing recorder (it always + // encoded at libx264's default CRF regardless of the UI value). // Audio is forced to AAC, a TS-native codec Premiere imports (PCM-in-TS is // tagged as bin_data and not reliably importable as audio). if (growing) { const args = []; if (isNetwork) args.push('-map', '0:v:0?', '-map', '0:a:0?'); args.push(...GROWING_VIDEO_ARGS); + // Honour the operator-configured target bitrate (e.g. "60M"). Without this + // libx264 falls back to its default CRF and the UI bitrate is ignored. + if (videoBitrate) args.push('-b:v', videoBitrate, '-maxrate', videoBitrate, '-bufsize', videoBitrate); if (framerate && framerate !== 'native') args.push('-r', framerate); args.push('-c:a', 'aac', '-b:a', '256k'); if (audioChannels) args.push('-ac', String(audioChannels)); diff --git a/services/web-ui/public/modal-new-recorder.jsx b/services/web-ui/public/modal-new-recorder.jsx index 236da92..236b303 100644 --- a/services/web-ui/public/modal-new-recorder.jsx +++ b/services/web-ui/public/modal-new-recorder.jsx @@ -506,6 +506,14 @@ function NewRecorderModal({ open, onClose }) { Write the live master to the SMB share so editors can cut while it's still recording. Requires the SMB share to be configured in Settings → Storage. + {growingOn && ( +