code wiki / (root) / nx_mediafacts_lib.nx

nx_mediafacts_lib.nx source

↩ module page · 155 lines · 7742 B

1// nx_mediafacts_lib.nx -- MEDIA FACT EXTRACTION CORE (R1 of the render-docs ingest program, 2// knowledge/render_docs_ingest_program.txt). Turns raw media bytes into a CLOSED VOCABULARY of 3// integer measurements -- the substrate rows the refcorpus admission contract was built for. 4// 5// COMPOSES INCUMBENTS, BUILDS NO NEW DECODER (the duplicate-inflate lesson, debt 1785854636: 6// ask the corpus by capability before building): dimensions come from nx_img_dims.nx 7// (PNG/GIF/JPEG/WebP header walk, in-tree since the media importer). The WAV walk below is NOT 8// a copy of nx_wav_header.nx -- that parser's contract requires the WHOLE file in memory (it 9// bounds-checks every chunk BODY, incl. the data payload). Fact extraction reads a BOUNDED 10// HEADER WINDOW of possibly-huge files, so it needs a walk that only touches chunk HEADERS and 11// never requires the body to be present: a different contract, not a duplicate. 12// 13// ★THE PII WALL IS STRUCTURAL: no byte from the input file ever flows to the output. Every fact 14// is a computed integer or a fixed label from the enum below. EXIF/XMP are surfaced as a 15// PRESENCE FLAG only -- the segment is never parsed, so GPS coordinates, serial numbers and 16// embedded thumbnails are structurally unreadable through this lane. 17// 18// Fact slots (flat i64 array, MF_N_SLOTS): 19// [0] format 0=unknown 1=png 2=gif 3=jpeg 4=webp 5=wav 20// [1] width_px [2] height_px [3] exif_present (jpeg only) 21// [4] channels [5] sample_rate_hz [6] bits_per_sample [7] duration_ms (wav only) 22// Unfilled slots stay -1: "not measured" is DISTINCT from 0 -- a fabricated zero is the exact 23// plausible-number failure the soft-tissue lane banked. 24// license_tier: ORIGINAL No hw writes (Rule 26). 25import "nx_syscalls.nx" 26import "nx_img_dims.nx" 27 28const MF_N_SLOTS: i64 = 8 29const MF_FMT_UNKNOWN: i64 = 0 30const MF_FMT_PNG: i64 = 1 31const MF_FMT_GIF: i64 = 2 32const MF_FMT_JPEG: i64 = 3 33const MF_FMT_WEBP: i64 = 4 34const MF_FMT_WAV: i64 = 5 35 36func mf_b(buf: *u8, o: i64) -> i64 { return buf[o] as i64 & 0xff } 37func mf_u16le(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) } 38func mf_u32le(b: *u8, o: i64) -> i64 { return (b[o] as i64 & 0xff) | ((b[o+1] as i64 & 0xff) << 8) | ((b[o+2] as i64 & 0xff) << 16) | ((b[o+3] as i64 & 0xff) << 24) } 39func mf_u16be(b: *u8, o: i64) -> i64 { return ((b[o] as i64 & 0xff) << 8) | (b[o+1] as i64 & 0xff) } 40 41// format id from magic bytes. This is DISPATCH, not a sniffer rebuild: nx_media_sniff.nx answers 42// "which NX_MEDIA_TYPE class" for the vault; facts need the concrete container, and only for the 43// five formats this lane measures. Anything else is UNKNOWN by design (never guessed from name). 44func mf_format(buf: *u8, n: i64) -> i64 { 45 if n < 16 { return MF_FMT_UNKNOWN } 46 if mf_b(buf,0) == 0x89 { if mf_b(buf,1) == 0x50 { if mf_b(buf,2) == 0x4e { if mf_b(buf,3) == 0x47 { return MF_FMT_PNG } } } } 47 if mf_b(buf,0) == 0x47 { if mf_b(buf,1) == 0x49 { if mf_b(buf,2) == 0x46 { if mf_b(buf,3) == 0x38 { return MF_FMT_GIF } } } } 48 if mf_b(buf,0) == 0xff { if mf_b(buf,1) == 0xd8 { return MF_FMT_JPEG } } 49 if mf_b(buf,0) == 0x52 { if mf_b(buf,1) == 0x49 { if mf_b(buf,2) == 0x46 { if mf_b(buf,3) == 0x46 { 50 if mf_b(buf,8) == 0x57 { if mf_b(buf,9) == 0x45 { if mf_b(buf,10) == 0x42 { if mf_b(buf,11) == 0x50 { return MF_FMT_WEBP } } } } 51 if mf_b(buf,8) == 0x57 { if mf_b(buf,9) == 0x41 { if mf_b(buf,10) == 0x56 { if mf_b(buf,11) == 0x45 { return MF_FMT_WAV } } } } 52 } } } } 53 return MF_FMT_UNKNOWN 54} 55 56// JPEG APP1/Exif PRESENCE flag. Walks marker headers only; stops at SOS (entropy data begins). 57// Reads exactly 4 payload bytes to match the label "Exif" -- the segment body is never parsed. 58func mf_jpeg_exif(buf: *u8, n: i64) -> i64 { 59 var p: i64 = 2 60 while p + 4 < n { 61 if mf_b(buf, p) != 0xff { return 0 } 62 var m: i64 = mf_b(buf, p + 1) 63 while m == 0xff { p = p + 1; if p + 4 >= n { return 0 } m = mf_b(buf, p + 1) } 64 if m == 0xda { return 0 } 65 if m == 0xd9 { return 0 } 66 if m >= 0xd0 { if m <= 0xd7 { p = p + 2 } else { 67 if m == 0xe1 { if p + 9 < n { 68 if mf_b(buf,p+4) == 0x45 { if mf_b(buf,p+5) == 0x78 { if mf_b(buf,p+6) == 0x69 { if mf_b(buf,p+7) == 0x66 { return 1 } } } } } } 69 let sl: i64 = mf_u16be(buf, p + 2) 70 if sl < 2 { return 0 } 71 p = p + 2 + sl 72 } } else { 73 if m == 0xe1 { if p + 9 < n { 74 if mf_b(buf,p+4) == 0x45 { if mf_b(buf,p+5) == 0x78 { if mf_b(buf,p+6) == 0x69 { if mf_b(buf,p+7) == 0x66 { return 1 } } } } } } 75 let sl2: i64 = mf_u16be(buf, p + 2) 76 if sl2 < 2 { return 0 } 77 p = p + 2 + sl2 78 } 79 } 80 return 0 81} 82 83// WAV facts from a bounded header window. Chunk-header walk: reads tag+size at each boundary and 84// records fmt fields + the data chunk's DECLARED size; never requires a body byte to be present, 85// so a 500MB recording measures from its first kilobytes. Stops when both found or the window ends. 86func mf_wav(buf: *u8, n: i64, facts: *i64) -> i64 { 87 var off: i64 = 12 88 var have_fmt: i64 = 0 89 var have_data: i64 = 0 90 var data_len: i64 = 0 91 while off + 8 <= n { 92 if have_fmt == 1 { if have_data == 1 { off = n + 8 } } 93 if off + 8 <= n { 94 let size: i64 = mf_u32le(buf, off + 4) 95 if mf_b(buf,off) == 0x66 { if mf_b(buf,off+1) == 0x6d { if mf_b(buf,off+2) == 0x74 { if mf_b(buf,off+3) == 0x20 { 96 if size >= 16 { if off + 24 <= n { 97 facts[4] = mf_u16le(buf, off + 10) 98 facts[5] = mf_u32le(buf, off + 12) 99 facts[6] = mf_u16le(buf, off + 22) 100 let brate: i64 = mf_u32le(buf, off + 16) 101 facts[7] = brate 102 have_fmt = 1 103 } } 104 } } } } 105 if mf_b(buf,off) == 0x64 { if mf_b(buf,off+1) == 0x61 { if mf_b(buf,off+2) == 0x74 { if mf_b(buf,off+3) == 0x61 { 106 data_len = size 107 have_data = 1 108 } } } } 109 var adv: i64 = 8 + size 110 if (adv % 2) == 1 { adv = adv + 1 } 111 if adv <= 0 { off = n + 8 } else { off = off + adv } 112 } 113 } 114 if have_fmt == 0 { return 0 - 1 } 115 // duration only when both the declared data size and a sane byte rate are in hand 116 if have_data == 1 { if facts[7] > 0 { facts[7] = (data_len * 1000) / facts[7] } else { facts[7] = 0 - 1 } } else { facts[7] = 0 - 1 } 117 return 0 118} 119 120// probe a byte window -> fill the fact slots. -1 = not measured, never fabricated. 121func mf_probe(buf: *u8, n: i64, facts: *i64) -> i64 { 122 var i: i64 = 0 123 while i < MF_N_SLOTS { facts[i] = 0 - 1; i = i + 1 } 124 let f: i64 = mf_format(buf, n) 125 facts[0] = f 126 if f == MF_FMT_UNKNOWN { return 0 } 127 if f == MF_FMT_WAV { mf_wav(buf, n, facts); return 0 } 128 // image formats: dims via the incumbent header reader 129 let wh: *i64 = sys_mmap(16) as *i64 130 if nx_img_dims(buf, n, wh) == 1 { 131 if wh[0] > 0 { facts[1] = wh[0] } 132 if wh[1] > 0 { facts[2] = wh[1] } 133 } 134 if f == MF_FMT_JPEG { facts[3] = mf_jpeg_exif(buf, n) } 135 return 0 136} 137 138// median of arr[0..n): insertion sort in place, upper median for even n (declared, deterministic). 139func mf_median(arr: *i64, n: i64) -> i64 { 140 if n <= 0 { return 0 - 1 } 141 var i: i64 = 1 142 while i < n { 143 let v: i64 = arr[i] 144 var j: i64 = i - 1 145 var stop: i64 = 0 146 while stop == 0 { 147 if j < 0 { stop = 1 } else { 148 if arr[j] > v { arr[j+1] = arr[j]; j = j - 1 } else { stop = 1 } 149 } 150 } 151 arr[j+1] = v 152 i = i + 1 153 } 154 return arr[n / 2] 155}