nx_ts_dur.nx source
↩ module page · 102 lines · 4510 B
1// nx_ts_dur.nx -- MPEG-TS total duration via FIRST + LAST PCR (no full-file scan; reads only an
2// 8 MB window at each end). PCR lives in the adaptation field (afc>=2, PCR_flag bit 0x10), a 33-bit
3// base clock @ 90 kHz. Also reports the PCR endpoints so the daemon can do proportional time<->byte
4// seeking (byte ~= start + (T-first)/(last-first)*size). Usage: nx_ts_dur <in.ts>
5// Prints: size=<n> first_pcr90=<n> last_pcr90=<n> dur_ms=<n>. license_tier: ORIGINAL
6import "nx_syscalls.nx"
7import "nx_vid_dur_lib.nx" // sovereign mp4/mov + mkv/webm container-duration parsers (so EVERY format gets a runtime)
8const K_MAGIC_8589934592: i64 = 8589934592
9
10const WIN: 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 {
14 var m: i64=v; var neg: i64=0; if m<0 { neg=1; m=0-m }
15 let b: *u8=sys_mmap(32); var i: i64=32
16 if m==0 { i=i-1; b[i]=(48 as u8) }
17 while m>0 { let q: i64=m/10; i=i-1; b[i]=((48+(m-q*10)) as u8); m=q }
18 if neg==1 { i=i-1; b[i]=(45 as u8) }
19 return sys_write(1, ((b as i64)+i) as *u8, 32-i)
20}
21
22// scan buf[0..n) for PCRs; set fp[0] to the first seen (if still -1), lp[0] to the last seen. returns count.
23func scan_pcr(buf: *u8, n: i64, fp: *i64, lp: *i64) -> i64 {
24 var i: i64 = 0; var cnt: i64 = 0
25 while i + 188 <= n {
26 if buf[i] != (0x47 as u8) { i = i + 1 } else {
27 let afc: i64 = ((buf[i+3] as i64)>>4)&3
28 if afc>=2 {
29 let afl: i64 = buf[i+4] as i64
30 if afl > 0 {
31 let flags: i64 = buf[i+5] as i64
32 if ((flags>>4)&1)==1 {
33 let base: i64 = ((buf[i+6] as i64)<<25)|((buf[i+7] as i64)<<17)|((buf[i+8] as i64)<<9)|((buf[i+9] as i64)<<1)|((buf[i+10] as i64)>>7)
34 if fp[0] < 0 { fp[0] = base }
35 lp[0] = base
36 cnt = cnt + 1
37 }
38 }
39 }
40 i = i + 188
41 }
42 }
43 return cnt
44}
45
46func read_window(fd: i64, buf: *u8, cap: i64) -> i64 {
47 var got: i64 = 0; var go: i64 = 1
48 while go==1 { if got>=cap { go=0 } else { let r: i64=sys_read(fd, ((buf as i64)+got) as *u8, cap-got); if r<=0 { go=0 } else { got=got+r } } }
49 return got
50}
51
52func main(argc: i64, argv: *i64) -> i64 {
53 if argc < 2 { tp_p("usage: nx_ts_dur <in.ts>\n" as *u8); return 2 }
54 let path: *u8 = argv[1] as *u8
55 let fd: i64 = sys_openat_rd(path)
56 if fd < 0 { tp_p("ERROR open failed\n" as *u8); return 1 }
57 let size: i64 = sys_lseek(fd, 0, 2)
58 let buf: *u8 = sys_mmap(WIN + 16)
59 let fp: *i64 = sys_mmap(16) as *i64; fp[0] = 0 - 1
60 let lp: *i64 = sys_mmap(16) as *i64; lp[0] = 0 - 1
61 // start window -> first PCR
62 sys_lseek(fd, 0, 0)
63 let g1: i64 = read_window(fd, buf, WIN)
64 // sniff the container: mp4/mov (moov->mvhd) or mkv/webm (EBML Duration) -> direct duration, no PCR scan.
65 let sf: i64 = vd_sniff(buf, g1)
66 if sf == 1 {
67 var dms: i64 = vd_mp4_dur(buf, g1)
68 if dms < 0 { var es: i64 = size - WIN; if es < 0 { es = 0 } sys_lseek(fd, es, 0); let ge: i64 = read_window(fd, buf, WIN); dms = vd_mp4_dur(buf, ge) }
69 if dms < 0 { dms = 0 }
70 sys_close(fd)
71 tp_p("size=" as *u8); tp_n(size); tp_p(" first_pcr90=0 last_pcr90=0 dur_ms=" as *u8); tp_n(dms); tp_p("\n" as *u8)
72 return 0
73 }
74 if sf == 2 {
75 var dms2: i64 = vd_mkv_dur(buf, g1); if dms2 < 0 { dms2 = 0 }
76 sys_close(fd)
77 tp_p("size=" as *u8); tp_n(size); tp_p(" first_pcr90=0 last_pcr90=0 dur_ms=" as *u8); tp_n(dms2); tp_p("\n" as *u8)
78 return 0
79 }
80 scan_pcr(buf, g1, fp, lp)
81 let first_pcr: i64 = fp[0]
82 // end window -> last PCR
83 let fp2: *i64 = sys_mmap(16) as *i64; fp2[0] = 0 - 1
84 let lp2: *i64 = sys_mmap(16) as *i64; lp2[0] = 0 - 1
85 var estart: i64 = size - WIN; if estart < 0 { estart = 0 }
86 sys_lseek(fd, estart, 0)
87 let g2: i64 = read_window(fd, buf, WIN)
88 scan_pcr(buf, g2, fp2, lp2)
89 let last_pcr: i64 = lp2[0]
90 sys_close(fd)
91 var dur90: i64 = 0
92 if first_pcr >= 0 { if last_pcr >= 0 {
93 dur90 = last_pcr - first_pcr
94 if dur90 < 0 { dur90 = dur90 + K_MAGIC_8589934592 } // 33-bit PCR wrap (2^33)
95 } }
96 tp_p("size=" as *u8); tp_n(size)
97 tp_p(" first_pcr90=" as *u8); tp_n(first_pcr)
98 tp_p(" last_pcr90=" as *u8); tp_n(last_pcr)
99 tp_p(" dur_ms=" as *u8); tp_n(dur90/90)
100 tp_p("\n" as *u8)
101 return 0
102}