nx_mp4_probe.nx source
↩ module page · 92 lines · 5015 B
1// nx_mp4_probe.nx -- SOVEREIGN MP4/fMP4 box-tree validator (no ffmpeg, no strings|grep). Walks the
2// ISO-BMFF box tree in the header region of a file, printing type/size/offset per box and recursing
3// into containers (moov/trak/mdia/minf/stbl/mvex/moof/traf) + the sample-entry boxes (stsd/avc1/mp4a),
4// flagging any box whose declared size is malformed. Stops at the first mdat (its payload is opaque).
5// Confirms track count + codec boxes (avc1/avcC, mp4a/esds) + first-fragment traf count. Used to verify
6// nx_ts2fmp4 output. Usage: nx_mp4_probe <file.mp4>. license_tier: ORIGINAL
7import "nx_syscalls.nx"
8
9const RDCAP: i64 = 2097152
10
11func mp(s: *u8) -> i64 { var n: i64=0; while s[n]!=(0 as u8){n=n+1} sys_write(1,s,n); return 0 }
12func mn(v: i64) -> i64 {
13 var m: i64=v; var neg: i64=0; if m<0 { neg=1; m=0-m }
14 let b: *u8=sys_mmap(32); var i: i64=32
15 if m==0 { i=i-1; b[i]=(48 as u8) }
16 while m>0 { let q: i64=m/10; i=i-1; b[i]=((48+(m-q*10)) as u8); m=q }
17 if neg==1 { i=i-1; b[i]=(45 as u8) }
18 return sys_write(1, ((b as i64)+i) as *u8, 32-i)
19}
20func mind(d: i64) -> i64 { var k: i64=0; while k<d { mp(" " as *u8); k=k+1 } return 0 }
21func be32(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) }
22func is4(b: *u8, o: i64, s: *u8) -> i64 { if b[o]==s[0] { if b[o+1]==s[1] { if b[o+2]==s[2] { if b[o+3]==s[3] { return 1 } } } } return 0 }
23func ptype(b: *u8, o: i64) -> i64 { sys_write(1, ((b as i64)+o) as *u8, 4); return 0 }
24func iscont(b: *u8, o: i64) -> i64 {
25 if is4(b,o,"moov" as *u8)==1 { return 1 }
26 if is4(b,o,"trak" as *u8)==1 { return 1 }
27 if is4(b,o,"mdia" as *u8)==1 { return 1 }
28 if is4(b,o,"minf" as *u8)==1 { return 1 }
29 if is4(b,o,"stbl" as *u8)==1 { return 1 }
30 if is4(b,o,"mvex" as *u8)==1 { return 1 }
31 if is4(b,o,"moof" as *u8)==1 { return 1 }
32 if is4(b,o,"traf" as *u8)==1 { return 1 }
33 if is4(b,o,"dinf" as *u8)==1 { return 1 }
34 return 0
35}
36
37// st[0]=trak st[1]=avc1 st[2]=mp4a st[3]=esds st[4]=moof st[5]=traf st[6]=bad
38// ISO-BMFF largesize: a 64-bit box length. Needed by every file over 4GB.
39func be64(b: *u8, o: i64) -> i64 {
40 var v: i64 = 0
41 var i: i64 = 0
42 while i < 8 { v = (v << 8) | (b[o+i] as i64); i = i + 1 }
43 return v
44}
45func walk(b: *u8, start: i64, end: i64, depth: i64, st: *i64) -> i64 {
46 var o: i64 = start
47 var stop: i64 = 0
48 while stop==0 {
49 if o + 8 > end { stop=1 } else {
50 var sz: i64 = be32(b, o)
51 var hdr: i64 = 8
52 if sz == 1 { if o + 16 <= end { sz = be64(b, o + 8); hdr = 16 } }
53 if sz == 0 { sz = end - o }
54 if sz < hdr {
55 mind(depth); mp("BAD-size(" as *u8); mn(sz); mp(") " as *u8); ptype(b,o+4); mp("\n" as *u8); st[6]=st[6]+1; stop=1
56 } else {
57 let cend: i64 = o + sz
58 mind(depth); ptype(b, o+4); mp(" size=" as *u8); mn(sz); mp(" @" as *u8); mn(o); mp("\n" as *u8)
59 if is4(b,o+4,"trak" as *u8)==1 { st[0]=st[0]+1 }
60 if is4(b,o+4,"avc1" as *u8)==1 { st[1]=st[1]+1 }
61 if is4(b,o+4,"mp4a" as *u8)==1 { st[2]=st[2]+1 }
62 if is4(b,o+4,"esds" as *u8)==1 { st[3]=st[3]+1 }
63 if is4(b,o+4,"moof" as *u8)==1 { st[4]=st[4]+1 }
64 if is4(b,o+4,"traf" as *u8)==1 { st[5]=st[5]+1 }
65 if is4(b,o+4,"mdat" as *u8)==1 { stop=1 } else {
66 if cend > end { mind(depth+1); mp("(beyond read window)\n" as *u8); stop=1 } else {
67 if iscont(b,o+4)==1 { walk(b, o+hdr, cend, depth+1, st) } else { if is4(b,o+4,"stsd" as *u8)==1 { walk(b, o+16, cend, depth+1, st) } else { if is4(b,o+4,"avc1" as *u8)==1 { walk(b, o+86, cend, depth+1, st) } else { if is4(b,o+4,"mp4a" as *u8)==1 { walk(b, o+36, cend, depth+1, st) } } } }
68 o = cend
69 }
70 }
71 }
72 }
73 }
74 return 0
75}
76
77func main(argc: i64, argv: *i64) -> i64 {
78 if argc < 2 { mp("usage: nx_mp4_probe <file.mp4>\n" as *u8); return 2 }
79 let path: *u8 = argv[1] as *u8
80 let fd: i64 = sys_openat_rd(path)
81 if fd < 0 { mp("ERROR open failed\n" as *u8); return 1 }
82 let b: *u8 = sys_mmap(RDCAP + 16)
83 var got: i64 = 0; var go: i64 = 1
84 while go==1 { if got>=RDCAP { go=0 } else { let r: i64=sys_read(fd, ((b as i64)+got) as *u8, RDCAP-got); if r<=0 { go=0 } else { got=got+r } } }
85 sys_close(fd)
86 let st: *i64 = sys_mmap(64) as *i64; var z: i64=0; while z<8 { st[z]=0; z=z+1 }
87 mp("=== " as *u8); mp(path); mp(" (read " as *u8); mn(got); mp(" bytes) ===\n" as *u8)
88 walk(b, 0, got, 0, st)
89 mp("SUMMARY trak=" as *u8); mn(st[0]); mp(" avc1=" as *u8); mn(st[1]); mp(" mp4a=" as *u8); mn(st[2]); mp(" esds=" as *u8); mn(st[3]); mp(" moof=" as *u8); mn(st[4]); mp(" traf=" as *u8); mn(st[5]); mp(" bad=" as *u8); mn(st[6]); mp("\n" as *u8)
90 if st[6]==0 { mp("STRUCTURE OK\n" as *u8) } else { mp("STRUCTURE BAD\n" as *u8) }
91 return 0
92}