From fd7e6906598e7fe87d928e5c7f4116a51a08a3ac Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Sat, 9 Oct 2021 11:34:40 +0200 Subject: [PATCH] add IVF demuxer for VP8 test samples --- nihav-allstuff/src/lib.rs | 2 + nihav-duck/Cargo.toml | 9 ++- nihav-duck/src/demuxers/ivf.rs | 118 +++++++++++++++++++++++++++++++++ nihav-duck/src/demuxers/mod.rs | 22 ++++++ nihav-duck/src/lib.rs | 3 + nihav-registry/src/detect.rs | 6 ++ 6 files changed, 158 insertions(+), 2 deletions(-) create mode 100644 nihav-duck/src/demuxers/ivf.rs create mode 100644 nihav-duck/src/demuxers/mod.rs diff --git a/nihav-allstuff/src/lib.rs b/nihav-allstuff/src/lib.rs index 36d5d50..195d67d 100644 --- a/nihav-allstuff/src/lib.rs +++ b/nihav-allstuff/src/lib.rs @@ -23,6 +23,7 @@ use nihav_commonfmt::generic_register_all_encoders; use nihav_commonfmt::generic_register_all_muxers; use nihav_duck::duck_register_all_decoders; +use nihav_duck::duck_register_all_demuxers; use nihav_duck::duck_register_all_encoders; use nihav_game::game_register_all_decoders; @@ -66,6 +67,7 @@ pub fn nihav_register_all_decoders(rd: &mut RegisteredDecoders) { /// Registers all known demuxers. pub fn nihav_register_all_demuxers(rd: &mut RegisteredDemuxers) { + duck_register_all_demuxers(rd); generic_register_all_demuxers(rd); game_register_all_demuxers(rd); llaudio_register_all_demuxers(rd); diff --git a/nihav-duck/Cargo.toml b/nihav-duck/Cargo.toml index 69aa800..7de0d50 100644 --- a/nihav-duck/Cargo.toml +++ b/nihav-duck/Cargo.toml @@ -15,7 +15,7 @@ features = ["fft", "dsp_window", "blockdsp"] nihav_commonfmt = { path = "../nihav-commonfmt", default-features=false, features = ["all_demuxers", "all_muxers"] } [features] -default = ["all_decoders", "all_encoders"] +default = ["all_decoders", "all_encoders", "all_demuxers"] all_decoders = ["all_video_decoders", "all_audio_decoders"] all_video_decoders = ["decoder_truemotion1", "decoder_truemotionrt", "decoder_truemotion2", "decoder_truemotion2x", "decoder_vp3", "decoder_vp4", "decoder_vp5", "decoder_vp6", "decoder_vp7"] @@ -39,4 +39,9 @@ all_encoders = ["all_video_encoders"] all_video_encoders = ["encoder_vp6"] encoders = [] -encoder_vp6 = ["encoders"] \ No newline at end of file +encoder_vp6 = ["encoders"] + +all_demuxers = ["demuxer_ivf"] +demuxers = [] + +demuxer_ivf = ["demuxers"] \ No newline at end of file diff --git a/nihav-duck/src/demuxers/ivf.rs b/nihav-duck/src/demuxers/ivf.rs new file mode 100644 index 0000000..7d44460 --- /dev/null +++ b/nihav-duck/src/demuxers/ivf.rs @@ -0,0 +1,118 @@ +use nihav_core::demuxers::*; + +struct IVFDemuxer<'a> { + src: &'a mut ByteReader<'a>, + nframes: u32, + frameno: u32, +} + +impl<'a> IVFDemuxer<'a> { + fn new(src: &'a mut ByteReader<'a>) -> Self { + IVFDemuxer { + src, + nframes: 0, + frameno: 0, + } + } +} + +impl<'a> DemuxCore<'a> for IVFDemuxer<'a> { + fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> { + let tag = self.src.read_tag()?; + validate!(&tag == b"DKIF"); + let ver = self.src.read_u16le()?; + validate!(ver == 0); + let hdr_len = self.src.read_u16le()? as usize; + validate!(hdr_len >= 32); + let fcc = self.src.read_tag()?; + let codec_name = match &fcc { + b"VP80" => "vp8", + _ => "unknown", + }; + let width = self.src.read_u16le()? as usize; + let height = self.src.read_u16le()? as usize; + validate!(width > 0 && height > 0); + let tb_den = self.src.read_u32le()?; + let tb_num = self.src.read_u32le()?; + self.nframes = self.src.read_u32le()?; + self.frameno = 0; + + self.src.seek(SeekFrom::Start(hdr_len as u64))?; + + let vci = NACodecTypeInfo::Video(NAVideoInfo::new(width, height, false, YUV420_FORMAT)); + let vinfo = NACodecInfo::new(codec_name, vci, None); + if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, tb_num, tb_den, u64::from(self.nframes))).is_none() { + return Err(DemuxerError::MemoryError); + } + + Ok(()) + } + + fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult { + while self.frameno < self.nframes { + let fsize = self.src.read_u32le()? as usize; + let tstamp = self.src.read_u64le()?; + self.frameno += 1; + + if fsize == 0 { + continue; + } + + if let Some(stream) = strmgr.get_stream(0) { + let (tb_num, tb_den) = stream.get_timebase(); + let ts = NATimeInfo::new(Some(tstamp), None, None, tb_num, tb_den); + return self.src.read_packet(stream, ts, false, fsize); + } else { + return Err(DemuxerError::InvalidData); + } + } + 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 IVFDemuxer<'a> { + fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] } + fn set_options(&mut self, _options: &[NAOption]) { } + fn query_option_value(&self, _name: &str) -> Option { None } +} + +pub struct IVFDemuxerCreator { } + +impl DemuxerCreator for IVFDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box + 'a> { + Box::new(IVFDemuxer::new(br)) + } + fn get_name(&self) -> &'static str { "ivf" } +} + +#[cfg(test)] +mod test { + use super::*; + use std::fs::File; + + #[test] + fn test_ivf_demux() { + let mut file = File::open("assets/Duck/VP8/vp80-00-comprehensive-001.ivf").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = IVFDemuxer::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); + } + } +} diff --git a/nihav-duck/src/demuxers/mod.rs b/nihav-duck/src/demuxers/mod.rs new file mode 100644 index 0000000..3ac19a2 --- /dev/null +++ b/nihav-duck/src/demuxers/mod.rs @@ -0,0 +1,22 @@ +use nihav_core::demuxers::*; + + +#[allow(unused_macros)] +macro_rules! validate { + ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DemuxerError::InvalidData); } }; +} + +#[cfg(feature="demuxer_ivf")] +mod ivf; + +const DEMUXERS: &[&dyn DemuxerCreator] = &[ +#[cfg(feature="demuxer_ivf")] + &ivf::IVFDemuxerCreator {}, +]; + +/// Registers all available demuxers provided by this crate. +pub fn duck_register_all_demuxers(rd: &mut RegisteredDemuxers) { + for demuxer in DEMUXERS.iter() { + rd.add_demuxer(*demuxer); + } +} diff --git a/nihav-duck/src/lib.rs b/nihav-duck/src/lib.rs index eb8560e..0885514 100644 --- a/nihav-duck/src/lib.rs +++ b/nihav-duck/src/lib.rs @@ -9,8 +9,11 @@ extern crate nihav_codec_support; #[allow(clippy::verbose_bit_mask)] mod codecs; +mod demuxers; + pub use crate::codecs::duck_register_all_decoders; pub use crate::codecs::duck_register_all_encoders; +pub use crate::demuxers::duck_register_all_demuxers; #[cfg(test)] extern crate nihav_commonfmt; diff --git a/nihav-registry/src/detect.rs b/nihav-registry/src/detect.rs index c51936d..11fd2ed 100644 --- a/nihav-registry/src/detect.rs +++ b/nihav-registry/src/detect.rs @@ -235,6 +235,12 @@ const DETECTORS: &[DetectConditions] = &[ extensions: ".y4m", conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"YUV4MPEG2 ") }], }, + DetectConditions { + demux_name: "ivf", + extensions: ".ivf", + conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"DKIF\x00\x00")}, + CheckItem{offs: 6, cond: &CC::Ge(Arg::U16LE(32))}], + }, DetectConditions { demux_name: "fcmp", extensions: ".cmp", -- 2.30.2