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