moq_mux/codec/opus/
mod.rs1mod import;
7
8pub use import::*;
9
10use bytes::{Buf, Bytes};
11
12const OPUS_HEAD: u64 = u64::from_be_bytes(*b"OpusHead");
13
14#[derive(Debug, Clone, thiserror::Error)]
16#[non_exhaustive]
17pub enum Error {
18 #[error("OpusHead must be at least 19 bytes")]
20 HeadTooShort,
21
22 #[error("invalid OpusHead signature")]
24 InvalidSignature,
25
26 #[error("channel mapping family 0 only supports mono/stereo (got {0} channels)")]
29 UnsupportedChannelCount(u32),
30}
31
32pub type Result<T> = std::result::Result<T, Error>;
33
34pub struct Config {
36 pub sample_rate: u32,
37 pub channel_count: u32,
38}
39
40impl Config {
41 pub fn parse<T: Buf>(buf: &mut T) -> Result<Self> {
47 if buf.remaining() < 19 {
48 return Err(Error::HeadTooShort);
49 }
50 let signature = buf.get_u64();
51 if signature != OPUS_HEAD {
52 return Err(Error::InvalidSignature);
53 }
54
55 buf.advance(1); let channel_count = buf.get_u8() as u32;
57 buf.advance(2); let sample_rate = buf.get_u32_le();
59
60 if buf.remaining() > 0 {
62 buf.advance(buf.remaining());
63 }
64
65 Ok(Self {
66 sample_rate,
67 channel_count,
68 })
69 }
70
71 pub fn encode(&self) -> Result<Bytes> {
79 if !(1..=2).contains(&self.channel_count) {
80 return Err(Error::UnsupportedChannelCount(self.channel_count));
81 }
82 let mut head = Vec::with_capacity(19);
83 head.extend_from_slice(b"OpusHead");
84 head.push(1); head.push(self.channel_count as u8);
86 head.extend_from_slice(&0u16.to_le_bytes()); head.extend_from_slice(&self.sample_rate.to_le_bytes());
88 head.extend_from_slice(&0i16.to_le_bytes()); head.push(0); Ok(Bytes::from(head))
91 }
92}
93
94pub(crate) fn packet_samples(packet: &[u8]) -> Option<u32> {
101 let toc = *packet.first()?;
102 let frames = match toc & 0b11 {
103 0 => 1,
104 1 | 2 => 2,
105 _ => (packet.get(1)? & 0b0011_1111) as u32,
107 };
108 Some(config_samples(toc >> 3) * frames)
109}
110
111fn config_samples(config: u8) -> u32 {
113 match config {
114 0 | 4 | 8 => 480,
116 1 | 5 | 9 => 960,
117 2 | 6 | 10 => 1920,
118 3 | 7 | 11 => 2880,
119 12 | 14 => 480,
121 13 | 15 => 960,
122 16 | 20 | 24 | 28 => 120,
124 17 | 21 | 25 | 29 => 240,
125 18 | 22 | 26 | 30 => 480,
126 _ => 960,
128 }
129}
130
131#[cfg(test)]
132mod tests {
133 use super::*;
134
135 #[test]
136 fn packet_samples_reads_toc() {
137 assert_eq!(packet_samples(&[16 << 3]), Some(120));
139 assert_eq!(packet_samples(&[3 << 3]), Some(2880));
141 assert_eq!(packet_samples(&[(1 << 3) | 1]), Some(1920));
143 assert_eq!(packet_samples(&[(1 << 3) | 3, 4]), Some(3840));
145 assert_eq!(packet_samples(&[]), None);
146 }
147
148 #[test]
149 fn parses_valid_opus_head() {
150 let cfg = Config {
151 sample_rate: 48000,
152 channel_count: 2,
153 };
154 let encoded = cfg.encode().unwrap();
155 assert_eq!(encoded.len(), 19);
156 let parsed = Config::parse(&mut encoded.as_ref()).unwrap();
157 assert_eq!(parsed.sample_rate, 48000);
158 assert_eq!(parsed.channel_count, 2);
159 }
160
161 #[test]
162 fn parse_rejects_invalid_signature() {
163 let mut bytes = Config {
164 sample_rate: 48000,
165 channel_count: 1,
166 }
167 .encode()
168 .unwrap()
169 .to_vec();
170 bytes[0] = b'X';
171 assert!(Config::parse(&mut bytes.as_slice()).is_err());
172 }
173
174 #[test]
175 fn encode_rejects_multichannel() {
176 let err = Config {
177 sample_rate: 48000,
178 channel_count: 6,
179 }
180 .encode()
181 .unwrap_err();
182 assert!(matches!(err, Error::UnsupportedChannelCount(6)));
183 }
184}