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