feat(growing): TRUE 1080p59.94 via AVC-Intra 100 (libx264 4:2:2 10-bit)
User wants native 59.94p, not 30p. XDCAM HD422/MPEG-2 422 cannot do 1080p59.94 (raw2bmx rejects 60000/1001). AVC-Intra 100 CAN. Switched growing essence to libx264 high422 10-bit avcintra-class=100 (NVENC h264 cannot do 4:2:2, so CPU), -f h264, raw2bmx -t op1a --avci100_1080p -f 60000/1001. Verified via the live-equivalent fc_pipe|bash path WITH the FD-9 stdin fix: produces h264 High 4:2:2 Intra yuv422p10le 1920x1080 progressive 60000/1001 MXF (true 59.94p), openable in Premiere.
This commit is contained in:
parent
a00a280689
commit
bf486b93dd
1 changed files with 26 additions and 26 deletions
|
|
@ -359,15 +359,19 @@ const CONTAINER_EXT = {
|
||||||
// bitrate (operator target, default 50 Mbps) match XDCAM HD422 essence. `-g 15`
|
// bitrate (operator target, default 50 Mbps) match XDCAM HD422 essence. `-g 15`
|
||||||
// keeps a short GOP. Muxed to a raw `mpeg2video` elementary stream (no
|
// keeps a short GOP. Muxed to a raw `mpeg2video` elementary stream (no
|
||||||
// container) so raw2bmx ingests it via --mpeg2lg_*.
|
// container) so raw2bmx ingests it via --mpeg2lg_*.
|
||||||
// XDCAM HD422 (MPEG-2 422 Long GOP) — the PROVEN-working growing essence.
|
// AVC-Intra Class 100 — the growing essence that supports TRUE 1080p59.94.
|
||||||
// ffprobe of the working Delta7/SDI2 files on the share: mpeg2video, profile
|
// XDCAM HD422 (MPEG-2 422) cannot do 1080p59.94 (raw2bmx rejects 60000/1001),
|
||||||
// 4:2:2, yuv422p, 1920x1080, PROGRESSIVE, r_frame_rate 30000/1001. The source
|
// so for native 59.94p we use AVC-Intra 100 = H.264 High 4:2:2 Intra (10-bit,
|
||||||
// is 1080p59.94 but it is frame-rate-halved to 1080p29.97 (NOT interlaced) for
|
// all-intra). NVENC h264 cannot produce 4:2:2, so this is libx264 (CPU). The
|
||||||
// the XDCAM HD422 essence. raw2bmx wraps this via --mpeg2lg_422p_hl_1080p
|
// essence is a raw H.264 stream (-f h264) wrapped by raw2bmx --avci100_1080p
|
||||||
// at -f 30000/1001, clip type rdd9.
|
// at -f 60000/1001, clip type op1a. Verified on-node: produces a valid
|
||||||
|
// "h264 / High 4:2:2 Intra / yuv422p10le / 60000/1001" MXF.
|
||||||
const GROWING_VIDEO_ELEMENTARY_ARGS = [
|
const GROWING_VIDEO_ELEMENTARY_ARGS = [
|
||||||
'-c:v', 'mpeg2video', '-pix_fmt', 'yuv422p',
|
'-c:v', 'libx264', '-profile:v', 'high422', '-level', '4.2',
|
||||||
'-dc', '10', '-g', '15', '-bf', '2',
|
'-preset', 'ultrafast', '-tune', 'zerolatency',
|
||||||
|
'-pix_fmt', 'yuv422p10le',
|
||||||
|
'-x264-params', 'avcintra-class=100:bframes=0:keyint=1:scenecut=0',
|
||||||
|
'-aud', '1',
|
||||||
];
|
];
|
||||||
const GROWING_DEFAULT_BITRATE = '25M';
|
const GROWING_DEFAULT_BITRATE = '25M';
|
||||||
const GROWING_EXT = 'mxf';
|
const GROWING_EXT = 'mxf';
|
||||||
|
|
@ -417,34 +421,30 @@ function deriveGrowingRaster(resolution, framerate, scanHint = null) {
|
||||||
}
|
}
|
||||||
if (height == null) height = 1080; // default raster
|
if (height == null) height = 1080; // default raster
|
||||||
|
|
||||||
// ffmpeg rate + raw2bmx rate strings. XDCAM HD422 at 1080 lines does not
|
// ffmpeg rate + raw2bmx rate strings. AVC-Intra 100 supports TRUE 1080p59.94,
|
||||||
// support 60000/1001; the proven-working files wrap the 1080p59.94 SDI feed as
|
// so a 1080p59.94 SDI feed is wrapped at its native 60000/1001 (no frame drop).
|
||||||
// 1080p29.97 PROGRESSIVE (frame-rate halved). So 59.94 maps to 30000/1001 and
|
|
||||||
// ffmpeg's -r drops every other frame (60p -> 30p), matching the working files.
|
|
||||||
function rates(fps) {
|
function rates(fps) {
|
||||||
if (fps == null) return { ff: '30000/1001', raw: '30000/1001' };
|
if (fps == null) return { ff: '60000/1001', raw: '60000/1001' };
|
||||||
if (Math.abs(fps - 59.94) < 0.2 || Math.abs(fps - 29.97) < 0.05)
|
if (Math.abs(fps - 59.94) < 0.2) return { ff: '60000/1001', raw: '60000/1001' };
|
||||||
return { ff: '30000/1001', raw: '30000/1001' };
|
if (Math.abs(fps - 29.97) < 0.05) return { ff: '30000/1001', raw: '30000/1001' };
|
||||||
if (Math.abs(fps - 60) < 0.05) return { ff: '30', raw: '30' };
|
if (Math.abs(fps - 60) < 0.05) return { ff: '60', raw: '60' };
|
||||||
if (Math.abs(fps - 50) < 0.05) return { ff: '25', raw: '25' };
|
if (Math.abs(fps - 50) < 0.05) return { ff: '50', raw: '50' };
|
||||||
if (Math.abs(fps - 25) < 0.05) return { ff: '25', raw: '25' };
|
if (Math.abs(fps - 25) < 0.05) return { ff: '25', raw: '25' };
|
||||||
if (Math.abs(fps - 24) < 0.2) return { ff: '24000/1001', raw: '24000/1001' };
|
if (Math.abs(fps - 24) < 0.2) return { ff: '24000/1001', raw: '24000/1001' };
|
||||||
if (Math.abs(fps - 30) < 0.05) return { ff: '30', raw: '30' };
|
if (Math.abs(fps - 30) < 0.05) return { ff: '30', raw: '30' };
|
||||||
return { ff: String(fps), raw: String(fps) };
|
return { ff: String(fps), raw: String(fps) };
|
||||||
}
|
}
|
||||||
|
|
||||||
// The proven-working files are PROGRESSIVE 1080p29.97 (field_order=progressive).
|
// AVC-Intra wraps progressive natively. Deltacast reports progressive; honor it.
|
||||||
// Deltacast reports progressive; honor it. 1080-line MPEG-2 422 progressive is
|
if (scan == null) scan = scanHint || ((height >= 1080) ? 'i' : 'p');
|
||||||
// accepted by raw2bmx at 30000/1001.
|
|
||||||
if (scan == null) scan = scanHint || 'p';
|
|
||||||
const r = rates(fpsNum);
|
const r = rates(fpsNum);
|
||||||
|
|
||||||
// XDCAM HD422 (MPEG-2 422 Long GOP) raster flags — matches the working files.
|
// AVC-Intra 100 raster flags. --avci100_1080p accepts true 1080p59.94 (verified).
|
||||||
let rawFlag;
|
let rawFlag;
|
||||||
if (height >= 1080) {
|
if (height >= 1080) {
|
||||||
rawFlag = (scan === 'i') ? '--mpeg2lg_422p_hl_1080i' : '--mpeg2lg_422p_hl_1080p';
|
rawFlag = (scan === 'i') ? '--avci100_1080i' : '--avci100_1080p';
|
||||||
} else if (height >= 720) {
|
} else if (height >= 720) {
|
||||||
rawFlag = '--mpeg2lg_422p_hl_720p';
|
rawFlag = '--avci100_720p';
|
||||||
if (fpsNum == null) { r.ff = '60000/1001'; r.raw = '60000/1001'; }
|
if (fpsNum == null) { r.ff = '60000/1001'; r.raw = '60000/1001'; }
|
||||||
} else {
|
} else {
|
||||||
rawFlag = '--mpeg2lg_422p_ml_576i';
|
rawFlag = '--mpeg2lg_422p_ml_576i';
|
||||||
|
|
@ -867,7 +867,7 @@ class CaptureManager {
|
||||||
...GROWING_VIDEO_ELEMENTARY_ARGS,
|
...GROWING_VIDEO_ELEMENTARY_ARGS,
|
||||||
'-b:v', vb, '-minrate', vb, '-maxrate', vb, '-bufsize', vb,
|
'-b:v', vb, '-minrate', vb, '-maxrate', vb, '-bufsize', vb,
|
||||||
'-r', ffRate,
|
'-r', ffRate,
|
||||||
'-f', 'mpeg2video', '@VF@',
|
'-f', 'h264', '@VF@',
|
||||||
// (b) PCM s16le audio → "$AF"
|
// (b) PCM s16le audio → "$AF"
|
||||||
'-map', audioMap,
|
'-map', audioMap,
|
||||||
'-c:a', 'pcm_s16le', '-ar', '48000', '-ac', String(ach),
|
'-c:a', 'pcm_s16le', '-ar', '48000', '-ac', String(ach),
|
||||||
|
|
@ -916,7 +916,7 @@ class CaptureManager {
|
||||||
// fields with the live frame count every 3s (Premiere reads the header
|
// fields with the live frame count every 3s (Premiere reads the header
|
||||||
// Duration on each refresh; without the patch it sees duration=N/A).
|
// Duration on each refresh; without the patch it sees duration=N/A).
|
||||||
const bmx = [
|
const bmx = [
|
||||||
'raw2bmx', '-t', 'rdd9', '-o', '"$OUT"', '-f', frameRate,
|
'raw2bmx', '-t', 'op1a', '-o', '"$OUT"', '-f', frameRate,
|
||||||
'--part', String(GROWING_PART_INTERVAL_FRAMES),
|
'--part', String(GROWING_PART_INTERVAL_FRAMES),
|
||||||
'--index-follows',
|
'--index-follows',
|
||||||
rawFlag, '"$VF"',
|
rawFlag, '"$VF"',
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue