1use crate::media::{Error, Result};
2
3#[derive(Clone, Debug, PartialEq, Eq)]
4pub struct OpusHead {
5 pub channels: usize,
6 pub pre_skip: u16,
7 pub input_sample_rate: u32,
8 pub output_gain: i16,
9 pub mapping_family: u8,
10 pub streams: usize,
11 pub coupled_streams: usize,
12 pub mapping: Vec<u8>,
13}
14
15impl OpusHead {
16 pub fn simple(channels: usize) -> Result<OpusHead> {
17 if !(1..=2).contains(&channels) {
18 return Err(Error::Invalid("family 0 supports 1 or 2 channels".into()));
19 }
20 Ok(OpusHead {
21 channels,
22 pre_skip: 0,
23 input_sample_rate: 48000,
24 output_gain: 0,
25 mapping_family: 0,
26 streams: 1,
27 coupled_streams: channels - 1,
28 mapping: (0..channels as u8).collect(),
29 })
30 }
31
32 pub fn parse(data: &[u8]) -> Result<OpusHead> {
33 if data.len() < 19 || &data[..8] != b"OpusHead" {
34 return Err(Error::Invalid("missing OpusHead magic".into()));
35 }
36 let version = data[8];
37 if version >> 4 != 0 {
38 return Err(Error::Unsupported("OpusHead major version".into()));
39 }
40 let channels = data[9] as usize;
41 let pre_skip = u16::from_le_bytes([data[10], data[11]]);
42 let input_sample_rate = u32::from_le_bytes([data[12], data[13], data[14], data[15]]);
43 let output_gain = i16::from_le_bytes([data[16], data[17]]);
44 let family = data[18];
45 Self::build(
46 channels,
47 pre_skip,
48 input_sample_rate,
49 output_gain,
50 family,
51 &data[19..],
52 )
53 }
54
55 pub fn from_dops(data: &[u8]) -> Result<OpusHead> {
56 if data.len() < 11 {
57 return Err(Error::Invalid("dOps too short".into()));
58 }
59 if data[0] != 0 {
60 return Err(Error::Unsupported("dOps version".into()));
61 }
62 let channels = data[1] as usize;
63 let pre_skip = u16::from_be_bytes([data[2], data[3]]);
64 let input_sample_rate = u32::from_be_bytes([data[4], data[5], data[6], data[7]]);
65 let output_gain = i16::from_be_bytes([data[8], data[9]]);
66 Self::build(
67 channels,
68 pre_skip,
69 input_sample_rate,
70 output_gain,
71 data[10],
72 &data[11..],
73 )
74 }
75
76 pub fn to_bytes(&self) -> Vec<u8> {
77 let mut v = b"OpusHead".to_vec();
78 v.push(1);
79 v.push(self.channels as u8);
80 v.extend(self.pre_skip.to_le_bytes());
81 v.extend(self.input_sample_rate.to_le_bytes());
82 v.extend(self.output_gain.to_le_bytes());
83 v.push(self.mapping_family);
84 if self.mapping_family != 0 {
85 v.push(self.streams as u8);
86 v.push(self.coupled_streams as u8);
87 v.extend(&self.mapping);
88 }
89 v
90 }
91
92 fn build(
93 channels: usize,
94 pre_skip: u16,
95 input_sample_rate: u32,
96 output_gain: i16,
97 family: u8,
98 rest: &[u8],
99 ) -> Result<OpusHead> {
100 if channels == 0 {
101 return Err(Error::Invalid("zero channels".into()));
102 }
103 let (streams, coupled, mapping) = if family == 0 {
104 if channels > 2 {
105 return Err(Error::Invalid("family 0 allows at most 2 channels".into()));
106 }
107 (1, channels - 1, (0..channels as u8).collect::<Vec<_>>())
108 } else {
109 if family == 3 {
110 return Err(Error::Unsupported(
111 "channel mapping family 3 (projection)".into(),
112 ));
113 }
114 if rest.len() < 2 + channels {
115 return Err(Error::Invalid("truncated channel mapping table".into()));
116 }
117 let streams = rest[0] as usize;
118 let coupled = rest[1] as usize;
119 if streams == 0 || coupled > streams || streams + coupled > 255 {
120 return Err(Error::Invalid("bad stream counts".into()));
121 }
122 if family == 1 && !(1..=8).contains(&channels) {
123 return Err(Error::Invalid("family 1 allows 1 to 8 channels".into()));
124 }
125 let mapping = rest[2..2 + channels].to_vec();
126 if mapping
127 .iter()
128 .any(|&m| m != 255 && m as usize >= streams + coupled)
129 {
130 return Err(Error::Invalid("channel mapping out of range".into()));
131 }
132 (streams, coupled, mapping)
133 };
134 Ok(OpusHead {
135 channels,
136 pre_skip,
137 input_sample_rate,
138 output_gain,
139 mapping_family: family,
140 streams,
141 coupled_streams: coupled,
142 mapping,
143 })
144 }
145
146 pub fn gain_linear(&self) -> f32 {
147 10f32.powf(self.output_gain as f32 / (20.0 * 256.0))
148 }
149}
150
151#[cfg(test)]
152mod tests {
153 use super::*;
154
155 #[test]
156 fn parse_family0_and_roundtrip() {
157 let mut h = b"OpusHead".to_vec();
158 h.extend([1, 2, 0x38, 0x01, 0x80, 0xBB, 0, 0, 0x00, 0x01, 0]);
159 let head = OpusHead::parse(&h).unwrap();
160 assert_eq!(head.channels, 2);
161 assert_eq!(head.pre_skip, 312);
162 assert_eq!(head.input_sample_rate, 48000);
163 assert_eq!(head.output_gain, 256);
164 assert_eq!((head.streams, head.coupled_streams), (1, 1));
165 assert_eq!(head.to_bytes(), h);
166 assert!((head.gain_linear() - 1.122).abs() < 1e-3);
167 }
168
169 #[test]
170 fn parse_family1_51() {
171 let mut h = b"OpusHead".to_vec();
172 h.extend([
173 1, 6, 0x38, 0x01, 0x80, 0xBB, 0, 0, 0, 0, 1, 4, 2, 0, 4, 1, 2, 3, 5,
174 ]);
175 let head = OpusHead::parse(&h).unwrap();
176 assert_eq!(
177 (head.channels, head.streams, head.coupled_streams),
178 (6, 4, 2)
179 );
180 assert_eq!(head.mapping, vec![0, 4, 1, 2, 3, 5]);
181 assert_eq!(OpusHead::parse(&head.to_bytes()).unwrap(), head);
182 }
183
184 #[test]
185 fn rejects_garbage() {
186 assert!(OpusHead::parse(b"OpusHea").is_err());
187 assert!(OpusHead::parse(b"OpusTags...........").is_err());
188 let mut h = b"OpusHead".to_vec();
189 h.extend([1, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0]);
190 assert!(OpusHead::parse(&h).is_err());
191 let mut h = b"OpusHead".to_vec();
192 h.extend([1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0, 5]);
193 assert!(OpusHead::parse(&h).is_err());
194 }
195
196 #[test]
197 fn dops() {
198 let d = [0u8, 2, 0x01, 0x38, 0, 0, 0xBB, 0x80, 0, 0, 0];
199 let head = OpusHead::from_dops(&d).unwrap();
200 assert_eq!(head.pre_skip, 312);
201 assert_eq!(head.channels, 2);
202 }
203}