code wiki / (root) / nx_video_codec.nx

nx_video_codec.nx source

↩ module page · 522 lines · 21138 B

1// nx_video_codec.nx -- NishiVideo v1: I-frame intra-coded video codec. 2// Composes nx_dct8 + uniform quantisation + nx_video_frame. No P/B 3// frames in v1; one frame stands alone, deterministic, audit-replayable. 4// 5// Pipeline (encode): 6// pixel block (8x8 i64, range 0..255 luma) 7// -> subtract 128 (centre on zero per JPEG convention) 8// -> nx_dct8_forward_2d -> 64 frequency coefficients 9// -> quantise: coeff_q = round(coeff / quant_step) 10// -> pack as 64 int16 LE 11// For a multi-block frame, concatenate per-block payloads. 12// Then nx_vid_build_header + payload. 13// 14// Decode reverses: parse header -> for each block, unpack int16 15// coefficients -> dequantise (multiply by quant_step) -> IDCT -> add 16// back 128 -> clamp to 0..255 -> output pixel block. 17// 18// Honest verdict vs Jitsi/Discord/Zoom (the codecs they use): 19// WIN_axes: deterministic, audit-replayable (hash chain), patent-clean, 20// refuses BAD_MAGIC frames cleanly (no silent corruption) 21// LOSE_axes: rate-distortion at parity bitrate (no motion compensation 22// in v1; no entropy coding yet; quantisation is uniform 23// not perceptually-weighted) 24// Named improvements queued: nx_motion_compensation, nx_zigzag_scan, 25// nx_rle_coefficients, nx_range_code_coefficients, 26// nx_chroma_subsample, nx_perceptual_quant_table. 27// 28// genealogy_id: nx_dct8_q10 + nx_video_frame_q10 + jpeg_iso_10918 + 29// nx_voice_codec_v1_q10 30// lineage_id: nishi_video_codec_v1_q10 31 32// nx_safety_envelope: 33// intended_use: AUTO_APPLIED -- primitive-specific tuning queued 34// sil_target: SIL1 35// evidence: [bulk_applied_2026-05-16, see-file-comment-for-detail] 36// verdict: NOT_YET_EVALUATED 37 38import "nx_syscalls.nx" 39import "nx_dct8.nx" 40import "nx_video_frame.nx" 41import "nx_zigzag.nx" 42import "nx_rle.nx" 43import "nx_quant_table.nx" 44import "nx_deblock.nx" 45 46// Bit 7 of frame_kind = entropy-coded payload (zigzag + RLE). Cleared 47// = raw int16 coefficients. Backward-compatible additive flag. 48const NX_VID_FLAG_ENTROPY: i64 = 128 49// Bit 5 of frame_kind = apply deblock filter at end of decode. 50const NX_VID_FLAG_DEBLOCK: i64 = 32 51// Bit 4 of frame_kind = P-frame blocks carry a 1-byte motion vector before 52// the residual (integer-pel block motion compensation). Additive flag. 53const NX_VID_FLAG_MOTION: i64 = 16 54const NX_VID_MVR: i64 = 7 // motion search range +/-7 px (4-bit dx,dy) 55 56// Bit 7 of quant_step = perceptual quantisation mode. Bits 0..6 hold 57// the JPEG quality factor 1..100. When cleared, bits 0..6 hold the 58// uniform quant step 1..127 (legacy v1 behaviour). 59const NX_VID_FLAG_PERCEPTUAL_QUANT: i64 = 128 60 61const NX_VC_VID_VERDICT_UNKNOWN: i64 = 0 62const NX_VC_VID_VERDICT_OK: i64 = 1 63const NX_VC_VID_VERDICT_BUF_TOO_SMALL: i64 = 2 64const NX_VC_VID_VERDICT_BAD_PARAMS: i64 = 3 65const NX_VC_VID_VERDICT_FRAME_REJECTED: i64 = 4 66const NX_VC_VID_VERDICT_N: i64 = 5 67 68// Persistent encoder/decoder state. Caller allocates all buffers. 69struct VideoCodecState { 70 M: *i64, // 64-entry DCT matrix 71 scratch: *i64, // 64 i64 72 tmp_in: *i64, // 8 i64 73 tmp_out: *i64, // 8 i64 74 block_buf: *i64, // 64 i64 (one block working area) 75 coeffs_buf: *i64, // 64 i64 76 prev_hash: *u8, // 32 bytes 77 sequence: i64, 78 tier: i64, 79 quant_step: i64, 80 init_done: i64, 81 // Entropy-coding scratch (optional; populated by *_init_entropy). 82 to_zz: *i64, // 64 i64 83 from_zz: *i64, // 64 i64 84 zz_stream: *i64, // 64 i64 85 entropy_enabled: i64, // 0/1; defaults 0 86 // Quantisation table. When perceptual_quality > 0 the table is 87 // a JPEG Annex K matrix scaled by quality; otherwise it's all 88 // quant_step (uniform mode, backward compatible). 89 quant_table: *i64, // 64 i64; caller-supplied 90 perceptual_quality: i64, // 0 = uniform; 1..100 = JPEG quality 91 deblock_enabled: i64, // 0/1; if 1, decoder runs nx_deblock after IDCT 92 ref_frame: *i64, // P-frame reference (0 = I-frame, else residual vs ref) 93 mvr: i64, // motion search range (px); 0 = motion comp off 94 skip_enabled: i64 // 1 = code static blocks as a 1-byte SKIP sentinel 95} 96 97// Initialise codec state. 98func nx_vc_vid_init( 99 s: *VideoCodecState, 100 M: *i64, scratch: *i64, 101 tmp_in: *i64, tmp_out: *i64, 102 block_buf: *i64, coeffs_buf: *i64, 103 prev_hash: *u8, tier: i64, quant_step: i64 104) -> i64 { 105 if quant_step < 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 106 if quant_step > 64 { return NX_VC_VID_VERDICT_BAD_PARAMS } 107 s.M = M 108 s.scratch = scratch 109 s.tmp_in = tmp_in 110 s.tmp_out = tmp_out 111 s.block_buf = block_buf 112 s.coeffs_buf = coeffs_buf 113 s.prev_hash = prev_hash 114 s.sequence = 0 115 s.tier = tier 116 s.quant_step = quant_step 117 s.init_done = 1 118 s.entropy_enabled = 0 119 s.to_zz = 0 as *i64 120 s.from_zz = 0 as *i64 121 s.zz_stream = 0 as *i64 122 s.quant_table = 0 as *i64 123 s.perceptual_quality = 0 124 s.deblock_enabled = 0 125 s.ref_frame = 0 as *i64 126 s.mvr = NX_VID_MVR 127 s.skip_enabled = 1 128 nx_dct8_init(M) 129 var i: i64 = 0 130 while i < 32 { prev_hash[i] = 0; i = i + 1 } 131 return NX_VC_VID_VERDICT_OK 132} 133 134// Optional: wire up entropy-coding scratch buffers and turn the 135// flag on. Caller passes 64-i64 each for to_zz / from_zz / zz_stream. 136func nx_vc_vid_init_entropy( 137 s: *VideoCodecState, 138 to_zz: *i64, from_zz: *i64, zz_stream: *i64 139) -> i64 { 140 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 141 s.to_zz = to_zz 142 s.from_zz = from_zz 143 s.zz_stream = zz_stream 144 s.entropy_enabled = 1 145 nx_zigzag_init(to_zz, from_zz) 146 return NX_VC_VID_VERDICT_OK 147} 148 149// Optional: switch to JPEG perceptual quant at the given quality 150// (1..100). Caller pre-allocates a 64 i64 buffer for the table. 151// Once set, encode + decode use this table for per-coefficient 152// quantisation instead of the uniform quant_step. 153func nx_vc_vid_set_perceptual_quant( 154 s: *VideoCodecState, 155 quant_table_buf: *i64, 156 quality: i64 157) -> i64 { 158 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 159 if quality < 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 160 if quality > 100 { return NX_VC_VID_VERDICT_BAD_PARAMS } 161 nx_qt_luma(quant_table_buf, quality) 162 s.quant_table = quant_table_buf 163 s.perceptual_quality = quality 164 return NX_VC_VID_VERDICT_OK 165} 166 167// Turn deblock on/off. Decoder applies nx_deblock after IDCT when 168// the frame's deblock flag is set. 169func nx_vc_vid_set_deblock(s: *VideoCodecState, enabled: i64) -> i64 { 170 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 171 if enabled == 0 { s.deblock_enabled = 0 } 172 else { s.deblock_enabled = 1 } 173 return NX_VC_VID_VERDICT_OK 174} 175 176// Set the P-frame reference (a full decoded previous frame, img_w*img_h 177// i64 luma). When set, encode/decode code the residual (current - ref) 178// instead of an I-frame -- huge for talking-head video where frames 179// barely change. Pass 0 to clear (back to I-frame mode). 180func nx_vc_vid_set_reference(s: *VideoCodecState, ref: *i64) -> i64 { 181 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 182 s.ref_frame = ref 183 return NX_VC_VID_VERDICT_OK 184} 185 186// Set the integer-pel motion search range (px). 0 disables motion comp 187// (co-located residual). Default NX_VID_MVR. Decoder reads the per-block MV 188// from the bitstream regardless, so enc/dec stay in sync at any range. 189func nx_vc_vid_set_mvr(s: *VideoCodecState, mvr: i64) -> i64 { 190 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 191 s.mvr = mvr 192 return NX_VC_VID_VERDICT_OK 193} 194 195// Enable/disable SKIP coding of static blocks (1-byte sentinel for MV=(0,0) 196// + zero residual). Default on. 0 forces every P block to carry MV + residual. 197func nx_vc_vid_set_skip(s: *VideoCodecState, enabled: i64) -> i64 { 198 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 199 if enabled == 0 { s.skip_enabled = 0 } else { s.skip_enabled = 1 } 200 return NX_VC_VID_VERDICT_OK 201} 202 203// Internal helper: divisor for coefficient `idx` based on whether 204// the state is using uniform or perceptual mode. 205func _qstep_for(s: *VideoCodecState, idx: i64) -> i64 { 206 if s.perceptual_quality > 0 { 207 return s.quant_table[idx] 208 } 209 return s.quant_step 210} 211 212// Pack one int16 little-endian. 213func _w16(buf: *u8, off: i64, v: i64) -> i64 { 214 var vv: i64 = v 215 if vv < 0 { vv = vv + 65536 } 216 buf[off] = vv & 0xff 217 buf[off + 1] = (vv >> 8) & 0xff 218 return off + 2 219} 220 221func _r16(buf: *u8, off: i64) -> i64 { 222 var v: i64 = buf[off] | (buf[off + 1] << 8) 223 if v >= 32768 { v = v - 65536 } 224 return v 225} 226 227// Integer-pel block motion search: find (dx,dy) in [-mvr,mvr] minimizing the 228// sum-of-absolute-differences between the current 8x8 block at (bx,by) and the 229// reference shifted by (dx,dy). The window is clamped so the shifted block stays 230// fully in-bounds. Writes dx->out[0], dy->out[1]. Standard full-search ME. 231func _mv_search(pixels: *i64, ref: *i64, img_w: i64, img_h: i64, 232 bx: i64, by: i64, mvr: i64, out: *i64) -> i64 { 233 let x0: i64 = bx * 8 234 let y0: i64 = by * 8 235 var lo_dx: i64 = 0 - mvr; if x0 + lo_dx < 0 { lo_dx = 0 - x0 } 236 var hi_dx: i64 = mvr; if x0 + 7 + hi_dx > img_w - 1 { hi_dx = img_w - 8 - x0 } 237 var lo_dy: i64 = 0 - mvr; if y0 + lo_dy < 0 { lo_dy = 0 - y0 } 238 var hi_dy: i64 = mvr; if y0 + 7 + hi_dy > img_h - 1 { hi_dy = img_h - 8 - y0 } 239 var bestsad: i64 = -1; var bdx: i64 = 0; var bdy: i64 = 0 240 var dy: i64 = lo_dy 241 while dy <= hi_dy { 242 var dx: i64 = lo_dx 243 while dx <= hi_dx { 244 var sad: i64 = 0; var r: i64 = 0 245 while r < 8 { 246 var c: i64 = 0 247 while c < 8 { 248 let p: i64 = pixels[(y0 + r) * img_w + (x0 + c)] 249 let q: i64 = ref[(y0 + r + dy) * img_w + (x0 + c + dx)] 250 var d: i64 = p - q; if d < 0 { d = 0 - d } 251 sad = sad + d; c = c + 1 252 } 253 r = r + 1 254 } 255 // bias toward (0,0): only switch on a strict improvement; (0,0) is 256 // visited first (lo_dx/lo_dy may be <0, so seed with its own pass). 257 if bestsad < 0 { bestsad = sad; bdx = dx; bdy = dy } 258 else { if sad < bestsad { bestsad = sad; bdx = dx; bdy = dy } } 259 dx = dx + 1 260 } 261 dy = dy + 1 262 } 263 out[0] = bdx; out[1] = bdy 264 return 0 265} 266 267// Encode one I-frame: `pixels` is width_blocks*8 by height_blocks*8 268// luma values, row-major. Returns total bytes written; writes via 269// out_total. Path chosen by s.entropy_enabled. 270func nx_vc_vid_encode( 271 s: *VideoCodecState, 272 pixels: *i64, width_blocks: i64, height_blocks: i64, 273 out_buf: *u8, out_cap: i64, 274 out_total: *i64 275) -> i64 { 276 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 277 if width_blocks < 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 278 if height_blocks < 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 279 if out_cap < NX_VID_HEADER_BYTES + 8 { return NX_VC_VID_VERDICT_BUF_TOO_SMALL } 280 281 // Write payload first (starting after the header slot), so we 282 // know its true length before building the header. 283 let img_w: i64 = width_blocks * 8 284 let img_h: i64 = height_blocks * 8 285 let is_p: i64 = (s.ref_frame as i64) 286 let mvout: *i64 = sys_mmap(4 * 8) as *i64 287 var off: i64 = NX_VID_HEADER_BYTES 288 var by: i64 = 0 289 while by < height_blocks { 290 var bx: i64 = 0 291 while bx < width_blocks { 292 // P-frame: motion-search this block (the 1-byte MV / skip sentinel 293 // is written AFTER quantisation so we can detect skippable blocks). 294 var mdx: i64 = 0; var mdy: i64 = 0 295 if is_p != 0 { 296 _mv_search(pixels, s.ref_frame, img_w, img_h, bx, by, s.mvr, mvout) 297 mdx = mvout[0]; mdy = mvout[1] 298 } 299 // Copy 8x8 block, subtract the (motion-compensated) reference. 300 var r: i64 = 0 301 while r < 8 { 302 var c: i64 = 0 303 while c < 8 { 304 let idx: i64 = (by * 8 + r) * img_w + (bx * 8 + c) 305 var base: i64 = 128 306 if is_p != 0 { base = s.ref_frame[(by * 8 + r + mdy) * img_w + (bx * 8 + c + mdx)] } 307 s.block_buf[r * 8 + c] = pixels[idx] - base 308 c = c + 1 309 } 310 r = r + 1 311 } 312 nx_dct8_forward_2d(s.M, s.block_buf, s.coeffs_buf, 313 s.scratch, s.tmp_in, s.tmp_out) 314 // Quantise into coeffs_buf in place (per-coefficient divisor). 315 var i: i64 = 0 316 while i < 64 { 317 let divisor: i64 = _qstep_for(s, i) 318 var q: i64 = s.coeffs_buf[i] / divisor 319 if q > 32767 { q = 32767 } 320 if q < -32768 { q = -32768 } 321 s.coeffs_buf[i] = q 322 i = i + 1 323 } 324 // SKIP coding (P only): MV=(0,0) AND all-zero quantised residual -> 325 // a single 0x00 sentinel, no MV byte, no residual. Real MVs always 326 // have both nibbles in 1..15, so 0x00 is unambiguous. This removes 327 // the per-block MV overhead on static regions (the talking-head case 328 // where most blocks don't change) -- making MC a win on static too. 329 var skip: i64 = 0 330 if is_p != 0 { 331 if s.skip_enabled == 1 { if mdx == 0 { if mdy == 0 { 332 var az: i64 = 1; var zk: i64 = 0 333 while zk < 64 { if s.coeffs_buf[zk] != 0 { az = 0 } zk = zk + 1 } 334 skip = az 335 } } } 336 if off + 1 > out_cap { return NX_VC_VID_VERDICT_BUF_TOO_SMALL } 337 if skip == 1 { out_buf[off] = 0 } 338 else { out_buf[off] = (((mdx + 8) << 4) | ((mdy + 8) & 0xf)) & 0xff } 339 off = off + 1 340 } 341 if skip == 0 { 342 if s.entropy_enabled == 1 { 343 // Zigzag + RLE. 344 nx_zigzag_scan(s.coeffs_buf, s.to_zz, s.zz_stream) 345 let rle_n_slot: *i64 = sys_mmap(16) as *i64 346 let cur_remaining: i64 = out_cap - off 347 let r2: i64 = nx_rle_encode_block( 348 s.zz_stream, (out_buf as i64 + off) as *u8, cur_remaining, rle_n_slot) 349 if r2 != NX_RLE_VERDICT_OK { return NX_VC_VID_VERDICT_BUF_TOO_SMALL } 350 off = off + *rle_n_slot 351 } else { 352 // Raw int16 pack. 353 if off + 64 * 2 > out_cap { return NX_VC_VID_VERDICT_BUF_TOO_SMALL } 354 var j: i64 = 0 355 while j < 64 { 356 off = _w16(out_buf, off, s.coeffs_buf[j]) 357 j = j + 1 358 } 359 } 360 } 361 bx = bx + 1 362 } 363 by = by + 1 364 } 365 366 let payload_bytes: i64 = off - NX_VID_HEADER_BYTES 367 var kind: i64 = NX_VID_KIND_I 368 if (s.ref_frame as i64) != 0 { kind = NX_VID_KIND_P | NX_VID_FLAG_MOTION } 369 if s.entropy_enabled == 1 { kind = kind | NX_VID_FLAG_ENTROPY } 370 if s.deblock_enabled == 1 { kind = kind | NX_VID_FLAG_DEBLOCK } 371 var qbyte: i64 = s.quant_step 372 if s.perceptual_quality > 0 { 373 qbyte = NX_VID_FLAG_PERCEPTUAL_QUANT | (s.perceptual_quality & 0x7f) 374 } 375 376 let hb: i64 = nx_vid_build_header( 377 out_buf, out_cap, 378 s.tier, width_blocks, height_blocks, 379 kind, qbyte, 380 s.sequence, s.prev_hash, payload_bytes) 381 if hb < 0 { return NX_VC_VID_VERDICT_BUF_TOO_SMALL } 382 383 s.sequence = s.sequence + 1 384 *out_total = off 385 return NX_VC_VID_VERDICT_OK 386} 387 388// Decode one I-frame. pixels_out must accommodate 389// width_blocks*8 * height_blocks*8 i64 values. Writes width/height 390// observed to out_w/out_h. 391func nx_vc_vid_decode( 392 s: *VideoCodecState, 393 in_buf: *u8, in_len: i64, 394 pixels_out: *i64, pixels_cap: i64, 395 out_w: *i64, out_h: *i64 396) -> i64 { 397 if s.init_done != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 398 let wb_slot: *i64 = sys_mmap(16) as *i64 399 let hb_slot: *i64 = sys_mmap(16) as *i64 400 let kind_slot: *i64 = sys_mmap(16) as *i64 401 let q_slot: *i64 = sys_mmap(16) as *i64 402 let seq_slot: *i64 = sys_mmap(16) as *i64 403 let po_slot: *i64 = sys_mmap(16) as *i64 404 let pl_slot: *i64 = sys_mmap(16) as *i64 405 406 let v: i64 = nx_vid_parse_header( 407 in_buf, in_len, 0 as *u8, 408 wb_slot, hb_slot, kind_slot, q_slot, seq_slot, 409 po_slot, pl_slot) 410 if v != NX_VID_VERDICT_OK { return NX_VC_VID_VERDICT_FRAME_REJECTED } 411 412 let width_blocks: i64 = *wb_slot 413 let height_blocks: i64 = *hb_slot 414 let qbyte: i64 = *q_slot 415 let payload_off: i64 = *po_slot 416 let payload_len: i64 = *pl_slot 417 let frame_kind: i64 = *kind_slot 418 let entropy_path: i64 = frame_kind & NX_VID_FLAG_ENTROPY 419 let deblock_path: i64 = frame_kind & NX_VID_FLAG_DEBLOCK 420 let motion_path: i64 = frame_kind & NX_VID_FLAG_MOTION 421 let img_w: i64 = width_blocks * 8 422 let img_h: i64 = height_blocks * 8 423 let total_px: i64 = img_w * img_h 424 if total_px > pixels_cap { return NX_VC_VID_VERDICT_BUF_TOO_SMALL } 425 if entropy_path != 0 { 426 if s.entropy_enabled != 1 { return NX_VC_VID_VERDICT_BAD_PARAMS } 427 } 428 429 // Reconstruct the quantisation table from the q byte. If bit 7 430 // is set, it's a JPEG perceptual table at quality bits 0..6; 431 // otherwise uniform quant_step = bits 0..6. 432 let perceptual_mode: i64 = qbyte & NX_VID_FLAG_PERCEPTUAL_QUANT 433 let q_field: i64 = qbyte & 0x7f 434 let dec_table: *i64 = sys_mmap(64 * 8) as *i64 435 if perceptual_mode != 0 { 436 nx_qt_luma(dec_table, q_field) 437 } else { 438 var ti: i64 = 0 439 while ti < 64 { dec_table[ti] = q_field; ti = ti + 1 } 440 } 441 442 var off: i64 = payload_off 443 var by: i64 = 0 444 while by < height_blocks { 445 var bx: i64 = 0 446 while bx < width_blocks { 447 // P-frame motion vector (1 byte) precedes the residual; 0x00 = SKIP 448 // (MV=(0,0), no residual -> copy the co-located reference). 449 var mdx: i64 = 0; var mdy: i64 = 0; var skip: i64 = 0 450 if motion_path != 0 { 451 let mvb: i64 = in_buf[off] & 0xff 452 off = off + 1 453 if mvb == 0 { skip = 1 } 454 else { mdx = ((mvb >> 4) & 0xf) - 8; mdy = (mvb & 0xf) - 8 } 455 } 456 if skip == 1 { 457 var zb: i64 = 0 458 while zb < 64 { s.block_buf[zb] = 0; zb = zb + 1 } 459 } else { 460 if entropy_path != 0 { 461 // RLE decode -> zigzag unscan -> dequantise. 462 let cons: *i64 = sys_mmap(16) as *i64 463 let rv: i64 = nx_rle_decode_block( 464 (in_buf as i64 + off) as *u8, 465 payload_off + payload_len - off, 466 s.zz_stream, cons) 467 if rv != NX_RLE_VERDICT_OK { return NX_VC_VID_VERDICT_FRAME_REJECTED } 468 off = off + *cons 469 nx_zigzag_unscan(s.zz_stream, s.from_zz, s.coeffs_buf) 470 var ii: i64 = 0 471 while ii < 64 { 472 s.coeffs_buf[ii] = s.coeffs_buf[ii] * dec_table[ii] 473 ii = ii + 1 474 } 475 } else { 476 // Unpack + dequantise raw int16. 477 var i: i64 = 0 478 while i < 64 { 479 let q: i64 = _r16(in_buf, off) 480 s.coeffs_buf[i] = q * dec_table[i] 481 off = off + 2 482 i = i + 1 483 } 484 } 485 nx_dct8_inverse_2d(s.M, s.coeffs_buf, s.block_buf, 486 s.scratch, s.tmp_in, s.tmp_out) 487 } 488 // Place block back into image, add 128, clamp. 489 var r: i64 = 0 490 while r < 8 { 491 var c: i64 = 0 492 while c < 8 { 493 let didx: i64 = (by * 8 + r) * img_w + (bx * 8 + c) 494 var dbase: i64 = 128 495 if (frame_kind & NX_VID_KIND_P) != 0 { dbase = s.ref_frame[(by * 8 + r + mdy) * img_w + (bx * 8 + c + mdx)] } 496 var px: i64 = s.block_buf[r * 8 + c] + dbase 497 if px < 0 { px = 0 } 498 if px > 255 { px = 255 } 499 pixels_out[didx] = px 500 c = c + 1 501 } 502 r = r + 1 503 } 504 bx = bx + 1 505 } 506 by = by + 1 507 } 508 509 if deblock_path != 0 { 510 nx_deblock(pixels_out, img_w, img_h) 511 } 512 513 *out_w = img_w 514 *out_h = img_h 515 return NX_VC_VID_VERDICT_OK 516} 517 518func nx_vc_vid_verdict_is_valid(v: i64) -> i64 { 519 if v < 0 { return 0 } 520 if v >= NX_VC_VID_VERDICT_N { return 0 } 521 return 1 522}