Skip to main content

mp4_edit/
atom.rs

1/*!
2 * This mod is concerned with mp4 atoms and how to {de}serialize them.
3*/
4
5#[cfg(test)]
6pub mod test_utils;
7pub(crate) mod util;
8
9pub(crate) mod atom_ref;
10pub mod container;
11pub mod fourcc;
12pub mod iter;
13pub mod leaf;
14
15use bon::bon;
16
17use crate::{
18    atom::util::parser::rest_vec, parser::ParseAtomData, writer::SerializeAtom, ParseError,
19};
20
21pub use self::{container::*, fourcc::*, leaf::*};
22
23/// Represents raw atom bytes.
24#[derive(Clone)]
25pub struct RawData {
26    atom_type: FourCC,
27    data: Vec<u8>,
28}
29
30impl RawData {
31    pub fn new(atom_type: FourCC, data: Vec<u8>) -> Self {
32        Self { atom_type, data }
33    }
34
35    pub fn as_slice(&self) -> &[u8] {
36        &self.data
37    }
38
39    pub fn to_vec(self) -> Vec<u8> {
40        self.data
41    }
42}
43
44impl ParseAtomData for RawData {
45    fn parse_atom_data(atom_type: FourCC, input: &[u8]) -> Result<Self, ParseError> {
46        use crate::atom::util::parser::stream;
47        use winnow::Parser;
48        let data = rest_vec.parse(stream(input))?;
49        Ok(RawData::new(atom_type, data))
50    }
51}
52
53impl SerializeAtom for RawData {
54    fn atom_type(&self) -> FourCC {
55        self.atom_type
56    }
57
58    fn into_body_bytes(self) -> Vec<u8> {
59        self.data
60    }
61}
62
63impl std::fmt::Debug for RawData {
64    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
65        write!(f, "[u8; {}]", self.data.len())
66    }
67}
68
69#[derive(Debug, Clone)]
70pub struct AtomHeader {
71    pub atom_type: FourCC,
72    pub offset: usize,
73    pub header_size: usize,
74    pub data_size: usize,
75}
76
77impl AtomHeader {
78    pub fn new(atom_type: impl Into<FourCC>) -> Self {
79        Self {
80            atom_type: atom_type.into(),
81            offset: 0,
82            header_size: 0,
83            data_size: 0,
84        }
85    }
86
87    pub fn location(&self) -> (usize, usize) {
88        (self.offset, self.header_size + self.data_size)
89    }
90
91    pub fn atom_size(&self) -> usize {
92        self.header_size + self.data_size
93    }
94}
95
96/// Represents a tree of mp4 atoms. Container atoms usually don't have [`Self::data`] (except for e.g. [meta]),
97/// and leaf atoms don't have any [`Self::children`].
98///
99/// This structure allows us to represent any tree of mp4 atoms (boxes), even ones we don't (yet) support (via [RawData]).
100#[derive(Debug, Clone)]
101pub struct Atom {
102    pub header: AtomHeader,
103    pub data: Option<AtomData>,
104    pub children: Vec<Atom>,
105}
106
107#[bon]
108impl Atom {
109    #[builder]
110    pub fn new(
111        header: AtomHeader,
112        #[builder(into)] data: Option<AtomData>,
113        #[builder(default = Vec::new())] children: Vec<Atom>,
114    ) -> Self {
115        Self {
116            header,
117            data,
118            children,
119        }
120    }
121
122    /// Recursively retains only the atoms that satisfy the predicate,
123    /// one level of depth at a time (least to most nested).
124    pub fn children_flat_retain_mut<P>(&mut self, mut pred: P)
125    where
126        P: FnMut(&mut Atom) -> bool,
127    {
128        let mut current_level = vec![self];
129
130        while !current_level.is_empty() {
131            let mut next_level = Vec::new();
132
133            for atom in current_level {
134                // Apply retain to this atom's children
135                atom.children.retain_mut(|child| pred(child));
136
137                // Collect remaining children for next level processing
138                for child in &mut atom.children {
139                    next_level.push(child);
140                }
141            }
142
143            current_level = next_level;
144        }
145    }
146}
147
148impl SerializeAtom for Atom {
149    fn atom_type(&self) -> FourCC {
150        self.header.atom_type
151    }
152
153    /// Serialize [Atom]'s body and all children
154    fn into_body_bytes(self) -> Vec<u8> {
155        // Serialize all children
156        let mut children_bytes = Vec::new();
157        for child in self.children {
158            let mut child_bytes = child.into_bytes();
159            children_bytes.append(&mut child_bytes);
160        }
161
162        let mut body = self
163            .data
164            .map(SerializeAtom::into_body_bytes)
165            .unwrap_or_default();
166
167        body.append(&mut children_bytes);
168        body
169    }
170}
171
172macro_rules! define_atom_data {
173    ( $(#[$meta:meta])* $enum:ident { $( $pattern:pat => $variant:ident($struct:ident) ),+ $(,)? } $(,)? ) => {
174        $(#[$meta])*
175        pub enum $enum {
176            $( $variant($struct), )+
177        }
178
179        $(
180            impl From<$struct> for $enum {
181                fn from(atom: $struct) -> Self {
182                    $enum::$variant(atom)
183                }
184            }
185        )+
186
187        impl ParseAtomData for $enum {
188            fn parse_atom_data(
189                atom_type: FourCC,
190                input: &[u8],
191            ) -> Result<Self, ParseError> {
192                match atom_type {
193                    $($pattern => $struct::parse_atom_data(atom_type, input).map($enum::from), )+
194                }
195            }
196        }
197
198        impl SerializeAtom for $enum {
199            fn atom_type(&self) -> FourCC {
200                match self {
201                    $( $enum::$variant(atom) => atom.atom_type(), )+
202                }
203            }
204
205            fn into_body_bytes(self) -> Vec<u8> {
206                match self {
207                    $( $enum::$variant(atom) => atom.into_body_bytes(), )+
208                }
209            }
210        }
211    };
212}
213
214define_atom_data!(
215    /// Represents data contained in an atom (other than children).
216    ///
217    /// Usually only leaf atoms contain data, but some container types such as [meta] have some extra headers.
218    #[derive(Debug, Clone)]
219    AtomData {
220        ftyp::FTYP => FileType(FileTypeAtom),
221        mvhd::MVHD => MovieHeader(MovieHeaderAtom),
222        mdhd::MDHD => MediaHeader(MediaHeaderAtom),
223        elst::ELST => EditList(EditListAtom),
224        hdlr::HDLR => HandlerReference(HandlerReferenceAtom),
225        smhd::SMHD => SoundMediaHeader(SoundMediaHeaderAtom),
226        gmin::GMIN => BaseMediaInfo(BaseMediaInfoAtom),
227        text::TEXT => TextMediaInfo(TextMediaInfoAtom),
228        ilst::ILST => ItemList(ItemListAtom),
229        tkhd::TKHD => TrackHeader(TrackHeaderAtom),
230        stsd::STSD => SampleDescriptionTable(SampleDescriptionTableAtom),
231        tref::TREF => TrackReference(TrackReferenceAtom),
232        dref::DREF => DataReference(DataReferenceAtom),
233        stsz::STSZ => SampleSize(SampleSizeAtom),
234        stco_co64::STCO | stco_co64::CO64 => ChunkOffset(ChunkOffsetAtom),
235        stts::STTS => TimeToSample(TimeToSampleAtom),
236        stsc::STSC => SampleToChunk(SampleToChunkAtom),
237        chpl::CHPL => ChapterList(ChapterListAtom),
238        free::FREE | free::SKIP => Free(FreeAtom),
239        _ => RawData(RawData),
240    },
241);