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}