From: Kostya Shishkov Date: Sat, 12 Apr 2025 09:53:50 +0000 (+0200) Subject: add support for TCA in ARMovie X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=df67d50c3b5ebbb7fef48a8b8bfc56330026c41a;p=nihav.git add support for TCA in ARMovie --- diff --git a/nihav-acorn/src/codecs/euclid.rs b/nihav-acorn/src/codecs/euclid.rs index 3f7e13d..bffb95f 100644 --- a/nihav-acorn/src/codecs/euclid.rs +++ b/nihav-acorn/src/codecs/euclid.rs @@ -267,6 +267,58 @@ pub fn get_decoder() -> Box { Box::new(EuclidDecoder::new()) } +#[derive(Default)] +struct EuclidPacketiser { + stream: Option, + buf: Vec, + frameno: u32, +} + +impl EuclidPacketiser { + fn new() -> Self { Self::default() } +} + +impl NAPacketiser for EuclidPacketiser { + fn attach_stream(&mut self, stream: NAStreamRef) { + self.stream = Some(stream); + } + fn add_data(&mut self, src: &[u8]) -> bool { + self.buf.extend_from_slice(src); + false + } + fn parse_stream(&mut self, id: u32) -> DecoderResult { + if let Some(ref stream) = self.stream { + let mut stream = NAStream::clone(stream); + stream.id = id; + Ok(stream.into_ref()) + } else { + Err(DecoderError::MissingReference) + } + } + fn skip_junk(&mut self) -> DecoderResult { + Err(DecoderError::NotImplemented) + } + fn get_packet(&mut self, stream: NAStreamRef) -> DecoderResult> { + if !self.buf.is_empty() { + let mut data = Vec::new(); + std::mem::swap(&mut data, &mut self.buf); + let ts = NATimeInfo::new(Some(u64::from(self.frameno)), None, None, stream.tb_num, stream.tb_den); + self.frameno += 1; + Ok(Some(NAPacket::new(stream, ts, false, data))) + } else { + Ok(None) + } + } + fn reset(&mut self) { + self.buf.clear(); + } + fn bytes_left(&self) -> usize { self.buf.len() } +} + +pub fn get_packetiser() -> Box { + Box::new(EuclidPacketiser::new()) +} + #[cfg(test)] mod test { use nihav_core::codecs::RegisteredDecoders; diff --git a/nihav-acorn/src/codecs/mod.rs b/nihav-acorn/src/codecs/mod.rs index 5503d1a..4649531 100644 --- a/nihav-acorn/src/codecs/mod.rs +++ b/nihav-acorn/src/codecs/mod.rs @@ -112,6 +112,9 @@ const ACORN_PACKETISERS: &[PacketiserInfo] = &[ #[cfg(feature="decoder_rawaudio")] PacketiserInfo { name: "arm_rawaudio", get_packetiser: rawaudio::get_packetiser }, +#[cfg(feature="decoder_euclid")] + PacketiserInfo { name: "euclid", get_packetiser: euclid::get_packetiser }, + #[cfg(feature="packetiser_cinepak")] PacketiserInfo { name: "cinepak", get_packetiser: wss_packetisers::get_packetiser_cinepak }, #[cfg(feature="packetiser_msvideo1")] diff --git a/nihav-acorn/src/demuxers/armovie.rs b/nihav-acorn/src/demuxers/armovie.rs index 7c64b1f..eabd5d3 100644 --- a/nihav-acorn/src/demuxers/armovie.rs +++ b/nihav-acorn/src/demuxers/armovie.rs @@ -1,5 +1,8 @@ use nihav_core::demuxers::*; +#[cfg(feature="demuxer_tca")] +use super::tca::TCARawDemuxer; + const VIDEO_CODECS: &[(i32, &str)] = &[ ( 1, "movinglines"), ( 2, "arm_rawvideo"), @@ -13,6 +16,7 @@ const VIDEO_CODECS: &[(i32, &str)] = &[ (122, "escape122"), (124, "escape124"), (130, "escape130"), + (500, "euclid"), (600, "msvideo1"), (601, "msvideo1"), (602, "cinepak"), @@ -155,6 +159,8 @@ struct ARMovieDemuxer<'a> { state: ReadState, video_id: Option, audio_ids: Vec, + #[cfg(feature="demuxer_tca")] + tca: Option, } impl<'a> ARMovieDemuxer<'a> { @@ -166,6 +172,8 @@ impl<'a> ARMovieDemuxer<'a> { state: ReadState::None, video_id: None, audio_ids: Vec::new(), + #[cfg(feature="demuxer_tca")] + tca: None, } } fn parse_catalogue(&mut self, offset: u32, num_chunks: usize, even_csize: usize, odd_csize: usize, aud_tracks: usize) -> DemuxerResult<()> { @@ -262,6 +270,24 @@ impl<'a> RawDemuxCore<'a> for ARMovieDemuxer<'a> { self.parse_catalogue(cat_offset, num_chunks, even_chunk_size, odd_chunk_size, num_sound)?; + #[cfg(feature="demuxer_tca")] + if video_codec == 500 { + validate!(fps > 1.0e-4); + let mut tbase = fps; + let mut tb_num = 1; + while tbase.fract() > 1.0e-4 { + tb_num *= 10; + tbase *= 10.0; + } + let tb_den = tbase as u32; + + self.src.seek(SeekFrom::Start(u64::from(self.chunk_offs[0].offset)))?; + let mut tca = TCARawDemuxer::default(); + tca.open(self.src, strmgr, tb_num, tb_den)?; + self.tca = Some(tca); + return Ok(()); + } + let mut stream_id = 0; if video_codec > 0 { let codec_name = if let Some(idx) = VIDEO_CODECS.iter().position(|&(id, _)| id == video_codec) { @@ -338,6 +364,11 @@ impl<'a> RawDemuxCore<'a> for ARMovieDemuxer<'a> { } fn get_data(&mut self, strmgr: &mut StreamManager) -> DemuxerResult { + #[cfg(feature="demuxer_tca")] + if let Some(ref mut tca) = self.tca { + return tca.get_frame(self.src, strmgr); + } + while self.cur_chunk < self.chunk_offs.len() { let chunk = &self.chunk_offs[self.cur_chunk]; match self.state { diff --git a/nihav-acorn/src/demuxers/tca.rs b/nihav-acorn/src/demuxers/tca.rs index 300b68a..e8c6c64 100644 --- a/nihav-acorn/src/demuxers/tca.rs +++ b/nihav-acorn/src/demuxers/tca.rs @@ -118,6 +118,93 @@ impl DemuxerCreator for TCADemuxerCreator { fn get_name(&self) -> &'static str { "tca" } } +#[derive(Default)] +pub(crate) struct TCARawDemuxer { + data_end: u64, +} + +impl TCARawDemuxer { + pub(crate) fn open(&mut self, src: &mut ByteReader, strmgr: &mut StreamManager, tb_num: u32, tb_den: u32) -> DemuxerResult<()> { + let start_pos = src.tell(); + let tag = src.peek_tag()?; + let is_acef = &tag == b"ACEF"; + let acef_size = if is_acef { + src.read_skip(4)?; + src.read_u32le()? + } else { 0 }; + let size2 = src.read_u32le()?; + if is_acef { + validate!(acef_size > 0x44 && size2 + 8 <= acef_size); + } + self.data_end = start_pos + u64::from(size2 + 8); + + src.read_skip(12)?; + + const HDR_SIZE: usize = 0x30; + + let mut hdr = vec![0; HDR_SIZE + 4]; + write_u32le(&mut hdr, HDR_SIZE as u32 + 4)?; + src.read_buf(&mut hdr[4..])?; + + let width = read_u32le(&hdr[8..])? as usize; + let height = read_u32le(&hdr[12..])? as usize; + validate!(width > 0 && height > 0); + validate!((width | height) & 1 == 0); + + if is_acef { + let data_start = src.tell(); + + // scan tail for palette and such + if src.seek(SeekFrom::Start(start_pos + u64::from(acef_size))).is_ok() { + while let Ok(tag) = src.read_tag() { + let size = src.read_u32le()? as usize; + validate!(size >= 8); + if &tag == b"PALE" { + validate!((0x28..=0x428).contains(&size) && (size & 3) == 0); + src.read_skip(0x1C)?; + let nclrs = (size - 0x24) / 4; + hdr.resize(HDR_SIZE + 4 + 256 * 3, 0); + for _ in 0..nclrs { + let idx = usize::from(src.read_byte()?); + src.read_buf(&mut hdr[HDR_SIZE + 4 + idx * 3..][..3])?; + } + } else { + src.read_skip(size - 8)?; + } + } + } + src.seek(SeekFrom::Start(data_start))?; + } + + let vci = NACodecTypeInfo::Video(NAVideoInfo::new(width / 2, height / 2, false, PAL8_FORMAT)); + let vinfo = NACodecInfo::new("euclid", vci, Some(hdr)); + let ret = strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, tb_num, tb_den, 0)); + if ret.is_none() { + return Err(DemuxerError::MemoryError); + } + + Ok(()) + } + + pub(crate) fn get_frame(&mut self, src: &mut ByteReader, strmgr: &mut StreamManager) -> DemuxerResult { + if src.tell() >= self.data_end { + return Err(DemuxerError::EOF); + } + let fsize = src.read_u32le()? as usize; + if fsize == 0 { + return Err(DemuxerError::EOF); + } + validate!((9..=1048576).contains(&fsize)); + if let Some(stream) = strmgr.get_stream(0) { + let mut buf = vec![0; fsize - 4]; + src.read_buf(&mut buf)?; + Ok(NARawData::new(stream, buf)) + } else { + Err(DemuxerError::InvalidData) + } + } +} + #[cfg(test)] mod test { use super::*;