Skip to main content

adts_core/
demux.rs

1//! ADTS frame reader — incremental, byte-chunk push/poll.
2
3#![forbid(unsafe_code)]
4
5use bytes::Bytes;
6
7use crate::error::Error;
8use crate::types::{AacProfile, AdtsConfig, sample_rate_from_index};
9
10const HEADER_LEN_NO_CRC: usize = 7;
11const HEADER_LEN_WITH_CRC: usize = 9;
12
13/// Reads back-to-back ADTS frames from pushed byte chunks.
14#[derive(Debug, Clone, Default)]
15pub struct Demuxer {
16    buf: Vec<u8>,
17    config: Option<AdtsConfig>,
18}
19
20impl Demuxer {
21    /// New, empty demux session.
22    #[must_use]
23    pub fn new() -> Self {
24        Self::default()
25    }
26
27    /// Append incoming bytes.
28    pub fn push_bytes(&mut self, data: &[u8]) {
29        self.buf.extend_from_slice(data);
30    }
31
32    /// `AdtsConfig` parsed from the most recently returned frame's header, if any.
33    #[must_use]
34    pub const fn config(&self) -> Option<AdtsConfig> {
35        self.config
36    }
37
38    /// Pop the next complete frame's raw AAC payload (ADTS header stripped), or
39    /// `Ok(None)` if the buffer doesn't yet hold a full frame — call again after
40    /// more `push_bytes`. Errors on a bad sync word or a reserved
41    /// `sampling_frequency_index`, rather than silently emitting garbage.
42    pub fn poll_frame(&mut self) -> Result<Option<Bytes>, Error> {
43        if self.buf.len() < HEADER_LEN_NO_CRC {
44            return Ok(None);
45        }
46        if self.buf[0] != 0xFF || (self.buf[1] & 0xF0) != 0xF0 {
47            return Err(Error::BadSync);
48        }
49        let protection_absent = (self.buf[1] & 0x01) != 0;
50        let header_len = if protection_absent {
51            HEADER_LEN_NO_CRC
52        } else {
53            HEADER_LEN_WITH_CRC
54        };
55        if self.buf.len() < header_len {
56            return Ok(None);
57        }
58
59        let profile_bits = (self.buf[2] >> 6) & 0x03;
60        let sfi = (self.buf[2] >> 2) & 0x0F;
61        let channels = ((self.buf[2] & 0x01) << 2) | ((self.buf[3] >> 6) & 0x03);
62        let frame_len = ((usize::from(self.buf[3]) & 0x03) << 11)
63            | (usize::from(self.buf[4]) << 3)
64            | (usize::from(self.buf[5]) >> 5);
65
66        if self.buf.len() < frame_len {
67            return Ok(None);
68        }
69        if frame_len < header_len {
70            return Err(Error::BadSync);
71        }
72
73        let sample_rate =
74            sample_rate_from_index(sfi).ok_or(Error::UnsupportedSamplingFrequencyIndex(sfi))?;
75        self.config = Some(AdtsConfig {
76            profile: AacProfile::from_bits(profile_bits),
77            sample_rate,
78            channels,
79        });
80
81        let payload = Bytes::copy_from_slice(&self.buf[header_len..frame_len]);
82        self.buf.drain(0..frame_len);
83        Ok(Some(payload))
84    }
85}
86
87#[cfg(test)]
88#[path = "demux_tests.rs"]
89mod tests;