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