code wiki / _hdl_build / nx_cam_poster.nx

nx_cam_poster.nx source

↩ module page · 288 lines · 14284 B

1// nx_cam_poster.nx -- SOVEREIGN cam-recording POSTER generator (STEP 4 glue), CAVLC path. 2// TS -> first IDR (reuse nx_ts2fmp4's PAT/PMT/vidPID demux logic) -> nx_h264_decode_iframe (CAVLC full-YUV 3// recon) -> YUV420->RGB (BT.601) -> integer box-average downscale (reuse nx_galx_thumb's scaler) -> 4// jcw_write_color -> baseline JPEG on disk. No ffmpeg, no external calls. For CABAC streams it skips 5// gracefully (the CABAC decoder is a separate rung). Usage: nx_cam_poster <in.ts> <out.jpg> 6// license_tier: ORIGINAL 7import "nx_syscalls.nx" 8import "nx_ts_nal.nx" // codec-aware stream_type dispatch (gated 16/16); HEVC named, not silently ignored 9import "nx_h264_sps.nx" 10import "nx_h264_pps.nx" 11import "nx_h264_iframe.nx" 12import "nx_h264_bitwriter.nx" 13import "nx_dct8.nx" 14import "nx_quant_table.nx" 15import "nx_zigzag.nx" 16import "nx_jpeg_huff_enc.nx" 17import "nx_jpeg_block_enc.nx" 18import "nx_jpeg_write.nx" 19import "nx_jpeg_color_write.nx" 20import "nx_ts_es.nx" // demux_video_es + find_first_nal + nal_to_rbsp, lifted verbatim 2026-07-31 (rule 15) 21import "nx_mp4_es.nx" // mp4 container path: SPS/PPS from avcC + first IDR of sample 0 22const POSTER_MAGIC_2026: i64 = 2026 23const POSTER_MAGIC_91881: i64 = 91881 24const POSTER_MAGIC_22554: i64 = 22554 25const POSTER_MAGIC_46802: i64 = 46802 26const POSTER_MAGIC_116130: i64 = 116130 27const POSTER_MAGIC_1024: i64 = 1024 28const POSTER_MAGIC_8192: i64 = 8192 29 30const PRECHECK: i64 = 262144 // phase-1 read: covers PAT/PMT/SPS/PPS -> fast CABAC skip (rule 11) 31const POSTER_MAX: i64 = 480 // longest poster side in px (data-driven cap, rule 11) 32const JPEG_QUALITY: i64 = 80 33// DEFENSIVE frame-dimension bounds (rule 12): the SPS is UNTRUSTED file input. A corrupt/hostile SPS decodes 34// to absurd mbW/mbH -> the yf/uf/vf sys_mmap + their zeroing loops below would fault in GIGABYTES of anon pages 35// -> OOM-kill the box (observed 2026-07-17: repeated 15-18GB anon-rss OOM-kills of nx_cam_poster during batch 36// runs; the giant mmap now SUCCEEDS lazily under vm.overcommit_memory=1 and the touch-loop balloons instead of 37// failing fast). Cap to the largest LEGAL H.264 frame (Level 6.2 MaxFS=696320 MBs, e.g. 8192x4352); skip beyond. 38const CP_MAX_MB_DIM: i64 = 1024 // per-axis MB cap (16384 px, above 8K) -- also prevents mbW*mbH i64 overflow 39const CP_MAX_MBS: i64 = 696320 // H.264 Level 6.2 MaxFS (largest legal coded frame, in macroblocks) 40 41func cp_puts(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 42func cp_num(v: i64) -> i64 { 43 let bb: *u8 = sys_mmap(32) 44 var m: i64 = v 45 if m < 0 { sys_write(1, "-\x00" as *u8, 1); m = 0 - m } 46 let t: *u8 = sys_mmap(32) 47 var k: i64 = 0 48 if m == 0 { t[0] = 48 as u8; k = 1 } 49 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 50 var i: i64 = 0 51 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 52 sys_write(1, bb, k) 53 return 0 54} 55 56func read_head(path: *u8, buf: *u8) -> i64 { 57 let fd: i64 = sys_openat_rd(path) 58 if fd < 0 { return 0 - 1 } 59 var total: i64 = 0 60 var go: i64 = 1 61 while go == 1 { 62 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, MAXSCAN - total) 63 if r <= 0 { go = 0 } 64 if r > 0 { total = total + r; if total >= MAXSCAN { go = 0 } } 65 } 66 sys_close(fd) 67 return total 68} 69 70func build_poster_rgb(yf: *u8, uf: *u8, vf: *u8, W: i64, cpW: i64, cropW: i64, cropH: i64, dstW: i64, dstH: i64, dst: *u8) -> i64 { 71 var dy: i64 = 0 72 while dy < dstH { 73 let sy0: i64 = dy * cropH / dstH 74 var sy1: i64 = (dy + 1) * cropH / dstH 75 if sy1 <= sy0 { sy1 = sy0 + 1 } 76 if sy1 > cropH { sy1 = cropH } 77 var dx: i64 = 0 78 while dx < dstW { 79 let sx0: i64 = dx * cropW / dstW 80 var sx1: i64 = (dx + 1) * cropW / dstW 81 if sx1 <= sx0 { sx1 = sx0 + 1 } 82 if sx1 > cropW { sx1 = cropW } 83 var sr: i64 = 0 84 var sg: i64 = 0 85 var sb: i64 = 0 86 var cnt: i64 = 0 87 var yy: i64 = sy0 88 while yy < sy1 { 89 var xx: i64 = sx0 90 while xx < sx1 { 91 let Y: i64 = yf[yy*W + xx] as i64 92 let U: i64 = (uf[(yy/2)*cpW + (xx/2)] as i64) - 128 93 let V: i64 = (vf[(yy/2)*cpW + (xx/2)] as i64) - 128 94 var R: i64 = Y + ((POSTER_MAGIC_91881*V) >> 16) 95 var G: i64 = Y - ((POSTER_MAGIC_22554*U + POSTER_MAGIC_46802*V) >> 16) 96 var B: i64 = Y + ((POSTER_MAGIC_116130*U) >> 16) 97 if R < 0 { R = 0 } if R > 255 { R = 255 } 98 if G < 0 { G = 0 } if G > 255 { G = 255 } 99 if B < 0 { B = 0 } if B > 255 { B = 255 } 100 sr = sr + R; sg = sg + G; sb = sb + B; cnt = cnt + 1 101 xx = xx + 1 102 } 103 yy = yy + 1 104 } 105 if cnt < 1 { cnt = 1 } 106 let p: i64 = (dy*dstW + dx) * 3 107 dst[p] = (sr/cnt) as u8 108 dst[p+1] = (sg/cnt) as u8 109 dst[p+2] = (sb/cnt) as u8 110 dx = dx + 1 111 } 112 dy = dy + 1 113 } 114 return 0 115} 116 117// encode dst RGB (dstW*dstH*3) -> baseline JPEG into out; returns byte length. 118func encode_jpeg(out: *u8, dstW: i64, dstH: i64, rgb: *u8) -> i64 { 119 let dcB: *i64 = sys_mmap(20*8) as *i64 120 let dcV: *i64 = sys_mmap(20*8) as *i64 121 let acB: *i64 = sys_mmap(20*8) as *i64 122 let acV: *i64 = sys_mmap(200*8) as *i64 123 let ndc: i64 = jhe_dc_bits(dcB) 124 jhe_dc_val(dcV) 125 let nac: i64 = jhe_ac_bits(acB) 126 jhe_ac_val(acV) 127 let dcCO: *i64 = sys_mmap(256*8) as *i64 128 let dcSI: *i64 = sys_mmap(256*8) as *i64 129 let acCO: *i64 = sys_mmap(256*8) as *i64 130 let acSI: *i64 = sys_mmap(256*8) as *i64 131 jhe_gen(dcB, dcV, ndc, dcCO, dcSI) 132 jhe_gen(acB, acV, nac, acCO, acSI) 133 let qtL: *i64 = sys_mmap(64*8) as *i64 134 nx_qt_luma(qtL, JPEG_QUALITY) 135 let qtC: *i64 = sys_mmap(64*8) as *i64 136 nx_qt_chroma(qtC, JPEG_QUALITY) 137 let to_zz: *i64 = sys_mmap(64*8) as *i64 138 let from_zz: *i64 = sys_mmap(64*8) as *i64 139 nx_zigzag_init(to_zz, from_zz) 140 let DM: *i64 = sys_mmap(64*8) as *i64 141 nx_dct8_init(DM) 142 let scratch: *i64 = sys_mmap(64*8) as *i64 143 let ti: *i64 = sys_mmap(8*8) as *i64 144 let to: *i64 = sys_mmap(8*8) as *i64 145 return jcw_write_color(out, dstW, dstH, rgb, qtL, qtC, dcB, dcV, acB, acV, dcCO, dcSI, acCO, acSI, to_zz, from_zz, DM, scratch, ti, to) 146} 147 148func rd8(v: i64) -> i64 { var r: i64 = (v/8)*8; if r < 8 { r = 8 } return r } 149 150func main(argc: i64, argv: *i64) -> i64 { 151 if argc < 3 { cp_puts("usage: nx_cam_poster <in.ts> <out.jpg>\n\x00" as *u8); return 2 } 152 let inpath: *u8 = argv[1] as *u8 153 let outpath: *u8 = argv[2] as *u8 154 155 let fd: i64 = sys_openat_rd(inpath) 156 if fd < 0 { cp_puts("ERR: cannot open input\n\x00" as *u8); return 1 } 157 let filebuf: *u8 = sys_mmap(MAXSCAN + 512) 158 let es: *u8 = sys_mmap(MAXSCAN + 512) 159 let pidout: *i64 = sys_mmap(64) as *i64 160 let sps_nal: *u8 = sys_mmap(POSTER_MAGIC_1024) 161 let pps_nal: *u8 = sys_mmap(POSTER_MAGIC_1024) 162 let idr_nal: *u8 = sys_mmap(4 * POSTER_MAGIC_1024 * POSTER_MAGIC_1024) 163 164 // ---- PHASE 1: fast SPS/PPS pre-check (read only PRECHECK bytes -> ~30x faster CABAC skip) ---- 165 var total: i64 = 0 166 var g1: i64 = 1 167 while g1 == 1 { 168 let r: i64 = sys_read(fd, ((filebuf as i64) + total) as *u8, PRECHECK - total) 169 if r <= 0 { g1 = 0 } 170 if r > 0 { total = total + r; if total >= PRECHECK { g1 = 0 } } 171 } 172 var eslen: i64 = demux_video_es(filebuf, total, es, pidout) 173 var spsnl: i64 = find_first_nal(es, eslen, 7, sps_nal, POSTER_MAGIC_1024) 174 var ppsnl: i64 = find_first_nal(es, eslen, 8, pps_nal, POSTER_MAGIC_1024) 175 if spsnl > 0 { if ppsnl > 0 { 176 let prb0: *u8 = sys_mmap(POSTER_MAGIC_1024) 177 let prl0: i64 = nal_to_rbsp(pps_nal, ppsnl, prb0) 178 let pp0: *i64 = sys_mmap(128) as *i64 179 nx_h264_parse_pps(prb0, prl0, pp0) 180 if pp0[2] == 1 { sys_close(fd); cp_puts("SKIP: CABAC stream (fast pre-check); CABAC decoder is a separate rung\n\x00" as *u8); return 10 } 181 } } 182 183 // ---- PHASE 2: CAVLC (or SPS/PPS not yet seen) -> read the rest up to MAXSCAN, then decode ---- 184 var g2: i64 = 1 185 while g2 == 1 { 186 let r: i64 = sys_read(fd, ((filebuf as i64) + total) as *u8, MAXSCAN - total) 187 if r <= 0 { g2 = 0 } 188 if r > 0 { total = total + r; if total >= MAXSCAN { g2 = 0 } } 189 } 190 sys_close(fd) 191 eslen = demux_video_es(filebuf, total, es, pidout) 192 spsnl = find_first_nal(es, eslen, 7, sps_nal, POSTER_MAGIC_1024) 193 ppsnl = find_first_nal(es, eslen, 8, pps_nal, POSTER_MAGIC_1024) 194 let idrnl: i64 = find_first_nal(es, eslen, 5, idr_nal, 4 * POSTER_MAGIC_1024 * POSTER_MAGIC_1024) 195 // Name the real reason. Before this, an HEVC recording fell through to 196 // "no SPS" -- which reads as a CORRUPT FILE and sends you debugging the 197 // recording instead of the missing decoder. 198 if pidout[2] == 1 { if pidout[0] < 0 { cp_puts("SKIP: HEVC/H.265 stream; this poster path decodes H.264 only (H.265 decoder not built)\n\x00" as *u8); return 11 } } 199 if spsnl == 0 { cp_puts("ERR: no SPS in first 8MiB\n\x00" as *u8); return 3 } 200 if ppsnl == 0 { cp_puts("ERR: no PPS in first 8MiB\n\x00" as *u8); return 3 } 201 if idrnl == 0 { cp_puts("ERR: no IDR slice in first 8MiB\n\x00" as *u8); return 3 } 202 203 let sps_rbsp: *u8 = sys_mmap(POSTER_MAGIC_1024) 204 let srlen: i64 = nal_to_rbsp(sps_nal, spsnl, sps_rbsp) 205 let sps: *i64 = sys_mmap(128) as *i64 206 nx_h264_parse_sps(sps_rbsp, srlen, sps) 207 let pps_rbsp: *u8 = sys_mmap(POSTER_MAGIC_1024) 208 let prlen: i64 = nal_to_rbsp(pps_nal, ppsnl, pps_rbsp) 209 let pps: *i64 = sys_mmap(128) as *i64 210 nx_h264_parse_pps(pps_rbsp, prlen, pps) 211 212 if pps[2] == 1 { cp_puts("SKIP: CABAC stream (CAVLC poster decoder only); CABAC decoder is a separate rung\n\x00" as *u8); return 10 } 213 if sps[6] == 0 { cp_puts("SKIP: interlaced (frame_mbs_only=0) not supported\n\x00" as *u8); return 11 } 214 215 let mbW: i64 = sps[4] + 1 216 let mbH: i64 = sps[5] + 1 217 let Wcod: i64 = mbW * 16 218 let Hcod: i64 = mbH * 16 219 var cropW: i64 = sps[2] 220 var cropH: i64 = sps[3] 221 if cropW <= 0 { cropW = Wcod } 222 if cropH <= 0 { cropH = Hcod } 223 if cropW > Wcod { cropW = Wcod } 224 if cropH > Hcod { cropH = Hcod } 225 let cpW: i64 = mbW * 8 226 let cpH: i64 = mbH * 8 227 228 // reject a corrupt/oversized frame BEFORE any dimension-sized allocation (yf/uf/vf below). See CP_MAX_* above: 229 // an unvalidated mbW/mbH from a garbage SPS is the root of the 15-18GB anon-rss OOM-kills. 230 if mbW < 1 { cp_puts("ERR: bad SPS (mbW<1)\n\x00" as *u8); return 3 } 231 if mbH < 1 { cp_puts("ERR: bad SPS (mbH<1)\n\x00" as *u8); return 3 } 232 if mbW > CP_MAX_MB_DIM { cp_puts("SKIP: frame too wide (corrupt/unsupported SPS)\n\x00" as *u8); return 12 } 233 if mbH > CP_MAX_MB_DIM { cp_puts("SKIP: frame too tall (corrupt/unsupported SPS)\n\x00" as *u8); return 12 } 234 if mbW * mbH > CP_MAX_MBS { cp_puts("SKIP: frame too large (corrupt/unsupported SPS)\n\x00" as *u8); return 12 } 235 236 let idr_rbsp: *u8 = sys_mmap(4 * POSTER_MAGIC_1024 * POSTER_MAGIC_1024) 237 let irlen: i64 = nal_to_rbsp(idr_nal, idrnl, idr_rbsp) 238 let ntype: i64 = idr_nal[0] as i64 239 240 let yf: *u8 = sys_mmap(Wcod * Hcod + 64) 241 let uf: *u8 = sys_mmap(cpW * cpH + 64) 242 let vf: *u8 = sys_mmap(cpW * cpH + 64) 243 // ALLOCATION CHECK 2026-08-01: sys_mmap returns the RAW syscall value, so a 244 // failure is a negative errno cast to a pointer -- and the zeroing loop below 245 // writes straight through it. Under a ulimit -v cap that was a SIGSEGV that 246 // read as a codec limitation. Refuse with a distinct rc so a caller can retry 247 // at a higher cap instead of silently losing the recording. 248 if (yf as i64) < 0 { cp_puts("ERR: cannot allocate luma plane (raise the memory cap)\n\x00" as *u8); return 13 } 249 if (uf as i64) < 0 { cp_puts("ERR: cannot allocate chroma-U plane (raise the memory cap)\n\x00" as *u8); return 13 } 250 if (vf as i64) < 0 { cp_puts("ERR: cannot allocate chroma-V plane (raise the memory cap)\n\x00" as *u8); return 13 } 251 var zz: i64 = 0 252 while zz < Wcod * Hcod { yf[zz] = 0 as u8; zz = zz + 1 } 253 zz = 0 254 while zz < cpW * cpH { uf[zz] = 0 as u8; vf[zz] = 0 as u8; zz = zz + 1 } 255 256 let ok: i64 = nx_h264_decode_iframe(idr_rbsp, irlen, ntype, sps, pps, yf, uf, vf, mbW, mbH) 257 258 cp_puts("DECODE profile=\x00" as *u8); cp_num(sps[0]); cp_puts(" coded=\x00" as *u8); cp_num(Wcod); cp_puts("x\x00" as *u8); cp_num(Hcod) 259 cp_puts(" crop=\x00" as *u8); cp_num(cropW); cp_puts("x\x00" as *u8); cp_num(cropH); cp_puts(" mbs=\x00" as *u8); cp_num(mbW*mbH) 260 cp_puts(" idr_rbsp=\x00" as *u8); cp_num(irlen); cp_puts(" full_sync=\x00" as *u8); cp_num(ok); cp_puts("\n\x00" as *u8) 261 262 // HARDEN: never serve a partial/garbled frame -- suppress the poster write unless entropy fully synced. 263 if ok == 0 { cp_puts("PARTIAL: full_sync=0 -> poster SUPPRESSED (not written)\n\x00" as *u8); return 7 } 264 265 // poster dims (multiples of 8, aspect-preserving, no upscale) 266 var dstW: i64 = POSTER_MAX 267 var dstH: i64 = POSTER_MAX 268 if cropW >= cropH { dstW = POSTER_MAX; dstH = cropH * POSTER_MAX / cropW } else { dstH = POSTER_MAX; dstW = cropW * POSTER_MAX / cropH } 269 if cropW <= POSTER_MAX { if cropH <= POSTER_MAX { dstW = cropW; dstH = cropH } } 270 dstW = rd8(dstW) 271 dstH = rd8(dstH) 272 273 let dstrgb: *u8 = sys_mmap(dstW * dstH * 3 + 64) 274 build_poster_rgb(yf, uf, vf, Wcod, cpW, cropW, cropH, dstW, dstH, dstrgb) 275 276 let out: *u8 = sys_mmap(dstW * dstH * 4 + POSTER_MAGIC_8192) 277 let jlen: i64 = encode_jpeg(out, dstW, dstH, dstrgb) 278 279 let fd: i64 = sys_openat_wr(outpath, 0x1a4) 280 if fd < 0 { cp_puts("ERR: cannot open output\n\x00" as *u8); return 1 } 281 let wrote: i64 = sys_write(fd, out, jlen) 282 sys_close(fd) 283 284 cp_puts("POSTER wrote=\x00" as *u8); cp_puts(outpath); cp_puts(" dims=\x00" as *u8); cp_num(dstW); cp_puts("x\x00" as *u8); cp_num(dstH) 285 cp_puts(" jpeg_bytes=\x00" as *u8); cp_num(jlen); cp_puts(" written=\x00" as *u8); cp_num(wrote); cp_puts("\n\x00" as *u8) 286 cp_puts("POSTER-OK\n\x00" as *u8) 287 return 0 288}