diff --git a/services/mam-api/src/routes/recorders.js b/services/mam-api/src/routes/recorders.js index cf6bff3..316d39e 100644 --- a/services/mam-api/src/routes/recorders.js +++ b/services/mam-api/src/routes/recorders.js @@ -197,7 +197,7 @@ router.post('/', async (req, res, next) => { // Defaults — written on insert so the DB row is always self-contained. const defaults = { source_config: {}, - recording_codec: 'prores_hq', + recording_codec: 'hevc_nvenc', recording_resolution: 'native', recording_audio_codec: 'pcm_s24le', recording_audio_channels: 2, diff --git a/services/web-ui/public/modal-new-recorder.jsx b/services/web-ui/public/modal-new-recorder.jsx index ba4041a..fa341c7 100644 --- a/services/web-ui/public/modal-new-recorder.jsx +++ b/services/web-ui/public/modal-new-recorder.jsx @@ -148,8 +148,18 @@ function NewRecorderModal({ open, onClose }) { }); const [dcDevices, setDcDevices] = React.useState(null); const [recTab, setRecTab] = React.useState('video'); - const [recCodec, setRecCodec] = React.useState('prores_hq'); - const [recContainer, setRecContainer] = React.useState('mov'); + // All-Intra HEVC (NVENC) is the default master — GPU-encoded, growing-file + // capable in fragmented MOV. ProRes stays selectable for 4:2:2 mezzanine. + const [recCodec, setRecCodec] = React.useState('hevc_nvenc'); + // Custom target bitrate in Mbps for bitrate-controlled codecs (NVENC / x264 / + // x265 / DNxHD). ProRes ignores bitrate (quality is profile-driven). + const [recBitrate, setRecBitrate] = React.useState('60'); + // Container is derived from the codec, not manually chosen: HEVC/ProRes/DNxHR + // → MOV (fragmented, growing-capable); H.264 → MP4. + const recContainer = (recCodec === 'h264' || recCodec === 'h264_nvenc' || recCodec === 'libx264') ? 'mp4' : 'mov'; + // Codecs whose bitrate is operator-controlled (everything except ProRes). + const BITRATE_CODECS = new Set(['hevc_nvenc', 'h264_nvenc', 'libx264', 'libx265', 'dnxhd', 'dnxhr_hq']); + const codecUsesBitrate = BITRATE_CODECS.has(recCodec); const [proxyOn, setProxyOn] = React.useState(true); const [projectId, setProjectId] = React.useState(PROJECTS[0]?.id || ''); const [submitting, setSubmitting] = React.useState(false); @@ -198,7 +208,14 @@ function NewRecorderModal({ open, onClose }) { generate_proxy: proxyOn, recording_codec: recCodec, recording_container: recContainer, + // Framerate + resolution are auto-detected from the source signal/stream. + recording_framerate: '', // empty = match source + recording_resolution: 'native', }; + // Custom bitrate only applies to bitrate-controlled codecs (ProRes ignores it). + if (codecUsesBitrate && recBitrate) { + body.recording_video_bitrate = `${recBitrate}M`; + } if (sourceType === 'SRT') { body.source_config = { url: srtUrl }; @@ -382,22 +399,48 @@ function NewRecorderModal({ open, onClose }) {