mp4_atom/
atom_ext.rs

1use crate::*;
2
3// Combine the version and flags into a single struct
4// We use a special trait to ensure it's always a u32
5pub(crate) trait Ext: Default {
6    fn encode(&self) -> Result<u32>;
7    fn decode(v: u32) -> Result<Self>;
8}
9
10// Rather than encoding/decoding the header in every atom, use this trait.
11pub(crate) trait AtomExt: Sized {
12    const KIND_EXT: FourCC;
13
14    // One day default associated types will be a thing, then this can be ()
15    type Ext: Ext;
16
17    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext>;
18    fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self>;
19}
20
21impl<T: AtomExt> Atom for T {
22    const KIND: FourCC = Self::KIND_EXT;
23
24    fn decode_body<B: Buf>(buf: &mut B) -> Result<Self> {
25        let ext = Ext::decode(u32::decode(buf)?)?;
26        AtomExt::decode_body_ext(buf, ext)
27    }
28
29    fn encode_body<B: BufMut>(&self, buf: &mut B) -> Result<()> {
30        // Here's the magic, we reserve space for the version/flags first
31        let start = buf.len();
32        0u32.encode(buf)?;
33
34        // That way we can return them as part of the trait, avoiding boilerplate
35        let ext = self.encode_body_ext(buf)?;
36
37        // Go back and update the version/flags
38        let header = ext.encode()?;
39        buf.set_slice(start, &header.to_be_bytes());
40
41        Ok(())
42    }
43}
44
45// Some atoms don't have any version/flags, so we provide a default implementation
46impl Ext for () {
47    fn encode(&self) -> Result<u32> {
48        Ok(0)
49    }
50
51    fn decode(_: u32) -> Result<()> {
52        Ok(())
53    }
54}
55
56// Here's a macro to make life easier:
57/* input:
58ext! {
59    name: Tfdt,
60    versions: [0, 1],
61    flags: {
62        base_data_offset = 0,
63        sample_description_index = 1,
64        default_sample_duration = 3,
65        default_sample_size = 4,
66        default_sample_flags = 5,
67        duration_is_empty = 16,
68        default_base_is_moof = 17,
69    },
70}
71
72output:
73enum TfdtVersion {
74    V0 = 0,
75    V1 = 1,
76}
77
78struct TfdtExt {
79    pub version: TfdtVersion,
80    pub base_data_offset: bool,
81    pub sample_description_index: bool,
82    pub default_sample_duration: bool,
83    pub default_sample_size: bool,
84    pub default_sample_flags: bool,
85    pub duration_is_empty: bool,
86    pub default_base_is_moof: bool,
87}
88*/
89
90macro_rules! ext {
91    (name: $name:ident, versions: [$($version:expr),*], flags: { $($flag:ident = $bit:expr,)* }) => {
92        paste::paste! {
93            #[derive(Debug, Clone, Copy, PartialEq, Eq)]
94            pub(crate) enum [<$name Version>] {
95                $(
96                    [<V $version>] = $version,
97                )*
98            }
99
100            impl TryFrom<u8> for [<$name Version>] {
101                type Error = Error;
102
103                fn try_from(v: u8) -> Result<Self> {
104                    match v {
105                        $(
106                            $version => Ok(Self::[<V $version>]),
107                        )*
108                        _ => Err(Error::UnknownVersion(v)),
109                    }
110                }
111            }
112
113            impl Default for [<$name Version>] {
114                // Hilarious way to return the first version in the list
115                #[allow(unreachable_code)]
116                fn default() -> Self {
117                    $(
118                        return Self::[<V $version>];
119                    )*
120                }
121            }
122
123            #[derive(Debug, Clone, PartialEq, Eq, Default)]
124            pub(crate) struct [<$name Ext>] {
125                pub version: [<$name Version>],
126                $(
127                    pub $flag: bool,
128                )*
129            }
130
131            impl Ext for [<$name Ext>] {
132                fn encode(&self) -> Result<u32>{
133                    Ok((self.version as u32) << 24 $(| (self.$flag as u32) << $bit)*)
134                }
135
136                fn decode(v: u32) -> Result<Self> {
137                    Ok([<$name Ext>] {
138                        version: [<$name Version>]::try_from((v >> 24) as u8)?,
139                        $(
140                            $flag: (v & (1 << $bit)) != 0,
141                        )*
142                    })
143                }
144            }
145
146            // Helper when there are no flags
147            impl From<[<$name Version>]> for [<$name Ext>] {
148                fn from(version: [<$name Version>]) -> Self {
149                    // Not using ..Default::default() to avoid Clippy
150                    let mut ext = Self::default();
151                    ext.version = version;
152                    ext
153                }
154            }
155        }
156    };
157}
158
159pub(crate) use ext;