Skip to main content

mp4_edit/atom/leaf/
ftyp.rs

1use bon::Builder;
2
3use crate::{
4    atom::{atom_ref, FourCC},
5    parser::ParseAtomData,
6    writer::SerializeAtom,
7    AtomData, ParseError,
8};
9
10pub const FTYP: FourCC = FourCC::new(b"ftyp");
11
12#[derive(Debug, Clone, Copy)]
13pub struct FtypAtomRef<'a>(pub(crate) atom_ref::AtomRef<'a>);
14
15impl<'a> FtypAtomRef<'a> {
16    pub fn data(&self) -> Option<&'a FileTypeAtom> {
17        self.0
18            .inner()
19            .and_then(|ftyp| ftyp.data.as_ref())
20            .and_then(|data| match data {
21                AtomData::FileType(data) => Some(data),
22                _ => None,
23            })
24    }
25}
26
27#[derive(Debug)]
28pub struct FtypAtomRefMut<'a>(pub(crate) atom_ref::AtomRefMut<'a>);
29
30impl<'a> FtypAtomRefMut<'a> {
31    pub fn as_ref(&self) -> FtypAtomRef<'_> {
32        FtypAtomRef(self.0.as_ref())
33    }
34
35    pub fn into_ref(self) -> FtypAtomRef<'a> {
36        FtypAtomRef(self.0.into_ref())
37    }
38
39    pub fn replace(&mut self, data: FileTypeAtom) {
40        self.0.atom_mut().data = Some(data.into());
41    }
42}
43
44/// File Type Atom (ftyp) - ISO/IEC 14496-12
45/// This atom identifies the specifications to which this file complies.
46#[derive(Debug, Clone, Builder)]
47pub struct FileTypeAtom {
48    /// Major brand - identifies the 'best use' of the file
49    #[builder(into)]
50    pub major_brand: FourCC,
51    /// Minor version - an informative integer for the minor version of the major brand
52    #[builder(default = Default::default())]
53    pub minor_version: u32,
54    /// Compatible brands - a list of brands compatible with this file
55    #[builder(default = vec![major_brand], into)]
56    pub compatible_brands: Vec<FourCC>,
57}
58
59impl Default for FileTypeAtom {
60    fn default() -> Self {
61        Self {
62            major_brand: FourCC(*b"isom"),
63            minor_version: 512,
64            compatible_brands: vec![FourCC::from(*b"isom")],
65        }
66    }
67}
68
69impl ParseAtomData for FileTypeAtom {
70    fn parse_atom_data(atom_type: FourCC, input: &[u8]) -> Result<Self, ParseError> {
71        crate::atom::util::parser::assert_atom_type!(atom_type, FTYP);
72        use crate::atom::util::parser::stream;
73        use winnow::Parser;
74        Ok(parser::parse_ftyp_data.parse(stream(input))?)
75    }
76}
77
78mod parser {
79    use winnow::{
80        binary::be_u32,
81        combinator::{repeat, seq, trace},
82        error::StrContext,
83        ModalResult, Parser,
84    };
85
86    use super::FileTypeAtom;
87    use crate::atom::util::parser::{fourcc, Stream};
88
89    pub fn parse_ftyp_data(input: &mut Stream<'_>) -> ModalResult<FileTypeAtom> {
90        trace(
91            "ftyp",
92            seq!(FileTypeAtom {
93                major_brand: fourcc.context(StrContext::Label("major_brand")),
94                minor_version: be_u32.context(StrContext::Label("minor_version")),
95                compatible_brands: repeat(
96                    0..,
97                    fourcc.context(StrContext::Label("compatible_brand"))
98                ),
99            }),
100        )
101        .parse_next(input)
102    }
103}
104
105impl SerializeAtom for FileTypeAtom {
106    fn atom_type(&self) -> FourCC {
107        FTYP
108    }
109
110    fn into_body_bytes(self) -> Vec<u8> {
111        let mut data = Vec::new();
112
113        // Major brand (4 bytes)
114        data.extend_from_slice(&self.major_brand.0);
115
116        // Minor version (4 bytes, big-endian)
117        data.extend_from_slice(&self.minor_version.to_be_bytes());
118
119        // Compatible brands (4 bytes each)
120        for brand in self.compatible_brands {
121            data.extend_from_slice(&brand.0);
122        }
123
124        data
125    }
126}
127
128#[cfg(test)]
129mod tests {
130    use super::*;
131    use crate::atom::test_utils::test_atom_roundtrip;
132
133    /// Test round-trip for all available ftyp test data files
134    #[test]
135    fn test_ftyp_roundtrip() {
136        test_atom_roundtrip::<FileTypeAtom>(FTYP);
137    }
138}