]> git.nihav.org Git - nihav.git/commitdiff
nihav_mpeg: add ADTS packetiser
authorKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 22 May 2025 16:23:57 +0000 (18:23 +0200)
committerKostya Shishkov <kostya.shishkov@gmail.com>
Thu, 22 May 2025 16:23:57 +0000 (18:23 +0200)
nihav-mpeg/src/codecs/aac/info.rs
nihav-mpeg/src/codecs/aac/mod.rs
nihav-mpeg/src/codecs/aac/packetiser.rs [new file with mode: 0644]
nihav-mpeg/src/codecs/mod.rs

index a9607d618a16bdd40f6f1ffebd7557e3723a4956..91dae7294e48e323135377016d49853a72126e8a 100644 (file)
@@ -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,
index fb15d955142be0e73f0e19a410578e44c5bbab52..192b4e333b77a901d4fdafad757a0a5e179cea9d 100644 (file)
@@ -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 (file)
index 0000000..daecb2d
--- /dev/null
@@ -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<u8>,
+    hdr:        Option<ADTSHeader>,
+}
+
+impl PacketiserADTS {
+    fn new() -> Self { Self::default() }
+    fn skip_id3(buf: &[u8]) -> DecoderResult<usize> {
+        // 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<ADTSHeader> {
+        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<NAStreamRef> {
+        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<usize> {
+        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<Option<NAPacket>> {
+        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<dyn NAPacketiser + Send> {
+    Box::new(PacketiserADTS::new())
+}
index 62184b1ed515d415f2c996c47d48ee364dccfe5a..0b36cf68f86fe4fd96ca12d03d3af1daf23400ac 100644 (file)
@@ -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.