code wiki / (root) / nx_h264_decode_frame.nx

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}