]> git.nihav.org Git - nihav.git/commitdiff
Adorage decoder
authorKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 4 Mar 2026 17:18:20 +0000 (18:18 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 4 Mar 2026 17:18:20 +0000 (18:18 +0100)
nihav-misc/Cargo.toml
nihav-misc/src/codecs/adorage.rs [new file with mode: 0644]
nihav-misc/src/codecs/mod.rs
nihav-registry/src/register.rs

index 36c8b298703c8b68157d8e8d0d7fc26b0bb4cace..8347d9fc6dee234e3ab457f50d315e4bc93866c8 100644 (file)
@@ -24,7 +24,8 @@ demuxers = []
 
 all_decoders = ["all_video_decoders", "all_audio_decoders"]
 
-all_video_decoders = ["decoder_arbc", "decoder_fif", "decoder_moviepak", "decoder_mvi", "decoder_mwv1", "decoder_pgvv", "decoder_pivc", "decoder_qpeg", "decoder_tealvid", "decoder_ultimotion"]
+all_video_decoders = ["decoder_adorage", "decoder_arbc", "decoder_fif", "decoder_moviepak", "decoder_mvi", "decoder_mwv1", "decoder_pgvv", "decoder_pivc", "decoder_qpeg", "decoder_tealvid", "decoder_ultimotion"]
+decoder_adorage = ["decoders"]
 decoder_arbc = ["decoders"]
 decoder_fif = ["decoders"]
 decoder_moviepak = ["decoders"]
diff --git a/nihav-misc/src/codecs/adorage.rs b/nihav-misc/src/codecs/adorage.rs
new file mode 100644 (file)
index 0000000..96af78d
--- /dev/null
@@ -0,0 +1,441 @@
+use nihav_core::io::byteio::{ByteIO,MemoryReader};
+use nihav_core::codecs::*;
+use nihav_codec_support::codecs::jpeg::*;
+
+struct DecoderWrapper {
+    info:       NACodecInfoRef,
+    jpeg:       JPEGDecoder,
+    buf:        Vec<u8>,
+}
+
+impl DecoderWrapper {
+    fn new() -> Self {
+        Self {
+            info:       NACodecInfo::new_dummy(),
+            jpeg:       JPEGDecoder::new(),
+            buf:        Vec::new(),
+        }
+    }
+    fn reset(&mut self) {
+        self.jpeg.quant      = [[0; 64]; 4];
+        self.jpeg.cb         = JCodebooks::default();
+        self.jpeg.width      = 0;
+        self.jpeg.height     = 0;
+        self.jpeg.depth      = 0;
+    }
+
+    fn parse_sof(&mut self, br: &mut dyn ByteIO) -> DecoderResult<NABufferType> {
+        validate!(self.jpeg.width == 0);
+
+        let len                         = br.read_u16be()? as usize;
+        validate!(len >= 11);
+        let p                           = br.read_byte()?;
+        validate!(p > 2);
+        if p != 8 {
+            return Err(DecoderError::NotImplemented);
+        }
+        let y                           = br.read_u16be()? as usize;
+        let x                           = br.read_u16be()? as usize;
+        validate!(x > 0);
+        if y == 0 {
+            return Err(DecoderError::NotImplemented);
+        }
+        self.jpeg.depth = p;
+        self.jpeg.width = x;
+        self.jpeg.height = y;
+        let nf                          = br.read_byte()? as usize;
+        validate!(nf > 0);
+        validate!(len == 8 + nf * 3);
+        if nf > MAX_CHROMATONS {
+            return Err(DecoderError::NotImplemented);
+        }
+        self.jpeg.max_h = 0;
+        self.jpeg.max_v = 0;
+        for i in 0..nf {
+            let c                       = br.read_byte()?;
+            self.jpeg.comp_id[i] = c;
+            let hv                      = br.read_byte()?;
+            let t                       = br.read_byte()?;
+            validate!(t < 4);
+            self.jpeg.qselect[i] = t;
+            self.jpeg.subsamp[i] = hv;
+            let hs = hv >> 4;
+
+            validate!(hs == 1 || hs == 2 || (i == 0 && hs == 4));
+            let vs = hv & 0xF;
+            validate!(vs == 1 || vs == 2 || (i == 0 && vs == 4));
+            self.jpeg.max_h = self.jpeg.max_h.max(hs);
+            self.jpeg.max_v = self.jpeg.max_v.max(vs);
+        }
+        let mut chromatons = [None; MAX_CHROMATONS];
+        for (i, chr) in chromatons[..nf].iter_mut().enumerate() {
+            let h_ss = match self.jpeg.max_h / (self.jpeg.subsamp[i] >> 4) {
+                    1 => 0,
+                    2 => 1,
+                    4 => 2,
+                    _ => unreachable!(),
+                };
+            let v_ss = match self.jpeg.max_v / (self.jpeg.subsamp[i] & 0xF) {
+                    1 => 0,
+                    2 => 1,
+                    4 => 2,
+                    _ => return Err(DecoderError::InvalidData),
+                };
+
+            *chr = Some(NAPixelChromaton {
+                    h_ss, v_ss,
+                    packed:     false,
+                    depth:      p,
+                    shift:      0,
+                    comp_offs:  i as u8,
+                    next_elem:  (p + 7) >> 3,
+                });
+        }
+        for i in 0..nf {
+            for j in i + 1..nf {
+                validate!(self.jpeg.comp_id[i] != self.jpeg.comp_id[j]);
+            }
+        }
+        let formaton = NAPixelFormaton {
+                model:      ColorModel::YUV(YUVSubmodel::YUVJ),
+                components: nf as u8,
+                comp_info:  chromatons,
+                elem_size:  0,
+                be:         false,
+                alpha:      nf == 2 || nf == 4,
+                palette:    false,
+            };
+        let vinfo = NAVideoInfo::new(x, y, true, formaton);
+        Ok(alloc_video_buffer(vinfo, 4)?)
+    }
+}
+
+impl NADecoder for DecoderWrapper {
+    fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+        if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+            let w = vinfo.get_width();
+            let h = vinfo.get_height();
+            let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, true, YUV420_FORMAT));
+            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();
+        if src.len() <= 24 { return Err(DecoderError::ShortData); }
+
+        let mut bufinfo = NABufferType::None;
+        let mut br = MemoryReader::new_read(&src);
+        let twelve                      = br.read_u32le()?;
+        validate!(twelve == 12);
+        let tag                         = br.read_tag()?;
+        if &tag[..2] != b"JP" {
+            return Err(DecoderError::NotImplemented);
+        }
+                                          br.read_u32le()?;
+        let jpg_size                    = br.read_u32le()?;
+        validate!(jpg_size as usize + 20 <= src.len());
+        let mask_size                   = br.read_u32le()?; // PNG or JPG mask
+        validate!(mask_size as usize <= src.len() - 20 - (jpg_size as usize));
+
+        let start_tag                   = br.read_u16be()?;
+        validate!(start_tag == 0xFFD8);
+
+        let mut jtype = JPEGType::None;
+        let mut arith = false;
+        self.reset();
+        loop {
+            let tag                     = br.read_u16be()?;
+            match tag {
+                0xFFC0 => { //baseline DCT header
+                    jtype = JPEGType::Baseline;
+                    arith = false;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC1 => {
+                    jtype = JPEGType::Extended;
+                    arith = false;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC2 => {
+                    jtype = JPEGType::Progressive;
+                    arith = false;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC3 => {
+                    jtype = JPEGType::Lossless;
+                    arith = false;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC5 => {
+                    jtype = JPEGType::Differential;
+                    arith = false;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC6 => {
+                    jtype = JPEGType::DiffProgressive;
+                    arith = false;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC7 => {
+                    jtype = JPEGType::DiffLossless;
+                    arith = false;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC8 => return Err(DecoderError::NotImplemented),
+                0xFFC9 => {
+                    jtype = JPEGType::Extended;
+                    arith = true;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFCA => {
+                    jtype = JPEGType::Progressive;
+                    arith = true;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFCB => {
+                    jtype = JPEGType::Lossless;
+                    arith = true;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFCD => {
+                    jtype = JPEGType::Differential;
+                    arith = true;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFCE => {
+                    jtype = JPEGType::DiffProgressive;
+                    arith = true;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFCF => {
+                    jtype = JPEGType::DiffLossless;
+                    arith = true;
+                    bufinfo = self.parse_sof(&mut br)?;
+                },
+                0xFFC4 => { //huff table
+                    validate!(!arith);
+                    let len             = u64::from(br.read_u16be()?);
+                    validate!(len > 2);
+                    let end = br.tell() + len - 2;
+                    let mut lens = [0; 16];
+                    let mut syms = [0; 256];
+                    while br.tell() < end {
+                        let tctn        = br.read_byte()? as usize;
+                        let tclass = tctn >> 4;
+                        validate!(tclass < 2);
+                        let id = tctn & 0xF;
+                        validate!(id < 4);
+                                          br.read_buf(&mut lens)?;
+                        let mut tot_len = 0usize;
+                        for &el in lens.iter() {
+                            tot_len += usize::from(el);
+                        }
+                        validate!(tot_len > 0 && tot_len <= 256);
+                                          br.read_buf(&mut syms[..tot_len])?;
+                        self.jpeg.cb.codebook[tclass][id] = Some(JCodebooks::generate_cb(&lens, &syms[..tot_len])?);
+                    }
+                    validate!(br.tell() == end);
+                },
+                0xFFCC => { // arith coding conditioning
+                    return Err(DecoderError::NotImplemented);
+                }
+                0xFFD0..=0xFFD7 => return Err(DecoderError::NotImplemented),
+                0xFFD9 => break,
+                0xFFDA => { //start of scan
+                    let len             = br.read_u16be()? as usize;
+                    let ns              = br.read_byte()? as usize;
+                    validate!(len == ns * 2 + 6);
+                    let mut ci = [ComponentInfo::default(); MAX_CHROMATONS];
+                    for info in ci[..ns].iter_mut() {
+                        let id          = br.read_byte()?;
+                        let mut found = false;
+                        for (i, &c_id) in self.jpeg.comp_id.iter().enumerate() {
+                            if c_id == id {
+                                info.component_id = i;
+                                found = true;
+                                break;
+                            }
+                        }
+                        validate!(found);
+                        let tdta        = br.read_byte()? as usize;
+                        let dc_id = tdta >> 4;
+                        validate!(dc_id < 4);
+                        if self.jpeg.cb.codebook[0][dc_id].is_none() {
+                            validate!(dc_id < 2);
+                            self.jpeg.cb.codebook[0][dc_id] = Some(JCodebooks::build_default_cb(true, dc_id)?);
+                        }
+                        let ac_id = tdta & 0xF;
+                        validate!(ac_id < 4);
+                        if self.jpeg.cb.codebook[1][ac_id].is_none() {
+                            validate!(ac_id < 2);
+                            self.jpeg.cb.codebook[1][ac_id] = Some(JCodebooks::build_default_cb(false, ac_id)?);
+                        }
+                        info.dc_table_id = dc_id;
+                        info.ac_table_id = ac_id;
+                    }
+                    let ss              = br.read_byte()? as usize;
+                    let se              = br.read_byte()? as usize;
+                    let ahal            = br.read_byte()?;
+                    let ah = ahal >> 4;
+                    let al = ahal & 0xF;
+                    match jtype {
+                        JPEGType::Baseline | JPEGType::Extended => {
+                            if arith {
+                                println!("arithmetic coding!");
+                                return Err(DecoderError::NotImplemented);
+                            }
+                            validate!(ss == 0 && se == 63);
+                            validate!(ah == 0 && al == 0);
+                            if let Some(buf) = bufinfo.get_vbuf() {
+                                let max_size = src.len() - (br.tell() as usize);
+                                self.buf.clear();
+                                self.buf.reserve(max_size);
+                                loop {
+                                    let b   = br.read_byte()?;
+                                    if b != 0xFF {
+                                        self.buf.push(b);
+                                    } else {
+                                        let b2 = br.read_byte()?;
+                                        if b2 == 0 {
+                                            self.buf.push(b);
+                                        } else {
+                                                 br.seek(std::io::SeekFrom::Current(-2))?;
+                                            break;
+                                        }
+                                    }
+                                }
+
+                                let mut data = Vec::new();
+                                std::mem::swap(&mut self.buf, &mut data);
+                                let ret = self.jpeg.decode_scan(jtype, &data, buf, &ci[..ns], ss, se, None);
+                                std::mem::swap(&mut self.buf, &mut data);
+                                ret?;
+                            } else { unreachable!(); }
+                        },
+                        JPEGType::Progressive => {
+                            validate!(ss < 64 && se < 64 && se >= ss);
+                            validate!(ah < 14 && al < 14);
+                            println!("Progressive JPEG");
+                            return Err(DecoderError::NotImplemented);
+                        },
+                        JPEGType::Lossless => {
+                            validate!((1..8).contains(&ss) && se == 0);
+                            validate!(ah == 0);
+                            println!("LJPEG");
+                            return Err(DecoderError::NotImplemented);
+                        },
+                        _ => return Err(DecoderError::NotImplemented),
+                    };
+                    let tag             = br.peek_u16be()?;
+                    validate!(matches!(tag, 0xFFC4 | 0xFFD0..=0xFFD7 | 0xFFD9));
+                },
+                0xFFDB => { //quant tables
+                    let mut len         = br.read_u16be()? as usize;
+                    validate!(len >= 64 + 3);
+                    len -= 2;
+                    while len > 0 {
+                        let pt          = br.read_byte()?;
+                        let precision = pt >> 4;
+                        validate!(precision < 2);
+                        let id = (pt & 0xF) as usize;
+                        validate!(id < 4);
+                        let qsize = if precision == 0 { 64 } else { 64 * 2 } + 1;
+                        validate!(len >= qsize);
+                        if precision == 0 {
+                            for el in self.jpeg.quant[id].iter_mut() {
+                                *el     = i16::from(br.read_byte()?);
+                            }
+                        } else {
+                            for el in self.jpeg.quant[id].iter_mut() {
+                                *el     = br.read_u16be()? as i16;
+                            }
+                        }
+                        len -= qsize;
+                    }
+                },
+                0xFFDC => { //number of lines
+                    return Err(DecoderError::NotImplemented);
+                },
+                0xFFDD => {
+                    let len             = br.read_u16be()?;
+                    validate!(len == 4);
+                    let ri              = br.read_u16be()?;
+                    if ri != 0 {
+                        println!("restart interval {}", ri);
+                        return Err(DecoderError::NotImplemented);
+                    }
+                },
+                0xFFDE => return Err(DecoderError::NotImplemented),
+                0xFFDF => return Err(DecoderError::NotImplemented),
+                0xFFE0..=0xFFEF => { // application data
+                    let len             = br.read_u16be()? as usize;
+                    validate!(len >= 2);
+                                          br.read_skip(len - 2)?;
+                },
+                0xFFF0..=0xFFF6 => return Err(DecoderError::NotImplemented),
+                0xFFF7 => {
+                    //jtype = JPEGType::JPEGLS;
+                    //arith = false;
+                    println!("JPEG-LS");
+                    return Err(DecoderError::NotImplemented);
+                },
+                0xFFF8 => return Err(DecoderError::NotImplemented), //JPEG-LS parameters
+                0xFFF9..=0xFFFD => return Err(DecoderError::NotImplemented),
+                0xFFFE => { //comment
+                    let len             = br.read_u16be()? as usize;
+                    validate!(len >= 2);
+                                          br.read_skip(len - 2)?;
+                },
+                0xFF01 => return Err(DecoderError::NotImplemented),
+                0xFF02..=0xFFBF => return Err(DecoderError::NotImplemented),
+                _ => return Err(DecoderError::InvalidData),
+            };
+        }
+        validate!(jtype != JPEGType::None);
+
+        if let NABufferType::None = bufinfo {
+println!("no buffer");
+            return Err(DecoderError::InvalidData);
+        }
+
+        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 DecoderWrapper {
+    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(DecoderWrapper::new())
+}
+
+#[cfg(test)]
+mod test {
+    use nihav_core::codecs::RegisteredDecoders;
+    use nihav_core::demuxers::RegisteredDemuxers;
+    use nihav_codec_support::test::dec_video::*;
+    use crate::misc_register_all_decoders;
+    use nihav_commonfmt::generic_register_all_demuxers;
+    #[test]
+    fn test_adorage() {
+        let mut dmx_reg = RegisteredDemuxers::new();
+        generic_register_all_demuxers(&mut dmx_reg);
+        let mut dec_reg = RegisteredDecoders::new();
+        misc_register_all_decoders(&mut dec_reg);
+        // sample from Adorage 2.0 effects
+        test_decoding("avi", "adorage", "assets/Misc/ballon6.avi", Some(0), &dmx_reg,
+                     &dec_reg, ExpectedTestResult::MD5Frames(vec![
+                            [0x242e775c, 0x1ea0c40a, 0x5a6c4734, 0xd8fd852b]]));
+    }
+}
index efd399e7573efd497d01689e1c632e7cc5a63a7b..3144bf9a5caa090cf31568282ea0f1c750dea658 100644 (file)
@@ -9,6 +9,9 @@ macro_rules! validate {
     ($a:expr) => { if !$a { return Err(DecoderError::InvalidData); } };
 }
 
+#[cfg(feature="decoder_adorage")]
+mod adorage;
+
 #[cfg(feature="decoder_arbc")]
 mod arbc;
 
@@ -40,6 +43,8 @@ mod tealvid;
 mod ultimotion;
 
 const DECODERS: &[DecoderInfo] = &[
+#[cfg(feature="decoder_adorage")]
+    DecoderInfo { name: "adorage", get_decoder: adorage::get_decoder },
 #[cfg(feature="decoder_arbc")]
     DecoderInfo { name: "gryphon-arbc-vfw", get_decoder: arbc::get_decoder_vfw },
 #[cfg(feature="decoder_arbc")]
index 57b9a33b11ad4f47552f066de846f47c9aea1602..917ad107f5a69f3f267e9ff9a5c8cadf8bb97dc9 100644 (file)
@@ -336,6 +336,7 @@ static CODEC_REGISTER: &[CodecDescription] = &[
 
     desc!(video-llp; "pivideo",      "PI-Video"),
 
+    desc!(video-im; "adorage",      "proDAD Adorage"),
     desc!(video-im; "moviepak",      "RasterOps MoviePak"),
     desc!(video-im; "pgvv",          "Radius Studio Video"),
 
@@ -404,6 +405,8 @@ static AVI_VIDEO_CODEC_REGISTER: &[(&[u8;4], &str)] = &[
 
     (b"FVF1", "fif"),
 
+    (b"pDAD", "adorage"),
+
     (b"pivc", "pivideo"),
 
     (b"ULTI", "ultimotion"),