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;