From: Kostya Shishkov Date: Tue, 16 Nov 2021 17:20:40 +0000 (+0100) Subject: add FLAC packetiser and raw stream demuxer X-Git-Url: https://git.nihav.org/?a=commitdiff_plain;h=2cd9d8a600ef475711646fea5a3617e030440068;p=nihav.git add FLAC packetiser and raw stream demuxer --- diff --git a/nihav-allstuff/src/lib.rs b/nihav-allstuff/src/lib.rs index f48deee..233238a 100644 --- a/nihav-allstuff/src/lib.rs +++ b/nihav-allstuff/src/lib.rs @@ -16,6 +16,7 @@ use nihav_core::codecs::RegisteredDecoders; use nihav_core::codecs::RegisteredPacketisers; use nihav_core::codecs::RegisteredEncoders; use nihav_core::demuxers::RegisteredDemuxers; +use nihav_core::demuxers::RegisteredRawDemuxers; use nihav_core::muxers::RegisteredMuxers; use nihav_commonfmt::*; @@ -51,6 +52,7 @@ pub fn nihav_register_all_decoders(rd: &mut RegisteredDecoders) { /// Registers all known packetisers. pub fn nihav_register_all_packetisers(rp: &mut RegisteredPacketisers) { + llaudio_register_all_packetisers(rp); mpeg_register_all_packetisers(rp); } @@ -66,6 +68,11 @@ pub fn nihav_register_all_demuxers(rd: &mut RegisteredDemuxers) { vivo_register_all_demuxers(rd); } +/// Registers all known raw stream demuxers. +pub fn nihav_register_all_raw_demuxers(rd: &mut RegisteredRawDemuxers) { + llaudio_register_all_raw_demuxers(rd); +} + /// Registers all known encoders. pub fn nihav_register_all_encoders(re: &mut RegisteredEncoders) { flash_register_all_encoders(re); diff --git a/nihav-llaudio/src/codecs/flac.rs b/nihav-llaudio/src/codecs/flac.rs index 0ca80aa..a3145ac 100644 --- a/nihav-llaudio/src/codecs/flac.rs +++ b/nihav-llaudio/src/codecs/flac.rs @@ -429,10 +429,209 @@ pub fn get_decoder() -> Box { Box::new(FlacDecoder::new()) } +#[derive(Clone,Copy,Default)] +struct FrameHeader { + blocksize: u32, + srate: u32, + channels: u8, + bits: u8, + time: u64, + blk_strat: bool, +} + +#[derive(Default)] +struct FLACPacketiser { + hdr: FrameHeader, + buf: Vec, + ref_crc: u16, + cur_crc: u16, + end: usize, + hdr_ok: bool, +} + +fn read_utf8(br: &mut BitReader) -> DecoderResult { + let byte = br.read(8)? as u8; + let len = (!byte).leading_zeros(); + if (len == 1) || (len > 5) { + return Err(DecoderError::InvalidData); + } + if len > 1 { + let mut val = u64::from(byte << len >> len); + for _ in 1..len { + let byte = br.read(8)?; + if (byte & 0xC0) != 0x80 { + return Err(DecoderError::InvalidData); + } + val = (val << 6) | u64::from(byte & 0x3F); + } + Ok(val) + } else { + Ok(u64::from(byte)) + } +} + +impl FLACPacketiser { + fn new() -> Self { Self::default() } + fn parse_header(&self) -> DecoderResult { + if self.buf.len() < 5 { + return Err(DecoderError::ShortData); + } + let mut br = BitReader::new(&self.buf, BitReaderMode::BE); + let sync_code = br.read(14)?; + if sync_code != 0x3FFE { + return Err(DecoderError::InvalidData); + } + let marker = br.read(1)?; + if marker != 0 { + return Err(DecoderError::InvalidData); + } + let blk_strat = br.read_bool()?; + let bsize = br.read(4)?; + let srate_idx = br.read(4)? as u8; + let chan_idx = br.read(4)? as u8; + let bits = match br.read(3)? { + 0 => 0, + 1 => 8, + 2 => 12, + 4 => 16, + 5 => 20, + 6 => 24, + _ => return Err(DecoderError::InvalidData), + }; + let marker = br.read(1)?; + if marker != 0 { + return Err(DecoderError::InvalidData); + } + + let time = read_utf8(&mut br)?; + + let blocksize = match bsize { + 1 => 192, + 2..=5 => 576 << (bsize - 2), + 6 => br.read(8)? + 1, + 7 => br.read(16)? + 1, + 8..=15 => 256 << (bsize - 8), + _ => return Err(DecoderError::InvalidData), + }; + let srate = match srate_idx { + 0 => 0, + 1 => 88200, + 2 => 176400, + 3 => 192000, + 4 => 8000, + 5 => 16000, + 6 => 22050, + 7 => 24000, + 8 => 32000, + 9 => 44100, + 10 => 48000, + 11 => 96000, + 12 => br.read(8)? * 1000, + 13 => br.read(16)?, + 14 => br.read(16)? * 10, + _ => return Err(DecoderError::InvalidData), + }; + let channels = match chan_idx { + 0..=7 => chan_idx + 1, + 8 | 9 | 10 => 2, + _ => return Err(DecoderError::InvalidData), + }; + + let hdr_size = br.tell() / 8; + let ref_crc = br.read(8)? as u8; + let mut crc = 0; + for &b in self.buf[..hdr_size].iter() { + crc = update_crc8(crc, b); + } + if crc != ref_crc { + return Err(DecoderError::ChecksumError); + } + + Ok(FrameHeader{ blk_strat, time, srate, channels, bits, blocksize }) + } +} + +impl NAPacketiser for FLACPacketiser { + fn add_data(&mut self, src: &[u8]) -> bool { + self.buf.extend_from_slice(src); + self.buf.len() < 4096 + } + fn parse_stream(&mut self, id: u32) -> DecoderResult { + let hdr = self.parse_header()?; + let ainfo = NAAudioInfo::new(hdr.srate, hdr.channels, if hdr.bits <= 16 { SND_S16P_FORMAT } else { SND_S32P_FORMAT }, hdr.blocksize as usize); + let info = NACodecInfo::new("flac", NACodecTypeInfo::Audio(ainfo), None); + Ok(NAStream::new(StreamType::Audio, id, info, 1, hdr.srate, 0).into_ref()) + } + fn skip_junk(&mut self) -> DecoderResult { + Err(DecoderError::NotImplemented) + } + fn get_packet(&mut self, stream: NAStreamRef) -> DecoderResult> { + if self.end == self.buf.len() || self.buf.len() < 5 { + return Err(DecoderError::ShortData); + } + if !self.hdr_ok { + self.hdr = self.parse_header()?; + self.hdr_ok = true; + self.cur_crc = 0; + for i in 0..5 { + self.cur_crc = update_crc16(self.cur_crc, self.buf[i]); + } + self.end = 5; + } + while self.end < self.buf.len() { + let b = self.buf[self.end]; + self.end += 1; + match self.end { + 0..=5 => unreachable!(), + 6 => self.ref_crc = u16::from(b), + 7 => self.ref_crc = (self.ref_crc << 8) | u16::from(b), + _ => { + let bbb = (self.ref_crc >> 8) as u8; + self.ref_crc = (self.ref_crc << 8) | u16::from(b); + self.cur_crc = update_crc16(self.cur_crc, bbb); + let mut found = self.ref_crc == self.cur_crc; + if self.end + 2 < self.buf.len() { + let b1 = self.buf[self.end]; + let b2 = self.buf[self.end + 1]; + if b1 != 0xFF || (b2 & 0xFC) != 0xF8 { + found = false; + } + } + if found { + let mut data = Vec::with_capacity(self.end); + data.extend_from_slice(&self.buf[..self.end]); + self.buf.drain(..self.end); + let mut ts = NATimeInfo::new(None, None, Some(u64::from(self.hdr.blocksize)), 1, self.hdr.srate); + ts.pts = if self.hdr.blk_strat { + Some(self.hdr.time) + } else { + Some(self.hdr.time * u64::from(self.hdr.blocksize)) + }; + self.end = 0; + self.hdr_ok = false; + + return Ok(Some(NAPacket::new(stream, ts, true, data))); + } + }, + } + } + Ok(None) + } + fn reset(&mut self) { + self.buf.clear(); + self.end = 0; + self.hdr_ok = false; + } +} + +pub fn get_packetiser() -> Box { + Box::new(FLACPacketiser::new()) +} + #[cfg(test)] mod test { - use nihav_core::codecs::RegisteredDecoders; - use nihav_core::demuxers::RegisteredDemuxers; + use nihav_core::codecs::*; + use nihav_core::demuxers::*; use nihav_codec_support::test::dec_video::*; use crate::llaudio_register_all_decoders; use crate::llaudio_register_all_demuxers; @@ -446,6 +645,90 @@ mod test { test_decoding("flac", "flac", "assets/LLaudio/luckynight.flac", Some(6), &dmx_reg, &dec_reg, ExpectedTestResult::MD5([0xe689787a, 0x032a98f7, 0xeb6e64f4, 0xfa652132])); } + use std::io::{Read, Seek, SeekFrom}; + #[test] + fn test_flac_packetiser() { + let mut dmx_reg = RegisteredDemuxers::new(); + llaudio_register_all_demuxers(&mut dmx_reg); + let dmx_f = dmx_reg.find_demuxer("flac").unwrap(); + let mut file = std::fs::File::open("assets/LLaudio/luckynight.flac").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = create_demuxer(dmx_f, &mut br).unwrap(); + + let mut pkt_sizes = Vec::new(); + while let Ok(pkt) = dmx.get_frame() { + pkt_sizes.push(pkt.get_buffer().len()); + } + + let mut file = std::fs::File::open("assets/LLaudio/luckynight.flac").unwrap(); + file.seek(SeekFrom::Start(0x115E)).unwrap(); + + let mut pkts = super::FLACPacketiser::new(); + let mut buf = [0; 8192]; + for _ in 0..16 { + file.read_exact(&mut buf).unwrap(); + pkts.add_data(&buf); + } + let stream = pkts.parse_stream(0).unwrap(); + let mut pkt_sizes2 = Vec::new(); + let mut piter = pkt_sizes.iter(); + loop { + let res = pkts.get_packet(stream.clone()); + match res { + Ok(Some(pkt)) => { + assert_eq!(*piter.next().unwrap(), pkt.get_buffer().len()); + pkt_sizes2.push(pkt.get_buffer().len()); + continue; + }, + Ok(None) | Err(DecoderError::ShortData) => {}, + Err(err) => { + println!("error {:?}", err); + panic!("packetising error"); + }, + }; + let ret = file.read(&mut buf); + match ret { + Ok(0) => { + let res = pkts.get_packet(stream.clone()); + match res { + Ok(Some(pkt)) => { + assert_eq!(*piter.next().unwrap(), pkt.get_buffer().len()); + pkt_sizes2.push(pkt.get_buffer().len()); + continue; + }, + Ok(None) | Err(DecoderError::ShortData) => break, + Err(err) => { + println!("error {:?}", err); + panic!("packetising error"); + }, + }; + }, + Ok(size) => pkts.add_data(&buf[..size]), + Err(err) => { + if err.kind() == std::io::ErrorKind::UnexpectedEof { + let res = pkts.get_packet(stream.clone()); + match res { + Ok(Some(pkt)) => { + assert_eq!(*piter.next().unwrap(), pkt.get_buffer().len()); + pkt_sizes2.push(pkt.get_buffer().len()); + continue; + }, + Ok(None) | Err(DecoderError::ShortData) => break, + Err(err) => { + println!("error {:?}", err); + panic!("packetising error"); + }, + }; + } else { + println!(" {:?}", err.kind()); + panic!("i/o error!"); + } + }, + }; + } + assert_eq!(pkt_sizes.len(), pkt_sizes2.len()); + } } fn update_crc8(crc: u8, byte: u8) -> u8 { diff --git a/nihav-llaudio/src/codecs/mod.rs b/nihav-llaudio/src/codecs/mod.rs index ed253c8..a0e92bb 100644 --- a/nihav-llaudio/src/codecs/mod.rs +++ b/nihav-llaudio/src/codecs/mod.rs @@ -38,6 +38,18 @@ pub fn llaudio_register_all_decoders(rd: &mut RegisteredDecoders) { } } +const LL_PACKETISERS: &[PacketiserInfo] = &[ +#[cfg(feature="decoder_flac")] + PacketiserInfo { name: "flac", get_packetiser: flac::get_packetiser }, +]; + +/// Registers all available packetisers provided by this crate. +pub fn llaudio_register_all_packetisers(rp: &mut RegisteredPacketisers) { + for pkt in LL_PACKETISERS.iter() { + rp.add_packetiser(*pkt); + } +} + #[cfg(feature="encoder_flac")] pub mod flacenc; diff --git a/nihav-llaudio/src/demuxers/flacraw.rs b/nihav-llaudio/src/demuxers/flacraw.rs new file mode 100644 index 0000000..d86d14e --- /dev/null +++ b/nihav-llaudio/src/demuxers/flacraw.rs @@ -0,0 +1,183 @@ +use nihav_core::frame::*; +use nihav_core::demuxers::*; + +struct FLACDemuxer<'a> { + src: &'a mut ByteReader<'a>, + data_start: u64, + tot_samples: u64, + cur_samples: u64, + blk_samples: u16, + min_samples: u16, + min_size: usize, + max_size: usize, + srate: u32, + build_index: bool, +} + +impl<'a> FLACDemuxer<'a> { + fn new(io: &'a mut ByteReader<'a>) -> Self { + Self { + src: io, + data_start: 0, + tot_samples: 0, + cur_samples: 0, + blk_samples: 0, + min_samples: 0, + min_size: 0, + max_size: 0, + srate: 0, + build_index: false, + } + } +} + +impl<'a> RawDemuxCore<'a> for FLACDemuxer<'a> { + fn open(&mut self, strmgr: &mut StreamManager, seek_index: &mut SeekIndex) -> DemuxerResult<()> { + let tag = self.src.read_tag()?; + validate!(&tag == b"fLaC"); + let mut streaminfo: Vec = Vec::new(); + let mut srate = 0u32; + let mut channels = 0u8; + loop { + let id1 = self.src.read_byte()?; + let len = self.src.read_u24be()? as usize; + let id = id1 & 0x7F; + + match id { + 0x00 => { + validate!(len >= 34); + streaminfo = vec![0u8; len]; + self.src.read_buf(&mut streaminfo)?; + let min_bs = read_u16be(&streaminfo[0..])?; + let max_bs = read_u16be(&streaminfo[2..])?; + if min_bs == max_bs { + self.blk_samples = max_bs; + } + self.min_samples = min_bs; + self.min_size = read_u24be(&streaminfo[4..])? as usize; + self.max_size = read_u24be(&streaminfo[7..])? as usize; + let word = read_u24be(&streaminfo[10..])?; + srate = word >> 4; + channels = (((word >> 1) & 7) + 1) as u8; + self.tot_samples = (u64::from(streaminfo[13] & 0xF) << 32) | u64::from(read_u32be(&streaminfo[14..])?); + }, + 0x03 => { + validate!((len % 18) == 0); + seek_index.mode = SeekIndexMode::Present; + for _ in 0..len / 18 { + let sample = self.src.read_u64be()?; + let offset = self.src.read_u64be()?; + let _nsamps = self.src.read_u16be()?; + let time = sample * 1000 / u64::from(srate.max(1000)); + seek_index.add_entry(0, SeekEntry { time, pts: sample, pos: offset }); + } + }, + _ => self.src.read_skip(len)?, + }; + + if (id1 & 0x80) != 0 { + break; + } + } + if seek_index.mode != SeekIndexMode::Present { + seek_index.mode = SeekIndexMode::Automatic; + self.build_index = true; + } else { + self.build_index = false; + } + self.data_start = self.src.tell(); + validate!(srate != 0); + self.srate = srate; + + let base = if self.blk_samples != 0 { u32::from(self.blk_samples) } else { 1 }; + let ahdr = NAAudioInfo::new(srate, channels as u8, SND_S16P_FORMAT, base as usize); + let ainfo = NACodecInfo::new("flac", NACodecTypeInfo::Audio(ahdr), Some(streaminfo)); + strmgr.add_stream(NAStream::new(StreamType::Audio, 0, ainfo, base, srate, 0)).unwrap(); + + Ok(()) + } + fn get_data(&mut self, strmgr: &mut StreamManager) -> DemuxerResult { + let stream = strmgr.get_stream(0).unwrap(); + let mut buf = vec![0; 8192]; + let size = self.src.read_buf_some(&mut buf)?; + buf.truncate(size); + Ok(NARawData::new(stream, buf)) + } + fn seek(&mut self, time: NATimePoint, seek_index: &SeekIndex) -> DemuxerResult<()> { + if seek_index.mode == SeekIndexMode::Present { + let ret = seek_index.find_pos(time); + if ret.is_none() { + return Err(DemuxerError::SeekError); + } + let seek_info = ret.unwrap(); + self.cur_samples = seek_info.pts; + self.src.seek(SeekFrom::Start(self.data_start + seek_info.pos))?; + Ok(()) + } else { + Err(DemuxerError::NotPossible) + } + } + fn get_duration(&self) -> u64 { self.tot_samples * 1000 / u64::from(self.srate) } +} + +impl<'a> NAOptionHandler for FLACDemuxer<'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 FLACDemuxerCreator { } + +impl RawDemuxerCreator for FLACDemuxerCreator { + fn new_demuxer<'a>(&self, br: &'a mut ByteReader<'a>) -> Box + 'a> { + Box::new(FLACDemuxer::new(br)) + } + fn get_name(&self) -> &'static str { "flac" } + fn check_format(&self, br: &mut ByteReader) -> bool { + if br.seek(SeekFrom::Start(0)).is_err() { + return false; + } + if let Ok([b'f', b'L', b'a', b'C']) = br.read_tag() { + true + } else { + false + } + } +} + +#[cfg(test)] +mod test { + use nihav_core::codecs::*; + use super::*; + use crate::llaudio_register_all_packetisers; + use std::fs::File; + + #[test] + fn test_flac_raw_demux() { + let mut file = File::open("assets/LLaudio/luckynight.flac").unwrap(); + let mut fr = FileReader::new_read(&mut file); + let mut br = ByteReader::new(&mut fr); + let mut dmx = FLACDemuxer::new(&mut br); + let mut sm = StreamManager::new(); + let mut si = SeekIndex::new(); + dmx.open(&mut sm, &mut si).unwrap(); + let stream = sm.get_stream(0).unwrap(); + let mut pkt_reg = RegisteredPacketisers::new(); + llaudio_register_all_packetisers(&mut pkt_reg); + let creator = pkt_reg.find_packetiser("flac").unwrap(); + let mut pkts = (creator)(); + let mut tot_size = 0; + while let Ok(pkt) = dmx.get_data(&mut sm) { + tot_size += pkt.get_buffer().len(); + pkts.add_data(&pkt.get_buffer()); + } + let mut tot_size2 = 0; + let mut npkts = 0; + while let Ok(Some(pkt)) = pkts.get_packet(stream.clone()) { + tot_size2 += pkt.get_buffer().len(); + npkts += 1; + } + assert_eq!(npkts, 579); + assert_eq!(tot_size, tot_size2); + } +} diff --git a/nihav-llaudio/src/demuxers/mod.rs b/nihav-llaudio/src/demuxers/mod.rs index d1d38a2..41dd9fd 100644 --- a/nihav-llaudio/src/demuxers/mod.rs +++ b/nihav-llaudio/src/demuxers/mod.rs @@ -9,6 +9,8 @@ macro_rules! validate { mod ape; #[cfg(feature="demuxer_flac")] mod flac; +#[cfg(feature="demuxer_flac")] +mod flacraw; #[cfg(feature="demuxer_tta")] mod tta; #[cfg(feature="demuxer_wavpack")] @@ -31,3 +33,15 @@ pub fn llaudio_register_all_demuxers(rd: &mut RegisteredDemuxers) { rd.add_demuxer(*demuxer); } } + +const LL_RAW_AUDIO_DEMUXERS: &[&dyn RawDemuxerCreator] = &[ +#[cfg(feature="demuxer_flac")] + &flacraw::FLACDemuxerCreator {}, +]; + +/// Registers all available raw stream demuxers provided by this crate. +pub fn llaudio_register_all_raw_demuxers(rd: &mut RegisteredRawDemuxers) { + for demuxer in LL_RAW_AUDIO_DEMUXERS.iter() { + rd.add_demuxer(*demuxer); + } +} diff --git a/nihav-llaudio/src/lib.rs b/nihav-llaudio/src/lib.rs index 710a22e..d7a2633 100644 --- a/nihav-llaudio/src/lib.rs +++ b/nihav-llaudio/src/lib.rs @@ -11,5 +11,7 @@ mod demuxers; mod muxers; pub use crate::codecs::llaudio_register_all_decoders; pub use crate::demuxers::llaudio_register_all_demuxers; +pub use crate::codecs::llaudio_register_all_packetisers; +pub use crate::demuxers::llaudio_register_all_raw_demuxers; pub use crate::codecs::llaudio_register_all_encoders; pub use crate::muxers::llaudio_register_all_muxers;