Skip to main content

mp4_atom/moov/trak/mdia/minf/stbl/stsd/
opus.rs

1use 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        // Find d0ps in mp4a or wave
21        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/*
46    class ChannelMappingTable (unsigned int(8) OutputChannelCount){
47        unsigned int(8) StreamCount;
48        unsigned int(8) CoupledCount;
49        unsigned int(8 * OutputChannelCount) ChannelMapping;
50    }
51
52    aligned(8) class OpusSpecificBox extends Box('dOps'){
53        unsigned int(8) Version;
54        unsigned int(8) OutputChannelCount;
55        unsigned int(16) PreSkip;
56        unsigned int(32) InputSampleRate;
57        signed int(16) OutputGain;
58        unsigned int(8) ChannelMappingFamily;
59        if (ChannelMappingFamily != 0) {
60            ChannelMappingTable(OutputChannelCount);
61        }
62    }
63*/
64
65// Opus specific data
66#[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    /// The channel mapping family and table, or `None` for family 0 (mono or stereo, no table).
74    pub channel_mapping: Option<OpusChannelMapping>,
75}
76
77/// A nonzero channel mapping family and its table, laid out as RFC 7845 §5.1.1 describes.
78#[derive(Debug, Clone, PartialEq, Eq)]
79#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
80pub struct OpusChannelMapping {
81    /// The channel mapping family; never 0, which has no table.
82    pub family: u8,
83    /// The number of Opus streams in each packet.
84    pub stream_count: u8,
85    /// How many of those streams are coupled (stereo); they come first.
86    pub coupled_count: u8,
87    /// The decoded channel feeding each output channel, or 255 for silence.
88    /// It has one entry per output channel.
89    pub channel_mapping: Vec<u8>,
90}
91
92impl OpusChannelMapping {
93    // The checks RFC 7845 §5.1.1 applies to every family, so a table read from
94    // a file is refused rather than trusted.
95    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    // A stereo dOps with family 0 and no table.
210    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    // A 5.1 dOps with family 1 and the Vorbis order table libopus uses.
216    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        // Drop the last table entry, leaving five for six output channels.
291        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            // Write the same table past the encoder's check to prove decode refuses it too.
315            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}