code wiki / (root) / nx_ts_es.nx

nx_ts_es.nx source

↩ module page · 100 lines · 5452 B

1// nx_ts_es.nx -- MPEG-TS -> H.264 elementary-stream helpers, LIFTED VERBATIM from 2// nx_cam_poster.nx (lines 68-152) 2026-07-31 so a second consumer does not copy-paste 3// them (rule 15). Behaviour must stay byte-identical: nx_cam_poster_gate is the oracle. 4// demux_video_es -- PAT/PMT walk -> video PID -> ES bytes; codec via nx_ts_nal 5// find_first_nal -- first NAL of a given type out of an ES 6// nal_to_rbsp -- emulation-prevention-byte removal 7// license_tier: ORIGINAL 8import "nx_syscalls.nx" 9import "nx_ts_nal.nx" 10 11// scan window for the TS->ES pass. Owned HERE because demux_video_es reads it; 12// the compiler rejects a module const used above its declaration (it would 13// silently read 0), so a lib cannot borrow this from its caller. 14const MAXSCAN: i64 = 8388608 15 16func demux_video_es(filebuf: *u8, total: i64, es: *u8, pidout: *i64) -> i64 { 17 var eslen: i64 = 0 18 var pmtpid: i64 = 0 - 1 19 var vidpid: i64 = 0 - 1 20 // HEVC seen in the PMT but deliberately NOT claimed as vidpid (this organ 21 // decodes H.264). Surfaced so the caller can refuse BY NAME instead of 22 // reporting the generic "no video PID", which reads as a corrupt file. 23 var cphevc: i64 = 0 24 var i: i64 = 0 25 while i + 188 <= total { 26 if filebuf[i] != (0x47 as u8) { i = i + 1 } else { 27 let b1: i64 = filebuf[i+1] as i64 28 let pusi: i64 = (b1 >> 6) & 1 29 let pid: i64 = ((b1 & 0x1f) << 8) | (filebuf[i+2] as i64) 30 let afc: i64 = ((filebuf[i+3] as i64) >> 4) & 3 31 var payoff: i64 = i + 4 32 if afc == 2 { payoff = i + 188 } else { if afc == 3 { payoff = i + 5 + (filebuf[i+4] as i64) } } 33 if payoff < i + 188 { 34 if pid == 0 { if pmtpid < 0 { 35 var p: i64 = payoff 36 if pusi == 1 { p = p + 1 + (filebuf[p] as i64) } 37 let sl: i64 = (((filebuf[p+1] as i64) & 0x0f) << 8) | (filebuf[p+2] as i64) 38 var pp2: i64 = p + 8 39 let pe: i64 = p + 3 + sl - 4 40 while pp2 + 4 <= pe { let pr: i64 = ((filebuf[pp2] as i64) << 8) | (filebuf[pp2+1] as i64); let pm: i64 = (((filebuf[pp2+2] as i64) & 0x1f) << 8) | (filebuf[pp2+3] as i64); if pr != 0 { if pmtpid < 0 { pmtpid = pm } } pp2 = pp2 + 4 } 41 } } 42 if pid == pmtpid { if pmtpid >= 0 { if vidpid < 0 { 43 var p: i64 = payoff 44 if pusi == 1 { p = p + 1 + (filebuf[p] as i64) } 45 let sl: i64 = (((filebuf[p+1] as i64) & 0x0f) << 8) | (filebuf[p+2] as i64) 46 let pil: i64 = (((filebuf[p+10] as i64) & 0x0f) << 8) | (filebuf[p+11] as i64) 47 var ep: i64 = p + 12 + pil 48 let pe: i64 = p + 3 + sl - 4 49 while ep + 5 <= pe { let st: i64 = filebuf[ep] as i64; let ed: i64 = (((filebuf[ep+1] as i64) & 0x1f) << 8) | (filebuf[ep+2] as i64); let el: i64 = (((filebuf[ep+3] as i64) & 0x0f) << 8) | (filebuf[ep+4] as i64); let vc: i64 = tsn_codec_of_stream_type(st); if vc == TSN_H264 { if vidpid < 0 { vidpid = ed } } if vc == TSN_HEVC { cphevc = 1 } ep = ep + 5 + el } 50 } } } 51 if pid == vidpid { if vidpid >= 0 { 52 var p: i64 = payoff 53 if pusi == 1 { if filebuf[p] == (0 as u8) { if filebuf[p+1] == (0 as u8) { if filebuf[p+2] == (1 as u8) { p = p + 9 + (filebuf[p+8] as i64) } } } } 54 var qv: i64 = p 55 while qv < i + 188 { if eslen < MAXSCAN { es[eslen] = filebuf[qv]; eslen = eslen + 1 } qv = qv + 1 } 56 } } 57 } 58 i = i + 188 59 } 60 } 61 pidout[0] = vidpid 62 pidout[1] = pmtpid 63 pidout[2] = cphevc 64 return eslen 65} 66 67// H.264 ONLY, DELIBERATELY. This organ DECODES the bitstream (nx_h264_parse_pps, 68// CAVLC/CABAC) to render the poster image -- it does not merely demux. Widening 69// the PMT to accept HEVC here would hand H.265 NALs to an H.264 decoder and turn 70// a clean "no video PID" refusal into a MISDECODE. An HEVC poster needs an H.265 71// decoder, which is a separate arc; until then HEVC is REFUSED BY NAME (see the 72// cphevc path) rather than silently producing nothing. 73func find_first_nal(es: *u8, eslen: i64, want: i64, dst: *u8, cap: i64) -> i64 { 74 var k: i64 = 0 75 while k + 3 < eslen { 76 if es[k] == (0 as u8) { if es[k+1] == (0 as u8) { if es[k+2] == (1 as u8) { 77 let ns: i64 = k + 3 78 let nt: i64 = (es[ns] as i64) & 0x1f 79 if nt == want { 80 var m: i64 = ns 81 var ne: i64 = eslen 82 var fnd: i64 = 0 83 while m + 3 < eslen { if fnd == 0 { if es[m] == (0 as u8) { if es[m+1] == (0 as u8) { if es[m+2] == (1 as u8) { ne = m; fnd = 1 } } } if fnd == 0 { m = m + 1 } } else { m = eslen } } 84 if fnd == 1 { if es[ne-1] == (0 as u8) { ne = ne - 1 } } 85 let nl: i64 = ne - ns 86 var c: i64 = 0 87 while c < nl { if c < cap { dst[c] = es[ns+c] } c = c + 1 } 88 return nl 89 } 90 k = ns 91 } else { k = k + 1 } } else { k = k + 1 } } else { k = k + 1 } 92 } 93 return 0 94} 95 96func nal_to_rbsp(nal: *u8, nallen: i64, rbsp: *u8) -> i64 { 97 return nx_h264_unescape_rbsp(((nal as i64) + 1) as *u8, nallen - 1, rbsp) 98} 99 100// YUV420 (cropped region) -> RGB, integer box-average downscale into dst[dstW*dstH*3].