Skip to main content

mp4_atom/
atom.rs

1use std::io::Read;
2
3use crate::*;
4
5/// A helper to encode/decode a known atom type.
6pub trait Atom: Sized {
7    const KIND: FourCC;
8
9    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self>;
10    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()>;
11
12    /// Either logs or returns an error depending on the environment/flag.
13    fn decode_unknown(atom: &crate::Any) -> Result<()> {
14        crate::decode_unknown(atom, Self::KIND)
15    }
16}
17
18impl<T: Atom> Encode for T {
19    fn encode<B: BufMut>(&self, buf: &mut B) -> Result<()> {
20        let start = buf.len();
21
22        // Encode a 0 for the size, we'll come back to it later
23        0u32.encode(buf)?;
24        Self::KIND.encode(buf)?;
25        self.encode_body(buf)?;
26
27        // Update the size field
28        // TODO support sizes larger than u32 (4GB)
29        let size: u32 = (buf.len() - start)
30            .try_into()
31            .map_err(|_| Error::TooLarge(T::KIND))?;
32
33        buf.set_slice(start, &size.to_be_bytes());
34
35        Ok(())
36    }
37}
38
39impl<T: Atom> Decode for T {
40    fn decode<B: Buf>(buf: &mut B) -> Result<Self> {
41        Self::decode_maybe(buf)?.ok_or(Error::OutOfBounds)
42    }
43}
44
45impl<T: Atom> DecodeMaybe for T {
46    fn decode_maybe<B: Buf>(buf: &mut B) -> Result<Option<Self>> {
47        // Decode the header from a view so an incomplete atom leaves the
48        // caller's buffer untouched.
49        let remaining = buf.remaining();
50        let mut peek = buf.slice(remaining);
51        let header = match Header::decode_maybe(&mut peek)? {
52            Some(header) => header,
53            None => return Ok(None),
54        };
55
56        let size = header.size.unwrap_or(peek.remaining());
57        if size > peek.remaining() {
58            return Ok(None);
59        }
60
61        buf.advance(remaining - peek.remaining());
62        let body = &mut buf.slice(size);
63
64        let atom = match Self::decode_body(body) {
65            Ok(atom) => atom,
66            Err(Error::OutOfBounds) => return Err(Error::OverDecode(T::KIND)),
67            Err(Error::ShortRead) => return Err(Error::UnderDecode(T::KIND)),
68            Err(err) => return Err(err),
69        };
70
71        if body.has_remaining() {
72            return Err(Error::UnderDecode(T::KIND));
73        }
74
75        buf.advance(size);
76
77        Ok(Some(atom))
78    }
79}
80
81impl<T: Atom> ReadFrom for T {
82    fn read_from<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
83        <Option<T> as ReadFrom>::read_from(r)?.ok_or(Error::MissingBox(T::KIND))
84    }
85}
86
87impl<T: Atom> ReadFrom for Option<T> {
88    fn read_from<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
89        let header = match <Option<Header> as ReadFrom>::read_from(r)? {
90            Some(header) => header,
91            None => return Ok(None),
92        };
93
94        let body = &mut header.read_body(r)?;
95
96        let atom = match T::decode_body(body) {
97            Ok(atom) => atom,
98            Err(Error::OutOfBounds) => return Err(Error::OverDecode(T::KIND)),
99            Err(Error::ShortRead) => return Err(Error::UnderDecode(T::KIND)),
100            Err(err) => return Err(err),
101        };
102
103        if body.has_remaining() {
104            return Err(Error::UnderDecode(T::KIND));
105        }
106
107        Ok(Some(atom))
108    }
109}
110
111impl<T: Atom> ReadUntil for T {
112    fn read_until<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
113        <Option<T> as ReadUntil>::read_until(r)?.ok_or(Error::MissingBox(T::KIND))
114    }
115}
116
117impl<T: Atom> ReadUntil for Option<T> {
118    fn read_until<R: Read + ?Sized>(r: &mut R) -> Result<Self> {
119        while let Some(header) = <Option<Header> as ReadFrom>::read_from(r)? {
120            if header.kind == T::KIND {
121                let body = &mut header.read_body(r)?;
122                return Ok(Some(T::decode_atom(&header, body)?));
123            }
124        }
125
126        Ok(None)
127    }
128}
129
130impl<T: Atom> DecodeAtom for T {
131    fn decode_atom<B: Buf>(header: &Header, buf: &mut B) -> Result<T> {
132        if header.kind != T::KIND {
133            return Err(Error::UnexpectedBox(header.kind));
134        }
135
136        let size = header.size.unwrap_or(buf.remaining());
137        if size > buf.remaining() {
138            return Err(Error::OutOfBounds);
139        }
140
141        let body = &mut buf.slice(size);
142
143        let atom = match T::decode_body(body) {
144            Ok(atom) => atom,
145            Err(Error::OutOfBounds) => return Err(Error::OverDecode(T::KIND)),
146            Err(Error::ShortRead) => return Err(Error::UnderDecode(T::KIND)),
147            Err(err) => return Err(err),
148        };
149
150        if body.has_remaining() {
151            return Err(Error::UnderDecode(T::KIND));
152        }
153
154        buf.advance(size);
155
156        Ok(atom)
157    }
158}
159
160impl<T: Atom> ReadAtom for T {
161    fn read_atom<R: Read + ?Sized>(header: &Header, r: &mut R) -> Result<Self> {
162        if header.kind != T::KIND {
163            return Err(Error::UnexpectedBox(header.kind));
164        }
165
166        let body = &mut header.read_body(r)?;
167        Self::decode_atom(header, body)
168    }
169}
170
171// A helper for generating nested atoms.
172/* example:
173nested! {
174    required: [ Mvhd ],
175    optional: [ Meta, Mvex, Udta ],
176    multiple: [ Trak ],
177};
178*/
179
180macro_rules! nested {
181    (required: [$($required:ident),*$(,)?], optional: [$($optional:ident),*$(,)?], multiple: [$($multiple:ident),*$(,)?],) => {
182        pastey::paste! {
183            fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
184                $( let mut [<$required:lower>] = None;)*
185                $( let mut [<$optional:lower>] = None;)*
186                $( let mut [<$multiple:lower>] = Vec::new();)*
187
188                while let Some(atom) = Any::decode_maybe(buf)? {
189                    match atom {
190                        $(Any::$required(atom) => {
191                            if [<$required:lower>].is_some() {
192                                return Err(Error::DuplicateBox($required::KIND));
193                            }
194                            [<$required:lower>] = Some(atom);
195                        },)*
196                        $(Any::$optional(atom) => {
197                            if [<$optional:lower>].is_some() {
198                                return Err(Error::DuplicateBox($optional::KIND));
199                            }
200                            [<$optional:lower>] = Some(atom);
201                        },)*
202                        $(Any::$multiple(atom) => {
203                            [<$multiple:lower>].push(atom.into());
204                        },)*
205                        Any::Skip(atom) => tracing::debug!(size = atom.zeroed.size, "skipping skip box"),
206                        Any::Free(atom) => tracing::debug!(size = atom.zeroed.size, "skipping free box"),
207                        unknown => Self::decode_unknown(&unknown)?,
208                    }
209                }
210                // Tolerate a sub-header padding remainder (e.g. a QuickTime zero
211                // terminator) after the child boxes.
212                skip_trailing_padding(buf);
213
214                Ok(Self {
215                    $([<$required:lower>]: [<$required:lower>].ok_or(Error::MissingBox($required::KIND))? ,)*
216                    $([<$optional:lower>],)*
217                    $([<$multiple:lower>],)*
218                })
219            }
220
221            fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
222                $( self.[<$required:lower>].encode(buf)?; )*
223                $( self.[<$optional:lower>].encode(buf)?; )*
224                $( self.[<$multiple:lower>].iter().map(|x| x.encode(buf)).collect::<Result<()>>()?; )*
225
226                Ok(())
227            }
228        }
229    };
230}
231
232pub(crate) use nested;