]> git.nihav.org Git - nihav.git/commitdiff
DVI AVSS demuxer
authorKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 4 Dec 2024 17:04:10 +0000 (18:04 +0100)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Wed, 4 Dec 2024 17:04:10 +0000 (18:04 +0100)
nihav-indeo/Cargo.toml
nihav-indeo/src/demuxers/dvi.rs [new file with mode: 0644]
nihav-indeo/src/demuxers/mod.rs
nihav-registry/src/detect.rs

index 2594903691263c011d442337cd51df3f3b2bf80a..82343f74deda1ea9b3988969d40126407dd8407e 100644 (file)
@@ -29,9 +29,10 @@ decoder_indeo4 = ["decoders"]
 decoder_indeo5 = ["decoders"]
 decoder_intel263 = ["decoders"]
 
-all_demuxers = ["demuxer_ivf"]
+all_demuxers = ["demuxer_dvi", "demuxer_ivf"]
 demuxers = []
 
+demuxer_dvi = ["demuxers"]
 demuxer_ivf = ["demuxers"]
 
 all_encoders = ["all_video_encoders"]
diff --git a/nihav-indeo/src/demuxers/dvi.rs b/nihav-indeo/src/demuxers/dvi.rs
new file mode 100644 (file)
index 0000000..6a2d90e
--- /dev/null
@@ -0,0 +1,260 @@
+use std::collections::VecDeque;
+use nihav_core::demuxers::*;
+
+const GRAY_FORMAT: NAPixelFormaton = NAPixelFormaton {
+    model: ColorModel::YUV(YUVSubmodel::YUVJ),
+    components: 1,
+    comp_info: [Some(NAPixelChromaton{h_ss: 0, v_ss: 0, packed: false, depth: 8, shift: 0, comp_offs: 0, next_elem: 1}), None, None, None, None],
+    elem_size: 1,
+    be: true,
+    alpha: false,
+    palette: false,
+};
+
+struct DVIDemuxer<'a> {
+    src:            &'a mut ByteReader<'a>,
+    data_end:       u64,
+    packets:        VecDeque<NAPacket>,
+    fsizes:         Vec<usize>,
+}
+
+impl<'a> DVIDemuxer<'a> {
+    fn new(src: &'a mut ByteReader<'a>) -> Self {
+        Self {
+            src,
+            data_end:   0,
+            packets:    VecDeque::new(),
+            fsizes:     Vec::new(),
+        }
+    }
+}
+
+impl<'a> DemuxCore<'a> for DVIDemuxer<'a> {
+    fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
+        let br = &mut self.src;
+
+        // standard file header
+        let tag                         = br.read_tag()?;
+        validate!(&tag == b"IVDV");
+        let std_hdr_size                = br.read_u16le()? as usize;
+        validate!(std_hdr_size >= 12);
+        let _version                    = br.read_u16le()?;
+        let _annotation_offset          = br.read_u32le()?;
+                                          br.read_skip(std_hdr_size - 12)?;
+
+        // AVL file header
+        let tag                         = br.read_tag()?;
+        validate!(&tag == b"SSVA");
+        let avl_hdr_size                = br.read_u16le()? as usize;
+        validate!(avl_hdr_size >= 120);
+        let data_start = (std_hdr_size + avl_hdr_size) as u32;
+        let _version                    = br.read_u16le()?;
+        let _stream_group_count         = br.read_u16le()? as usize;
+        let _stream_group_size          = br.read_u16le()? as usize;
+        let _stream_group_offset        = br.read_u32le()?;
+        let _stream_group_version       = br.read_u16le()?;
+        let _stream_size                = br.read_u16le()? as usize;
+        let _stream_version             = br.read_u16le()?;
+        let stream_count                = br.read_u16le()? as usize;
+        validate!(stream_count > 0);
+        let stream_offset               = br.read_u32le()?;
+        validate!(stream_offset >= data_start);
+        let hdr_pool_offset             = br.read_u32le()?;
+        validate!(hdr_pool_offset >= data_start);
+        let _label_count                = br.read_u32le()?;
+        let _label_offset               = br.read_u32le()?;
+        let _label_size                 = br.read_u16le()? as usize;
+        let _label_version              = br.read_u16le()?;
+        let _video_seq_hdr_off          = br.read_u32le()?;
+        let _video_seq_hdr_size         = br.read_u16le()? as usize;
+        let _frame_hdr_version          = br.read_u16le()?;
+        let frame_count                 = br.read_u32le()? as usize;
+        let _frame_size                 = br.read_u32le()? as usize;
+        let first_frame_offset          = br.read_u32le()?;
+        let end_of_frame_data           = br.read_u32le()?;
+        let frame_hdr_size              = br.read_u16le()? as usize;
+        if frame_hdr_size != 16 { return Err(DemuxerError::NotImplemented); }
+        let frame_dir_size              = br.read_u16le()? as usize;
+        if frame_dir_size != 4 { return Err(DemuxerError::NotImplemented); }
+        let frame_dir_offset            = br.read_u32le()?;
+        validate!((frame_dir_offset >= data_start && frame_dir_offset < first_frame_offset) || (frame_dir_offset >= end_of_frame_data));
+        let _frame_dir_version          = br.read_u16le()?;
+        let fps                         = br.read_u16le()?;
+        validate!(fps > 0);
+        let _update_flag                = br.read_u32le()?;
+        let _free_block_offset          = br.read_u32le()?;
+                                          br.read_skip(32)?; // patch - unused
+
+        br.seek(SeekFrom::Start(u64::from(stream_offset)))?;
+
+        self.fsizes = vec![0; stream_count];
+        let mut substream_pos = u64::from(hdr_pool_offset);
+        for stream_id in 0..stream_count {
+            let tag                     = br.read_tag()?;
+            validate!(&tag == b"MRTS");
+            let hdr_type                = br.read_u16le()?;
+            let hdr_subtype             = br.read_u16le()?;
+            let hdr_count               = br.read_u16le()?;
+            validate!(hdr_count == 1);
+            let _next_stream            = br.read_u16le()?;
+            let _group_num              = br.read_u16le()?;
+                                          br.read_skip(2)?; // pad
+            let _flags                  = br.read_u32le()?;
+            let _max_frame_size         = br.read_u32le()? as usize;
+            let _first_offset           = br.read_u32le()?;
+                                          br.read_skip(16)?; // stream name
+            let stream_hdr_pos = br.tell();
+            br.seek(SeekFrom::Start(substream_pos))?;
+            match hdr_type {
+                2 => { // audio
+                    let tag             = br.read_tag()?;
+                    validate!(&tag == b"IDUA");
+                    let size            = br.read_u16le()? as usize;
+                    validate!(size >= 168);
+                    // TODO support PCM or IMA ADPCM if there are any samples
+                                          br.read_skip(size - 6)?;
+                },
+                3 => { // video
+                    let tag             = br.read_tag()?;
+                    validate!(&tag == b"GMIC");
+                    let size            = br.read_u16le()? as usize;
+                    validate!(size >= 136);
+                    let _header_version = br.read_u16le()?;
+                                          br.read_skip(80)?; // original file name
+                    let _orig_frame     = br.read_u32le()?;
+                    let _orig_id        = br.read_u16le()?;
+                                          br.read_skip(2)?; // pad
+                    let _frame_count    = br.read_u32le()? as usize;
+                    let _next_offset    = br.read_u32le()?;
+                    let _xpos           = br.read_u16le()? as usize;
+                    let _ypos           = br.read_u16le()? as usize;
+                    let width           = br.read_u16le()? as usize;
+                    let height          = br.read_u16le()? as usize;
+                    validate!(width > 0 && height > 0);
+                    let _xkoda          = br.read_u16le()? as usize;
+                    let _ykoda          = br.read_u16le()? as usize;
+                    let _drop_frame     = br.read_u16le()?;
+                    let _drop_phrase    = br.read_u16le()?;
+                    let _still_period   = br.read_u32le()?;
+                    let _buf_min        = br.read_u16le()? as usize;
+                    let _buf_max        = br.read_u16le()? as usize;
+                    let codec_id        = br.read_u16le()?;
+                                          br.read_skip(2)?; // pad
+                    let _dcfid          = br.read_u32le()?;
+                                          br.read_skip(size - 136)?;
+
+                    let cname = match codec_id {
+                            0x01 | 0x02 | 0x80 => "ima-pic",
+                            0x05 | 0x06 => "ima-plv",
+                            0x14 | 0x15 => "ima-plv2",
+                            0x81 => "jpeg",
+                            0xC0 | 0xC2 | 0xC3 => "ima-rtv1",
+                            0xC8 | 0xC9 | 0xCA => "ima-rtv2",
+                            _ => "unknown",
+                        };
+
+                    let fmt = match hdr_subtype {
+                            1 | 11 | 12 => GRAY_FORMAT, // Y/U/V component only
+                            _ => YUV410_FORMAT,
+                        };
+
+                    let mut vhdr = NAVideoInfo::new(width, height, false, fmt);
+                    vhdr.bits = 24;
+
+                    let vinfo = NACodecInfo::new(cname, NACodecTypeInfo::Video(vhdr), None);
+                    if strmgr.add_stream(NAStream::new(StreamType::Video, stream_id as u32, vinfo, 1, u32::from(fps), frame_count as u64)).is_none() {
+                        return Err(DemuxerError::MemoryError);
+                    }
+                },
+                _ => {
+                    let _tag            = br.read_tag()?;
+                    let size            = br.read_u16le()? as usize;
+                    validate!(size >= 6);
+                                          br.read_skip(size - 6)?;
+                },
+            }
+            substream_pos = br.tell();
+            br.seek(SeekFrom::Start(stream_hdr_pos))?;
+        }
+        br.seek(SeekFrom::Start(u64::from(first_frame_offset)))?;
+        self.data_end = u64::from(end_of_frame_data);
+        Ok(())
+    }
+
+    fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+        while self.src.tell() < self.data_end {
+            if let Some(pkt) = self.packets.pop_front() {
+                return Ok(pkt);
+            }
+
+            let mut hdr = [0; 12];
+                                      self.src.read_buf(&mut hdr)?;
+            let frame_id  = read_u32le(&hdr).unwrap_or(0);
+            for el in self.fsizes.iter_mut() {
+                *el                 = self.src.read_u32le()? as usize;
+            }
+            for (id, &size) in self.fsizes.iter().enumerate() {
+                if let Some(stream) = strmgr.get_stream_by_id(id as u32) {
+                    let mut buf = vec![0; size + 16];
+                    buf[..12].copy_from_slice(&hdr);
+                    write_u32le(&mut buf[12..16], size as u32).unwrap();
+                                      self.src.read_buf(&mut buf[16..])?;
+
+                    let ts = stream.make_ts(Some(u64::from(frame_id)), None, None);
+                    let pkt = NAPacket::new_from_refbuf(stream, ts, frame_id == 0, NABufferRef::new(buf));
+                    self.packets.push_back(pkt);
+                } else {
+                                      self.src.read_skip(size)?;
+                }
+            }
+        }
+        Err(DemuxerError::EOF)
+    }
+    fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
+        Err(DemuxerError::NotPossible)
+    }
+    fn get_duration(&self) -> u64 { 0 }
+}
+
+impl<'a> NAOptionHandler for DVIDemuxer<'a> {
+    fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+    fn set_options(&mut self, _options: &[NAOption]) {}
+    fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+pub struct DVIDemuxerCreator { }
+
+impl DemuxerCreator for DVIDemuxerCreator {
+    fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+        Box::new(DVIDemuxer::new(br))
+    }
+    fn get_name(&self) -> &'static str { "dvi" }
+}
+
+#[cfg(test)]
+mod test {
+    use super::*;
+    use std::fs::File;
+
+    #[test]
+    fn test_dvi_demux() {
+        // sample comes from a demo by Digital Video Arts
+        let mut file = File::open("assets/Indeo/YULELOG.AVS").unwrap();
+        let mut fr = FileReader::new_read(&mut file);
+        let mut br = ByteReader::new(&mut fr);
+        let mut dmx = DVIDemuxer::new(&mut br);
+        let mut sm = StreamManager::new();
+        let mut si = SeekIndex::new();
+        dmx.open(&mut sm, &mut si).unwrap();
+
+        loop {
+            let pktres = dmx.get_frame(&mut sm);
+            if let Err(e) = pktres {
+                if e == DemuxerError::EOF { break; }
+                panic!("error");
+            }
+            let pkt = pktres.unwrap();
+            println!("Got {}", pkt);
+        }
+    }
+}
index 6e25fccac616507ce42bac7f70c7bcb4ffe49baf..a00831043646441e962eb12c90b8f397cb707597 100644 (file)
@@ -11,10 +11,15 @@ macro_rules! validate {
     ($a:expr) => { if !$a { return Err(DemuxerError::InvalidData); } };
 }
 
+#[cfg(feature="demuxer_dvi")]
+mod dvi;
+
 #[cfg(feature="demuxer_ivf")]
 mod ivf;
 
 const DEMUXERS: &[&dyn DemuxerCreator] = &[
+#[cfg(feature="demuxer_dvi")]
+    &dvi::DVIDemuxerCreator {},
 #[cfg(feature="demuxer_ivf")]
     &ivf::IVFDemuxerCreator {},
 ];
index 287f1f6801b88c04bb1ca337b072739aa4af5b74..553f5420a230016ddeb69d1c7385fa52e5032f05 100644 (file)
@@ -261,6 +261,12 @@ const DETECTORS: &[DetectConditions] = &[
         conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"FLV") },
                       CheckItem{offs: 3, cond: &CC::Le(Arg::Byte(1)) }],
     },
+    DetectConditions {
+        demux_name: "dvi",
+        extensions: ".avs,.dvi",
+        conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"IVDV")},
+                      CheckItem{offs: 12, cond: &CC::Str(b"SSVA")}],
+    },
     DetectConditions {
         demux_name: "ivf",
         extensions: ".ivf",