nx_mp4_demux.nx source
↩ module page · 88 lines · 3769 B
1// nx_mp4_demux.nx -- MP4/ISO-BMFF container demuxer (first rung of the real-video
2// bridge). Walks the box ('atom') tree -- ftyp / moov / trak / mdia / hdlr --
3// and enumerates the tracks and their kinds (video 'vide' / audio 'soun').
4//
5// This is the DEMUX level: it reads the container structure, not the codec. It
6// answers "does this recording contain an audio track at all?" -- the exact
7// signal that separates an AUDIO-DROPPED-AT-CAPTURE recording (no 'soun' track)
8// from a mux/codec playback problem (track present, won't decode). The codec
9// decode (H.264/AAC -> frames/PCM) is a later rung.
10//
11// ISO/IEC 14496-12 box = [size:u32 BE][type:4 ascii][payload]. size includes
12// the 8-byte header. (64-bit large-size + size==0 to EOF are TODO; flagged.)
13//
14// genealogy_id: iso_bmff_14496_12 + quicktime_atom_tree
15// lineage_id: box_tree_walk + hdlr_track_enumeration
16// license_tier: ORIGINAL
17import "nx_syscalls.nx"
18const K_MAGIC_16777216: i64 = 16777216
19const K_MAGIC_65536: i64 = 65536
20
21// 32-bit big-endian read
22func mp4_be32(b: *u8, o: i64) -> i64 {
23 return (b[o] as i64) * K_MAGIC_16777216 + (b[o + 1] as i64) * K_MAGIC_65536 + (b[o + 2] as i64) * 256 + (b[o + 3] as i64)
24}
25// 4-char type compare at offset o against a 4-char (+nul) string
26func mp4_t4(b: *u8, o: i64, s: *u8) -> i64 {
27 if (b[o] as i64) != (s[0] as i64) { return 0 }
28 if (b[o + 1] as i64) != (s[1] as i64) { return 0 }
29 if (b[o + 2] as i64) != (s[2] as i64) { return 0 }
30 if (b[o + 3] as i64) != (s[3] as i64) { return 0 }
31 return 1
32}
33// find the first child box of type `s` within [start,end); writes payload range
34// to res[0]=payload_start res[1]=payload_end; returns 1 if found else 0.
35// ISO-BMFF largesize (size==1 -> 64-bit length follows) and size==0 (runs to EOF).
36// Without these every mp4 over 4GB reads as corrupt.
37func mp4_be64(b: *u8, o: i64) -> i64 {
38 var v: i64 = 0
39 var i: i64 = 0
40 while i < 8 { v = (v << 8) | (b[o+i] as i64); i = i + 1 }
41 return v
42}
43func mp4_find_box(b: *u8, start: i64, end: i64, s: *u8, res: *i64) -> i64 {
44 var pos: i64 = start
45 while pos + 8 <= end {
46 var sz: i64 = mp4_be32(b, pos)
47 var hdr: i64 = 8
48 if sz == 1 { if pos + 16 <= end { sz = mp4_be64(b, pos + 8); hdr = 16 } }
49 if sz == 0 { sz = end - pos }
50 if sz < hdr { return 0 }
51 if mp4_t4(b, pos + 4, s) == 1 { res[0] = pos + hdr; res[1] = pos + sz; return 1 }
52 pos = pos + sz
53 }
54 return 0
55}
56
57// Demux: out[0]=n_tracks out[1]=n_video out[2]=n_audio out[3]=has_ftyp
58// out[4]=has_moov. Returns 0.
59func nx_mp4_demux(b: *u8, len: i64, out: *i64) -> i64 {
60 out[0] = 0; out[1] = 0; out[2] = 0; out[3] = 0; out[4] = 0
61 let r: *i64 = sys_mmap(32) as *i64
62 if mp4_find_box(b, 0, len, "ftyp\x00" as *u8, r) == 1 { out[3] = 1 }
63 if mp4_find_box(b, 0, len, "moov\x00" as *u8, r) == 0 { return 0 }
64 out[4] = 1
65 let ms: i64 = r[0]
66 let me: i64 = r[1]
67 let r2: *i64 = sys_mmap(32) as *i64
68 let r3: *i64 = sys_mmap(32) as *i64
69 var pos: i64 = ms
70 while pos + 8 <= me {
71 let sz: i64 = mp4_be32(b, pos)
72 if sz < 8 { pos = me }
73 if sz >= 8 {
74 if mp4_t4(b, pos + 4, "trak\x00" as *u8) == 1 {
75 out[0] = out[0] + 1
76 if mp4_find_box(b, pos + 8, pos + sz, "mdia\x00" as *u8, r2) == 1 {
77 if mp4_find_box(b, r2[0], r2[1], "hdlr\x00" as *u8, r3) == 1 {
78 let ho: i64 = r3[0] + 8
79 if mp4_t4(b, ho, "vide\x00" as *u8) == 1 { out[1] = out[1] + 1 }
80 if mp4_t4(b, ho, "soun\x00" as *u8) == 1 { out[2] = out[2] + 1 }
81 }
82 }
83 }
84 pos = pos + sz
85 }
86 }
87 return 0
88}