nx_ts_index.nx source
↩ module page · 252 lines · 18944 B
1// nx_ts_index.nx -- SOVEREIGN media index ("NXVI", our own format) for an MPEG-TS recording.
2// Scans the .ts ONCE and records: exact duration (sum of per-AU PTS deltas, discontinuity-robust),
3// audio params, SPS/PPS, and a KEYFRAME TABLE (time_ms -> byte_offset of the IDR access unit) so the
4// daemon can seek EXACTLY and instantly without re-scanning. MP4 is emitted only at the browser
5// last-mile; this index is pure-Nishi. Output is a binary index file. Usage: nx_ts_index <in.ts> <out.idx>
6//
7// Format (all big-endian): "NXVI"(4) ver(4)=2 dur_ms(8) arate(4) achan(4) vidpid(4) audpid(4)
8// spslen(4) sps[] ppslen(4) pps[] nkf(8) then nkf * { time_ms(8) byte_off(8) }.
9// v2 adds vidpid/audpid so the muxer can demux a media segment starting EXACTLY at a keyframe AU's
10// first packet without waiting for a PAT/PMT (deterministic exact seek). kf time_ms is 0-based (the
11// clamped totdur timeline), matching dur_ms. license_tier: ORIGINAL
12import "nx_syscalls.nx"
13import "nx_ts_marks.nx"
14import "nx_ts_nal.nx" // codec-aware NAL classification (H.264 + HEVC), gated 16/16
15const K_MAGIC_262144: i64 = 262144
16const K_MAGIC_65536: i64 = 65536
17const K_MAGIC_900000: i64 = 900000
18const K_MAGIC_48000: i64 = 48000
19const K_MAGIC_44100: i64 = 44100
20const K_MAGIC_32000: i64 = 32000
21const K_MAGIC_24000: i64 = 24000
22const K_MAGIC_22050: i64 = 22050
23const K_MAGIC_16000: i64 = 16000
24const K_MAGIC_4096: i64 = 4096
25
26const CHUNK: i64 = 16777216
27const AUCAP: i64 = 8388608
28const MAXKF: i64 = 200000
29// Only the AU's first SCANCAP bytes are needed to find SPS/PPS + the first slice NAL (which classifies the
30// frame as keyframe/not). Copying + scanning the whole AU (the slice DATA, the bulk) was O(total video bytes)
31// of byte-by-byte work PER FRAME -- the >4min .idx build. We cap the copy and break the scan at the 1st slice.
32const SCANCAP: i64 = 16384
33
34func w32(b: *u8, o: i64, v: i64) -> i64 { b[o]=((v>>24)&0xff) as u8; b[o+1]=((v>>16)&0xff) as u8; b[o+2]=((v>>8)&0xff) as u8; b[o+3]=(v&0xff) as u8; return o+4 }
35func w64(b: *u8, o: i64, v: i64) -> i64 { o=w32(b,o,(v>>32)&0xffffffff); o=w32(b,o,v&0xffffffff); return o }
36func pe(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} return sys_write(2,s,n) }
37func pn(v: i64) -> i64 { var m: i64=v; if m<0{m=0-m} let b: *u8=sys_mmap(32); var i: i64=32; if m==0{i=i-1;b[i]=(48 as u8)} while m>0{let q: i64=m/10; i=i-1; b[i]=((48+(m-q*10)) as u8); m=q} return sys_write(2,((b as i64)+i) as *u8,32-i) }
38func r32(b: *u8, o: i64) -> i64 { return ((b[o] as i64)<<24)|((b[o+1] as i64)<<16)|((b[o+2] as i64)<<8)|(b[o+3] as i64) }
39func r64(b: *u8, o: i64) -> i64 { return (r32(b,o)<<32)|(r32(b,o+4)&0xffffffff) }
40// nx_ts_index --dump <idx> -- sovereign inspector: print the NXVI header + sampled keyframe entries.
41func dump_idx(p: *u8) -> i64 {
42 let szp: *i64 = sys_mmap(16) as *i64
43 let b: *u8 = sys_read_file(p, szp)
44 if (b as i64)==0 { pe("dump: cannot read index\n" as *u8); return 1 }
45 let ver: i64 = r32(b,4); let dur: i64 = r64(b,8); let arate: i64 = r32(b,16); let achan: i64 = r32(b,20)
46 let vidpid: i64 = r32(b,24); let audpid: i64 = r32(b,28)
47 var D: i64 = 0
48 var srcsize: i64 = 0
49 if ver >= 3 { D = 8; srcsize = r64(b,32) }
50 let spslen: i64 = r32(b,32+D)
51 let ppslen: i64 = r32(b, 36+D+spslen); let nkf: i64 = r64(b, 40+D+spslen+ppslen); let kfo: i64 = 48+D+spslen+ppslen
52 pe("ver=" as *u8); pn(ver); pe(" dur_ms=" as *u8); pn(dur); pe(" arate=" as *u8); pn(arate); pe(" achan=" as *u8); pn(achan); pe(" vidpid=" as *u8); pn(vidpid); pe(" audpid=" as *u8); pn(audpid); pe(" srcsize=" as *u8); pn(srcsize); pe(" spslen=" as *u8); pn(spslen); pe(" ppslen=" as *u8); pn(ppslen); pe(" nkf=" as *u8); pn(nkf); pe("\n" as *u8)
53 var i: i64 = 0
54 while i < nkf {
55 var show: i64 = 0
56 if i < 4 { show = 1 } if i == nkf/2 { show = 1 } if i == nkf-1 { show = 1 }
57 if show == 1 { pe("kf[" as *u8); pn(i); pe("] t_ms=" as *u8); pn(r64(b,kfo+i*16)); pe(" byte=" as *u8); pn(r64(b,kfo+i*16+8)); pe(" gap=" as *u8); if i+1<nkf { pn(r64(b,kfo+(i+1)*16+8)-r64(b,kfo+i*16+8)) } else { pn(0) } pe("\n" as *u8) }
58 i = i + 1
59 }
60 return 0
61}
62
63const TS_PROBE_BYTES: i64 = 576 // 3 MPEG-TS packets: enough to require 0x47 sync three times running
64func main(argc: i64, argv: *i64) -> i64 {
65 if argc < 3 { pe("usage: nx_ts_index <in.ts> <out.idx> | nx_ts_index --dump <idx>\n" as *u8); return 2 }
66 let a1d: *u8 = argv[1] as *u8; if a1d[0]==(45 as u8) { return dump_idx(argv[2] as *u8) }
67 let inpath: *u8 = argv[1] as *u8
68 let outpath: *u8 = argv[2] as *u8
69 let fd: i64 = sys_openat_rd(inpath)
70 if fd < 0 { pe("ERROR open\n" as *u8); return 1 }
71 // FAIL FAST on non-MPEG-TS input. MEASURED 2026-07-31: an .mp4 scanned for 47s
72 // and returned rc=0 with a 60-byte header, keyframes=0, and garbage PIDs read
73 // out of MP4 boxes as if they were TS packets -- SUCCESS REPORTED FOR AN INDEX
74 // THAT CAN NEVER SEEK. The catalog holds 23,257 mkv/mp4 rows, so the backfill
75 // would burn ~300-600h producing them. MPEG-TS is 188-byte packets each
76 // starting 0x47; require three in a row before scanning a single byte more.
77 let probe: *u8 = sys_mmap(TS_PROBE_BYTES)
78 let pr: i64 = sys_read(fd, probe, TS_PROBE_BYTES)
79 var tsok: i64 = 0
80 if pr >= TS_PROBE_BYTES {
81 if probe[0] == (0x47 as u8) {
82 if probe[188] == (0x47 as u8) {
83 if probe[376] == (0x47 as u8) { tsok = 1 }
84 }
85 }
86 }
87 if tsok == 0 {
88 pe("REFUSED not-MPEG-TS (no 0x47 sync at 0/188/376) path=" as *u8); pe(inpath); pe("\n" as *u8)
89 sys_close(fd)
90 return 12
91 }
92 let srcsize: i64 = sys_lseek(fd, 0, 2) // v3: remember WHAT WE INDEXED so staleness is detectable later
93 sys_lseek(fd, 0, 0)
94 let t0: i64 = sys_clock_now_us()
95 let chunk: *u8 = sys_mmap(CHUNK + 512)
96 let aubuf: *u8 = sys_mmap(AUCAP + 16); var aulen: i64 = 0; var aupts: i64 = 0; var auactive: i64 = 0; var aupos: i64 = 0
97 var pmtpid: i64 = 0 - 1; var vidpid: i64 = 0 - 1; var audpid: i64 = 0 - 1
98 // 0 = H.264 (stream_type 0x1b), 1 = HEVC/H.265 (stream_type 0x24). Before
99 // 2026-07-31 only 0x1b was accepted, so an HEVC recording never got a video
100 // PID at all -- it could never be indexed, which meant no keyframe seek AND
101 // no NXMK dead-air/motion markers, permanently. A CODEC-BLIND hole, not a
102 // tuning gap.
103 var vcodec: i64 = 0
104 let sps: *u8 = sys_mmap(512); var spslen: i64 = 0
105 let pps: *u8 = sys_mmap(512); var ppslen: i64 = 0
106 var asfreq: i64 = 0 - 1; var achan: i64 = 0 - 1
107 let kft: *i64 = sys_mmap(8 * MAXKF) as *i64 // time_ms
108 let kfb: *i64 = sys_mmap(8 * MAXKF) as *i64 // byte_off
109 var nkf: i64 = 0
110 var totdur: i64 = 0; var prevpts: i64 = 0 - 1
111 var carry: i64 = 0; var cpos: i64 = 0; var go: i64 = 1
112 let aes: *u8 = sys_mmap(K_MAGIC_262144 + 16); var aeslen: i64 = 0
113 let abins: *i64 = sys_mmap(8 * K_MAGIC_65536) as *i64; var maxbin: i64 = 0 // audio bytes / 500ms window (dead-air)
114 let vbins: *i64 = sys_mmap(8 * K_MAGIC_65536) as *i64; var vmaxbin: i64 = 0 // video bytes / 500ms window (motion + scene-cut)
115 while go == 1 {
116 let r: i64 = sys_read(fd, ((chunk as i64)+carry) as *u8, CHUNK - carry)
117 let avail: i64 = carry + r
118 if r <= 0 { go = 0 }
119 var i: i64 = 0
120 while i + 188 <= avail {
121 if chunk[i] != (0x47 as u8) { i = i + 1 } else {
122 let b1: i64 = chunk[i+1] as i64
123 let pusi: i64 = (b1>>6)&1
124 let pid: i64 = ((b1&0x1f)<<8)|(chunk[i+2] as i64)
125 let afc: i64 = ((chunk[i+3] as i64)>>4)&3
126 var payoff: i64 = i + 4
127 if afc==2 { payoff = i+188 } else { if afc==3 { payoff = i+5+(chunk[i+4] as i64) } }
128 if payoff < i+188 {
129 if pid==0 { if pmtpid<0 {
130 var p: i64=payoff; if pusi==1 { p=p+1+(chunk[p] as i64) }
131 let sl: i64=(((chunk[p+1] as i64)&0x0f)<<8)|(chunk[p+2] as i64); var pp: i64=p+8; let pend: i64=p+3+sl-4
132 while pp+4<=pend { let pr: i64=((chunk[pp] as i64)<<8)|(chunk[pp+1] as i64); let pm: i64=(((chunk[pp+2] as i64)&0x1f)<<8)|(chunk[pp+3] as i64); if pr!=0 { if pmtpid<0 { pmtpid=pm } } pp=pp+4 }
133 } }
134 if pid==pmtpid { if pmtpid>=0 { if vidpid<0 {
135 var p: i64=payoff; if pusi==1 { p=p+1+(chunk[p] as i64) }
136 let sl: i64=(((chunk[p+1] as i64)&0x0f)<<8)|(chunk[p+2] as i64); let pil: i64=(((chunk[p+10] as i64)&0x0f)<<8)|(chunk[p+11] as i64); var ep: i64=p+12+pil; let pend: i64=p+3+sl-4
137 while ep+5<=pend { let st: i64=chunk[ep] as i64; let ed: i64=(((chunk[ep+1] as i64)&0x1f)<<8)|(chunk[ep+2] as i64); let el: i64=(((chunk[ep+3] as i64)&0x0f)<<8)|(chunk[ep+4] as i64); let vc: i64 = tsn_codec_of_stream_type(st); if vc>=0 { if vidpid<0 { vidpid=ed; vcodec=vc } } if st==0x0f { if audpid<0 { audpid=ed } } if st==0x11 { if audpid<0 { audpid=ed } } ep=ep+5+el }
138 } } }
139 if pid==vidpid { if vidpid>=0 {
140 let vpb: i64 = (i+188)-payoff; let vms0: i64 = totdur/90; let vbi: i64 = vms0/500
141 if vbi>=0 { if vbi<K_MAGIC_65536 { vbins[vbi]=vbins[vbi]+vpb; if vbi>vmaxbin { vmaxbin=vbi } } }
142 var p: i64 = payoff
143 var newau: i64 = 0
144 var npts: i64 = aupts
145 if pusi==1 { if chunk[p]==(0 as u8) { if chunk[p+1]==(0 as u8) { if chunk[p+2]==(1 as u8) {
146 newau = 1
147 if ((chunk[p+7] as i64)&0x80)!=0 { npts = (((chunk[p+9] as i64)&0x0E)<<29)|((chunk[p+10] as i64)<<22)|(((chunk[p+11] as i64)&0xFE)<<14)|((chunk[p+12] as i64)<<7)|((chunk[p+13] as i64)>>1) }
148 p = p + 9 + (chunk[p+8] as i64)
149 } } } }
150 if newau == 1 {
151 if auactive == 1 {
152 var iskey: i64 = 0
153 var k: i64 = 0
154 // parameter-set NAL types differ by codec: H.264 SPS=7/PPS=8,
155 // HEVC SPS=33/PPS=34. Selecting them by DATA rather than
156 // hardcoding 7/8 keeps HEVC slice NALs from being captured as
157 // if they were parameter sets.
158 let t_sps: i64 = tsn_sps_type(vcodec)
159 let t_pps: i64 = tsn_pps_type(vcodec)
160 while k + 4 < aulen {
161 if aubuf[k]==(0 as u8) { if aubuf[k+1]==(0 as u8) { if aubuf[k+2]==(1 as u8) {
162 // H.264: 1-byte NAL header, type = b & 0x1f, keyframe = IDR (5).
163 // HEVC: 2-byte NAL header, type = (b >> 1) & 0x3f, and the keyframe
164 // class is IRAP = 16..23 (BLA/IDR/CRA), NOT a single value. Reading
165 // HEVC through the H.264 mask takes the wrong bits entirely and
166 // would mark arbitrary frames as keyframes -- a seek index that is
167 // WRONG is worse than one that is absent, because it looks present.
168 let nt: i64 = tsn_nal_type(aubuf[k+3] as i64, vcodec)
169 if tsn_nal_is_key(nt, vcodec)==1 { iskey=1 }
170 if nt==t_sps { if spslen==0 { let ns: i64=k+3; var ne: i64=aulen; var m: i64=ns; var fnd: i64=0; while m+3<aulen { if fnd==0 { if aubuf[m]==(0 as u8) { if aubuf[m+1]==(0 as u8) { if aubuf[m+2]==(1 as u8) { ne=m; fnd=1 } } } if fnd==0 { m=m+1 } } else { m=aulen } } if fnd==1 { if aubuf[ne-1]==(0 as u8) { ne=ne-1 } } let nl: i64=ne-ns; var c: i64=0; while c<nl { if c<512 { sps[c]=aubuf[ns+c] } c=c+1 } spslen=nl } }
171 if nt==t_pps { if ppslen==0 { let ns: i64=k+3; var ne: i64=aulen; var m: i64=ns; var fnd: i64=0; while m+3<aulen { if fnd==0 { if aubuf[m]==(0 as u8) { if aubuf[m+1]==(0 as u8) { if aubuf[m+2]==(1 as u8) { ne=m; fnd=1 } } } if fnd==0 { m=m+1 } } else { m=aulen } } if fnd==1 { if aubuf[ne-1]==(0 as u8) { ne=ne-1 } } let nl: i64=ne-ns; var c: i64=0; while c<nl { if c<512 { pps[c]=aubuf[ns+c] } c=c+1 } ppslen=nl } }
172 // stop scanning once the access unit's VIDEO CODING layer is
173 // reached: H.264 VCL = 1..5, HEVC VCL = 0..31. Using the H.264
174 // range on HEVC would keep walking past the slice into payload
175 // bytes and re-trigger on false start codes.
176 if tsn_nal_is_vcl(nt, vcodec)==1 { k = aulen } else { k = k + 3 }
177 } else { k=k+1 } } else { k=k+1 } } else { k=k+1 }
178 }
179 if prevpts >= 0 { let dd: i64 = aupts - prevpts; if dd>0 { if dd<K_MAGIC_900000 { totdur = totdur + dd } } }
180 prevpts = aupts
181 if iskey==1 { if nkf < MAXKF { kft[nkf]=totdur/90; kfb[nkf]=aupos; nkf=nkf+1 } }
182 }
183 aulen = 0; aupts = npts; auactive = 1; aupos = cpos + i
184 }
185 if auactive == 1 { if aulen < SCANCAP { var q: i64 = p; while q < i+188 { if aulen < SCANCAP { aubuf[aulen]=chunk[q]; aulen=aulen+1 } q=q+1 } } }
186 } }
187 if pid==audpid { if audpid>=0 { let payb: i64 = (i+188) - payoff; let ms0: i64 = totdur/90; let bi: i64 = ms0/500; if bi>=0 { if bi<K_MAGIC_65536 { abins[bi]=abins[bi]+payb; if bi>maxbin { maxbin=bi } } } if asfreq<0 {
188 var ap: i64 = payoff
189 if pusi==1 { if chunk[ap]==(0 as u8) { if chunk[ap+1]==(0 as u8) { if chunk[ap+2]==(1 as u8) { ap = ap + 9 + (chunk[ap+8] as i64) } } } }
190 if chunk[ap]==(0xff as u8) { if ((chunk[ap+1] as i64)&0xf0)==0xf0 { asfreq=((chunk[ap+2] as i64)>>2)&0x0f; achan=(((chunk[ap+2] as i64)&1)<<2)|(((chunk[ap+3] as i64)>>6)&3) } }
191 } } }
192 }
193 i = i + 188
194 }
195 }
196 carry = avail - i
197 var c: i64 = 0; while c < carry { chunk[c]=chunk[i+c]; c=c+1 }
198 cpos = cpos + i
199 go = go
200 if r <= 0 { go = 0 }
201 }
202 sys_close(fd)
203 // arate from sfreq index
204 var arate: i64 = K_MAGIC_48000
205 if asfreq==4 { arate=K_MAGIC_44100 } if asfreq==5 { arate=K_MAGIC_32000 } if asfreq==3 { arate=K_MAGIC_48000 } if asfreq==6 { arate=K_MAGIC_24000 } if asfreq==7 { arate=K_MAGIC_22050 } if asfreq==8 { arate=K_MAGIC_16000 }
206 if achan<0 { achan=2 }
207 // dead-air (silence) clip-analysis markers from the audio-byte bins collected during the scan
208 let mtype: *i64 = sys_mmap(8*K_MAGIC_4096) as *i64
209 let mstart: *i64 = sys_mmap(8*K_MAGIC_4096) as *i64
210 let mend: *i64 = sys_mmap(8*K_MAGIC_4096) as *i64
211 let nm1: i64 = ts_silence_marks(abins, maxbin+1, 500, mtype, mstart, mend, K_MAGIC_4096)
212 let mt2: *i64 = ((mtype as i64)+nm1*8) as *i64; let ms2: *i64 = ((mstart as i64)+nm1*8) as *i64; let me2: *i64 = ((mend as i64)+nm1*8) as *i64
213 let nm2: i64 = ts_motion_marks(vbins, vmaxbin+1, 500, mt2, ms2, me2, K_MAGIC_4096-nm1)
214 let mt3: *i64 = ((mtype as i64)+(nm1+nm2)*8) as *i64; let ms3: *i64 = ((mstart as i64)+(nm1+nm2)*8) as *i64; let me3: *i64 = ((mend as i64)+(nm1+nm2)*8) as *i64
215 let nm3: i64 = ts_scene_marks(vbins, vmaxbin+1, 500, mt3, ms3, me3, K_MAGIC_4096-nm1-nm2)
216 let mt4: *i64 = ((mtype as i64)+(nm1+nm2+nm3)*8) as *i64; let ms4: *i64 = ((mstart as i64)+(nm1+nm2+nm3)*8) as *i64; let me4: *i64 = ((mend as i64)+(nm1+nm2+nm3)*8) as *i64
217 let nm4: i64 = ts_rhythm_marks(vbins, vmaxbin+1, 500, mt4, ms4, me4, K_MAGIC_4096-nm1-nm2-nm3)
218 let mt5: *i64 = ((mtype as i64)+(nm1+nm2+nm3+nm4)*8) as *i64; let ms5: *i64 = ((mstart as i64)+(nm1+nm2+nm3+nm4)*8) as *i64; let me5: *i64 = ((mend as i64)+(nm1+nm2+nm3+nm4)*8) as *i64
219 let nm5: i64 = ts_static_marks(vbins, vmaxbin+1, 500, mt5, ms5, me5, K_MAGIC_4096-nm1-nm2-nm3-nm4)
220 let nmarks: i64 = nm1 + nm2 + nm3 + nm4 + nm5
221 if argc > 3 {
222 let bs: *i64 = sys_mmap(8*64) as *i64
223 tm_bin_stats(abins, maxbin+1, bs)
224 pe("abins n=" as *u8); pn(maxbin+1); pe(" min=" as *u8); pn(bs[0]); pe(" med=" as *u8); pn(bs[1]); pe(" max=" as *u8); pn(bs[2]); pe(" below_sil=" as *u8); pn(bs[3]); pe(" zero=" as *u8); pn(bs[6]); pe("\n" as *u8)
225 tm_bin_stats(vbins, vmaxbin+1, bs)
226 pe("vbins n=" as *u8); pn(vmaxbin+1); pe(" min=" as *u8); pn(bs[0]); pe(" med=" as *u8); pn(bs[1]); pe(" max=" as *u8); pn(bs[2]); pe(" above_mot=" as *u8); pn(bs[4]); pe(" longest_run=" as *u8); pn(bs[5]); pe("\n" as *u8)
227 pe("vpct p10=" as *u8); pn(tm_percentile(vbins, vmaxbin+1, 10)); pe(" p25=" as *u8); pn(tm_percentile(vbins, vmaxbin+1, 25)); pe(" p50=" as *u8); pn(tm_percentile(vbins, vmaxbin+1, 50)); pe(" p90=" as *u8); pn(tm_percentile(vbins, vmaxbin+1, 90)); pe("\n" as *u8)
228 }
229 // write the index
230 let cap: i64 = 64 + spslen + ppslen + nkf*16 + nmarks*24 + 128
231 let ob: *u8 = sys_mmap(cap)
232 var o: i64 = 0
233 ob[0]=78 as u8; ob[1]=88 as u8; ob[2]=86 as u8; ob[3]=73 as u8; o=4 // "NXVI"
234 o = w32(ob, o, 3)
235 let dur_ms: i64 = totdur/90
236 o = w64(ob, o, dur_ms)
237 o = w32(ob, o, arate)
238 o = w32(ob, o, achan)
239 o = w32(ob, o, vidpid)
240 o = w32(ob, o, audpid)
241 o = w64(ob, o, srcsize)
242 o = w32(ob, o, spslen); var s1: i64=0; while s1<spslen { ob[o]=sps[s1]; o=o+1; s1=s1+1 }
243 o = w32(ob, o, ppslen); var s2: i64=0; while s2<ppslen { ob[o]=pps[s2]; o=o+1; s2=s2+1 }
244 o = w64(ob, o, nkf)
245 var ki: i64=0; while ki<nkf { o=w64(ob,o,kft[ki]); o=w64(ob,o,kfb[ki]); ki=ki+1 }
246 o = ts_marks_write(ob, o, mtype, mstart, mend, nmarks) // NXMK dead-air tail (v2 readers ignore it)
247 let wf: i64 = sys_openat_wr(outpath, 0x1a4)
248 if wf < 0 { pe("ERROR write open\n" as *u8); return 1 }
249 sys_write(wf, ob, o); sys_close(wf)
250 pe("indexed(v3) dur_ms=" as *u8); pn(dur_ms); pe(" keyframes=" as *u8); pn(nkf); pe(" vidpid=" as *u8); pn(vidpid); pe(" audpid=" as *u8); pn(audpid); pe(" arate=" as *u8); pn(arate); pe(" achan=" as *u8); pn(achan); pe(" idxbytes=" as *u8); pn(o); pe(" scan_ms=" as *u8); pn((sys_clock_now_us()-t0)/1000); pe(" deadair=" as *u8); pn(nm1); pe(" motion=" as *u8); pn(nm2); pe(" scene=" as *u8); pn(nm3); pe(" rhythm=" as *u8); pn(nm4); pe(" static=" as *u8); pn(nm5); pe("\n" as *u8)
251 return 0
252}