From 801bba83e901a9d496d05692d0489d31b21e570f Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Thu, 26 Sep 2019 18:52:50 +0200 Subject: [PATCH] MidiVid decoder --- nihav-core/src/register.rs | 7 ++ nihav-game/Cargo.toml | 6 +- nihav-game/src/codecs/midivid.rs | 202 +++++++++++++++++++++++++++++++ nihav-game/src/codecs/mod.rs | 4 + 4 files changed, 218 insertions(+), 1 deletion(-) create mode 100644 nihav-game/src/codecs/midivid.rs diff --git a/nihav-core/src/register.rs b/nihav-core/src/register.rs index 817263e..e00447e 100644 --- a/nihav-core/src/register.rs +++ b/nihav-core/src/register.rs @@ -168,6 +168,9 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[ desc!(audio; "bmv-audio", "BMV audio"), desc!(video; "bmv3-video", "DW Noir BMV video"), desc!(audio; "bmv3-audio", "DW Noir BMV audio"), + desc!(video; "midivid", "MidiVid"), + desc!(video; "midivid3", "MidiVid 3"), + desc!(video-ll; "midivid-ll", "MidiVid Lossless"), desc!(video; "vmd-video", "VMD video"), desc!(audio; "vmd-audio", "VMD audio"), @@ -190,6 +193,10 @@ static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[ (b"UCOD", "clearvideo"), + (b"MVDV", "midivid"), + (b"MV30", "midivid3"), + (b"MVLZ", "midivid-ll"), + (b"DUCK", "truemotion1"), (b"TR20", "truemotionrt"), (b"TM20", "truemotion2"), diff --git a/nihav-game/Cargo.toml b/nihav-game/Cargo.toml index 3907a6f..a429135 100644 --- a/nihav-game/Cargo.toml +++ b/nihav-game/Cargo.toml @@ -8,6 +8,9 @@ edition = "2018" path = "../nihav-core" features = [] +[dev-dependencies] +nihav_commonfmt = { path = "../nihav-commonfmt" } + [features] default = ["all_decoders", "all_demuxers"] demuxers = [] @@ -20,10 +23,11 @@ demuxer_vmd = ["demuxers"] all_decoders = ["all_video_decoders", "all_audio_decoders"] decoders = [] -all_video_decoders = ["decoder_bmv", "decoder_bmv3", "decoder_gdvvid", "decoder_vmd"] +all_video_decoders = ["decoder_bmv", "decoder_bmv3", "decoder_gdvvid", "decoder_midivid", "decoder_vmd"] decoder_bmv = ["decoders"] decoder_bmv3 = ["decoders"] decoder_gdvvid = ["decoders"] +decoder_midivid = ["decoders"] decoder_vmd = ["decoders"] all_audio_decoders = [] diff --git a/nihav-game/src/codecs/midivid.rs b/nihav-game/src/codecs/midivid.rs new file mode 100644 index 0000000..299e5ac --- /dev/null +++ b/nihav-game/src/codecs/midivid.rs @@ -0,0 +1,202 @@ +use nihav_core::codecs::*; +use nihav_core::io::byteio::*; + +#[derive(Default)] +struct MidividDecoder { + info: NACodecInfoRef, + hams: HAMShuffler, + lzbuf: Vec, + width: usize, + height: usize, +} + +impl MidividDecoder { + fn new() -> Self { + Self::default() + } +} + +fn lz_decompress(src: &[u8], dst: &mut [u8]) -> DecoderResult<()> { + let mut spos = 0; + let mut dpos = 0; + let end = src.len(); + while spos < end { + let oplo = src[spos] as u16; + spos += 1; + if spos >= end { return Err(DecoderError::ShortData); } + let ophi = src[spos] as u16; + spos += 1; + let mut op = (ophi << 8) | oplo; + for _ in 0..16 { + if spos >= end { return Ok(()); } + let b = src[spos]; + spos += 1; + + if (op & 1) == 0 { + validate!(dpos < dst.len()); + dst[dpos] = b; + dpos += 1; + } else { + validate!(spos < end); + let bb = src[spos]; + spos += 1; + + let offset = (((b as usize) & 0xF0) << 4) | (bb as usize); + let copy_len = ((b & 0xF) as usize) + 3; + validate!(offset <= dpos); + validate!(offset > 0); + validate!(dpos + copy_len <= dst.len()); + for _ in 0..copy_len { + dst[dpos] = dst[dpos - offset]; + dpos += 1; + } + } + op >>= 1; + } + } + Ok(()) +} + +fn decode_frame(frm: &mut NASimpleVideoFrame, src: &[u8], width: usize, height: usize) -> DecoderResult { + validate!(src.len() > 8); + let num_vec = read_u16le(&src[0..])? as usize; + validate!(num_vec <= 512); + let is_intra = read_u16le(&src[2..])? == 1; + + let (vecs, nblocks, idx_start) = if is_intra { + (&src[4..], width / 2 * height / 2, num_vec * 12 + 4) + } else { + let num_blocks = read_u32le(&src[4..])? as usize; + let changeset_size = (width >> 5) * (height >> 2); + (&src[8+changeset_size..], num_blocks, num_vec * 12 + 8 + changeset_size) + }; + validate!(src.len() > idx_start); + + let src1 = if num_vec > 256 { &src[idx_start + (nblocks + 7)/8..] } else { &src[idx_start..] }; + let mut mr = MemoryReader::new_read(src1); + let mut idx_br = ByteReader::new(&mut mr); + let mut mr = MemoryReader::new_read(&src[idx_start..]); + let mut idx9_br = ByteReader::new(&mut mr); + let mut hi9 = 0u8; + let mut bits = 0u8; + for y in (0..height).step_by(2) { + for x in (0..width).step_by(2) { + if !is_intra { + let x4 = x >> 2; + let flag_b = src[8 + x4/8 + (y/4) * ((width + 31) >> 5)]; + if ((flag_b >> (x4 & 7)) & 1) == 0 { + continue; + } + } + let idx = if num_vec <= 256 { + idx_br.read_byte()? as usize + } else { + if bits == 0 { + hi9 = idx9_br.read_byte()?; + bits = 8; + } + bits -= 1; + let lo = idx_br.read_byte()? as usize; + + ((((hi9 >> (7 - bits)) & 1) as usize) << 8) | lo + }; + validate!(idx < num_vec); + let vec = &vecs[idx * 12..]; + + for comp in 0..3 { + let dst = &mut frm.data[frm.offset[comp] + x + y * frm.stride[comp]..]; + dst[0] = vec[0 + comp]; + dst[1] = vec[3 + comp]; + dst[frm.stride[comp] + 0] = vec[6 + comp]; + dst[frm.stride[comp] + 1] = vec[9 + comp]; + } + } + } + + Ok(is_intra) +} + +impl NADecoder for MidividDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { + let fmt = NAPixelFormaton::new(ColorModel::YUV(YUVSubmodel::YCbCr), + Some(NAPixelChromaton::new(0, 0, false, 8, 0, 0, 1)), + Some(NAPixelChromaton::new(0, 0, false, 8, 0, 1, 1)), + Some(NAPixelChromaton::new(0, 0, false, 8, 0, 2, 1)), + None, None, 0, 3); + self.width = vinfo.get_width(); + self.height = vinfo.get_height(); + let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(self.width, self.height, true, fmt)); + self.info = NACodecInfo::new_ref(info.get_name(), myinfo, info.get_extradata()).into_ref(); + self.lzbuf = vec![0; self.width * self.height * 3]; + + Ok(()) + } else { + Err(DecoderError::InvalidData) + } + } + fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult { + let src = pkt.get_buffer(); + validate!(src.len() > 4); + + let size = read_u32le(&src[0..])? as usize; + validate!(size + 8 == src.len()); + let data_ptr; + validate!(src.len() > 12); + if read_u32le(&src[8..])? == 0 { + lz_decompress(&src[12..], self.lzbuf.as_mut_slice())?; + data_ptr = self.lzbuf.as_slice(); + } else { + data_ptr = &src[12..]; + } + + let mut buf; + let bufret = self.hams.clone_ref(); + if let Some(bbuf) = bufret { + buf = bbuf; + } else { + let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 4)?; + buf = bufinfo.get_vbuf().unwrap(); + self.hams.add_frame(buf); + buf = self.hams.get_output_frame().unwrap(); + } + + let mut frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap(); + let is_intra = decode_frame(&mut frm, data_ptr, self.width, self.height)?; + + let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), NABufferType::Video(buf)); + frm.set_keyframe(is_intra); + frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P }); + Ok(frm.into_ref()) + } +} + + +pub fn get_decoder_video() -> Box { + Box::new(MidividDecoder::new()) +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::RegisteredDecoders; + use nihav_core::demuxers::RegisteredDemuxers; + use nihav_core::test::dec_video::*; + use crate::codecs::game_register_all_codecs; + use nihav_commonfmt::demuxers::generic_register_all_demuxers; + #[test] + fn test_midivid_video() { + let mut dmx_reg = RegisteredDemuxers::new(); + generic_register_all_demuxers(&mut dmx_reg); + let mut dec_reg = RegisteredDecoders::new(); + game_register_all_codecs(&mut dec_reg); + + let file = "assets/Game/MVDV.avi"; + //let file = "assets/Game/bbglogo.avi"; + //let file = "assets/Game/close.avi"; + //let file = "assets/Game/inland.avi"; + //let file = "assets/Game/midway.avi"; + //let file = "assets/Game/midway.1.avi"; + //let file = "assets/Game/open.avi"; + test_file_decoding("avi", file, Some(16), true, false, None, &dmx_reg, &dec_reg); + } +} diff --git a/nihav-game/src/codecs/mod.rs b/nihav-game/src/codecs/mod.rs index 29e025e..6f707e9 100644 --- a/nihav-game/src/codecs/mod.rs +++ b/nihav-game/src/codecs/mod.rs @@ -10,6 +10,8 @@ pub mod bmv; pub mod bmv3; #[cfg(feature="decoder_gdvvid")] pub mod gremlinvideo; +#[cfg(feature="decoder_midivid")] +pub mod midivid; #[cfg(feature="decoder_vmd")] pub mod vmd; @@ -30,6 +32,8 @@ const GAME_CODECS: &[DecoderInfo] = &[ DecoderInfo { name: "vmd-audio", get_decoder: vmd::get_decoder_audio }, #[cfg(feature="decoder_vmd")] DecoderInfo { name: "vmd-video", get_decoder: vmd::get_decoder_video }, +#[cfg(feature="decoder_midivid")] + DecoderInfo { name: "midivid", get_decoder: midivid::get_decoder_video }, ]; pub fn game_register_all_codecs(rd: &mut RegisteredDecoders) { -- 2.30.2