nx_vid_dur_lib.nx source
↩ module page · 105 lines · 5451 B
1// nx_vid_dur_lib.nx -- SOVEREIGN container-duration parsers (no main -> build the _gate). Reads the raw
2// container bytes (no external tool): mp4/mov/m4v via the moov->mvhd movie header (timescale + duration,
3// integer), and mkv/webm via EBML Info->Duration (an IEEE float in TimecodeScale ticks). Returns dur in ms,
4// or -1 if not found in the given window. Composes into nx_ts_dur so EVERY video format gets a runtime.
5// license_tier: ORIGINAL
6import "nx_syscalls.nx"
7const K_MAGIC_86400000: i64 = 86400000
8const K_MAGIC_1000000: i64 = 1000000
9
10// first offset in buf[0..n) where the plen-byte pattern matches, else -1.
11func vd_find(buf: *u8, n: i64, pat: *u8, plen: i64) -> i64 {
12 var i: i64 = 0
13 while i + plen <= n {
14 var j: i64 = 0; var ok: i64 = 1
15 while j < plen { if buf[i + j] != pat[j] { ok = 0; j = plen } else { j = j + 1 } }
16 if ok == 1 { return i }
17 i = i + 1
18 }
19 return 0 - 1
20}
21func vd_u32(buf: *u8, off: i64) -> i64 { return ((buf[off] as i64) << 24) | ((buf[off+1] as i64) << 16) | ((buf[off+2] as i64) << 8) | (buf[off+3] as i64) }
22func vd_u64(buf: *u8, off: i64) -> i64 { var v: i64 = 0; var k: i64 = 0; while k < 8 { v = (v << 8) | (buf[off+k] as i64); k = k + 1 } return v }
23
24// ---- MP4 / MOV / M4V: moov -> mvhd movie header ----
25// mvhd layout after the 4-byte type: version(1) flags(3); v0: [create4][mod4][timescale4][duration4];
26// v1: [create8][mod8][timescale4][duration8]. dur_ms = duration * 1000 / timescale.
27func vd_mp4_dur(buf: *u8, n: i64) -> i64 {
28 let m: i64 = vd_find(buf, n, "mvhd" as *u8, 4)
29 if m < 0 { return 0 - 1 }
30 let d: i64 = m + 4
31 if d + 4 > n { return 0 - 1 }
32 let ver: i64 = buf[d] as i64
33 var tsoff: i64 = d + 12; var duroff: i64 = d + 16; var is64: i64 = 0; var durlen: i64 = 4
34 if ver == 1 { tsoff = d + 20; duroff = d + 24; is64 = 1; durlen = 8 }
35 if duroff + durlen > n { return 0 - 1 }
36 let ts: i64 = vd_u32(buf, tsoff)
37 if ts <= 0 { return 0 - 1 }
38 var dur: i64 = 0
39 if is64 == 1 { dur = vd_u64(buf, duroff) } else { dur = vd_u32(buf, duroff) }
40 if dur < 0 { return 0 - 1 }
41 let r: i64 = dur * 1000 / ts
42 if r > K_MAGIC_86400000 { return 0 - 1 } // >24h = a false "mvhd" match / mis-parse, not a real recording
43 return r
44}
45
46// ---- IEEE 754 big-endian float -> truncated integer (NishiLang is integer-only) ----
47func vd_ieee64(buf: *u8, off: i64) -> i64 {
48 let b0: i64 = buf[off] as i64
49 let exp: i64 = ((b0 & 0x7F) << 4) | ((buf[off+1] as i64) >> 4)
50 if exp == 0 { return 0 }
51 var mant: i64 = ((buf[off+1] as i64) & 0xF) << 48
52 mant = mant | ((buf[off+2] as i64) << 40) | ((buf[off+3] as i64) << 32) | ((buf[off+4] as i64) << 24) | ((buf[off+5] as i64) << 16) | ((buf[off+6] as i64) << 8) | (buf[off+7] as i64)
53 let full: i64 = mant | (1 << 52)
54 let e: i64 = exp - 1023 - 52
55 if e >= 0 { if e >= 12 { return 0 } return full << e }
56 let sh: i64 = 0 - e
57 if sh >= 63 { return 0 }
58 return full >> sh
59}
60func vd_ieee32(buf: *u8, off: i64) -> i64 {
61 let b0: i64 = buf[off] as i64
62 let exp: i64 = ((b0 & 0x7F) << 1) | ((buf[off+1] as i64) >> 7)
63 if exp == 0 { return 0 }
64 var mant: i64 = ((buf[off+1] as i64) & 0x7F) << 16
65 mant = mant | ((buf[off+2] as i64) << 8) | (buf[off+3] as i64)
66 let full: i64 = mant | (1 << 23)
67 let e: i64 = exp - 127 - 23
68 if e >= 0 { if e >= 40 { return 0 } return full << e }
69 let sh: i64 = 0 - e
70 if sh >= 31 { return 0 }
71 return full >> sh
72}
73
74// ---- MKV / WEBM: EBML Info -> Duration (float, ticks) * TimecodeScale (ns/tick, default 1e6) / 1e6 ms ----
75// Duration element = ID 44 89, then a 1-byte size VINT (84=4-byte float / 88=8-byte float), then the float.
76// TimecodeScale = ID 2A D7 B1, then a size VINT, then a big-endian uint (default 1000000 if absent).
77func vd_mkv_dur(buf: *u8, n: i64) -> i64 {
78 var tcs: i64 = K_MAGIC_1000000
79 let tci: *u8 = sys_mmap(8); tci[0] = 0x2A as u8; tci[1] = 0xD7 as u8; tci[2] = 0xB1 as u8
80 let tp: i64 = vd_find(buf, n, tci, 3)
81 if tp >= 0 { let szb: i64 = (buf[tp+3] as i64) & 0x7F; if szb >= 1 { if szb <= 8 { if tp + 4 + szb <= n {
82 var v: i64 = 0; var k: i64 = 0; while k < szb { v = (v << 8) | (buf[tp+4+k] as i64); k = k + 1 }
83 if v > 0 { tcs = v }
84 } } } }
85 // Duration: search for "44 89 84" (4-byte float) or "44 89 88" (8-byte float)
86 let d4: *u8 = sys_mmap(8); d4[0] = 0x44 as u8; d4[1] = 0x89 as u8; d4[2] = 0x84 as u8
87 let d8: *u8 = sys_mmap(8); d8[0] = 0x44 as u8; d8[1] = 0x89 as u8; d8[2] = 0x88 as u8
88 var ticks: i64 = 0 - 1
89 let p8: i64 = vd_find(buf, n, d8, 3)
90 if p8 >= 0 { if p8 + 11 <= n { ticks = vd_ieee64(buf, p8 + 3) } }
91 if ticks < 0 { let p4: i64 = vd_find(buf, n, d4, 3); if p4 >= 0 { if p4 + 7 <= n { ticks = vd_ieee32(buf, p4 + 3) } } }
92 if ticks < 0 { return 0 - 1 }
93 let r: i64 = ticks * tcs / K_MAGIC_1000000
94 if r > K_MAGIC_86400000 { return 0 - 1 } // >24h = a false "44 89 8x"/TimecodeScale match, not a real recording
95 return r
96}
97
98// sniff: 1 = mp4-family (ftyp/moov present), 2 = mkv/webm (EBML magic), 0 = other (TS / unknown).
99func vd_sniff(buf: *u8, n: i64) -> i64 {
100 let eb: *u8 = sys_mmap(8); eb[0] = 0x1A as u8; eb[1] = 0x45 as u8; eb[2] = 0xDF as u8; eb[3] = 0xA3 as u8
101 if vd_find(buf, n, eb, 4) >= 0 { return 2 }
102 if vd_find(buf, n, "ftyp" as *u8, 4) >= 0 { return 1 }
103 if vd_find(buf, n, "moov" as *u8, 4) >= 0 { return 1 }
104 return 0
105}