add raw RGB support for AVI
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 3 Oct 2021 16:37:30 +0000 (18:37 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sun, 3 Oct 2021 16:37:30 +0000 (18:37 +0200)
nihav-commonfmt/Cargo.toml
nihav-commonfmt/src/codecs/mod.rs
nihav-commonfmt/src/codecs/rawvideo_ms.rs [new file with mode: 0644]
nihav-commonfmt/src/demuxers/avi.rs
nihav-registry/src/register.rs

index 7f4cc4f13c66fad11311e4eeebc13d125a507de9..acccd96b61ee25b89da601a956ec4fb40cfa3c5b 100644 (file)
@@ -34,10 +34,11 @@ muxer_wav = ["muxers"]
 
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
 
-all_video_decoders = ["decoder_cinepak", "decoder_clearvideo", "decoder_rawvideo", "decoder_zmbv"]
+all_video_decoders = ["decoder_cinepak", "decoder_clearvideo", "decoder_rawvideo", "decoder_rawvideo_ms", "decoder_zmbv"]
 decoder_cinepak = ["decoders"]
 decoder_clearvideo = ["decoders"]
 decoder_rawvideo = ["decoders"]
+decoder_rawvideo_ms = ["decoders"]
 decoder_zmbv = ["decoders"]
 
 all_audio_decoders = ["decoder_pcm", "decoder_ts102366", "decoder_sipro", "decoder_atrac3", "decoder_aac"]
index 2c9eef764eef7d194b95e7ec144fc5fc55bc167c..cc1cf74f1121fb217f80501b6bde3cb4c1e2c166 100644 (file)
@@ -10,6 +10,8 @@ mod cinepak;
 mod clearvideo;
 #[cfg(feature="decoder_rawvideo")]
 mod rawvideo;
+#[cfg(feature="decoder_rawvideo_ms")]
+mod rawvideo_ms;
 #[cfg(feature="decoder_zmbv")]
 mod zmbv;
 
@@ -40,6 +42,8 @@ const DECODERS: &[DecoderInfo] = &[
     DecoderInfo { name: "clearvideo_rm", get_decoder: clearvideo::get_decoder_rm },
 #[cfg(feature="decoder_rawvideo")]
     DecoderInfo { name: "rawvideo", get_decoder: rawvideo::get_decoder },
+#[cfg(feature="decoder_rawvideo_ms")]
+    DecoderInfo { name: "rawvideo-ms", get_decoder: rawvideo_ms::get_decoder },
 #[cfg(feature="decoder_zmbv")]
     DecoderInfo { name: "zmbv", get_decoder: zmbv::get_decoder },
 
diff --git a/nihav-commonfmt/src/codecs/rawvideo_ms.rs b/nihav-commonfmt/src/codecs/rawvideo_ms.rs
new file mode 100644 (file)
index 0000000..0b35881
--- /dev/null
@@ -0,0 +1,159 @@
+use nihav_core::codecs::*;
+
+struct RawDecoder {
+    info:   NACodecInfoRef,
+    pal:    [u8; 768],
+}
+
+impl RawDecoder {
+    fn new() -> Self {
+        Self {
+            info:   NACodecInfo::new_dummy(),
+            pal:    [0; 768],
+        }
+    }
+}
+
+impl NADecoder for RawDecoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(_vinfo) = info.get_properties() {
+            self.info = info.clone();
+            Ok(())
+        } else {
+            Err(DecoderError::InvalidData)
+        }
+    }
+    fn decode(&mut self, _supp: &mut NADecoderSupport, pkt: &NAPacket) -> DecoderResult<NAFrameRef> {
+        if let NACodecTypeInfo::Video(ref vinfo) = self.info.get_properties() {
+            let src = pkt.get_buffer();
+            let width  = vinfo.width;
+            let height = vinfo.height;
+            if !vinfo.format.model.is_rgb() {
+                return Err(DecoderError::InvalidData);
+            }
+            let depth = if vinfo.format.is_paletted() { 8 } else { vinfo.format.get_total_depth() } as usize;
+            let buf = match depth {
+                    8 => {
+                        for sd in pkt.side_data.iter() {
+                            match *sd {
+                                NASideData::Palette(_, ref pal) => {
+                                    for (dst, src) in self.pal.chunks_mut(3).zip(pal.chunks(4)) {
+                                        dst[0] = src[0];
+                                        dst[1] = src[1];
+                                        dst[2] = src[2];
+                                    }
+                                    break;
+                                },
+                                _ => {},
+                            };
+                        }
+
+                        let sstride = (vinfo.width + 3) & !3;
+
+                        let buf = alloc_video_buffer(vinfo.clone(), 0)?;
+
+                        let mut frm = buf.get_vbuf().unwrap();
+                        let dstride = frm.get_stride(0);
+                        let paloff  = frm.get_offset(1);
+                        let dst     = frm.get_data_mut().unwrap();
+                        for (drow, srow) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(height) {
+                            drow[..width].copy_from_slice(&srow[..width]);
+                        }
+                        dst[paloff..][..768].copy_from_slice(&self.pal);
+
+                        buf
+                    },
+                    15 | 16 => {
+                        let sstride = (vinfo.width * 2 + 3) & !3;
+
+                        let buf = alloc_video_buffer(vinfo.clone(), 0)?;
+
+                        let mut frm = buf.get_vbuf16().unwrap();
+                        let dstride = frm.get_stride(0);
+                        let dst     = frm.get_data_mut().unwrap();
+
+                        for (drow, srow) in dst.chunks_mut(dstride).zip(src.chunks(sstride)).take(height) {
+                            for (dp, sp) in drow.iter_mut().zip(srow.chunks_exact(2)).take(width) {
+                                *dp = u16::from(sp[0]) | (u16::from(sp[1]) << 8);
+                            }
+                        }
+
+                        buf
+                    },
+                    24 | 32 => {
+                        let ncomp = vinfo.format.components as usize;
+                        let sstride = (width * (depth / 8) + 3) & !3;
+                        let offs    = vec![0; ncomp];
+                        let mut strides = vec![0; ncomp];
+                        strides[0] = sstride;
+                         NABufferType::VideoPacked(NAVideoBuffer::from_raw_parts(vinfo.clone(), src.clone(), offs, strides).into_ref())
+                    },
+                    _ => return Err(DecoderError::NotImplemented),
+                };
+
+            let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), buf);
+            frm.set_keyframe(true);
+            frm.set_frame_type(FrameType::I);
+            Ok(frm.into_ref())
+        } else {
+            Err(DecoderError::Bug)
+        }
+    }
+    fn flush(&mut self) {}
+}
+
+
+impl NAOptionHandler for RawDecoder {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) { }
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+pub fn get_decoder() -> Box<dyn NADecoder + Send> {
+    Box::new(RawDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::generic_register_all_decoders;
+    use crate::generic_register_all_demuxers;
+    #[test]
+    fn test_rawvideo_ms_8() {
+        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);
+        test_decoding("avi", "rawvideo-ms", "assets/Misc/8bpp.avi", Some(0), &dmx_reg,
+                     &dec_reg, ExpectedTestResult::MD5([0xb6629439, 0x6ea482e9, 0x42c84d7c, 0x46c94431]));
+    }
+    #[test]
+    fn test_rawvideo_ms_16() {
+        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);
+        test_decoding("avi", "rawvideo-ms", "assets/Misc/16bpp.avi", Some(0), &dmx_reg,
+                     &dec_reg, ExpectedTestResult::MD5([0xe80e16a1, 0x2d50659e, 0x413d24af, 0xea3bee05]));
+    }
+    #[test]
+    fn test_rawvideo_ms_24() {
+        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);
+        test_decoding("avi", "rawvideo-ms", "assets/Misc/keve.avi", Some(0), &dmx_reg,
+                     &dec_reg, ExpectedTestResult::MD5([0x9514ac1f, 0x4512cc62, 0x069485ba, 0x084a1e63]));
+    }
+    #[test]
+    fn test_rawvideo_ms_32() {
+        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);
+        test_decoding("avi", "rawvideo-ms", "assets/Misc/VRMLuncompressed.avi", Some(0), &dmx_reg,
+                     &dec_reg, ExpectedTestResult::MD5([0xf4c9d468, 0x8f42c576, 0xc8eb522a, 0x75f654b1]));
+    }
+}
index 9adf54b55d5c3261c3915802b080e4fff71c689c..3e372e0d4c0ed9f4ab90ba9d070b27b832599e3b 100644 (file)
@@ -1,6 +1,7 @@
 use nihav_core::demuxers::*;
 use nihav_registry::register;
 use nihav_core::demuxers::DemuxerError::*;
+use std::str::FromStr;
 
 macro_rules! mktag {
     ($a:expr, $b:expr, $c:expr, $d:expr) => ({
@@ -423,6 +424,14 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize
     let format = if bitcount > 8 { RGB24_FORMAT } else { PAL8_FORMAT };
     let mut vhdr = NAVideoInfo::new(width as usize, if flip { -height as usize } else { height as usize}, flip, format);
     vhdr.bits = (planes as u8) * (bitcount as u8);
+    let cname = if find_raw_fmt(&compression, planes, bitcount, &mut vhdr) {
+            "rawvideo-ms"
+        } else {
+            match register::find_codec_from_avi_fourcc(&compression) {
+                None => "unknown",
+                Some(name) => name,
+            }
+        };
     let vci = NACodecTypeInfo::Video(vhdr);
     let edata = dmx.read_extradata(size - 40)?;
     if colors > 0 {
@@ -438,10 +447,6 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize
             dmx.pal.push(pal);
         }
     }
-    let cname = match register::find_codec_from_avi_fourcc(&compression) {
-                    None => "unknown",
-                    Some(name) => name,
-                };
     let vinfo = NACodecInfo::new(cname, vci, edata);
     let res = strmgr.add_stream(NAStream::new(StreamType::Video, u32::from(dmx.sstate.strm_no), vinfo, dmx.tb_num, dmx.tb_den, u64::from(dmx.strm_duration)));
     if res.is_none() { return Err(MemoryError); }
@@ -449,6 +454,31 @@ fn parse_strf_vids(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize
     Ok(size)
 }
 
+fn find_raw_fmt(compr: &[u8; 4], planes: u16, bitcount: u16, vhdr: &mut NAVideoInfo) -> bool {
+    match compr {
+        &[0, 0, 0, 0] | b"DIB " => {
+            if planes != 1 {
+                return false;
+            }
+            let fmt_name = match bitcount {
+                     8 => "pal8",
+                    16 => "bgr555",
+                    24 => "bgr24",
+                    32 => "bgra24",
+                    _ => return false,
+                };
+            if let Ok(fmt) = NAPixelFormaton::from_str(fmt_name) {
+                vhdr.format = fmt;
+                vhdr.flipped = true;
+                true
+            } else {
+                false
+            }
+        },
+        _ => false,
+    }
+}
+
 #[allow(unused_variables)]
 fn parse_strf_auds(dmx: &mut AVIDemuxer, strmgr: &mut StreamManager, size: usize) -> DemuxerResult<usize> {
     if size < 16 { return Err(InvalidData); }
index d929e28b9a7ae3a736565f93d1a18b935602aea3..a89e2a79e2b7c8016e262e384af963e26bdc8404 100644 (file)
@@ -187,6 +187,7 @@ static CODEC_REGISTER: &[CodecDescription] = &[
 
 
     desc!(video-ll; "rawvideo",   "Raw video data"),
+    desc!(video-ll; "rawvideo-ms", "Raw video data"),
 
     desc!(video;    "cinepak",    "Cinepak"),