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