mp4_atom/moov/trak/mdia/minf/stbl/stsd/
opus.rs1use crate::*;
2
3#[derive(Debug, Clone, PartialEq, Eq)]
4#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
5pub struct Opus {
6 pub audio: Audio,
7 pub dops: Dops,
8 pub btrt: Option<Btrt>,
9}
10
11impl Atom for Opus {
12 const KIND: FourCC = FourCC::new(b"Opus");
13
14 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
15 let audio = Audio::decode(buf)?;
16
17 let mut dops = None;
18 let mut btrt = None;
19
20 while let Some(atom) = Any::decode_maybe(buf)? {
22 match atom {
23 Any::Dops(atom) => dops = atom.into(),
24 Any::Btrt(atom) => btrt = atom.into(),
25 unknown => Self::decode_unknown(&unknown)?,
26 }
27 }
28 skip_trailing_padding(buf);
29
30 Ok(Self {
31 audio,
32 dops: dops.ok_or(Error::MissingBox(Dops::KIND))?,
33 btrt,
34 })
35 }
36
37 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
38 self.audio.encode(buf)?;
39 self.dops.encode(buf)?;
40 self.btrt.encode(buf)?;
41 Ok(())
42 }
43}
44
45#[derive(Debug, Clone, PartialEq, Eq)]
67#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
68pub struct Dops {
69 pub output_channel_count: u8,
70 pub pre_skip: u16,
71 pub input_sample_rate: u32,
72 pub output_gain: i16,
73 pub channel_mapping: Option<OpusChannelMapping>,
75}
76
77#[derive(Debug, Clone, PartialEq, Eq)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub struct OpusChannelMapping {
81 pub family: u8,
83 pub stream_count: u8,
85 pub coupled_count: u8,
87 pub channel_mapping: Vec<u8>,
90}
91
92impl OpusChannelMapping {
93 fn validate(&self, output_channel_count: u8) -> Result<()> {
96 if self.family == 0 {
97 return Err(Error::InvalidCombination(
98 "Opus channel mapping family 0 has no table",
99 ));
100 }
101 if output_channel_count == 0 {
102 return Err(Error::InvalidCombination(
103 "Opus output channel count must not be zero",
104 ));
105 }
106 if self.channel_mapping.len() != output_channel_count as usize {
107 return Err(Error::InvalidCombination(
108 "Opus channel mapping needs one entry per output channel",
109 ));
110 }
111 if self.stream_count == 0 {
112 return Err(Error::InvalidCombination(
113 "Opus stream count must not be zero",
114 ));
115 }
116 if self.coupled_count > self.stream_count {
117 return Err(Error::InvalidCombination(
118 "Opus coupled count exceeds the stream count",
119 ));
120 }
121
122 let decoded = self.stream_count as u16 + self.coupled_count as u16;
123 if decoded > 255 {
124 return Err(Error::InvalidCombination(
125 "Opus streams decode to more than 255 channels",
126 ));
127 }
128 if self
129 .channel_mapping
130 .iter()
131 .any(|&index| index != 255 && index as u16 >= decoded)
132 {
133 return Err(Error::InvalidCombination(
134 "Opus channel mapping names a channel past the decoded ones",
135 ));
136 }
137
138 Ok(())
139 }
140}
141
142impl Atom for Dops {
143 const KIND: FourCC = FourCC::new(b"dOps");
144
145 fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
146 let version = u8::decode(buf)?;
147 if version != 0 {
148 return Err(Error::UnknownVersion(version));
149 }
150
151 let output_channel_count = u8::decode(buf)?;
152 let pre_skip = u16::decode(buf)?;
153 let input_sample_rate = u32::decode(buf)?;
154 let output_gain = i16::decode(buf)?;
155
156 let family = u8::decode(buf)?;
157 let channel_mapping = match family {
158 0 => None,
159 family => {
160 let mapping = OpusChannelMapping {
161 family,
162 stream_count: u8::decode(buf)?,
163 coupled_count: u8::decode(buf)?,
164 channel_mapping: Vec::decode_exact(buf, output_channel_count as usize)?,
165 };
166 mapping.validate(output_channel_count)?;
167 Some(mapping)
168 }
169 };
170
171 Ok(Self {
172 output_channel_count,
173 pre_skip,
174 input_sample_rate,
175 output_gain,
176 channel_mapping,
177 })
178 }
179
180 fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
181 if let Some(mapping) = &self.channel_mapping {
182 mapping.validate(self.output_channel_count)?;
183 }
184
185 (0u8).encode(buf)?;
186 self.output_channel_count.encode(buf)?;
187 self.pre_skip.encode(buf)?;
188 self.input_sample_rate.encode(buf)?;
189 self.output_gain.encode(buf)?;
190
191 match &self.channel_mapping {
192 None => (0u8).encode(buf)?,
193 Some(mapping) => {
194 mapping.family.encode(buf)?;
195 mapping.stream_count.encode(buf)?;
196 mapping.coupled_count.encode(buf)?;
197 mapping.channel_mapping.encode(buf)?;
198 }
199 }
200
201 Ok(())
202 }
203}
204
205#[cfg(test)]
206mod tests {
207 use super::*;
208
209 const ENCODED_DOPS_STEREO: &[u8] = &[
211 0x00, 0x00, 0x00, 0x13, b'd', b'O', b'p', b's', 0x00, 0x02, 0x01, 0x38, 0x00, 0x00, 0xbb,
212 0x80, 0xff, 0xfe, 0x00,
213 ];
214
215 const ENCODED_DOPS_SURROUND: &[u8] = &[
217 0x00, 0x00, 0x00, 0x1b, b'd', b'O', b'p', b's', 0x00, 0x06, 0x01, 0x38, 0x00, 0x00, 0xbb,
218 0x80, 0x00, 0x00, 0x01, 0x04, 0x02, 0x00, 0x04, 0x01, 0x02, 0x03, 0x05,
219 ];
220
221 fn surround() -> Dops {
222 Dops {
223 output_channel_count: 6,
224 pre_skip: 312,
225 input_sample_rate: 48000,
226 output_gain: 0,
227 channel_mapping: Some(OpusChannelMapping {
228 family: 1,
229 stream_count: 4,
230 coupled_count: 2,
231 channel_mapping: vec![0, 4, 1, 2, 3, 5],
232 }),
233 }
234 }
235
236 fn decode(bytes: &[u8]) -> Result<Dops> {
237 Dops::decode(&mut std::io::Cursor::new(bytes))
238 }
239
240 fn encode(dops: &Dops) -> Result<Vec<u8>> {
241 let mut buf = Vec::new();
242 dops.encode(&mut buf)?;
243 Ok(buf)
244 }
245
246 #[test]
247 fn test_dops_family_0() {
248 let dops = Dops {
249 output_channel_count: 2,
250 pre_skip: 312,
251 input_sample_rate: 48000,
252 output_gain: -2,
253 channel_mapping: None,
254 };
255
256 assert_eq!(decode(ENCODED_DOPS_STEREO).unwrap(), dops);
257 assert_eq!(encode(&dops).unwrap(), ENCODED_DOPS_STEREO);
258 }
259
260 #[test]
261 fn test_dops_family_1() {
262 let dops = surround();
263
264 assert_eq!(decode(ENCODED_DOPS_SURROUND).unwrap(), dops);
265 assert_eq!(encode(&dops).unwrap(), ENCODED_DOPS_SURROUND);
266 }
267
268 #[test]
269 fn test_dops_any_family() {
270 for family in [2, 3, 255] {
271 let mut dops = surround();
272 dops.channel_mapping.as_mut().unwrap().family = family;
273
274 let encoded = encode(&dops).unwrap();
275 assert_eq!(decode(&encoded).unwrap(), dops);
276 }
277 }
278
279 #[test]
280 fn test_dops_silent_channel() {
281 let mut dops = surround();
282 dops.channel_mapping.as_mut().unwrap().channel_mapping[5] = 255;
283
284 let encoded = encode(&dops).unwrap();
285 assert_eq!(decode(&encoded).unwrap(), dops);
286 }
287
288 #[test]
289 fn test_dops_truncated_table() {
290 let mut bytes = ENCODED_DOPS_SURROUND[..ENCODED_DOPS_SURROUND.len() - 1].to_vec();
292 bytes[3] -= 1;
293
294 assert!(decode(&bytes).is_err());
295 }
296
297 #[test]
298 fn test_dops_rejects_invalid_table() {
299 let invalid: [fn(&mut OpusChannelMapping); 4] = [
300 |mapping| mapping.stream_count = 0,
301 |mapping| mapping.coupled_count = mapping.stream_count + 1,
302 |mapping| mapping.channel_mapping[0] = 6,
303 |mapping| {
304 mapping.stream_count = 200;
305 mapping.coupled_count = 100;
306 },
307 ];
308
309 for corrupt in invalid {
310 let mut dops = surround();
311 corrupt(dops.channel_mapping.as_mut().unwrap());
312 assert!(matches!(encode(&dops), Err(Error::InvalidCombination(_))));
313
314 let mapping = dops.channel_mapping.as_ref().unwrap();
316 let mut bytes = ENCODED_DOPS_SURROUND.to_vec();
317 bytes[19] = mapping.stream_count;
318 bytes[20] = mapping.coupled_count;
319 bytes[21..].copy_from_slice(&mapping.channel_mapping);
320 assert!(matches!(decode(&bytes), Err(Error::InvalidCombination(_))));
321 }
322 }
323
324 #[test]
325 fn test_dops_rejects_family_0_table() {
326 let mut dops = surround();
327 dops.channel_mapping.as_mut().unwrap().family = 0;
328
329 assert!(matches!(encode(&dops), Err(Error::InvalidCombination(_))));
330 }
331
332 #[test]
333 fn test_dops_rejects_table_length_mismatch() {
334 let mut dops = surround();
335 dops.output_channel_count = 5;
336
337 assert!(matches!(encode(&dops), Err(Error::InvalidCombination(_))));
338 }
339}