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