From: Kostya Shishkov Date: Sat, 6 May 2017 11:45:15 +0000 (+0200) Subject: demuxer initial work X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=5869fd634c6a174ef2c541ddad4b1a4e9ec26d29;p=nihav.git demuxer initial work --- diff --git a/src/demuxers/gdv.rs b/src/demuxers/gdv.rs new file mode 100644 index 0000000..c556197 --- /dev/null +++ b/src/demuxers/gdv.rs @@ -0,0 +1,157 @@ +use super::*; +use io::byteio::*; +use frame::*; +use std::io::SeekFrom; +//use std::collections::HashMap; + +enum GDVState { + NewFrame, + AudioRead, +} + +#[allow(dead_code)] +pub struct GremlinVideoDemuxer<'a> { + opened: bool, + src: &'a mut ByteReader<'a>, + streams: Vec>>, + frames: u16, + cur_frame: u16, + asize: usize, + apacked: bool, + state: GDVState, + pktdta: Vec, +} + +impl<'a> NADemuxer<'a> for GremlinVideoDemuxer<'a> { + #[allow(unused_variables)] + fn open(&mut self) -> DemuxerResult<()> { + let src = &mut self.src; + let magic = src.read_u32le()?; + if magic != 0x29111994 { return Err(DemuxerError::InvalidData); } + let id = src.read_u16le()?; + let frames = src.read_u16le()?; + let fps = src.read_u16le()?; + let aflags = src.read_u16le()?; + let rate = src.read_u16le()?; + let depth = src.read_u16le()?; + let max_fs = src.read_u16le()?; + src.read_skip(2)?; + let width = src.read_u16le()?; + let height = src.read_u16le()?; +println!("id {} frames {} fps {} sound {} Hz {:X} img {} - {}x{}",id,frames,fps,rate,aflags,depth,width,height); + if max_fs > 0 { + let vhdr = NAVideoInfo::new(width as u32, height as u32, false, PAL8_FORMAT); + let vci = NACodecTypeInfo::Video(vhdr); + let vinfo = NACodecInfo::new(vci, None); + let vstr = NAStream::new(StreamType::Video, 0, vinfo); + self.streams.push(Rc::new(vstr)); + } + if (aflags & 1) != 0 { + let channels = if (aflags & 2) != 0 { 2 } else { 1 }; + let ahdr = NAAudioInfo::new(rate as u32, channels as u8, if (aflags & 4) != 0 { SND_S16_FORMAT } else { SND_U8_FORMAT }, 2); + let ainfo = NACodecInfo::new(NACodecTypeInfo::Audio(ahdr), None); + let astr = NAStream::new(StreamType::Audio, 1, ainfo); + self.streams.push(Rc::new(astr)); + + let packed = if (aflags & 8) != 0 { 1 } else { 0 }; + self.asize = (((rate / fps) * channels * (if (aflags & 4) != 0 { 2 } else { 1 })) >> packed) as usize; + self.apacked = (aflags & 8) != 0; +println!("audio chunk size {}({:X})",self.asize,self.asize); + } + if max_fs > 0 && depth == 1 { + src.read_skip(768)?; + } + self.frames = frames; + self.opened = true; + self.state = GDVState::NewFrame; + Ok(()) + } + + #[allow(unused_variables)] + fn get_frame(&mut self) -> DemuxerResult { + if !self.opened { return Err(DemuxerError::NoSuchInput); } + if self.cur_frame >= self.frames { return Err(DemuxerError::EOF); } + match self.state { + GDVState::NewFrame if self.asize > 0 => { self.read_achunk() } + _ => { self.read_vchunk() } + } + } + + #[allow(unused_variables)] + fn seek(&mut self, time: u64) -> DemuxerResult<()> { + if !self.opened { return Err(DemuxerError::NoSuchInput); } + Err(DemuxerError::NotImplemented) + } +} +/*impl<'a> Drop for GremlinVideoDemuxer<'a> { + #[allow(unused_variables)] + fn drop(&mut self) { + } +}*/ +impl<'a> GremlinVideoDemuxer<'a> { + pub fn new(io: &'a mut ByteReader<'a>) -> Self { + GremlinVideoDemuxer { + cur_frame: 0, + frames: 0, + opened: false, + asize: 0, + apacked: false, + state: GDVState::NewFrame, +pktdta: Vec::new(), + src: io, + streams: Vec::new() + } + } + + fn find_stream(&mut self, id: u32) -> Rc> { + for i in 0..self.streams.len() { + if self.streams[i].get_id() == id { + return self.streams[i].clone(); + } + } + panic!("stream not found"); + } + fn read_achunk(&mut self) -> DemuxerResult { + self.state = GDVState::AudioRead; + let str = self.find_stream(1); + self.src.read_packet(str, Some(self.cur_frame as u64), None, None, true, self.asize) + } + + fn read_vchunk(&mut self) -> DemuxerResult { + let str = self.find_stream(0); + let mut src = &mut self.src; + let magic = src.read_u16be()?; + if magic != 0x0513 { return Err(DemuxerError::InvalidData); } + let size = (src.read_u16le()? as usize) + 4; + let tmp = src.read_u32le()?; + let flags = (tmp & 0xFF) as usize; + src.seek(SeekFrom::Current(-4))?; + self.state = GDVState::NewFrame; + self.cur_frame = self.cur_frame + 1; + src.read_packet(str, Some((self.cur_frame - 1) as u64), None, None, if (flags & 64) != 0 { true } else { false }, size) + } +} + +#[cfg(test)] +mod test { + use super::*; + use std::fs::File; + + #[test] + fn test_gdv_demux() { + let mut file = File::open("assets/intro1.gdv").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = GremlinVideoDemuxer::new(&mut br); + dmx.open().unwrap(); + loop { + let pktres = dmx.get_frame(); + if let Err(e) = pktres { + if (e as i32) == (DemuxerError::EOF as i32) { break; } + panic!("error"); + } + let pkt = pktres.unwrap(); + println!("Got {}", pkt); + } + } +} diff --git a/src/demuxers/mod.rs b/src/demuxers/mod.rs new file mode 100644 index 0000000..5c397ac --- /dev/null +++ b/src/demuxers/mod.rs @@ -0,0 +1,142 @@ +pub mod gdv; + +use std::fmt; +use std::rc::Rc; +use frame::*; +//use std::collections::HashMap; +use io::byteio::*; + +#[derive(Debug)] +#[allow(dead_code)] +pub enum StreamType { + Video, + Audio, + Subtitles, + Data, +} + +impl fmt::Display for StreamType { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + match *self { + StreamType::Video => write!(f, "Video"), + StreamType::Audio => write!(f, "Audio"), + StreamType::Subtitles => write!(f, "Subtitles"), + StreamType::Data => write!(f, "Data"), + } + } +} + + +#[allow(dead_code)] +pub struct NAStream<'a> { + media_type: StreamType, + id: u32, + info: Rc>, +} + +impl<'a> NAStream<'a> { + pub fn new(mt: StreamType, id: u32, info: NACodecInfo<'a>) -> Self { + NAStream { media_type: mt, id: id, info: Rc::new(info) } + } + pub fn get_id(&self) -> u32 { self.id } + pub fn get_info(&self) -> Rc> { self.info.clone() } +} + +impl<'a> fmt::Display for NAStream<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + write!(f, "({}#{})", self.media_type, self.id) + } +} + +#[allow(dead_code)] +pub struct NAPacket<'a> { + stream: Rc>, + pts: Option, + dts: Option, + duration: Option, + buffer: Rc>, + keyframe: bool, +// options: HashMap>, +} + +impl<'a> NAPacket<'a> { + pub fn new(str: Rc>, pts: Option, dts: Option, dur: Option, kf: bool, vec: Vec) -> Self { +// let mut vec: Vec = Vec::new(); +// vec.resize(size, 0); + NAPacket { stream: str, pts: pts, dts: dts, duration: dur, keyframe: kf, buffer: Rc::new(vec) } + } + pub fn get_stream(&self) -> Rc> { self.stream.clone() } + pub fn get_pts(&self) -> Option { self.pts } + pub fn get_buffer(&self) -> Rc> { self.buffer.clone() } +} + +impl<'a> fmt::Display for NAPacket<'a> { + fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { + let mut foo = format!("[pkt for {} size {}", self.stream, self.buffer.len()); + if let Some(pts) = self.pts { foo = format!("{} pts {}", foo, pts); } + if let Some(dts) = self.dts { foo = format!("{} dts {}", foo, dts); } + if let Some(dur) = self.duration { foo = format!("{} duration {}", foo, dur); } + if self.keyframe { foo = format!("{} kf", foo); } + foo = foo + "]"; + write!(f, "{}", foo) + } +} + +#[derive(Debug)] +#[allow(dead_code)] +pub enum DemuxerError { + EOF, + NoSuchInput, + InvalidData, + IOError, + NotImplemented, + MemoryError, +} + +type DemuxerResult = Result; + +pub trait NADemuxer<'a> { + fn open(&mut self) -> DemuxerResult<()>; + fn get_frame(&mut self) -> DemuxerResult; + fn seek(&mut self, time: u64) -> DemuxerResult<()>; +} + +pub trait NAPacketReader<'a> { + fn read_packet(&mut self, str: Rc>, pts: Option, dts: Option, dur: Option, keyframe: bool, size: usize) -> DemuxerResult; + fn fill_packet(&mut self, pkt: &mut NAPacket) -> DemuxerResult<()>; +} + +impl<'a> NAPacketReader<'a> for ByteReader<'a> { + fn read_packet(&mut self, str: Rc>, pts: Option, dts: Option, dur: Option, kf: bool, size: usize) -> DemuxerResult { + let mut buf: Vec = Vec::with_capacity(size); + if buf.capacity() < size { return Err(DemuxerError::MemoryError); } + buf.resize(size, 0); + let res = self.read_buf(buf.as_mut_slice()); + if let Err(_) = res { return Err(DemuxerError::IOError); } + if res.unwrap() < buf.len() { return Err(DemuxerError::IOError); } + let pkt = NAPacket::new(str, pts, dts, dur, kf, buf); + Ok(pkt) + } + fn fill_packet(&mut self, pkt: &mut NAPacket) -> DemuxerResult<()> { + let mut refbuf = pkt.get_buffer(); + let mut buf = Rc::make_mut(&mut refbuf); + let res = self.read_buf(buf.as_mut_slice()); + if let Err(_) = res { return Err(DemuxerError::IOError); } + if res.unwrap() < buf.len() { return Err(DemuxerError::IOError); } + Ok(()) + } +} + +pub struct NADemuxerBuilder { +} + +impl From for DemuxerError { + fn from(_: ByteIOError) -> Self { DemuxerError::IOError } +} + +impl NADemuxerBuilder { + #[allow(unused_variables)] + pub fn create_demuxer(name: &str, url: &str) -> DemuxerResult>> { + unimplemented!() + } +} diff --git a/src/frame.rs b/src/frame.rs new file mode 100644 index 0000000..066a016 --- /dev/null +++ b/src/frame.rs @@ -0,0 +1,156 @@ +use std::collections::HashMap; + +#[allow(dead_code)] +pub struct NASoniton { + bits: u8, + is_be: bool, + packed: bool, + planar: bool, + float: bool, +} + +#[allow(dead_code)] +pub const SND_U8_FORMAT: NASoniton = NASoniton { bits: 8, is_be: false, packed: false, planar: false, float: false }; +#[allow(dead_code)] +pub const SND_S16_FORMAT: NASoniton = NASoniton { bits: 16, is_be: false, packed: false, planar: false, float: false }; + +#[allow(dead_code)] +pub struct NAAudioInfo { + sample_rate: u32, + channels: u8, + format: NASoniton, + block_len: usize, +} + +impl NAAudioInfo { + pub fn new(sr: u32, ch: u8, fmt: NASoniton, bl: usize) -> Self { + NAAudioInfo { sample_rate: sr, channels: ch, format: fmt, block_len: bl } + } +} + +#[derive(Debug)] +pub enum ColorModel { + RGB, + YUV, + CMYK, + HSV, + LAB, +} + +#[allow(dead_code)] +pub struct NAPixelChromaton { + h_ss: u8, + v_ss: u8, + is_packed: bool, + depth: u8, + shift: u8, + comp_offs: u8, + next_elem: u8, +} + +#[allow(dead_code)] +pub struct NAPixelFormaton { + model: ColorModel, + components: u8, + comp_info: [Option; 5], + elem_size: u8, + has_alpha: bool, + is_palette: bool, +} + +macro_rules! chromaton { + ($hs: expr, $vs: expr, $pck: expr, $d: expr, $sh: expr, $co: expr, $ne: expr) => ({ + Some(NAPixelChromaton{ h_ss: $hs, v_ss: $vs, is_packed: $pck, depth: $d, shift: $sh, comp_offs: $co, next_elem: $ne }) + }); + (yuv8; $hs: expr, $vs: expr, $co: expr) => ({ + Some(NAPixelChromaton{ h_ss: $hs, v_ss: $vs, is_packed: false, depth: 8, shift: 0, comp_offs: $co, next_elem: 1 }) + }); + (pal8; $co: expr) => ({ + Some(NAPixelChromaton{ h_ss: 0, v_ss: 0, is_packed: true, depth: 8, shift: 0, comp_offs: $co, next_elem: 3 }) + }); +} + +#[allow(dead_code)] +pub const YUV420_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::YUV, components: 3, + comp_info: [ + chromaton!(0, 0, false, 8, 0, 0, 1), + chromaton!(yuv8; 1, 1, 1), + chromaton!(yuv8; 1, 1, 2), + None, None], + elem_size: 0, has_alpha: false, is_palette: false }; + +#[allow(dead_code)] +pub const PAL8_FORMAT: NAPixelFormaton = NAPixelFormaton { model: ColorModel::RGB, components: 3, + comp_info: [ + chromaton!(pal8; 0), + chromaton!(pal8; 1), + chromaton!(pal8; 2), + None, None], + elem_size: 1, has_alpha: false, is_palette: true }; + + +#[allow(dead_code)] +pub struct NAVideoInfo { + width: u32, + height: u32, + flipped: bool, + format: NAPixelFormaton, +} + +impl NAVideoInfo { + pub fn new(w: u32, h: u32, flip: bool, fmt: NAPixelFormaton) -> Self { + NAVideoInfo { width: w, height: h, flipped: flip, format: fmt } + } +} + +pub enum NACodecTypeInfo { + None, + Audio(NAAudioInfo), + Video(NAVideoInfo), +} + +#[allow(dead_code)] +pub struct NABuffer<'a> { + id: u64, + data: &'a mut [u8], +} + +#[allow(dead_code)] +pub struct NACodecInfo<'a> { + properties: NACodecTypeInfo, + extradata: Option<&'a[u8]>, +} + +impl<'a> NACodecInfo<'a> { + pub fn new(p: NACodecTypeInfo, edata: Option<&'a[u8]>) -> Self { + NACodecInfo { properties: p, extradata: edata } + } +} + +pub trait NABufferAllocator { + fn alloc_buf(info: &NACodecInfo) -> NABuffer<'static>; +} + +#[derive(Debug)] +pub enum NAValue<'a> { + None, + Int(i32), + Long(i64), + String(String), + Data(&'a [u8]), +} + +#[allow(dead_code)] +pub struct NAFrame<'a> { + pts: Option, + dts: Option, + duration: Option, + buffer: &'a mut NABuffer<'a>, + info: &'a NACodecInfo<'a>, + options: HashMap>, +} + +#[allow(dead_code)] +pub struct NACodecContext<'a> { + info: &'a NACodecInfo<'a>, +} diff --git a/src/lib.rs b/src/lib.rs index f039bdf..28f47d1 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -1,2 +1,4 @@ +pub mod demuxers; +pub mod frame; pub mod io;