code wiki / (root) / nx_ts_index.nx

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}