From: Kostya Shishkov Date: Sat, 22 May 2021 12:41:43 +0000 (+0200) Subject: Zip Motion Blocks Video encoder X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=f17870054cf46115cd1eb95176b1767bd874d95c;p=nihav.git Zip Motion Blocks Video encoder --- diff --git a/nihav-commonfmt/Cargo.toml b/nihav-commonfmt/Cargo.toml index 044e5ec..b322049 100644 --- a/nihav-commonfmt/Cargo.toml +++ b/nihav-commonfmt/Cargo.toml @@ -47,8 +47,9 @@ decoder_aac = ["decoders"] all_encoders = ["all_video_encoders", "all_audio_encoders"] -all_video_encoders = ["encoder_cinepak"] +all_video_encoders = ["encoder_cinepak", "encoder_zmbv"] encoder_cinepak = ["encoders"] +encoder_zmbv = ["encoders"] all_audio_encoders = ["encoder_pcm"] encoder_pcm = ["encoders"] diff --git a/nihav-commonfmt/src/codecs/mod.rs b/nihav-commonfmt/src/codecs/mod.rs index 37de6b3..7fef1cb 100644 --- a/nihav-commonfmt/src/codecs/mod.rs +++ b/nihav-commonfmt/src/codecs/mod.rs @@ -64,10 +64,14 @@ pub fn generic_register_all_decoders(rd: &mut RegisteredDecoders) { #[cfg(feature="encoder_cinepak")] mod cinepakenc; +#[cfg(feature="encoder_zmbv")] +mod zmbvenc; const ENCODERS: &[EncoderInfo] = &[ #[cfg(feature="encoder_cinepak")] EncoderInfo { name: "cinepak", get_encoder: cinepakenc::get_encoder }, +#[cfg(feature="encoder_zmbv")] + EncoderInfo { name: "zmbv", get_encoder: zmbvenc::get_encoder }, #[cfg(feature="encoder_pcm")] EncoderInfo { name: "pcm", get_encoder: pcm::get_encoder }, diff --git a/nihav-commonfmt/src/codecs/zmbvenc.rs b/nihav-commonfmt/src/codecs/zmbvenc.rs new file mode 100644 index 0000000..23e8aad --- /dev/null +++ b/nihav-commonfmt/src/codecs/zmbvenc.rs @@ -0,0 +1,708 @@ +use nihav_core::codecs::*; +use nihav_core::compr::deflate::{Deflate, DeflateMode, DeflateWriter}; +use nihav_core::io::byteio::*; + +const RGB555_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3, + comp_info: [ + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 10, comp_offs: 0, next_elem: 2 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 5, comp_offs: 0, next_elem: 2 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 5, shift: 0, comp_offs: 0, next_elem: 2 }), + None, None], + elem_size: 2, be: false, alpha: false, palette: false }; +const RGB24_0_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB(RGBSubmodel::RGB), components: 3, + comp_info: [ + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 2, next_elem: 4 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 1, next_elem: 4 }), + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, packed: true, depth: 8, shift: 0, comp_offs: 0, next_elem: 4 }), + None, None], + elem_size: 4, be: false, alpha: false, palette: false }; + +struct ZMBVEncoder { + stream: Option, + frm1: Vec, + frm2: Vec, + tmp_buf: Vec, + zbuf: Vec, + pal: [u8; 768], + pkt: Option, + frmcount: u8, + key_int: u8, + tile_w: usize, + tile_h: usize, + cmode: DeflateMode, + compr: Deflate, + bpp: u8, + width: usize, + height: usize, + range: usize, +} + +fn buf_type_to_bpp(buf: &NABufferType) -> u8 { + match buf { + NABufferType::Video(ref vbuf) => { + let vinfo = vbuf.get_info(); + if vinfo.get_format().is_paletted() { + 8 + } else { + vinfo.get_format().get_total_depth() + } + }, + NABufferType::VideoPacked(ref vbuf) => { + let vinfo = vbuf.get_info(); + vinfo.get_format().elem_size * 8 + }, + NABufferType::Video16(ref vbuf) => { + let vinfo = vbuf.get_info(); + vinfo.get_format().get_total_depth() + }, + _ => 0, + } +} + +fn copy_frame(buf: NABufferType, dst: &mut [u8], bpp: u8) -> EncoderResult<()> { + match buf { + NABufferType::Video(ref vbuf) => { + if bpp != 8 { + return Err(EncoderError::FormatError); + } + let off = vbuf.get_offset(0); + let stride = vbuf.get_stride(0); + let data = vbuf.get_data(); + let w = vbuf.get_info().get_width(); + let h = vbuf.get_info().get_height(); + + for (dline, sline) in dst.chunks_mut(w).zip(data[off..].chunks(stride)).take(h) { + dline[..w].copy_from_slice(&sline[..w]); + } + }, + NABufferType::Video16(ref vbuf) => { + let off = vbuf.get_offset(0); + let stride = vbuf.get_stride(0); + let data = vbuf.get_data(); + let w = vbuf.get_info().get_width(); + let h = vbuf.get_info().get_height(); + + for (dline, sline) in dst.chunks_mut(w * 2).zip(data[off..].chunks(stride)).take(h) { + for (dst, &src) in dline[..w * 2].chunks_exact_mut(2).zip(sline.iter()) { + dst[0] = src as u8; + dst[1] = (src >> 8) as u8; + } + } + }, + NABufferType::VideoPacked(ref vbuf) => { + let off = vbuf.get_offset(0); + let stride = vbuf.get_stride(0); + let data = vbuf.get_data(); + let w = vbuf.get_info().get_width(); + let h = vbuf.get_info().get_height(); + let w = w * (((bpp as usize) + 7) / 8); + + for (dline, sline) in dst.chunks_mut(w).zip(data[off..].chunks(stride)).take(h) { + dline[..w].copy_from_slice(&sline[..w]); + } + }, + _ => return Err(EncoderError::FormatError), + }; + Ok(()) +} + +fn to_signed(val: usize) -> isize { + if (val & 1) == 0 { + (val >> 1) as isize + } else { + -((val >> 1) as isize) - 1 + } +} + +impl ZMBVEncoder { + fn new() -> Self { + Self { + stream: None, + pkt: None, + frm1: Vec::new(), + frm2: Vec::new(), + pal: [0; 768], + tmp_buf: Vec::new(), + zbuf: Vec::new(), + frmcount: 0, + key_int: 25, + tile_w: 16, + tile_h: 16, + cmode: DeflateMode::default(), + compr: Deflate::new(DeflateMode::default()), + bpp: 0, + width: 0, + height: 0, + range: 128, + } + } + fn encode_intra(&mut self, bw: &mut ByteWriter, buf: NABufferType) -> EncoderResult<()> { + let bpp = buf_type_to_bpp(&buf); + + if let NABufferType::None = buf { + if self.bpp == 0 { + return Err(EncoderError::FormatError); + } + self.frm1.copy_from_slice(&self.frm2); + } else { + if bpp == 0 { + return Err(EncoderError::FormatError); + } + self.bpp = bpp; + + if let (NABufferType::Video(ref vbuf), true) = (&buf, bpp == 8) { + let off = vbuf.get_offset(1); + let data = vbuf.get_data(); + self.pal.copy_from_slice(&data[off..][..768]); + } + + copy_frame(buf, &mut self.frm1, self.bpp)?; + } + + bw.write_byte(1)?; // intra flag + bw.write_byte(0)?; // high version + bw.write_byte(1)?; // low version + bw.write_byte(if self.cmode == DeflateMode::NoCompr { 0 } else { 1 })?; + let fmt = match self.bpp { + 8 => 4, + 15 => 5, + 16 => 6, + 24 => 7, + 32 => 8, + _ => unreachable!(), + }; + bw.write_byte(fmt)?; + bw.write_byte(self.tile_w as u8)?; + bw.write_byte(self.tile_h as u8)?; + + let bm = ((bpp as usize) + 7) / 8; + if self.cmode == DeflateMode::NoCompr { + if bpp == 8 { + bw.write_buf(&self.pal)?; + } + bw.write_buf(&self.frm1[..self.width * self.height * bm])?; + } else { + self.tmp_buf.truncate(0); + if bpp == 8 { + self.tmp_buf.extend_from_slice(&self.pal); + } + self.tmp_buf.extend_from_slice(&self.frm1[..self.width * self.height * bm]); + self.compr = Deflate::new(self.cmode); + + let mut db = Vec::new(); + std::mem::swap(&mut db, &mut self.zbuf); + db.truncate(0); + let mut wr = DeflateWriter::new(db); + self.compr.write_zlib_header(&mut wr); + self.compr.compress(&self.tmp_buf, &mut wr); + self.compr.compress_flush(&mut wr); + let mut db = wr.end(); + std::mem::swap(&mut db, &mut self.zbuf); + + bw.write_buf(&self.zbuf)?; + } + + Ok(()) + } + fn encode_inter(&mut self, bw: &mut ByteWriter, buf: NABufferType) -> EncoderResult<()> { + if let NABufferType::None = buf { + self.frm1.copy_from_slice(&self.frm2); + + bw.write_byte(0)?; + self.tmp_buf.truncate(0); + let tile_w = (self.width + self.tile_w - 1) / self.tile_w; + let tile_h = (self.height + self.tile_h - 1) / self.tile_h; + let mv_size = (tile_w * tile_h * 2 + 3) & !3; + for _ in 0..mv_size { + self.tmp_buf.push(0); + } + if self.cmode == DeflateMode::NoCompr { + bw.write_buf(&self.tmp_buf)?; + } else { + let mut db = Vec::new(); + + std::mem::swap(&mut db, &mut self.zbuf); + db.truncate(0); + let mut wr = DeflateWriter::new(db); + self.compr.compress(&self.tmp_buf, &mut wr); + self.compr.compress_flush(&mut wr); + let mut db = wr.end(); + std::mem::swap(&mut db, &mut self.zbuf); + + bw.write_buf(&self.zbuf)?; + } + return Ok(()); + } + let bpp = buf_type_to_bpp(&buf); + if bpp == 0 || bpp != self.bpp { + return Err(EncoderError::FormatError); + } + + self.tmp_buf.truncate(0); + if let (NABufferType::Video(ref vbuf), true) = (&buf, bpp == 8) { + let mut npal = [0; 768]; + let off = vbuf.get_offset(1); + let data = vbuf.get_data(); + npal.copy_from_slice(&data[off..][..768]); + let mut cmp = true; + for (&a, &b) in self.pal.iter().zip(npal.iter()) { + if a != b { + cmp = false; + break; + } + } + if !cmp { + for (&a, &b) in self.pal.iter().zip(npal.iter()) { + self.tmp_buf.push(a ^ b); + } + self.pal = npal; + + bw.write_byte(2)?; + } else { + bw.write_byte(0)?; + } + } else { + bw.write_byte(0)?; + } + copy_frame(buf, &mut self.frm1, self.bpp)?; + + let tile_w = (self.width + self.tile_w - 1) / self.tile_w; + let tile_h = (self.height + self.tile_h - 1) / self.tile_h; + let mut mv_start = self.tmp_buf.len(); + let mv_size = (tile_w * tile_h * 2 + 3) & !3; + for _ in 0..mv_size { + self.tmp_buf.push(0); + } + + let bpp = ((self.bpp as usize) + 7) / 8; + let stride = self.width * bpp; + let mut off = 0; + for y in (0..self.height).step_by(self.tile_h) { + let cur_h = (self.height - y).min(self.tile_h); + for x in (0..self.width).step_by(self.tile_w) { + let cur_w = (self.width - x).min(self.tile_w); + + let mut best_dist = std::u32::MAX; + let mut best_x = x; + let mut best_y = y; + + 'search: for yoff in 0..self.range { + let ypos = (y as isize) + to_signed(yoff); + if ypos < 0 { + continue; + } + let ypos = ypos as usize; + if ypos + cur_h > self.height { + break; + } + for xoff in 0..self.range { + let xpos = (x as isize) + to_signed(xoff); + if xpos < 0 { + continue; + } + let xpos = xpos as usize; + if xpos + cur_w > self.width { + break; + } + + let mut diff = 0; + let roff = xpos * bpp + ypos * stride; + for (line0, line1) in self.frm1[off..].chunks(stride).take(cur_h).zip(self.frm2[roff..].chunks(stride)) { + for (&a, &b) in line0[..cur_w * bpp].iter().zip(line1[..cur_w * bpp].iter()) { + diff += u32::from(a ^ b); + } + } + + if best_dist > diff { + best_dist = diff; + best_x = xpos; + best_y = ypos; + if diff == 0 { + break 'search; + } + } + } + } + + let has_delta = best_dist != 0; + self.tmp_buf[mv_start] = (best_x.wrapping_sub(x) << 1) as u8; + if has_delta { + self.tmp_buf[mv_start] |= 1; + } + self.tmp_buf[mv_start + 1] = (best_y.wrapping_sub(y) << 1) as u8; + mv_start += 2; + if has_delta { + let rpos = best_x * bpp + best_y * stride; + for (line0, line1) in self.frm1[off..].chunks(stride).take(cur_h).zip(self.frm2[rpos..].chunks(stride)) { + for (&a, &b) in line0[..cur_w * bpp].iter().zip(line1[..cur_w * bpp].iter()) { + self.tmp_buf.push(a ^ b); + } + } + } + + off += self.tile_w * bpp; + } + off -= tile_w * self.tile_w * bpp; + off += stride * self.tile_h; + } + + if self.cmode == DeflateMode::NoCompr { + bw.write_buf(&self.tmp_buf)?; + } else { + let mut db = Vec::new(); + + std::mem::swap(&mut db, &mut self.zbuf); + db.truncate(0); + let mut wr = DeflateWriter::new(db); + self.compr.compress(&self.tmp_buf, &mut wr); + self.compr.compress_flush(&mut wr); + let mut db = wr.end(); + std::mem::swap(&mut db, &mut self.zbuf); + + bw.write_buf(&self.zbuf)?; + } + + Ok(()) + } +} + +impl NAEncoder for ZMBVEncoder { + fn negotiate_format(&self, encinfo: &EncodeParameters) -> EncoderResult { + match encinfo.format { + NACodecTypeInfo::None => { + let mut ofmt = EncodeParameters::default(); + ofmt.format = NACodecTypeInfo::Video(NAVideoInfo::new(0, 0, true, YUV420_FORMAT)); + Ok(ofmt) + }, + NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), + NACodecTypeInfo::Video(vinfo) => { + let depth = vinfo.format.get_total_depth(); + let pix_fmt = if vinfo.format.is_paletted() { + PAL8_FORMAT + } else if !vinfo.format.model.is_rgb() || depth > 16 { + RGB24_0_FORMAT + } else if depth < 16 { + RGB555_FORMAT + } else { + RGB565_FORMAT + }; + let outinfo = NAVideoInfo::new(vinfo.width, vinfo.height, false, pix_fmt); + let mut ofmt = *encinfo; + ofmt.format = NACodecTypeInfo::Video(outinfo); + Ok(ofmt) + } + } + } + fn init(&mut self, stream_id: u32, encinfo: EncodeParameters) -> EncoderResult { + match encinfo.format { + NACodecTypeInfo::None => Err(EncoderError::FormatError), + NACodecTypeInfo::Audio(_) => Err(EncoderError::FormatError), + NACodecTypeInfo::Video(vinfo) => { + self.width = vinfo.width; + self.height = vinfo.height; + + let out_info = NAVideoInfo::new(vinfo.width, vinfo.height, false, vinfo.format); + let info = NACodecInfo::new("zmbv", NACodecTypeInfo::Video(out_info), None); + let mut stream = NAStream::new(StreamType::Video, stream_id, info, encinfo.tb_num, encinfo.tb_den, 0); + stream.set_num(stream_id as usize); + let stream = stream.into_ref(); + + self.stream = Some(stream.clone()); + + self.frm1 = vec![0; vinfo.width * vinfo.height * 4]; + self.frm2 = vec![0; vinfo.width * vinfo.height * 4]; + + Ok(stream) + }, + } + } + fn encode(&mut self, frm: &NAFrame) -> EncoderResult<()> { + let buf = frm.get_buffer(); + let mut dbuf = Vec::with_capacity(4); + let mut gw = GrowableMemoryWriter::new_write(&mut dbuf); + let mut bw = ByteWriter::new(&mut gw); + let is_intra = if self.frmcount == 0 { + self.encode_intra(&mut bw, buf)?; + true + } else { + self.encode_inter(&mut bw, buf)?; + false + }; + self.pkt = Some(NAPacket::new(self.stream.clone().unwrap(), frm.ts, is_intra, dbuf)); + self.frmcount += 1; + if self.frmcount == self.key_int { + self.frmcount = 0; + } + std::mem::swap(&mut self.frm1, &mut self.frm2); + Ok(()) + } + fn get_packet(&mut self) -> EncoderResult> { + let mut npkt = None; + std::mem::swap(&mut self.pkt, &mut npkt); + Ok(npkt) + } + fn flush(&mut self) -> EncoderResult<()> { + self.frmcount = 0; + Ok(()) + } +} + +const ENCODER_OPTS: &[NAOptionDefinition] = &[ + NAOptionDefinition { + name: KEYFRAME_OPTION, description: KEYFRAME_OPTION_DESC, + opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, + NAOptionDefinition { + name: "range", description: "Block search range (0-128)", + opt_type: NAOptionDefinitionType::Int(Some(0), Some(128)) }, + NAOptionDefinition { + name: "tile_width", description: "Block width (1-255)", + opt_type: NAOptionDefinitionType::Int(Some(1), Some(255)) }, + NAOptionDefinition { + name: "tile_height", description: "Block width (1-255)", + opt_type: NAOptionDefinitionType::Int(Some(1), Some(255)) }, + NAOptionDefinition { + name: "compr_level", description: "Compression level", + opt_type: DEFLATE_OPTION_VALUES }, +]; + +impl NAOptionHandler for ZMBVEncoder { + fn get_supported_options(&self) -> &[NAOptionDefinition] { ENCODER_OPTS } + fn set_options(&mut self, options: &[NAOption]) { + for option in options.iter() { + for opt_def in ENCODER_OPTS.iter() { + if opt_def.check(option).is_ok() { + match option.name { + "compr_level" => { + if let NAValue::String(ref s) = option.value { + if let Ok(val) = s.parse::() { + self.cmode = val; + } + } + }, + KEYFRAME_OPTION => { + if let NAValue::Int(intval) = option.value { + self.key_int = intval as u8; + } + }, + "range" => { + if let NAValue::Int(intval) = option.value { + self.range = intval as usize; + } + }, + "tile_width" => { + if let NAValue::Int(intval) = option.value { + self.tile_w = intval as usize; + } + }, + "tile_height" => { + if let NAValue::Int(intval) = option.value { + self.tile_h = intval as usize; + } + }, + _ => {}, + }; + } + } + } + } + fn query_option_value(&self, name: &str) -> Option { + match name { + "compr_level" => Some(NAValue::String(self.cmode.to_string())), + KEYFRAME_OPTION => Some(NAValue::Int(i64::from(self.key_int))), + "range" => Some(NAValue::Int(self.range as i64)), + "tile_width" => Some(NAValue::Int(self.tile_w as i64)), + "tile_height" => Some(NAValue::Int(self.tile_h as i64)), + _ => None, + } + } +} + +pub fn get_encoder() -> Box { + Box::new(ZMBVEncoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::*; + use nihav_core::demuxers::*; + use nihav_core::muxers::*; + use crate::*; + use nihav_codec_support::test::enc_video::*; + use super::{RGB555_FORMAT, RGB24_0_FORMAT}; + + #[test] + fn test_zmbv_encoder_8bit() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + let mut mux_reg = RegisteredMuxers::new(); + generic_register_all_muxers(&mut mux_reg); + let mut enc_reg = RegisteredEncoders::new(); + generic_register_all_encoders(&mut enc_reg); + + let dec_config = DecoderTestParams { + demuxer: "avi", + in_name: "assets/Misc/td3_000.avi", + stream_type: StreamType::Video, + limit: Some(20), + dmx_reg, dec_reg, + }; + let enc_config = EncoderTestParams { + muxer: "avi", + enc_name: "zmbv", + out_name: "zmbv8.avi", + mux_reg, enc_reg, + }; + let dst_vinfo = NAVideoInfo { + width: 0, + height: 0, + format: PAL8_FORMAT, + flipped: false, + bits: 8, + }; + let enc_params = EncodeParameters { + format: NACodecTypeInfo::Video(dst_vinfo), + quality: 0, + bitrate: 0, + tb_num: 0, + tb_den: 0, + flags: 0, + }; + //test_encoding_to_file(&dec_config, &enc_config, enc_params); + test_encoding_md5(&dec_config, &enc_config, enc_params, + &[0x50df10e2, 0x606f3268, 0xdd4bc2ff, 0x844e7d87]); + } + + #[test] + fn test_zmbv_encoder_15bit() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + let mut mux_reg = RegisteredMuxers::new(); + generic_register_all_muxers(&mut mux_reg); + let mut enc_reg = RegisteredEncoders::new(); + generic_register_all_encoders(&mut enc_reg); + + let dec_config = DecoderTestParams { + demuxer: "avi", + in_name: "assets/Misc/zmbv_15bit.avi", + stream_type: StreamType::Video, + limit: Some(4), + dmx_reg, dec_reg, + }; + let enc_config = EncoderTestParams { + muxer: "avi", + enc_name: "zmbv", + out_name: "zmbv15.avi", + mux_reg, enc_reg, + }; + let dst_vinfo = NAVideoInfo { + width: 0, + height: 0, + format: RGB555_FORMAT, + flipped: false, + bits: 8, + }; + let enc_params = EncodeParameters { + format: NACodecTypeInfo::Video(dst_vinfo), + quality: 0, + bitrate: 0, + tb_num: 0, + tb_den: 0, + flags: 0, + }; + //test_encoding_to_file(&dec_config, &enc_config, enc_params); + test_encoding_md5(&dec_config, &enc_config, enc_params, + &[0x0b4cb528, 0x66c91f6c, 0x1c2187a5, 0x2723a08d]); + } + + #[test] + fn test_zmbv_encoder_16bit() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + let mut mux_reg = RegisteredMuxers::new(); + generic_register_all_muxers(&mut mux_reg); + let mut enc_reg = RegisteredEncoders::new(); + generic_register_all_encoders(&mut enc_reg); + + let dec_config = DecoderTestParams { + demuxer: "avi", + in_name: "assets/Misc/zmbv_16bit.avi", + stream_type: StreamType::Video, + limit: Some(4), + dmx_reg, dec_reg, + }; + let enc_config = EncoderTestParams { + muxer: "avi", + enc_name: "zmbv", + out_name: "zmbv16.avi", + mux_reg, enc_reg, + }; + let dst_vinfo = NAVideoInfo { + width: 0, + height: 0, + format: RGB565_FORMAT, + flipped: false, + bits: 8, + }; + let enc_params = EncodeParameters { + format: NACodecTypeInfo::Video(dst_vinfo), + quality: 0, + bitrate: 0, + tb_num: 0, + tb_den: 0, + flags: 0, + }; + //test_encoding_to_file(&dec_config, &enc_config, enc_params); + test_encoding_md5(&dec_config, &enc_config, enc_params, + &[0x1a522743, 0x6c320a6e, 0xd08539e1, 0x03fc17ea]); + } + + #[test] + fn test_zmbv_encoder_32bit() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + generic_register_all_decoders(&mut dec_reg); + let mut mux_reg = RegisteredMuxers::new(); + generic_register_all_muxers(&mut mux_reg); + let mut enc_reg = RegisteredEncoders::new(); + generic_register_all_encoders(&mut enc_reg); + + let dec_config = DecoderTestParams { + demuxer: "avi", + in_name: "assets/Misc/zmbv_32bit.avi", + stream_type: StreamType::Video, + limit: Some(4), + dmx_reg, dec_reg, + }; + let enc_config = EncoderTestParams { + muxer: "avi", + enc_name: "zmbv", + out_name: "zmbv32.avi", + mux_reg, enc_reg, + }; + let dst_vinfo = NAVideoInfo { + width: 0, + height: 0, + format: RGB24_0_FORMAT, + flipped: false, + bits: 8, + }; + let enc_params = EncodeParameters { + format: NACodecTypeInfo::Video(dst_vinfo), + quality: 0, + bitrate: 0, + tb_num: 0, + tb_den: 0, + flags: 0, + }; + //test_encoding_to_file(&dec_config, &enc_config, enc_params); + test_encoding_md5(&dec_config, &enc_config, enc_params, + &[0x3880e045, 0xe6c88dc7, 0x21066058, 0xc789f1e9]); + } +}