add a crate for common MS formats
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 24 May 2020 13:19:43 +0000 (15:19 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 24 May 2020 13:19:43 +0000 (15:19 +0200)
nihav-allstuff/Cargo.toml
nihav-allstuff/src/lib.rs
nihav-ms/Cargo.toml [new file with mode: 0644]
nihav-ms/src/codecs/imaadpcm.rs [new file with mode: 0644]
nihav-ms/src/codecs/mod.rs [new file with mode: 0644]
nihav-ms/src/codecs/msadpcm.rs [new file with mode: 0644]
nihav-ms/src/codecs/msvideo1.rs [new file with mode: 0644]
nihav-ms/src/lib.rs [new file with mode: 0644]
nihav-registry/src/register.rs

index b8ef8698dbc7b69095b6d9fe3a481cdee675c49c..49aeaae28ce918335773b0b42a8b322449d6a295 100644 (file)
@@ -10,6 +10,7 @@ nihav_commonfmt = { path = "../nihav-commonfmt" }
 nihav_duck = { path = "../nihav-duck" }
 nihav_game = { path = "../nihav-game" }
 nihav_indeo = { path = "../nihav-indeo" }
+nihav_ms = { path = "../nihav-ms" }
 nihav_rad = { path = "../nihav-rad" }
 nihav_realmedia = { path = "../nihav-realmedia" }
 
index 4c2214a82fd2be5a746e5e9eaa7a349cd01b91fd..759ef54e502634e43c6bc832a4d16ef3386f5e9c 100644 (file)
@@ -4,6 +4,7 @@ extern crate nihav_commonfmt;
 extern crate nihav_duck;
 extern crate nihav_game;
 extern crate nihav_indeo;
+extern crate nihav_ms;
 extern crate nihav_rad;
 extern crate nihav_realmedia;
 
@@ -20,6 +21,8 @@ use nihav_game::game_register_all_demuxers;
 
 use nihav_indeo::indeo_register_all_codecs;
 
+use nihav_ms::ms_register_all_codecs;
+
 use nihav_rad::rad_register_all_codecs;
 use nihav_rad::rad_register_all_demuxers;
 
@@ -32,6 +35,7 @@ pub fn nihav_register_all_codecs(rd: &mut RegisteredDecoders) {
     duck_register_all_codecs(rd);
     game_register_all_codecs(rd);
     indeo_register_all_codecs(rd);
+    ms_register_all_codecs(rd);
     rad_register_all_codecs(rd);
     realmedia_register_all_codecs(rd);
 }
diff --git a/nihav-ms/Cargo.toml b/nihav-ms/Cargo.toml
new file mode 100644 (file)
index 0000000..6c925ad
--- /dev/null
@@ -0,0 +1,28 @@
+[package]
+name = "nihav_ms"
+version = "0.1.0"
+authors = ["Kostya Shishkov <kostya.shishkov@gmail.com>"]
+edition = "2018"
+
+[dependencies.nihav_core]
+path = "../nihav-core"
+features = []
+
+[dependencies.nihav_codec_support]
+path = "../nihav-codec-support"
+features = []
+
+[dev-dependencies]
+nihav_commonfmt = { path = "../nihav-commonfmt" }
+
+[features]
+default = ["all_decoders"]
+all_decoders = ["all_video_decoders", "all_audio_decoders"]
+decoders = []
+
+all_video_decoders = ["decoder_msvideo1"]
+decoder_msvideo1 = ["decoders"]
+
+all_audio_decoders = ["decoder_ima_adpcm_ms", "decoder_ms_adpcm"]
+decoder_ima_adpcm_ms = ["decoders"]
+decoder_ms_adpcm = ["decoders"]
diff --git a/nihav-ms/src/codecs/imaadpcm.rs b/nihav-ms/src/codecs/imaadpcm.rs
new file mode 100644 (file)
index 0000000..42730ee
--- /dev/null
@@ -0,0 +1,106 @@
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+use nihav_codec_support::codecs::imaadpcm::*;
+use std::str::FromStr;
+
+struct IMAADPCMDecoder {
+    ainfo:      NAAudioInfo,
+    chmap:      NAChannelMap,
+    ch_state:   [IMAState; 2],
+    block_len:  usize,
+}
+
+impl IMAADPCMDecoder {
+    fn new() -> Self {
+        Self {
+            ainfo:      NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0),
+            chmap:      NAChannelMap::new(),
+            ch_state:   [IMAState::new(), IMAState::new()],
+            block_len:  0,
+        }
+    }
+}
+
+impl NADecoder for IMAADPCMDecoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() {
+            self.block_len = ainfo.get_block_len();
+            let channels = ainfo.get_channels() as usize;
+            validate!(channels == 2 || channels == 1);
+            validate!(self.block_len >= 8 * channels);
+            validate!((self.block_len & 3) == 0);
+            let len = (self.block_len / channels - 4) * 2 + 1;
+            self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, len);
+            self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap();
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let info = pkt.get_stream().get_info();
+        if let NACodecTypeInfo::Audio(_) = info.get_properties() {
+            let pktbuf = pkt.get_buffer();
+            let channels = self.chmap.num_channels();
+            validate!(pktbuf.len() >= 8 * channels);
+            let nsamples = (pktbuf.len() / channels - 4) * 2 + 1;
+            let abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?;
+            let mut adata = abuf.get_abuf_i16().unwrap();
+            let mut off = [adata.get_offset(0), adata.get_offset(1)];
+            let dst = adata.get_data_mut().unwrap();
+
+            let mut mr = MemoryReader::new_read(pktbuf.as_slice());
+            let mut br = ByteReader::new(&mut mr);
+            for ch in 0..channels {
+                let pred                        = br.read_u16le()? as i16;
+                let step                        = br.read_byte()?;
+                                                  br.read_skip(1)?;
+                validate!(step <= IMA_MAX_STEP);
+                self.ch_state[ch].reset(pred, step);
+                dst[off[ch]] = pred;
+                off[ch] += 1;
+            }
+            while br.left() > 0 {
+                for ch in 0..channels {
+                    let mut cw                  = br.read_u32le()?;
+                    for _ in 0..8 {
+                        dst[off[ch]] = self.ch_state[ch].expand_sample((cw & 0xF) as u8);
+                        off[ch] += 1;
+                        cw >>= 4;
+                    }
+                }
+            }
+            let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf);
+            frm.set_duration(Some(nsamples as u64));
+            frm.set_keyframe(false);
+            Ok(frm.into_ref())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn flush(&mut self) {
+    }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(IMAADPCMDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::ms_register_all_codecs;
+    use nihav_commonfmt::generic_register_all_demuxers;
+    #[test]
+    fn test_ima_adpcm_ms() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        ms_register_all_codecs(&mut dec_reg);
+
+        test_decoding("avi", "ima-adpcm-ms", "assets/MS/IMAG0006.AVI", None, &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5([0x0cdc640f, 0xb00df235, 0x1ec4a280, 0x065b5e9e]));
+    }
+}
diff --git a/nihav-ms/src/codecs/mod.rs b/nihav-ms/src/codecs/mod.rs
new file mode 100644 (file)
index 0000000..0e1a237
--- /dev/null
@@ -0,0 +1,30 @@
+use nihav_core::codecs::*;
+
+macro_rules! validate {
+    ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DecoderError::InvalidData); } };
+}
+
+#[cfg(feature="decoder_msvideo1")]
+pub mod msvideo1;
+
+#[cfg(feature="decoder_ima_adpcm_ms")]
+pub mod imaadpcm;
+
+#[cfg(feature="decoder_ms_adpcm")]
+pub mod msadpcm;
+
+const MS_CODECS: &[DecoderInfo] = &[
+#[cfg(feature="decoder_msvideo1")]
+    DecoderInfo { name: "msvideo1", get_decoder: msvideo1::get_decoder },
+#[cfg(feature="decoder_ima_adpcm_ms")]
+    DecoderInfo { name: "ima-adpcm-ms", get_decoder: imaadpcm::get_decoder },
+#[cfg(feature="decoder_ms_adpcm")]
+    DecoderInfo { name: "ms-adpcm", get_decoder: msadpcm::get_decoder },
+];
+
+/// Registers all available codecs provided by this crate.
+pub fn ms_register_all_codecs(rd: &mut RegisteredDecoders) {
+    for decoder in MS_CODECS.iter() {
+        rd.add_decoder(decoder.clone());
+    }
+}
diff --git a/nihav-ms/src/codecs/msadpcm.rs b/nihav-ms/src/codecs/msadpcm.rs
new file mode 100644 (file)
index 0000000..687e0bd
--- /dev/null
@@ -0,0 +1,178 @@
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+use std::str::FromStr;
+
+const ADAPT_TABLE: [i32; 16] = [
+    230, 230, 230, 230, 307, 409, 512, 614, 
+    768, 614, 512, 409, 307, 230, 230, 230
+];
+const ADAPT_COEFFS: [[i32; 2]; 7] = [
+    [ 256, 0 ], [ 512, -256 ], [ 0, 0 ], [ 192, 64 ],
+    [ 240, 0 ], [ 460, -208 ], [ 392, -232 ]
+];
+
+#[derive(Default)]
+struct Predictor {
+    sample1:    i32,
+    sample2:    i32,
+    delta:      i32,
+    coef1:      i32,
+    coef2:      i32,
+}
+
+impl Predictor {
+    fn expand_nibble(&mut self, nibble: u8) -> i16 {
+        let mul = if (nibble & 8) == 0 { i32::from(nibble) } else { i32::from(nibble) - 16 };
+        let pred = ((self.sample1.wrapping_mul(self.coef1) + self.sample2.wrapping_mul(self.coef2)) >> 8) + self.delta.wrapping_mul(mul);
+        self.sample2 = self.sample1;
+        self.sample1 = pred.max(-0x8000).min(0x7FFF);
+        self.delta = (ADAPT_TABLE[nibble as usize].wrapping_mul(self.delta) >> 8).max(16);
+        self.sample1 as i16
+    }
+}
+
+struct MSADPCMDecoder {
+    ainfo:          NAAudioInfo,
+    chmap:          NAChannelMap,
+    adapt_coeffs:   Vec<[i32; 2]>,
+    block_len:      usize,
+    block_samps:    usize,
+}
+
+impl MSADPCMDecoder {
+    fn new() -> Self {
+        Self {
+            ainfo:          NAAudioInfo::new(0, 1, SND_S16P_FORMAT, 0),
+            chmap:          NAChannelMap::new(),
+            adapt_coeffs:   Vec::with_capacity(7),
+            block_len:      0,
+            block_samps:    0,
+        }
+    }
+}
+
+impl NADecoder for MSADPCMDecoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Audio(ainfo) = info.get_properties() {
+            self.block_len = ainfo.get_block_len();
+            let channels = ainfo.get_channels() as usize;
+            validate!(channels == 2 || channels == 1);
+            validate!(self.block_len >= 7 * channels + 1);
+            self.block_samps = (self.block_len / channels - 7) * 2 + 2;
+            self.ainfo = NAAudioInfo::new(ainfo.get_sample_rate(), channels as u8, SND_S16P_FORMAT, self.block_samps);
+            self.chmap = NAChannelMap::from_str(if channels == 1 { "C" } else { "L,R" }).unwrap();
+            self.adapt_coeffs.truncate(0);
+            if let Some(ref buf) = info.get_extradata() {
+                validate!(buf.len() >= 6);
+                validate!((buf.len() & 3) == 0);
+                let mut mr = MemoryReader::new_read(buf.as_slice());
+                let mut br = ByteReader::new(&mut mr);
+                let _smth               = br.read_u16le()?;
+                let ncoeffs             = br.read_u16le()? as usize;
+                validate!(buf.len() == ncoeffs * 4 + 4);
+
+                for _ in 0..ncoeffs {
+                    let pair = [
+                        i32::from(br.read_u16le()? as i16),
+                        i32::from(br.read_u16le()? as i16)];
+                    self.adapt_coeffs.push(pair);
+                }
+            } else {
+                self.adapt_coeffs.extend_from_slice(&ADAPT_COEFFS);
+            }
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let info = pkt.get_stream().get_info();
+        if let NACodecTypeInfo::Audio(_) = info.get_properties() {
+            let pktbuf = pkt.get_buffer();
+            let channels = self.chmap.num_channels();
+            validate!(pktbuf.len() > 0 && (pktbuf.len() % self.block_len) == 0);
+            let nblocks = pktbuf.len() / self.block_len;
+            let nsamples = nblocks * self.block_samps;
+            let abuf = alloc_audio_buffer(self.ainfo, nsamples, self.chmap.clone())?;
+            let mut adata = abuf.get_abuf_i16().unwrap();
+            let mut off = [adata.get_offset(0), adata.get_offset(1)];
+            let dst = adata.get_data_mut().unwrap();
+
+            let mut pred = [Predictor::default(), Predictor::default()];
+
+            for blk in pktbuf.chunks(self.block_len) {
+                let mut mr = MemoryReader::new_read(blk);
+                let mut br = ByteReader::new(&mut mr);
+                for ch in 0..channels {
+                    let coef_idx                = br.read_byte()? as usize;
+                    validate!(coef_idx < self.adapt_coeffs.len());
+                    pred[ch].coef1 = self.adapt_coeffs[coef_idx][0];
+                    pred[ch].coef2 = self.adapt_coeffs[coef_idx][1];
+                }
+                for ch in 0..channels {
+                    pred[ch].delta              = i32::from(br.read_u16le()?);
+                }
+                for ch in 0..channels {
+                    let samp                    = br.read_u16le()? as i16;
+                    pred[ch].sample1            = i32::from(samp);
+                    dst[off[ch]] = samp;
+                    off[ch] += 1;
+                }
+                for ch in 0..channels {
+                    let samp                    = br.read_u16le()? as i16;
+                    pred[ch].sample2            = i32::from(samp);
+                    dst[off[ch]] = samp;
+                    off[ch] += 1;
+                }
+                if channels == 1 {
+                    while br.left() > 0 {
+                        let idx                 = br.read_byte()?;
+                        dst[off[0]] = pred[0].expand_nibble(idx >> 4);
+                        off[0] += 1;
+                        dst[off[0]] = pred[0].expand_nibble(idx & 0xF);
+                        off[0] += 1;
+                    }
+                } else {
+                    while br.left() > 0 {
+                        let idx                 = br.read_byte()?;
+                        dst[off[0]] = pred[0].expand_nibble(idx >> 4);
+                        off[0] += 1;
+                        dst[off[1]] = pred[1].expand_nibble(idx & 0xF);
+                        off[1] += 1;
+                    }
+                }
+            }
+            let mut frm = NAFrame::new_from_pkt(pkt, info.replace_info(NACodecTypeInfo::Audio(self.ainfo)), abuf);
+            frm.set_duration(Some(nsamples as u64));
+            frm.set_keyframe(false);
+            Ok(frm.into_ref())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn flush(&mut self) {
+    }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(MSADPCMDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::ms_register_all_codecs;
+    use nihav_commonfmt::generic_register_all_demuxers;
+    #[test]
+    fn test_ms_adpcm() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        ms_register_all_codecs(&mut dec_reg);
+
+        test_decoding("avi", "ms-adpcm", "assets/MS/dance.avi", None, &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5([0x9d6619e1, 0x60d83560, 0xfe5c1fb7, 0xad5d130d]));
+    }
+}
diff --git a/nihav-ms/src/codecs/msvideo1.rs b/nihav-ms/src/codecs/msvideo1.rs
new file mode 100644 (file)
index 0000000..b7bea58
--- /dev/null
@@ -0,0 +1,312 @@
+use nihav_core::codecs::*;
+use nihav_core::io::byteio::*;
+use nihav_codec_support::codecs::HAMShuffler;
+
+struct HAMShuffler16 {
+    lastframe: Option<NAVideoBufferRef<u16>>,
+}
+
+impl HAMShuffler16 {
+    fn clear(&mut self) { self.lastframe = None; }
+    fn add_frame(&mut self, buf: NAVideoBufferRef<u16>) {
+        self.lastframe = Some(buf);
+    }
+    fn clone_ref(&mut self) -> Option<NAVideoBufferRef<u16>> {
+        if let Some(ref mut frm) = self.lastframe {
+            let newfrm = frm.copy_buffer();
+            *frm = newfrm.clone().into_ref();
+            Some(newfrm.into_ref())
+        } else {
+            None
+        }
+    }
+    fn get_output_frame(&mut self) -> Option<NAVideoBufferRef<u16>> {
+        match self.lastframe {
+            Some(ref frm) => Some(frm.clone()),
+            None => None,
+        }
+    }
+}
+
+impl Default for HAMShuffler16 {
+    fn default() -> Self { Self { lastframe: None } }
+}
+
+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 };
+
+#[derive(Default)]
+struct Video1Decoder {
+    info:       NACodecInfoRef,
+    hams:       HAMShuffler,
+    hams16:     HAMShuffler16,
+    width:      usize,
+    height:     usize,
+    is_16bit:   bool,
+}
+
+impl Video1Decoder {
+    fn new() -> Self {
+        Self::default()
+    }
+    fn decode_frame(&self, br: &mut ByteReader, frm: &mut NASimpleVideoFrame<u8>) -> DecoderResult<FrameType> {
+        let off = frm.offset[0];
+        let stride = frm.stride[0];
+        let mut skip_count = 0;
+        let blk_w = (self.width  + 3) >> 2;
+        let blk_h = (self.height + 3) >> 2;
+        let mut cur_x = 0;
+        let mut cur_y = 0;
+        while cur_x < blk_w && cur_y < blk_h {
+            let op                      = br.read_u16le()?;
+            let advance;
+            if op < 0x8000 {
+                let mut clr = [0; 2];
+                                          br.read_buf(&mut clr)?;
+                let mut flags = !op as usize;
+                let cur_off = off + cur_x * 4 + cur_y * 4 * stride;
+                for j in 0..4 {
+                    for i in 0..4 {
+                        frm.data[cur_off + i + j * stride] = clr[flags & 1];
+                        flags >>= 1;
+                    }
+                }
+                advance = 1;
+            } else if op < 0x8400 {
+                let cur_off = off + cur_x * 4 + cur_y * 4 * stride;
+                let clr = op as u8;
+                for j in 0..4 {
+                    for i in 0..4 {
+                        frm.data[cur_off + i + j * stride] = clr;
+                    }
+                }
+                advance = 1;
+            } else if op < 0x8800 {
+                advance = (op & 0x3FF) as usize;
+                validate!(advance > 0);
+                skip_count += advance;
+            } else {
+                let mut clr = [0; 8];
+                                      br.read_buf(&mut clr)?;
+                let mut flags = !op as usize;
+                let cur_off = off + cur_x * 4 + cur_y * 4 * stride;
+                for j in 0..4 {
+                    for i in 0..4 {
+                        frm.data[cur_off + i + j * stride] = clr[(i >> 1) * 2 + (j >> 1) * 4 + (flags & 1)];
+                        flags >>= 1;
+                    }
+                }
+                advance = 1;
+            }
+            cur_x += advance;
+            while cur_x >= blk_w {
+                cur_x -= blk_w;
+                cur_y += 1;
+            }
+        }
+        validate!(cur_x == 0 && cur_y == blk_h);
+        if skip_count == 0 {
+            Ok(FrameType::I)
+        } else if skip_count < blk_w * blk_h {
+            Ok(FrameType::P)
+        } else {
+            Ok(FrameType::Skip)
+        }
+    }
+    fn decode_frame16(&self, br: &mut ByteReader, frm: &mut NASimpleVideoFrame<u16>) -> DecoderResult<FrameType> {
+        let off = frm.offset[0];
+        let stride = frm.stride[0];
+        let mut skip_count = 0;
+        let blk_w = (self.width  + 3) >> 2;
+        let blk_h = (self.height + 3) >> 2;
+        let mut cur_x = 0;
+        let mut cur_y = 0;
+        while cur_x < blk_w && cur_y < blk_h {
+            let op                      = br.read_u16le()?;
+            let advance;
+            if (op & 0x8000) == 0 {
+                let mut clr = [0; 8];
+                clr[0]                  = br.read_u16le()?;
+                clr[1]                  = br.read_u16le()?;
+                let mut flags = !op as usize;
+                let cur_off = off + cur_x * 4 + cur_y * 4 * stride;
+                if (clr[0] & 0x8000) == 0 {
+                    for j in 0..4 {
+                        for i in 0..4 {
+                            frm.data[cur_off + i + j * stride] = clr[flags & 1];
+                            flags >>= 1;
+                        }
+                    }
+                } else {
+                    clr[2]              = br.read_u16le()?;
+                    clr[3]              = br.read_u16le()?;
+                    clr[4]              = br.read_u16le()?;
+                    clr[5]              = br.read_u16le()?;
+                    clr[6]              = br.read_u16le()?;
+                    clr[7]              = br.read_u16le()?;
+                    for j in 0..4 {
+                        for i in 0..4 {
+                            frm.data[cur_off + i + j * stride] = clr[(i >> 1) * 2 + (j >> 1) * 4 + (flags & 1)];
+                            flags >>= 1;
+                        }
+                    }
+                }
+                advance = 1;
+            } else if (op & 0xFC00) == 0x8400 {
+                advance = (op & 0x3FF) as usize;
+                validate!(advance > 0);
+                skip_count += advance;
+            } else {
+                let cur_off = off + cur_x * 4 + cur_y * 4 * stride;
+                let clr = op & 0x7FFF;
+                for j in 0..4 {
+                    for i in 0..4 {
+                        frm.data[cur_off + i + j * stride] = clr;
+                    }
+                }
+                advance = 1;
+            }
+            cur_x += advance;
+            while cur_x >= blk_w {
+                cur_x -= blk_w;
+                cur_y += 1;
+            }
+        }
+        validate!((cur_x == 0 || cur_x == 1) && cur_y == blk_h);
+        if skip_count == 0 {
+            Ok(FrameType::I)
+        } else if skip_count < blk_w * blk_h {
+            Ok(FrameType::P)
+        } else {
+            Ok(FrameType::Skip)
+        }
+    }
+}
+
+impl NADecoder for Video1Decoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+println!("fmt {}", vinfo.get_format());
+            self.is_16bit = !vinfo.get_format().palette;
+            let fmt = if !self.is_16bit { PAL8_FORMAT } else { RGB555_FORMAT };
+            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();
+
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        let src = pkt.get_buffer();
+        validate!(src.len() >= 2);
+        let mut mr = MemoryReader::new_read(src.as_slice());
+        let mut br = ByteReader::new(&mut mr);
+
+        let buftype;
+        let ftype;
+        if !self.is_16bit {
+            let bufret = self.hams.clone_ref();
+            let mut buf;
+            if let Some(bbuf) = bufret {
+                buf = bbuf;
+            } else {
+                let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 2)?;
+                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();
+            ftype = self.decode_frame(&mut br, &mut frm)?;
+            let paloff = frm.offset[1];
+            let dpal = &mut frm.data[paloff..];
+let mut found_pal = false;
+            for sd in pkt.side_data.iter() {
+                match *sd {
+                    NASideData::Palette(_, ref pal) => {
+                        for (dst, src) in dpal.chunks_mut(3).zip(pal.chunks(4)) {
+                            dst[0] = src[0];
+                            dst[1] = src[1];
+                            dst[2] = src[2];
+                        }
+found_pal = true;
+                        break;
+                    },
+                    _ => {},
+                };
+            }
+if !found_pal {
+    for i in 0..256 {
+        dpal[i * 3 + 0] = i as u8;
+        dpal[i * 3 + 1] = i as u8;
+        dpal[i * 3 + 2] = i as u8;
+    }
+}
+            buftype = NABufferType::Video(buf);
+        } else {
+            let bufret = self.hams16.clone_ref();
+            let mut buf;
+            if let Some(bbuf) = bufret {
+                buf = bbuf;
+            } else {
+                let bufinfo = alloc_video_buffer(self.info.get_properties().get_video_info().unwrap(), 2)?;
+                buf = bufinfo.get_vbuf16().unwrap();
+                self.hams16.add_frame(buf);
+                buf = self.hams16.get_output_frame().unwrap();
+            }
+            let mut frm = NASimpleVideoFrame::from_video_buf(&mut buf).unwrap();
+            ftype = self.decode_frame16(&mut br, &mut frm)?;
+            buftype = NABufferType::Video16(buf);
+        }
+
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buftype);
+        frm.set_keyframe(ftype == FrameType::I);
+        frm.set_frame_type(ftype);
+        Ok(frm.into_ref())
+    }
+    fn flush(&mut self) {
+        self.hams.clear();
+        self.hams16.clear();
+    }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(Video1Decoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::ms_register_all_codecs;
+    use nihav_commonfmt::generic_register_all_demuxers;
+    #[test]
+    fn test_ms_video1_8bit() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        ms_register_all_codecs(&mut dec_reg);
+
+        test_decoding("avi", "msvideo1", "assets/MS/toon.avi", Some(66), &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5([0x0c26ec42, 0xb75bfea7, 0x1e6342ae, 0xb14dcfa3]));
+    }
+    #[test]
+    fn test_ms_video1_16bit() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        ms_register_all_codecs(&mut dec_reg);
+
+        test_decoding("avi", "msvideo1", "assets/MS/clock-cram16.avi", None, &dmx_reg, &dec_reg,
+                      ExpectedTestResult::MD5([0x03381fa4, 0x5b294fec, 0xb97a7575, 0xa1a3ffe9]));
+    }
+}
diff --git a/nihav-ms/src/lib.rs b/nihav-ms/src/lib.rs
new file mode 100644 (file)
index 0000000..d543744
--- /dev/null
@@ -0,0 +1,5 @@
+extern crate nihav_core;
+extern crate nihav_codec_support;
+
+mod codecs;
+pub use crate::codecs::ms_register_all_codecs;
index 1665b6783fddd2171c63d0643817977727b238e3..331748afe82e4185c07cdc32ffa5fee77ec2fa3d 100644 (file)
@@ -174,6 +174,10 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[
 
     desc!(video;    "cinepak",    "Cinepak"),
 
+    desc!(video;    "msvideo1",      "MS Video 1"),
+    desc!(audio;    "ms-adpcm",      "MS ADPCM"),
+    desc!(audio;    "ima-adpcm-ms",  "IMA ADPCM (MS variant)"),
+
     desc!(video;    "truemotion1",   "TrueMotion 1"),
     desc!(video-im; "truemotionrt",  "TrueMotion RT"),
     desc!(video;    "truemotion2",   "TrueMotion 2"),
@@ -222,6 +226,10 @@ static CODEC_REGISTER: &'static [CodecDescription] = &[
 ];
 
 static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[
+    (b"CRAM", "msvideo1"),
+    (b"MSVC", "msvideo1"),
+    (b"WHAM", "msvideo1"),
+
     (b"IF09", "indeo1"),
     (b"RT21", "indeo2"),
     (b"IV31", "indeo3"),
@@ -256,7 +264,9 @@ static AVI_VIDEO_CODEC_REGISTER: &'static [(&[u8;4], &str)] = &[
 static WAV_CODEC_REGISTER: &'static [(u16, &str)] = &[
     (0x0000, "pcm"),
     (0x0001, "pcm"),
+    (0x0002, "ms-adpcm"),
     (0x0003, "pcm"),
+    (0x0011, "ima-adpcm-ms"),
     (0x0061, "adpcm-dk4"),
     (0x0062, "adpcm-dk3"),
     (0x0401, "imc"),