code wiki / (root) / nx_mp4_demux.nx

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}