From: Kostya Shishkov Date: Sat, 30 Aug 2025 10:12:38 +0000 (+0200) Subject: Indeo YVU9 Compressed decoder X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=3a20ae8d0ede5ce331a4a95bfa114bf0d96d9fe1;p=nihav.git Indeo YVU9 Compressed decoder --- diff --git a/nihav-indeo/Cargo.toml b/nihav-indeo/Cargo.toml index 82343f7..9ded2a7 100644 --- a/nihav-indeo/Cargo.toml +++ b/nihav-indeo/Cargo.toml @@ -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 = [] diff --git a/nihav-indeo/src/codecs/mod.rs b/nihav-indeo/src/codecs/mod.rs index 317a636..4e764b0 100644 --- a/nihav-indeo/src/codecs/mod.rs +++ b/nihav-indeo/src/codecs/mod.rs @@ -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 index 0000000..22de5b0 --- /dev/null +++ b/nihav-indeo/src/codecs/yv92.rs @@ -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, + top: Vec, +} + +#[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, 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 { + 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 { None } +} + +pub fn get_decoder() -> Box { + 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]])); + } +} diff --git a/nihav-registry/src/register.rs b/nihav-registry/src/register.rs index eeb6c66..e19668f 100644 --- a/nihav-registry/src/register.rs +++ b/nihav-registry/src/register.rs @@ -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"),