Skip to main content

mpeg_audio/
demux.rs

1//! MPEG audio (Layer III) frame reader — incremental, byte-chunk push/poll.
2
3#![forbid(unsafe_code)]
4
5use bytes::Bytes;
6
7use crate::error::Error;
8use crate::types::{ChannelMode, FrameHeader, MpegVersion, bitrate_table, sample_rate_table};
9
10const HEADER_LEN: usize = 4;
11const CRC_LEN: usize = 2;
12
13const fn version_from_bits(bits: u8) -> Option<MpegVersion> {
14    match bits & 0x03 {
15        0b00 => Some(MpegVersion::Mpeg25),
16        0b10 => Some(MpegVersion::Mpeg2),
17        0b11 => Some(MpegVersion::Mpeg1),
18        _ => None, // 0b01 reserved
19    }
20}
21
22/// Reads back-to-back MPEG audio (Layer III) frames from pushed byte chunks.
23///
24/// Assumes the input is already frame-aligned (no ID3 tag or leading garbage
25/// skipping) — a bad sync word or reserved header field is a hard `Err`, never a
26/// silent resync scan (matches this workspace's `adts-core` crate).
27#[derive(Debug, Clone, Default)]
28pub struct Demuxer {
29    buf: Vec<u8>,
30    header: Option<FrameHeader>,
31}
32
33impl Demuxer {
34    /// New, empty demux session.
35    #[must_use]
36    pub fn new() -> Self {
37        Self::default()
38    }
39
40    /// Append incoming bytes.
41    pub fn push_bytes(&mut self, data: &[u8]) {
42        self.buf.extend_from_slice(data);
43    }
44
45    /// `FrameHeader` parsed from the most recently returned frame, if any.
46    #[must_use]
47    pub const fn header(&self) -> Option<FrameHeader> {
48        self.header
49    }
50
51    /// Pop the next complete frame's payload (header + optional CRC stripped), or
52    /// `Ok(None)` if the buffer doesn't yet hold a full frame.
53    pub fn poll_frame(&mut self) -> Result<Option<Bytes>, Error> {
54        if self.buf.len() < HEADER_LEN {
55            return Ok(None);
56        }
57        if self.buf[0] != 0xFF || (self.buf[1] & 0xE0) != 0xE0 {
58            return Err(Error::BadSyncOrReservedField);
59        }
60        let version = version_from_bits(self.buf[1] >> 3).ok_or(Error::BadSyncOrReservedField)?;
61        let layer_bits = (self.buf[1] >> 1) & 0x03;
62        if layer_bits != 0b01 {
63            return Err(Error::UnsupportedLayer);
64        }
65        let protection_absent = (self.buf[1] & 0x01) != 0;
66
67        let bitrate_index = (self.buf[2] >> 4) & 0x0F;
68        if bitrate_index == 0 || bitrate_index == 0x0F {
69            return Err(Error::BadSyncOrReservedField);
70        }
71        let bitrate_kbps = bitrate_table(version)[usize::from(bitrate_index) - 1];
72
73        let sample_rate_index = (self.buf[2] >> 2) & 0x03;
74        if sample_rate_index == 0x03 {
75            return Err(Error::BadSyncOrReservedField);
76        }
77        let sample_rate = sample_rate_table(version)[usize::from(sample_rate_index)];
78
79        let padding = (self.buf[2] >> 1) & 0x01 != 0;
80        let channel_mode = ChannelMode::from_bits(self.buf[3] >> 6);
81
82        let header = FrameHeader {
83            version,
84            bitrate_kbps,
85            sample_rate,
86            channel_mode,
87        };
88        let frame_len = header.frame_len(padding);
89        if self.buf.len() < frame_len {
90            return Ok(None);
91        }
92
93        let payload_start = if protection_absent {
94            HEADER_LEN
95        } else {
96            HEADER_LEN + CRC_LEN
97        };
98        self.header = Some(header);
99        let payload = Bytes::copy_from_slice(&self.buf[payload_start.min(frame_len)..frame_len]);
100        self.buf.drain(0..frame_len);
101        Ok(Some(payload))
102    }
103}
104
105#[cfg(test)]
106#[path = "demux_tests.rs"]
107mod tests;