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}