game_register_all_demuxers(rd);
indeo_register_all_demuxers(rd);
llaudio_register_all_demuxers(rd);
+ misc_register_all_demuxers(rd);
rad_register_all_demuxers(rd);
realmedia_register_all_demuxers(rd);
vivo_register_all_demuxers(rd);
nihav_commonfmt = { path = "../nihav-commonfmt", default-features=false, features = ["all_demuxers"] }
[features]
-default = ["all_decoders"]
+default = ["all_decoders", "all_demuxers"]
decoders = []
+demuxers = []
all_decoders = ["all_video_decoders", "all_audio_decoders"]
-all_video_decoders = ["decoder_mwv1", "decoder_pgvv"]
+all_video_decoders = ["decoder_mwv1", "decoder_pgvv", "decoder_qpeg"]
decoder_mwv1 = ["decoders"]
decoder_pgvv = ["decoders"]
+decoder_qpeg = ["decoders"]
all_audio_decoders = []
+
+all_demuxers = ["demuxer_qpeg"]
+demuxer_qpeg = ["demuxers"]
\ No newline at end of file
#[cfg(feature="decoder_pgvv")]
mod pgvv;
+#[cfg(feature="decoder_qpeg")]
+mod qpeg;
+
const DECODERS: &[DecoderInfo] = &[
#[cfg(feature="decoder_mwv1")]
DecoderInfo { name: "mwv1", get_decoder: mwv1::get_decoder },
#[cfg(feature="decoder_pgvv")]
DecoderInfo { name: "pgvv", get_decoder: pgvv::get_decoder },
+#[cfg(feature="decoder_qpeg")]
+ DecoderInfo { name: "qpeg-dvc", get_decoder: qpeg::get_decoder_dvc },
];
/// Registers all available codecs provided by this crate.
--- /dev/null
+use nihav_core::io::byteio::{ByteReader,MemoryReader};
+use nihav_core::codecs::*;
+
+struct DVCDecoder {
+ info: NACodecInfoRef,
+ pal: [u8; 768],
+ frame: Vec<u8>,
+ width: usize,
+ height: usize,
+}
+
+impl DVCDecoder {
+ fn new() -> Self {
+ Self {
+ info: NACodecInfo::new_dummy(),
+ pal: [0; 768],
+ frame: Vec::new(),
+ width: 0,
+ height: 0,
+ }
+ }
+}
+
+impl NADecoder for DVCDecoder {
+ fn init(&mut self, _supp: &mut NADecoderSupport, info: NACodecInfoRef) -> DecoderResult<()> {
+ if let NACodecTypeInfo::Video(vinfo) = info.get_properties() {
+ let w = vinfo.get_width();
+ let h = vinfo.get_height();
+ self.width = w;
+ self.height = h;
+ self.frame = vec![0; self.width * self.height];
+ if let Some(pal) = info.get_extradata() {
+ validate!(pal.len() == 768);
+ self.pal.copy_from_slice(&pal);
+ }
+ let myinfo = NACodecTypeInfo::Video(NAVideoInfo::new(w, h, false, PAL8_FORMAT));
+ 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<NAFrameRef> {
+ let src = pkt.get_buffer();
+ let mut mr = MemoryReader::new_read(&src);
+ let mut br = ByteReader::new(&mut mr);
+
+ let mut pos = 0;
+ let mut is_intra = true;
+ br.read_skip(128)?;
+ let remap = &src[..128];
+ loop {
+ let op = br.read_byte()?;
+ match op {
+ 0x00 => {
+ validate!(pos < self.frame.len());
+ pos += 1;
+ is_intra = false;
+ },
+ 0x01..=0x7F => {
+ validate!(pos < self.frame.len());
+ self.frame[pos] = remap[usize::from(op)];
+ pos += 1;
+ },
+ 0x80..=0xBF => {
+ let skip = usize::from(op & 0x3F);
+ let skip = match skip {
+ 0x00 => usize::from(br.read_byte()?) + 0x40,
+ 0x01 => usize::from(br.read_byte()?) + 0x140,
+ _ => skip,
+ };
+ validate!(pos + skip <= self.frame.len());
+ pos += skip;
+ is_intra = false;
+ },
+ 0xC0..=0xDF => {
+ let len = usize::from(op & 0x1F) + 1;
+ validate!(pos + len <= self.frame.len());
+ br.read_buf(&mut self.frame[pos..][..len])?;
+ pos += len;
+ },
+ 0xE0 => {
+ if pos != self.frame.len() {
+ is_intra = false;
+ }
+ break;
+ },
+ 0xE1..=0xFF => {
+ let run = usize::from(op & 0x1F) + 1;
+ validate!(pos + run <= self.frame.len());
+ let clr = br.read_byte()?;
+ for el in self.frame[pos..][..run].iter_mut() {
+ *el = clr;
+ }
+ pos += run;
+ },
+ }
+ }
+
+ let vinfo = NAVideoInfo::new(self.width, self.height, false, PAL8_FORMAT);
+ let bufinfo = alloc_video_buffer(vinfo, 0)?;
+
+ if let Some(mut buf) = bufinfo.get_vbuf() {
+ let stride = buf.get_stride(0);
+ let pal_off = buf.get_offset(1);
+ let data = buf.get_data_mut().unwrap();
+
+ for (dline, sline) in data.chunks_mut(stride).zip(self.frame.chunks_exact(self.width)) {
+ dline[..self.width].copy_from_slice(sline);
+ }
+ data[pal_off..][..768].copy_from_slice(&self.pal);
+ } else { unreachable!(); }
+
+ let mut frm = NAFrame::new_from_pkt(pkt, self.info.clone(), bufinfo);
+ frm.set_keyframe(is_intra);
+ frm.set_frame_type(if is_intra { FrameType::I } else { FrameType::P });
+ Ok(frm.into_ref())
+ }
+ fn flush(&mut self) {
+ for el in self.frame.iter_mut() {
+ *el = 0;
+ }
+ }
+}
+
+impl NAOptionHandler for DVCDecoder {
+ fn get_supported_options(&self) -> &[NAOptionDefinition] { &[] }
+ fn set_options(&mut self, _options: &[NAOption]) { }
+ fn query_option_value(&self, _name: &str) -> Option<NAValue> { None }
+}
+
+pub fn get_decoder_dvc() -> Box<dyn NADecoder + Send> {
+ Box::new(DVCDecoder::new())
+}
+
+#[cfg(test)]
+mod test {
+ use nihav_core::codecs::RegisteredDecoders;
+ use nihav_core::demuxers::RegisteredDemuxers;
+ use nihav_codec_support::test::dec_video::*;
+ use crate::*;
+ use nihav_commonfmt::generic_register_all_demuxers;
+ #[test]
+ fn test_qpeg_dvc() {
+ let mut dmx_reg = RegisteredDemuxers::new();
+ misc_register_all_demuxers(&mut dmx_reg);
+ generic_register_all_demuxers(&mut dmx_reg);
+ let mut dec_reg = RegisteredDecoders::new();
+ misc_register_all_decoders(&mut dec_reg);
+
+ // sample from Inside Multimedia 1994 October
+ test_decoding("qpeg", "qpeg-dvc", "assets/Misc/club01.dvc", Some(3), &dmx_reg,
+ &dec_reg, ExpectedTestResult::MD5Frames(vec![
+ [0x90f20329, 0x305bf02c, 0xb14217d1, 0x9213dfe6],
+ [0x22e11154, 0x51deb566, 0xda570987, 0xa044b123],
+ [0x7eba24ae, 0x3b786669, 0xb49681b1, 0x447d48c9],
+ [0xdad2b6bf, 0xa533f767, 0x6ace31cf, 0x5d8a7318]]));
+ }
+}
--- /dev/null
+use nihav_core::demuxers::*;
+
+
+#[allow(unused_macros)]
+#[cfg(debug_assertions)]
+macro_rules! validate {
+ ($a:expr) => { if !$a { println!("check failed at {}:{}", file!(), line!()); return Err(DemuxerError::InvalidData); } };
+}
+#[cfg(not(debug_assertions))]
+macro_rules! validate {
+ ($a:expr) => { if !$a { return Err(DemuxerError::InvalidData); } };
+}
+
+#[cfg(feature="demuxer_qpeg")]
+mod qpeg;
+
+const DEMUXERS: &[&dyn DemuxerCreator] = &[
+#[cfg(feature="demuxer_qpeg")]
+ &qpeg::QPEGDemuxerCreator {},
+];
+
+/// Registers all available demuxers provided by this crate.
+pub fn misc_register_all_demuxers(rd: &mut RegisteredDemuxers) {
+ for demuxer in DEMUXERS.iter() {
+ rd.add_demuxer(*demuxer);
+ }
+}
--- /dev/null
+use nihav_core::demuxers::*;
+
+struct QPEGDemuxer<'a> {
+ src: &'a mut ByteReader<'a>,
+ frameno: u32,
+}
+
+impl<'a> QPEGDemuxer<'a> {
+ fn new(src: &'a mut ByteReader<'a>) -> Self {
+ QPEGDemuxer {
+ src,
+ frameno: 0,
+ }
+ }
+}
+
+impl<'a> DemuxCore<'a> for QPEGDemuxer<'a> {
+ fn open(&mut self, strmgr: &mut StreamManager, _seek_index: &mut SeekIndex) -> DemuxerResult<()> {
+ let mut magic = [0; 6];
+ self.src.read_buf(&mut magic)?;
+ validate!(&magic == b"IDVCd\0");
+ let height = self.src.read_u16le()? as usize;
+ let width = self.src.read_u16le()? as usize;
+ validate!((1..=640).contains(&width) && (1..=480).contains(&height));
+ self.src.seek(SeekFrom::Start(0x60))?;
+
+ let mut pal = vec![0; 768];
+ self.src.read_buf(&mut pal[60..])?;
+
+ let vci = NACodecTypeInfo::Video(NAVideoInfo::new(width, height, false, PAL8_FORMAT));
+ let vinfo = NACodecInfo::new("qpeg-dvc", vci, Some(pal));
+ if strmgr.add_stream(NAStream::new(StreamType::Video, 0, vinfo, 1, 24, 0)).is_none() {
+ return Err(DemuxerError::MemoryError);
+ }
+
+ Ok(())
+ }
+
+ fn get_frame(&mut self, strmgr: &mut StreamManager) -> DemuxerResult<NAPacket> {
+ let tag = self.src.read_u16le()?;
+ if tag == 0 {
+ return Err(DemuxerError::EOF);
+ }
+ let fsize = self.src.read_u32le()? as usize;
+ validate!(fsize > 4);
+ self.frameno += 1;
+
+ if let Some(stream) = strmgr.get_stream(0) {
+ let ts = stream.make_ts(Some(u64::from(self.frameno - 1)), None, None);
+ self.src.read_packet(stream, ts, self.frameno == 1, fsize - 4)
+ } else {
+ Err(DemuxerError::InvalidData)
+ }
+ }
+
+ fn seek(&mut self, _time: NATimePoint, _seek_index: &SeekIndex) -> DemuxerResult<()> {
+ Err(DemuxerError::NotPossible)
+ }
+ fn get_duration(&self) -> u64 { 0 }
+}
+
+impl<'a> NAOptionHandler for QPEGDemuxer<'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 QPEGDemuxerCreator { }
+
+impl DemuxerCreator for QPEGDemuxerCreator {
+ fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box<dyn DemuxCore<'a> + 'a> {
+ Box::new(QPEGDemuxer::new(br))
+ }
+ fn get_name(&self) -> &'static str { "qpeg" }
+}
+
+#[cfg(test)]
+mod test {
+ use super::*;
+ use std::fs::File;
+
+ #[test]
+ fn test_qpeg_demux() {
+ // sample from Inside Multimedia 1994 October
+ let mut file = File::open("assets/Misc/club01.dvc").unwrap();
+ let mut fr = FileReader::new_read(&mut file);
+ let mut br = ByteReader::new(&mut fr);
+ let mut dmx = QPEGDemuxer::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);
+ }
+ }
+}
#[cfg(feature="decoders")]
mod codecs;
+#[cfg(feature="demuxers")]
+mod demuxers;
+
#[cfg(feature="decoders")]
pub use crate::codecs::misc_register_all_decoders;
+
+#[cfg(feature="demuxers")]
+pub use crate::demuxers::misc_register_all_demuxers;
conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"SANM")},
CheckItem{offs: 8, cond: &CC::Str(b"SHDR")}],
},
+ DetectConditions {
+ demux_name: "qpeg",
+ extensions: ".dvc",
+ conditions: &[CheckItem{offs: 0, cond: &CC::Str(b"IDVCd")}],
+ },
DetectConditions {
demux_name: "realaudio",
extensions: ".ra,.ram",
desc!(video-im; "mwv1", "Aware MotionWavelets"),
desc!(video-im; "pgvv", "Radius Studio Video"),
+
+ desc!(video-llp; "qpeg-dvc", "QPEG video in DVC"),
];
static AVI_VIDEO_CODEC_REGISTER: &[(&[u8;4], &str)] = &[