89b4b2200aed810f46ecc15ed45609a60177283f
[nihav.git] / nihav-ms / src / codecs / msvideo1enc.rs
1 use nihav_core::codecs::*;
2 use nihav_core::io::byteio::*;
3 use nihav_codec_support::vq::*;
4
5 #[derive(Default,Clone,Copy,PartialEq)]
6 struct Pixel16(u16);
7
8 impl Pixel16 {
9 fn unpack(self) -> (u16, u16, u16) {
10 ((self.0 >> 10) & 0x1F, (self.0 >> 5) & 0x1F, self.0 & 0x1F)
11 }
12 fn pack(r: u16, g: u16, b: u16) -> Self {
13 Pixel16{ 0: (r << 10) | (g << 5) | b }
14 }
15 fn invalid() -> Self { Self(0x8000) }
16 fn is_invalid(self) -> bool { self == Self::invalid() }
17 }
18 impl VQElement for Pixel16 {
19 fn dist(&self, rval: Self) -> u32 {
20 let (r0, g0, b0) = self.unpack();
21 let (r1, g1, b1) = rval.unpack();
22 let rd = i32::from(r0) - i32::from(r1);
23 let gd = i32::from(g0) - i32::from(g1);
24 let bd = i32::from(b0) - i32::from(b1);
25 (rd * rd + gd * gd + bd * bd) as u32
26 }
27 fn min_cw() -> Self { Pixel16(0x0000) }
28 fn max_cw() -> Self { Pixel16(0x7FFF) }
29 fn min(&self, rval: Self) -> Self {
30 let (r0, g0, b0) = self.unpack();
31 let (r1, g1, b1) = rval.unpack();
32 Self::pack(r0.min(r1), g0.min(g1), b0.min(b1))
33 }
34 fn max(&self, rval: Self) -> Self {
35 let (r0, g0, b0) = self.unpack();
36 let (r1, g1, b1) = rval.unpack();
37 Self::pack(r0.max(r1), g0.max(g1), b0.max(b1))
38 }
39 fn num_components() -> usize { 3 }
40 fn sort_by_component(arr: &mut [Self], component: usize) {
41 let mut counts = [0; 32];
42 for pix in arr.iter() {
43 let (r, g, b) = pix.unpack();
44 let idx = match component {
45 0 => r,
46 1 => g,
47 _ => b,
48 } as usize;
49 counts[idx] += 1;
50 }
51 let mut offs = [0; 32];
52 for i in 0..31 {
53 offs[i + 1] = offs[i] + counts[i];
54 }
55 let mut dst = [Pixel16(0); 16];
56 assert!(dst.len() >= arr.len());
57 for pix in arr.iter() {
58 let (r, g, b) = pix.unpack();
59 let idx = match component {
60 0 => r,
61 1 => g,
62 _ => b,
63 } as usize;
64 dst[offs[idx]] = *pix;
65 offs[idx] += 1;
66 }
67 let len = arr.len();
68 arr.copy_from_slice(&dst[..len]);
69 }
70 fn max_dist_component(min: &Self, max: &Self) -> usize {
71 let (r0, g0, b0) = max.unpack();
72 let (r1, g1, b1) = min.unpack();
73 let rd = u32::from(r0) - u32::from(r1);
74 let gd = u32::from(g0) - u32::from(g1);
75 let bd = u32::from(b0) - u32::from(b1);
76 if rd > gd && rd > bd {
77 0
78 } else if bd > rd && bd > gd {
79 2
80 } else {
81 1
82 }
83 }
84 }
85
86 struct Pixel16Sum {
87 rsum: u16,
88 gsum: u16,
89 bsum: u16,
90 count: u16,
91 }
92
93 impl VQElementSum<Pixel16> for Pixel16Sum {
94 fn zero() -> Self { Pixel16Sum { rsum: 0, gsum: 0, bsum: 0, count: 0 } }
95 fn add(&mut self, rval: Pixel16, count: u64) {
96 let (r, g, b) = rval.unpack();
97 let count = count as u16;
98 self.rsum += r * count;
99 self.gsum += g * count;
100 self.bsum += b * count;
101 self.count += count;
102 }
103 fn get_centroid(&self) -> Pixel16 {
104 if self.count != 0 {
105 let r = (self.rsum + self.count / 2) / self.count;
106 let g = (self.gsum + self.count / 2) / self.count;
107 let b = (self.bsum + self.count / 2) / self.count;
108 Pixel16::pack(r, g, b)
109 } else {
110 Pixel16(0x0000)
111 }
112 }
113 }
114
115 #[derive(Default)]
116 struct BlockState {
117 fill_dist: u32,
118 fill_val: Pixel16,
119 clr2_dist: u32,
120 clr2_flags: u16,
121 clr2: [Pixel16; 2],
122 clr8_dist: u32,
123 clr8_flags: u16,
124 clr8: [[Pixel16; 2]; 4],
125 }
126
127 impl BlockState {
128 fn calc_clrs(buf: &[Pixel16; 16]) -> Option<(Pixel16, Pixel16)> {
129 let clr0 = buf[0];
130 let mut clr1 = Pixel16::invalid();
131 for &pix in buf[1..].iter() {
132 if pix != clr0 && pix != clr1 {
133 if clr1.is_invalid() {
134 clr1 = pix;
135 } else {
136 return None;
137 }
138 }
139 }
140 Some((clr0, clr1))
141 }
142 fn calc_stats(&mut self, buf: &[Pixel16; 16]) {
143 let mut filled = false;
144 let mut two_clr = false;
145 if let Some((clr0, clr1)) = Self::calc_clrs(buf) {
146 self.clr2[0] = clr0;
147 self.clr2[1] = if !clr1.is_invalid() { clr1 } else { clr0 };
148 if clr0 == clr1 {
149 self.fill_val = Pixel16 { 0: buf[0].0 & !0x400 };
150 filled = true;
151 }
152 two_clr = true;
153 }
154 self.fill_dist = 0;
155 if !filled {
156 let mut avg = Pixel16Sum::zero();
157 for pix in buf.iter() {
158 avg.add(*pix, 1);
159 }
160 self.fill_val = Pixel16 { 0: avg.get_centroid().0 & !0x400 };
161 for pix in buf.iter() {
162 self.fill_dist += pix.dist(self.fill_val);
163 }
164 }
165 if self.fill_dist == 0 {
166 self.clr2_dist = std::u32::MAX;
167 self.clr8_dist = std::u32::MAX;
168 return;
169 }
170
171 self.clr2_flags = 0u16;
172 if two_clr {
173 let mut mask = 1;
174 self.clr2_dist = 0;
175 for &pix in buf.iter() {
176 if pix == self.clr2[0] {
177 self.clr2_flags |= mask;
178 } else {
179 }
180 mask <<= 1;
181 }
182 if (self.clr2_flags & 0x8000) != 0 {
183 self.clr2_flags = !self.clr2_flags;
184 self.clr2.swap(0, 1);
185 }
186 } else if quantise_median_cut::<Pixel16, Pixel16Sum>(buf, &mut self.clr2) == 2 {
187 let mut mask = 1;
188 self.clr2_dist = 0;
189 for pix in buf.iter() {
190 let dist0 = pix.dist(self.clr2[0]);
191 let dist1 = pix.dist(self.clr2[1]);
192 if dist0 < dist1 {
193 self.clr2_flags |= mask;
194 self.clr2_dist += dist0;
195 } else {
196 self.clr2_dist += dist1;
197 }
198 mask <<= 1;
199 }
200 if (self.clr2_flags & 0x8000) != 0 {
201 self.clr2_flags = !self.clr2_flags;
202 self.clr2.swap(0, 1);
203 }
204 } else {
205 self.clr2_dist = self.fill_dist;
206 self.clr2 = [self.fill_val; 2];
207 }
208 if self.clr2_dist == 0 {
209 self.clr8_dist = std::u32::MAX;
210 return;
211 }
212
213 self.clr8 = [[Pixel16 { 0: 0}; 2]; 4];
214 self.clr8_flags = 0;
215 self.clr8_dist = 0;
216 let mut mask = 1;
217 for i in 0..4 {
218 let off = (i & 1) * 2 + (i & 2) * 4;
219 let src2 = [buf[off], buf[off + 1], buf[off + 4], buf[off + 5]];
220 let nc = quantise_median_cut::<Pixel16, Pixel16Sum>(&src2, &mut self.clr8[i]);
221 if nc < 2 {
222 self.clr8[i][1] = self.clr8[i][0];
223 }
224 for j in 0..4 {
225 let dist0 = src2[j].dist(self.clr8[i][0]);
226 let dist1 = src2[j].dist(self.clr8[i][1]);
227 if dist0 < dist1 {
228 self.clr8_flags |= mask;
229 self.clr8_dist += dist0;
230 } else {
231 self.clr8_dist += dist1;
232 }
233 mask <<= 1;
234 }
235 }
236 if (self.clr8_flags & 0x8000) != 0 {
237 self.clr8_flags ^= 0xF000;
238 self.clr8[3].swap(0, 1);
239 }
240 }
241 fn put_fill(&self, dst: &mut [u16], dstride: usize) {
242 for line in dst.chunks_mut(dstride) {
243 for i in 0..4 {
244 line[i] = self.fill_val.0;
245 }
246 }
247 }
248 fn put_clr2(&self, dst: &mut [u16], dstride: usize) {
249 for j in 0..4 {
250 for i in 0..4 {
251 if (self.clr2_flags & (1 << (i + j * 4))) == 0 {
252 dst[i + j * dstride] = self.clr2[0].0;
253 } else {
254 dst[i + j * dstride] = self.clr2[1].0;
255 }
256 }
257 }
258 }
259 fn put_clr8(&self, dst: &mut [u16], dstride: usize) {
260 for i in 0..4 {
261 let off = (i & 1) * 2 + (i & 2) * dstride;
262 let cur_flg = (self.clr8_flags >> (i * 4)) & 0xF;
263 dst[off] = self.clr8[i][( !cur_flg & 1) as usize].0;
264 dst[off + 1] = self.clr8[i][((!cur_flg >> 1) & 1) as usize].0;
265 dst[off + dstride] = self.clr8[i][((!cur_flg >> 2) & 1) as usize].0;
266 dst[off + 1 + dstride] = self.clr8[i][((!cur_flg >> 3) & 1) as usize].0;
267 }
268 }
269 fn write_fill(&self, bw: &mut ByteWriter) -> EncoderResult<()> {
270 bw.write_u16le(self.fill_val.0 | 0x8000)?;
271 Ok(())
272 }
273 fn write_clr2(&self, bw: &mut ByteWriter) -> EncoderResult<()> {
274 bw.write_u16le(self.clr2_flags)?;
275 bw.write_u16le(self.clr2[0].0)?;
276 bw.write_u16le(self.clr2[1].0)?;
277 Ok(())
278 }
279 fn write_clr8(&self, bw: &mut ByteWriter) -> EncoderResult<()> {
280 bw.write_u16le(self.clr8_flags)?;
281 bw.write_u16le(self.clr8[0][0].0 | 0x8000)?;
282 bw.write_u16le(self.clr8[0][1].0)?;
283 bw.write_u16le(self.clr8[1][0].0)?;
284 bw.write_u16le(self.clr8[1][1].0)?;
285 bw.write_u16le(self.clr8[2][0].0)?;
286 bw.write_u16le(self.clr8[2][1].0)?;
287 bw.write_u16le(self.clr8[3][0].0)?;
288 bw.write_u16le(self.clr8[3][1].0)?;
289 Ok(())
290 }
291 }
292
293 struct MSVideo1Encoder {
294 stream: Option<NAStreamRef>,
295 pkt: Option<NAPacket>,
296 pool: NAVideoBufferPool<u16>,
297 lastfrm: Option<NAVideoBufferRef<u16>>,
298 quality: u8,
299 frmcount: u8,
300 key_int: u8,
301 }
302
303 impl MSVideo1Encoder {
304 fn new() -> Self {
305 Self {
306 stream: None,
307 pkt: None,
308 pool: NAVideoBufferPool::new(2),
309 lastfrm: None,
310 quality: 0,
311 frmcount: 0,
312 key_int: 25,
313 }
314 }
315 fn get_block(src: &[u16], sstride: usize, buf: &mut [Pixel16; 16]) {
316 for (line, dst) in src.chunks(sstride).zip(buf.chunks_mut(4)) {
317 for i in 0..4 {
318 dst[i] = Pixel16 { 0: line[i] };
319 }
320 }
321 }
322 fn write_skips(bw: &mut ByteWriter, skips: usize) -> EncoderResult<()> {
323 bw.write_u16le((skips as u16) | 0x8400)?;
324 Ok(())
325 }
326 fn encode_inter(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, prev_frm: &NAVideoBuffer<u16>, _quality: u8) -> EncoderResult<bool> {
327 let mut is_intra = true;
328 let src = in_frm.get_data();
329 let sstride = in_frm.get_stride(0);
330 let soff = in_frm.get_offset(0);
331 let (w, h) = in_frm.get_dimensions(0);
332 let rsrc = prev_frm.get_data();
333 let rstride = prev_frm.get_stride(0);
334 let roff = prev_frm.get_offset(0);
335 let dstride = cur_frm.get_stride(0);
336 let doff = cur_frm.get_offset(0);
337 let dst = cur_frm.get_data_mut().unwrap();
338 let mut skip_run = 0;
339 for ((sstrip, rstrip), dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&rsrc[roff..]).chunks(rstride * 4)).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) {
340 for x in (0..w).step_by(4) {
341 let mut buf = [Pixel16::min_cw(); 16];
342 let mut refbuf = [Pixel16::min_cw(); 16];
343 Self::get_block(&sstrip[x..], sstride, &mut buf);
344 Self::get_block(&rstrip[x..], rstride, &mut refbuf);
345
346 let mut skip_dist = 0;
347 for (pix, rpix) in buf.iter().zip(refbuf.iter()) {
348 skip_dist += pix.dist(*rpix);
349 }
350 if skip_dist == 0 {
351 skip_run += 1;
352 is_intra = false;
353 if skip_run == 1023 {
354 Self::write_skips(bw, skip_run)?;
355 skip_run = 0;
356 }
357 continue;
358 }
359
360 let mut bstate = BlockState::default();
361 bstate.calc_stats(&buf);
362
363 let dst = &mut dstrip[x..];
364 if skip_dist <= bstate.fill_dist {
365 skip_run += 1;
366 is_intra = false;
367 if skip_run == 1023 {
368 Self::write_skips(bw, skip_run)?;
369 skip_run = 0;
370 }
371 } else if bstate.fill_dist <= bstate.clr2_dist {
372 bstate.put_fill(dst, dstride);
373 if skip_run != 0 {
374 Self::write_skips(bw, skip_run)?;
375 skip_run = 0;
376 }
377 bstate.write_fill(bw)?;
378 } else if bstate.clr8_dist < bstate.clr2_dist {
379 bstate.put_clr8(dst, dstride);
380 if skip_run != 0 {
381 Self::write_skips(bw, skip_run)?;
382 skip_run = 0;
383 }
384 bstate.write_clr8(bw)?;
385 } else {
386 bstate.put_clr2(dst, dstride);
387 if skip_run != 0 {
388 Self::write_skips(bw, skip_run)?;
389 skip_run = 0;
390 }
391 bstate.write_clr2(bw)?;
392 }
393 }
394 }
395 if skip_run != 0 {
396 Self::write_skips(bw, skip_run)?;
397 }
398 if is_intra {
399 bw.write_u16le(0)?;
400 } //xxx: something for inter?
401 Ok(is_intra)
402 }
403 fn encode_intra(bw: &mut ByteWriter, cur_frm: &mut NAVideoBuffer<u16>, in_frm: &NAVideoBuffer<u16>, _quality: u8) -> EncoderResult<bool> {
404 let src = in_frm.get_data();
405 let sstride = in_frm.get_stride(0);
406 let soff = in_frm.get_offset(0);
407 let (w, h) = in_frm.get_dimensions(0);
408 let dstride = cur_frm.get_stride(0);
409 let doff = cur_frm.get_offset(0);
410 let dst = cur_frm.get_data_mut().unwrap();
411 for (sstrip, dstrip) in (&src[soff..]).chunks(sstride * 4).take(h / 4).zip((&mut dst[doff..]).chunks_mut(dstride * 4)) {
412 for x in (0..w).step_by(4) {
413 let mut buf = [Pixel16::min_cw(); 16];
414 Self::get_block(&sstrip[x..], sstride, &mut buf);
415 let mut bstate = BlockState::default();
416 bstate.calc_stats(&buf);
417
418 let dst = &mut dstrip[x..];
419 if bstate.fill_dist <= bstate.clr2_dist {
420 bstate.put_fill(dst, dstride);
421 bstate.write_fill(bw)?;
422 } else if bstate.clr8_dist < bstate.clr2_dist {
423 bstate.put_clr8(dst, dstride);
424 bstate.write_clr8(bw)?;
425 } else {
426 bstate.put_clr2(dst, dstride);
427 bstate.write_clr2(bw)?;
428 }
429 }
430 }
431 bw.write_u16le(0)?;
432 Ok(true)
433 }
434 }
435
436 const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton {
437 model: ColorModel::RGB(RGBSubmodel::RGB), components: 3,
438 comp_info: [
439 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }),
440 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }),
441 Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }),
442 None, None],
443 elem_size: 2, be: false, alpha: false, palette: false };
444
445 impl NAEncoder for MSVideo1Encoder {
446 fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult<EncodeParameters> {
447 match encinfo.format {
448 NACodecTypeInfo::None => {
449 let mut ofmt = EncodeParameters::default();
450 ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, RGB555_FORMAT));
451 Ok(ofmt)
452 },
453 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
454 NACodecTypeInfo::Video(vinfo) => {
455 let outinfo = NAVideoInfo::new((vinfo.width + 3) & !3, (vinfo.height + 3) & !3, true, RGB555_FORMAT);
456 let mut ofmt = *encinfo;
457 ofmt.format = NACodecTypeInfo::Video(outinfo);
458 Ok(ofmt)
459 }
460 }
461 }
462 fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult<NAStreamRef> {
463 match encinfo.format {
464 NACodecTypeInfo::None => Err(EncoderError::FormatError),
465 NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError),
466 NACodecTypeInfo::Video(vinfo) => {
467 if vinfo.format != RGB555_FORMAT {
468 return Err(EncoderError::FormatError);
469 }
470 if ((vinfo.width | vinfo.height) & 3) != 0 {
471 return Err(EncoderError::FormatError);
472 }
473
474 let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, true, RGB555_FORMAT);
475 let info = NACodecInfo::new("msvideo1", NACodecTypeInfo::Video(out_info), None);
476 let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0);
477 stream.set_num(stream_id as usize);
478 let stream = stream.into_ref();
479 if self.pool.prealloc_video(out_info, 2).is_err() {
480 return Err(EncoderError::AllocError);
481 }
482
483 self.stream = Some(stream.clone());
484 self.quality = encinfo.quality;
485
486 Ok(stream)
487 },
488 }
489 }
490 fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> {
491 let buf = frm.get_buffer();
492 if let Some(ref vbuf) = buf.get_vbuf16() {
493 let mut cur_frm = self.pool.get_free().unwrap();
494 let mut dbuf = Vec::with_capacity(4);
495 let mut gw = GrowableMemoryWriter::new_write(&mut dbuf);
496 let mut bw = ByteWriter::new(&mut gw);
497 if self.frmcount == 0 {
498 self.lastfrm = None;
499 }
500 let is_intra = if let Some(ref prev_buf) = self.lastfrm {
501 Self::encode_inter(&mut bw, &mut cur_frm, vbuf, prev_buf, self.quality)?
502 } else {
503 Self::encode_intra(&mut bw, &mut cur_frm, vbuf, self.quality)?
504 };
505 self.lastfrm = Some(cur_frm);
506 self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf));
507 self.frmcount += 1;
508 if self.frmcount == self.key_int {
509 self.frmcount = 0;
510 }
511 Ok(())
512 } else {
513 Err(EncoderError::InvalidParameters)
514 }
515 }
516 fn get_packet(&mut self) -> EncoderResult<Option<NAPacket>> {
517 let mut npkt = None;
518 std::mem::swap(&mut self.pkt, &mut npkt);
519 Ok(npkt)
520 }
521 fn flush(&mut self) -> EncoderResult<()> {
522 self.frmcount = 0;
523 Ok(())
524 }
525 }
526
527 const ENCODER_OPTS: &[NAOptionDefinition] = &[
528 NAOptionDefinition {
529 name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC,
530 opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) },
531 ];
532
533 impl NAOptionHandler for MSVideo1Encoder {
534 fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS }
535 fn set_options(&mut self, options: &[NAOption]) {
536 for option in options.iter() {
537 for opt_def in ENCODER_OPTS.iter() {
538 if opt_def.check(option).is_ok() {
539 match option.name {
540 KEYFRAME_OPTION => {
541 if let NAValue::Int(intval) = option.value {
542 self.key_int = intval as u8;
543 }
544 },
545 _ => {},
546 };
547 }
548 }
549 }
550 }
551 fn query_option_value(&self, name: &str) -> Option<NAValue> {
552 match name {
553 KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))),
554 _ => None,
555 }
556 }
557 }
558
559 pub fn get_encoder() -> Box<dyn NAEncoder + Send> {
560 Box::new(MSVideo1Encoder::new())
561 }
562
563 #[cfg(test)]
564 mod test {
565 use nihav_core::codecs::*;
566 use nihav_core::demuxers::*;
567 use nihav_core::muxers::*;
568 use crate::*;
569 use nihav_commonfmt::*;
570 use nihav_codec_support::test::enc_video::*;
571 use super::RGB555_FORMAT;
572
573 #[test]
574 fn test_ms_video1_encoder() {
575 let mut dmx_reg = RegisteredDemuxers::new();
576 generic_register_all_demuxers(&mut dmx_reg);
577 let mut dec_reg = RegisteredDecoders::new();
578 generic_register_all_decoders(&mut dec_reg);
579 ms_register_all_decoders(&mut dec_reg);
580 let mut mux_reg = RegisteredMuxers::new();
581 generic_register_all_muxers(&mut mux_reg);
582 let mut enc_reg = RegisteredEncoders::new();
583 ms_register_all_encoders(&mut enc_reg);
584
585 let dec_config = DecoderTestParams {
586 demuxer: "avi",
587 in_name: "assets/Misc/TalkingHead_352x288.avi",
588 stream_type: StreamType::Video,
589 limit: Some(3),
590 dmx_reg, dec_reg,
591 };
592 let enc_config = EncoderTestParams {
593 muxer: "avi",
594 enc_name: "msvideo1",
595 out_name: "msvideo1.avi",
596 mux_reg, enc_reg,
597 };
598 let dst_vinfo = NAVideoInfo {
599 width: 0,
600 height: 0,
601 format: RGB555_FORMAT,
602 flipped: true,
603 bits: 16,
604 };
605 let enc_params = EncodeParameters {
606 format: NACodecTypeInfo::Video(dst_vinfo),
607 quality: 0,
608 bitrate: 0,
609 tb_num: 0,
610 tb_den: 0,
611 flags: 0,
612 };
613 //test_encoding_to_file(&dec_config, &enc_config, enc_params, &[]);
614 test_encoding_md5(&dec_config, &enc_config, enc_params, &[],
615 &[0x4339421d, 0x6393f1b6, 0x653d6cd2, 0x3a184382]);
616 }
617 }