1mod import;
7pub mod mapping;
8
9pub use import::*;
10pub use mapping::Mapping;
11
12use bytes::{Buf, Bytes};
13
14const OPUS_HEAD: u64 = u64::from_be_bytes(*b"OpusHead");
15
16#[derive(Debug, Clone, thiserror::Error)]
18#[non_exhaustive]
19pub enum Error {
20 #[error("OpusHead must be at least 19 bytes")]
22 HeadTooShort,
23
24 #[error("invalid OpusHead signature")]
26 InvalidSignature,
27
28 #[error("unsupported OpusHead version {0}")]
31 UnsupportedVersion(u8),
32
33 #[error("channel mapping family does not allow {0} channels")]
36 UnsupportedChannelCount(u32),
37
38 #[error("OpusHead channel mapping table is truncated")]
40 MappingTableTooShort,
41
42 #[error("invalid OpusHead channel mapping table")]
45 InvalidMappingTable,
46
47 #[error("unsupported channel mapping family {0}")]
52 UnsupportedMappingFamily(u8),
53}
54
55pub type Result<T> = std::result::Result<T, Error>;
56
57#[derive(Clone, Copy, Debug, PartialEq, Eq)]
59#[non_exhaustive]
60pub struct Config {
61 pub sample_rate: u32,
66 pub channel_count: u32,
68 pub pre_skip: u16,
70 pub output_gain: i16,
72 pub mapping_family: u8,
76 pub mapping: Option<Mapping>,
78}
79
80impl Config {
81 pub fn new(sample_rate: u32, channel_count: u32) -> Self {
83 Self {
84 sample_rate,
85 channel_count,
86 pre_skip: 0,
87 output_gain: 0,
88 mapping_family: 0,
89 mapping: None,
90 }
91 }
92
93 pub fn with_pre_skip(mut self, pre_skip: u16) -> Self {
95 self.pre_skip = pre_skip;
96 self
97 }
98
99 pub fn parse<T: Buf>(buf: &mut T) -> Result<Self> {
105 if buf.remaining() < 19 {
106 return Err(Error::HeadTooShort);
107 }
108 let signature = buf.get_u64();
109 if signature != OPUS_HEAD {
110 return Err(Error::InvalidSignature);
111 }
112
113 let version = buf.get_u8();
114 if version > 15 {
115 return Err(Error::UnsupportedVersion(version));
116 }
117 let channel_count = buf.get_u8() as u32;
118 let pre_skip = buf.get_u16_le();
119 let sample_rate = buf.get_u32_le();
120 let output_gain = buf.get_i16_le();
121 let mapping_family = buf.get_u8();
122
123 let mapping = match mapping_family {
124 0 if (1..=2).contains(&channel_count) => None,
125 0 => return Err(Error::UnsupportedChannelCount(channel_count)),
126 family => Some(Mapping::parse(buf, family, channel_count as u8)?),
127 };
128
129 Ok(Self {
130 sample_rate,
131 channel_count,
132 pre_skip,
133 output_gain,
134 mapping_family,
135 mapping,
136 })
137 }
138
139 pub fn encode(&self) -> Result<Bytes> {
148 if self.mapping_family != self.mapping.map_or(0, |mapping| mapping.family()) {
149 return Err(Error::UnsupportedMappingFamily(self.mapping_family));
150 }
151 let valid = match &self.mapping {
152 None => (1..=2).contains(&self.channel_count),
153 Some(mapping) => self.channel_count == mapping.table().len() as u32,
154 };
155 if !valid {
156 return Err(Error::UnsupportedChannelCount(self.channel_count));
157 }
158
159 let mut head = Vec::with_capacity(21 + self.channel_count as usize);
160 head.extend_from_slice(b"OpusHead");
161 head.push(1); head.push(self.channel_count as u8);
163 head.extend_from_slice(&self.pre_skip.to_le_bytes());
164 head.extend_from_slice(&self.sample_rate.to_le_bytes());
165 head.extend_from_slice(&self.output_gain.to_le_bytes());
166 match &self.mapping {
167 None => head.push(0),
168 Some(mapping) => {
169 head.extend_from_slice(&[mapping.family(), mapping.streams(), mapping.coupled()]);
170 head.extend_from_slice(mapping.table());
171 }
172 }
173 Ok(Bytes::from(head))
174 }
175}
176
177pub(crate) fn packet_samples(packet: &[u8]) -> Option<u32> {
184 let toc = *packet.first()?;
185 let frames = match toc & 0b11 {
186 0 => 1,
187 1 | 2 => 2,
188 _ => (packet.get(1)? & 0b0011_1111) as u32,
190 };
191 Some(config_samples(toc >> 3) * frames)
192}
193
194fn config_samples(config: u8) -> u32 {
196 match config {
197 0 | 4 | 8 => 480,
199 1 | 5 | 9 => 960,
200 2 | 6 | 10 => 1920,
201 3 | 7 | 11 => 2880,
202 12 | 14 => 480,
204 13 | 15 => 960,
205 16 | 20 | 24 | 28 => 120,
207 17 | 21 | 25 | 29 => 240,
208 18 | 22 | 26 | 30 => 480,
209 _ => 960,
211 }
212}
213
214#[cfg(test)]
215mod tests {
216 use super::*;
217
218 #[test]
219 fn packet_samples_reads_toc() {
220 assert_eq!(packet_samples(&[16 << 3]), Some(120));
222 assert_eq!(packet_samples(&[3 << 3]), Some(2880));
224 assert_eq!(packet_samples(&[(1 << 3) | 1]), Some(1920));
226 assert_eq!(packet_samples(&[(1 << 3) | 3, 4]), Some(3840));
228 assert_eq!(packet_samples(&[]), None);
229 }
230
231 #[test]
232 fn parses_valid_opus_head() {
233 let cfg = Config::new(48_000, 2).with_pre_skip(312);
234 let encoded = cfg.encode().unwrap();
235 assert_eq!(encoded.len(), 19);
236 let parsed = Config::parse(&mut encoded.as_ref()).unwrap();
237 assert_eq!(parsed.sample_rate, 48_000);
238 assert_eq!(parsed.channel_count, 2);
239 assert_eq!(parsed.pre_skip, 312);
240 assert_eq!(parsed, cfg);
241 }
242
243 #[test]
244 fn parse_rejects_invalid_signature() {
245 let mut bytes = Config::new(48_000, 1).encode().unwrap().to_vec();
246 bytes[0] = b'X';
247 assert!(Config::parse(&mut bytes.as_slice()).is_err());
248 }
249
250 #[test]
251 fn encode_rejects_multichannel() {
252 let err = Config::new(48_000, 6).encode().unwrap_err();
253 assert!(matches!(err, Error::UnsupportedChannelCount(6)));
254 }
255
256 fn head(version: u8, channels: u8, gain: i16, family: u8) -> Vec<u8> {
258 let mut head = b"OpusHead".to_vec();
259 head.push(version);
260 head.push(channels);
261 head.extend_from_slice(&312u16.to_le_bytes());
262 head.extend_from_slice(&44_100u32.to_le_bytes());
263 head.extend_from_slice(&gain.to_le_bytes());
264 head.push(family);
265 head
266 }
267
268 #[test]
269 fn parses_gain_and_input_rate() {
270 let bytes = head(1, 2, -1536, 0);
271 let parsed = Config::parse(&mut bytes.as_slice()).unwrap();
272 assert_eq!(parsed.sample_rate, 44_100);
273 assert_eq!(parsed.pre_skip, 312);
274 assert_eq!(parsed.output_gain, -1536);
275 assert_eq!(parsed.mapping_family, 0);
276
277 let encoded = parsed.encode().unwrap();
279 assert_eq!(encoded.as_ref(), bytes.as_slice());
280 }
281
282 #[test]
283 fn parse_accepts_minor_versions_only() {
284 for version in [0, 1, 15] {
285 assert!(Config::parse(&mut head(version, 2, 0, 0).as_slice()).is_ok());
286 }
287 assert!(matches!(
288 Config::parse(&mut head(16, 2, 0, 0).as_slice()),
289 Err(Error::UnsupportedVersion(16))
290 ));
291 }
292
293 #[test]
294 fn parses_a_mapping_table() {
295 let mut bytes = head(1, 6, 0, 1);
297 bytes.extend_from_slice(&[4, 2, 0, 4, 1, 2, 3, 5]);
298 let trailing = [bytes.as_slice(), b"trailing"].concat();
299 let mut buf = trailing.as_slice();
300 let parsed = Config::parse(&mut buf).unwrap();
301 assert_eq!(buf, b"trailing");
302 assert_eq!(parsed.channel_count, 6);
303 assert_eq!(parsed.mapping_family, 1);
304
305 let mapping = parsed.mapping.unwrap();
306 assert_eq!(mapping.family(), 1);
307 assert_eq!(mapping.streams(), 4);
308 assert_eq!(mapping.coupled(), 2);
309 assert_eq!(mapping.table(), &[0, 4, 1, 2, 3, 5]);
310
311 assert_eq!(parsed.encode().unwrap(), bytes);
313 }
314
315 #[test]
316 fn encodes_the_vorbis_mappings() {
317 for channels in 1..=8u8 {
318 let mut config = Config::new(48_000, channels as u32);
319 config.mapping_family = 1;
320 config.mapping = Some(Mapping::vorbis(channels).unwrap());
321 let head = config.encode().unwrap();
322 assert_eq!(head.len(), 21 + channels as usize, "{channels} channels");
323 assert_eq!(
324 Config::parse(&mut head.as_ref()).unwrap(),
325 config,
326 "{channels} channels"
327 );
328 }
329
330 let five_one = Mapping::vorbis(6).unwrap();
332 assert_eq!((five_one.streams(), five_one.coupled()), (4, 2));
333 assert_eq!(five_one.table(), &[0, 4, 1, 2, 3, 5]);
334
335 assert!(matches!(Mapping::vorbis(0), Err(Error::UnsupportedChannelCount(0))));
336 assert!(matches!(Mapping::vorbis(9), Err(Error::UnsupportedChannelCount(9))));
337
338 let mut config = Config::new(48_000, 5);
340 config.mapping_family = 1;
341 config.mapping = Some(five_one);
342 assert!(matches!(config.encode(), Err(Error::UnsupportedChannelCount(5))));
343
344 let mut config = Config::new(48_000, 6);
346 config.mapping = Some(five_one);
347 assert!(matches!(config.encode(), Err(Error::UnsupportedMappingFamily(0))));
348 let mut config = Config::new(48_000, 2);
349 config.mapping_family = 1;
350 assert!(matches!(config.encode(), Err(Error::UnsupportedMappingFamily(1))));
351 }
352
353 #[test]
354 fn parse_rejects_channel_counts_the_family_does_not_allow() {
355 for (channels, family) in [(0, 0), (3, 0), (0, 1), (9, 1), (0, 255)] {
356 let mut bytes = head(1, channels, 0, family);
357 bytes.extend_from_slice(&[1, 0]);
358 bytes.extend(std::iter::repeat_n(0, channels as usize));
359 assert!(
360 matches!(
361 Config::parse(&mut bytes.as_slice()),
362 Err(Error::UnsupportedChannelCount(_))
363 ),
364 "{channels} channels in family {family}"
365 );
366 }
367 }
368
369 #[test]
370 fn parse_rejects_a_bad_mapping_table() {
371 assert!(matches!(
373 Config::parse(&mut head(1, 2, 0, 1).as_slice()),
374 Err(Error::MappingTableTooShort)
375 ));
376 let mut short = head(1, 2, 0, 1);
377 short.extend_from_slice(&[1, 1, 0]);
378 assert!(matches!(
379 Config::parse(&mut short.as_slice()),
380 Err(Error::MappingTableTooShort)
381 ));
382
383 for table in [
384 [0, 0, 0, 1],
386 [1, 2, 0, 1],
388 [1, 1, 0, 2],
390 ] {
391 let mut bytes = head(1, 2, 0, 1);
392 bytes.extend_from_slice(&table);
393 assert!(
394 matches!(Config::parse(&mut bytes.as_slice()), Err(Error::InvalidMappingTable)),
395 "{table:?}"
396 );
397 }
398
399 let mut silent = head(1, 2, 0, 1);
401 silent.extend_from_slice(&[1, 0, 0, 255]);
402 assert_eq!(
403 Config::parse(&mut silent.as_slice()).unwrap().mapping.unwrap().table(),
404 &[0, 255]
405 );
406 }
407}