]> git.nihav.org Git - nihav.git/blame - nihav-indeo/src/codecs/indeo5.rs
registry: add common QuickTime codecs
[nihav.git] / nihav-indeo / src / codecs / indeo5.rs
CommitLineData
5641dccf
KS
1use nihav_core::io::bitreader::*;
2use nihav_core::formats;
3use nihav_core::frame::*;
4use nihav_core::codecs::*;
b4d5b851 5use nihav_codec_support::codecs::ZIGZAG;
01c971c5
KS
6use super::ivi::*;
7use super::ivibr::*;
8
9fn calc_quant(glob_q: u32, qd: i16) -> usize {
10 let qq = (glob_q as i16) + (qd as i16);
11 if qq < 0 {
12 0
13 } else if qq > 23 {
14 23
15 } else {
16 qq as usize
17 }
18}
19
20struct Indeo5Parser {
21 mb_cb: IVICodebook,
22
23 width: usize,
24 height: usize,
25 tile_w: usize,
26 tile_h: usize,
27 luma_bands: usize,
28 chroma_bands: usize,
29
30 is_hpel: [bool; 5],
31 mb_size: [usize; 5],
32 blk_size: [usize; 5],
33}
34
35impl Indeo5Parser {
36 fn new() -> Self {
37 Indeo5Parser {
38 mb_cb: IVI_CB_ZERO,
39
40 width: 0,
41 height: 0,
42 tile_w: 0,
43 tile_h: 0,
44 luma_bands: 0,
45 chroma_bands: 0,
46
47 is_hpel: [false; 5],
48 mb_size: [0; 5],
49 blk_size: [0; 5],
50 }
51 }
52}
53
54fn skip_extension(br: &mut BitReader) -> DecoderResult<()> {
55 loop {
56 let len = br.read(8)?;
57 if len == 0 { break; }
58 br.skip(len * 8)?;
59 }
60 Ok(())
61}
62
63impl IndeoXParser for Indeo5Parser {
64#[allow(unused_variables)]
65#[allow(unused_assignments)]
66 fn decode_picture_header(&mut self, br: &mut BitReader) -> DecoderResult<PictureHeader> {
67 let sync = br.read(5)?;
68 validate!(sync == 0x1F);
69 let ftype_idx = br.read(3)?;
70 validate!(ftype_idx < 5);
71 let ftype = INDEO5_FRAME_TYPE[ftype_idx as usize];
72 let fnum = br.read(8)?;
73 if ftype == IVIFrameType::Intra {
74 let gop_flags = br.read(8)?;
75 let hdr_size;
76 if (gop_flags & 0x01) != 0 {
77 hdr_size = br.read(16)?;
78 } else {
79 hdr_size = 0;
80 }
81 if (gop_flags & 0x20) != 0 {
82 br.skip(32)?; // lock word
83 }
84 self.tile_w = 0;
85 self.tile_h = 0;
86 if (gop_flags & 0x40) != 0 {
87 self.tile_w = 64 << br.read(2)?;
88 self.tile_h = self.tile_w;
89 }
90 validate!(self.tile_w < 256);
91 self.luma_bands = (br.read(2)? * 3 + 1) as usize;
92 self.chroma_bands = (br.read(1)? * 3 + 1) as usize;
93 validate!((self.luma_bands == 4) || (self.luma_bands == 1));
94 validate!(self.chroma_bands == 1);
95 let pic_size_idx = br.read(4)? as usize;
96 let w;
97 let h;
98 if pic_size_idx < 15 {
99 w = INDEO5_PICTURE_SIZE_TAB[pic_size_idx][0];
100 h = INDEO5_PICTURE_SIZE_TAB[pic_size_idx][1];
101 } else {
102 h = br.read(13)? as usize;
103 w = br.read(13)? as usize;
104 }
105 validate!((w != 0) && (h != 0));
106 self.width = w;
107 self.height = h;
108
109 validate!((gop_flags & 0x02) == 0);
110 if self.tile_w == 0 {
111 self.tile_w = w;
112 self.tile_h = h;
113 }
114 for b in 0..self.luma_bands+self.chroma_bands {
115 self.is_hpel[b] = br.read_bool()?;
116 let mb_scale = br.read(1)?;
117 self.blk_size[b] = 8 >> br.read(1)?;
118 self.mb_size[b] = self.blk_size[b] << (1 - mb_scale);
119 let ext_tr = br.read_bool()?;
120 validate!(!ext_tr);
121 let end_marker = br.read(2)?;
122 validate!(end_marker == 0);
123 }
124 if (gop_flags & 0x08) != 0 {
125 let align = br.read(3)?;
126 validate!(align == 0);
127 if br.read_bool()? {
128 br.skip(24)?; // transparency color
129 }
130 }
131 br.align();
132 br.skip(23)?;
133 if br.read_bool()? { // gop extension
134 loop {
135 let v = br.read(16)?;
136 if (v & 0x8000) == 0 { break; }
137 }
138 }
139 br.align();
140 }
141 if ftype.is_null() {
142 br.align();
143 return Ok(PictureHeader::new_null(ftype));
144 }
145 let flags = br.read(8)?;
146 let size;
147 if (flags & 0x01) != 0 {
148 size = br.read(24)?;
149 } else {
150 size = 0;
151 }
152 let checksum;
153 if (flags & 0x10) != 0 {
154 checksum = br.read(16)?;
155 } else {
156 checksum = 0;
157 }
158 if (flags & 0x20) != 0 {
159 skip_extension(br)?;
160 }
161 let in_q = (flags & 0x08) != 0;
162 self.mb_cb = br.read_ivi_codebook_desc(true, (flags & 0x40) != 0)?;
163 br.skip(3)?;
164 br.align();
165
166 Ok(PictureHeader::new(ftype, self.width, self.height, self.tile_w, self.tile_h, false, self.luma_bands, self.chroma_bands, in_q))
167 }
168
169#[allow(unused_variables)]
170 fn decode_band_header(&mut self, br: &mut BitReader, pic_hdr: &PictureHeader, plane_no: usize, band_no: usize) -> DecoderResult<BandHeader> {
171 let band_flags = br.read(8)?;
172
173 if (band_flags & 0x01) != 0 {
174 br.align();
175 return Ok(BandHeader::new_empty(plane_no, band_no));
176 }
177 let inherit_mv = (band_flags & 0x02) != 0;
178 let has_qdelta = (band_flags & 0x04) != 0;
179 let inherit_qd = ((band_flags & 0x08) != 0) || !has_qdelta;
180 let data_size: usize;
181 if (band_flags & 0x80) != 0 {
182 data_size = br.read(24)? as usize;
183 } else {
184 data_size = 0;
185 }
186 validate!(data_size <= ((br.left() / 8) as usize));
187
188 let num_corr: usize;
189 let mut corr_map: [u8; CORR_MAP_SIZE] = [0; CORR_MAP_SIZE];
190 if (band_flags & 0x10) != 0 {
191 num_corr = br.read(8)? as usize;
192 validate!(num_corr*2 <= CORR_MAP_SIZE);
193 for i in 0..num_corr*2 {
194 corr_map[i] = br.read(8)? as u8;
195 }
196 } else {
197 num_corr = 0;
198 }
199 let rvmap_idx;
200 if (band_flags & 0x40) != 0 {
201 rvmap_idx = br.read(3)? as usize;
202 } else {
203 rvmap_idx = 8;
204 }
205 let blk_cb = br.read_ivi_codebook_desc(false, (band_flags & 0x80) != 0)?;
206 if br.read_bool()? {
207 br.skip(16)?; // checksum
208 }
209 let band_q = br.read(5)?;
210 if (band_flags & 0x20) != 0 {
211 skip_extension(br)?;
212 }
213 br.align();
214
215 let tr;
216 let txtype;
217 let band_id = if plane_no == 0 { band_no } else { self.luma_bands };
218 match plane_no {
219 0 => {
220 let scan = INDEO5_SCAN8X8[band_no];
221 let qintra;
222 let qinter;
223 validate!(self.blk_size[band_id] == 8);
224 match band_no {
225 0 => {
226 tr = IVITransformType::Slant(TSize::T8x8, TDir::TwoD);
227 if self.luma_bands == 1 {
228 qintra = INDEO5_Q8_INTRA[0];
229 qinter = INDEO5_Q8_INTER[0];
230 } else {
231 qintra = INDEO5_Q8_INTRA[1];
232 qinter = INDEO5_Q8_INTER[1];
233 }
234 },
235 1 => {
236 tr = IVITransformType::Slant(TSize::T8x8, TDir::Row);
237 qintra = INDEO5_Q8_INTRA[2];
238 qinter = INDEO5_Q8_INTER[2];
239 },
240 2 => {
241 tr = IVITransformType::Slant(TSize::T8x8, TDir::Col);
242 qintra = INDEO5_Q8_INTRA[3];
243 qinter = INDEO5_Q8_INTER[3];
244 },
245 3 => {
246 tr = IVITransformType::None(TSize::T8x8);
247 qintra = INDEO5_Q8_INTRA[4];
248 qinter = INDEO5_Q8_INTER[4];
249 },
250 _ => { unreachable!(); }
251 };
252 txtype = TxType::Transform8(TxParams8x8::new(qintra, qinter, scan));
253 },
254 1 | 2 => {
255 validate!(self.blk_size[band_id] == 4);
256 tr = IVITransformType::Slant(TSize::T4x4, TDir::TwoD);
257 let scan = INDEO5_SCAN4X4;
258 let qintra = INDEO5_Q4_INTRA;
259 let qinter = INDEO5_Q4_INTER;
260 txtype = TxType::Transform4(TxParams4x4::new(qintra, qinter, scan));
261 },
262 _ => { unreachable!(); }
263 };
1a151e53 264
01c971c5
KS
265 Ok(BandHeader::new(plane_no, band_no, self.mb_size[band_id], self.blk_size[band_id], self.is_hpel[band_id], inherit_mv, has_qdelta, inherit_qd, band_q, rvmap_idx, num_corr, corr_map, blk_cb, tr, txtype))
266 }
267
08a1fab7 268 fn decode_mb_info(&mut self, br: &mut BitReader, pic_hdr: &PictureHeader, band: &BandHeader, tile: &mut IVITile, ref_tile: Option<&IVITile>, mv_scale: u8) -> DecoderResult<()> {
01c971c5
KS
269 let mut mv_x = 0;
270 let mut mv_y = 0;
271 let band_id = if pic_hdr.luma_bands == 4 { band.band_no + 1 } else { 0 };
272 let mut mb_idx = 0;
273 for mb_y in 0..tile.mb_h {
274 for mb_x in 0..tile.mb_w {
275 let mut mb = MB::new(tile.pos_x + mb_x * band.mb_size, tile.pos_y + mb_y * band.mb_size);
276 if !br.read_bool()? {
277 if pic_hdr.ftype.is_intra() {
278 mb.mtype = MBType::Intra;
279 } else if band.inherit_mv {
280 if let Some(ref tileref) = ref_tile {
281 mb.mtype = tileref.mb[mb_idx].mtype;
282 } else {
283 return Err(DecoderError::MissingReference);
284 }
285 } else {
286 mb.mtype = if br.read_bool()? { MBType::Inter } else { MBType::Intra };
287 }
288 if band.mb_size == band.blk_size {
289 mb.cbp = br.read(1)? as u8;
290 } else {
291 mb.cbp = br.read(4)? as u8;
292 }
293 let q;
294 if band.has_qdelta {
295 if band.inherit_qd {
296 if let Some(ref tileref) = ref_tile {
297 mb.qd = tileref.mb[mb_idx].qd;
298 q = calc_quant(band.quant, mb.qd);
299 } else {
300 return Err(DecoderError::MissingReference);
301 }
302 } else if (mb.cbp != 0) || ((band.plane_no == 0) && (band.band_no == 0) && pic_hdr.in_q) {
303 mb.qd = br.read_ivi_cb_s(&self.mb_cb)? as i16;
304 q = calc_quant(band.quant, mb.qd);
305 } else {
306 q = band.quant as usize;
307 }
308 } else {
309 q = band.quant as usize;
310 }
311
312 if mb.mtype == MBType::Intra {
313 if band.blk_size == 8 {
314 mb.q = INDEO5_QSCALE8_INTRA[band_id][q];
315 } else {
316 mb.q = INDEO5_QSCALE4_INTRA[q];
317 }
318 } else {
319 if band.blk_size == 8 {
320 mb.q = INDEO5_QSCALE8_INTER[band_id][q];
321 } else {
322 mb.q = INDEO5_QSCALE4_INTER[q];
323 }
324 }
325
326 if mb.mtype != MBType::Intra {
327 if band.inherit_mv {
328 if let Some(ref tileref) = ref_tile {
329 let mx = tileref.mb[mb_idx].mv_x;
330 let my = tileref.mb[mb_idx].mv_y;
331 if mv_scale == 0 {
332 mb.mv_x = mx;
333 mb.mv_y = my;
334 } else {
335 mb.mv_x = scale_mv(mx, mv_scale);
336 mb.mv_y = scale_mv(my, mv_scale);
337 }
338 }
339 } else {
340 mv_y += br.read_ivi_cb_s(&self.mb_cb)?;
341 mv_x += br.read_ivi_cb_s(&self.mb_cb)?;
342 mb.mv_x = mv_x;
343 mb.mv_y = mv_y;
344 }
345 }
346 } else {
347 validate!(!pic_hdr.ftype.is_intra());
348 mb.mtype = MBType::Inter;
349 mb.cbp = 0;
350 mb.qd = 0;
351 if (band.plane_no == 0) && (band.band_no == 0) && pic_hdr.in_q {
352 mb.qd = br.read_ivi_cb_s(&self.mb_cb)? as i16;
353 let q = calc_quant(band.quant, mb.qd);
354 if mb.mtype == MBType::Intra {
355 if band.blk_size == 8 {
356 mb.q = INDEO5_QSCALE8_INTRA[band_id][q];
357 } else {
358 mb.q = INDEO5_QSCALE4_INTRA[q];
359 }
360 } else {
361 if band.blk_size == 8 {
362 mb.q = INDEO5_QSCALE8_INTER[band_id][q];
363 } else {
364 mb.q = INDEO5_QSCALE4_INTER[q];
365 }
366 }
367 }
368 if band.inherit_mv {
369 if let Some(ref tileref) = ref_tile {
370 let mx = tileref.mb[mb_idx].mv_x;
371 let my = tileref.mb[mb_idx].mv_y;
372 if mv_scale == 0 {
373 mb.mv_x = mx;
374 mb.mv_y = my;
375 } else {
376 mb.mv_x = scale_mv(mx, mv_scale);
377 mb.mv_y = scale_mv(my, mv_scale);
378 }
379 }
380 }
381 }
382 tile.mb[mb_idx] = mb;
383 mb_idx += 1;
384 }
385 }
386 br.align();
387 Ok(())
388 }
389
390 fn recombine_plane(&mut self, src: &[i16], sstride: usize, dst: &mut [u8], dstride: usize, w: usize, h: usize) {
391 let mut idx0 = 0;
392 let mut idx1 = w / 2;
393 let mut idx2 = (h / 2) * sstride;
394 let mut idx3 = idx2 + idx1;
395 let mut bidx1 = idx1;
396 let mut bidx3 = idx3;
397 let mut oidx0 = 0;
398 let mut oidx1 = dstride;
399 let filt_lo = |a: i16, b: i16| a + b;
400 let filt_hi = |a: i16, b: i16, c: i16| a - b * 6 + c;
401
402 for _ in 0..(h/2) {
403 let mut b0_1 = src[idx0];
404 let mut b0_2 = src[idx0 + sstride];
405 let mut b1_1 = src[bidx1];
406 let mut b1_2 = src[idx1];
407 let mut b1_3 = filt_hi(b1_1, b1_2, src[idx1 + sstride]);
408 let mut b2_1;
409 let mut b2_2 = src[idx2];
410 let mut b2_3 = b2_2;
411 let mut b2_4;
412 let mut b2_5 = src[idx2 + sstride];
413 let mut b2_6 = b2_5;
414 let mut b3_1;
415 let mut b3_2 = src[bidx3];
416 let mut b3_3 = b3_2;
417 let mut b3_4;
418 let mut b3_5 = src[idx3];
419 let mut b3_6 = b3_5;
420 let mut b3_8 = filt_hi(b3_2, b3_5, src[idx3 + sstride]);
421 let mut b3_9 = b3_8;
422 let mut b3_7;
423
424 for x in 0..(w/2) {
425 b2_1 = b2_2;
426 b2_2 = b2_3;
427 b2_4 = b2_5;
428 b2_5 = b2_6;
429 b3_1 = b3_2;
430 b3_2 = b3_3;
431 b3_4 = b3_5;
432 b3_5 = b3_6;
433 b3_7 = b3_8;
434 b3_8 = b3_9;
435
436 let tmp0 = b0_1;
437 let tmp1 = b0_2;
438 b0_1 = src[idx0 + x + 1];
439 b0_2 = src[idx0 + x + 1 + sstride];
440 let mut p0 = tmp0 << 4;
441 let mut p1 = (tmp0 + b0_1) << 3;
442 let mut p2 = (tmp0 + tmp1) << 3;
443 let mut p3 = (tmp0 + tmp1 + b0_1 + b0_2) << 2;
444
445 let tmp0 = b1_1;
446 let tmp1 = b1_2;
447 let tmp2 = filt_lo(tmp0, tmp1);
448 let tmp3 = filt_hi(tmp0, tmp1, b1_3);
449 b1_2 = src[ idx1 + x + 1];
450 b1_1 = src[bidx1 + x + 1];
451 b1_3 = filt_hi(b1_1, b1_2, src[idx1 + x + 1 + sstride]);
452 p0 += tmp2 << 3;
453 p1 += (tmp2 + b1_1 + b1_2) << 2;
454 p2 += tmp3 << 2;
455 p3 += (tmp3 + b1_3) << 1;
456
457 b2_3 = src[idx2 + x + 1];
458 b2_6 = src[idx2 + x + 1 + sstride];
459 let tmp0 = filt_lo(b2_1, b2_2);
460 let tmp1 = filt_hi(b2_1, b2_2, b2_3);
461 p0 += tmp0 << 3;
462 p1 += tmp1 << 2;
463 p2 += (tmp0 + filt_lo(b2_4, b2_5)) << 2;
464 p3 += (tmp1 + filt_hi(b2_4, b2_5, b2_6)) << 1;
465
466 b3_6 = src[idx3 + x + 1];
467 b3_3 = src[bidx3 + x + 1];
468 b3_9 = filt_hi(b3_3, b3_6, src[idx3 + x + 1 + sstride]);
469 let tmp0 = b3_1 + b3_4;
470 let tmp1 = b3_2 + b3_5;
471 let tmp2 = b3_3 + b3_6;
472 p0 += filt_lo(tmp0, tmp1) << 2;
473 p1 += filt_hi(tmp0, tmp1, tmp2) << 1;
474 p2 += filt_lo(b3_7, b3_8) << 1;
475 p3 += filt_hi(b3_7, b3_8, b3_9) << 0;
476
477 dst[oidx0 + x * 2 + 0] = clip8((p0 >> 6) + 128);
478 dst[oidx0 + x * 2 + 1] = clip8((p1 >> 6) + 128);
479 dst[oidx1 + x * 2 + 0] = clip8((p2 >> 6) + 128);
480 dst[oidx1 + x * 2 + 1] = clip8((p3 >> 6) + 128);
481 }
482 bidx1 = idx1;
483 bidx3 = idx3;
484 idx0 += sstride;
485 idx1 += sstride;
486 idx2 += sstride;
487 idx3 += sstride;
488 oidx0 += dstride * 2;
489 oidx1 += dstride * 2;
490 }
491 }
492}
493
494struct Indeo5Decoder {
2422d969 495 info: NACodecInfoRef,
01c971c5
KS
496 dec: IVIDecoder,
497 ip: Indeo5Parser,
498}
499
500impl Indeo5Decoder {
501 fn new() -> Self {
502 Indeo5Decoder {
503 info: NACodecInfo::new_dummy(),
504 dec: IVIDecoder::new(),
505 ip: Indeo5Parser::new(),
506 }
507 }
508}
509
510impl NADecoder for Indeo5Decoder {
01613464 511 fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
01c971c5
KS
512 if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
513 let w = vinfo.get_width();
514 let h = vinfo.get_height();
515 let f = vinfo.is_flipped();
516 let fmt = formats::YUV410_FORMAT;
517 let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, f, fmt));
2422d969 518 self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref();
01c971c5
KS
519 Ok(())
520 } else {
521 Err(DecoderError::InvalidData)
522 }
523 }
01613464 524 fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
01c971c5 525 let src = pkt.get_buffer();
fa90ccfb 526 let mut br = BitReader::new(src.as_slice(), BitReaderMode::LE);
01c971c5
KS
527
528 let bufinfo = self.dec.decode_frame(&mut self.ip, &mut br)?;
529 let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
530 frm.set_keyframe(self.dec.is_intra());
531 frm.set_frame_type(self.dec.get_frame_type());
171860fc 532 Ok(frm.into_ref())
01c971c5 533 }
f9be4e75
KS
534 fn flush(&mut self) {
535 self.dec.flush();
536 }
01c971c5
KS
537}
538
7d57ae2f
KS
539impl NAOptionHandler for Indeo5Decoder {
540 fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
541 fn set_options(&mut self, _options: &[NAOption]) { }
542 fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
543}
544
01c971c5
KS
545const INDEO5_PICTURE_SIZE_TAB: [[usize; 2]; 15] = [
546 [640, 480], [320, 240], [160, 120], [704, 480], [352, 240], [352, 288], [176, 144],
547 [240, 180], [640, 240], [704, 240], [80, 60], [88, 72], [0, 0], [0, 0], [0, 0]
548];
549
550const INDEO5_FRAME_TYPE: [IVIFrameType; 5] = [
551 IVIFrameType::Intra, IVIFrameType::Inter, IVIFrameType::InterScal,
552 IVIFrameType::InterDroppable, IVIFrameType::NULL,
553];
554
555const INDEO5_QUANT8X8_INTRA: [[u16; 64]; 5] = [
556 [
557 0x1a, 0x2e, 0x36, 0x42, 0x46, 0x4a, 0x4e, 0x5a,
558 0x2e, 0x32, 0x3e, 0x42, 0x46, 0x4e, 0x56, 0x6a,
559 0x36, 0x3e, 0x3e, 0x44, 0x4a, 0x54, 0x66, 0x72,
560 0x42, 0x42, 0x44, 0x4a, 0x52, 0x62, 0x6c, 0x7a,
561 0x46, 0x46, 0x4a, 0x52, 0x5e, 0x66, 0x72, 0x8e,
562 0x4a, 0x4e, 0x54, 0x62, 0x66, 0x6e, 0x86, 0xa6,
563 0x4e, 0x56, 0x66, 0x6c, 0x72, 0x86, 0x9a, 0xca,
564 0x5a, 0x6a, 0x72, 0x7a, 0x8e, 0xa6, 0xca, 0xfe,
565 ], [
566 0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a,
567 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
568 0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62,
569 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
570 0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a,
571 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
572 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72,
573 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
574 ], [
575 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
576 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
577 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
578 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
579 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
580 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
581 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
582 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
583 ], [
584 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
585 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
586 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
587 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
588 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
589 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
590 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
591 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
592 ], [
593 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
594 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
595 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
596 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
597 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
598 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
599 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
600 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
601 ]
602];
603const INDEO5_QUANT8X8_INTER: [[u16; 64]; 5] = [
604 [
605 0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a,
606 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
607 0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62,
608 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
609 0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a,
610 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
611 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72,
612 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
613 ], [
614 0x26, 0x3a, 0x3e, 0x46, 0x4a, 0x4e, 0x52, 0x5a,
615 0x3a, 0x3e, 0x42, 0x46, 0x4a, 0x4e, 0x56, 0x5e,
616 0x3e, 0x42, 0x46, 0x48, 0x4c, 0x52, 0x5a, 0x62,
617 0x46, 0x46, 0x48, 0x4a, 0x4e, 0x56, 0x5e, 0x66,
618 0x4a, 0x4a, 0x4c, 0x4e, 0x52, 0x5a, 0x62, 0x6a,
619 0x4e, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x66, 0x6e,
620 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x72,
621 0x5a, 0x5e, 0x62, 0x66, 0x6a, 0x6e, 0x72, 0x76,
622 ], [
623 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
624 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
625 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
626 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
627 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
628 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
629 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
630 0x4e, 0xaa, 0xf2, 0xd4, 0xde, 0xc2, 0xd6, 0xc2,
631 ], [
632 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e, 0x4e,
633 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa, 0xaa,
634 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2, 0xf2,
635 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4, 0xd4,
636 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde, 0xde,
637 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
638 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6, 0xd6,
639 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2, 0xc2,
640 ], [
641 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
642 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
643 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
644 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
645 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
646 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
647 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
648 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e, 0x5e,
649 ]
650];
651const INDEO5_QUANT4X4_INTRA: [u16; 16] = [
652 0x1e, 0x3e, 0x4a, 0x52,
653 0x3e, 0x4a, 0x52, 0x5e,
654 0x4a, 0x52, 0x5e, 0x7a,
655 0x52, 0x5e, 0x7a, 0x92
656];
657const INDEO5_QUANT4X4_INTER: [u16; 16] = [
658 0x1e, 0x3e, 0x4a, 0x52,
659 0x3e, 0x4a, 0x52, 0x56,
660 0x4a, 0x52, 0x56, 0x5e,
661 0x52, 0x56, 0x5e, 0x66
662];
663const INDEO5_Q8_INTRA: [&[u16; 64]; 5] = [
664 &INDEO5_QUANT8X8_INTRA[0], &INDEO5_QUANT8X8_INTRA[1], &INDEO5_QUANT8X8_INTRA[2],
665 &INDEO5_QUANT8X8_INTRA[3], &INDEO5_QUANT8X8_INTRA[4],
666];
667const INDEO5_Q8_INTER: [&[u16; 64]; 5] = [
668 &INDEO5_QUANT8X8_INTER[0], &INDEO5_QUANT8X8_INTER[1], &INDEO5_QUANT8X8_INTER[2],
669 &INDEO5_QUANT8X8_INTER[3], &INDEO5_QUANT8X8_INTER[4],
670];
671const INDEO5_Q4_INTRA: &[u16; 16] = &INDEO5_QUANT4X4_INTRA;
672const INDEO5_Q4_INTER: &[u16; 16] = &INDEO5_QUANT4X4_INTER;
673
674const INDEO5_SCAN8X8: [&[usize; 64]; 4] = [
64e8b971 675 &ZIGZAG, &IVI_SCAN_8X8_VER, &IVI_SCAN_8X8_HOR, &IVI_SCAN_8X8_HOR
01c971c5
KS
676];
677const INDEO5_SCAN4X4: &[usize; 16] = &IVI_SCAN_4X4;
678
679const INDEO5_QSCALE8_INTRA: [[u8; 24]; 5] = [
680 [
681 0x0b, 0x0e, 0x10, 0x12, 0x14, 0x16, 0x17, 0x18, 0x1a, 0x1c, 0x1e, 0x20,
682 0x22, 0x24, 0x27, 0x28, 0x2a, 0x2d, 0x2f, 0x31, 0x34, 0x37, 0x39, 0x3c,
683 ], [
684 0x01, 0x10, 0x12, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x28, 0x2c,
685 0x30, 0x34, 0x38, 0x3d, 0x42, 0x47, 0x4c, 0x52, 0x58, 0x5e, 0x65, 0x6c,
686 ], [
687 0x13, 0x22, 0x27, 0x2a, 0x2d, 0x33, 0x36, 0x3c, 0x41, 0x45, 0x49, 0x4e,
688 0x53, 0x58, 0x5d, 0x63, 0x69, 0x6f, 0x75, 0x7c, 0x82, 0x88, 0x8e, 0x95,
689 ], [
690 0x13, 0x1f, 0x21, 0x24, 0x27, 0x29, 0x2d, 0x2f, 0x34, 0x37, 0x3a, 0x3d,
691 0x40, 0x44, 0x48, 0x4c, 0x4f, 0x52, 0x56, 0x5a, 0x5e, 0x62, 0x66, 0x6b,
692 ], [
693 0x31, 0x42, 0x47, 0x47, 0x4d, 0x52, 0x58, 0x58, 0x5d, 0x63, 0x67, 0x6b,
694 0x6f, 0x73, 0x78, 0x7c, 0x80, 0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa4,
695 ]
696];
697const INDEO5_QSCALE8_INTER: [[u8; 24]; 5] = [
698 [
699 0x0b, 0x11, 0x13, 0x14, 0x15, 0x16, 0x18, 0x1a, 0x1b, 0x1d, 0x20, 0x22,
700 0x23, 0x25, 0x28, 0x2a, 0x2e, 0x32, 0x35, 0x39, 0x3d, 0x41, 0x44, 0x4a,
701 ], [
702 0x07, 0x14, 0x16, 0x18, 0x1b, 0x1e, 0x22, 0x25, 0x29, 0x2d, 0x31, 0x35,
703 0x3a, 0x3f, 0x44, 0x4a, 0x50, 0x56, 0x5c, 0x63, 0x6a, 0x71, 0x78, 0x7e,
704 ], [
705 0x15, 0x25, 0x28, 0x2d, 0x30, 0x34, 0x3a, 0x3d, 0x42, 0x48, 0x4c, 0x51,
706 0x56, 0x5b, 0x60, 0x65, 0x6b, 0x70, 0x76, 0x7c, 0x82, 0x88, 0x8f, 0x97,
707 ], [
708 0x13, 0x1f, 0x20, 0x22, 0x25, 0x28, 0x2b, 0x2d, 0x30, 0x33, 0x36, 0x39,
709 0x3c, 0x3f, 0x42, 0x45, 0x48, 0x4b, 0x4e, 0x52, 0x56, 0x5a, 0x5e, 0x62,
710 ], [
711 0x3c, 0x52, 0x58, 0x5d, 0x63, 0x68, 0x68, 0x6d, 0x73, 0x78, 0x7c, 0x80,
712 0x84, 0x89, 0x8e, 0x93, 0x98, 0x9d, 0xa3, 0xa9, 0xad, 0xb1, 0xb5, 0xba
713 ]
714];
715const INDEO5_QSCALE4_INTRA: [u8; 24] = [
716 0x01, 0x0b, 0x0b, 0x0d, 0x0d, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x13, 0x14,
717 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20
718];
719const INDEO5_QSCALE4_INTER: [u8; 24] = [
720 0x0b, 0x0d, 0x0d, 0x0e, 0x11, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,
721 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23
722];
723
08a1fab7 724pub fn get_decoder() -> Box<dyn NADecoder + Send> {
01c971c5
KS
725 Box::new(Indeo5Decoder::new())
726}
727
728#[cfg(test)]
729mod test {
3167c45c
KS
730 use nihav_core::codecs::RegisteredDecoders;
731 use nihav_core::demuxers::RegisteredDemuxers;
ce742854 732 use nihav_codec_support::test::dec_video::*;
e64739f8
KS
733 use crate::indeo_register_all_codecs;
734 use nihav_commonfmt::generic_register_all_demuxers;
01c971c5
KS
735 #[test]
736 fn test_indeo5() {
3167c45c
KS
737 let mut dmx_reg = RegisteredDemuxers::new();
738 generic_register_all_demuxers(&mut dmx_reg);
739 let mut dec_reg = RegisteredDecoders::new();
740 indeo_register_all_codecs(&mut dec_reg);
741
122c047f
KS
742 test_decoding("avi", "indeo5", "assets/Indeo/IV5/sample.avi", Some(100),
743 &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
744 [0xd73ef6e2, 0x099dc18f, 0x46450af9, 0x1b390a48],
745 [0xbe3295d6, 0xf4afd9fd, 0x820d35e8, 0x4b593c9a],
746 [0x415e5aed, 0x33afb9a2, 0x14ae9308, 0x53e906d3],
747 [0x7fa94dd3, 0x58582fc3, 0xe39977bc, 0xd88036d5],
748 [0x8eef68f7, 0xace88c0c, 0x3f6e4388, 0xfcd82f46],
749 [0xfe22fcc6, 0x8c4666ab, 0xd9888786, 0x7d9adbc8],
750 [0x37f8b6bc, 0xaea9e94a, 0x05a98f2e, 0x2dce51fa],
751 [0x37492cbd, 0x8fd516fa, 0x48a0bcd1, 0x5eb6584f],
752 [0x6f464159, 0xa2af785b, 0xb440493b, 0x86b21911],
753 [0x3a52de08, 0x9f5762b0, 0xe58a6979, 0x0abb295e],
754 [0xe8f56414, 0x36e76d76, 0xd0927365, 0x15dc5327],
755 [0x0fc17e06, 0x8cb6581c, 0x86eb730d, 0x9bedf951],
756 [0x54fb3627, 0xc02bffc6, 0x87748ee5, 0x8b12d57d],
757 [0x8e4fd3a5, 0x3a7b9cd7, 0x0a4ba1a0, 0x48e10237],
758 [0xce87ea8b, 0x1ec40c98, 0x12c9a682, 0x57d02bf0],
759 [0x7024e691, 0x6bc493ba, 0x617a7a91, 0x65997b4c],
760 [0xb8d53b7c, 0x132ffec9, 0x827cf176, 0x68e97292],
761 [0x12ed76a9, 0x11eced60, 0x473a364f, 0x1e197803],
762 [0x6c23ba3a, 0x12e2f7e3, 0x8fc0c2bc, 0x20726bb2],
763 [0x3307e5e6, 0x3e4fa871, 0x55df1d59, 0xbe055301],
764 [0x8198ee6c, 0x82a33414, 0x9fd8c430, 0x1fca7b93],
765 [0x557662c2, 0xeb3226fc, 0x2a125be4, 0xd475ffa9],
766 [0x850c0326, 0x7a0105e5, 0x37799945, 0x927d1237],
767 [0xe770097e, 0xabd460f4, 0x3d9260e0, 0x5a8132e2],
768 [0xdb6644e7, 0xde6986eb, 0x12cc4916, 0x977d2177],
769 [0xd58ced6c, 0x91c0e7b6, 0x8c5926fc, 0x2dbf3117],
770 [0x6e76dd5f, 0x088884f0, 0x8f94451f, 0xc8df4daf],
771 [0x726b2f8f, 0xd44af9ba, 0x1e188962, 0xd37c1a38],
772 [0x84035565, 0xd2370a8c, 0x8ecb4a3f, 0xd6758196],
773 [0xa1e75a16, 0xc9e230ed, 0x23de50f3, 0x2366967a],
774 [0x690a2a91, 0xfa4acef1, 0xd3de6dd0, 0x973031d9],
775 [0xb392e62a, 0x22b0d3f2, 0x0e975a86, 0x14d6dcb3],
776 [0x5e002202, 0xc80e236e, 0x0b484e02, 0x00035f47],
777 [0x4fc0f301, 0x8ec0d33d, 0xe71a12dd, 0xe799731f],
778 [0x278c9096, 0xec7fa833, 0x2094d81f, 0x52e21165],
779 [0xd55238a8, 0xf040101a, 0x1152b6fe, 0x661c9e64],
780 [0x3699d16e, 0x89d9f2d7, 0x9ad59597, 0x7361ee21],
781 [0x1419c93c, 0x91b75784, 0x18f7121d, 0xec2c6b78],
782 [0x07c435da, 0x05f18557, 0xf28ce1e0, 0x43cadcba],
783 [0x2015269d, 0x52cad948, 0xd6485611, 0x06fe33d7],
784 [0x0cea56f3, 0x82c30841, 0x9b2a8cab, 0x8a6f07cb],
785 [0x81f82aa9, 0x233060d5, 0x00f4171e, 0xe14c0c2a],
786 [0x9b2f8b08, 0x7d091eac, 0x09dcb2c3, 0xa7670405],
787 [0x99c97f75, 0xf91c6b12, 0xfbad7705, 0x1c6e6f27],
788 [0xc762b89c, 0xbf44a194, 0xb2a54dc2, 0xae2103e4],
789 [0xba4f52ed, 0xe35aff77, 0x50d8c9d3, 0xeb382d32],
790 [0x9bc9d9a0, 0x7cb4c594, 0xbc1af6f4, 0x1f718229],
791 [0x5f19eea2, 0x6260982e, 0x393fb360, 0x71abe746],
792 [0xd13f2fcc, 0x88a6a714, 0xf4f53d55, 0xf42b11ba],
793 [0x4208b476, 0xaf06ffce, 0x38e59bfe, 0x588567a2],
794 [0xbedfb7b7, 0x8300a39d, 0x964a3c0f, 0x577d52d7],
795 [0x18e5a6f2, 0x7ec85996, 0x27694f30, 0x7717748a],
796 [0xb5e6d70f, 0xc43261bb, 0xd4e6ae7c, 0xcc11f79c],
797 [0xc808cba7, 0xbb042416, 0x2f01ebe1, 0x7d176a38],
798 [0x03353805, 0x4b6e9d66, 0x25933123, 0x4213aaf7],
799 [0x189a6da5, 0x04a4cbe6, 0xea3c9d09, 0x153fdee2],
800 [0x41f8ac6b, 0xb476356b, 0xc70b67d0, 0x28caf359],
801 [0x4514b6a4, 0x788545ff, 0x4ee9139b, 0xa45bedf9],
802 [0x2a39be04, 0xac9921cb, 0x685c1bf9, 0x904bdab2],
803 [0x2c18f3ef, 0x416c0335, 0x0face768, 0x1b9d5cd2],
804 [0x898cd63f, 0x60af727f, 0x6bdf1be6, 0x0df05cfe],
805 [0x8a06787b, 0x7cee2f8b, 0xdc8aac77, 0x2e0e740a],
806 [0x3d340571, 0xbf1c8d4c, 0xddc23f69, 0xd1903942],
807 [0x7d179e85, 0x54048c4d, 0xba047d33, 0x2e9e5edb],
808 [0x65e26600, 0x87c8421d, 0xa77e2c6c, 0x32b4971a],
809 [0x69041052, 0xa4858c7b, 0x904d84f7, 0xb4ad3dcf],
810 [0x3ea0246d, 0x533e752d, 0x1d55798a, 0x30e17e72],
811 [0x4254a700, 0x07365f23, 0x0f9da313, 0xaecd38ce],
812 [0xa5756d9d, 0x79f31387, 0x0ded3654, 0xa7299663],
813 [0x4ef027c9, 0xeebb1383, 0x26a55289, 0x3746969d],
814 [0xdc6acadf, 0x23e1b6e1, 0x07fcdc26, 0x9914b684],
815 [0x52bb8b80, 0x1a5688ae, 0xd429662d, 0x1cc1485d],
816 [0x76b35f59, 0x24b64e5b, 0xbcbeaee7, 0xf568a832],
817 [0x0756d15f, 0x9cc288bf, 0x9f882a3c, 0xfe7c7161],
818 [0x0503113a, 0x95e716ff, 0x304cf65e, 0x490725e8],
819 [0x7db7ba62, 0x08e4e77d, 0xc9db6413, 0xea3f1a39],
820 [0x7cef6d67, 0xc94867e6, 0x5c674de6, 0x5eb74081],
821 [0x7573b799, 0x069d4f03, 0x63b537a1, 0xdfe25db6],
822 [0xc401e705, 0x834828bc, 0xd99da4a1, 0xd0f3bee8],
823 [0x02817844, 0xada6433e, 0x31761e98, 0x901ccf68],
824 [0x8f9432b4, 0x9f860957, 0xcba54c86, 0x8beb8209],
825 [0x6a46e58c, 0x7d299228, 0x5c001d12, 0xd8db2a00],
826 [0x0c12586d, 0x866d8ca9, 0x849bbb17, 0x5af63ea2],
827 [0xe48671b6, 0xc4377063, 0xc4d03c02, 0x621bd894],
828 [0x5f7f82eb, 0xcdb5abf5, 0x325f2d9d, 0x24a5d200],
829 [0xec6b6fe7, 0x347316c4, 0x6241904a, 0x4e2497a5],
830 [0xf661b7fd, 0xa00e2fc7, 0x90e11456, 0x507fef21],
831 [0x77c7addd, 0x67148dce, 0x1cd27059, 0xefbf4abf],
832 [0x11270d9c, 0xb352779d, 0x81f21055, 0xae93a8b6],
833 [0x3d1f0aaf, 0x3b4aa6d8, 0xca1c160c, 0x6fe4f2bd],
834 [0x17c6bec4, 0x54b568cd, 0xd19c78d6, 0x9a3d897a],
835 [0xc4ab4ca6, 0xbf3b2573, 0xb4d837dd, 0x4dfab799],
836 [0x6fd5645d, 0xa34978b2, 0x6696dd1a, 0x665ca09b],
837 [0x87984bb9, 0xd4d3bc30, 0x7f8bb7a8, 0x2d83b303],
838 [0x21fb5d58, 0x1ee47d1a, 0x97200d83, 0x1d596a88],
839 [0x2656f329, 0x497693be, 0xca971ddf, 0x410d4092],
840 [0xd285c512, 0xfc1ed632, 0x63c43ec2, 0xac5766d1],
841 [0x46fb80ee, 0xcfeecdaa, 0x7237a433, 0x5708ff56],
842 [0x4fccd9c8, 0x7b1a4f31, 0x51516a80, 0x27bf3cae],
843 [0xd649d2f5, 0xebadf1f7, 0x6b34e8ce, 0xb87e82f1],
844 [0x6eb0aec6, 0xfbe9cb51, 0x39e695b4, 0xa6e46e70]]));
01c971c5
KS
845 }
846}