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}