isobmff/
header.rs

1use std::fmt::Debug;
2use std::io;
3
4use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, U24Be, ZeroCopyReader};
5
6use crate::IsoSized;
7
8/// Represents the size of a box.
9///
10/// Use [`Self::size`] to get the size as a number of bytes.
11#[derive(Debug, Clone, Copy, PartialEq, Eq)]
12pub enum BoxSize {
13    /// 32-bit encoded size. Can't be 0 or 1.
14    Short(u32),
15    /// 64-bit encoded size.
16    Long(u64),
17    /// The box this size beongs to goes to the end of the file.
18    ToEnd,
19}
20
21impl BoxSize {
22    /// Returns the size as a number of bytes.
23    ///
24    /// Returns [`None`] if this is a [`BoxSize::ToEnd`].
25    pub fn size(&self) -> Option<usize> {
26        match self {
27            BoxSize::Short(size) => Some(*size as usize),
28            BoxSize::Long(size) => Some(*size as usize),
29            BoxSize::ToEnd => None,
30        }
31    }
32}
33
34impl From<usize> for BoxSize {
35    fn from(value: usize) -> Self {
36        // If the size does not fit in a u32 we use a long size.
37        // 0 and 1 are reserved for special cases, so we have to use long size for them too.
38        if value > u32::MAX as usize || value == 0 || value == 1 {
39            BoxSize::Long(value as u64)
40        } else {
41            BoxSize::Short(value as u32)
42        }
43    }
44}
45
46/// Represents the box type.
47///
48/// Can either be a FourCC value or a user-defined UUID.
49#[derive(Clone, Copy, PartialEq, Eq)]
50pub enum BoxType {
51    /// A FourCC value, which is a 4-byte identifier. Cannot be "uuid".
52    FourCc([u8; 4]),
53    /// A user extended identifier, which is a 16-byte UUID.
54    Uuid(uuid::Uuid),
55}
56
57impl Debug for BoxType {
58    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
59        match self {
60            BoxType::FourCc(fourcc) => f.debug_tuple("FourCc").field(&String::from_utf8_lossy(fourcc)).finish(),
61            BoxType::Uuid(uuid) => f.debug_tuple("Uuid").field(uuid).finish(),
62        }
63    }
64}
65
66impl BoxType {
67    /// Returns `true` if this is a FourCC value that matches the given FourCC.
68    pub fn is_four_cc(&self, four_cc: &[u8; 4]) -> bool {
69        match self {
70            BoxType::FourCc(box_four_cc) => box_four_cc == four_cc,
71            BoxType::Uuid(_) => false,
72        }
73    }
74}
75
76impl From<BoxType> for uuid::Uuid {
77    fn from(box_type: BoxType) -> Self {
78        match box_type {
79            BoxType::FourCc(fourcc) => {
80                #[rustfmt::skip]
81                let bytes = [
82                    fourcc[0], fourcc[1], fourcc[2], fourcc[3],
83                    0x00, 0x11, 0x00, 0x10, 0x80, 0x00, 0x00, 0xAA, 0x00, 0x38, 0x9B, 0x71,
84                ];
85                uuid::Uuid::from_bytes(bytes)
86            }
87            BoxType::Uuid(uuid) => uuid,
88        }
89    }
90}
91
92/// Represents the header of any box.
93///
94/// Every ISOBMFF box starts with this header, even boxes inheriting from `FullBox`.
95/// Please use [`FullBoxHeader`] in combination with this to represent full boxes,
96/// as [`FullBoxHeader`] only contains the `version` and `flags` field.
97#[derive(Debug, PartialEq, Eq, Clone)]
98pub struct BoxHeader {
99    /// An integer that specifies the number of bytes in this box, including all its fields and contained
100    /// boxes.
101    pub size: BoxSize,
102    /// Identifies the box type.
103    pub box_type: BoxType,
104}
105
106impl IsoSized for BoxHeader {
107    fn size(&self) -> usize {
108        let mut size = 4 + 4; // size + type
109
110        if let BoxSize::Long(_) = self.size {
111            size += 8; // large size
112        }
113
114        if let BoxType::Uuid(_) = self.box_type {
115            size += 16; // usertype
116        }
117
118        size
119    }
120}
121
122impl BoxHeader {
123    /// Uses the size stored in this header to calculate the size of the payload.
124    ///
125    /// Returns `None` if the box size is [`BoxSize::ToEnd`], as the payload size cannot be determined.
126    pub fn payload_size(&self) -> Option<usize> {
127        let header_size = self.size();
128        Some(self.size.size()?.saturating_sub(header_size))
129    }
130}
131
132impl<'a> Deserialize<'a> for BoxHeader {
133    fn deserialize<R>(mut reader: R) -> io::Result<Self>
134    where
135        R: ZeroCopyReader<'a>,
136    {
137        let size = u32::deserialize(&mut reader)?;
138        let box_type = u32::deserialize(&mut reader)?.to_be_bytes();
139
140        let size = match size {
141            0 => BoxSize::ToEnd,
142            1 => {
143                let size = u64::deserialize(&mut reader)?;
144                BoxSize::Long(size)
145            }
146            _ => BoxSize::Short(size),
147        };
148
149        let box_type = if box_type == *b"uuid" {
150            let uuid = u128::deserialize(&mut reader)?;
151            let uuid = uuid::Uuid::from_u128(uuid);
152            BoxType::Uuid(uuid)
153        } else {
154            BoxType::FourCc(box_type)
155        };
156
157        Ok(Self { size, box_type })
158    }
159}
160
161impl<'a> DeserializeSeed<'a, BoxHeader> for BoxHeader {
162    fn deserialize_seed<R>(_reader: R, seed: BoxHeader) -> io::Result<Self>
163    where
164        R: ZeroCopyReader<'a>,
165    {
166        Ok(seed)
167    }
168}
169
170impl Serialize for BoxHeader {
171    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
172    where
173        W: std::io::Write,
174    {
175        let size = match self.size {
176            BoxSize::Short(size) => size,
177            BoxSize::Long(_) => 1,
178            BoxSize::ToEnd => 0,
179        };
180        size.serialize(&mut writer)?;
181
182        let box_type = match &self.box_type {
183            BoxType::FourCc(fourcc) => fourcc,
184            BoxType::Uuid(_) => b"uuid",
185        };
186        box_type.serialize(&mut writer)?;
187
188        if let BoxSize::Long(size) = self.size {
189            size.serialize(&mut writer)?;
190        }
191
192        if let BoxType::Uuid(uuid) = &self.box_type {
193            uuid.as_u128().to_be_bytes().serialize(&mut writer)?;
194        }
195
196        Ok(())
197    }
198}
199
200/// Contains the `version` and `flags` fields.
201///
202/// **Attention**: This does NOT represent the `FullBoxHeader` defined by ISO/IEC 14496-12 - 4.
203/// This struct only contains the additional fields (compared to [`BoxHeader`]), which are `version` and `flags`.
204/// That means that every box, even boxes inheriting from `FullBox`, should contain the [`BoxHeader`] as well.
205#[derive(Debug, PartialEq, Eq, Clone, Default)]
206pub struct FullBoxHeader {
207    /// Is an integer that specifies the version of this format of the box.
208    pub version: u8,
209    /// A map of flags.
210    pub flags: U24Be,
211}
212
213impl IsoSized for FullBoxHeader {
214    fn size(&self) -> usize {
215        1 // version
216        + 3 // flags
217    }
218}
219
220impl<'a> Deserialize<'a> for FullBoxHeader {
221    fn deserialize<R>(mut reader: R) -> io::Result<Self>
222    where
223        R: ZeroCopyReader<'a>,
224    {
225        let version = u8::deserialize(&mut reader)?;
226        let flags = U24Be::deserialize(&mut reader)?;
227
228        Ok(Self { version, flags })
229    }
230}
231
232impl Serialize for FullBoxHeader {
233    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
234    where
235        W: std::io::Write,
236    {
237        self.version.serialize(&mut writer)?;
238        self.flags.serialize(&mut writer)?;
239
240        Ok(())
241    }
242}