mpeg_audio_header/
frame.rs

1// SPDX-FileCopyrightText: The mpeg-audio-header authors
2// SPDX-License-Identifier: MPL-2.0
3
4use std::{io::Read, time::Duration};
5
6use crate::{reader::Reader, PositionalError, PositionalResult};
7
8pub(crate) const FRAME_HEADER_SIZE: u8 = 4;
9pub(crate) const XING_HEADER_MIN_SIZE: u8 = 8;
10pub(crate) const XING_VBRI_HEADER_MIN_SIZE: u8 = 22; // 4 + 8 + 22 = 30 (= start of TOC entries)
11
12// Tag frame/header sizes (including FRAME_HEADER_SIZE)
13const ID3V1_FRAME_SIZE: u8 = 128;
14const ID3V2_HEADER_SIZE: u8 = 10;
15const ID3V2_FOOTER_SIZE: u8 = 10;
16const APEV2_HEADER_SIZE: u8 = 32;
17
18const HEADER_WORD_SYNC_MASK: u32 = 0xFFE0_0000;
19
20fn is_header_word_synced(header_word: u32) -> bool {
21    (header_word & HEADER_WORD_SYNC_MASK) == HEADER_WORD_SYNC_MASK
22}
23
24fn maybe_valid_header_word(header_word: u32) -> bool {
25    if version_from_header_word(header_word).is_none()
26        || layer_from_header_word(header_word).is_none()
27        || !is_valid_bitrate_bits(bitrate_bits_from_header_word(header_word))
28        || !is_valid_sample_rate_bits(sample_rate_bits_from_header_word(header_word))
29    {
30        return false;
31    }
32    // Emphasis
33    if header_word & 0b11 == 0b10 {
34        return false;
35    }
36    true
37}
38
39/// MPEG Version
40#[derive(Clone, Copy, Debug, PartialEq, Eq)]
41pub enum Version {
42    /// MPEG-1
43    Mpeg1 = 0,
44
45    /// MPEG-2
46    Mpeg2 = 1,
47
48    /// MPEG 2.5
49    Mpeg25 = 2,
50}
51
52const fn version_index(version: Version) -> usize {
53    version as usize
54}
55
56fn version_from_header_word(header_word: u32) -> Option<Version> {
57    match (header_word >> 19) & 0b11 {
58        0b00 => Some(Version::Mpeg25),
59        0b01 => None,
60        0b10 => Some(Version::Mpeg2),
61        0b11 => Some(Version::Mpeg1),
62        _ => unreachable!("exhaustive match on version bits not recognized by compiler"),
63    }
64}
65
66/// MPEG Audio Layer
67#[derive(Clone, Copy, Debug, PartialEq, Eq)]
68pub enum Layer {
69    /// Layer I
70    Layer1 = 0,
71
72    /// Layer II
73    Layer2 = 1,
74
75    /// Layer III
76    Layer3 = 2,
77}
78
79const fn layer_index(layer: Layer) -> usize {
80    layer as usize
81}
82
83fn layer_from_header_word(header_word: u32) -> Option<Layer> {
84    match (header_word >> 17) & 0b11 {
85        0b00 => None,
86        0b01 => Some(Layer::Layer3),
87        0b10 => Some(Layer::Layer2),
88        0b11 => Some(Layer::Layer1),
89        _ => unreachable!("exhaustive match on layer bits not recognized by compiler"),
90    }
91}
92
93/// Channel Mode
94#[derive(Clone, Copy, Debug, PartialEq, Eq)]
95pub enum Mode {
96    /// Stereo
97    Stereo = 0,
98
99    /// Joint Stereo
100    JointStereo = 1,
101
102    /// Dual Channel
103    DualChannel = 2,
104
105    /// Mono
106    Mono = 3,
107}
108
109const fn mode_index(mode: Mode) -> usize {
110    mode as usize
111}
112
113fn mode_from_header_word(header_word: u32) -> Mode {
114    match (header_word >> 6) & 0b11 {
115        0b00 => Mode::Stereo,
116        0b01 => Mode::JointStereo,
117        0b10 => Mode::DualChannel,
118        0b11 => Mode::Mono,
119        _ => unreachable!("exhaustive match on mode bits not recognized by compiler"),
120    }
121}
122
123static BIT_RATES_KBPS: [[[u32; 15]; 3]; 3] = [
124    [
125        [
126            // Mpeg1 Layer1
127            /*free*/ 0, 32, 64, 96, 128, 160, 192, 224, 256, 288,
128            320, 352, 384, 416, 448,
129        ],
130        [
131            // Mpeg1 Layer2
132            /*free*/ 0, 32, 48, 56, 64, 80, 96, 112, 128, 160,
133            192, 224, 256, 320, 384,
134        ],
135        [
136            // Mpeg1 Layer3
137            /*free*/ 0, 32, 40, 48, 56, 64, 80, 96, 112, 128, 160,
138            192, 224, 256, 320,
139        ],
140    ],
141    [
142        [
143            // Mpeg2 Layer1
144            /*free*/ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144,
145            160, 176, 192, 224, 256,
146        ],
147        [
148            // Mpeg2 Layer2
149            /*free*/ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
150            112, 128, 144, 160,
151        ],
152        [
153            // Mpeg2 Layer3
154            /*free*/ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
155            112, 128, 144, 160,
156        ],
157    ],
158    [
159        [
160            // Mpeg25 Layer1
161            /*free*/ 0, 32, 48, 56, 64, 80, 96, 112, 128, 144,
162            160, 176, 192, 224, 256,
163        ],
164        [
165            // Mpeg25 Layer2
166            /*free*/ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
167            112, 128, 144, 160,
168        ],
169        [
170            // Mpeg25 Layer3
171            /*free*/ 0, 8, 16, 24, 32, 40, 48, 56, 64, 80, 96,
172            112, 128, 144, 160,
173        ],
174    ],
175];
176
177const BITRATE_BITS_MASK: u8 = 0b1111;
178
179fn bitrate_bits_from_header_word(header_word: u32) -> u8 {
180    ((header_word >> 12) & u32::from(BITRATE_BITS_MASK)) as u8
181}
182
183fn is_valid_bitrate_bits(bitrate_bits: u8) -> bool {
184    bitrate_bits & BITRATE_BITS_MASK < BITRATE_BITS_MASK
185}
186
187fn bitrate_bps_from_bits(version: Version, layer: Layer, bitrate_bits: u8) -> u32 {
188    debug_assert!(is_valid_bitrate_bits(bitrate_bits));
189    1000 * BIT_RATES_KBPS[version_index(version)][layer_index(layer)][bitrate_bits as usize]
190}
191
192const SAMPLE_RATES_HZ: [[u16; 3]; 3] = [
193    [44100, 48000, 32000], // Mpeg1
194    [22050, 24000, 16000], // Mpeg2
195    [11025, 12000, 8000],  // Mpeg25
196];
197
198const SAMPLE_RATE_BITS_MASK: u8 = 0b11;
199
200fn sample_rate_bits_from_header_word(header_word: u32) -> u8 {
201    ((header_word >> 10) & u32::from(SAMPLE_RATE_BITS_MASK)) as u8
202}
203
204fn is_valid_sample_rate_bits(sample_rate_bits: u8) -> bool {
205    sample_rate_bits & SAMPLE_RATE_BITS_MASK < SAMPLE_RATE_BITS_MASK
206}
207
208fn sample_rate_hz_from_bits(version: Version, sample_rate_bits: u8) -> u16 {
209    debug_assert!(is_valid_sample_rate_bits(sample_rate_bits));
210    SAMPLE_RATES_HZ[version_index(version)][sample_rate_bits as usize]
211}
212
213const SAMPLE_COUNT: [[u16; 3]; 3] = [
214    [384, 1152, 1152], // Mpeg1
215    [384, 1152, 576],  // Mpeg2
216    [384, 1152, 576],  // Mpeg25
217];
218
219const fn sample_count(version: Version, layer: Layer) -> u16 {
220    SAMPLE_COUNT[version_index(version)][layer_index(layer)]
221}
222
223const SIDE_INFORMATION_SIZES: [[u16; 4]; 3] = [
224    [32, 32, 32, 17], // Mpeg1
225    [17, 17, 17, 9],  // Mpeg2
226    [17, 17, 17, 9],  // Mpeg25
227];
228
229const fn side_information_size(version: Version, mode: Mode) -> u16 {
230    SIDE_INFORMATION_SIZES[version_index(version)][mode_index(mode)]
231}
232
233#[derive(Debug, Clone)]
234pub(crate) struct FrameHeader {
235    pub(crate) version: Version,
236    pub(crate) layer: Layer,
237    pub(crate) mode: Mode,
238    pub(crate) sample_count: u16,
239    pub(crate) sample_rate_hz: u16,
240    pub(crate) bitrate_bps: Option<u32>,
241    pub(crate) frame_size: Option<u16>,
242}
243
244impl FrameHeader {
245    pub(crate) fn check_payload_size(&self, payload_size: u16) -> bool {
246        if let Some(frame_size) = self.frame_size {
247            payload_size <= frame_size
248        } else {
249            // If the frame size is unknown we assume that the
250            // frame is big enough to carry the payload.
251            true
252        }
253    }
254}
255
256fn try_read_next_header_word<R: Read>(reader: &mut Reader<'_, R>) -> PositionalResult<Option<u32>> {
257    let mut next_byte_buf = [0u8; 1];
258    let mut initial_byte_offset = reader.position().byte_offset;
259    let mut frame_header_word = 0u32;
260    loop {
261        while !is_header_word_synced(frame_header_word) {
262            if reader.position().byte_offset - initial_byte_offset >= u64::from(FRAME_HEADER_SIZE)
263                && skip_metadata(reader, frame_header_word.to_be_bytes())?
264            {
265                if reader.position().duration == Duration::ZERO {
266                    // Restart the loop after skipping leading metadata frames before the MPEG frames
267                    initial_byte_offset = reader.position().byte_offset;
268                    frame_header_word = 0u32;
269                    continue;
270                }
271                // Ignore all additional data after the first trailing metadata frame
272                return Ok(None);
273            }
274            if !reader.try_read_exact_until_eof(&mut next_byte_buf)? {
275                return Ok(None);
276            }
277            frame_header_word = (frame_header_word << 8) | u32::from(next_byte_buf[0]);
278        }
279
280        if maybe_valid_header_word(frame_header_word) {
281            break;
282        }
283
284        // Start next round
285        if !reader.try_read_exact_until_eof(&mut next_byte_buf)? {
286            return Ok(None);
287        }
288    }
289
290    debug_assert!(is_header_word_synced(frame_header_word));
291    debug_assert!(maybe_valid_header_word(frame_header_word));
292    Ok(Some(frame_header_word))
293}
294
295pub(crate) fn skip_metadata<R: Read>(
296    reader: &mut Reader<'_, R>,
297    frame_header_bytes: [u8; FRAME_HEADER_SIZE as usize],
298) -> PositionalResult<bool> {
299    match &frame_header_bytes[..3] {
300        b"ID3" => {
301            // ID3v2 frame
302            let mut id3v2 = [0; (ID3V2_HEADER_SIZE - FRAME_HEADER_SIZE) as usize];
303            if !reader.try_read_exact_until_eof(&mut id3v2)? {
304                // EOF
305                return Ok(true);
306            }
307            let flags = id3v2[1];
308            let footer_size = if flags & 0b0001_0000 == 0 {
309                0
310            } else {
311                u32::from(ID3V2_FOOTER_SIZE)
312            };
313            // 32/28-bit synchronization safe integer
314            let tag_size = u32::from(id3v2[5])
315                | (u32::from(id3v2[4]) << 7)
316                | (u32::from(id3v2[3]) << 14)
317                | (u32::from(id3v2[2]) << 21);
318            reader.try_skip_exact_until_eof((tag_size + footer_size).into())?;
319            Ok(true)
320        }
321        b"TAG" => {
322            // ID3v1 frame
323            reader.try_skip_exact_until_eof((ID3V1_FRAME_SIZE - FRAME_HEADER_SIZE).into())?;
324            Ok(true)
325        }
326        b"APE" if frame_header_bytes[3] == b'T' => {
327            // APEv2 frame
328            let mut ape_header = [0; (APEV2_HEADER_SIZE - FRAME_HEADER_SIZE) as usize];
329            if !reader.try_read_exact_until_eof(&mut ape_header)? {
330                // EOF
331                return Ok(true);
332            }
333            if &ape_header[..4] == b"AGEX" {
334                let tag_size = u32::from_le_bytes(ape_header[8..12].try_into().expect("4 bytes"));
335                reader.try_skip_exact_until_eof(tag_size.into())?;
336            }
337            Ok(true)
338        }
339        _ => Ok(false),
340    }
341}
342
343pub(crate) type UnrecognizedFrameHeaderError = ([u8; FRAME_HEADER_SIZE as usize], PositionalError);
344
345pub(crate) type TryReadFrameHeaderOutcome =
346    std::result::Result<Option<FrameHeader>, UnrecognizedFrameHeaderError>;
347
348impl FrameHeader {
349    pub(crate) const fn channel_count(&self) -> u8 {
350        match self.mode {
351            Mode::Stereo | Mode::JointStereo | Mode::DualChannel => 2,
352            Mode::Mono => 1,
353        }
354    }
355
356    pub(crate) fn side_information_size(&self) -> u16 {
357        side_information_size(self.version, self.mode)
358    }
359
360    #[allow(clippy::panic_in_result_fn)] // version/layer/mode unreachable!()
361    pub(crate) fn try_read<R: Read>(
362        reader: &mut Reader<'_, R>,
363    ) -> PositionalResult<TryReadFrameHeaderOutcome> {
364        let Some(header_word) = try_read_next_header_word(reader)? else {
365            return Ok(Ok(None));
366        };
367
368        let version = version_from_header_word(header_word).expect("valid version");
369
370        let sample_rate_hz =
371            sample_rate_hz_from_bits(version, sample_rate_bits_from_header_word(header_word));
372        debug_assert!(sample_rate_hz > 0);
373
374        let layer = layer_from_header_word(header_word).expect("valid layer");
375
376        let bitrate_bps =
377            bitrate_bps_from_bits(version, layer, bitrate_bits_from_header_word(header_word));
378
379        let sample_count = sample_count(version, layer);
380
381        let mode = mode_from_header_word(header_word);
382
383        let padding = (header_word >> 9) & 0b1;
384
385        let frame_size = if layer == Layer::Layer1 {
386            (12 * bitrate_bps / u32::from(sample_rate_hz) + padding) * 4
387        } else {
388            u32::from(sample_count) * (bitrate_bps / 8) / u32::from(sample_rate_hz) + padding
389        };
390        debug_assert!(frame_size <= u16::MAX.into());
391        let frame_size = frame_size as u16;
392
393        Ok(Ok(Some(Self {
394            version,
395            layer,
396            mode,
397            sample_rate_hz,
398            sample_count,
399            bitrate_bps: (bitrate_bps > 0).then_some(bitrate_bps),
400            frame_size: (frame_size > 0).then_some(frame_size),
401        })))
402    }
403}