nx_video_codec_v1_adapter.nx source
↩ module page · 213 lines · 8088 B
1// nx_video_codec_v1_adapter.nx -- wraps NishiVideo v1 behind nx_codec.
2//
3// NishiVideo v1's native API: i64 pixel arrays + explicit
4// (width_blocks, height_blocks) + writes total length to out-param.
5// nx_codec's vtable: *u8 byte buffers + returns bytes-written, no
6// dimension args.
7//
8// The adapter bridges by:
9// - Storing width/height in adapter state (fixed at construction)
10// - Treating *u8 src as packed luma bytes (1 byte/pixel, 0..255)
11// - Unpacking to i64 for the codec
12// - Repacking decoded i64 pixels back to *u8 luma on decode
13//
14// Per [[feedback-bits-up-exceed-never-match]]: zero new codec logic.
15// All 11 WIN axes (per codec_grade_card.nx) belong to nx_video_codec.
16
17// nx_safety_envelope:
18// intended_use: glue between nx_codec OOP interface and NishiVideo v1
19// sil_target: SIL2
20// evidence: composes nx_video_codec v1 (codec_grade_card asserts
21// 11 WINs) via the same vtable pattern as the audio adapter
22// verdict: NOT_YET_EVALUATED -- pending adapter smoke
23
24import "nx_syscalls_x86_64.nx"
25import "nx_codec.nx"
26import "nx_video_codec.nx"
27
28// ---- Adapter state ----
29
30struct nx_video_codec_v1_state {
31 width_blocks: i64, // image width = width_blocks * 8
32 height_blocks: i64,
33 quant_step: i64, // 1..64; lower = better quality
34 tier: i64,
35 perceptual: i64, // 0 or 1..100
36
37 vc_state: *VideoCodecState,
38
39 // Owned scratch.
40 M: *i64, // 64 i64 (DCT matrix)
41 scratch: *i64, // 64 i64
42 tmp_in: *i64, // 8 i64
43 tmp_out: *i64, // 8 i64
44 block_buf: *i64, // 64 i64
45 coeffs_buf: *i64, // 64 i64
46 prev_hash: *u8, // 32 bytes
47 quant_table: *i64, // 64 i64 when perceptual > 0; null otherwise
48
49 // Per-frame conversion scratch.
50 pixel_scratch: *i64, // width*height i64
51 pixel_scratch_cap: i64,
52 out_total_slot: *i64,
53 out_w_slot: *i64,
54 out_h_slot: *i64,
55}
56
57const NX_VID_V1A_STATE_BYTES: i64 = 168
58
59// ---- Conversion helpers ----
60
61// Unpack u8 luma bytes (0..255) into i64 pixels (0..255).
62func _unpack_luma_u8(src: *u8, src_n: i64, dst: *i64, dst_cap: i64) -> i64 {
63 if src_n > dst_cap { return -1 }
64 var i: i64 = 0
65 while i < src_n {
66 dst[i] = src[i] as i64
67 i = i + 1
68 }
69 return src_n
70}
71
72// Pack i64 luma values back into u8 bytes (clip to 0..255).
73func _pack_luma_u8(src: *i64, n: i64, dst: *u8, dst_cap: i64) -> i64 {
74 if n > dst_cap { return -1 }
75 var i: i64 = 0
76 while i < n {
77 var v: i64 = src[i]
78 if v < 0 { v = 0 }
79 if v > 255 { v = 255 }
80 dst[i] = v as u8
81 i = i + 1
82 }
83 return n
84}
85
86// ---- Vtable impls ----
87
88func nx_video_codec_v1_encode(c: *nx_codec, src: *u8, src_n: i64,
89 dst: *u8, dst_cap: i64) -> i64 {
90 let st: *nx_video_codec_v1_state = c.state as *nx_video_codec_v1_state
91
92 // Expect src_n == width_pixels * height_pixels.
93 let want_pixels: i64 = (st.width_blocks * 8) * (st.height_blocks * 8)
94 if src_n != want_pixels { return NX_CODEC_ERR_BAD_INPUT }
95
96 let n_pixels: i64 = _unpack_luma_u8(src, src_n,
97 st.pixel_scratch,
98 st.pixel_scratch_cap)
99 if n_pixels < 0 { return NX_CODEC_ERR_BAD_INPUT }
100
101 let out_slot: *i64 = st.out_total_slot
102 out_slot[0] = 0
103 let v: i64 = nx_vc_vid_encode(st.vc_state,
104 st.pixel_scratch,
105 st.width_blocks, st.height_blocks,
106 dst, dst_cap,
107 out_slot)
108 if v != NX_VC_VID_VERDICT_OK {
109 if v == NX_VC_VID_VERDICT_BUF_TOO_SMALL { return NX_CODEC_ERR_OUT_TOO_SMALL }
110 if v == NX_VC_VID_VERDICT_BAD_PARAMS { return NX_CODEC_ERR_BAD_INPUT }
111 return NX_CODEC_ERR_BAD_INPUT
112 }
113 return out_slot[0]
114}
115
116func nx_video_codec_v1_decode(c: *nx_codec, src: *u8, src_n: i64,
117 dst: *u8, dst_cap: i64) -> i64 {
118 let st: *nx_video_codec_v1_state = c.state as *nx_video_codec_v1_state
119
120 let w_slot: *i64 = st.out_w_slot
121 let h_slot: *i64 = st.out_h_slot
122 w_slot[0] = 0
123 h_slot[0] = 0
124 let v: i64 = nx_vc_vid_decode(st.vc_state,
125 src, src_n,
126 st.pixel_scratch, st.pixel_scratch_cap,
127 w_slot, h_slot)
128 if v != NX_VC_VID_VERDICT_OK {
129 if v == NX_VC_VID_VERDICT_BUF_TOO_SMALL { return NX_CODEC_ERR_OUT_TOO_SMALL }
130 if v == NX_VC_VID_VERDICT_FRAME_REJECTED { return NX_CODEC_ERR_BAD_MAGIC }
131 if v == NX_VC_VID_VERDICT_BAD_PARAMS { return NX_CODEC_ERR_BAD_INPUT }
132 return NX_CODEC_ERR_BAD_INPUT
133 }
134
135 // *out_w/*out_h hold PIXEL dimensions (img_w = width_blocks*8),
136 // not block counts. n_pixels = img_w * img_h directly.
137 let n_pixels: i64 = w_slot[0] * h_slot[0]
138 return _pack_luma_u8(st.pixel_scratch, n_pixels, dst, dst_cap)
139}
140
141// ---- Constructor ----
142
143func nx_video_codec_v1_new(c: *nx_codec,
144 width_blocks: i64, height_blocks: i64,
145 quant_step: i64) -> i64 {
146 if width_blocks < 1 { return -1 }
147 if height_blocks < 1 { return -1 }
148 if quant_step < 1 { return -1 }
149 if quant_step > 64 { return -1 }
150
151 let img_w: i64 = width_blocks * 8
152 let img_h: i64 = height_blocks * 8
153 let n_pix: i64 = img_w * img_h
154
155 // Allocate all scratch buffers.
156 let M: *i64 = sys_mmap(64 * 8) as *i64
157 let scratch: *i64 = sys_mmap(64 * 8) as *i64
158 let tmp_in: *i64 = sys_mmap(8 * 8) as *i64
159 let tmp_out: *i64 = sys_mmap(8 * 8) as *i64
160 let block_buf: *i64 = sys_mmap(64 * 8) as *i64
161 let coeffs_buf: *i64 = sys_mmap(64 * 8) as *i64
162 let prev_hash: *u8 = sys_mmap(32)
163
164 // 256 bytes -- matches the existing video_codec_smoke.nx allocation
165 // and provides slack beyond the 18*8=144 minimum struct fields.
166 let vcs: *VideoCodecState = sys_mmap(256) as *VideoCodecState
167 let init_rc: i64 = nx_vc_vid_init(vcs, M, scratch, tmp_in, tmp_out,
168 block_buf, coeffs_buf, prev_hash,
169 3, quant_step)
170 if init_rc != NX_VC_VID_VERDICT_OK { return -1 }
171
172 // 4x slack on pixel scratch -- the codec's decode can over-walk
173 // by a block boundary internally; 4x avoids hitting any edge.
174 let pix_scratch: *i64 = sys_mmap(n_pix * 8 * 4) as *i64
175 let out_total_slot: *i64 = sys_mmap(16) as *i64
176 let out_w_slot: *i64 = sys_mmap(16) as *i64
177 let out_h_slot: *i64 = sys_mmap(16) as *i64
178
179 let st: *nx_video_codec_v1_state = sys_mmap(NX_VID_V1A_STATE_BYTES)
180 as *nx_video_codec_v1_state
181 st.width_blocks = width_blocks
182 st.height_blocks = height_blocks
183 st.quant_step = quant_step
184 st.tier = 3
185 st.perceptual = 0
186 st.vc_state = vcs
187 st.M = M
188 st.scratch = scratch
189 st.tmp_in = tmp_in
190 st.tmp_out = tmp_out
191 st.block_buf = block_buf
192 st.coeffs_buf = coeffs_buf
193 st.prev_hash = prev_hash
194 st.quant_table = 0 as *i64
195 st.pixel_scratch = pix_scratch
196 st.pixel_scratch_cap = n_pix * 4
197 st.out_total_slot = out_total_slot
198 st.out_w_slot = out_w_slot
199 st.out_h_slot = out_h_slot
200
201 nx_codec_init(c,
202 NX_CODEC_ID_NISHIVIDEO_V1,
203 NX_CODEC_KIND_VIDEO,
204 n_pix, // frame_size_in (1 byte luma per pixel)
205 n_pix * 4, // frame_size_out (rough upper bound for v1 raw coeffs)
206 0, // sample_rate (n/a for video)
207 0, // bitrate_target (v1: no rate control yet)
208 3) // tier
209 c.state = st as i64
210 c.encode_fn = nx_video_codec_v1_encode
211 c.decode_fn = nx_video_codec_v1_decode
212 return 0
213}