code wiki / (root) / nx_ts_probe.nx

nx_ts_probe.nx source

↩ module page · 187 lines · 10858 B

1// nx_ts_probe.nx -- Rung 1 of the sovereign MPEG-TS -> fMP4 remuxer. Parses an MPEG-TS file (188-byte 2// packets), reads PAT -> PMT -> elementary-stream table, identifies the video PID + codec, reassembles the 3// video PES into an Annex-B elementary stream, and scans it for NAL units (SPS=7 / PPS=8 / IDR=5). Reports 4// what it finds so we can confirm H.264 before building the MP4 muxer. NishiLang only, no ffmpeg. 5// Usage: nx_ts_probe <file.ts> (default = first recording in the vid index). license_tier: ORIGINAL 6import "nx_syscalls.nx" 7const K_MAGIC_2048: i64 = 2048 8 9const TSREAD: i64 = 8388608 // probe the first 8 MB (PAT/PMT + plenty of video to find SPS/PPS/IDR) 10const ESCAP: i64 = 8388608 11 12func tp_p(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 } 13func tp_n(v: i64) -> i64 { let b: *u8=sys_mmap(28); var m: i64=v; if m<0{m=0-m}; let t: *u8=sys_mmap(28); var k: i64=0; if m==0{t[0]=48;k=1}; while m>0{t[k]=(48+(m%10)) as u8;m=m/10;k=k+1}; var i: i64=0; while i<k{b[i]=t[k-1-i];i=i+1}; sys_write(1,b,k); return 0 } 14func tp_hex(v: i64) -> i64 { let h: *u8="0123456789abcdef" as *u8; let b: *u8=sys_mmap(4); b[0]=h[(v>>4)&0xf]; b[1]=h[v&0xf]; sys_write(1,b,2); return 0 } 15func tp_rate(idx: i64) -> i64 { 16 if idx==0 { return tp_p("96000" as *u8) } 17 if idx==1 { return tp_p("88200" as *u8) } 18 if idx==2 { return tp_p("64000" as *u8) } 19 if idx==3 { return tp_p("48000" as *u8) } 20 if idx==4 { return tp_p("44100" as *u8) } 21 if idx==5 { return tp_p("32000" as *u8) } 22 if idx==6 { return tp_p("24000" as *u8) } 23 if idx==7 { return tp_p("22050" as *u8) } 24 if idx==8 { return tp_p("16000" as *u8) } 25 if idx==9 { return tp_p("12000" as *u8) } 26 if idx==10 { return tp_p("11025" as *u8) } 27 if idx==11 { return tp_p("8000" as *u8) } 28 return tp_p("?" as *u8) 29} 30 31func main(argc: i64, argv: *i64) -> i64 { 32 var path: *u8 = 0 as *u8 33 if argc >= 2 { path = argv[1] as *u8 } else { 34 let szp0: *i64 = sys_mmap(16) as *i64 35 let vb: *u8 = sys_read_file("knowledge/status/galx_vid_paths.tsv" as *u8, szp0) 36 if (vb as i64) == 0 { tp_p("PROBE FAIL: no path arg + no vid index\n" as *u8); sys_exit(1); return 1 } 37 let pb: *u8 = sys_mmap(K_MAGIC_2048); var o: i64 = 0 38 while vb[o] != (10 as u8) { pb[o] = vb[o]; o = o + 1 } pb[o] = 0 as u8 39 path = pb 40 } 41 tp_p("probe " as *u8); tp_p(path); tp_p("\n" as *u8) 42 let fd: i64 = sys_openat_rd(path) 43 if fd < 0 { tp_p("PROBE FAIL: cannot open\n" as *u8); sys_exit(1); return 1 } 44 let buf: *u8 = sys_mmap(TSREAD + 16) 45 var n: i64 = 0 46 var go0: i64 = 1 47 while go0 == 1 { if n >= TSREAD { go0 = 0 } else { let r: i64 = sys_read(fd, ((buf as i64)+n) as *u8, TSREAD - n); if r <= 0 { go0 = 0 } else { n = n + r } } } 48 sys_close(fd) 49 50 let es: *u8 = sys_mmap(ESCAP + 16) 51 var eslen: i64 = 0 52 var pmtpid: i64 = 0 - 1 53 var vidpid: i64 = 0 - 1 54 var vidtype: i64 = 0 - 1 55 var audpid: i64 = 0 - 1 56 var audtype: i64 = 0 - 1 57 var audbytes: i64 = 0 58 var asfreq: i64 = 0 - 1 59 var achan: i64 = 0 - 1 60 var ntypes: i64 = 0 61 var pkts: i64 = 0 62 var i: i64 = 0 63 while i + 188 <= n { 64 if buf[i] != (0x47 as u8) { i = i + 1 } else { 65 pkts = pkts + 1 66 let b1: i64 = buf[i+1] as i64 67 let b2: i64 = buf[i+2] as i64 68 let b3: i64 = buf[i+3] as i64 69 let pusi: i64 = (b1 >> 6) & 1 70 let pid: i64 = ((b1 & 0x1f) << 8) | b2 71 let afc: i64 = (b3 >> 4) & 3 72 var payoff: i64 = i + 4 73 if afc == 2 { payoff = i + 188 } else { if afc == 3 { let aflen: i64 = buf[i+4] as i64; payoff = i + 5 + aflen } } 74 if payoff < i + 188 { 75 // PAT 76 if pid == 0 { if pmtpid < 0 { 77 var p: i64 = payoff 78 if pusi == 1 { let ptr: i64 = buf[p] as i64; p = p + 1 + ptr } 79 let seclen: i64 = (((buf[p+1] as i64) & 0x0f) << 8) | (buf[p+2] as i64) 80 var pp: i64 = p + 8 81 let pend: i64 = p + 3 + seclen - 4 82 while pp + 4 <= pend { 83 let prog: i64 = ((buf[pp] as i64) << 8) | (buf[pp+1] as i64) 84 let pmt: i64 = (((buf[pp+2] as i64) & 0x1f) << 8) | (buf[pp+3] as i64) 85 if prog != 0 { if pmtpid < 0 { pmtpid = pmt } } 86 pp = pp + 4 87 } 88 } } 89 // PMT 90 if pid == pmtpid { if pmtpid >= 0 { if vidpid < 0 { 91 var p: i64 = payoff 92 if pusi == 1 { let ptr: i64 = buf[p] as i64; p = p + 1 + ptr } 93 let seclen: i64 = (((buf[p+1] as i64) & 0x0f) << 8) | (buf[p+2] as i64) 94 let proginfolen: i64 = (((buf[p+10] as i64) & 0x0f) << 8) | (buf[p+11] as i64) 95 var esp: i64 = p + 12 + proginfolen 96 let pend: i64 = p + 3 + seclen - 4 97 while esp + 5 <= pend { 98 let stype: i64 = buf[esp] as i64 99 let epid: i64 = (((buf[esp+1] as i64) & 0x1f) << 8) | (buf[esp+2] as i64) 100 let esinfolen: i64 = (((buf[esp+3] as i64) & 0x0f) << 8) | (buf[esp+4] as i64) 101 tp_p(" stream_type=0x" as *u8); tp_hex(stype); tp_p(" pid=" as *u8); tp_n(epid); tp_p("\n" as *u8) 102 ntypes = ntypes + 1 103 if stype == 0x1b { if vidpid < 0 { vidpid = epid; vidtype = stype } } // H.264 104 if stype == 0x24 { if vidpid < 0 { vidpid = epid; vidtype = stype } } // HEVC 105 if stype == 0x0f { if audpid < 0 { audpid = epid; audtype = stype } } // AAC (ADTS) 106 if stype == 0x11 { if audpid < 0 { audpid = epid; audtype = stype } } // AAC (LATM) 107 if stype == 0x03 { if audpid < 0 { audpid = epid; audtype = stype } } // MPEG-1 audio 108 if stype == 0x04 { if audpid < 0 { audpid = epid; audtype = stype } } // MPEG-2 audio 109 esp = esp + 5 + esinfolen 110 } 111 } } } 112 // video ES collection 113 if pid == vidpid { if vidpid >= 0 { 114 var p: i64 = payoff 115 if pusi == 1 { if buf[p]==(0 as u8) { if buf[p+1]==(0 as u8) { if buf[p+2]==(1 as u8) { let hdrlen: i64 = buf[p+8] as i64; p = p + 9 + hdrlen } } } } 116 var q: i64 = p 117 while q < i + 188 { if eslen < ESCAP { es[eslen] = buf[q]; eslen = eslen + 1 } q = q + 1 } 118 } } 119 // audio ES: confirm real payload + capture the first ADTS header (sampling rate + channels) 120 if pid == audpid { if audpid >= 0 { 121 audbytes = audbytes + (i + 188 - payoff) 122 var ap: i64 = payoff 123 if pusi == 1 { if buf[ap]==(0 as u8) { if buf[ap+1]==(0 as u8) { if buf[ap+2]==(1 as u8) { 124 let ahdr: i64 = buf[ap+8] as i64; ap = ap + 9 + ahdr 125 if asfreq < 0 { if buf[ap]==(0xff as u8) { if ((buf[ap+1] as i64) & 0xf0)==0xf0 { 126 asfreq = ((buf[ap+2] as i64) >> 2) & 0x0f 127 achan = (((buf[ap+2] as i64) & 1) << 2) | (((buf[ap+3] as i64) >> 6) & 3) 128 } } } 129 } } } } 130 } } 131 } 132 i = i + 188 133 } 134 } 135 // scan ES for NAL units (start code 00 00 01, optionally preceded by an extra 00) 136 var spsoff: i64 = 0 - 1; var spslen: i64 = 0 137 var ppsoff: i64 = 0 - 1; var ppslen: i64 = 0 138 var nsps: i64 = 0; var npps: i64 = 0; var nidr: i64 = 0; var nnon: i64 = 0; var nnal: i64 = 0 139 var k: i64 = 0 140 var prevnal: i64 = 0 - 1 141 var prevtype: i64 = 0 - 1 142 while k + 3 < eslen { 143 if es[k]==(0 as u8) { if es[k+1]==(0 as u8) { if es[k+2]==(1 as u8) { 144 let nstart: i64 = k + 3 145 let ntype: i64 = (es[nstart] as i64) & 0x1f 146 // close previous NAL length 147 if prevnal >= 0 { 148 var endp: i64 = k 149 if es[k-1]==(0 as u8) { endp = k - 1 } // strip the extra 00 of a 4-byte start code 150 let plen: i64 = endp - prevnal 151 if prevtype == 7 { if spsoff < 0 { spsoff = prevnal; spslen = plen } } 152 if prevtype == 8 { if ppsoff < 0 { ppsoff = prevnal; ppslen = plen } } 153 } 154 nnal = nnal + 1 155 if ntype == 7 { nsps = nsps + 1 } 156 if ntype == 8 { npps = npps + 1 } 157 if ntype == 5 { nidr = nidr + 1 } 158 if ntype == 1 { nnon = nnon + 1 } 159 prevnal = nstart 160 prevtype = ntype 161 k = nstart 162 } else { k = k + 1 } } else { k = k + 1 } } else { k = k + 1 } 163 } 164 if prevnal >= 0 { 165 let plen: i64 = eslen - prevnal 166 if prevtype == 7 { if spsoff < 0 { spsoff = prevnal; spslen = plen } } 167 if prevtype == 8 { if ppsoff < 0 { ppsoff = prevnal; ppslen = plen } } 168 } 169 tp_p("packets=" as *u8); tp_n(pkts); tp_p(" stream_types=" as *u8); tp_n(ntypes) 170 tp_p(" video_pid=" as *u8); tp_n(vidpid); tp_p(" video_type=0x" as *u8); tp_hex(vidtype); tp_p("\n" as *u8) 171 tp_p("audio_pid=" as *u8); tp_n(audpid); tp_p(" audio_type=0x" as *u8); tp_hex(audtype); tp_p(" audio_bytes=" as *u8); tp_n(audbytes); tp_p(" adts_sfreq_idx=" as *u8); tp_n(asfreq); tp_p(" hz=" as *u8); tp_rate(asfreq); tp_p(" channels=" as *u8); tp_n(achan); tp_p("\n" as *u8) 172 tp_p("es_bytes=" as *u8); tp_n(eslen); tp_p(" nals=" as *u8); tp_n(nnal) 173 tp_p(" sps=" as *u8); tp_n(nsps); tp_p("(len " as *u8); tp_n(spslen); tp_p(")" as *u8) 174 tp_p(" pps=" as *u8); tp_n(npps); tp_p("(len " as *u8); tp_n(ppslen); tp_p(")" as *u8) 175 tp_p(" idr=" as *u8); tp_n(nidr); tp_p(" nonidr=" as *u8); tp_n(nnon); tp_p("\n" as *u8) 176 if vidtype == 0x1b { if nsps > 0 { if npps > 0 { tp_p("PROBE OK: H.264 with SPS+PPS -> remuxable to MP4\n" as *u8); sys_exit(0); return 0 } } } 177 // HEVC is DETECTED at the PMT above (stream_type 0x24) but the NAL counters 178 // below it are H.264-masked, so nsps/npps/nidr are not meaningful here yet. 179 // Say THAT, rather than reporting the generic 'not confirmed H.264', which 180 // reads as a broken file when the stream is simply a different codec. 181 // Indexing/seek/dead-air for HEVC IS supported (nx_ts_index + nx_ts_nal, 182 // 2026-07-31); what is still missing is hvcC in nx_ts2fmp4 for remux. 183 if vidtype == 0x24 { tp_p("PROBE: HEVC/H.265 stream (indexable for seek+dead-air; NOT yet remuxable -- nx_ts2fmp4 has no hvcC)\n" as *u8); sys_exit(0); return 0 } 184 tp_p("PROBE: not confirmed H.264+SPS+PPS (see above)\n" as *u8) 185 sys_exit(0) 186 return 0 187}