From bd83136cf6da5cf1f51e39deff2c0eba87242b91 Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Sat, 14 Jun 2025 16:08:07 +0200 Subject: [PATCH] add wrapper codec for Escape formats in AVI and MOV --- nihav-acorn/src/codecs/escape.rs | 119 +++++++++++++++++++++++++++++++ nihav-acorn/src/codecs/mod.rs | 2 + nihav-registry/src/register.rs | 5 ++ 3 files changed, 126 insertions(+) diff --git a/nihav-acorn/src/codecs/escape.rs b/nihav-acorn/src/codecs/escape.rs index dea9d5b..d2e0b4d 100644 --- a/nihav-acorn/src/codecs/escape.rs +++ b/nihav-acorn/src/codecs/escape.rs @@ -2,6 +2,7 @@ use nihav_core::codecs::*; use nihav_core::io::byteio::*; use nihav_core::io::bitreader::*; use nihav_codec_support::codecs::imaadpcm::*; +use std::convert::TryFrom; use std::str::FromStr; use super::RGB555_FORMAT; use super::yuvtab::YUV2RGB; @@ -699,6 +700,124 @@ pub fn get_decoder130() -> Box { Box::new(Escape130Decoder::new()) } +#[derive(Debug,Copy,Clone,PartialEq)] +enum EscapeCodec { + Escape102, + Escape122, + Escape124, + Escape130, +} + +impl TryFrom for EscapeCodec { + type Error = (); + fn try_from(val: u16) -> Result { + match val { + 0x102 => Ok(EscapeCodec::Escape102), + 0x116 => Ok(EscapeCodec::Escape122), + 0x114 => Ok(EscapeCodec::Escape124), + 0x130 => Ok(EscapeCodec::Escape130), + _ => Err(()) + } + } +} + +#[derive(Default)] +#[allow(clippy::large_enum_variant)] +enum EscapeDecoderVariant { + #[default] + None, + Escape102(Escape102Decoder), + Escape122(Escape122Decoder), + Escape124(Escape124Decoder), + Escape130(Escape130Decoder), +} + +#[derive(Default)] +struct EscapeAnyDecoder { + info: NACodecInfoRef, + decoder: EscapeDecoderVariant, +} + +impl NADecoder for EscapeAnyDecoder { + fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> { + if let NACodecTypeInfo::Video(vinfo) = info.get_properties() { + let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(vinfo.get_width(), vinfo.get_height(), false, YUV420_FORMAT)); + validate!((vinfo.get_width() | vinfo.get_height()) & 7 == 0); + 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 { + let src = pkt.get_buffer(); + validate!(src.len() > 16); + + let id = read_u16le(&src).unwrap_or_default(); + let etype = EscapeCodec::try_from(id).map_err(|_| DecoderError::InvalidData)?; + + if matches!(self.decoder, EscapeDecoderVariant::None) { + match etype { + EscapeCodec::Escape102 => { + let mut dec = Escape102Decoder::new(); + dec.init(supp, self.info.clone())?; + self.decoder = EscapeDecoderVariant::Escape102(dec); + }, + EscapeCodec::Escape122 => { + let mut dec = Escape122Decoder::new(); + dec.init(supp, self.info.clone())?; + self.decoder = EscapeDecoderVariant::Escape122(dec); + }, + EscapeCodec::Escape124 => { + let mut dec = Escape124Decoder::new(); + dec.init(supp, self.info.clone())?; + self.decoder = EscapeDecoderVariant::Escape124(dec); + }, + EscapeCodec::Escape130 => { + let mut dec = Escape130Decoder::new(); + dec.init(supp, self.info.clone())?; + self.decoder = EscapeDecoderVariant::Escape130(dec); + }, + } + } + + match (etype, &mut self.decoder) { + (EscapeCodec::Escape102, EscapeDecoderVariant::Escape102(ref mut dec)) => { + dec.decode(supp, pkt) + }, + (EscapeCodec::Escape122, EscapeDecoderVariant::Escape122(ref mut dec)) => { + dec.decode(supp, pkt) + }, + (EscapeCodec::Escape124, EscapeDecoderVariant::Escape124(ref mut dec)) => { + dec.decode(supp, pkt) + }, + (EscapeCodec::Escape130, EscapeDecoderVariant::Escape130(ref mut dec)) => { + dec.decode(supp, pkt) + }, + _ => Err(DecoderError::InvalidData), + } + } + fn flush(&mut self) { + match self.decoder { + EscapeDecoderVariant::None => {}, + EscapeDecoderVariant::Escape102(ref mut dec) => { dec.flush(); }, + EscapeDecoderVariant::Escape122(ref mut dec) => { dec.flush(); }, + EscapeDecoderVariant::Escape124(ref mut dec) => { dec.flush(); }, + EscapeDecoderVariant::Escape130(ref mut dec) => { dec.flush(); }, + } + } +} + +impl NAOptionHandler for EscapeAnyDecoder { + 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_any() -> Box { + Box::::default() +} + pub struct EscapeIMAState { pub predictor: i32, pub step: usize, diff --git a/nihav-acorn/src/codecs/mod.rs b/nihav-acorn/src/codecs/mod.rs index 3dcdd15..9230527 100644 --- a/nihav-acorn/src/codecs/mod.rs +++ b/nihav-acorn/src/codecs/mod.rs @@ -61,6 +61,8 @@ const ACORN_CODECS: &[DecoderInfo] = &[ #[cfg(feature="decoder_linepack")] DecoderInfo { name: "linepack", get_decoder: linepack::get_decoder }, +#[cfg(feature="decoder_escape")] + DecoderInfo { name: "escape-any", get_decoder: escape::get_decoder_any }, #[cfg(feature="decoder_escape")] DecoderInfo { name: "escape102", get_decoder: escape::get_decoder102 }, #[cfg(feature="decoder_escape")] diff --git a/nihav-registry/src/register.rs b/nihav-registry/src/register.rs index 5d94ff9..09f53a3 100644 --- a/nihav-registry/src/register.rs +++ b/nihav-registry/src/register.rs @@ -216,6 +216,7 @@ static CODEC_REGISTER: &[CodecDescription] = &[ desc!(video; "supermovingblocks", "Acorn Super Moving Blocks"), desc!(video; "linepack", "Henrik Pedersen's LinePack"), desc!(video; "movie16_3", "Henrik Pedersen's Movie 16:3"), + desc!(video; "escape-any", "wrapper for Eidos Escape codecs"), desc!(video; "escape100", "Eidos Escape 100"), desc!(video; "escape102", "Eidos Escape 102"), desc!(video; "escape122", "Eidos Escape 122"), @@ -380,6 +381,8 @@ static AVI_VIDEO_CODEC_REGISTER: &[(&[u8;4], &str)] = &[ (b"azpr", "apple-video"), (b"PGVV", "pgvv"), + (b"ESCP", "escape-any"), + (b"VXS1", "vxvideo"), (b"DX50", "mpeg4asp"), @@ -427,6 +430,8 @@ static MOV_VIDEO_CODEC_REGISTER: &[(&[u8;4], &str)] = &[ (b"VP30", "vp3"), (b"VP31", "vp3"), + (b"ESCP", "escape-any"), + (b"mp4v", "mpeg4asp"), (b"avc1", "h264"), ]; -- 2.39.5