mp4_atom/moov/trak/mdia/minf/stbl/
subs.rs

1use crate::*;
2
3/// SubSampleInformationBox, ISO/IEC 14496-12:2024 Sect 8.7.7
4#[derive(Debug, Clone, PartialEq, Eq, Default)]
5#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
6pub struct Subs {
7    pub flags: [u8; 3], // flags are codec specific and not defined directly on subs
8    pub entries: Vec<SubsEntry>,
9}
10
11#[derive(Debug, Clone, PartialEq, Eq, Default)]
12#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
13pub struct SubsEntry {
14    pub sample_delta: u32,
15    pub subsamples: Vec<SubsSubsample>,
16}
17
18#[derive(Debug, Clone, PartialEq, Eq, Default)]
19#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
20pub struct SubsSubsample {
21    pub size: SubsSubsampleSize,
22    pub priority: u8,
23    pub discardable: bool,
24    pub codec_specific_parameters: Vec<u8>,
25}
26
27#[derive(Debug, Clone, PartialEq, Eq)]
28#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
29pub enum SubsSubsampleSize {
30    U16(u16),
31    U32(u32),
32}
33impl Default for SubsSubsampleSize {
34    fn default() -> Self {
35        // The precedent set by the `ext!` macro is to set V0 as default. Given that for V0 the
36        // subsample_size is u16, I set 0u16 as the default.
37        Self::U16(0)
38    }
39}
40impl SubsSubsampleSize {
41    pub fn value(&self) -> u32 {
42        match self {
43            Self::U16(n) => u32::from(*n),
44            Self::U32(n) => *n,
45        }
46    }
47}
48
49// We can't use the `ext!` macro to implement `Ext` because we need to keep track of all possible
50// flags. This is because the box doesn't specify any flags directly, and instead:
51// > The semantics of `flags`, if any, shall be supplied for a given coding system. If flags have no
52// > semantics for a given coding system, the flags shall be 0.
53//
54// Therefore, I need to keep all possible flags on the struct, as they may have semantic meaning
55// that we can't know solely based on the definition of subs, but the user may require knowledge of
56// those flags.
57#[derive(Debug, Clone, Copy, PartialEq, Eq, Default)]
58pub(crate) enum SubsVersion {
59    #[default]
60    V0 = 0,
61    V1 = 1,
62}
63
64impl TryFrom<u8> for SubsVersion {
65    type Error = Error;
66
67    fn try_from(value: u8) -> std::result::Result<Self, Self::Error> {
68        match value {
69            0 => Ok(Self::V0),
70            1 => Ok(Self::V1),
71            _ => Err(Error::UnknownVersion(value)),
72        }
73    }
74}
75
76#[derive(Debug, Clone, PartialEq, Eq, Default)]
77pub(crate) struct SubsExt {
78    pub version: SubsVersion,
79    pub flags: [u8; 3],
80}
81
82impl Ext for SubsExt {
83    fn encode(&self) -> Result<u32> {
84        Ok((self.version as u32) << 24
85            | (self.flags[0] as u32) << 16
86            | (self.flags[1] as u32) << 8
87            | (self.flags[2] as u32))
88    }
89
90    fn decode(v: u32) -> Result<Self> {
91        let bytes = v.to_be_bytes();
92        let version = SubsVersion::try_from(bytes[0])?;
93        let flags = [bytes[1], bytes[2], bytes[3]];
94        Ok(Self { version, flags })
95    }
96}
97
98impl AtomExt for Subs {
99    type Ext = SubsExt;
100
101    const KIND_EXT: FourCC = FourCC::new(b"subs");
102
103    fn decode_body_ext<B: Buf>(buf: &mut B, ext: Self::Ext) -> Result<Self> {
104        let flags = ext.flags;
105        let entry_count = u32::decode(buf)?;
106        let mut entries = Vec::with_capacity((entry_count as usize).min(1024));
107        for _ in 0..entry_count {
108            let sample_delta = u32::decode(buf)?;
109            let subsample_count = u16::decode(buf)?;
110            let mut subsamples = Vec::with_capacity(usize::from(subsample_count).min(1024));
111            for _ in 0..subsample_count {
112                let size = if ext.version == SubsVersion::V1 {
113                    SubsSubsampleSize::U32(u32::decode(buf)?)
114                } else {
115                    SubsSubsampleSize::U16(u16::decode(buf)?)
116                };
117                let priority = u8::decode(buf)?;
118                let discardable = u8::decode(buf)? == 1;
119                let codec_specific_parameters = buf.slice(4).to_vec();
120                buf.advance(4);
121                subsamples.push(SubsSubsample {
122                    size,
123                    priority,
124                    discardable,
125                    codec_specific_parameters,
126                });
127            }
128            entries.push(SubsEntry {
129                sample_delta,
130                subsamples,
131            });
132        }
133        Ok(Self { flags, entries })
134    }
135
136    fn encode_body_ext<B: BufMut>(&self, buf: &mut B) -> Result<Self::Ext> {
137        let ext = match &self
138            .entries
139            .first()
140            .and_then(|e| e.subsamples.first())
141            .map(|s| &s.size)
142        {
143            Some(SubsSubsampleSize::U16(_)) => SubsExt {
144                version: SubsVersion::V0,
145                flags: self.flags,
146            },
147            Some(SubsSubsampleSize::U32(_)) => SubsExt {
148                version: SubsVersion::V1,
149                flags: self.flags,
150            },
151            // Should I store the version somewhere so that I can always decode and encode back to
152            // the exact same bytes?
153            None => SubsExt {
154                version: SubsVersion::default(),
155                flags: self.flags,
156            },
157        };
158        (self.entries.len() as u32).encode(buf)?;
159        for entry in &self.entries {
160            entry.sample_delta.encode(buf)?;
161            (entry.subsamples.len() as u16).encode(buf)?;
162            for subsample in &entry.subsamples {
163                match subsample.size {
164                    SubsSubsampleSize::U16(n) => n.encode(buf)?,
165                    SubsSubsampleSize::U32(n) => n.encode(buf)?,
166                }
167                subsample.priority.encode(buf)?;
168                if subsample.discardable {
169                    1u8.encode(buf)?;
170                } else {
171                    0u8.encode(buf)?;
172                }
173                subsample.codec_specific_parameters.encode(buf)?;
174            }
175        }
176        Ok(ext)
177    }
178}
179
180#[cfg(test)]
181mod tests {
182    use super::*;
183    use std::io::Cursor;
184
185    // This example was taken from:
186    // https://mpeggroup.github.io/FileFormatConformance/files/published/uvvu/Solekai007_1920_29_1x1_v7clear.uvu
187    //
188    // I just extracted the bytes for the subs atom location.
189    const SUBS: &[u8] = &[
190        0x00, 0x00, 0x00, 0x16, 0x73, 0x75, 0x62, 0x73, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
191        0x01, 0x00, 0x00, 0x00, 0x01, 0x00, 0x00,
192    ];
193
194    #[test]
195    fn subs_decodes_from_bytes_correctly() {
196        let mut buf = Cursor::new(SUBS);
197        let subs = Subs::decode(&mut buf).expect("subs should decode successfully");
198        assert_eq!(
199            subs,
200            Subs {
201                flags: [0, 0, 0],
202                entries: vec![SubsEntry {
203                    sample_delta: 1,
204                    subsamples: vec![],
205                }],
206            }
207        )
208    }
209
210    // This example was taken from:
211    // https://mpeggroup.github.io/FileFormatConformance/files/published/nalu/hevc/subs_tile_hvc1.mp4
212    //
213    // I just extracted the bytes for the subs atom location and then modified it to make it
214    // shorter.
215    //
216    // I added this example so that I could test subsample decoding/encoding and therefore also have
217    // an encoding test.
218    const SUBS_COMPLEX: &[u8] = &[
219        0x00, 0x00, 0x00, 0x9C, 0x73, 0x75, 0x62, 0x73, 0x00, 0x00, 0x00, 0x02, 0x00, 0x00, 0x00,
220        0x02, 0x00, 0x00, 0x00, 0x00, 0x00, 0x08, 0x0F, 0x97, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
221        0x05, 0x52, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x05, 0xA3, 0x00, 0x00, 0x00, 0x00, 0x00,
222        0x00, 0x0B, 0x88, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x07, 0xA3, 0x00, 0x00, 0x00, 0x00,
223        0x00, 0x00, 0x05, 0xF5, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x4A, 0x00, 0x00, 0x00,
224        0x00, 0x00, 0x00, 0x05, 0x6A, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01,
225        0x00, 0x08, 0x00, 0xC2, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xAB, 0x00, 0x00, 0x00,
226        0x00, 0x00, 0x00, 0x02, 0xE0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x02, 0xDE, 0x00, 0x00,
227        0x00, 0x00, 0x00, 0x00, 0x05, 0xBE, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x01, 0x4A, 0x00,
228        0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x96, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0xF6,
229        0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
230    ];
231
232    #[test]
233    fn subs_decodes_from_bytes_and_encodes_to_bytes_correctly_with_more_complex_example() {
234        let mut buf = Cursor::new(SUBS_COMPLEX);
235        let subs = Subs {
236            flags: [0, 0, 2],
237            entries: vec![
238                SubsEntry {
239                    sample_delta: 0,
240                    subsamples: vec![
241                        SubsSubsample {
242                            size: SubsSubsampleSize::U16(3991),
243                            priority: 0,
244                            discardable: false,
245                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
246                        },
247                        SubsSubsample {
248                            size: SubsSubsampleSize::U16(1362),
249                            priority: 0,
250                            discardable: false,
251                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
252                        },
253                        SubsSubsample {
254                            size: SubsSubsampleSize::U16(1443),
255                            priority: 0,
256                            discardable: false,
257                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
258                        },
259                        SubsSubsample {
260                            size: SubsSubsampleSize::U16(2952),
261                            priority: 0,
262                            discardable: false,
263                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
264                        },
265                        SubsSubsample {
266                            size: SubsSubsampleSize::U16(1955),
267                            priority: 0,
268                            discardable: false,
269                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
270                        },
271                        SubsSubsample {
272                            size: SubsSubsampleSize::U16(1525),
273                            priority: 0,
274                            discardable: false,
275                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
276                        },
277                        SubsSubsample {
278                            size: SubsSubsampleSize::U16(842),
279                            priority: 0,
280                            discardable: false,
281                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
282                        },
283                        SubsSubsample {
284                            size: SubsSubsampleSize::U16(1386),
285                            priority: 0,
286                            discardable: false,
287                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
288                        },
289                    ],
290                },
291                SubsEntry {
292                    sample_delta: 1,
293                    subsamples: vec![
294                        SubsSubsample {
295                            size: SubsSubsampleSize::U16(194),
296                            priority: 0,
297                            discardable: false,
298                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
299                        },
300                        SubsSubsample {
301                            size: SubsSubsampleSize::U16(171),
302                            priority: 0,
303                            discardable: false,
304                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
305                        },
306                        SubsSubsample {
307                            size: SubsSubsampleSize::U16(736),
308                            priority: 0,
309                            discardable: false,
310                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
311                        },
312                        SubsSubsample {
313                            size: SubsSubsampleSize::U16(734),
314                            priority: 0,
315                            discardable: false,
316                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
317                        },
318                        SubsSubsample {
319                            size: SubsSubsampleSize::U16(1470),
320                            priority: 0,
321                            discardable: false,
322                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
323                        },
324                        SubsSubsample {
325                            size: SubsSubsampleSize::U16(330),
326                            priority: 0,
327                            discardable: false,
328                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
329                        },
330                        SubsSubsample {
331                            size: SubsSubsampleSize::U16(150),
332                            priority: 0,
333                            discardable: false,
334                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
335                        },
336                        SubsSubsample {
337                            size: SubsSubsampleSize::U16(1014),
338                            priority: 0,
339                            discardable: false,
340                            codec_specific_parameters: 0u32.to_be_bytes().to_vec(),
341                        },
342                    ],
343                },
344            ],
345        };
346        let decoded = Subs::decode(&mut buf).expect("subs should decode successfully");
347        assert_eq!(subs, decoded);
348        let mut encoded = Vec::new();
349        subs.encode(&mut encoded)
350            .expect("encode should be successful");
351        assert_eq!(SUBS_COMPLEX, &encoded);
352    }
353}