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