code wiki / (root) / nx_vp8probe.nx

nx_vp8probe.nx source

↩ module page · 167 lines · 6196 B

1// nx_vp8probe.nx -- DIAGNOSTIC bring-up harness for the C3 VP8 keyframe 2// decoder. Decodes one lossy WebP file and prints machine-readable evidence: 3// dimensions, global Y/U/V means, and an 8x8 grid of Y tile means that an 4// INDEPENDENT oracle (libwebp via PIL, laptop-side) can be compared against 5// number-by-number. Also embeds the prob-128 bool-decoder property check 6// (prob-128 decode IS the raw bitstream) so a desync shows up here first. 7// 8// usage: nx_vp8probe <file.webp> 9// 10// license_tier: ORIGINAL 11 12import "nx_str.nx" 13import "nx_syscalls.nx" 14import "nx_vp8.nx" 15import "nx_vp8_pred.nx" 16import "nx_vp8_kf.nx" 17const K_MAGIC_42375: i64 = 42375 18const K_MAGIC_7602: i64 = 7602 19const K_MAGIC_65536: i64 = 65536 20const K_MAGIC_65537: i64 = 65537 21 22func vpb_puts(s: *u8) -> i64 { sys_write(1, s, nx_str_len(s)); return 0 } 23 24func vpb_pi(v: i64) -> i64 { 25 let t: *u8 = sys_mmap(32) 26 let o: *u8 = sys_mmap(32) 27 var m: i64 = v 28 var k: i64 = 0 29 if m < 0 { m = 0 - m; sys_write(1, "-" as *u8, 1) } 30 if m == 0 { t[0] = 48; k = 1 } 31 while m > 0 { t[k] = 48 + (m % 10); m = m / 10; k = k + 1 } 32 var i: i64 = 0 33 while i < k { o[i] = t[k - 1 - i]; i = i + 1 } 34 sys_write(1, o, k) 35 return 0 36} 37 38func vpb_cc(b: *u8, off: i64, a: i64, c: i64, d: i64, e: i64) -> i64 { 39 if ((b[off] as i64)&255) != a { return 0 } 40 if ((b[off+1] as i64)&255) != c { return 0 } 41 if ((b[off+2] as i64)&255) != d { return 0 } 42 if ((b[off+3] as i64)&255) != e { return 0 } 43 return 1 44} 45 46func vpb_u32(b: *u8, off: i64) -> i64 { 47 return ((b[off] as i64)&255) | (((b[off+1] as i64)&255)<<8) | (((b[off+2] as i64)&255)<<16) | (((b[off+3] as i64)&255)<<24) 48} 49 50func main(argc: i64, argv: *i64) -> i64 { 51 // ---- bool-decoder KAT: prob-128 literals over bytes A5 3C 0F 77. 52 // NOTE the values are NOT the raw bytes: VP8's range starts at 255, so 53 // the coder never degenerates to raw bit extraction (the old header 54 // comment claimed it did -- an impossible expectation). Expected values 55 // computed by an exact independent port of RFC 6386 sec 7.3. 56 let pb: *u8 = sys_mmap(16) 57 pb[0] = 0xA5 as u8 58 pb[1] = 0x3C as u8 59 pb[2] = 0x0F as u8 60 pb[3] = 0x77 as u8 61 let sbd: *NxVp8Bool = nx_vp8_bool_init(pb, 4, 0) 62 var p128: i64 = 0 63 if nx_vp8_bool_literal(sbd, 16) == K_MAGIC_42375 { 64 if nx_vp8_bool_literal(sbd, 16) == K_MAGIC_7602 { p128 = 1 } 65 } 66 // floor-shift helpers on negatives 67 var srok: i64 = 0 68 if vp8p_sr16(0 - K_MAGIC_65536) == (0 - 1) { 69 if vp8p_sr16(0 - K_MAGIC_65537) == (0 - 2) { 70 if vp8p_sr3(0 - 1) == (0 - 1) { if vp8p_sr3(0 - 8) == (0 - 1) { srok = 1 } } 71 } 72 } 73 vpb_puts("{\"selftest_prob128\":" as *u8); vpb_pi(p128) 74 vpb_puts(",\"selftest_floorshift\":" as *u8); vpb_pi(srok) 75 vpb_puts("}\n" as *u8) 76 77 if argc < 2 { vpb_puts("usage: nx_vp8probe <file.webp>\nVP8PROBE-FAIL\n" as *u8); sys_exit(2); return 2 } 78 let path: *u8 = argv[1] as *u8 79 let lenbox: *i64 = sys_mmap(16) as *i64 80 let raw: *u8 = sys_read_file(path, lenbox) 81 if raw == (0 as *u8) { vpb_puts("read-fail\nVP8PROBE-FAIL\n" as *u8); sys_exit(2); return 2 } 82 let n: i64 = lenbox[0] 83 84 // RIFF walk to the 'VP8 ' chunk 85 var vo: i64 = 0 86 var vlen: i64 = 0 87 if n >= 20 { if vpb_cc(raw,0,82,73,70,70) == 1 { if vpb_cc(raw,8,87,69,66,80) == 1 { 88 var pos: i64 = 12 89 var go: i64 = 1 90 while go == 1 { 91 if pos + 8 > n { go = 0 } else { 92 let clen: i64 = vpb_u32(raw, pos+4) 93 if vpb_cc(raw,pos,86,80,56,32) == 1 { 94 vo = pos + 8 95 vlen = clen 96 go = 0 97 } else { 98 var adv: i64 = clen 99 if (adv & 1) == 1 { adv = adv + 1 } 100 pos = pos + 8 + adv 101 } 102 } 103 } 104 } } } 105 if vo == 0 { vpb_puts("no-vp8-chunk\nVP8PROBE-FAIL\n" as *u8); sys_exit(2); return 2 } 106 if vo + vlen > n { vlen = n - vo } 107 108 let outp: *i64 = sys_mmap(64) as *i64 109 let rc: i64 = nx_vp8_kf_decode(raw + vo, vlen, outp) 110 vpb_puts("{\"tool\":\"nx_vp8probe\",\"decode\":" as *u8); vpb_pi(rc) 111 if rc != 1 { vpb_puts("}\nVP8PROBE-FAIL\n" as *u8); sys_exit(1); return 1 } 112 113 let py: *u8 = outp[NX_VP8KF_OUT_Y] as *u8 114 let pu: *u8 = outp[NX_VP8KF_OUT_U] as *u8 115 let pv: *u8 = outp[NX_VP8KF_OUT_V] as *u8 116 let w: i64 = outp[NX_VP8KF_OUT_W] 117 let h: i64 = outp[NX_VP8KF_OUT_H] 118 let ys: i64 = outp[NX_VP8KF_OUT_YS] 119 let us: i64 = outp[NX_VP8KF_OUT_UVS] 120 vpb_puts(",\"w\":" as *u8); vpb_pi(w) 121 vpb_puts(",\"h\":" as *u8); vpb_pi(h) 122 123 // global means 124 var ysum: i64 = 0 125 var yy: i64 = 0 126 while yy < h { var xx: i64 = 0; while xx < w { ysum = ysum + ((py[yy*ys+xx] as i64)&255); xx = xx + 1 } yy = yy + 1 } 127 var usum: i64 = 0 128 var vsum: i64 = 0 129 let cw: i64 = (w+1)>>1 130 let ch: i64 = (h+1)>>1 131 yy = 0 132 while yy < ch { var xx: i64 = 0; while xx < cw { usum = usum + ((pu[yy*us+xx] as i64)&255); vsum = vsum + ((pv[yy*us+xx] as i64)&255); xx = xx + 1 } yy = yy + 1 } 133 vpb_puts(",\"y_mean\":" as *u8); vpb_pi(ysum / (w*h)) 134 vpb_puts(",\"u_mean\":" as *u8); vpb_pi(usum / (cw*ch)) 135 vpb_puts(",\"v_mean\":" as *u8); vpb_pi(vsum / (cw*ch)) 136 137 // 8x8 Y tile means 138 vpb_puts(",\"y_tiles\":[" as *u8) 139 var first: i64 = 1 140 var ty: i64 = 0 141 while ty < 8 { 142 var tx: i64 = 0 143 while tx < 8 { 144 let x0: i64 = tx*w/8 145 let x1: i64 = (tx+1)*w/8 146 let y0: i64 = ty*h/8 147 let y1: i64 = (ty+1)*h/8 148 var s: i64 = 0 149 var cnt: i64 = 0 150 var py2: i64 = y0 151 while py2 < y1 { 152 var px2: i64 = x0 153 while px2 < x1 { s = s + ((py[py2*ys+px2] as i64)&255); cnt = cnt + 1; px2 = px2 + 1 } 154 py2 = py2 + 1 155 } 156 var mv: i64 = 0 157 if cnt > 0 { mv = s / cnt } 158 if first == 1 { first = 0 } else { vpb_puts("," as *u8) } 159 vpb_pi(mv) 160 tx = tx + 1 161 } 162 ty = ty + 1 163 } 164 vpb_puts("]}\nVP8PROBE-OK\n" as *u8) 165 sys_exit(0) 166 return 0 167}