]> git.nihav.org Git - nihav.git/commitdiff
add support for TCA in ARMovie
authorKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 12 Apr 2025 09:53:50 +0000 (11:53 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Sat, 12 Apr 2025 09:53:50 +0000 (11:53 +0200)
nihav-acorn/src/codecs/euclid.rs
nihav-acorn/src/codecs/mod.rs
nihav-acorn/src/demuxers/armovie.rs
nihav-acorn/src/demuxers/tca.rs

index 3f7e13d10a1eaed27d7b418c3f38a18995450abf..bffb95f4b8104572593282646ffca185f975fa0b 100644 (file)
@@ -267,6 +267,58 @@ pub fn get_decoder() -> Box<dyn NADecoder + Send> {
     Box::new(EuclidDecoder::new())
 }
 
+#[derive(Default)]
+struct EuclidPacketiser {
+    stream:     Option<NAStreamRef>,
+    buf:        Vec<u8>,
+    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<NAStreamRef> {
+        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<usize> {
+        Err(DecoderError::NotImplemented)
+    }
+    fn get_packet(&mut self, stream: NAStreamRef) -> DecoderResult<Option<NAPacket>> {
+        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<dyn NAPacketiser + Send> {
+    Box::new(EuclidPacketiser::new())
+}
+
 #[cfg(test)]
 mod test {
     use nihav_core::codecs::RegisteredDecoders;
index 5503d1ae721106cca0146ceebff9633eb3abb63c..46495319b965678895c490f747b287a87fc97a9f 100644 (file)
@@ -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")]
index 7c64b1f710f1250f027cab2082bb5fbe5b1913e4..eabd5d368e5d58679bc7666dd69a5e4f52d47b8e 100644 (file)
@@ -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<usize>,
     audio_ids:      Vec<usize>,
+    #[cfg(feature="demuxer_tca")]
+    tca:            Option<TCARawDemuxer>,
 }
 
 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<NARawData> {
+        #[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 {
index 300b68a967125094bd32020c2c4d1fa5eaa284d0..e8c6c64c5ac70ecd3500e19f394f400d1fdbfce1 100644 (file)
@@ -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<NARawData> {
+        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::*;