TrueMotion 1 encoder
[nihav.git] / nihav-duck / src / codecs / truemotion1enc.rs
CommitLineData
0774ed3d
KS
1use std::ops::*;
2use nihav_core::codecs::*;
3use nihav_core::io::byteio::*;
4use super::truemotion1data::*;
5
6#[derive(Clone,Copy,PartialEq)]
7enum BlockMode {
8 FourByFour,
9 TwoByTwo,
10 FourByTwo,
11 TwoByFour,
12}
13
14impl Default for BlockMode {
15 fn default() -> Self {
16 BlockMode::TwoByTwo
17 }
18}
19
20impl FromStr for BlockMode {
21 type Err = ();
22 fn from_str(s: &str) -> Result<BlockMode, Self::Err> {
23 match s {
24 "4x4" => Ok(BlockMode::FourByFour),
25 "2x4" => Ok(BlockMode::TwoByFour),
26 "4x2" => Ok(BlockMode::FourByTwo),
27 "2x2" => Ok(BlockMode::TwoByTwo),
28 _ => Err(()),
29 }
30 }
31}
32
33impl ToString for BlockMode {
34 fn to_string(&self) -> String {
35 match *self {
36 BlockMode::FourByFour => "4x4".to_string(),
37 BlockMode::FourByTwo => "4x2".to_string(),
38 BlockMode::TwoByFour => "2x4".to_string(),
39 BlockMode::TwoByTwo => "2x2".to_string(),
40 }
41 }
42}
43
44#[derive(Clone,Copy,PartialEq)]
45enum OptionMode {
46 Off,
47 On,
48 Auto,
49}
50
51impl Default for OptionMode {
52 fn default() -> Self {
53 OptionMode::Auto
54 }
55}
56
57impl FromStr for OptionMode {
58 type Err = ();
59 fn from_str(s: &str) -> Result<OptionMode, Self::Err> {
60 match s {
61 "off" => Ok(OptionMode::Off),
62 "on" => Ok(OptionMode::On),
63 "auto" => Ok(OptionMode::Auto),
64 _ => Err(()),
65 }
66 }
67}
68
69impl ToString for OptionMode {
70 fn to_string(&self) -> String {
71 match *self {
72 OptionMode::Off => "off".to_string(),
73 OptionMode::On => "on".to_string(),
74 OptionMode::Auto => "auto".to_string(),
75 }
76 }
77}
78
79#[derive(Default,Clone,Copy,PartialEq)]
80struct WorkPixel ([i16; 3]);
81
82impl WorkPixel {
83 fn decorr(self) -> Self {
84 WorkPixel([self.0[0] - self.0[1], self.0[1], self.0[2] - self.0[1]])
85 }
86 fn recon(self) -> Self {
87 WorkPixel([self.0[0] + self.0[1], self.0[1], self.0[2] + self.0[1]])
88 }
89 fn check16(self) -> bool {
90 let full = self.recon();
91 for comp in full.0.iter() {
92 if !(0i16..32i16).contains(comp) {
93 return false;
94 }
95 }
96 true
97 }
98 fn clear_chroma(&mut self) {
99 self.0[0] = 0;
100 self.0[2] = 0;
101 }
102 fn avg(mut self, other: Self) -> Self {
103 self += other;
104 for el in self.0.iter_mut() {
105 *el = (*el + 1) >> 1;
106 }
107 self
108 }
109}
110
111impl std::fmt::Display for WorkPixel {
112 fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> Result<(), std::fmt::Error> {
113 write!(f, "({:3},{:3},{:3})", self.0[0], self.0[1], self.0[2])
114 }
115}
116
117impl Add for WorkPixel {
118 type Output = Self;
119 fn add(self, rhs: Self) -> Self::Output {
120 WorkPixel([self.0[0] + rhs.0[0], self.0[1] + rhs.0[1], self.0[2] + rhs.0[2]])
121 }
122}
123impl AddAssign for WorkPixel {
124 fn add_assign(&mut self, rhs: Self) {
125 self.0[0] += rhs.0[0];
126 self.0[1] += rhs.0[1];
127 self.0[2] += rhs.0[2];
128 }
129}
130impl Sub for WorkPixel {
131 type Output = Self;
132 fn sub(self, rhs: Self) -> Self::Output {
133 WorkPixel([self.0[0] - rhs.0[0], self.0[1] - rhs.0[1], self.0[2] - rhs.0[2]])
134 }
135}
136impl SubAssign for WorkPixel {
137 fn sub_assign(&mut self, rhs: Self) {
138 self.0[0] -= rhs.0[0];
139 self.0[1] -= rhs.0[1];
140 self.0[2] -= rhs.0[2];
141 }
142}
143
144fn load16(frm: &mut [WorkPixel], width: usize, vbuf: &NAVideoBuffer<u16>) {
145 let stride = vbuf.get_stride(0);
146 let src = vbuf.get_data();
147
148 for (dline, sline) in frm.chunks_exact_mut(width).zip(
149 src[vbuf.get_offset(0)..].chunks_exact(stride)) {
150 for (dst, &src) in dline.iter_mut().zip(sline.iter()) {
151 let el = (src & 0x7FFF) as i16;
152 *dst = WorkPixel([el >> 10, (el >> 5) & 0x1F, el & 0x1F]).decorr();
153 }
154 }
155}
156
157fn load24(frm: &mut [WorkPixel], width: usize, vbuf: &NAVideoBuffer<u8>) {
158 let stride = vbuf.get_stride(0);
159 let src = vbuf.get_data();
160
161 for (dline, sline) in frm.chunks_exact_mut(width).zip(
162 src[vbuf.get_offset(0)..].chunks_exact(stride)) {
163 for (dst, src) in dline.iter_mut().zip(sline.chunks_exact(8)) {
164 let pix1 = WorkPixel([i16::from(src[0]), i16::from(src[1]), i16::from(src[2])]);
165 let pix2 = WorkPixel([i16::from(src[4]), i16::from(src[5]), i16::from(src[6])]);
166 *dst = pix1.avg(pix2);
167 }
168 }
169}
170
171const MAX_DIFF: i32 = std::i32::MAX;
172
173fn luma15_delta(pix: &mut WorkPixel, pred: WorkPixel, tgt: WorkPixel, deltas: &[i32; 8], fat_deltas: &[i32; 8]) {
174 let mut tpix = *pix;
175 let tgt_val = i32::from(tgt.0[1]);
176 let base_val = i32::from(pred.0[1]);
177 let mut best_delta = 0;
178 let mut best_diff = MAX_DIFF;
179 if i32::from(pix.0[1].abs()) <= deltas[deltas.len() - 2] {
180 for &delta in deltas.iter() {
181 let diff = (base_val + delta - tgt_val) * (base_val + delta - tgt_val);
182 if diff < best_diff {
183 tpix.0[1] = delta as i16;
184 if (tpix + pred).check16() {
185 best_diff = diff;
186 best_delta = delta as i16;
187 if diff == 0 {
188 break;
189 }
190 }
191 }
192 }
193 }
194 if best_diff == MAX_DIFF {
195 for &f_delta in fat_deltas.iter().skip(1) {
196 for &delta1 in deltas.iter() {
197 let delta = delta1 + f_delta;
198 let diff = (base_val + delta - tgt_val) * (base_val + delta - tgt_val);
199 if diff < best_diff {
200 tpix.0[1] = delta as i16;
201 if (tpix + pred).check16() {
202 best_diff = diff;
203 best_delta = delta as i16;
204 if diff == 0 {
205 break;
206 }
207 }
208 }
209 }
210 }
211 }
212 pix.0[1] = best_delta;
213}
214
215fn chroma15_deltas(pix: &mut [WorkPixel; 2], pred: &[WorkPixel; 2], tgt: &[WorkPixel], component: usize, deltas: &[i32; 8], fat_deltas: &[i32; 8]) {
216 let mut tpix = *pix;
217 let pred_val = [i32::from(pred[0].0[component]), i32::from(pred[1].0[component])];
218 let tgt_val = [i32::from(tgt[0].0[component]), i32::from(tgt[1].0[component])];
219
220 let mut best_delta = 0;
221 let mut best_diff = MAX_DIFF;
222
223 let delta_thr = deltas[deltas.len() - 2];
224 if i32::from(pix[0].0[component].abs()) <= delta_thr &&
225 i32::from(pix[1].0[component].abs()) <= delta_thr {
226 for &delta in deltas.iter() {
227 let diffs = [pred_val[0] + delta - tgt_val[0], pred_val[1] + delta - tgt_val[1]];
228 let diff = diffs[0] * diffs[0] + diffs[1] * diffs[1];
229 if diff < best_diff {
230 tpix[0].0[component] = delta as i16;
231 tpix[1].0[component] = delta as i16;
232 if (tpix[0] + pred[0]).check16() && (tpix[1] + pred[1]).check16() {
233 best_diff = diff;
234 best_delta = delta as i16;
235 if diff == 0 {
236 break;
237 }
238 }
239 }
240 }
241 }
242 if best_diff == MAX_DIFF {
243 for &f_delta in fat_deltas.iter().skip(1) {
244 for &delta1 in deltas.iter() {
245 let delta = delta1 + f_delta;
246 let diffs = [pred_val[0] + delta - tgt_val[0], pred_val[1] + delta - tgt_val[1]];
247 let diff = diffs[0] * diffs[0] + diffs[1] * diffs[1];
248 if diff < best_diff {
249 tpix[0].0[component] = delta as i16;
250 tpix[1].0[component] = delta as i16;
251 if (tpix[0] + pred[0]).check16() && (tpix[1] + pred[1]).check16() {
252 best_diff = diff;
253 best_delta = delta as i16;
254 if diff == 0 {
255 break;
256 }
257 }
258 }
259 }
260 }
261 }
262 pix[0].0[component] = best_delta;
263 pix[1].0[component] = best_delta;
264}
265
266#[derive(Default)]
267struct MaskWriter {
268 data: Vec<u8>,
269 buf: u8,
270 pos: u8,
271}
272
273impl MaskWriter {
274 fn new() -> Self { Self::default() }
275 fn reset(&mut self) {
276 self.data.clear();
277 self.buf = 0;
278 self.pos = 0;
279 }
280 fn write_bit(&mut self, val: bool) {
281 if val {
282 self.buf |= 1 << self.pos;
283 }
284 self.pos += 1;
285 if self.pos == 8 {
286 self.data.push(self.buf);
287 self.buf = 0;
288 self.pos = 0;
289 }
290 }
291 fn flush(&mut self) {
292 if self.pos != 0 {
293 self.data.push(self.buf);
294 }
295 }
296}
297
298struct IndexWriter {
299 dst: Vec<u8>,
300 in_seq: [u8; 4],
301 in_len: usize,
302 cand: u8,
303 table: &'static [u8],
304}
305
306enum SearchResult {
307 Delta(u8),
308 Escape(u8, u8),
309}
310
311impl IndexWriter {
312 fn new() -> Self {
313 Self {
314 dst: Vec::new(),
315 in_seq: [0; 4],
316 in_len: 0,
317 cand: 0,
318 table: DUCK_VECTABLES[0],
319 }
320 }
321 fn reset(&mut self, idx: usize) {
322 self.dst.clear();
323 self.in_len = 0;
324 self.table = DUCK_VECTABLES[idx];
325 }
326 fn flush(&mut self) {
327 if self.in_len > 0 {
328 let idx = self.find(self.in_len).unwrap();
329 assert_eq!(idx, self.cand);
330 self.dst.push(self.cand);
331 self.in_len = 0;
332 }
333 }
334 fn find(&self, len: usize) -> Option<u8> {
335 let mut cur_idx = 0u8;
336 let src = self.table;
337 let mut cb_pos = 0;
338 while cb_pos < src.len() {
339 let entry_len = usize::from(src[cb_pos]) / 2;
340 cb_pos += 1;
341 if entry_len < len {
342 break;
343 }
344 if entry_len == len && src[cb_pos..][..len] == self.in_seq[..len] {
345 return Some(cur_idx.max(1));
346 }
347 cb_pos += entry_len;
348 cur_idx += 1;
349 }
350 None
351 }
352 fn find_delta(dval: i16, deltas: &[i32; 8], fat_deltas: &[i32; 8]) -> SearchResult {
353 if let Some(pos) = deltas.iter().position(|&x| x == i32::from(dval)) {
354 SearchResult::Delta(pos as u8)
355 } else {
356 for (i, &delta1) in deltas.iter().enumerate() {
357 for (j, &f_delta) in fat_deltas.iter().enumerate().skip(1) {
358 let delta = delta1 + f_delta;
359 if delta == i32::from(dval) {
360 return SearchResult::Escape(i as u8, j as u8);
361 }
362 }
363 }
364 unreachable!()
365 }
366 }
367 fn add_deltas(&mut self, d0: i16, d1: i16, deltas: &[i32; 8], fat_deltas: &[i32; 8]) {
368 let idx0 = Self::find_delta(d0, deltas, fat_deltas);
369 let idx1 = Self::find_delta(d1, deltas, fat_deltas);
370 match (idx0, idx1) {
371 (SearchResult::Delta(i0), SearchResult::Delta(i1)) => {
372 self.add(i0, i1);
373 },
374 (SearchResult::Delta(i0), SearchResult::Escape(i10, i11)) => {
375 self.add(i0, i10);
376 self.add_byte(0);
377 self.add(0, i11);
378 },
379 (SearchResult::Escape(i00, i01), SearchResult::Delta(i1)) => {
380 self.add(i00, i1);
381 self.add_byte(0);
382 self.add(i01, 0);
383 },
384 (SearchResult::Escape(i00, i01), SearchResult::Escape(i10, i11)) => {
385 self.add(i00, i10);
386 self.add_byte(0);
387 self.add(i01, i11);
388 },
389 }
390 }
391 fn add(&mut self, idx0: u8, idx1: u8) {
392 let pair = idx0 * 16 + idx1;
393 self.in_seq[self.in_len] = pair;
394 self.in_len += 1;
395 match self.find(self.in_len) {
396 None => {
397 self.dst.push(self.cand);
398 self.in_seq[0] = self.in_seq[self.in_len - 1];
399 self.in_len = 1;
400 self.cand = self.find(self.in_len).unwrap();
401 },
402 Some(idx) if self.in_len == 4 => {
403 self.dst.push(idx);
404 self.in_len = 0;
405 },
406 Some(idx) => {
407 self.cand = idx;
408 }
409 }
410 }
411 fn add_byte(&mut self, val: u8) {
412 self.flush();
413 self.dst.push(val);
414 }
415}
416
417struct TM1Encoder {
418 stream: Option<NAStreamRef>,
419 pkt: Option<NAPacket>,
420 top_line: Vec<WorkPixel>,
421 skip_map: Vec<bool>,
422 frame: Vec<WorkPixel>,
423 prev_frame: Vec<WorkPixel>,
424 width: usize,
425 height: usize,
426 is16: bool,
427 mask: MaskWriter,
428 idx_wr: IndexWriter,
429 tm1type: usize,
430 block_mode: BlockMode,
431 delta_set: usize,
432 table_idx: usize,
433 delta_mode: OptionMode,
434 table_mode: OptionMode,
435 frameno: usize,
436 key_int: usize,
437}
438
439impl TM1Encoder {
440 fn new() -> Self {
441 Self {
442 stream: None,
443 pkt: None,
444 top_line: Vec::new(),
445 skip_map: Vec::new(),
446 frame: Vec::new(),
447 prev_frame: Vec::new(),
448 width: 0,
449 height: 0,
450 is16: false,
451 mask: MaskWriter::new(),
452 idx_wr: IndexWriter::new(),
453 tm1type: 0,
454 block_mode: BlockMode::default(),
455 delta_set: 0,
456 table_idx: 0,
457 delta_mode: OptionMode::Off,
458 table_mode: OptionMode::Off,
459 frameno: 0,
460 key_int: 10,
461 }
462 }
463 fn encode_16(&mut self, is_intra: bool) -> EncoderResult<()> {
464 let (blk_w, blk_h) = if let Some(ref info) = TM1_COMPR_TYPES[self.tm1type] {
465 (info.block_w, info.block_h)
466 } else {
467 unreachable!();
468 };
469 self.mask.reset();
470 self.idx_wr.reset(self.table_idx);
471 for el in self.top_line.iter_mut() {
472 *el = WorkPixel::default();
473 }
474
475 let y_deltas = &DUCK_Y_DELTAS [self.delta_set];
476 let yf_deltas = &DUCK_Y_DELTAS_5[self.delta_set];
477 let c_deltas = &DUCK_C_DELTAS [self.delta_set];
478 let cf_deltas = &DUCK_C_DELTAS_5[self.delta_set];
479
480 let mut has_c = [[false; 2]; 4];
481 has_c[0][0] = true;
482 if blk_w == 2 {
483 has_c[0][1] = true;
484 }
485 if blk_h == 2 {
486 has_c[2] = has_c[0];
487 }
488
489 if !is_intra {
490 self.skip_map.clear();
491 for (stripe, pstripe) in self.frame.chunks_exact(self.width * 4).zip(
492 self.prev_frame.chunks_exact(self.width * 4)) {
493 for x in (0..self.width).step_by(4) {
494 let mut skip = true;
495 'cmp_loop: for (line, pline) in stripe[x..].chunks(self.width).zip(
496 pstripe[x..].chunks(self.width)) {
497 for (&a, &b) in line[..4].iter().zip(pline[..4].iter()) {
498 if a != b {
499 skip = false;
500 break 'cmp_loop;
501 }
502 }
503 }
504 self.mask.write_bit(skip);
505 self.skip_map.push(skip);
506 }
507 }
508 }
509
510 let mut skip_blk_addr = 0;
511 for (y, line) in self.frame.chunks_exact_mut(self.width).enumerate() {
512 let mut hpred = [WorkPixel::default(); 2];
513
514 for (x, (pair, vpred)) in line.chunks_exact_mut(2).zip(
515 self.top_line.chunks_exact_mut(2)).enumerate() {
516 if !is_intra && self.skip_map[skip_blk_addr + x / 2] {
517 pair[0] = self.prev_frame[x * 2 + y * self.width];
518 pair[1] = self.prev_frame[x * 2 + 1 + y * self.width];
519 hpred[0] = pair[0] - vpred[0];
520 hpred[1] = pair[1] - vpred[1];
521 vpred.copy_from_slice(&pair);
522 continue;
523 }
524 let pval = [vpred[0] + hpred[0], vpred[1] + hpred[1]];
525 let mut deltas = [pair[0] - pval[0], pair[1] - pval[1]];
526 if has_c[y & 3][x & 1] {
527 chroma15_deltas(&mut deltas, &pval, pair, 0, c_deltas, cf_deltas);
528 chroma15_deltas(&mut deltas, &pval, pair, 2, c_deltas, cf_deltas);
529 self.idx_wr.add_deltas(deltas[0].0[0], deltas[0].0[2], c_deltas, cf_deltas);
530 } else {
531 deltas[0].clear_chroma();
532 deltas[1].clear_chroma();
533 }
534 luma15_delta(&mut deltas[0], pval[0], pair[0], y_deltas, yf_deltas);
535 luma15_delta(&mut deltas[1], pval[1], pair[1], y_deltas, yf_deltas);
536 self.idx_wr.add_deltas(deltas[0].0[1], deltas[1].0[1], y_deltas, yf_deltas);
537
538 hpred[0] += deltas[0];
539 hpred[1] += deltas[1];
540 vpred[0] += hpred[0];
541 vpred[1] += hpred[1];
542 pair[0] = vpred[0];
543 pair[1] = vpred[1];
544 }
545
546 if (y & 3) == 3 {
547 skip_blk_addr += self.width / 4;
548 }
549 }
550
551 Ok(())
552 }
553}
554
555impl NAEncoder for TM1Encoder {
556 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
557 match encinfo.format {
558 NACodecTypeInfo::None => {
559 Ok(EncodeParameters {
560 format: NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, BGR0_FORMAT)),
561 ..Default::default()
562 })
563 },
564 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
565 NACodecTypeInfo::Video(vinfo) => {
566 /*let in_fmt = vinfo.format;
567 let pix_fmt = if in_fmt.model.is_rgb() && in_fmt.get_total_depth() <= 16 {
568 RGB555_FORMAT
569 } else {
570 BGR0_FORMAT
571 };*/
572 let pix_fmt = RGB555_FORMAT;
573 let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, false, pix_fmt);
574 let mut ofmt = *encinfo;
575 ofmt.format = NACodecTypeInfo::Video(outinfo);
576 Ok(ofmt)
577 }
578 }
579 }
580 fn get_capabilities(&self) -> u64 { ENC_CAPS_PARAMCHANGE }
581 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
582 match encinfo.format {
583 NACodecTypeInfo::None => Err(EncoderError::FormatError),
584 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
585 NACodecTypeInfo::Video(vinfo) => {
586 if vinfo.format != BGR0_FORMAT && vinfo.format != RGB555_FORMAT {
587 return Err(EncoderError::FormatError);
588 }
589 if vinfo.format == BGR0_FORMAT {
590 return Err(EncoderError::NotImplemented);
591 }
592 if ((vinfo.width | vinfo.height) & 3) != 0 {
593 return Err(EncoderError::FormatError);
594 }
595 if (vinfo.width | vinfo.height) >= (1 << 10) {
596 return Err(EncoderError::FormatError);
597 }
598
599 let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format);
600 let info = NACodecInfo::new("truemotion1", NACodecTypeInfo::Video(out_info), None);
601 let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
602 stream.set_num(stream_id as usize);
603 let stream = stream.into_ref();
604
605 self.stream = Some(stream.clone());
606
607 Ok(stream)
608 },
609 }
610 }
611 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
612 let buf = frm.get_buffer();
613
614 let mut meta_type = 2;
615 let mut is_intra = meta_type == 1 || self.frameno == 0;
616
617 if let Some(vinfo) = buf.get_video_info() {
618 if vinfo.width != self.width || vinfo.height != self.height {
619 self.top_line.resize(vinfo.width, WorkPixel::default());
620 self.skip_map.resize((vinfo.width / 4) * (vinfo.height / 4), false);
621 self.frame.resize(vinfo.width * vinfo.height, WorkPixel::default());
622 self.prev_frame.resize(vinfo.width * vinfo.height, WorkPixel::default());
623 self.width = vinfo.width;
624 self.height = vinfo.height;
625 is_intra = true;
626 if meta_type == 1 && self.width < 213 && self.height >= 176 {
627 // switch to a newer version in order to avoid being an interpolated frame
628 meta_type = 2;
629 }
630 }
631 } else {
632 return Err(EncoderError::InvalidParameters);
633 }
634
635 let old_is16 = self.is16;
636 match buf {
637 NABufferType::Video(ref vbuf) | NABufferType::VideoPacked(ref vbuf) => {
638 self.is16 = false;
639 load24(&mut self.frame, self.width / 2, vbuf); // 24-bit video is half-size
640 },
641 NABufferType::Video16(ref vbuf16) => {
642 self.is16 = true;
643 load16(&mut self.frame, self.width, vbuf16);
644 },
645 _ => return Err(EncoderError::InvalidParameters),
646 };
647 if old_is16 != self.is16 {
648 is_intra = true;
649 }
650
651 let mut dbuf = Vec::with_capacity(4);
652 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
653 let mut bw = ByteWriter::new(&mut gw);
654
655 self.tm1type = match (self.is16, self.block_mode) {
656 (true, BlockMode::FourByFour) => 2,
657 (true, BlockMode::FourByTwo) => 4,
658 (true, BlockMode::TwoByFour) => 6,
659 (true, BlockMode::TwoByTwo) => 8,
660 (false, BlockMode::FourByFour) => 10,
661 (false, BlockMode::FourByTwo) => 12,
662 (false, BlockMode::TwoByFour) => 14,
663 (false, BlockMode::TwoByTwo) => 16,
664 };
665 self.delta_set = if self.is16 { 0 } else { 3 };
666 if self.delta_mode == OptionMode::On && self.delta_set < 3 {
667 self.delta_set += 1;
668 }
669 if (self.tm1type & 1) != 0 && meta_type != 0 {
670 self.table_idx = 0;
671 } else {
672 self.table_idx = if self.is16 {
673 0
674 } else {
675 match self.table_mode {
676 OptionMode::Off => 1,
677 OptionMode::On => 2,
678 OptionMode::Auto => self.delta_set.saturating_sub(1),
679 }
680 };
681 }
682
683 bw.write_byte(0)?; // header size
684 bw.write_byte(self.tm1type as u8)?;
685 bw.write_byte(self.delta_set as u8)?;
686 bw.write_byte(self.table_idx as u8 + 1)?;
687 bw.write_u16le(self.height as u16)?;
688 bw.write_u16le(self.width as u16)?;
689 bw.write_u16le(0)?; // checksum
690 bw.write_byte(2)?; // version
691 bw.write_byte(meta_type)?;
692 if meta_type == 2 {
693 let flags = if is_intra { 0x10 } else { 0x08 };
694 bw.write_byte(flags)?;
695 bw.write_byte(1 << 4)?; // control - PC flavour
696 }
697
698 if self.is16 {
699 self.encode_16(is_intra)?;
700 } else {
701 unimplemented!();
702 }
703 std::mem::swap(&mut self.frame, &mut self.prev_frame);
704 self.frameno += 1;
705 if self.frameno >= self.key_int {
706 self.frameno = 0;
707 }
708
709 let hdr_size = bw.tell() as usize;
710
711 if !is_intra {
712 self.mask.flush();
713 dbuf.extend_from_slice(&self.mask.data);
714 }
715 self.idx_wr.add_byte(1); // TM1 need a non-zero code at the end to make sure it's not an escape
716 dbuf.extend_from_slice(&self.idx_wr.dst);
717
718 dbuf[0] = ((hdr_size | 0x80) as u8).rotate_right(3);
719 for i in (1..hdr_size).rev() {
720 dbuf[i] ^= dbuf[i + 1];
721 }
722
723 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
724 Ok(())
725 }
726 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
727 let mut npkt = None;
728 std::mem::swap(&mut self.pkt, &mut npkt);
729 Ok(npkt)
730 }
731 fn flush(&mut self) -> EncoderResult<()> {
732 Ok(())
733 }
734}
735
736const ENCODER_OPTS: &[NAOptionDefinition] = &[
737 NAOptionDefinition {
738 name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
739 opt_type: NAOptionDefinitionType::Int(Some(0), Some(64)) },
740 NAOptionDefinition {
741 name: "delta_mode", description: "Alternative delta mode",
742 opt_type: NAOptionDefinitionType::String(Some(&["off", "on", "auto"])) },
743 NAOptionDefinition {
744 name: "table_mode", description: "Alternative table mode",
745 opt_type: NAOptionDefinitionType::String(Some(&["off", "on", "auto"])) },
746 NAOptionDefinition {
747 name: "block_mode", description: "Block mode",
748 opt_type: NAOptionDefinitionType::String(Some(&["2x2", "2x4", "4x2", "4x4"])) },
749];
750
751impl NAOptionHandler for TM1Encoder {
752 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
753 fn set_options(&mut self, options: &[NAOption]) {
754 for option in options.iter() {
755 for opt_def in ENCODER_OPTS.iter() {
756 if opt_def.check(option).is_ok() {
757 match option.name {
758 KEYFRAME_OPTION => {
759 if let NAValue::Int(val) = option.value {
760 self.key_int = val as usize;
761 }
762 },
763 "delta_mode" => {
764 if let NAValue::String(ref val) = option.value {
765 self.delta_mode = val.parse::<OptionMode>().unwrap();
766 }
767 },
768 "table_mode" => {
769 if let NAValue::String(ref val) = option.value {
770 self.table_mode = val.parse::<OptionMode>().unwrap();
771 }
772 },
773 "block_mode" => {
774 if let NAValue::String(ref val) = option.value {
775 self.block_mode = val.parse::<BlockMode>().unwrap();
776 }
777 },
778 _ => {},
779 };
780 }
781 }
782 }
783 }
784 fn query_option_value(&self, name: &str) -> Option<NAValue> {
785 match name {
786 KEYFRAME_OPTION => Some(NAValue::Int(self.key_int as i64)),
787 "delta_mode" => Some(NAValue::String(self.delta_mode.to_string())),
788 "table_mode" => Some(NAValue::String(self.table_mode.to_string())),
789 "block_mode" => Some(NAValue::String(self.block_mode.to_string())),
790 _ => None,
791 }
792 }
793}
794
795pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
796 Box::new(TM1Encoder::new())
797}
798
799#[cfg(test)]
800mod test {
801 use nihav_core::codecs::*;
802 use nihav_core::demuxers::*;
803 use nihav_core::muxers::*;
804 use crate::*;
805 use nihav_commonfmt::*;
806 use nihav_codec_support::test::enc_video::*;
807 use super::super::truemotion1data::{RGB555_FORMAT, BGR0_FORMAT};
808
809 #[allow(unused_variables)]
810 fn encode_test(name: &'static str, enc_options: &[NAOption], hash: &[u32; 4], is16: bool) {
811 let mut dmx_reg = RegisteredDemuxers::new();
812 generic_register_all_demuxers(&mut dmx_reg);
813 let mut dec_reg = RegisteredDecoders::new();
814 duck_register_all_decoders(&mut dec_reg);
815 let mut mux_reg = RegisteredMuxers::new();
816 generic_register_all_muxers(&mut mux_reg);
817 let mut enc_reg = RegisteredEncoders::new();
818 duck_register_all_encoders(&mut enc_reg);
819
820 // sample: https://samples.mplayerhq.hu/V-codecs/TM20/tm20.avi
821 let dec_config = DecoderTestParams {
822 demuxer: "avi",
823 in_name: "assets/Duck/tm20.avi",
824 stream_type: StreamType::Video,
825 limit: Some(2),
826 dmx_reg, dec_reg,
827 };
828 let enc_config = EncoderTestParams {
829 muxer: "avi",
830 enc_name: "truemotion1",
831 out_name: name,
832 mux_reg, enc_reg,
833 };
834 let dst_vinfo = NAVideoInfo {
835 width: 0,
836 height: 0,
837 format: if is16 { RGB555_FORMAT } else { BGR0_FORMAT },
838 flipped: false,
839 bits: if is16 { 16 } else { 24 },
840 };
841 let enc_params = EncodeParameters {
842 format: NACodecTypeInfo::Video(dst_vinfo),
843 quality: 0,
844 bitrate: 0,
845 tb_num: 0,
846 tb_den: 0,
847 flags: 0,
848 };
849 //test_encoding_to_file(&dec_config, &enc_config, enc_params, enc_options);
850 test_encoding_md5(&dec_config, &enc_config, enc_params, enc_options, hash);
851 }
852 #[test]
853 fn test_truemotion1_encoder_rgb15_4x4() {
854 let enc_options = &[
855 NAOption { name: "key_int", value: NAValue::Int(8) },
856 NAOption { name: "block_mode", value: NAValue::String("4x4".to_string()) },
857 ];
858 encode_test("tm1-15.avi", enc_options, &[0x6b8a5d15, 0xc9c9a391, 0x588c95c5, 0x5568d3b3], true);
859 }
860 #[test]
861 fn test_truemotion1_encoder_rgb15_4x2() {
862 let enc_options = &[
863 NAOption { name: "key_int", value: NAValue::Int(8) },
864 NAOption { name: "block_mode", value: NAValue::String("4x2".to_string()) },
865 ];
866 encode_test("tm1-15.avi", enc_options, &[0x999c2ffd, 0xd637f7a3, 0x4ebc070a, 0xef6fca4b], true);
867 }
868 #[test]
869 fn test_truemotion1_encoder_rgb15_2x4() {
870 let enc_options = &[
871 NAOption { name: "key_int", value: NAValue::Int(8) },
872 NAOption { name: "block_mode", value: NAValue::String("2x4".to_string()) },
873 ];
874 encode_test("tm1-15.avi", enc_options, &[0xfe62d1e6, 0xbdd8f28b, 0xf7fd810e, 0x0a9142f1], true);
875 }
876 #[test]
877 fn test_truemotion1_encoder_rgb15_2x2() {
878 let enc_options = &[
879 NAOption { name: "key_int", value: NAValue::Int(8) },
880 NAOption { name: "block_mode", value: NAValue::String("2x2".to_string()) },
881 ];
882 encode_test("tm1-15.avi", enc_options, &[0x3b445eb8, 0xac7cab31, 0x4c2ce978, 0x9b698658], true);
883 }
884 /*#[test]
885 fn test_truemotion1_encoder_rgb24() {
886 let enc_options = &[
887 NAOption { name: "key_int", value: NAValue::Int(0) },
888 ];
889 encode_test("tm1-24.avi", enc_options, &[0x36cf8f48, 0x3e8ff2ce, 0x6f3822cf, 0xf7fbf19d], false);
890panic!("end");
891 }*/
892}
893
894// fat deltas for 15-bit mode
895#[allow(clippy::neg_multiply)]
896const DUCK_Y_DELTAS_5: [[i32; 8]; 4] = [
897 [ 0, -1 * 5, 1 * 5, -3 * 5, 3 * 5, -6 * 5, 6 * 5, -6 * 5 ],
898 [ 0, -1 * 5, 2 * 5, -3 * 5, 4 * 5, -6 * 5, 6 * 5, -6 * 5 ],
899 [ 2 * 5, -3 * 5, 10 * 5, -10 * 5, 23 * 5, -23 * 5, 47 * 5, -47 * 5 ],
900 [ 0, -2 * 5, 2 * 5, -8 * 5, 8 * 5, -18 * 5, 18 * 5, -40 * 5 ]
901];
902#[allow(clippy::neg_multiply)]
903const DUCK_C_DELTAS_5: [[i32; 8]; 4] = [
904 [ 0, -1 * 5, 1 * 5, -2 * 5, 3 * 5, -4 * 5, 5 * 5, -4 * 5 ],
905 [ 0, -1 * 5, 1 * 5, -2 * 5, 3 * 5, -4 * 5, 5 * 5, -4 * 5 ],
906 [ 0, -4 * 5, 3 * 5, -16 * 5, 20 * 5, -32 * 5, 36 * 5, -32 * 5 ],
907 [ 0, -2 * 5, 2 * 5, -8 * 5, 8 * 5, -18 * 5, 18 * 5, -40 * 5 ]
908];