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