Skip to main content

mp4_edit/atom/leaf/
smhd.rs

1use crate::{atom::FourCC, parser::ParseAtomData, writer::SerializeAtom, ParseError};
2
3pub const SMHD: FourCC = FourCC::new(b"smhd");
4
5#[derive(Debug, Clone, Default)]
6pub struct SoundMediaHeaderAtom {
7    /// Version of the smhd atom format (0)
8    pub version: u8,
9    /// Flags for the smhd atom (usually all zeros)
10    pub flags: [u8; 3],
11    /// Audio balance (fixed-point 8.8 format, 0.0 = center)
12    /// Negative values favor left channel, positive favor right
13    pub balance: f32,
14    /// Reserved field
15    pub reserved: [u8; 2],
16}
17
18impl ParseAtomData for SoundMediaHeaderAtom {
19    fn parse_atom_data(atom_type: FourCC, input: &[u8]) -> Result<Self, ParseError> {
20        crate::atom::util::parser::assert_atom_type!(atom_type, SMHD);
21        use crate::atom::util::parser::stream;
22        use winnow::Parser;
23        Ok(parser::parse_smhd_data.parse(stream(input))?)
24    }
25}
26
27impl SerializeAtom for SoundMediaHeaderAtom {
28    fn atom_type(&self) -> FourCC {
29        SMHD
30    }
31
32    fn into_body_bytes(self) -> Vec<u8> {
33        serializer::serialize_smhd_data(self)
34    }
35}
36
37mod serializer {
38    use crate::atom::util::serializer::fixed_point_8x8;
39
40    use super::SoundMediaHeaderAtom;
41
42    pub fn serialize_smhd_data(smhd: SoundMediaHeaderAtom) -> Vec<u8> {
43        let mut data = Vec::new();
44
45        data.push(smhd.version);
46        data.extend(smhd.flags);
47        data.extend(fixed_point_8x8(smhd.balance));
48        data.extend(smhd.reserved);
49
50        data
51    }
52}
53
54mod parser {
55    use winnow::{
56        combinator::{seq, trace},
57        error::StrContext,
58        ModalResult, Parser,
59    };
60
61    use super::SoundMediaHeaderAtom;
62    use crate::atom::util::parser::{byte_array, fixed_point_8x8, version, Stream};
63
64    pub fn parse_smhd_data(input: &mut Stream<'_>) -> ModalResult<SoundMediaHeaderAtom> {
65        trace(
66            "smhd",
67            seq!(SoundMediaHeaderAtom {
68                version: version,
69                flags: byte_array.context(StrContext::Label("flags")),
70                balance: fixed_point_8x8.context(StrContext::Label("balance")),
71                reserved: byte_array.context(StrContext::Label("reserved")),
72            })
73            .context(StrContext::Label("chpl")),
74        )
75        .parse_next(input)
76    }
77}
78
79#[cfg(test)]
80mod tests {
81    use super::*;
82    use crate::atom::test_utils::test_atom_roundtrip;
83
84    /// Test round-trip for all available smhd test data files
85    #[test]
86    fn test_smhd_roundtrip() {
87        test_atom_roundtrip::<SoundMediaHeaderAtom>(SMHD);
88    }
89}