introduce a way for encoder to manifest its capabilities
[nihav.git] / nihav-commonfmt / src / codecs / cinepakenc.rs
1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
3 use nihav_codec_support::vq::*;
4
5 #[derive(Default,Clone,Copy,PartialEq,Debug)]
6 struct YUVCode {
7 y: [u8; 4],
8 u: u8,
9 v: u8,
10 }
11 impl VQElement for YUVCode {
12 fn dist(&self, rval: Self) -> u32 {
13 let mut ysum = 0;
14 for (y0, y1) in self.y.iter().zip(rval.y.iter()) {
15 let yd = i32::from(*y0) - i32::from(*y1);
16 ysum += yd * yd;
17 }
18 let ud = i32::from(self.u) - i32::from(rval.u);
19 let vd = i32::from(self.v) - i32::from(rval.v);
20 (ysum + ud * ud + vd * vd) as u32
21 }
22 fn min_cw() -> Self { YUVCode { y: [0; 4], u: 0, v: 0 } }
23 fn max_cw() -> Self { YUVCode { y: [255; 4], u: 255, v: 255 } }
24 fn min(&self, rval: Self) -> Self {
25 let mut ycode = YUVCode::default();
26 for i in 0..4 {
27 ycode.y[i] = self.y[i].min(rval.y[i]);
28 }
29 ycode.u = self.u.min(rval.u);
30 ycode.v = self.v.min(rval.v);
31 ycode
32 }
33 fn max(&self, rval: Self) -> Self {
34 let mut ycode = YUVCode::default();
35 for i in 0..4 {
36 ycode.y[i] = self.y[i].max(rval.y[i]);
37 }
38 ycode.u = self.u.max(rval.u);
39 ycode.v = self.v.max(rval.v);
40 ycode
41 }
42 fn num_components() -> usize { 6 }
43 fn sort_by_component(arr: &mut [Self], component: usize) {
44 let mut counts = [0; 256];
45 for entry in arr.iter() {
46 let idx = match component {
47 0 | 1 | 2 | 3 => entry.y[component],
48 4 => entry.u,
49 _ => entry.v,
50 } as usize;
51 counts[idx] += 1;
52 }
53 let mut offs = [0; 256];
54 for i in 0..255 {
55 offs[i + 1] = offs[i] + counts[i];
56 }
57 let mut dst = vec![YUVCode::default(); arr.len()];
58 for entry in arr.iter() {
59 let idx = match component {
60 0 | 1 | 2 | 3 => entry.y[component],
61 4 => entry.u,
62 _ => entry.v,
63 } as usize;
64 dst[offs[idx]] = *entry;
65 offs[idx] += 1;
66 }
67 arr.copy_from_slice(dst.as_slice());
68 }
69 fn max_dist_component(min: &Self, max: &Self) -> usize {
70 let mut comp = 0;
71 let mut diff = 0;
72 for i in 0..4 {
73 let d = u32::from(max.y[i]) - u32::from(min.y[i]);
74 if d > diff {
75 diff = d;
76 comp = i;
77 }
78 }
79 let ud = u32::from(max.u) - u32::from(min.u);
80 if ud > diff {
81 diff = ud;
82 comp = 4;
83 }
84 let vd = u32::from(max.v) - u32::from(min.v);
85 if vd > diff {
86 comp = 5;
87 }
88 comp
89 }
90 }
91
92 #[derive(Default)]
93 struct YUVCodeSum {
94 ysum: [u64; 4],
95 usum: u64,
96 vsum: u64,
97 count: u64,
98 }
99
100 impl VQElementSum<YUVCode> for YUVCodeSum {
101 fn zero() -> Self { Self::default() }
102 fn add(&mut self, rval: YUVCode, count: u64) {
103 for i in 0..4 {
104 self.ysum[i] += u64::from(rval.y[i]) * count;
105 }
106 self.usum += u64::from(rval.u) * count;
107 self.vsum += u64::from(rval.v) * count;
108 self.count += count;
109 }
110 fn get_centroid(&self) -> YUVCode {
111 if self.count != 0 {
112 let mut ycode = YUVCode::default();
113 for i in 0..4 {
114 ycode.y[i] = ((self.ysum[i] + self.count / 2) / self.count) as u8;
115 }
116 ycode.u = ((self.usum + self.count / 2) / self.count) as u8;
117 ycode.v = ((self.vsum + self.count / 2) / self.count) as u8;
118 ycode
119 } else {
120 YUVCode::default()
121 }
122 }
123 }
124
125 struct RNG {
126 seed: u32,
127 }
128
129 impl RNG {
130 fn new() -> Self { Self { seed: 0x12345678 } }
131 fn next(&mut self) -> u8 {
132 let mut x = self.seed;
133 x ^= x.wrapping_shl(13);
134 x ^= x >> 17;
135 self.seed = x;
136 (self.seed >> 24) as u8
137 }
138 fn fill_entry(&mut self, entry: &mut YUVCode) {
139 for y in entry.y.iter_mut() {
140 *y = self.next();
141 }
142 entry.u = self.next();
143 entry.v = self.next();
144 }
145 }
146
147 const GRAY_FORMAT: NAPixelFormaton = NAPixelFormaton {
148 model: ColorModel::YUV(YUVSubmodel::YUVJ),
149 components: 1,
150 comp_info: [Some(NAPixelChromaton{h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 0, next_elem: 1}), None, None, None, None],
151 elem_size: 1,
152 be: true,
153 alpha: false,
154 palette: false,
155 };
156
157 struct MaskWriter {
158 masks: Vec<u32>,
159 mask: u32,
160 pos: u8,
161 }
162
163 impl MaskWriter {
164 fn new() -> Self {
165 Self {
166 masks: Vec::new(),
167 mask: 0,
168 pos: 0,
169 }
170 }
171 fn reset(&mut self) {
172 self.masks.clear();
173 self.mask = 0;
174 self.pos = 0;
175 }
176 fn put_v1(&mut self) {
177 self.mask <<= 1;
178 self.pos += 1;
179 if self.pos == 32 {
180 self.flush();
181 }
182 }
183 fn put_v4(&mut self) {
184 self.mask <<= 1;
185 self.mask |= 1;
186 self.pos += 1;
187 if self.pos == 32 {
188 self.flush();
189 }
190 }
191 fn put_inter(&mut self, skip: bool) {
192 self.mask <<= 1;
193 self.mask |= !skip as u32;
194 self.pos += 1;
195 if self.pos == 32 {
196 self.flush();
197 }
198 }
199 fn flush(&mut self) {
200 self.masks.push(self.mask);
201 self.mask = 0;
202 self.pos = 0;
203 }
204 fn end(&mut self) {
205 if self.pos == 0 { return; }
206 while self.pos < 32 {
207 self.mask <<= 1;
208 self.pos += 1;
209 }
210 self.flush();
211 }
212 }
213
214 #[derive(Clone,Copy,PartialEq)]
215 enum QuantMode {
216 ELBG,
217 Hybrid,
218 MedianCut,
219 }
220
221 impl std::string::ToString for QuantMode {
222 fn to_string(&self) -> String {
223 match *self {
224 QuantMode::ELBG => "elbg".to_string(),
225 QuantMode::Hybrid => "hybrid".to_string(),
226 QuantMode::MedianCut => "mediancut".to_string(),
227 }
228 }
229 }
230
231 struct CinepakEncoder {
232 stream: Option<NAStreamRef>,
233 lastfrm: Option<NAVideoBufferRef<u8>>,
234 pkt: Option<NAPacket>,
235 frmcount: u8,
236 key_int: u8,
237 qmode: QuantMode,
238 quality: u8,
239 nstrips: usize,
240 force_v1: bool,
241 cur_strip: usize,
242 v1_entries: Vec<YUVCode>,
243 v4_entries: Vec<YUVCode>,
244 v1_cb: Vec<[YUVCode; 256]>,
245 v4_cb: Vec<[YUVCode; 256]>,
246 v1_cur_cb: Vec<[YUVCode; 256]>,
247 v4_cur_cb: Vec<[YUVCode; 256]>,
248 v1_len: usize,
249 v4_len: usize,
250 v1_idx: Vec<u8>,
251 v4_idx: Vec<u8>,
252 grayscale: bool,
253 rng: RNG,
254 masks: MaskWriter,
255 skip_dist: Vec<u32>,
256 }
257
258 fn avg4(a: u8, b: u8, c: u8, d: u8) -> u8 {
259 ((u16::from(a) + u16::from(b) + u16::from(c) + u16::from(d) + 3) >> 2) as u8
260 }
261
262 fn patch_size(bw: &mut ByteWriter, pos: u64) -> EncoderResult<()> {
263 let size = bw.tell() - pos;
264 bw.seek(SeekFrom::Current(-((size + 3) as i64)))?;
265 bw.write_u24be((size + 4) as u32)?;
266 bw.seek(SeekFrom::End(0))?;
267 Ok(())
268 }
269
270 impl CinepakEncoder {
271 fn new() -> Self {
272 Self {
273 stream: None,
274 pkt: None,
275 lastfrm: None,
276 frmcount: 0,
277 qmode: QuantMode::MedianCut,
278 key_int: 25,
279 quality: 0,
280 nstrips: 2,
281 force_v1: false,
282 cur_strip: 0,
283 v1_entries: Vec::new(),
284 v4_entries: Vec::new(),
285 v1_cb: Vec::with_capacity(2),
286 v4_cb: Vec::with_capacity(2),
287 v1_cur_cb: Vec::with_capacity(2),
288 v4_cur_cb: Vec::with_capacity(2),
289 v1_len: 0,
290 v4_len: 0,
291 grayscale: false,
292 rng: RNG::new(),
293 v1_idx: Vec::new(),
294 v4_idx: Vec::new(),
295 masks: MaskWriter::new(),
296 skip_dist: Vec::new(),
297 }
298 }
299 fn read_strip(&mut self, in_frm: &NAVideoBuffer<u8>, start: usize, end: usize) {
300 let ystride = in_frm.get_stride(0);
301 let mut yoff = in_frm.get_offset(0) + start * ystride;
302 let ustride = in_frm.get_stride(1);
303 let mut uoff = in_frm.get_offset(1) + start / 2 * ustride;
304 let vstride = in_frm.get_stride(2);
305 let mut voff = in_frm.get_offset(2) + start / 2 * vstride;
306 let (width, _) = in_frm.get_dimensions(0);
307 let data = in_frm.get_data();
308 self.v1_entries.clear();
309 self.v4_entries.clear();
310 for _ in (start..end).step_by(4) {
311 for x in (0..width).step_by(4) {
312 let mut yblk = [0; 16];
313 let mut ublk = [128; 4];
314 let mut vblk = [128; 4];
315 for j in 0..4 {
316 for i in 0..4 {
317 yblk[i + j * 4] = data[yoff + x + i + j * ystride];
318 }
319 }
320 if !self.grayscale {
321 for j in 0..2 {
322 for i in 0..2 {
323 ublk[i + j * 2] = data[uoff + x / 2 + i + j * ustride];
324 vblk[i + j * 2] = data[voff + x / 2 + i + j * vstride];
325 }
326 }
327 }
328 self.v1_entries.push(YUVCode {
329 y: [avg4(yblk[ 0], yblk[ 1], yblk[ 4], yblk[ 5]),
330 avg4(yblk[ 2], yblk[ 3], yblk[ 6], yblk[ 7]),
331 avg4(yblk[ 8], yblk[ 9], yblk[12], yblk[13]),
332 avg4(yblk[10], yblk[11], yblk[14], yblk[15])],
333 u: avg4(ublk[0], ublk[1], ublk[2], ublk[3]),
334 v: avg4(vblk[0], vblk[1], vblk[2], vblk[3]),
335 });
336 for i in 0..4 {
337 let yidx = (i & 1) * 2 + (i & 2) * 4;
338 self.v4_entries.push(YUVCode {
339 y: [ yblk[yidx], yblk[yidx + 1], yblk[yidx + 4], yblk[yidx + 5] ],
340 u: ublk[i],
341 v: vblk[i],
342 });
343 }
344 }
345 yoff += ystride * 4;
346 uoff += ustride * 2;
347 voff += vstride * 2;
348 }
349 }
350 fn find_nearest(codebook: &[YUVCode], code: YUVCode) -> (u8, u32) {
351 let mut min_dist = std::u32::MAX;
352 let mut idx = 0;
353 for (i, cw) in codebook.iter().enumerate() {
354 let dist = cw.dist(code);
355 if dist < min_dist {
356 min_dist = dist;
357 idx = i;
358 if dist == 0 {
359 break;
360 }
361 }
362 }
363 (idx as u8, min_dist)
364 }
365 fn can_update_cb(new_cb: &[YUVCode], old_cb: &[YUVCode], cb_size: usize) -> bool {
366 let mut skip_count = 0;
367 for (new, old) in new_cb.iter().zip(old_cb.iter()) {
368 if new == old {
369 skip_count += 1;
370 }
371 }
372 let full_size = cb_size * new_cb.len();
373 let upd_size = cb_size * (new_cb.len() - skip_count) + (new_cb.len() + 31) / 32 * 4;
374 upd_size < full_size
375 }
376 fn write_cb(bw: &mut ByteWriter, mut id: u8, new_cb: &[YUVCode], old_cb: &[YUVCode], grayscale: bool, update: bool, num_elem: usize) -> EncoderResult<()> {
377 if grayscale {
378 id |= 4;
379 }
380 if update {
381 id |= 1;
382 }
383 bw.write_byte(id)?;
384 bw.write_u24be(0)?;
385 let chunk_pos = bw.tell();
386 if !update {
387 for entry in new_cb.iter().take(num_elem) {
388 bw.write_buf(&entry.y)?;
389 if !grayscale {
390 bw.write_byte(entry.u ^ 0x80)?;
391 bw.write_byte(entry.v ^ 0x80)?;
392 }
393 }
394 } else {
395 let mut end = num_elem;
396 for (i, (ncw, ocw)) in new_cb.iter().zip(old_cb.iter()).enumerate().take(num_elem).rev() {
397 if ncw == ocw {
398 end = i;
399 } else {
400 break;
401 }
402 }
403 for i in (0..end).step_by(32) {
404 let mut mask = 0;
405 for j in 0..32 {
406 mask <<= 1;
407 if new_cb[i + j] != old_cb[i + j] {
408 mask |= 1;
409 }
410 }
411 bw.write_u32be(mask)?;
412 for j in 0..32 {
413 if new_cb[i + j] == old_cb[i + j] { continue; }
414 bw.write_buf(&new_cb[i + j].y)?;
415 if !grayscale {
416 bw.write_byte(new_cb[i + j].u ^ 0x80)?;
417 bw.write_byte(new_cb[i + j].v ^ 0x80)?;
418 }
419 }
420 }
421 }
422 patch_size(bw, chunk_pos)?;
423 Ok(())
424 }
425 fn render_stripe(&mut self, intra: bool, start: usize, end: usize) {
426 if let Some(ref mut dst_frm) = self.lastfrm {
427 let ystride = dst_frm.get_stride(0);
428 let mut yoff = dst_frm.get_offset(0) + start * ystride;
429 let ustride = dst_frm.get_stride(1);
430 let mut uoff = dst_frm.get_offset(1) + start / 2 * ustride;
431 let vstride = dst_frm.get_stride(2);
432 let mut voff = dst_frm.get_offset(2) + start / 2 * vstride;
433 let (width, _) = dst_frm.get_dimensions(0);
434 let data = dst_frm.get_data_mut().unwrap();
435 let mut miter = self.masks.masks.iter();
436 let mut v1_iter = self.v1_idx.iter();
437 let mut v4_iter = self.v4_idx.iter();
438 let mut cur_mask = 0;
439 let mut cur_bit = 0;
440 for _ in (start..end).step_by(4) {
441 for x in (0..width).step_by(4) {
442 if cur_bit == 0 {
443 if !intra || !self.v1_idx.is_empty() {
444 cur_mask = *miter.next().unwrap();
445 } else {
446 cur_mask = 0xFFFFFFFF;
447 }
448 cur_bit = 1 << 31;
449 }
450 if !intra {
451 if (cur_mask & cur_bit) == 0 {
452 cur_bit >>= 1;
453 continue;
454 }
455 cur_bit >>= 1;
456 if cur_bit == 0 {
457 cur_mask = *miter.next().unwrap();
458 cur_bit = 1 << 31;
459 }
460 }
461 if (cur_mask & cur_bit) == 0 {
462 let idx = *v1_iter.next().unwrap() as usize;
463 let cb = &self.v1_cur_cb[self.cur_strip][idx];
464
465 let mut coff = yoff + x;
466 data[coff] = cb.y[0]; data[coff + 1] = cb.y[0];
467 data[coff + 2] = cb.y[1]; data[coff + 3] = cb.y[1];
468 coff += ystride;
469 data[coff] = cb.y[0]; data[coff + 1] = cb.y[0];
470 data[coff + 2] = cb.y[1]; data[coff + 3] = cb.y[1];
471 coff += ystride;
472 data[coff] = cb.y[2]; data[coff + 1] = cb.y[2];
473 data[coff + 2] = cb.y[3]; data[coff + 3] = cb.y[3];
474 coff += ystride;
475 data[coff] = cb.y[2]; data[coff + 1] = cb.y[2];
476 data[coff + 2] = cb.y[3]; data[coff + 3] = cb.y[3];
477
478 if !self.grayscale {
479 let mut coff = uoff + x / 2;
480 data[coff] = cb.u; data[coff + 1] = cb.u;
481 coff += ustride;
482 data[coff] = cb.u; data[coff + 1] = cb.u;
483
484 let mut coff = voff + x / 2;
485 data[coff] = cb.v; data[coff + 1] = cb.v;
486 coff += vstride;
487 data[coff] = cb.v; data[coff + 1] = cb.v;
488 }
489 } else {
490 let idx0 = *v4_iter.next().unwrap() as usize;
491 let cb0 = &self.v4_cur_cb[self.cur_strip][idx0];
492 let idx1 = *v4_iter.next().unwrap() as usize;
493 let cb1 = &self.v4_cur_cb[self.cur_strip][idx1];
494 let idx2 = *v4_iter.next().unwrap() as usize;
495 let cb2 = &self.v4_cur_cb[self.cur_strip][idx2];
496 let idx3 = *v4_iter.next().unwrap() as usize;
497 let cb3 = &self.v4_cur_cb[self.cur_strip][idx3];
498
499 let mut coff = yoff + x;
500 data[coff] = cb0.y[0]; data[coff + 1] = cb0.y[1];
501 data[coff + 2] = cb1.y[0]; data[coff + 3] = cb1.y[1];
502 coff += ystride;
503 data[coff] = cb0.y[2]; data[coff + 1] = cb0.y[3];
504 data[coff + 2] = cb1.y[2]; data[coff + 3] = cb1.y[3];
505 coff += ystride;
506 data[coff] = cb2.y[0]; data[coff + 1] = cb2.y[1];
507 data[coff + 2] = cb3.y[0]; data[coff + 3] = cb3.y[1];
508 coff += ystride;
509 data[coff] = cb2.y[2]; data[coff + 1] = cb2.y[3];
510 data[coff + 2] = cb3.y[2]; data[coff + 3] = cb3.y[3];
511
512 if !self.grayscale {
513 let mut coff = uoff + x / 2;
514 data[coff] = cb0.u; data[coff + 1] = cb1.u;
515 coff += ustride;
516 data[coff] = cb2.u; data[coff + 1] = cb3.u;
517
518 let mut coff = voff + x / 2;
519 data[coff] = cb0.v; data[coff + 1] = cb1.v;
520 coff += vstride;
521 data[coff] = cb2.v; data[coff + 1] = cb3.v;
522 }
523 }
524 cur_bit >>= 1;
525 }
526 yoff += ystride * 4;
527 uoff += ustride * 2;
528 voff += vstride * 2;
529 }
530 } else {
531 unreachable!();
532 }
533 }
534 fn calc_skip_dist(&mut self, in_frm: &NAVideoBuffer<u8>, start: usize, end: usize) {
535 self.skip_dist.clear();
536 if let Some(ref ref_frm) = self.lastfrm {
537 let rystride = ref_frm.get_stride(0);
538 let mut ryoff = ref_frm.get_offset(0) + start * rystride;
539 let rustride = ref_frm.get_stride(1);
540 let mut ruoff = ref_frm.get_offset(1) + start / 2 * rustride;
541 let rvstride = ref_frm.get_stride(2);
542 let mut rvoff = ref_frm.get_offset(2) + start / 2 * rvstride;
543 let (width, _) = ref_frm.get_dimensions(0);
544 let rdata = ref_frm.get_data();
545
546 let iystride = in_frm.get_stride(0);
547 let mut iyoff = in_frm.get_offset(0) + start * iystride;
548 let iustride = in_frm.get_stride(1);
549 let mut iuoff = in_frm.get_offset(1) + start / 2 * iustride;
550 let ivstride = in_frm.get_stride(2);
551 let mut ivoff = in_frm.get_offset(2) + start / 2 * ivstride;
552 let idata = in_frm.get_data();
553
554 for _ in (start..end).step_by(4) {
555 for x in (0..width).step_by(4) {
556 let mut dist = 0;
557 let mut roff = ryoff + x;
558 let mut ioff = iyoff + x;
559 for _ in 0..4 {
560 for i in 0..4 {
561 let d = i32::from(rdata[roff + i]) - i32::from(idata[ioff + i]);
562 dist += d * d;
563 }
564 roff += rystride;
565 ioff += iystride;
566 }
567 if !self.grayscale {
568 let mut roff = ruoff + x / 2;
569 let mut ioff = iuoff + x / 2;
570 let ud = i32::from(rdata[roff]) - i32::from(idata[ioff]);
571 dist += ud * ud;
572 let ud = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]);
573 dist += ud * ud;
574 roff += rustride; ioff += iustride;
575 let ud = i32::from(rdata[roff]) - i32::from(idata[ioff]);
576 dist += ud * ud;
577 let ud = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]);
578 dist += ud * ud;
579
580 let mut roff = rvoff + x / 2;
581 let mut ioff = ivoff + x / 2;
582 let vd = i32::from(rdata[roff]) - i32::from(idata[ioff]);
583 dist += vd * vd;
584 let vd = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]);
585 dist += vd * vd;
586 roff += rvstride; ioff += ivstride;
587 let vd = i32::from(rdata[roff]) - i32::from(idata[ioff]);
588 dist += vd * vd;
589 let vd = i32::from(rdata[roff + 1]) - i32::from(idata[ioff + 1]);
590 dist += vd * vd;
591 }
592 self.skip_dist.push(dist as u32);
593 }
594
595 iyoff += iystride * 4;
596 iuoff += iustride * 2;
597 ivoff += ivstride * 2;
598 ryoff += rystride * 4;
599 ruoff += rustride * 2;
600 rvoff += rvstride * 2;
601 }
602 } else {
603 unreachable!();
604 }
605 }
606 fn quant_vectors(&mut self) {
607 match self.qmode {
608 QuantMode::ELBG => {
609 let mut elbg_v1: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cb[self.cur_strip]);
610 let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v4_cb[self.cur_strip]);
611
612 for entry in self.v1_cb[self.cur_strip].iter_mut().skip(self.v1_len) {
613 self.rng.fill_entry(entry);
614 }
615 for entry in self.v4_cb[self.cur_strip].iter_mut().skip(self.v4_len) {
616 self.rng.fill_entry(entry);
617 }
618
619 self.v1_len = elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
620 self.v4_len = if !self.force_v1 { elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip]) } else { 0 };
621 },
622 QuantMode::Hybrid => {
623 let v1_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
624 let v4_len = if !self.force_v1 {
625 quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip])
626 } else {
627 0
628 };
629 self.v1_len = if v1_len < 256 {
630 v1_len
631 } else {
632 let mut elbg_v1: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v1_cur_cb[self.cur_strip]);
633 elbg_v1.quantise(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip])
634 };
635 self.v4_len = if v4_len < 256 {
636 v4_len
637 } else {
638 let mut elbg_v4: ELBG<YUVCode, YUVCodeSum> = ELBG::new(&self.v4_cur_cb[self.cur_strip]);
639 elbg_v4.quantise(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip])
640 };
641 },
642 QuantMode::MedianCut => {
643 self.v1_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v1_entries, &mut self.v1_cur_cb[self.cur_strip]);
644 if !self.force_v1 {
645 self.v4_len = quantise_median_cut::<YUVCode, YUVCodeSum>(&self.v4_entries, &mut self.v4_cur_cb[self.cur_strip]);
646 } else {
647 self.v4_len = 0;
648 }
649 },
650 };
651
652 for e in self.v1_cur_cb[self.cur_strip].iter_mut().skip(self.v1_len) { *e = YUVCode::default(); }
653 for e in self.v4_cur_cb[self.cur_strip].iter_mut().skip(self.v4_len) { *e = YUVCode::default(); }
654 }
655 fn encode_intra(&mut self, bw: &mut ByteWriter, in_frm: &NAVideoBuffer<u8>) -> EncoderResult<bool> {
656 let (width, height) = in_frm.get_dimensions(0);
657 let mut strip_h = (height / self.nstrips + 3) & !3;
658 if strip_h == 0 {
659 self.nstrips = 1;
660 strip_h = height;
661 }
662 let mut start_line = 0;
663 let mut end_line = strip_h;
664
665 bw.write_byte(0)?; // intra flag
666 bw.write_u24be(0)?; // frame size
667 let frame_data_pos = bw.tell();
668 bw.write_u16be(width as u16)?;
669 bw.write_u16be(height as u16)?;
670 bw.write_u16be(self.nstrips as u16)?;
671
672 self.cur_strip = 0;
673 for entry in self.v1_cb[self.cur_strip].iter_mut() {
674 self.rng.fill_entry(entry);
675 }
676 for entry in self.v4_cb[self.cur_strip].iter_mut() {
677 self.rng.fill_entry(entry);
678 }
679 while start_line < height {
680 self.read_strip(in_frm, start_line, end_line);
681
682 if self.cur_strip > 0 {
683 self.v1_cb[self.cur_strip] = self.v1_cb[self.cur_strip - 1];
684 self.v4_cb[self.cur_strip] = self.v4_cb[self.cur_strip - 1];
685 }
686 self.quant_vectors();
687 if self.grayscale {
688 for cw in self.v1_cur_cb[self.cur_strip].iter_mut() {
689 cw.u = 128;
690 cw.v = 128;
691 }
692 for cw in self.v4_cur_cb[self.cur_strip].iter_mut() {
693 cw.u = 128;
694 cw.v = 128;
695 }
696 }
697
698 self.v1_idx.clear();
699 self.v4_idx.clear();
700 self.masks.reset();
701
702 for (v1_entry, v4_entries) in self.v1_entries.iter().zip(self.v4_entries.chunks(4)) {
703 let (v1_idx, v1_dist) = Self::find_nearest(&self.v1_cur_cb[self.cur_strip][..self.v1_len], *v1_entry);
704 if v1_dist == 0 || self.force_v1 {
705 self.masks.put_v1();
706 self.v1_idx.push(v1_idx);
707 continue;
708 }
709 let (v40_idx, v40_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[0]);
710 let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[1]);
711 let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[2]);
712 let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[3]);
713 if v40_dist + v41_dist + v42_dist + v43_dist > v1_dist {
714 self.masks.put_v4();
715 self.v4_idx.push(v40_idx);
716 self.v4_idx.push(v41_idx);
717 self.v4_idx.push(v42_idx);
718 self.v4_idx.push(v43_idx);
719 } else {
720 self.masks.put_v1();
721 self.v1_idx.push(v1_idx);
722 }
723 }
724 self.masks.end();
725
726 let mut is_intra_strip = start_line == 0;
727 let (upd_v1, upd_v4) = if !is_intra_strip {
728 let cb_size = if self.grayscale { 4 } else { 6 };
729 (Self::can_update_cb(&self.v1_cur_cb[self.cur_strip][..self.v1_len], &self.v1_cb[self.cur_strip][..self.v1_len], cb_size),
730 Self::can_update_cb(&self.v4_cur_cb[self.cur_strip][..self.v4_len], &self.v4_cb[self.cur_strip][..self.v4_len], cb_size))
731 } else {
732 (false, false)
733 };
734 if !is_intra_strip && !upd_v1 && !upd_v4 {
735 is_intra_strip = true;
736 }
737 bw.write_byte(if is_intra_strip { 0x10 } else { 0x11 })?;
738 bw.write_u24be(0)?; // strip size
739 let strip_data_pos = bw.tell();
740 bw.write_u16be(0)?; // yoff
741 bw.write_u16be(0)?; // xoff
742 bw.write_u16be((end_line - start_line) as u16)?;
743 bw.write_u16be(width as u16)?;
744
745 Self::write_cb(bw, 0x20, &self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], self.grayscale, upd_v4, self.v4_len)?;
746 Self::write_cb(bw, 0x22, &self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], self.grayscale, upd_v1, self.v1_len)?;
747
748 self.render_stripe(true, start_line, end_line);
749
750 if self.v4_idx.is_empty() {
751 bw.write_byte(0x32)?;
752 bw.write_u24be((self.v1_idx.len() + 4) as u32)?;
753 bw.write_buf(self.v1_idx.as_slice())?;
754 } else {
755 bw.write_byte(0x30)?;
756 bw.write_u24be(0)?;
757 let chunk_pos = bw.tell();
758 let mut v1_pos = 0;
759 let mut v4_pos = 0;
760 for _ in 0..32 {
761 self.v1_idx.push(0);
762 self.v4_idx.push(0);
763 self.v4_idx.push(0);
764 self.v4_idx.push(0);
765 self.v4_idx.push(0);
766 }
767 for mask in self.masks.masks.iter() {
768 bw.write_u32be(*mask)?;
769 for j in (0..32).rev() {
770 if (mask & (1 << j)) == 0 {
771 bw.write_byte(self.v1_idx[v1_pos])?;
772 v1_pos += 1;
773 } else {
774 bw.write_byte(self.v4_idx[v4_pos])?;
775 bw.write_byte(self.v4_idx[v4_pos + 1])?;
776 bw.write_byte(self.v4_idx[v4_pos + 2])?;
777 bw.write_byte(self.v4_idx[v4_pos + 3])?;
778 v4_pos += 4;
779 }
780 }
781 }
782 patch_size(bw, chunk_pos)?;
783 }
784
785 patch_size(bw, strip_data_pos)?;
786
787 self.v1_cb[self.cur_strip].copy_from_slice(&self.v1_cur_cb[self.cur_strip]);
788 self.v4_cb[self.cur_strip].copy_from_slice(&self.v4_cur_cb[self.cur_strip]);
789 start_line = end_line;
790 end_line = (end_line + strip_h).min(height);
791
792 self.cur_strip += 1;
793 }
794 patch_size(bw, frame_data_pos)?;
795 Ok(true)
796 }
797 fn encode_inter(&mut self, bw: &mut ByteWriter, in_frm: &NAVideoBuffer<u8>) -> EncoderResult<bool> {
798 let (width, height) = in_frm.get_dimensions(0);
799 let mut strip_h = (height / self.nstrips + 3) & !3;
800 if strip_h == 0 {
801 self.nstrips = 1;
802 strip_h = height;
803 }
804 let mut start_line = 0;
805 let mut end_line = strip_h;
806
807 bw.write_byte(1)?; // intra flag
808 bw.write_u24be(0)?; // frame size
809 let frame_data_pos = bw.tell();
810 bw.write_u16be(width as u16)?;
811 bw.write_u16be(height as u16)?;
812 bw.write_u16be(self.nstrips as u16)?;
813
814 self.cur_strip = 0;
815 while start_line < height {
816 self.read_strip(in_frm, start_line, end_line);
817 self.calc_skip_dist(in_frm, start_line, end_line);
818
819 self.quant_vectors();
820 if self.grayscale {
821 for cw in self.v1_cur_cb[self.cur_strip].iter_mut() {
822 cw.u = 128;
823 cw.v = 128;
824 }
825 for cw in self.v4_cur_cb[self.cur_strip].iter_mut() {
826 cw.u = 128;
827 cw.v = 128;
828 }
829 }
830
831 self.v1_idx.clear();
832 self.v4_idx.clear();
833 self.masks.reset();
834
835 let mut skip_iter = self.skip_dist.iter();
836 for (v1_entry, v4_entries) in self.v1_entries.iter().zip(self.v4_entries.chunks(4)) {
837 let skip_dist = *skip_iter.next().unwrap();
838 if skip_dist == 0 {
839 self.masks.put_inter(true);
840 continue;
841 }
842 let (v1_idx, v1_dist) = Self::find_nearest(&self.v1_cur_cb[self.cur_strip][..self.v1_len], *v1_entry);
843 if skip_dist < v1_dist {
844 self.masks.put_inter(true);
845 continue;
846 } else {
847 self.masks.put_inter(false);
848 }
849 if v1_dist == 0 || self.force_v1 {
850 self.masks.put_v1();
851 self.v1_idx.push(v1_idx);
852 continue;
853 }
854 let (v40_idx, v40_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[0]);
855 let (v41_idx, v41_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[1]);
856 let (v42_idx, v42_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[2]);
857 let (v43_idx, v43_dist) = Self::find_nearest(&self.v4_cur_cb[self.cur_strip][..self.v4_len], v4_entries[3]);
858 if v40_dist + v41_dist + v42_dist + v43_dist > v1_dist {
859 self.masks.put_v4();
860 self.v4_idx.push(v40_idx);
861 self.v4_idx.push(v41_idx);
862 self.v4_idx.push(v42_idx);
863 self.v4_idx.push(v43_idx);
864 } else {
865 self.masks.put_v1();
866 self.v1_idx.push(v1_idx);
867 }
868 }
869 self.masks.end();
870
871 let (upd_v1, upd_v4) = {
872 let cb_size = if self.grayscale { 4 } else { 6 };
873 (Self::can_update_cb(&self.v1_cur_cb[self.cur_strip][..self.v1_len], &self.v1_cb[self.cur_strip][..self.v1_len], cb_size),
874 Self::can_update_cb(&self.v4_cur_cb[self.cur_strip][..self.v4_len], &self.v4_cb[self.cur_strip][..self.v4_len], cb_size))
875 };
876 bw.write_byte(0x11)?;
877 bw.write_u24be(0)?; // strip size
878 let strip_data_pos = bw.tell();
879 bw.write_u16be(0)?; // yoff
880 bw.write_u16be(0)?; // xoff
881 bw.write_u16be((end_line - start_line) as u16)?;
882 bw.write_u16be(width as u16)?;
883
884 Self::write_cb(bw, 0x20, &self.v4_cur_cb[self.cur_strip], &self.v4_cb[self.cur_strip], self.grayscale, upd_v4, self.v4_len)?;
885 Self::write_cb(bw, 0x22, &self.v1_cur_cb[self.cur_strip], &self.v1_cb[self.cur_strip], self.grayscale, upd_v1, self.v1_len)?;
886
887 self.render_stripe(false, start_line, end_line);
888
889 bw.write_byte(0x31)?;
890 bw.write_u24be(0)?;
891 let chunk_pos = bw.tell();
892 let mut v1_pos = 0;
893 let mut v4_pos = 0;
894 for _ in 0..32 {
895 self.v1_idx.push(0);
896 self.v4_idx.push(0);
897 self.v4_idx.push(0);
898 self.v4_idx.push(0);
899 self.v4_idx.push(0);
900 }
901 let mut skip = true;
902 for mask in self.masks.masks.iter() {
903 bw.write_u32be(*mask)?;
904 if *mask == 0 && skip { continue; }
905 let mut bit = 1 << 31;
906 while bit > 0 {
907 if skip {
908 skip = (mask & bit) == 0;
909 bit >>= 1;
910 } else {
911 if (mask & bit) == 0 {
912 bw.write_byte(self.v1_idx[v1_pos])?;
913 v1_pos += 1;
914 } else {
915 bw.write_byte(self.v4_idx[v4_pos])?;
916 bw.write_byte(self.v4_idx[v4_pos + 1])?;
917 bw.write_byte(self.v4_idx[v4_pos + 2])?;
918 bw.write_byte(self.v4_idx[v4_pos + 3])?;
919 v4_pos += 4;
920 }
921 bit >>= 1;
922 skip = true;
923 }
924 }
925 }
926 patch_size(bw, chunk_pos)?;
927
928 patch_size(bw, strip_data_pos)?;
929
930 self.v1_cb[self.cur_strip].copy_from_slice(&self.v1_cur_cb[self.cur_strip]);
931 self.v4_cb[self.cur_strip].copy_from_slice(&self.v4_cur_cb[self.cur_strip]);
932 start_line = end_line;
933 end_line = (end_line + strip_h).min(height);
934
935 self.cur_strip += 1;
936 }
937 patch_size(bw, frame_data_pos)?;
938 Ok(true)
939 }
940 }
941
942 impl NAEncoder for CinepakEncoder {
943 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
944 match encinfo.format {
945 NACodecTypeInfo::None => {
946 Ok(EncodeParameters {
947 format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT)),
948 ..Default::default()
949 })
950 },
951 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
952 NACodecTypeInfo::Video(vinfo) => {
953 let pix_fmt = if vinfo.format == GRAY_FORMAT { GRAY_FORMAT } else { YUV420_FORMAT };
954 let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, false, pix_fmt);
955 let mut ofmt = *encinfo;
956 ofmt.format = NACodecTypeInfo::Video(outinfo);
957 Ok(ofmt)
958 }
959 }
960 }
961 fn get_capabilities(&self) -> u64 { 0 }
962 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
963 match encinfo.format {
964 NACodecTypeInfo::None => Err(EncoderError::FormatError),
965 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
966 NACodecTypeInfo::Video(vinfo) => {
967 if vinfo.format != YUV420_FORMAT && vinfo.format != GRAY_FORMAT {
968 return Err(EncoderError::FormatError);
969 }
970 if ((vinfo.width | vinfo.height) & 3) != 0 {
971 return Err(EncoderError::FormatError);
972 }
973 if (vinfo.width | vinfo.height) >= (1 << 16) {
974 return Err(EncoderError::FormatError);
975 }
976
977 let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format);
978 let info = NACodecInfo::new("cinepak", NACodecTypeInfo::Video(out_info), None);
979 let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
980 stream.set_num(stream_id as usize);
981 let stream = stream.into_ref();
982
983 self.stream = Some(stream.clone());
984 self.quality = encinfo.quality;
985 self.grayscale = vinfo.format != YUV420_FORMAT;
986 let num_blocks = vinfo.width / 2 * vinfo.height / 2;
987 self.v1_entries = Vec::with_capacity(num_blocks);
988 self.v4_entries = Vec::with_capacity(num_blocks * 4);
989 self.v1_idx = Vec::with_capacity(num_blocks);
990 self.v4_idx = Vec::with_capacity(num_blocks * 4);
991 self.skip_dist = Vec::with_capacity(vinfo.width / 4 * vinfo.height / 4);
992
993 let buf = alloc_video_buffer(out_info, 2)?;
994 self.lastfrm = Some(buf.get_vbuf().unwrap());
995
996 Ok(stream)
997 },
998 }
999 }
1000 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
1001 let buf = frm.get_buffer();
1002 if let Some(ref vbuf) = buf.get_vbuf() {
1003 let cur_strips = self.v1_cb.len();
1004 if cur_strips != self.nstrips {
1005 self.frmcount = 0;
1006 }
1007 if cur_strips < self.nstrips {
1008 for _ in cur_strips..self.nstrips {
1009 self.v1_cb.push([YUVCode::default(); 256]);
1010 self.v4_cb.push([YUVCode::default(); 256]);
1011 self.v1_cur_cb.push([YUVCode::default(); 256]);
1012 self.v4_cur_cb.push([YUVCode::default(); 256]);
1013 }
1014 }
1015
1016 let mut dbuf = Vec::with_capacity(4);
1017 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
1018 let mut bw = ByteWriter::new(&mut gw);
1019 let is_intra = if self.frmcount == 0 {
1020 self.encode_intra(&mut bw, vbuf)?
1021 } else {
1022 self.encode_inter(&mut bw, vbuf)?
1023 };
1024 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
1025 self.frmcount += 1;
1026 if self.frmcount == self.key_int {
1027 self.frmcount = 0;
1028 }
1029 Ok(())
1030 } else {
1031 Err(EncoderError::InvalidParameters)
1032 }
1033 }
1034 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
1035 let mut npkt = None;
1036 std::mem::swap(&mut self.pkt, &mut npkt);
1037 Ok(npkt)
1038 }
1039 fn flush(&mut self) -> EncoderResult<()> {
1040 self.frmcount = 0;
1041 Ok(())
1042 }
1043 }
1044
1045 const ENCODER_OPTS: &[NAOptionDefinition] = &[
1046 NAOptionDefinition {
1047 name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
1048 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
1049 NAOptionDefinition {
1050 name: "nstrips", description: "Number of strips per frame (0 - automatic)",
1051 opt_type: NAOptionDefinitionType::Int(Some(0), Some(16)) },
1052 NAOptionDefinition {
1053 name: "quant_mode", description: "Quantisation mode",
1054 opt_type: NAOptionDefinitionType::String(Some(&["elbg", "hybrid", "mediancut"])) },
1055 NAOptionDefinition {
1056 name: "force_v1", description: "Force coarse (V1-only) mode",
1057 opt_type: NAOptionDefinitionType::Bool },
1058 ];
1059
1060 impl NAOptionHandler for CinepakEncoder {
1061 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
1062 fn set_options(&mut self, options: &[NAOption]) {
1063 for option in options.iter() {
1064 for opt_def in ENCODER_OPTS.iter() {
1065 if opt_def.check(option).is_ok() {
1066 match option.name {
1067 KEYFRAME_OPTION => {
1068 if let NAValue::Int(intval) = option.value {
1069 self.key_int = intval as u8;
1070 }
1071 },
1072 "nstrips" => {
1073 if let NAValue::Int(intval) = option.value {
1074 self.nstrips = intval as usize;
1075 }
1076 },
1077 "quant_mode" => {
1078 if let NAValue::String(ref strval) = option.value {
1079 match strval.as_str() {
1080 "elbg" => self.qmode = QuantMode::ELBG,
1081 "hybrid" => self.qmode = QuantMode::Hybrid,
1082 "mediancut" => self.qmode = QuantMode::MedianCut,
1083 _ => {},
1084 };
1085 }
1086 },
1087 "force_v1" => {
1088 if let NAValue::Bool(val) = option.value {
1089 self.force_v1 = val;
1090 }
1091 },
1092 _ => {},
1093 };
1094 }
1095 }
1096 }
1097 }
1098 fn query_option_value(&self, name: &str) -> Option<NAValue> {
1099 match name {
1100 KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
1101 "nstrips" => Some(NAValue::Int(self.nstrips as i64)),
1102 "quant_mode" => Some(NAValue::String(self.qmode.to_string())),
1103 "force_v1" => Some(NAValue::Bool(self.force_v1)),
1104 _ => None,
1105 }
1106 }
1107 }
1108
1109 pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
1110 Box::new(CinepakEncoder::new())
1111 }
1112
1113 #[cfg(test)]
1114 mod test {
1115 use nihav_core::codecs::*;
1116 use nihav_core::demuxers::*;
1117 use nihav_core::muxers::*;
1118 use crate::*;
1119 use nihav_codec_support::test::enc_video::*;
1120
1121 #[test]
1122 fn test_cinepak_encoder() {
1123 let mut dmx_reg = RegisteredDemuxers::new();
1124 generic_register_all_demuxers(&mut dmx_reg);
1125 let mut dec_reg = RegisteredDecoders::new();
1126 generic_register_all_decoders(&mut dec_reg);
1127 let mut mux_reg = RegisteredMuxers::new();
1128 generic_register_all_muxers(&mut mux_reg);
1129 let mut enc_reg = RegisteredEncoders::new();
1130 generic_register_all_encoders(&mut enc_reg);
1131
1132 // sample: https://samples.mplayerhq.hu/V-codecs/UCOD/TalkingHead_352x288.avi
1133 let dec_config = DecoderTestParams {
1134 demuxer: "avi",
1135 in_name: "assets/Misc/TalkingHead_352x288.avi",
1136 stream_type: StreamType::Video,
1137 limit: Some(2),
1138 dmx_reg, dec_reg,
1139 };
1140 let enc_config = EncoderTestParams {
1141 muxer: "avi",
1142 enc_name: "cinepak",
1143 out_name: "cinepak.avi",
1144 mux_reg, enc_reg,
1145 };
1146 let dst_vinfo = NAVideoInfo {
1147 width: 0,
1148 height: 0,
1149 format: YUV420_FORMAT,
1150 flipped: true,
1151 bits: 12,
1152 };
1153 let enc_params = EncodeParameters {
1154 format: NACodecTypeInfo::Video(dst_vinfo),
1155 quality: 0,
1156 bitrate: 0,
1157 tb_num: 0,
1158 tb_den: 0,
1159 flags: 0,
1160 };
1161 //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
1162 test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
1163 &[0x1d4690c8, 0x3b15b4b3, 0xc2df3c7b, 0x1a25b159]);
1164 }
1165 }