scuffle_opus/
boxes.rs

1//! ISO base media file format boxes for OPUS.
2//!
3//! [Encapsulation of Opus in ISO Base Media File Format Version 0.6.8](https://www.opus-codec.org/docs/opus_in_isobmff.html)
4
5use isobmff::boxes::AudioSampleEntry;
6use isobmff::{BoxHeader, IsoBox, IsoSized, UnknownBox};
7use scuffle_bytes_util::BytesCow;
8use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize};
9
10/// Opus Sample Entry Format
11///
12/// Encapsulation of Opus in ISO Base Media File Format - 4.3.1
13#[derive(IsoBox, Debug, PartialEq, Eq)]
14#[iso_box(box_type = b"Opus")]
15pub struct OpusSampleEntry<'a> {
16    /// The audio sample entry fields that this box inherits.
17    pub sample_entry: AudioSampleEntry,
18    /// Contains initializing information for the decoder.
19    #[iso_box(nested_box)]
20    pub dops: OpusSpecificBox<'a>,
21    /// Any other boxes contained in this box.
22    #[iso_box(nested_box(collect_unknown))]
23    pub sub_boxes: Vec<UnknownBox<'a>>,
24}
25
26/// Opus Specific Box
27///
28/// Encapsulation of Opus in ISO Base Media File Format - 4.3.2
29#[derive(IsoBox, Debug, PartialEq, Eq)]
30#[iso_box(box_type = b"dOps", skip_impl(deserialize_seed, serialize))]
31pub struct OpusSpecificBox<'a> {
32    /// Shall be set to 0.
33    pub version: u8,
34    /// Shall be set to the same value as the *Output Channel Count* field in the
35    /// identification header defined in Ogg Opus.
36    pub output_channel_count: u8,
37    /// Indicates the number of the priming samples, that is, the number of samples at 48000 Hz
38    /// to discard from the decoder output when starting playback.
39    pub pre_skip: u16,
40    /// Shall be set to the same value as the *Input Sample Rate* field in the
41    /// identification header defined in Ogg Opus.
42    pub input_sample_rate: u32,
43    /// Shall be set to the same value as the *Output Gain* field in the identification
44    /// header define in Ogg Opus.
45    pub output_gain: i16,
46    /// Shall be set to the same value as the *Channel Mapping Family* field in
47    /// the identification header defined in Ogg Opus.
48    pub channel_mapping_family: u8,
49    /// The optional channel mapping table contained in this box.
50    ///
51    /// Only present if [`channel_mapping_family`](Self::channel_mapping_family) is not 0.
52    pub channel_mapping_table: Option<ChannelMappingTable<'a>>,
53}
54
55// The IsoBox derive macro doesn't support conditional fields.
56// That's why we have to implement the traits manually here.
57
58impl<'a> DeserializeSeed<'a, BoxHeader> for OpusSpecificBox<'a> {
59    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> std::io::Result<Self>
60    where
61        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
62    {
63        let version = u8::deserialize(&mut reader)?;
64        if version != 0 {
65            return Err(std::io::Error::new(
66                std::io::ErrorKind::InvalidData,
67                "OpusSpecificBox version must be 0",
68            ));
69        }
70
71        let output_channel_count = u8::deserialize(&mut reader)?;
72        let pre_skip = u16::deserialize(&mut reader)?;
73        let input_sample_rate = u32::deserialize(&mut reader)?;
74        let output_gain = i16::deserialize(&mut reader)?;
75        let channel_mapping_family = u8::deserialize(&mut reader)?;
76        let channel_mapping_table = if channel_mapping_family != 0 {
77            Some(ChannelMappingTable::deserialize_seed(&mut reader, output_channel_count)?)
78        } else {
79            None
80        };
81
82        Ok(Self {
83            version,
84            output_channel_count,
85            pre_skip,
86            input_sample_rate,
87            output_gain,
88            channel_mapping_family,
89            channel_mapping_table,
90        })
91    }
92}
93
94impl Serialize for OpusSpecificBox<'_> {
95    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
96    where
97        W: std::io::Write,
98    {
99        self.serialize_box_header(&mut writer)?;
100
101        self.version.serialize(&mut writer)?;
102        self.output_channel_count.serialize(&mut writer)?;
103        self.pre_skip.serialize(&mut writer)?;
104        self.input_sample_rate.serialize(&mut writer)?;
105        self.output_gain.serialize(&mut writer)?;
106        self.channel_mapping_family.serialize(&mut writer)?;
107        if let Some(channel_mapping_table) = &self.channel_mapping_table {
108            channel_mapping_table.serialize(&mut writer)?;
109        }
110
111        Ok(())
112    }
113}
114
115/// Channel Mapping Table
116///
117/// Encapsulation of Opus in ISO Base Media File Format - 4.3.2
118#[derive(Debug, PartialEq, Eq)]
119pub struct ChannelMappingTable<'a> {
120    /// Shall be set to the same value as the *Stream Count* field in the identification
121    /// header defined in Ogg Opus.
122    pub stream_count: u8,
123    /// Shall be set to the same value as the *Coupled Count* field in the identification
124    /// header defined in Ogg Opus.
125    pub coupled_count: u8,
126    /// Shall be set to the same octet string as *Channel Mapping* field in the identification
127    /// header defined in Ogg Opus.
128    pub channel_mapping: BytesCow<'a>,
129}
130
131impl<'a> DeserializeSeed<'a, u8> for ChannelMappingTable<'a> {
132    fn deserialize_seed<R>(mut reader: R, seed: u8) -> std::io::Result<Self>
133    where
134        R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
135    {
136        // seed is OutputChannelCount
137        let stream_count = u8::deserialize(&mut reader)?;
138        let coupled_count = u8::deserialize(&mut reader)?;
139        let channel_mapping = reader.try_read(seed as usize)?;
140
141        Ok(Self {
142            stream_count,
143            coupled_count,
144            channel_mapping,
145        })
146    }
147}
148
149impl Serialize for ChannelMappingTable<'_> {
150    fn serialize<W>(&self, mut writer: W) -> std::io::Result<()>
151    where
152        W: std::io::Write,
153    {
154        self.stream_count.serialize(&mut writer)?;
155        self.coupled_count.serialize(&mut writer)?;
156        self.channel_mapping.serialize(&mut writer)?;
157
158        Ok(())
159    }
160}
161
162impl IsoSized for ChannelMappingTable<'_> {
163    fn size(&self) -> usize {
164        1 + 1 + self.channel_mapping.len()
165    }
166}