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
94#[cfg(test)]
95mod tests {
96 use super::*;
97
98 #[test]
99 fn parses_valid_opus_head() {
100 let cfg = Config {
101 sample_rate: 48000,
102 channel_count: 2,
103 };
104 let encoded = cfg.encode().unwrap();
105 assert_eq!(encoded.len(), 19);
106 let parsed = Config::parse(&mut encoded.as_ref()).unwrap();
107 assert_eq!(parsed.sample_rate, 48000);
108 assert_eq!(parsed.channel_count, 2);
109 }
110
111 #[test]
112 fn parse_rejects_invalid_signature() {
113 let mut bytes = Config {
114 sample_rate: 48000,
115 channel_count: 1,
116 }
117 .encode()
118 .unwrap()
119 .to_vec();
120 bytes[0] = b'X';
121 assert!(Config::parse(&mut bytes.as_slice()).is_err());
122 }
123
124 #[test]
125 fn encode_rejects_multichannel() {
126 let err = Config {
127 sample_rate: 48000,
128 channel_count: 6,
129 }
130 .encode()
131 .unwrap_err();
132 assert!(matches!(err, Error::UnsupportedChannelCount(6)));
133 }
134}