]> git.nihav.org Git - nihav.git/commitdiff
Indeo YVU9 Compressed decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 30 Aug 2025 10:12:38 +0000 (12:12 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 30 Aug 2025 10:12:38 +0000 (12:12 +0200)
nihav-indeo/Cargo.toml
nihav-indeo/src/codecs/mod.rs
nihav-indeo/src/codecs/yv92.rs [new file with mode: 0644]
nihav-registry/src/register.rs

index 82343f74deda1ea9b3988969d40126407dd8407e..9ded2a7a5cdabf1a7554e6a9a641fdddbf862b15 100644 (file)
@@ -18,7 +18,7 @@ nihav_commonfmt = { path = "../nihav-commonfmt", default-features=false, feature
 default = ["all_decoders", "all_demuxers", "all_encoders"]
 
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
-all_video_decoders = ["decoder_indeo2", "decoder_indeo3", "decoder_indeo4", "decoder_indeo5", "decoder_intel263"]
+all_video_decoders = ["decoder_indeo2", "decoder_indeo3", "decoder_indeo4", "decoder_indeo5", "decoder_intel263", "decoder_yv92"]
 all_audio_decoders = ["decoder_imc"]
 decoders = []
 
@@ -28,6 +28,7 @@ decoder_indeo3 = ["decoders"]
 decoder_indeo4 = ["decoders"]
 decoder_indeo5 = ["decoders"]
 decoder_intel263 = ["decoders"]
+decoder_yv92 = ["decoders"]
 
 all_demuxers = ["demuxer_dvi", "demuxer_ivf"]
 demuxers = []
index 317a636b089ac41b1d96d2ebf30826644d453eec..4e764b062ce8f1717998c2a203eb2c695cdf9877 100644 (file)
@@ -21,6 +21,8 @@ mod indeo3data;
 mod indeo4;
 #[cfg(feature="decoder_indeo5")]
 mod indeo5;
+#[cfg(feature="decoder_yv92")]
+mod yv92;
 
 #[cfg(any(feature="decoder_indeo4", feature="decoder_indeo5"))]
 #[allow(clippy::too_many_arguments)]
@@ -53,6 +55,8 @@ const INDEO_CODECS: &[DecoderInfo] = &[
     DecoderInfo { name: "indeo5s", get_decoder: indeo5::get_decoder_scalable },
 #[cfg(feature="decoder_intel263")]
     DecoderInfo { name: "intel263", get_decoder: intel263::get_decoder },
+#[cfg(feature="decoder_yv92")]
+    DecoderInfo { name: "yv92", get_decoder: yv92::get_decoder },
 
 #[cfg(feature="decoder_imc")]
     DecoderInfo { name: "imc", get_decoder: imc::get_decoder_imc },
diff --git a/nihav-indeo/src/codecs/yv92.rs b/nihav-indeo/src/codecs/yv92.rs
new file mode 100644 (file)
index 0000000..22de5b0
--- /dev/null
@@ -0,0 +1,156 @@
+use nihav_core::io::byteio::*;
+use nihav_core::io::bitreader::*;
+use nihav_core::io::codebook::*;
+use nihav_core::formats;
+use nihav_core::codecs::*;
+
+const ESCAPE: u8 = 0x80;
+
+static YV92C_SYMS: [u8; 46] = [
+ 0x00, 0x01, 0xFF, 0x02, 0xFE, 0x03, 0xFD, 0xFC,
+ 0x04, 0xFB, 0x05, 0xFA, 0x06, 0xF9, 0x07, 0x08,
+ 0xF8, ESCAPE, 0xF7, 0x09, 0xF6, 0x0A, 0xF5, 0x0B,
+ 0xF4, 0x0C, 0xF3, 0x0D, 0x0E, 0xF2, 0x0F, 0xF1,
+ 0x10, 0xF0, 0x11, 0xEF, 0x12, 0xEE, 0x13, 0xED,
+ 0x14, 0xEC, 0x15, 0xEB, 0x16, 0x17
+];
+
+static YV92C_CODE_BITS: &[u16] = &[
+ 0x0001, 0x0003, 0x0002, 0x0006, 0x0004, 0x000C, 0x0008, 0x0028,
+ 0x0018, 0x0038, 0x0010, 0x0050, 0x0030, 0x0070, 0x0020, 0x00A0,
+ 0x0060, 0x00E0, 0x0040, 0x0140, 0x00C0, 0x01C0, 0x0080, 0x0480,
+ 0x0280, 0x0680, 0x0180, 0x0580, 0x0380, 0x0780, 0x0100, 0x0500,
+ 0x0300, 0x0700, 0x0200, 0x0A00, 0x0600, 0x0E00, 0x0400, 0x1400,
+ 0x0C00, 0x1C00, 0x0800, 0x2800, 0x1800, 0x3800
+];
+
+static YV92C_CODE_LENGTHS: &[u8] = &[
+  2,  2,  3,  3,  4,  4,  6,  6,  6,  6,  7,  7,  7,  7,  8,  8,
+  8,  8,  9,  9,  9,  9, 11, 11, 11, 11, 11, 11, 11, 11, 11, 11,
+ 11, 11, 12, 12, 12, 12, 13, 13, 13, 13, 14, 14, 14, 14
+];
+
+struct YV92Decoder {
+    info:    NACodecInfoRef,
+    cb:      Codebook<u8>,
+    top:     Vec<u8>,
+}
+
+#[allow(clippy::identity_op)]
+impl YV92Decoder {
+    fn new() -> Self {
+        let dummy_info = NACodecInfo::new_dummy();
+        let mut coderead = TableCodebookDescReader::new(YV92C_CODE_BITS, YV92C_CODE_LENGTHS, |idx| YV92C_SYMS[idx]);
+        let cb = Codebook::new(&mut coderead, CodebookMode::LSB).unwrap();
+        YV92Decoder { info: dummy_info, cb, top: Vec::new() }
+    }
+
+    fn decode_plane(&mut self, br: &mut BitReader,
+                    buf: &mut NAVideoBuffer<u8>, planeno: usize) -> DecoderResult<()> {
+        let offs = buf.get_offset(planeno);
+        let (w, h) = buf.get_dimensions(planeno);
+        let stride = buf.get_stride(planeno);
+        let cb = &self.cb;
+
+        let data = buf.get_data_mut().unwrap();
+        let framebuf: &mut [u8] = data.as_mut_slice();
+
+        let mut pred = 0x80u8;
+        for dst in framebuf[offs..][..w].iter_mut() {
+            let mut delta = br.read_cb(cb)?;
+            if delta == ESCAPE {
+                delta = br.read(8)? as u8;
+            }
+            pred = pred.wrapping_add(delta << 1);
+            *dst = pred;
+        }
+        self.top.resize(w, 0);
+        self.top.copy_from_slice(&framebuf[offs..][..w]);
+
+        for line in framebuf[offs..].chunks_exact_mut(stride).take(h).skip(1) {
+            let mut last_top = 0x80;
+            pred = 0x80;
+            for (dst, top) in line.iter_mut().zip(self.top.iter_mut()) {
+                let mut delta = br.read_cb(cb)?;
+                if delta == ESCAPE {
+                    delta = br.read(8)? as u8;
+                }
+                pred = ((i16::from(*top) - last_top + i16::from(pred)).max(0).min(255) as u8).wrapping_add(delta << 1);
+                last_top = i16::from(*top);
+                *dst = pred;
+                *top = pred;
+            }
+        }
+        Ok(())
+    }
+}
+
+impl NADecoder for YV92Decoder {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            let (w, h) = (vinfo.width, vinfo.height);
+            let f = vinfo.is_flipped();
+            let fmt = formats::YUV410_FORMAT;
+            let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, f, 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> {
+        const YV92_START: usize = 32;
+
+        let src = pkt.get_buffer();
+        if src.len() <= YV92_START { return Err(DecoderError::ShortData); }
+
+        let vinfo = self.info.get_properties().get_video_info().unwrap();
+        let bufinfo = alloc_video_buffer(vinfo, 2)?;
+        let mut buf = bufinfo.get_vbuf().unwrap();
+        for plane in 0..3 {
+            let plane_off = read_u32le(&src[0x10 + plane * 4..])? as usize;
+            validate!(plane_off >= YV92_START && plane_off < src.len());
+            let mut br = BitReader::new(&src[plane_off..], BitReaderMode::LE);
+            let planeno = if plane == 0 { 0 } else { plane ^ 3 };
+            self.decode_plane(&mut br, &mut buf, planeno)?;
+        }
+        let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+        frm.set_keyframe(true);
+        frm.set_frame_type(FrameType::I);
+        Ok(frm.into_ref())
+    }
+    fn flush(&mut self) {}
+}
+
+impl NAOptionHandler for YV92Decoder {
+    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(YV92Decoder::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::*;
+    use nihav_commonfmt::generic_register_all_demuxers;
+    #[test]
+    fn test_yv92() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        indeo_register_all_decoders(&mut dec_reg);
+
+        // sample from PC Pro 1996 April - The Windows Show
+        test_decoding("avi", "yv92", "assets/Indeo/pcpos.avi", Some(2),
+                      &dmx_reg, &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                            [0xad4ef973, 0x49b97ea3, 0xe1d2868a, 0xd5462621],
+                            [0x2802891d, 0x7163b4e6, 0x160144f2, 0xd87529aa],
+                            [0xb95d4aee, 0xbb0275f8, 0xe250ea17, 0xc7666495]]));
+    }
+}
index eeb6c66cc10c16ccadc74b974e87ad544489663a..e19668fdff52bdbb26b1aaf0464a6ac01dc1a602 100644 (file)
@@ -163,6 +163,7 @@ static CODEC_REGISTER: &[CodecDescription] = &[
     desc!(video;    "indeo5", "Intel Indeo 5", CODEC_CAP_REORDER | CODEC_CAP_SCALABLE),
     desc!(video;    "indeo5s", "Intel Indeo 5 Scalable", CODEC_CAP_SCALABLE),
     desc!(video;    "intel263", "Intel I263", CODEC_CAP_REORDER),
+    desc!(video-im; "yv92",   "Intel Indeo YVU9 Compressed"),
     desc!(audio;    "iac",    "Intel Indeo audio"),
     desc!(audio;    "imc",    "Intel Music Coder"),
 
@@ -350,6 +351,7 @@ static AVI_VIDEO_CODEC_REGISTER: &[(&[u8;4], &str)] = &[
     (b"IV41", "indeo4"),
     (b"IV50", "indeo5"),
     (b"I263", "intel263"),
+    (b"YV92", "yv92"),
 
     (b"UCOD", "clearvideo"),
     (b"cvid", "cinepak"),