From 57268fe176085c6d8f27b89d96fb794839b55b0c Mon Sep 17 00:00:00 2001 From: Kostya Shishkov Date: Thu, 22 May 2025 18:23:57 +0200 Subject: [PATCH] nihav_mpeg: add ADTS packetiser --- nihav-mpeg/src/codecs/aac/info.rs | 4 +- nihav-mpeg/src/codecs/aac/mod.rs | 23 +++ nihav-mpeg/src/codecs/aac/packetiser.rs | 194 ++++++++++++++++++++++++ nihav-mpeg/src/codecs/mod.rs | 2 + 4 files changed, 221 insertions(+), 2 deletions(-) create mode 100644 nihav-mpeg/src/codecs/aac/packetiser.rs diff --git a/nihav-mpeg/src/codecs/aac/info.rs b/nihav-mpeg/src/codecs/aac/info.rs index a9607d6..91dae72 100644 --- a/nihav-mpeg/src/codecs/aac/info.rs +++ b/nihav-mpeg/src/codecs/aac/info.rs @@ -76,12 +76,12 @@ impl fmt::Display for M4AType { } } -const AAC_SAMPLE_RATES: [u32; 16] = [ +pub const AAC_SAMPLE_RATES: [u32; 16] = [ 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350, 0, 0, 0 ]; -const AAC_CHANNELS: [usize; 8] = [ 0, 1, 2, 3, 4, 5, 6, 8 ]; +pub const AAC_CHANNELS: [usize; 8] = [ 0, 1, 2, 3, 4, 5, 6, 8 ]; pub struct M4AInfo { pub otype: M4AType, diff --git a/nihav-mpeg/src/codecs/aac/mod.rs b/nihav-mpeg/src/codecs/aac/mod.rs index fb15d95..192b4e3 100644 --- a/nihav-mpeg/src/codecs/aac/mod.rs +++ b/nihav-mpeg/src/codecs/aac/mod.rs @@ -20,6 +20,9 @@ use tables::*; mod tools; use tools::*; +mod packetiser; +pub use packetiser::get_packetiser_adts; + const MAX_WINDOWS: usize = 8; const MAX_SFBS: usize = 64; @@ -807,6 +810,8 @@ mod test { use nihav_codec_support::test::dec_video::test_decode_audio; use crate::mpeg_register_all_decoders; use nihav_realmedia::realmedia_register_all_demuxers; + use std::io::Read; + #[test] fn test_aac() { let mut dmx_reg = RegisteredDemuxers::new(); @@ -828,6 +833,24 @@ mod test { let file = "assets/MPEG/SBRtestStereoAot29Sig0.mp4"; test_decode_audio("mov", file, Some(400), None/*Some("aacsbr")*/, &dmx_reg, &dec_reg); } + #[test] + fn test_adts_packetiser() { + let mut buf = [0; 4096]; + // sample obtained with yt-dlp -f 234 + let mut file = std::fs::File::open("assets/MPEG/Vf9Lvifxwk4.adts").unwrap(); + + let mut pkts = super::get_packetiser_adts(); + file.read_exact(&mut buf).unwrap(); + pkts.add_data(&buf); + let stream = pkts.parse_stream(0).unwrap(); + let mut frame_sizes = Vec::with_capacity(15); + while let Some(pkt) = pkts.get_packet(stream.clone()).unwrap() { + let frame_size = pkt.get_buffer().len(); + println!("pkt size {}", frame_size); + frame_sizes.push(frame_size); + } + assert_eq!(&frame_sizes, &[371, 372, 394, 402, 474, 400, 407, 399, 385]); + } } const DEFAULT_CHANNEL_MAP: [&str; 9] = [ diff --git a/nihav-mpeg/src/codecs/aac/packetiser.rs b/nihav-mpeg/src/codecs/aac/packetiser.rs new file mode 100644 index 0000000..daecb2d --- /dev/null +++ b/nihav-mpeg/src/codecs/aac/packetiser.rs @@ -0,0 +1,194 @@ +use nihav_core::codecs::*; +use nihav_core::io::bitreader::*; +use super::info::*; + +#[derive(Default,Clone,Copy)] +struct ADTSHeader { + srate: u32, + channels: u8, + hdr_size: usize, + frame_size: usize, + samples: usize, + hdr: u32, +} + +impl PartialEq for ADTSHeader { + fn eq(&self, other: &Self) -> bool { + self.hdr == other.hdr + } +} + +#[derive(Default)] +struct PacketiserADTS { + buf: Vec, + hdr: Option, +} + +impl PacketiserADTS { + fn new() -> Self { Self::default() } + fn skip_id3(buf: &[u8]) -> DecoderResult { + // check for ID3 tags in concatenated stream fragments + if &buf[0..3] == b"ID3" && (1..=4).contains(&buf[3]) && buf[4] == 0 { + if buf.len() < 10 { + return Err(DecoderError::ShortData); + } + let mut size = 0; + for &b in buf[6..10].iter() { + if (b & 0x80) != 0 { + return Err(DecoderError::InvalidData); + } + size = (size << 7) | usize::from(b); + } + Ok(size + 10) + } else { + Ok(0) + } + } + fn parse_header(&self, off: usize) -> DecoderResult { + if self.buf.len() < off + 7 { return Err(DecoderError::ShortData); } + + let mut br = BitReader::new(&self.buf[off..], BitReaderMode::BE); + + let syncword = br.read(12)?; + if syncword != 0xFFF { return Err(DecoderError::InvalidData); } + let _id = br.read(1)?; + let layer = br.read(2)?; + if layer != 0 { return Err(DecoderError::InvalidData); } + let protection_absent = br.read_bool()?; + let profile = br.read(2)?; + validate!(profile != 3); + let sf_idx = br.read(4)?; + validate!(sf_idx <= 12); + let _private_bit = br.read_bool()?; + let ch_config = br.read(3)?; + validate!(ch_config != 0); + let _original = br.read_bool()?; + let _home = br.read_bool()?; + + let _copy_id_bit = br.read_bool()?; + let _copy_id_start = br.read_bool()?; + let frame_length = br.read(13)? as usize; + let _buffer_fullness = br.read(11)?; + let num_raw_blocks = br.read(2)? as usize; + + let hdr = ((profile + 1) << 27) | (sf_idx << 23) | (ch_config << 19); + + if num_raw_blocks == 0 { + if !protection_absent { + br.skip(16)?; // CRC + } + let hdr_size = br.tell() / 8; + validate!(frame_length > hdr_size); + let frame_size = frame_length - hdr_size; + + Ok(ADTSHeader { + srate: AAC_SAMPLE_RATES[sf_idx as usize], + channels: AAC_CHANNELS[ch_config as usize] as u8, + hdr_size, frame_size, hdr, + samples: 1024 + }) + } else { + Err(DecoderError::NotImplemented) + } + } +} + +impl NAPacketiser for PacketiserADTS { + fn attach_stream(&mut self, _stream: NAStreamRef) {} + fn add_data(&mut self, src: &[u8]) -> bool { + self.buf.extend_from_slice(src); + self.buf.len() < 65536 + } + fn parse_stream(&mut self, id: u32) -> DecoderResult { + if self.hdr.is_none() { + if self.buf.len() < 7 { + return Err(DecoderError::ShortData); + } + let id3_len = Self::skip_id3(&self.buf)?; + if id3_len + 7 > self.buf.len() { + return Err(DecoderError::ShortData); + } + let hdr = self.parse_header(id3_len)?; + self.hdr = Some(hdr); + } + let hdr = self.hdr.unwrap(); + let ainfo = NAAudioInfo::new(hdr.srate, hdr.channels, SND_F32P_FORMAT, hdr.samples); + let edata = vec![(hdr.hdr >> 24) as u8, (hdr.hdr >> 16) as u8]; + let info = NACodecInfo::new("aac", NACodecTypeInfo::Audio(ainfo), Some(edata)); + Ok(NAStream::new(StreamType::Audio, id, info, hdr.samples as u32, hdr.srate, 0).into_ref()) + } + fn skip_junk(&mut self) -> DecoderResult { + if self.buf.len() <= 8 { + return Ok(0); + } + let mut off = 0; + let mut hdr = u16::from(self.buf[0]) * 256 + u16::from(self.buf[1]); + let mut iter = self.buf[2..].iter(); + loop { + if (hdr & 0xFFF6) == 0xFFF6 { + let ret = self.parse_header(off); + match ret { + Ok(hdr) => { + if self.hdr.is_none() { + self.hdr = Some(hdr); + } + if self.hdr.unwrap() != hdr { // header is valid but mismatches + self.buf.drain(..off + 1); + return Err(DecoderError::InvalidData); + } + break; + }, + Err(err) => { + self.buf.drain(..off + 1); + return Err(err); + }, + }; + } + off += 1; + if let Some(&b) = iter.next() { + hdr = (hdr << 8) | u16::from(b); + } else { + break; + } + } + self.buf.drain(..off); + Ok(off) + } + fn get_packet(&mut self, stream: NAStreamRef) -> DecoderResult> { + if self.buf.len() < 8 { + return Err(DecoderError::ShortData); + } + + let id3_len = Self::skip_id3(&self.buf)?; + if id3_len + 8 <= self.buf.len() { + self.buf.drain(..id3_len); + } else { + return Err(DecoderError::ShortData); + } + + let hdr = self.parse_header(0)?; + if self.hdr.is_none() { + self.hdr = Some(hdr); + } + if self.hdr.unwrap() != hdr { + return Err(DecoderError::InvalidData); + } + if hdr.frame_size + hdr.hdr_size <= self.buf.len() { + let mut data = Vec::with_capacity(hdr.frame_size); + data.extend_from_slice(&self.buf[hdr.hdr_size..][..hdr.frame_size]); + self.buf.drain(..hdr.hdr_size + hdr.frame_size); + let ts = NATimeInfo::new(None, None, Some(1), hdr.samples as u32, hdr.srate); + Ok(Some(NAPacket::new(stream, ts, true, data))) + } else { + Ok(None) + } + } + fn reset(&mut self) { + self.buf.clear(); + } + fn bytes_left(&self) -> usize { self.buf.len() } +} + +pub fn get_packetiser_adts() -> Box { + Box::new(PacketiserADTS::new()) +} diff --git a/nihav-mpeg/src/codecs/mod.rs b/nihav-mpeg/src/codecs/mod.rs index 62184b1..0b36cf6 100644 --- a/nihav-mpeg/src/codecs/mod.rs +++ b/nihav-mpeg/src/codecs/mod.rs @@ -41,6 +41,8 @@ pub fn mpeg_register_all_decoders(rd: &mut RegisteredDecoders) { const PACKETISERS: &[PacketiserInfo] = &[ #[cfg(feature="decoder_mpa")] PacketiserInfo { name: "mpa", get_packetiser: mpegaudio::get_packetiser }, +#[cfg(feature="decoder_aac")] + PacketiserInfo { name: "adts", get_packetiser: aac::get_packetiser_adts }, ]; /// Registers all available packetisers provided by this crate. -- 2.39.5