arcly_stream/codec/
aac.rs1pub const SAMPLE_RATES: [u32; 13] = [
5 96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050, 16000, 12000, 11025, 8000, 7350,
6];
7
8#[derive(Debug, Clone, Copy, PartialEq, Eq)]
10pub struct AdtsHeader {
11 pub object_type: u8,
13 pub sample_rate: u32,
15 pub channels: u8,
17 pub frame_length: usize,
19 pub header_len: usize,
21}
22
23pub fn parse_adts(data: &[u8]) -> Option<AdtsHeader> {
26 if data.len() < 7 {
27 return None;
28 }
29 if data[0] != 0xFF || (data[1] & 0xF0) != 0xF0 {
31 return None;
32 }
33 let protection_absent = data[1] & 0x01;
34 let profile = (data[2] >> 6) & 0x03;
35 let freq_index = ((data[2] >> 2) & 0x0F) as usize;
36 let channel_config = ((data[2] & 0x01) << 2) | ((data[3] >> 6) & 0x03);
37 let frame_length =
38 (((data[3] & 0x03) as usize) << 11) | ((data[4] as usize) << 3) | ((data[5] as usize) >> 5);
39
40 let sample_rate = *SAMPLE_RATES.get(freq_index)?;
41 let header_len = if protection_absent == 1 { 7 } else { 9 };
42 if frame_length < header_len {
43 return None;
44 }
45
46 Some(AdtsHeader {
47 object_type: profile + 1,
48 sample_rate,
49 channels: channel_config,
50 frame_length,
51 header_len,
52 })
53}
54
55#[cfg(test)]
56mod tests {
57 use super::*;
58
59 #[test]
60 fn parses_aac_lc_stereo_44100() {
61 let hdr = [0xFF, 0xF1, 0x50, 0x80, 0x01, 0xA0, 0x00];
68 let h = parse_adts(&hdr).expect("parse");
69 assert_eq!(h.object_type, 2); assert_eq!(h.sample_rate, 44100);
71 assert_eq!(h.channels, 2);
72 assert_eq!(h.frame_length, 13);
73 assert_eq!(h.header_len, 7); }
75
76 #[test]
77 fn rejects_without_syncword() {
78 assert!(parse_adts(&[0x00; 8]).is_none());
79 assert!(parse_adts(&[0xFF, 0x00]).is_none());
80 }
81}