MidiVid decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 26 Sep 2019 16:52:50 +0000 (18:52 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 26 Sep 2019 16:52:50 +0000 (18:52 +0200)
nihav-core/src/register.rs
nihav-game/Cargo.toml
nihav-game/src/codecs/midivid.rs [new file with mode: 0644]
nihav-game/src/codecs/mod.rs

index 817263e0c062ac81d90e3a091709138a526d9130..e00447e026dcae5223a0f0e2bae6970301732e3f 100644 (file)
@@ -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"),
index 3907a6f89b6b04fde7e9a0160a8ecc98cd2409c0..a429135d5aec5d0229d05abade690bd8a8aa2071 100644 (file)
@@ -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 (file)
index 0000000..299e5ac
--- /dev/null
@@ -0,0 +1,202 @@
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+
+#[derive(Default)]
+struct MidividDecoder {
+    info:       NACodecInfoRef,
+    hams:       HAMShuffler,
+    lzbuf:      Vec<u8>,
+    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<u8>, src: &[u8], width: usize, height: usize) -> DecoderResult<bool> {
+    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<NAFrameRef> {
+        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<dyn NADecoder> {
+    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);
+    }
+}
index 29e025e0d756c26e433b7a96e5ff684271a605c3..6f707e98bd1578046bbca36640f827ef28af1617 100644 (file)
@@ -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) {