code wiki / (root) / nx_h264_ts_probe.nx

nx_h264_ts_probe.nx source

↩ module page · 254 lines · 11384 B

1// nx_h264_ts_probe.nx -- STEP 0 decisive probe for the cam-poster build. 2// Reads a real MPEG-TS cam recording, demuxes the video PES (reusing the proven 3// nx_ts2fmp4 PAT->PMT->video-PID->ES logic), NAL-walks the elementary stream for 4// SPS (type 7) + PPS (type 8), and reports the two facts that LOCK the decode path: 5// PPS.entropy_coding_mode_flag (0=CAVLC, 1=CABAC) -> which entropy decoder 6// PPS.transform_8x8_mode_flag (High-profile 8x8) -> which inverse transform 7// plus SPS profile_idc/level/dims. main() is intentionally decomposed into small 8// functions (nx_cc SIGSEGVs on an over-large function body). license_tier: ORIGINAL 9import "nx_syscalls.nx" 10import "nx_h264_bits.nx" 11import "nx_h264_sps.nx" 12import "nx_h264_pps.nx" 13const K_MAGIC_1024: i64 = 1024 14 15const MAXSCAN: i64 = 8388608 // 8 MiB from head: covers the first IDR + its SPS/PPS 16 17func pp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 } 18func pn(v: i64) -> i64 { 19 let bb: *u8 = sys_mmap(32) 20 var m: i64 = v 21 if m < 0 { sys_write(1, "-\x00" as *u8, 1); m = 0 - m } 22 let t: *u8 = sys_mmap(32) 23 var k: i64 = 0 24 if m == 0 { t[0] = 48 as u8; k = 1 } 25 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 } 26 var i: i64 = 0 27 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 } 28 sys_write(1, bb, k) 29 return 0 30} 31 32// --- self-contained bit reader (explicit i64 cursor; NO imported struct) --- 33func lb_bit(data: *u8, len: i64, pos: *i64) -> i64 { 34 let bp: i64 = pos[0] 35 let byte: i64 = bp >> 3 36 pos[0] = bp + 1 37 if byte >= len { return 0 } 38 return ((data[byte] as i64) >> (7 - (bp & 7))) & 1 39} 40func lb_bits(data: *u8, len: i64, pos: *i64, n: i64) -> i64 { 41 var v: i64 = 0 42 var i: i64 = 0 43 while i < n { v = (v << 1) | lb_bit(data, len, pos); i = i + 1 } 44 return v 45} 46func lb_ue(data: *u8, len: i64, pos: *i64) -> i64 { 47 var z: i64 = 0 48 var seen: i64 = 0 49 while seen == 0 { 50 if (pos[0] >> 3) >= len { return (1 << z) - 1 } 51 let b: i64 = lb_bit(data, len, pos) 52 if b == 1 { seen = 1 } else { z = z + 1 } 53 } 54 let rest: i64 = lb_bits(data, len, pos, z) 55 return (1 << z) - 1 + rest 56} 57func lb_se(data: *u8, len: i64, pos: *i64) -> i64 { 58 let k: i64 = lb_ue(data, len, pos) 59 if k == 0 { return 0 } 60 let mag: i64 = (k + 1) / 2 61 if (k & 1) == 1 { return mag } 62 return 0 - mag 63} 64 65// H.264 more_rbsp_data(): cursor before the rbsp_stop_one_bit (last '1' bit). 66func probe_more_rbsp(data: *u8, len: i64, curpos: i64) -> i64 { 67 var last: i64 = len - 1 68 while last >= 0 { 69 if data[last] != (0 as u8) { 70 var bit: i64 = 0 71 while (((data[last] as i64) >> bit) & 1) == 0 { bit = bit + 1 } 72 let stop_pos: i64 = last * 8 + (7 - bit) 73 if curpos < stop_pos { return 1 } 74 return 0 75 } 76 last = last - 1 77 } 78 return 0 79} 80 81// Full PPS walk to reach transform_8x8_mode_flag. out[2]=entropy out[3]=pic_init_qp 82// out[4]=nsg out[5]=transform_8x8 out[6]=more_rbsp_present out[7]=slice_group_unparsed 83func probe_parse_pps_full(rbsp: *u8, len: i64, out: *i64) -> i64 { 84 var q: i64 = 0 85 while q < 8 { out[q] = 0; q = q + 1 } 86 let pos: *i64 = sys_mmap(16) as *i64 87 pos[0] = 0 88 out[0] = lb_ue(rbsp, len, pos) 89 out[1] = lb_ue(rbsp, len, pos) 90 out[2] = lb_bits(rbsp, len, pos, 1) 91 lb_bits(rbsp, len, pos, 1) 92 let nsg: i64 = lb_ue(rbsp, len, pos) 93 out[4] = nsg 94 if nsg > 0 { out[7] = 1; return 0 } 95 lb_ue(rbsp, len, pos) 96 lb_ue(rbsp, len, pos) 97 lb_bits(rbsp, len, pos, 1) 98 lb_bits(rbsp, len, pos, 2) 99 let qpd: i64 = lb_se(rbsp, len, pos) 100 out[3] = 26 + qpd 101 lb_se(rbsp, len, pos) 102 lb_se(rbsp, len, pos) 103 lb_bits(rbsp, len, pos, 1) 104 lb_bits(rbsp, len, pos, 1) 105 lb_bits(rbsp, len, pos, 1) 106 let mr: i64 = probe_more_rbsp(rbsp, len, pos[0]) 107 out[6] = mr 108 if mr == 1 { out[5] = lb_bits(rbsp, len, pos, 1) } 109 return 0 110} 111 112// Read up to MAXSCAN bytes from path into buf; returns byte count. 113func read_head(path: *u8, buf: *u8) -> i64 { 114 let fd: i64 = sys_openat_rd(path) 115 if fd < 0 { return 0 - 1 } 116 var total: i64 = 0 117 var go: i64 = 1 118 while go == 1 { 119 let r: i64 = sys_read(fd, ((buf as i64) + total) as *u8, MAXSCAN - total) 120 if r <= 0 { go = 0 } 121 if r > 0 { total = total + r; if total >= MAXSCAN { go = 0 } } 122 } 123 sys_close(fd) 124 return total 125} 126 127// Demux the video elementary stream from a TS buffer. pidout[0]=vidpid pidout[1]=pmtpid. 128// Returns es length. 129func demux_video_es(filebuf: *u8, total: i64, es: *u8, pidout: *i64) -> i64 { 130 var eslen: i64 = 0 131 var pmtpid: i64 = 0 - 1 132 var vidpid: i64 = 0 - 1 133 var i: i64 = 0 134 while i + 188 <= total { 135 if filebuf[i] != (0x47 as u8) { i = i + 1 } else { 136 let b1: i64 = filebuf[i+1] as i64 137 let pusi: i64 = (b1 >> 6) & 1 138 let pid: i64 = ((b1 & 0x1f) << 8) | (filebuf[i+2] as i64) 139 let afc: i64 = ((filebuf[i+3] as i64) >> 4) & 3 140 var payoff: i64 = i + 4 141 if afc == 2 { payoff = i + 188 } else { if afc == 3 { payoff = i + 5 + (filebuf[i+4] as i64) } } 142 if payoff < i + 188 { 143 if pid == 0 { if pmtpid < 0 { 144 var p: i64 = payoff 145 if pusi == 1 { p = p + 1 + (filebuf[p] as i64) } 146 let sl: i64 = (((filebuf[p+1] as i64) & 0x0f) << 8) | (filebuf[p+2] as i64) 147 var pp2: i64 = p + 8 148 let pe: i64 = p + 3 + sl - 4 149 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 } 150 } } 151 if pid == pmtpid { if pmtpid >= 0 { if vidpid < 0 { 152 var p: i64 = payoff 153 if pusi == 1 { p = p + 1 + (filebuf[p] as i64) } 154 let sl: i64 = (((filebuf[p+1] as i64) & 0x0f) << 8) | (filebuf[p+2] as i64) 155 let pil: i64 = (((filebuf[p+10] as i64) & 0x0f) << 8) | (filebuf[p+11] as i64) 156 var ep: i64 = p + 12 + pil 157 let pe: i64 = p + 3 + sl - 4 158 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); if st == 0x1b { if vidpid < 0 { vidpid = ed } } ep = ep + 5 + el } 159 } } } 160 if pid == vidpid { if vidpid >= 0 { 161 var p: i64 = payoff 162 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) } } } } 163 var qv: i64 = p 164 while qv < i + 188 { if eslen < MAXSCAN { es[eslen] = filebuf[qv]; eslen = eslen + 1 } qv = qv + 1 } 165 } } 166 } 167 i = i + 188 168 } 169 } 170 pidout[0] = vidpid 171 pidout[1] = pmtpid 172 return eslen 173} 174 175// Find the FIRST Annex-B NAL of type `want` in es[0..eslen); copy its bytes 176// (incl the 1-byte NAL header) into dst[0..cap); return the NAL length (0 if none). 177func find_first_nal(es: *u8, eslen: i64, want: i64, dst: *u8, cap: i64) -> i64 { 178 var k: i64 = 0 179 while k + 3 < eslen { 180 if es[k] == (0 as u8) { if es[k+1] == (0 as u8) { if es[k+2] == (1 as u8) { 181 let ns: i64 = k + 3 182 let nt: i64 = (es[ns] as i64) & 0x1f 183 if nt == want { 184 var m: i64 = ns 185 var ne: i64 = eslen 186 var fnd: i64 = 0 187 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 } } 188 if fnd == 1 { if es[ne-1] == (0 as u8) { ne = ne - 1 } } 189 let nl: i64 = ne - ns 190 var c: i64 = 0 191 while c < nl { if c < cap { dst[c] = es[ns+c] } c = c + 1 } 192 return nl 193 } 194 k = ns 195 } else { k = k + 1 } } else { k = k + 1 } } else { k = k + 1 } 196 } 197 return 0 198} 199 200// de-emulate a NAL payload (skip the 1-byte NAL header) into rbsp; return rbsp len. 201func nal_to_rbsp(nal: *u8, nallen: i64, rbsp: *u8) -> i64 { 202 return nx_h264_unescape_rbsp(((nal as i64) + 1) as *u8, nallen - 1, rbsp) 203} 204 205func report_sps(nal: *u8, nallen: i64) -> i64 { 206 let rbsp: *u8 = sys_mmap(K_MAGIC_1024) 207 let rlen: i64 = nal_to_rbsp(nal, nallen, rbsp) 208 let sout: *i64 = sys_mmap(8 * 16) as *i64 209 nx_h264_parse_sps(rbsp, rlen, sout) 210 pp("SPS: profile_idc=\x00" as *u8); pn(sout[0]); pp(" level_idc=\x00" as *u8); pn(sout[1]) 211 pp(" width=\x00" as *u8); pn(sout[2]); pp(" height=\x00" as *u8); pn(sout[3]) 212 pp(" chroma_format_idc=\x00" as *u8); pn(sout[7]); pp(" is_high=\x00" as *u8); pn(nx_h264_is_high(sout[0])) 213 pp(" scaling_unparsed=\x00" as *u8); pn(sout[8]); pp(" nal_len=\x00" as *u8); pn(nallen); pp("\n\x00" as *u8) 214 return 0 215} 216 217func report_pps(nal: *u8, nallen: i64) -> i64 { 218 let rbsp: *u8 = sys_mmap(K_MAGIC_1024) 219 let rlen: i64 = nal_to_rbsp(nal, nallen, rbsp) 220 let pout: *i64 = sys_mmap(8 * 16) as *i64 221 nx_h264_parse_pps(rbsp, rlen, pout) 222 let pf: *i64 = sys_mmap(8 * 16) as *i64 223 probe_parse_pps_full(rbsp, rlen, pf) 224 pp("PPS: entropy_coding_mode=\x00" as *u8); pn(pout[2]); pp(" pic_init_qp=\x00" as *u8); pn(pout[3]) 225 pp(" nsg=\x00" as *u8); pn(pout[4]); pp(" nal_len=\x00" as *u8); pn(nallen); pp("\n\x00" as *u8) 226 pp("PPS-FULL: entropy_xcheck=\x00" as *u8); pn(pf[2]); pp(" more_rbsp=\x00" as *u8); pn(pf[6]) 227 pp(" transform_8x8_mode=\x00" as *u8); pn(pf[5]); pp(" slice_group_unparsed=\x00" as *u8); pn(pf[7]); pp("\n\x00" as *u8) 228 if pout[2] == 1 { pp("VERDICT: CABAC entropy (High-profile CABAC decoder REQUIRED)\n\x00" as *u8) } 229 if pout[2] == 0 { pp("VERDICT: CAVLC entropy (existing bit-exact CAVLC path applies)\n\x00" as *u8) } 230 return 0 231} 232 233func main(argc: i64, argv: *i64) -> i64 { 234 if argc < 2 { pp("usage: nx_h264_ts_probe <in.ts>\n\x00" as *u8); return 2 } 235 let inpath: *u8 = argv[1] as *u8 236 let filebuf: *u8 = sys_mmap(MAXSCAN + 512) 237 let total: i64 = read_head(inpath, filebuf) 238 if total < 0 { pp("ERR: cannot open input\n\x00" as *u8); return 1 } 239 let es: *u8 = sys_mmap(MAXSCAN + 512) 240 let pidout: *i64 = sys_mmap(64) as *i64 241 let eslen: i64 = demux_video_es(filebuf, total, es, pidout) 242 pp("TS-PROBE file=\x00" as *u8); pp(inpath); pp("\n\x00" as *u8) 243 pp("scanned_bytes=\x00" as *u8); pn(total); pp(" video_pid=\x00" as *u8); pn(pidout[0]) 244 pp(" pmt_pid=\x00" as *u8); pn(pidout[1]); pp(" es_bytes=\x00" as *u8); pn(eslen); pp("\n\x00" as *u8) 245 let sps: *u8 = sys_mmap(K_MAGIC_1024) 246 let pps: *u8 = sys_mmap(K_MAGIC_1024) 247 let spslen: i64 = find_first_nal(es, eslen, 7, sps, K_MAGIC_1024) 248 let ppslen: i64 = find_first_nal(es, eslen, 8, pps, K_MAGIC_1024) 249 if spslen == 0 { pp("SPS: NOT-FOUND\n\x00" as *u8) } 250 if spslen > 0 { report_sps(sps, spslen) } 251 if ppslen == 0 { pp("PPS: NOT-FOUND\n\x00" as *u8) } 252 if ppslen > 0 { report_pps(pps, ppslen) } 253 return 0 254}