mp4_edit/atom/leaf/
ftyp.rs1use 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#[derive(Debug, Clone, Builder)]
47pub struct FileTypeAtom {
48 #[builder(into)]
50 pub major_brand: FourCC,
51 #[builder(default = Default::default())]
53 pub minor_version: u32,
54 #[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 data.extend_from_slice(&self.major_brand.0);
115
116 data.extend_from_slice(&self.minor_version.to_be_bytes());
118
119 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]
135 fn test_ftyp_roundtrip() {
136 test_atom_roundtrip::<FileTypeAtom>(FTYP);
137 }
138}