nx_h264_decode_frame.nx source
↩ module page · 927 lines · 42831 B
1// nx_h264_decode_frame.nx -- FULL-FRAME H.264 decoder (Constrained Baseline, CAVLC,
2// 4:2:0, single I-slice). STAGE 5a: walk EVERY macroblock of frame 0 of realtest.mp4,
3// fully entropy-decoding it (consuming bits exactly) while tracking per-4x4-block
4// TotalCoeff (for nC neighbour context), running QP, and intra4x4 modes. Proves
5// frame-wide entropy sync: all mbW*mbH MBs parse with valid mb_type and the reader
6// lands at the rbsp end. Reconstruction + pixel compare come in the next stages.
7// license_tier: ORIGINAL
8import "nx_syscalls.nx"
9import "nx_mp4_demux.nx"
10import "nx_mp4_stbl.nx"
11import "nx_h264_bits.nx"
12import "nx_h264_sps.nx"
13import "nx_h264_pps.nx"
14import "nx_h264_nal.nx"
15import "nx_h264_slice.nx"
16import "nx_h264_mb.nx"
17import "nx_h264_mb_pred.nx"
18import "nx_h264_coeff_token.nx"
19import "nx_h264_cavlc_level.nx"
20import "nx_h264_residual.nx"
21import "nx_h264_nc.nx"
22import "nx_h264_zigzag.nx"
23import "nx_h264_hadamard.nx"
24import "nx_h264_dequant.nx"
25import "nx_h264_idct.nx"
26import "nx_h264_intra16.nx"
27import "nx_h264_intra.nx"
28import "nx_h264_deblock.nx"
29import "nx_h264_chroma_pred.nx"
30import "nx_h264_chroma.nx"
31
32func gp(s: *u8) -> i64 { var n: i64 = 0; while s[n] != (0 as u8) { n = n + 1 } sys_write(1, s, n); return 0 }
33func gn(v: i64) -> i64 {
34 let bb: *u8 = sys_mmap(28); var m: i64 = v
35 if m < 0 { sys_write(1, "-\x00" as *u8, 1); m = 0 - m }
36 let t: *u8 = sys_mmap(28); var k: i64 = 0
37 if m == 0 { t[0] = 48 as u8; k = 1 }
38 while m > 0 { t[k] = (48 + (m % 10)) as u8; m = m / 10; k = k + 1 }
39 var i: i64 = 0
40 while i < k { bb[i] = t[k - 1 - i]; i = i + 1 }
41 sys_write(1, bb, k)
42 return 0
43}
44func find_tag(b: *u8, start: i64, end: i64, s: *u8) -> i64 {
45 var i: i64 = start
46 while i + 4 <= end { if mp4_t4(b, i, s) == 1 { return i - 4 } i = i + 1 }
47 return 0 - 1
48}
49
50// decode one luma 4x4 residual block: look up nC from neighbour TotalCoeff, decode,
51// store this block's TotalCoeff. bx,by = absolute 4x4 block coords. returns TotalCoeff.
52func dec_luma(br: *BitReader, lumaTC: *i64, lbW: i64, bx: i64, by: i64, maxc: i64, coeff: *i64) -> i64 {
53 var nA: i64 = 0
54 var avA: i64 = 0
55 if bx > 0 { nA = lumaTC[by * lbW + (bx - 1)]; avA = 1 }
56 var nB: i64 = 0
57 var avB: i64 = 0
58 if by > 0 { nB = lumaTC[(by - 1) * lbW + bx]; avB = 1 }
59 let nC: i64 = nx_h264_nc_luma(nA, avA, nB, avB)
60 let tc: i64 = nx_h264_residual_decode(br, maxc, nC, coeff)
61 lumaTC[by * lbW + bx] = tc
62 return tc
63}
64
65// decode one chroma 4x4 AC block (component map cTC). cbx,cby absolute chroma 4x4 coords.
66func dec_chroma_ac(br: *BitReader, cTC: *i64, cbW: i64, cbx: i64, cby: i64, coeff: *i64) -> i64 {
67 var nA: i64 = 0
68 var avA: i64 = 0
69 if cbx > 0 { nA = cTC[cby * cbW + (cbx - 1)]; avA = 1 }
70 var nB: i64 = 0
71 var avB: i64 = 0
72 if cby > 0 { nB = cTC[(cby - 1) * cbW + cbx]; avB = 1 }
73 let nC: i64 = nx_h264_nc_luma(nA, avA, nB, avB)
74 let tc: i64 = nx_h264_residual_decode(br, 15, nC, coeff)
75 cTC[cby * cbW + cbx] = tc
76 return tc
77}
78
79// I_16x16 luma DC: scan dc block -> raster -> 4x4 Hadamard -> QP scale -> dcY[16] raster
80func i16_dc_scale(dcblk: *i64, qp: i64, dcY: *i64) -> i64 {
81 let dcras: *i64 = sys_mmap(16*8) as *i64
82 nx_h264_inv_zigzag4x4(dcblk, dcras)
83 nx_h264_hadamard4x4(dcras)
84 let na: *i64 = sys_mmap(8*8) as *i64
85 na[0]=10; na[1]=11; na[2]=13; na[3]=14; na[4]=16; na[5]=18
86 let ls: i64 = 16 * na[qp % 6]
87 let sh6: i64 = qp / 6
88 var qi: i64 = 0
89 while qi < 16 {
90 var v: i64 = 0
91 if qp >= 36 { v = (dcras[qi] * ls) << (sh6 - 6) }
92 if qp < 36 { v = asr(dcras[qi] * ls + (1 << (5 - sh6)), 6 - sh6) }
93 dcY[qi] = v
94 qi = qi + 1
95 }
96 return 0
97}
98
99// reconstruct a full I_16x16 luma MB at (px,py) into yf. acstore[r*16+0..15] holds the
100// AC scan block (pos0=0) per raster 4x4 block r; dcY[r] the scaled DC. predMode 0..3.
101func recon_i16_luma(yf: *u8, W: i64, px: i64, py: i64, predMode: i64, dcY: *i64, acstore: *i64, qp: i64) -> i64 {
102 let availT: i64 = 0
103 var aT: i64 = 0
104 if py > 0 { aT = 1 }
105 var aL: i64 = 0
106 if px > 0 { aL = 1 }
107 let top: *i64 = sys_mmap(16*8) as *i64
108 let left: *i64 = sys_mmap(16*8) as *i64
109 var k: i64 = 0
110 while k < 16 {
111 var tv: i64 = 0
112 if aT == 1 { tv = yf[(py-1)*W + px + k] as i64 }
113 top[k] = tv
114 var lv: i64 = 0
115 if aL == 1 { lv = yf[(py+k)*W + px - 1] as i64 }
116 left[k] = lv
117 k = k + 1
118 }
119 var tl: i64 = 0
120 if aT == 1 { if aL == 1 { tl = yf[(py-1)*W + px - 1] as i64 } }
121 let pred: *i64 = sys_mmap(256*8) as *i64
122 nx_intra16x16_pred_full(predMode, top, left, tl, aT, aL, pred)
123 let scan: *i64 = sys_mmap(16*8) as *i64
124 let dq: *i64 = sys_mmap(16*8) as *i64
125 var br: i64 = 0
126 while br < 4 {
127 var bc: i64 = 0
128 while bc < 4 {
129 let r: i64 = br * 4 + bc
130 var z: i64 = 0
131 while z < 16 { scan[z] = acstore[r*16 + z]; z = z + 1 }
132 let rast: *i64 = sys_mmap(16*8) as *i64
133 nx_h264_inv_zigzag4x4(scan, rast)
134 nx_h264_dequant4x4(rast, qp, dq)
135 dq[0] = dcY[r]
136 nx_idct4x4(dq)
137 var py4: i64 = 0
138 while py4 < 4 {
139 var px4: i64 = 0
140 while px4 < 4 {
141 var val: i64 = pred[(br*4 + py4)*16 + (bc*4 + px4)] + dq[py4*4 + px4]
142 if val < 0 { val = 0 }
143 if val > 255 { val = 255 }
144 yf[(py + br*4 + py4)*W + (px + bc*4 + px4)] = val as u8
145 px4 = px4 + 1
146 }
147 py4 = py4 + 1
148 }
149 bc = bc + 1
150 }
151 br = br + 1
152 }
153 return 0
154}
155
156// reconstruct ONE I_4x4 luma block at pixel (bpx,bpy). coeffscan = 16 scan-order levels.
157// availability flags pre-computed by caller from the decoded-block bitmap.
158func recon_i4_block(yf: *u8, W: i64, bpx: i64, bpy: i64, mode: i64, aT: i64, aL: i64, aTL: i64, aTR: i64, coeffscan: *i64, qp: i64) -> i64 {
159 let top: *i64 = sys_mmap(8*8) as *i64
160 let left: *i64 = sys_mmap(4*8) as *i64
161 var k: i64 = 0
162 while k < 4 {
163 var tv: i64 = 0
164 if aT == 1 { tv = yf[(bpy-1)*W + bpx + k] as i64 }
165 top[k] = tv
166 var lv: i64 = 0
167 if aL == 1 { lv = yf[(bpy+k)*W + bpx - 1] as i64 }
168 left[k] = lv
169 k = k + 1
170 }
171 // top-right (top[4..7]): from above-right block if available, else replicate top[3]
172 k = 0
173 while k < 4 {
174 var tv: i64 = top[3]
175 if aTR == 1 { tv = yf[(bpy-1)*W + bpx + 4 + k] as i64 }
176 if aT == 0 { tv = 0 }
177 top[4 + k] = tv
178 k = k + 1
179 }
180 var tl: i64 = 0
181 if aTL == 1 { tl = yf[(bpy-1)*W + bpx - 1] as i64 }
182 let pred: *i64 = sys_mmap(16*8) as *i64
183 nx_intra4x4_pred_full(mode, top, left, tl, aT, aL, pred)
184 let rast: *i64 = sys_mmap(16*8) as *i64
185 nx_h264_inv_zigzag4x4(coeffscan, rast)
186 let dq: *i64 = sys_mmap(16*8) as *i64
187 nx_h264_dequant4x4(rast, qp, dq)
188 nx_idct4x4(dq)
189 var yy: i64 = 0
190 while yy < 4 {
191 var xx: i64 = 0
192 while xx < 4 {
193 var val: i64 = pred[yy*4 + xx] + dq[yy*4 + xx]
194 if val < 0 { val = 0 }
195 if val > 255 { val = 255 }
196 yf[(bpy + yy)*W + (bpx + xx)] = val as u8
197 xx = xx + 1
198 }
199 yy = yy + 1
200 }
201 return 0
202}
203
204// reconstruct one 8x8 chroma component (4:2:0) into cf at chroma-plane pixel (cpx,cpy).
205// cdc[4] = decoded 2x2 DC levels (raster c00,c01,c10,c11); acstore[blk*16+0..15] = AC scan per 4x4
206// block (blk=by*2+bx, pos0 unused -> 0); predMode 0 DC/1 H/2 V/3 plane; qpc = chroma QP. Mirrors
207// recon_i16_luma: predict -> per-block (AC dequant, DC := scaled chroma DC) -> idct -> +pred -> clip.
208func recon_chroma_comp(cf: *u8, cW: i64, cpx: i64, cpy: i64, predMode: i64, cdc: *i64, acstore: *i64, qpc: i64, availT: i64, availL: i64) -> i64 {
209 let top: *i64 = sys_mmap(8*8) as *i64
210 let left: *i64 = sys_mmap(8*8) as *i64
211 var k: i64 = 0
212 while k < 8 {
213 var tv: i64 = 0
214 if availT == 1 { tv = cf[(cpy-1)*cW + cpx + k] as i64 }
215 top[k] = tv
216 var lv: i64 = 0
217 if availL == 1 { lv = cf[(cpy+k)*cW + cpx - 1] as i64 }
218 left[k] = lv
219 k = k + 1
220 }
221 var tl: i64 = 0
222 if availT == 1 { if availL == 1 { tl = cf[(cpy-1)*cW + cpx - 1] as i64 } }
223 let pred: *i64 = sys_mmap(64*8) as *i64
224 nx_intra_chroma_pred(predMode, top, left, tl, availT, availL, pred)
225 let cc: *i64 = sys_mmap(8*8) as *i64
226 var di: i64 = 0
227 while di < 4 { cc[di] = cdc[di]; di = di + 1 }
228 let dcC: *i64 = sys_mmap(8*8) as *i64
229 nx_h264_chroma_dc_scale(cc, qpc, dcC)
230 let scan: *i64 = sys_mmap(16*8) as *i64
231 let rast: *i64 = sys_mmap(16*8) as *i64
232 let dq: *i64 = sys_mmap(16*8) as *i64
233 var by: i64 = 0
234 while by < 2 {
235 var bx: i64 = 0
236 while bx < 2 {
237 let blk: i64 = by*2 + bx
238 var z: i64 = 0
239 while z < 16 { scan[z] = acstore[blk*16 + z]; z = z + 1 }
240 nx_h264_inv_zigzag4x4(scan, rast)
241 nx_h264_dequant4x4(rast, qpc, dq)
242 dq[0] = dcC[blk]
243 nx_idct4x4(dq)
244 var yy: i64 = 0
245 while yy < 4 {
246 var xx: i64 = 0
247 while xx < 4 {
248 var val: i64 = pred[(by*4+yy)*8 + (bx*4+xx)] + dq[yy*4 + xx]
249 if val < 0 { val = 0 }
250 if val > 255 { val = 255 }
251 cf[(cpy + by*4 + yy)*cW + (cpx + bx*4 + xx)] = val as u8
252 xx = xx + 1
253 }
254 yy = yy + 1
255 }
256 bx = bx + 1
257 }
258 by = by + 1
259 }
260 return 0
261}
262
263// chroma QPc from luma QP + chroma_qp_index_offset (clip to [0,51], 8-bit).
264func chroma_qpc(qpL: i64, cqo: i64) -> i64 {
265 var q: i64 = qpL + cqo
266 if q < 0 { q = 0 }
267 if q > 51 { q = 51 }
268 return nx_h264_chroma_qp(q)
269}
270
271func main(argc: i64, argv: *i64) -> i64 {
272 let szp: *i64 = sys_mmap(16) as *i64
273 // PARAMETERISED 2026-08-01: this whole mp4 -> pixels chain already existed
274 // (avcC -> SPS/PPS -> nx_mp4_video_info -> per-macroblock recon) but was
275 // hardcoded to a test fixture, so the capability was unreachable. The
276 // container share is 386 of the sweep refusals vs 121 for CABAC.
277 var inpath: *u8 = "knowledge/staging/media/realtest.mp4\x00" as *u8
278 if argc >= 2 { inpath = argv[1] as *u8 }
279 let b: *u8 = sys_read_file(inpath, szp)
280 if (b as i64) == 0 { gp("decode_frame: cannot read input mp4\n\x00" as *u8); return 1 }
281 let len: i64 = szp[0]
282
283 // SPS + PPS
284 let avcc: i64 = find_tag(b, 0, len, "avcC\x00" as *u8)
285 let pay: i64 = avcc + 8
286 let spsLen: i64 = (b[pay + 6] as i64) * 256 + (b[pay + 7] as i64)
287 let spsdst: *u8 = sys_mmap(spsLen + 16)
288 let spsrbsp: i64 = nx_h264_unescape_rbsp((b as i64 + pay + 9) as *u8, spsLen - 1, spsdst)
289 let sps: *i64 = sys_mmap(128) as *i64
290 nx_h264_parse_sps(spsdst, spsrbsp, sps)
291 let ppspos: i64 = pay + 8 + spsLen
292 let ppsLen: i64 = (b[ppspos + 1] as i64) * 256 + (b[ppspos + 2] as i64)
293 let ppsdst: *u8 = sys_mmap(ppsLen + 16)
294 let ppsrbsp: i64 = nx_h264_unescape_rbsp((b as i64 + ppspos + 3 + 1) as *u8, ppsLen - 1, ppsdst)
295 let pps: *i64 = sys_mmap(128) as *i64
296 nx_h264_parse_pps(ppsdst, ppsrbsp, pps)
297 let W: i64 = sps[2]
298 let H: i64 = sps[3]
299 let mbW: i64 = W / 16
300 let mbH: i64 = H / 16
301 gp(" res=\x00" as *u8); gn(W); gp("x\x00" as *u8); gn(H); gp(" mbW=\x00" as *u8); gn(mbW); gp(" mbH=\x00" as *u8); gn(mbH); gp(" totalMBs=\x00" as *u8); gn(mbW * mbH); gp("\n\x00" as *u8)
302
303 // sample 0 + slice NAL
304 let vi: *i64 = sys_mmap(64) as *i64
305 nx_mp4_video_info(b, len, vi)
306 let stbl0: i64 = vi[5]
307 let stbl1: i64 = vi[6]
308 let rco: *i64 = sys_mmap(16) as *i64
309 let rsz: *i64 = sys_mmap(16) as *i64
310 mp4_find_box(b, stbl0, stbl1, "stco\x00" as *u8, rco)
311 mp4_find_box(b, stbl0, stbl1, "stsz\x00" as *u8, rsz)
312 let chunk0: i64 = mp4_be32(b, rco[0] + 8)
313 let samp_sz_field: i64 = mp4_be32(b, rsz[0] + 4)
314 var samp0: i64 = samp_sz_field
315 if samp_sz_field == 0 { samp0 = mp4_be32(b, rsz[0] + 12) }
316 let offs: *i64 = sys_mmap(256 * 8) as *i64
317 let types: *i64 = sys_mmap(256 * 8) as *i64
318 let nc: i64 = nx_nal_split_avcc(b, chunk0, chunk0 + samp0, 4, offs, types, 256)
319 var sidx: i64 = 0 - 1
320 var i: i64 = 0
321 while i < nc {
322 if types[i] == 5 { if sidx < 0 { sidx = i } }
323 if types[i] == 1 { if sidx < 0 { sidx = i } }
324 i = i + 1
325 }
326 let noff: i64 = offs[sidx]
327 let ntype: i64 = b[noff] as i64
328 let nlen: i64 = mp4_be32(b, noff - 4)
329 let sl_dst: *u8 = sys_mmap(nlen + 64)
330 let sl_rbsp: i64 = nx_h264_unescape_rbsp((b as i64 + noff + 1) as *u8, nlen - 1, sl_dst)
331 let sh: *i64 = sys_mmap(64) as *i64
332 let sbr: *BitReader = sys_mmap(NX_BR_BYTES) as *BitReader
333 br_init(sbr, sl_dst, sl_rbsp)
334 nx_h264_parse_slice_header_br(sbr, ntype & 31, (ntype >> 5) & 3, sps[9], sps[11], sps[10], sps[6], pps[3], pps[6], sh)
335 gp(" slice_type=\x00" as *u8); gn(sh[8]); gp(" sliceQP=\x00" as *u8); gn(sh[7]); gp(" rbsp_bytes=\x00" as *u8); gn(sl_rbsp)
336 gp(" | deblock_idc=\x00" as *u8); gn(sh[9]); gp(" offA=\x00" as *u8); gn(sh[10]); gp(" offB=\x00" as *u8); gn(sh[11]); gp("\n\x00" as *u8)
337
338 // per-4x4 TotalCoeff + intra4x4 mode maps
339 let lbW: i64 = mbW * 4
340 let lbH: i64 = mbH * 4
341 let lumaTC: *i64 = sys_mmap(lbW * lbH * 8) as *i64
342 let i4m: *i64 = sys_mmap(lbW * lbH * 8) as *i64
343 let cbW: i64 = mbW * 2
344 let cbH: i64 = mbH * 2
345 let uTC: *i64 = sys_mmap(cbW * cbH * 8) as *i64
346 let vTC: *i64 = sys_mmap(cbW * cbH * 8) as *i64
347 let decoded: *i64 = sys_mmap(lbW * lbH * 8) as *i64
348 let yf: *u8 = sys_mmap(W * H + 64)
349 let cpW: i64 = W / 2
350 let cpH: i64 = H / 2
351 let uf: *u8 = sys_mmap(cpW * cpH + 64)
352 let vf: *u8 = sys_mmap(cpW * cpH + 64)
353 let ucstore: *i64 = sys_mmap(4 * 16 * 8) as *i64
354 let vcstore: *i64 = sys_mmap(4 * 16 * 8) as *i64
355 var z: i64 = 0
356 while z < lbW * lbH { lumaTC[z] = 0; i4m[z] = 2; decoded[z] = 0; z = z + 1 }
357 z = 0
358 while z < cbW * cbH { uTC[z] = 0; vTC[z] = 0; z = z + 1 }
359 z = 0
360 while z < W * H { yf[z] = 0 as u8; z = z + 1 }
361 let acstore: *i64 = sys_mmap(16 * 16 * 8) as *i64
362 let mbQP: *i64 = sys_mmap(mbW * mbH * 8) as *i64
363 let mbCls: *i64 = sys_mmap(mbW * mbH * 8) as *i64
364
365 // 4x4 luma block scan order -> (blkX,blkY)
366 let bx4: *i64 = sys_mmap(16 * 8) as *i64
367 let by4: *i64 = sys_mmap(16 * 8) as *i64
368 bx4[0]=0; by4[0]=0; bx4[1]=1; by4[1]=0; bx4[2]=0; by4[2]=1; bx4[3]=1; by4[3]=1
369 bx4[4]=2; by4[4]=0; bx4[5]=3; by4[5]=0; bx4[6]=2; by4[6]=1; bx4[7]=3; by4[7]=1
370 bx4[8]=0; by4[8]=2; bx4[9]=1; by4[9]=2; bx4[10]=0; by4[10]=3; bx4[11]=1; by4[11]=3
371 bx4[12]=2; by4[12]=2; bx4[13]=3; by4[13]=2; bx4[14]=2; by4[14]=3; bx4[15]=3; by4[15]=3
372
373 let coeff: *i64 = sys_mmap(32 * 8) as *i64
374 var qp: i64 = sh[7]
375 var coeffsum: i64 = 0
376 var nzblocks: i64 = 0
377 var i16count: i64 = 0
378 var i4count: i64 = 0
379 var pcmcount: i64 = 0
380 var ok: i64 = 1
381 var mbAddr: i64 = 0
382 let totalMB: i64 = mbW * mbH
383 while mbAddr < totalMB {
384 let mbX: i64 = mbAddr % mbW
385 let mbY: i64 = mbAddr / mbW
386 let mb_type: i64 = nx_h264_mb_parse_itype(sbr)
387 if mb_type < 0 { ok = 0; gp(" DESYNC mb_type<0 at mbAddr=\x00" as *u8); gn(mbAddr); gp("\n\x00" as *u8); mbAddr = totalMB; }
388 if ok == 1 {
389 if mb_type > 25 { ok = 0; gp(" DESYNC mb_type>25 (\x00" as *u8); gn(mb_type); gp(") at mbAddr=\x00" as *u8); gn(mbAddr); gp("\n\x00" as *u8); mbAddr = totalMB; }
390 }
391 if ok == 1 {
392 let cls: i64 = nx_h264_mb_type_class(mb_type)
393
394 if cls == 2 {
395 // I_PCM: byte-align then raw samples; neighbours count as 16 for nC
396 pcmcount = pcmcount + 1
397 br_align(sbr)
398 var pp: i64 = 0
399 while pp < 256 + 64 + 64 { let dummy: i64 = br_read_bits(sbr, 8); pp = pp + 1 }
400 var bl: i64 = 0
401 while bl < 16 { lumaTC[(mbY*4 + by4[bl]) * lbW + (mbX*4 + bx4[bl])] = 16; bl = bl + 1 }
402 var cc: i64 = 0
403 while cc < 4 {
404 uTC[(mbY*2 + cc/2) * cbW + (mbX*2 + cc%2)] = 16
405 vTC[(mbY*2 + cc/2) * cbW + (mbX*2 + cc%2)] = 16
406 cc = cc + 1
407 }
408 }
409
410 if cls == 1 {
411 // I_16x16
412 i16count = i16count + 1
413 let der: *i64 = sys_mmap(32) as *i64
414 nx_h264_i16x16_derive(mb_type, der)
415 let cbpL: i64 = der[2]
416 let cbpC: i64 = der[1]
417 let chroma_pred: i64 = br_read_ue(sbr)
418 let mqd: i64 = br_read_se(sbr)
419 qp = (qp + mqd + 52) % 52
420 // luma DC (16 coeffs) at block-0 neighbour context
421 let bx0: i64 = mbX * 4
422 let by0: i64 = mbY * 4
423 var nA: i64 = 0
424 var avA: i64 = 0
425 if bx0 > 0 { nA = lumaTC[by0 * lbW + (bx0 - 1)]; avA = 1 }
426 var nB: i64 = 0
427 var avB: i64 = 0
428 if by0 > 0 { nB = lumaTC[(by0 - 1) * lbW + bx0]; avB = 1 }
429 let nCdc: i64 = nx_h264_nc_luma(nA, avA, nB, avB)
430 let dcblk: *i64 = sys_mmap(16 * 8) as *i64
431 let dctc: i64 = nx_h264_residual_decode(sbr, 16, nCdc, dcblk)
432 z = 0
433 while z < 16 { coeffsum = coeffsum + dcblk[z] * (z + 1); z = z + 1 }
434 z = 0
435 while z < 256 { acstore[z] = 0; z = z + 1 }
436 var bl: i64 = 0
437 while bl < 16 {
438 let abx: i64 = bx0 + bx4[bl]
439 let aby: i64 = by0 + by4[bl]
440 let r: i64 = by4[bl] * 4 + bx4[bl]
441 i4m[aby * lbW + abx] = 2
442 if cbpL != 0 {
443 let tc: i64 = dec_luma(sbr, lumaTC, lbW, abx, aby, 15, coeff)
444 if tc > 0 { nzblocks = nzblocks + 1 }
445 z = 0
446 while z < 15 { coeffsum = coeffsum + coeff[z] * (z + 1); acstore[r*16 + 1 + z] = coeff[z]; z = z + 1 }
447 }
448 if cbpL == 0 { lumaTC[aby * lbW + abx] = 0 }
449 decoded[aby * lbW + abx] = 1
450 bl = bl + 1
451 }
452 // RECONSTRUCT I_16x16 luma into the frame buffer
453 let dcY: *i64 = sys_mmap(16 * 8) as *i64
454 i16_dc_scale(dcblk, qp, dcY)
455 recon_i16_luma(yf, W, mbX * 16, mbY * 16, der[0], dcY, acstore, qp)
456 // chroma DC (U,V) + AC, then RECONSTRUCT into uf/vf
457 let ucdc: *i64 = sys_mmap(8 * 8) as *i64
458 let vcdc: *i64 = sys_mmap(8 * 8) as *i64
459 z = 0
460 while z < 4 { ucdc[z] = 0; vcdc[z] = 0; z = z + 1 }
461 z = 0
462 while z < 64 { ucstore[z] = 0; vcstore[z] = 0; z = z + 1 }
463 if cbpC != 0 {
464 nx_h264_residual_decode_cdc(sbr, ucdc)
465 nx_h264_residual_decode_cdc(sbr, vcdc)
466 z = 0
467 while z < 4 { coeffsum = coeffsum + (ucdc[z] + vcdc[z]) * (z + 1); z = z + 1 }
468 if cbpC == 2 {
469 var cc: i64 = 0
470 while cc < 4 {
471 dec_chroma_ac(sbr, uTC, cbW, mbX*2 + (cc%2), mbY*2 + (cc/2), coeff)
472 z = 0
473 while z < 15 { ucstore[cc*16 + 1 + z] = coeff[z]; z = z + 1 }
474 cc = cc + 1
475 }
476 cc = 0
477 while cc < 4 {
478 dec_chroma_ac(sbr, vTC, cbW, mbX*2 + (cc%2), mbY*2 + (cc/2), coeff)
479 z = 0
480 while z < 15 { vcstore[cc*16 + 1 + z] = coeff[z]; z = z + 1 }
481 cc = cc + 1
482 }
483 }
484 }
485 if cbpC == 0 {
486 var cc: i64 = 0
487 while cc < 4 { uTC[(mbY*2 + cc/2) * cbW + (mbX*2 + cc%2)] = 0; vTC[(mbY*2 + cc/2) * cbW + (mbX*2 + cc%2)] = 0; cc = cc + 1 }
488 }
489 var qpi16: i64 = qp + pps[9]
490 if qpi16 < 0 { qpi16 = 0 }
491 if qpi16 > 51 { qpi16 = 51 }
492 let qpc16: i64 = nx_h264_chroma_qp(qpi16)
493 var caT16: i64 = 0
494 if mbY > 0 { caT16 = 1 }
495 var caL16: i64 = 0
496 if mbX > 0 { caL16 = 1 }
497 recon_chroma_comp(uf, cpW, mbX*8, mbY*8, chroma_pred, ucdc, ucstore, qpc16, caT16, caL16)
498 recon_chroma_comp(vf, cpW, mbX*8, mbY*8, chroma_pred, vcdc, vcstore, qpc16, caT16, caL16)
499 }
500
501 if cls == 0 {
502 // I_NxN (I_4x4)
503 i4count = i4count + 1
504 let modes: *i64 = sys_mmap(24 * 8) as *i64
505 nx_h264_parse_inxn_predmodes(sbr, modes)
506 let cbp: i64 = nx_h264_decode_cbp_intra(sbr)
507 let cbpL: i64 = cbp & 15
508 let cbpC: i64 = (cbp >> 4) & 3
509 if cbp != 0 { let mqd: i64 = br_read_se(sbr); qp = (qp + mqd + 52) % 52 }
510 let bx0: i64 = mbX * 4
511 let by0: i64 = mbY * 4
512 // luma 4x4 blocks: derive mode, decode residual (per 8x8 CBP), reconstruct in scan order
513 var bl: i64 = 0
514 while bl < 16 {
515 let abx: i64 = bx0 + bx4[bl]
516 let aby: i64 = by0 + by4[bl]
517 let bpx: i64 = mbX * 16 + bx4[bl] * 4
518 let bpy: i64 = mbY * 16 + by4[bl] * 4
519 // intra4x4 mode derivation (8.3.1.1)
520 var avAm: i64 = 0
521 if abx > 0 { if decoded[aby*lbW + abx - 1] == 1 { avAm = 1 } }
522 var avBm: i64 = 0
523 if aby > 0 { if decoded[(aby-1)*lbW + abx] == 1 { avBm = 1 } }
524 var predMode: i64 = 2
525 if avAm == 1 { if avBm == 1 {
526 let ma: i64 = i4m[aby*lbW + abx - 1]
527 let mbb: i64 = i4m[(aby-1)*lbW + abx]
528 predMode = ma
529 if mbb < ma { predMode = mbb }
530 } }
531 var mode: i64 = predMode
532 if modes[bl] >= 0 {
533 let rem: i64 = modes[bl]
534 if rem < predMode { mode = rem }
535 if rem >= predMode { mode = rem + 1 }
536 }
537 i4m[aby*lbW + abx] = mode
538 // residual (only if this 8x8 is luma-coded)
539 let i8: i64 = bl / 4
540 z = 0
541 while z < 16 { coeff[z] = 0; z = z + 1 }
542 if ((cbpL >> i8) & 1) != 0 {
543 let tc: i64 = dec_luma(sbr, lumaTC, lbW, abx, aby, 16, coeff)
544 if tc > 0 { nzblocks = nzblocks + 1 }
545 z = 0
546 while z < 16 { coeffsum = coeffsum + coeff[z] * (z + 1); z = z + 1 }
547 }
548 if ((cbpL >> i8) & 1) == 0 { lumaTC[aby*lbW + abx] = 0 }
549 // reconstruct (availabilities from decoded-block bitmap)
550 var aT: i64 = 0
551 if avBm == 1 { aT = 1 }
552 var aL: i64 = 0
553 if avAm == 1 { aL = 1 }
554 var aTL: i64 = 0
555 if abx > 0 { if aby > 0 { if decoded[(aby-1)*lbW + abx - 1] == 1 { aTL = 1 } } }
556 var aTR: i64 = 0
557 if aby > 0 { if abx + 1 < lbW { if decoded[(aby-1)*lbW + abx + 1] == 1 { aTR = 1 } } }
558 recon_i4_block(yf, W, bpx, bpy, mode, aT, aL, aTL, aTR, coeff, qp)
559 decoded[aby*lbW + abx] = 1
560 bl = bl + 1
561 }
562 let ucdc4: *i64 = sys_mmap(8 * 8) as *i64
563 let vcdc4: *i64 = sys_mmap(8 * 8) as *i64
564 z = 0
565 while z < 4 { ucdc4[z] = 0; vcdc4[z] = 0; z = z + 1 }
566 z = 0
567 while z < 64 { ucstore[z] = 0; vcstore[z] = 0; z = z + 1 }
568 if cbpC != 0 {
569 nx_h264_residual_decode_cdc(sbr, ucdc4)
570 nx_h264_residual_decode_cdc(sbr, vcdc4)
571 z = 0
572 while z < 4 { coeffsum = coeffsum + (ucdc4[z] + vcdc4[z]) * (z + 1); z = z + 1 }
573 if cbpC == 2 {
574 var cc: i64 = 0
575 while cc < 4 {
576 dec_chroma_ac(sbr, uTC, cbW, mbX*2 + (cc%2), mbY*2 + (cc/2), coeff)
577 z = 0
578 while z < 15 { ucstore[cc*16 + 1 + z] = coeff[z]; z = z + 1 }
579 cc = cc + 1
580 }
581 cc = 0
582 while cc < 4 {
583 dec_chroma_ac(sbr, vTC, cbW, mbX*2 + (cc%2), mbY*2 + (cc/2), coeff)
584 z = 0
585 while z < 15 { vcstore[cc*16 + 1 + z] = coeff[z]; z = z + 1 }
586 cc = cc + 1
587 }
588 }
589 }
590 if cbpC == 0 {
591 var cc: i64 = 0
592 while cc < 4 { uTC[(mbY*2 + cc/2) * cbW + (mbX*2 + cc%2)] = 0; vTC[(mbY*2 + cc/2) * cbW + (mbX*2 + cc%2)] = 0; cc = cc + 1 }
593 }
594 var qpi4: i64 = qp + pps[9]
595 if qpi4 < 0 { qpi4 = 0 }
596 if qpi4 > 51 { qpi4 = 51 }
597 let qpc4: i64 = nx_h264_chroma_qp(qpi4)
598 var caT4: i64 = 0
599 if mbY > 0 { caT4 = 1 }
600 var caL4: i64 = 0
601 if mbX > 0 { caL4 = 1 }
602 recon_chroma_comp(uf, cpW, mbX*8, mbY*8, modes[16], ucdc4, ucstore, qpc4, caT4, caL4)
603 recon_chroma_comp(vf, cpW, mbX*8, mbY*8, modes[16], vcdc4, vcstore, qpc4, caT4, caL4)
604 }
605
606 mbQP[mbAddr] = qp
607 mbCls[mbAddr] = cls
608 mbAddr = mbAddr + 1
609 }
610 }
611
612 // snapshot pre-deblock recon, then compare vs ffmpeg's no-loop-filter output (isolates recon)
613 var bexact: i64 = 1 // strict gate: any plane/pass with MAXDIFF!=0 flips this -> verdict RED
614 let yf0: *u8 = sys_mmap(W * H + 64)
615 z = 0
616 while z < W * H { yf0[z] = yf[z]; z = z + 1 }
617 let nzp: *i64 = sys_mmap(16) as *i64
618 let refnd: *u8 = sys_read_file("knowledge/staging/media/ref_frame0_nodeblock.yuv\x00" as *u8, nzp)
619 if (refnd as i64) != 0 {
620 var ndmax: i64 = 0
621 var ndexact: i64 = 0
622 var pnd: i64 = 0
623 while pnd < W * H {
624 var dd: i64 = (yf0[pnd] as i64) - (refnd[pnd] as i64)
625 if dd < 0 { dd = 0 - dd }
626 if dd > ndmax { ndmax = dd }
627 if dd == 0 { ndexact = ndexact + 1 }
628 pnd = pnd + 1
629 }
630 gp(" PRE-deblock recon vs ffmpeg(no-loop-filter): exact=\x00" as *u8); gn(ndexact); gp("/\x00" as *u8); gn(W * H)
631 gp(" (\x00" as *u8); gn(ndexact * 100 / (W * H)); gp("%) MAXDIFF=\x00" as *u8); gn(ndmax)
632 if ndmax == 0 { gp(" *** RECON BIT-EXACT -> remaining diff is purely deblocking ***\n\x00" as *u8) }
633 if ndmax != 0 { gp(" (recon has residual diff -> investigate prediction/qp)\n\x00" as *u8) }
634 // CHROMA (U,V) pre-deblock vs ffmpeg no-loop-filter ref (planes after luma in the .yuv)
635 var cuex: i64 = 0
636 var cumax: i64 = 0
637 var cvex: i64 = 0
638 var cvmax: i64 = 0
639 let cbaseo: i64 = W * H
640 let csz: i64 = cpW * cpH
641 var cp: i64 = 0
642 while cp < csz {
643 var du: i64 = (uf[cp] as i64) - (refnd[cbaseo + cp] as i64)
644 if du < 0 { du = 0 - du }
645 if du > cumax { cumax = du }
646 if du == 0 { cuex = cuex + 1 }
647 var dv: i64 = (vf[cp] as i64) - (refnd[cbaseo + csz + cp] as i64)
648 if dv < 0 { dv = 0 - dv }
649 if dv > cvmax { cvmax = dv }
650 if dv == 0 { cvex = cvex + 1 }
651 cp = cp + 1
652 }
653 gp(" PRE-deblock CHROMA U vs ffmpeg: exact=\x00" as *u8); gn(cuex); gp("/\x00" as *u8); gn(csz); gp(" MAXDIFF=\x00" as *u8); gn(cumax); gp("\n\x00" as *u8)
654 gp(" PRE-deblock CHROMA V vs ffmpeg: exact=\x00" as *u8); gn(cvex); gp("/\x00" as *u8); gn(csz); gp(" MAXDIFF=\x00" as *u8); gn(cvmax); gp("\n\x00" as *u8)
655 if ndmax != 0 { bexact = 0 }
656 if cumax != 0 { bexact = 0 }
657 if cvmax != 0 { bexact = 0 }
658 }
659
660 // ===== in-loop deblocking (luma), per-MB raster order: each MB filters its 4
661 // vertical edges (left->right) then its 4 horizontal edges (top->bottom).
662 // MB-boundary edges bS=4 (intra), internal 4x4 edges bS=3. =====
663 if ok == 1 {
664 let s: *i64 = sys_mmap(8 * 8) as *i64
665 let ve: *i64 = sys_mmap(8 * 8) as *i64
666 ve[0]=0; ve[1]=4; ve[2]=8; ve[3]=12
667 var dmy: i64 = 0
668 while dmy < mbH {
669 var dmx: i64 = 0
670 while dmx < mbW {
671 let px: i64 = dmx * 16
672 let py: i64 = dmy * 16
673 let qpMB: i64 = mbQP[dmy * mbW + dmx]
674 var ei: i64 = 0
675 while ei < 4 {
676 let eo: i64 = ve[ei]
677 let ex: i64 = px + eo
678 var doit: i64 = 1
679 var bs: i64 = 3
680 var qpe: i64 = qpMB
681 if eo == 0 {
682 if dmx == 0 { doit = 0 }
683 if dmx > 0 { bs = 4; qpe = (mbQP[dmy * mbW + (dmx - 1)] + qpMB + 1) >> 1 }
684 }
685 if doit == 1 {
686 var ry: i64 = 0
687 while ry < 16 {
688 let base: i64 = (py + ry) * W + ex
689 s[0] = yf[base - 4] as i64; s[1] = yf[base - 3] as i64; s[2] = yf[base - 2] as i64; s[3] = yf[base - 1] as i64
690 s[4] = yf[base] as i64; s[5] = yf[base + 1] as i64; s[6] = yf[base + 2] as i64; s[7] = yf[base + 3] as i64
691 if bs == 4 { nx_deblock_luma_bs4(s, qpe) }
692 if bs < 4 { nx_deblock_luma_edge(s, bs, qpe) }
693 yf[base - 3] = s[1] as u8; yf[base - 2] = s[2] as u8; yf[base - 1] = s[3] as u8
694 yf[base] = s[4] as u8; yf[base + 1] = s[5] as u8; yf[base + 2] = s[6] as u8
695 ry = ry + 1
696 }
697 }
698 ei = ei + 1
699 }
700 ei = 0
701 while ei < 4 {
702 let eo: i64 = ve[ei]
703 let ey: i64 = py + eo
704 var doit: i64 = 1
705 var bs: i64 = 3
706 var qpe: i64 = qpMB
707 if eo == 0 {
708 if dmy == 0 { doit = 0 }
709 if dmy > 0 { bs = 4; qpe = (mbQP[(dmy - 1) * mbW + dmx] + qpMB + 1) >> 1 }
710 }
711 if doit == 1 {
712 var rx: i64 = 0
713 while rx < 16 {
714 let col: i64 = px + rx
715 s[0] = yf[(ey - 4) * W + col] as i64; s[1] = yf[(ey - 3) * W + col] as i64; s[2] = yf[(ey - 2) * W + col] as i64; s[3] = yf[(ey - 1) * W + col] as i64
716 s[4] = yf[ey * W + col] as i64; s[5] = yf[(ey + 1) * W + col] as i64; s[6] = yf[(ey + 2) * W + col] as i64; s[7] = yf[(ey + 3) * W + col] as i64
717 if bs == 4 { nx_deblock_luma_bs4(s, qpe) }
718 if bs < 4 { nx_deblock_luma_edge(s, bs, qpe) }
719 yf[(ey - 3) * W + col] = s[1] as u8; yf[(ey - 2) * W + col] = s[2] as u8; yf[(ey - 1) * W + col] = s[3] as u8
720 yf[ey * W + col] = s[4] as u8; yf[(ey + 1) * W + col] = s[5] as u8; yf[(ey + 2) * W + col] = s[6] as u8
721 rx = rx + 1
722 }
723 }
724 ei = ei + 1
725 }
726 dmx = dmx + 1
727 }
728 dmy = dmy + 1
729 }
730 }
731 // ===== in-loop deblocking (chroma U+V), 4:2:0: edges at chroma x/y in {0,4}; bS reuses luma
732 // (4 at MB boundary, 3 internal); qp = chroma QPc (averaged across the boundary). =====
733 if ok == 1 {
734 let cs: *i64 = sys_mmap(8 * 8) as *i64
735 let cve: *i64 = sys_mmap(8 * 8) as *i64
736 cve[0] = 0; cve[1] = 4
737 var dmy: i64 = 0
738 while dmy < mbH {
739 var dmx: i64 = 0
740 while dmx < mbW {
741 let ccpx: i64 = dmx * 8
742 let ccpy: i64 = dmy * 8
743 let qpcCur: i64 = chroma_qpc(mbQP[dmy * mbW + dmx], pps[9])
744 var ei: i64 = 0
745 while ei < 2 {
746 let eo: i64 = cve[ei]
747 let ex: i64 = ccpx + eo
748 var doit: i64 = 1
749 var bs: i64 = 3
750 var qpe: i64 = qpcCur
751 if eo == 0 {
752 if dmx == 0 { doit = 0 }
753 if dmx > 0 { bs = 4; qpe = (chroma_qpc(mbQP[dmy * mbW + (dmx - 1)], pps[9]) + qpcCur + 1) >> 1 }
754 }
755 if doit == 1 {
756 var ry: i64 = 0
757 while ry < 8 {
758 let base: i64 = (ccpy + ry) * cpW + ex
759 cs[0] = uf[base - 2] as i64; cs[1] = uf[base - 1] as i64; cs[2] = uf[base] as i64; cs[3] = uf[base + 1] as i64
760 nx_deblock_chroma_edge(cs, bs, qpe)
761 uf[base - 1] = cs[1] as u8; uf[base] = cs[2] as u8
762 cs[0] = vf[base - 2] as i64; cs[1] = vf[base - 1] as i64; cs[2] = vf[base] as i64; cs[3] = vf[base + 1] as i64
763 nx_deblock_chroma_edge(cs, bs, qpe)
764 vf[base - 1] = cs[1] as u8; vf[base] = cs[2] as u8
765 ry = ry + 1
766 }
767 }
768 ei = ei + 1
769 }
770 ei = 0
771 while ei < 2 {
772 let eo: i64 = cve[ei]
773 let ey: i64 = ccpy + eo
774 var doit: i64 = 1
775 var bs: i64 = 3
776 var qpe: i64 = qpcCur
777 if eo == 0 {
778 if dmy == 0 { doit = 0 }
779 if dmy > 0 { bs = 4; qpe = (chroma_qpc(mbQP[(dmy - 1) * mbW + dmx], pps[9]) + qpcCur + 1) >> 1 }
780 }
781 if doit == 1 {
782 var rx: i64 = 0
783 while rx < 8 {
784 let col: i64 = ccpx + rx
785 cs[0] = uf[(ey - 2) * cpW + col] as i64; cs[1] = uf[(ey - 1) * cpW + col] as i64; cs[2] = uf[ey * cpW + col] as i64; cs[3] = uf[(ey + 1) * cpW + col] as i64
786 nx_deblock_chroma_edge(cs, bs, qpe)
787 uf[(ey - 1) * cpW + col] = cs[1] as u8; uf[ey * cpW + col] = cs[2] as u8
788 cs[0] = vf[(ey - 2) * cpW + col] as i64; cs[1] = vf[(ey - 1) * cpW + col] as i64; cs[2] = vf[ey * cpW + col] as i64; cs[3] = vf[(ey + 1) * cpW + col] as i64
789 nx_deblock_chroma_edge(cs, bs, qpe)
790 vf[(ey - 1) * cpW + col] = cs[1] as u8; vf[ey * cpW + col] = cs[2] as u8
791 rx = rx + 1
792 }
793 }
794 ei = ei + 1
795 }
796 dmx = dmx + 1
797 }
798 dmy = dmy + 1
799 }
800 }
801 gp(" parsed MBs=\x00" as *u8); gn(mbAddr); gp("/\x00" as *u8); gn(totalMB)
802 gp(" (I16=\x00" as *u8); gn(i16count); gp(" I4=\x00" as *u8); gn(i4count); gp(" PCM=\x00" as *u8); gn(pcmcount); gp(")\n\x00" as *u8)
803 gp(" nz_luma_ac_blocks=\x00" as *u8); gn(nzblocks); gp(" coeff_checksum=\x00" as *u8); gn(coeffsum); gp(" finalQP=\x00" as *u8); gn(qp); gp("\n\x00" as *u8)
804 gp(" reader byte_pos=\x00" as *u8); gn(sbr.byte_pos); gp("/\x00" as *u8); gn(sl_rbsp); gp(" bit_pos=\x00" as *u8); gn(sbr.bit_pos); gp("\n\x00" as *u8)
805
806 // ===== compare reconstructed luma (PRE-deblock) vs ffmpeg post-deblock Y plane =====
807 let rzp: *i64 = sys_mmap(16) as *i64
808 let refp: *u8 = sys_read_file("knowledge/staging/media/ref_frame0.yuv\x00" as *u8, rzp)
809 if (refp as i64) != 0 {
810 var maxd: i64 = 0
811 var sumd: i64 = 0
812 var nexact: i64 = 0
813 var npix: i64 = W * H
814 var p: i64 = 0
815 while p < npix {
816 var dd: i64 = (yf[p] as i64) - (refp[p] as i64)
817 if dd < 0 { dd = 0 - dd }
818 if dd > maxd { maxd = dd }
819 if dd == 0 { nexact = nexact + 1 }
820 sumd = sumd + dd
821 p = p + 1
822 }
823 gp(" vs ffmpeg Y (POST-deblock): exact=\x00" as *u8); gn(nexact); gp("/\x00" as *u8); gn(npix)
824 gp(" (\x00" as *u8); gn(nexact * 100 / npix); gp("%) MAXDIFF=\x00" as *u8); gn(maxd); gp(" meandiff_x1000=\x00" as *u8); gn(sumd * 1000 / npix); gp("\n\x00" as *u8)
825 // CHROMA post-deblock vs ffmpeg ref_frame0.yuv U/V planes
826 var pcuex: i64 = 0
827 var pcumax: i64 = 0
828 var pcvex: i64 = 0
829 var pcvmax: i64 = 0
830 let pcbase: i64 = W * H
831 let pcsz: i64 = cpW * cpH
832 var pcp: i64 = 0
833 while pcp < pcsz {
834 var du2: i64 = (uf[pcp] as i64) - (refp[pcbase + pcp] as i64)
835 if du2 < 0 { du2 = 0 - du2 }
836 if du2 > pcumax { pcumax = du2 }
837 if du2 == 0 { pcuex = pcuex + 1 }
838 var dv2: i64 = (vf[pcp] as i64) - (refp[pcbase + pcsz + pcp] as i64)
839 if dv2 < 0 { dv2 = 0 - dv2 }
840 if dv2 > pcvmax { pcvmax = dv2 }
841 if dv2 == 0 { pcvex = pcvex + 1 }
842 pcp = pcp + 1
843 }
844 gp(" POST-deblock CHROMA U vs ffmpeg: exact=\x00" as *u8); gn(pcuex); gp("/\x00" as *u8); gn(pcsz); gp(" MAXDIFF=\x00" as *u8); gn(pcumax); gp("\n\x00" as *u8)
845 gp(" POST-deblock CHROMA V vs ffmpeg: exact=\x00" as *u8); gn(pcvex); gp("/\x00" as *u8); gn(pcsz); gp(" MAXDIFF=\x00" as *u8); gn(pcvmax); gp("\n\x00" as *u8)
846 if maxd != 0 { bexact = 0 }
847 if pcumax != 0 { bexact = 0 }
848 if pcvmax != 0 { bexact = 0 }
849 // histogram + interior-vs-edge split: deblocking changes pixels near the 4-pel grid;
850 // a recon bug shows as large diffs in block INTERIORS (col%4 in {1,2}, row%4 in {1,2}).
851 var b1: i64 = 0
852 var b2: i64 = 0
853 var b3: i64 = 0
854 var b4: i64 = 0
855 var interior_bad: i64 = 0
856 var interior_tot: i64 = 0
857 var mx: i64 = 0
858 var my: i64 = 0
859 var p2: i64 = 0
860 while p2 < npix {
861 var dd: i64 = (yf[p2] as i64) - (refp[p2] as i64)
862 if dd < 0 { dd = 0 - dd }
863 if dd >= 1 { if dd <= 4 { b1 = b1 + 1 } }
864 if dd >= 5 { if dd <= 12 { b2 = b2 + 1 } }
865 if dd >= 13 { if dd <= 25 { b3 = b3 + 1 } }
866 if dd > 25 { b4 = b4 + 1; if dd == maxd { mx = p2 % W; my = p2 / W } }
867 let cx: i64 = p2 % W
868 let cy: i64 = p2 / W
869 let mcx: i64 = cx % 4
870 let mcy: i64 = cy % 4
871 if mcx == 1 { if mcy == 1 { interior_tot = interior_tot + 1; if dd > 6 { interior_bad = interior_bad + 1 } } }
872 p2 = p2 + 1
873 }
874 gp(" diff histogram: [1-4]=\x00" as *u8); gn(b1); gp(" [5-12]=\x00" as *u8); gn(b2); gp(" [13-25]=\x00" as *u8); gn(b3); gp(" [>25]=\x00" as *u8); gn(b4); gp(" maxAt=(\x00" as *u8); gn(mx); gp(",\x00" as *u8); gn(my); gp(")\n\x00" as *u8)
875 gp(" block-interior(col%4=1,row%4=1) pixels with diff>6: \x00" as *u8); gn(interior_bad); gp("/\x00" as *u8); gn(interior_tot); gp(" (should be ~0 if recon is correct; deblock barely touches interiors)\n\x00" as *u8)
876 // localize: per-MB exactness by type (does the diff track I16 vs I4 MBs?)
877 var badI16: i64 = 0
878 var badI4: i64 = 0
879 var totI16: i64 = 0
880 var totI4: i64 = 0
881 var firstBadX: i64 = 0 - 1
882 var firstBadY: i64 = 0 - 1
883 var firstBadCls: i64 = 0 - 1
884 var dmy2: i64 = 0
885 while dmy2 < mbH {
886 var dmx2: i64 = 0
887 while dmx2 < mbW {
888 let c: i64 = mbCls[dmy2 * mbW + dmx2]
889 var bad: i64 = 0
890 var yy2: i64 = 0
891 while yy2 < 16 {
892 var xx2: i64 = 0
893 while xx2 < 16 {
894 let pp: i64 = (dmy2*16 + yy2) * W + (dmx2*16 + xx2)
895 var dd2: i64 = (yf[pp] as i64) - (refp[pp] as i64)
896 if dd2 < 0 { dd2 = 0 - dd2 }
897 if dd2 > 0 { bad = 1 }
898 xx2 = xx2 + 1
899 }
900 yy2 = yy2 + 1
901 }
902 if c == 1 { totI16 = totI16 + 1; if bad == 1 { badI16 = badI16 + 1 } }
903 if c == 0 { totI4 = totI4 + 1; if bad == 1 { badI4 = badI4 + 1 } }
904 if bad == 1 { if firstBadX < 0 { firstBadX = dmx2; firstBadY = dmy2; firstBadCls = c } }
905 dmx2 = dmx2 + 1
906 }
907 dmy2 = dmy2 + 1
908 }
909 gp(" MBs-with-any-diff: I16=\x00" as *u8); gn(badI16); gp("/\x00" as *u8); gn(totI16); gp(" I4=\x00" as *u8); gn(badI4); gp("/\x00" as *u8); gn(totI4)
910 gp(" firstBad=(\x00" as *u8); gn(firstBadX); gp(",\x00" as *u8); gn(firstBadY); gp(") cls=\x00" as *u8); gn(firstBadCls); gp("\n\x00" as *u8)
911 }
912 if ok == 1 {
913 if mbAddr == totalMB {
914 // landed within the final byte (<=1 byte slack for rbsp trailing bits)
915 if sbr.byte_pos >= sl_rbsp - 2 {
916 gp(" *** FRAME-WIDE ENTROPY SYNC: all MBs parsed, reader at rbsp end -> ENTROPY VERIFIED ***\n\x00" as *u8)
917 if bexact == 1 { gp(" *** FULL-FRAME Y+U+V BIT-EXACT (pre+post deblock) vs ffmpeg -> verdict=GREEN ***\n\x00" as *u8); return 0 }
918 gp(" *** entropy synced but a plane is NOT bit-exact -> verdict=RED ***\n\x00" as *u8)
919 return 7
920 }
921 gp(" all MBs parsed but reader NOT at rbsp end (over/under-read)\n\x00" as *u8)
922 return 5
923 }
924 }
925 gp(" DESYNC: did not cleanly parse all MBs\n\x00" as *u8)
926 return 6
927}