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

1use crate::*;
2
3// See ETSI TS 102 366 V1.4.1 (2017-09) for details of AC-3 and EAC-3
4
5#[derive(Debug, Clone, PartialEq, Eq)]
6#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
7pub struct Eac3 {
8    pub audio: Audio,
9    pub dec3: Ec3SpecificBox,
10}
11
12impl Atom for Eac3 {
13    const KIND: FourCC = FourCC::new(b"ec-3");
14
15    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
16        let audio = Audio::decode(buf)?;
17
18        let mut dec3 = None;
19
20        while let Some(atom) = Any::decode_maybe(buf)? {
21            match atom {
22                Any::Ec3SpecificBox(atom) => dec3 = atom.into(),
23                _ => tracing::warn!("unknown atom: {:?}", atom),
24            }
25        }
26
27        Ok(Self {
28            audio,
29            dec3: dec3.ok_or(Error::MissingBox(Ec3SpecificBox::KIND))?,
30        })
31    }
32
33    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
34        self.audio.encode(buf)?;
35        self.dec3.encode(buf)?;
36        Ok(())
37    }
38}
39
40// EAC-3 specific data
41#[derive(Debug, Clone, PartialEq, Eq)]
42#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
43pub struct Ec3IndependentSubstream {
44    pub fscod: u8,
45    pub bsid: u8,
46    pub asvc: bool,
47    pub bsmod: u8,
48    pub acmod: u8,
49    pub lfeon: bool,
50    pub num_dep_sub: u8,
51    pub chan_loc: Option<u16>,
52}
53
54#[derive(Debug, Clone, PartialEq, Eq)]
55#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
56pub struct Ec3SpecificBox {
57    pub data_rate: u16,
58    pub substreams: Vec<Ec3IndependentSubstream>,
59}
60
61impl Atom for Ec3SpecificBox {
62    const KIND: FourCC = FourCC::new(b"dec3");
63
64    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
65        let header = u16::decode(buf)?;
66        let data_rate = header >> 3;
67        let num_ind_sub = header & 0b111;
68        let mut substreams = Vec::with_capacity(num_ind_sub as usize + 1);
69        for _ in 0..num_ind_sub + 1 {
70            let b0 = u8::decode(buf)?;
71            let fscod = b0 >> 6;
72            let bsid = (b0 >> 1) & 0b11111;
73            // ignore low bit - reserved
74            let b1 = u8::decode(buf)?;
75            let asvc = (b1 & 0x80) == 0x80;
76            let bsmod = (b1 >> 4) & 0b111;
77            let acmod = (b1 >> 1) & 0b111;
78            let lfeon = (b1 & 0b1) == 0b1;
79            let b2 = u8::decode(buf)?;
80            // ignore next three bits
81            let num_dep_sub = (b2 >> 1) & 0b1111;
82            if num_dep_sub > 0 {
83                let b3 = u8::decode(buf)? as u16;
84                let chan_loc = ((b2 as u16 & 0x01) << 8) | b3;
85                substreams.push(Ec3IndependentSubstream {
86                    fscod,
87                    bsid,
88                    asvc,
89                    bsmod,
90                    acmod,
91                    lfeon,
92                    num_dep_sub,
93                    chan_loc: Some(chan_loc),
94                });
95            } else {
96                // ignore last bit in b2
97                substreams.push(Ec3IndependentSubstream {
98                    fscod,
99                    bsid,
100                    asvc,
101                    bsmod,
102                    acmod,
103                    lfeon,
104                    num_dep_sub,
105                    chan_loc: None,
106                });
107            }
108        }
109
110        // reserved bits may follow
111        buf.advance(buf.remaining());
112        Ok(Self {
113            data_rate,
114            substreams,
115        })
116    }
117
118    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
119        let header = self.data_rate << 3 | self.substreams.len().saturating_sub(1) as u16;
120        header.encode(buf)?;
121        for substream in &self.substreams {
122            // low bit is reserved = 0
123            let b = (substream.fscod << 6) | (substream.bsid << 1);
124            b.encode(buf)?;
125            let b = (if substream.asvc { 0x80 } else { 0x00 })
126                | (substream.bsmod << 4)
127                | (substream.acmod << 1)
128                | (if substream.lfeon { 0x01 } else { 0x00 });
129            b.encode(buf)?;
130            if substream.num_dep_sub > 0 {
131                let b: u16 =
132                    ((substream.num_dep_sub as u16) << 9) | (substream.chan_loc.unwrap_or(0u16));
133                b.encode(buf)?;
134            } else {
135                // high and low bits are reserved = 0
136                let b = substream.num_dep_sub << 1;
137                b.encode(buf)?;
138            }
139        }
140        Ok(())
141    }
142}
143
144#[cfg(test)]
145mod tests {
146    use super::*;
147
148    // EAC-3 metadata block only
149    const ENCODED_EAC3: &[u8] = &[
150        0x00, 0x00, 0x00, 0x31, 0x65, 0x63, 0x2d, 0x33, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
151        0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x00, 0x00,
152        0x00, 0x00, 0xac, 0x44, 0x00, 0x00, 0x00, 0x00, 0x00, 0x0d, 0x64, 0x65, 0x63, 0x33, 0x5f,
153        0xc0, 0x60, 0x04, 0x00,
154    ];
155
156    #[test]
157    fn test_eac3_decode() {
158        let buf: &mut std::io::Cursor<&[u8]> = &mut std::io::Cursor::new(ENCODED_EAC3);
159
160        let eac3 = Eac3::decode(buf).expect("failed to decode eac-3");
161
162        assert_eq!(
163            eac3,
164            Eac3 {
165                audio: Audio {
166                    data_reference_index: 1,
167                    channel_count: 2,
168                    sample_size: 16,
169                    sample_rate: 44100.into()
170                },
171                dec3: Ec3SpecificBox {
172                    data_rate: 3064,
173                    substreams: vec![Ec3IndependentSubstream {
174                        fscod: 1,
175                        bsid: 16,
176                        asvc: false,
177                        bsmod: 0,
178                        acmod: 2,
179                        lfeon: false,
180                        num_dep_sub: 0,
181                        chan_loc: None
182                    }]
183                }
184            }
185        );
186    }
187
188    #[test]
189    fn test_eac3_encode() {
190        let eac3 = Eac3 {
191            audio: Audio {
192                data_reference_index: 1,
193                channel_count: 2,
194                sample_size: 16,
195                sample_rate: 44100.into(),
196            },
197            dec3: Ec3SpecificBox {
198                data_rate: 3064,
199                substreams: vec![Ec3IndependentSubstream {
200                    fscod: 1,
201                    bsid: 16,
202                    asvc: false,
203                    bsmod: 0,
204                    acmod: 2,
205                    lfeon: false,
206                    num_dep_sub: 0,
207                    chan_loc: None,
208                }],
209            },
210        };
211
212        let mut buf = Vec::new();
213        eac3.encode(&mut buf).unwrap();
214
215        assert_eq!(buf.as_slice(), ENCODED_EAC3);
216    }
217
218    #[test]
219    fn test_eac3_with_dependent_substreams() {
220        // Test case with dependent substreams (num_dep_sub > 0)
221        let eac3 = Eac3 {
222            audio: Audio {
223                data_reference_index: 1,
224                channel_count: 6,
225                sample_size: 16,
226                sample_rate: 48000.into(),
227            },
228            dec3: Ec3SpecificBox {
229                data_rate: 768,
230                substreams: vec![Ec3IndependentSubstream {
231                    fscod: 0, // 48 kHz
232                    bsid: 16,
233                    asvc: true,
234                    bsmod: 1,
235                    acmod: 7, // 3/2 (L, C, R, Ls, Rs)
236                    lfeon: true,
237                    num_dep_sub: 2,
238                    chan_loc: Some(0x1FF),
239                }],
240            },
241        };
242
243        // Encode
244        let mut buf = Vec::new();
245        eac3.encode(&mut buf).unwrap();
246
247        // Decode and verify round-trip
248        let mut cursor = std::io::Cursor::new(&buf);
249        let decoded = Eac3::decode(&mut cursor).expect("failed to decode eac-3");
250
251        assert_eq!(decoded, eac3);
252        assert_eq!(decoded.dec3.substreams[0].chan_loc, Some(0x1FF));
253    }
254
255    #[test]
256    fn test_eac3_multiple_substreams() {
257        // Test case with multiple independent substreams
258        let eac3 = Eac3 {
259            audio: Audio {
260                data_reference_index: 1,
261                channel_count: 8,
262                sample_size: 16,
263                sample_rate: 48000.into(),
264            },
265            dec3: Ec3SpecificBox {
266                data_rate: 1536,
267                substreams: vec![
268                    Ec3IndependentSubstream {
269                        fscod: 0, // 48 kHz
270                        bsid: 16,
271                        asvc: false,
272                        bsmod: 0,
273                        acmod: 7, // 3/2
274                        lfeon: true,
275                        num_dep_sub: 0,
276                        chan_loc: None,
277                    },
278                    Ec3IndependentSubstream {
279                        fscod: 0, // 48 kHz
280                        bsid: 16,
281                        asvc: false,
282                        bsmod: 0,
283                        acmod: 2, // 2/0 (L, R)
284                        lfeon: false,
285                        num_dep_sub: 0,
286                        chan_loc: None,
287                    },
288                ],
289            },
290        };
291
292        // Encode
293        let mut buf = Vec::new();
294        eac3.encode(&mut buf).unwrap();
295
296        // Decode and verify round-trip
297        let mut cursor = std::io::Cursor::new(&buf);
298        let decoded = Eac3::decode(&mut cursor).expect("failed to decode eac-3");
299
300        assert_eq!(decoded, eac3);
301        assert_eq!(decoded.dec3.substreams.len(), 2);
302    }
303
304    #[test]
305    fn test_eac3_with_reserved_bits() {
306        // Test case with reserved bits following the substream data
307        // This simulates a dec3 box with extra padding/reserved bits at the end
308        let encoded_with_reserved: &[u8] = &[
309            0x00, 0x00, 0x00, 0x33, 0x65, 0x63, 0x2d, 0x33, // ec-3 atom header (size=51)
310            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, // audio sample entry
311            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // audio sample entry
312            0x00, 0x02, 0x00, 0x10, 0x00, 0x00, 0x00, 0x00, // audio sample entry
313            0xac, 0x44, 0x00, 0x00, // audio sample entry
314            0x00, 0x00, 0x00, 0x0f, 0x64, 0x65, 0x63, 0x33, // dec3 header (size 15 bytes)
315            0x5f, 0xc0, // header: data_rate=3064, num_ind_sub=0
316            0x60, 0x04, 0x00, // substream data
317            0xAB, 0xCD, // extra reserved bits (2 bytes)
318        ];
319
320        let mut cursor = std::io::Cursor::new(encoded_with_reserved);
321        let decoded = Eac3::decode(&mut cursor).expect("failed to decode eac-3 with reserved bits");
322
323        // Should decode successfully and ignore the reserved bits
324        assert_eq!(
325            decoded,
326            Eac3 {
327                audio: Audio {
328                    data_reference_index: 1,
329                    channel_count: 2,
330                    sample_size: 16,
331                    sample_rate: 44100.into()
332                },
333                dec3: Ec3SpecificBox {
334                    data_rate: 3064,
335                    substreams: vec![Ec3IndependentSubstream {
336                        fscod: 1,
337                        bsid: 16,
338                        asvc: false,
339                        bsmod: 0,
340                        acmod: 2,
341                        lfeon: false,
342                        num_dep_sub: 0,
343                        chan_loc: None
344                    }]
345                }
346            }
347        );
348    }
349}