isobmff/boxes/
sample_groups.rs

1use std::io;
2
3use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, ZeroCopyReader};
4use scuffle_bytes_util::{BytesCow, IoResultExt};
5
6use crate::IsoSized;
7
8/// All defined sample group description entries.
9#[derive(Debug, PartialEq, Eq)]
10pub enum SampleGroupDescriptionEntry<'a> {
11    /// `roll`
12    RollRecovery(RollRecoveryEntry),
13    /// `prol`
14    AudioPreRoll(AudioPreRollEntry),
15    /// `rash`
16    RateShare(RateShareEntry),
17    /// `alst`
18    AlternativeStartup(AlternativeStartupEntry),
19    /// `rap `
20    VisualRandomAccess(VisualRandomAccessEntry),
21    /// `tele`
22    TemporalLevel(TemporalLevelEntry),
23    /// `sap `
24    SAP(SAPEntry),
25    /// `stmi`
26    SampleToMetadataItem(SampleToMetadataItemEntry),
27    /// `drap`
28    VisualDRAP(VisualDRAPEntry),
29    /// `pasr`
30    PixelAspectRatio(PixelAspectRatioEntry),
31    /// `casg`
32    CleanAperture(CleanApertureEntry),
33    /// Unknown entry
34    Unknown {
35        /// The grouping type of the entry
36        grouping_type: [u8; 4],
37        /// The data
38        data: BytesCow<'a>,
39    },
40}
41
42impl<'a> DeserializeSeed<'a, ([u8; 4], Option<u32>)> for SampleGroupDescriptionEntry<'a> {
43    fn deserialize_seed<R>(mut reader: R, seed: ([u8; 4], Option<u32>)) -> io::Result<Self>
44    where
45        R: ZeroCopyReader<'a>,
46    {
47        let (grouping_type, length) = seed;
48
49        match &grouping_type {
50            b"roll" => Ok(Self::RollRecovery(RollRecoveryEntry::deserialize(reader)?)),
51            b"prol" => Ok(Self::AudioPreRoll(AudioPreRollEntry::deserialize(reader)?)),
52            b"rash" => Ok(Self::RateShare(RateShareEntry::deserialize(reader)?)),
53            b"alst" => Ok(Self::AlternativeStartup(AlternativeStartupEntry::deserialize(reader)?)),
54            b"rap " => Ok(Self::VisualRandomAccess(VisualRandomAccessEntry::deserialize(reader)?)),
55            b"tele" => Ok(Self::TemporalLevel(TemporalLevelEntry::deserialize(reader)?)),
56            b"sap " => Ok(Self::SAP(SAPEntry::deserialize(reader)?)),
57            b"stmi" => Ok(Self::SampleToMetadataItem(SampleToMetadataItemEntry::deserialize(reader)?)),
58            b"drap" => Ok(Self::VisualDRAP(VisualDRAPEntry::deserialize(reader)?)),
59            b"pasr" => Ok(Self::PixelAspectRatio(PixelAspectRatioEntry::deserialize(reader)?)),
60            b"casg" => Ok(Self::CleanAperture(CleanApertureEntry::deserialize(reader)?)),
61            _ => {
62                let data = if let Some(length) = length {
63                    reader.try_read(length as usize)?
64                } else {
65                    BytesCow::new()
66                };
67                Ok(Self::Unknown { grouping_type, data })
68            }
69        }
70    }
71}
72
73impl Serialize for SampleGroupDescriptionEntry<'_> {
74    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
75    where
76        W: std::io::Write,
77    {
78        match self {
79            Self::RollRecovery(entry) => entry.serialize(&mut writer),
80            Self::AudioPreRoll(entry) => entry.serialize(&mut writer),
81            Self::RateShare(entry) => entry.serialize(&mut writer),
82            Self::AlternativeStartup(entry) => entry.serialize(&mut writer),
83            Self::VisualRandomAccess(entry) => entry.serialize(&mut writer),
84            Self::TemporalLevel(entry) => entry.serialize(&mut writer),
85            Self::SAP(entry) => entry.serialize(&mut writer),
86            Self::SampleToMetadataItem(entry) => entry.serialize(&mut writer),
87            Self::VisualDRAP(entry) => entry.serialize(&mut writer),
88            Self::PixelAspectRatio(entry) => entry.serialize(&mut writer),
89            Self::CleanAperture(entry) => entry.serialize(&mut writer),
90            Self::Unknown { data, .. } => data.serialize(&mut writer),
91        }
92    }
93}
94
95impl IsoSized for SampleGroupDescriptionEntry<'_> {
96    fn size(&self) -> usize {
97        match self {
98            Self::RollRecovery(entry) => entry.size(),
99            Self::AudioPreRoll(entry) => entry.size(),
100            Self::RateShare(entry) => entry.size(),
101            Self::AlternativeStartup(entry) => entry.size(),
102            Self::VisualRandomAccess(entry) => entry.size(),
103            Self::TemporalLevel(entry) => entry.size(),
104            Self::SAP(entry) => entry.size(),
105            Self::SampleToMetadataItem(entry) => entry.size(),
106            Self::VisualDRAP(entry) => entry.size(),
107            Self::PixelAspectRatio(entry) => entry.size(),
108            Self::CleanAperture(entry) => entry.size(),
109            Self::Unknown { data, .. } => data.len(),
110        }
111    }
112}
113
114/// `VisualRollRecoveryEntry` and `AudioRollRecoveryEntry`
115///
116/// `roll`
117///
118/// ISO/IEC 14496-12 - 10.1
119#[derive(Debug, PartialEq, Eq)]
120pub struct RollRecoveryEntry {
121    roll_distance: i16,
122}
123
124impl<'a> Deserialize<'a> for RollRecoveryEntry {
125    fn deserialize<R>(reader: R) -> io::Result<Self>
126    where
127        R: ZeroCopyReader<'a>,
128    {
129        Ok(Self {
130            roll_distance: i16::deserialize(reader)?,
131        })
132    }
133}
134
135impl Serialize for RollRecoveryEntry {
136    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
137    where
138        W: std::io::Write,
139    {
140        self.roll_distance.serialize(&mut writer)?;
141        Ok(())
142    }
143}
144
145impl IsoSized for RollRecoveryEntry {
146    fn size(&self) -> usize {
147        2 // roll_distance
148    }
149}
150
151/// `AudioPreRollEntry`
152///
153/// `prol`
154///
155/// ISO/IEC 14496-12 - 10.1
156#[derive(Debug, PartialEq, Eq)]
157pub struct AudioPreRollEntry {
158    roll_distance: i16,
159}
160
161impl<'a> Deserialize<'a> for AudioPreRollEntry {
162    fn deserialize<R>(reader: R) -> io::Result<Self>
163    where
164        R: ZeroCopyReader<'a>,
165    {
166        Ok(AudioPreRollEntry {
167            roll_distance: i16::deserialize(reader)?,
168        })
169    }
170}
171
172impl Serialize for AudioPreRollEntry {
173    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
174    where
175        W: std::io::Write,
176    {
177        self.roll_distance.serialize(&mut writer)?;
178        Ok(())
179    }
180}
181
182impl IsoSized for AudioPreRollEntry {
183    fn size(&self) -> usize {
184        2 // roll_distance
185    }
186}
187
188/// Rate share sample group entry
189///
190/// `rash`
191///
192/// ISO/IEC 14496-12 - 10.2
193#[derive(Debug, PartialEq, Eq)]
194pub struct RateShareEntry {
195    operation_point_count: u16,
196    operation_points: Vec<RateShareEntryOperationPoint>,
197    maximum_bitrate: u32,
198    minimum_bitrate: u32,
199    discard_priority: u8,
200}
201
202impl<'a> Deserialize<'a> for RateShareEntry {
203    fn deserialize<R>(mut reader: R) -> io::Result<Self>
204    where
205        R: ZeroCopyReader<'a>,
206    {
207        let operation_point_count = u16::deserialize(&mut reader)?;
208        let mut operation_points = Vec::with_capacity(operation_point_count as usize);
209
210        if operation_point_count == 1 {
211            let target_rate_share = u16::deserialize(&mut reader)?;
212            operation_points.push(RateShareEntryOperationPoint {
213                target_rate_share,
214                available_bitrate: None,
215            });
216        } else {
217            for _ in 0..operation_point_count {
218                operation_points.push(RateShareEntryOperationPoint::deserialize(&mut reader)?);
219            }
220        }
221
222        let maximum_bitrate = u32::deserialize(&mut reader)?;
223        let minimum_bitrate = u32::deserialize(&mut reader)?;
224        let discard_priority = u8::deserialize(&mut reader)?;
225
226        Ok(Self {
227            operation_point_count,
228            operation_points,
229            maximum_bitrate,
230            minimum_bitrate,
231            discard_priority,
232        })
233    }
234}
235
236impl Serialize for RateShareEntry {
237    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
238    where
239        W: std::io::Write,
240    {
241        self.operation_point_count.serialize(&mut writer)?;
242        for operation_point in &self.operation_points {
243            if self.operation_point_count > 1 && operation_point.available_bitrate.is_none() {
244                return Err(io::Error::new(io::ErrorKind::InvalidData, "available_bitrate is required"));
245            }
246            operation_point.serialize(&mut writer)?;
247        }
248        self.maximum_bitrate.serialize(&mut writer)?;
249        self.minimum_bitrate.serialize(&mut writer)?;
250        self.discard_priority.serialize(&mut writer)?;
251        Ok(())
252    }
253}
254
255impl IsoSized for RateShareEntry {
256    fn size(&self) -> usize {
257        2 + self.operation_points.size() + 4 + 4 + 1
258    }
259}
260
261/// Operation point in [`RateShareEntry`].
262#[derive(Debug, PartialEq, Eq)]
263pub struct RateShareEntryOperationPoint {
264    /// An integer. A non-zero value indicates the percentage of available bandwidth that
265    /// should be allocated to the media for each operation point. The value of the first (last) operation
266    /// point applies to lower (higher) available bitrates than the operation point itself. The target
267    /// rate share between operation points is bounded by the target rate shares of the corresponding
268    /// operation points. A zero value indicates that no information on the preferred rate share percentage
269    /// is provided.
270    pub target_rate_share: u16,
271    /// A positive integer that defines an operation point (in kilobits per second). It is the
272    /// total available bitrate that can be allocated in shares to tracks. Each entry shall be greater than the
273    /// previous entry.
274    pub available_bitrate: Option<u32>,
275}
276
277impl<'a> Deserialize<'a> for RateShareEntryOperationPoint {
278    fn deserialize<R>(mut reader: R) -> io::Result<Self>
279    where
280        R: ZeroCopyReader<'a>,
281    {
282        let available_bitrate = u32::deserialize(&mut reader)?;
283        let target_rate_share = u16::deserialize(&mut reader)?;
284
285        Ok(Self {
286            available_bitrate: Some(available_bitrate),
287            target_rate_share,
288        })
289    }
290}
291
292impl Serialize for RateShareEntryOperationPoint {
293    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
294    where
295        W: std::io::Write,
296    {
297        if let Some(available_bitrate) = &self.available_bitrate {
298            available_bitrate.serialize(&mut writer)?;
299        }
300        self.target_rate_share.serialize(&mut writer)?;
301        Ok(())
302    }
303}
304
305impl IsoSized for RateShareEntryOperationPoint {
306    fn size(&self) -> usize {
307        if self.available_bitrate.is_some() {
308            4 + 2 // available_bitrate + target_rate_share
309        } else {
310            2 // target_rate_share
311        }
312    }
313}
314
315/// Alternative startup sequences
316///
317/// `alst`
318///
319/// ISO/IEC 14496-12 - 10.3
320#[derive(Debug, PartialEq, Eq)]
321pub struct AlternativeStartupEntry {
322    roll_count: u16,
323    first_output_sample: u16,
324    sample_offset: Vec<u32>,
325    nums: Vec<AlternativeStartupEntryNums>,
326}
327
328impl<'a> Deserialize<'a> for AlternativeStartupEntry {
329    fn deserialize<R>(mut reader: R) -> io::Result<Self>
330    where
331        R: ZeroCopyReader<'a>,
332    {
333        let roll_count = u16::deserialize(&mut reader)?;
334        let first_output_sample = u16::deserialize(&mut reader)?;
335
336        let mut sample_offset = Vec::with_capacity(roll_count as usize);
337        for _ in 0..roll_count {
338            sample_offset.push(u32::deserialize(&mut reader)?);
339        }
340
341        let mut nums = Vec::new();
342        loop {
343            let Some(num) = AlternativeStartupEntryNums::deserialize(&mut reader).eof_to_none()? else {
344                break;
345            };
346            nums.push(num);
347        }
348
349        Ok(Self {
350            roll_count,
351            first_output_sample,
352            sample_offset,
353            nums,
354        })
355    }
356}
357
358impl Serialize for AlternativeStartupEntry {
359    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
360    where
361        W: std::io::Write,
362    {
363        self.roll_count.serialize(&mut writer)?;
364        self.first_output_sample.serialize(&mut writer)?;
365        for offset in &self.sample_offset {
366            offset.serialize(&mut writer)?;
367        }
368        for num in &self.nums {
369            num.serialize(&mut writer)?;
370        }
371        Ok(())
372    }
373}
374
375impl IsoSized for AlternativeStartupEntry {
376    fn size(&self) -> usize {
377        2 + 2 + self.sample_offset.size() + self.nums.size()
378    }
379}
380
381/// Number of samples in an [`AlternativeStartupEntry`].
382///
383/// Indicates the sample output rate within the
384/// alternative startup sequence. The alternative startup sequence is divided into k consecutive pieces,
385/// where each piece has a constant sample output rate which is unequal to that of the adjacent pieces.
386/// The first piece starts from the sample indicated by `first_output_sample`. `num_output_samples[j]`
387/// indicates the number of the output samples of the j-th piece of the alternative startup sequence.
388/// `num_total_samples[j]` indicates the total number of samples, including those that are not in the
389/// alternative startup sequence, from the first sample in the j-th piece that is output to the earlier one
390/// (in composition order) of the sample that ends the alternative startup sequence and the sample
391/// that immediately precedes the first output sample of the (j+1)th piece.
392#[derive(Debug, PartialEq, Eq)]
393pub struct AlternativeStartupEntryNums {
394    /// `num_output_samples[j]`
395    pub num_output_samples: u16,
396    /// `num_total_samples[j]`
397    pub num_total_samples: u16,
398}
399
400impl<'a> Deserialize<'a> for AlternativeStartupEntryNums {
401    fn deserialize<R>(mut reader: R) -> io::Result<Self>
402    where
403        R: ZeroCopyReader<'a>,
404    {
405        Ok(Self {
406            num_output_samples: u16::deserialize(&mut reader)?,
407            num_total_samples: u16::deserialize(&mut reader)?,
408        })
409    }
410}
411
412impl Serialize for AlternativeStartupEntryNums {
413    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
414    where
415        W: std::io::Write,
416    {
417        self.num_output_samples.serialize(&mut writer)?;
418        self.num_total_samples.serialize(&mut writer)?;
419        Ok(())
420    }
421}
422
423impl IsoSized for AlternativeStartupEntryNums {
424    fn size(&self) -> usize {
425        2 + 2 // num_output_samples + num_total_samples
426    }
427}
428
429/// Random access point (RAP) sample group
430///
431/// `rap `
432///
433/// ISO/IEC 14496-12 - 10.4
434#[derive(Debug, PartialEq, Eq)]
435pub struct VisualRandomAccessEntry {
436    num_leading_samples_known: bool,
437    num_leading_samples: u8,
438}
439
440impl<'a> Deserialize<'a> for VisualRandomAccessEntry {
441    fn deserialize<R>(mut reader: R) -> io::Result<Self>
442    where
443        R: ZeroCopyReader<'a>,
444    {
445        let byte = u8::deserialize(&mut reader)?;
446        let num_leading_samples_known = (byte & 0b1000_0000) != 0;
447        let num_leading_samples = byte & 0b0111_1111;
448
449        Ok(Self {
450            num_leading_samples_known,
451            num_leading_samples,
452        })
453    }
454}
455
456impl Serialize for VisualRandomAccessEntry {
457    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
458    where
459        W: std::io::Write,
460    {
461        let mut byte = (self.num_leading_samples_known as u8) << 7;
462        byte |= self.num_leading_samples;
463        byte.serialize(&mut writer)?;
464        Ok(())
465    }
466}
467
468impl IsoSized for VisualRandomAccessEntry {
469    fn size(&self) -> usize {
470        1 // num_leading_samples_known + num_leading_samples
471    }
472}
473
474/// Temporal level sample group
475///
476/// `tele`
477///
478/// ISO/IEC 14496-12 - 10.5
479#[derive(Debug, PartialEq, Eq)]
480pub struct TemporalLevelEntry {
481    level_independently_decodable: bool,
482}
483
484impl<'a> Deserialize<'a> for TemporalLevelEntry {
485    fn deserialize<R>(mut reader: R) -> io::Result<Self>
486    where
487        R: ZeroCopyReader<'a>,
488    {
489        let level_independently_decodable = (u8::deserialize(&mut reader)? & 0b1000_0000) != 0;
490
491        Ok(Self {
492            level_independently_decodable,
493        })
494    }
495}
496
497impl Serialize for TemporalLevelEntry {
498    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
499    where
500        W: std::io::Write,
501    {
502        ((self.level_independently_decodable as u8) << 7).serialize(&mut writer)?;
503        Ok(())
504    }
505}
506
507impl IsoSized for TemporalLevelEntry {
508    fn size(&self) -> usize {
509        1 // level_independently_decodable
510    }
511}
512
513/// Stream access point sample group
514///
515/// `sap `
516///
517/// ISO/IEC 14496-12 - 10.6
518#[derive(Debug, PartialEq, Eq)]
519pub struct SAPEntry {
520    dependent_flag: bool,
521    sap_type: u8,
522}
523
524impl<'a> Deserialize<'a> for SAPEntry {
525    fn deserialize<R>(mut reader: R) -> io::Result<Self>
526    where
527        R: ZeroCopyReader<'a>,
528    {
529        // x000 xxxx
530        let byte = u8::deserialize(&mut reader)?;
531        let dependent_flag = (byte & 0b1000_0000) != 0;
532        let sap_type = byte & 0b0000_1111;
533
534        Ok(Self {
535            dependent_flag,
536            sap_type,
537        })
538    }
539}
540
541impl Serialize for SAPEntry {
542    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
543    where
544        W: std::io::Write,
545    {
546        let mut byte = (self.dependent_flag as u8) << 7;
547        byte |= self.sap_type & 0b0000_1111;
548        byte.serialize(&mut writer)?;
549        Ok(())
550    }
551}
552
553impl IsoSized for SAPEntry {
554    fn size(&self) -> usize {
555        1 // dependent_flag + sap_type
556    }
557}
558
559/// Sample-to-item sample group
560///
561/// `stmi`
562///
563/// ISO/IEC 14496-12 - 10.7
564#[derive(Debug, PartialEq, Eq)]
565pub struct SampleToMetadataItemEntry {
566    meta_box_handler_type: u32,
567    num_items: u32,
568    item_id: Vec<u32>,
569}
570
571impl<'a> Deserialize<'a> for SampleToMetadataItemEntry {
572    fn deserialize<R>(mut reader: R) -> io::Result<Self>
573    where
574        R: ZeroCopyReader<'a>,
575    {
576        let meta_box_handler_type = u32::deserialize(&mut reader)?;
577        let num_items = u32::deserialize(&mut reader)?;
578
579        let mut item_id = Vec::with_capacity(num_items as usize);
580        for _ in 0..num_items {
581            item_id.push(u32::deserialize(&mut reader)?);
582        }
583
584        Ok(Self {
585            meta_box_handler_type,
586            num_items,
587            item_id,
588        })
589    }
590}
591
592impl Serialize for SampleToMetadataItemEntry {
593    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
594    where
595        W: std::io::Write,
596    {
597        self.meta_box_handler_type.serialize(&mut writer)?;
598        self.num_items.serialize(&mut writer)?;
599        for id in &self.item_id {
600            id.serialize(&mut writer)?;
601        }
602        Ok(())
603    }
604}
605
606impl IsoSized for SampleToMetadataItemEntry {
607    fn size(&self) -> usize {
608        4 + 4 + self.item_id.size() // meta_box_handler_type + num_items + item_id
609    }
610}
611
612/// Dependent random access point (DRAP) sample group
613///
614/// `drap`
615///
616/// ISO/IEC 14496-12 - 10.8
617#[derive(Debug, PartialEq, Eq)]
618pub struct VisualDRAPEntry {
619    drap_type: u8,
620}
621
622impl<'a> Deserialize<'a> for VisualDRAPEntry {
623    fn deserialize<R>(mut reader: R) -> io::Result<Self>
624    where
625        R: ZeroCopyReader<'a>,
626    {
627        let drap_type = ((u32::deserialize(&mut reader)? >> 29) & 0b111) as u8;
628        Ok(Self { drap_type })
629    }
630}
631
632impl Serialize for VisualDRAPEntry {
633    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
634    where
635        W: std::io::Write,
636    {
637        let byte = ((self.drap_type & 0b1111) as u32) << 29;
638        byte.serialize(&mut writer)?;
639        Ok(())
640    }
641}
642
643impl IsoSized for VisualDRAPEntry {
644    fn size(&self) -> usize {
645        4 // drap_type
646    }
647}
648
649/// Pixel Aspect Ratio Sample Grouping
650///
651/// `pasr`
652///
653/// ISO/IEC 14496-12 - 10.9
654#[derive(Debug, PartialEq, Eq)]
655pub struct PixelAspectRatioEntry {
656    h_spacing: u32,
657    v_spacing: u32,
658}
659
660impl<'a> Deserialize<'a> for PixelAspectRatioEntry {
661    fn deserialize<R>(mut reader: R) -> io::Result<Self>
662    where
663        R: ZeroCopyReader<'a>,
664    {
665        Ok(Self {
666            h_spacing: u32::deserialize(&mut reader)?,
667            v_spacing: u32::deserialize(&mut reader)?,
668        })
669    }
670}
671
672impl Serialize for PixelAspectRatioEntry {
673    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
674    where
675        W: std::io::Write,
676    {
677        self.h_spacing.serialize(&mut writer)?;
678        self.v_spacing.serialize(&mut writer)?;
679        Ok(())
680    }
681}
682
683impl IsoSized for PixelAspectRatioEntry {
684    fn size(&self) -> usize {
685        4 + 4 // h_spacing + v_spacing
686    }
687}
688
689/// Clean Aperture Sample Grouping
690///
691/// `casg`
692///
693/// ISO/IEC 14496-12 - 10.10
694#[derive(Debug, PartialEq, Eq)]
695pub struct CleanApertureEntry {
696    clean_aperture_width_n: u32,
697    clean_aperture_width_d: u32,
698    clean_aperture_height_n: u32,
699    clean_aperture_height_d: u32,
700    horiz_off_n: u32,
701    horiz_off_d: u32,
702    vert_off_n: u32,
703    vert_off_d: u32,
704}
705
706impl<'a> Deserialize<'a> for CleanApertureEntry {
707    fn deserialize<R>(mut reader: R) -> io::Result<Self>
708    where
709        R: ZeroCopyReader<'a>,
710    {
711        Ok(Self {
712            clean_aperture_width_n: u32::deserialize(&mut reader)?,
713            clean_aperture_width_d: u32::deserialize(&mut reader)?,
714            clean_aperture_height_n: u32::deserialize(&mut reader)?,
715            clean_aperture_height_d: u32::deserialize(&mut reader)?,
716            horiz_off_n: u32::deserialize(&mut reader)?,
717            horiz_off_d: u32::deserialize(&mut reader)?,
718            vert_off_n: u32::deserialize(&mut reader)?,
719            vert_off_d: u32::deserialize(&mut reader)?,
720        })
721    }
722}
723
724impl Serialize for CleanApertureEntry {
725    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
726    where
727        W: std::io::Write,
728    {
729        self.clean_aperture_width_n.serialize(&mut writer)?;
730        self.clean_aperture_width_d.serialize(&mut writer)?;
731        self.clean_aperture_height_n.serialize(&mut writer)?;
732        self.clean_aperture_height_d.serialize(&mut writer)?;
733        self.horiz_off_n.serialize(&mut writer)?;
734        self.horiz_off_d.serialize(&mut writer)?;
735        self.vert_off_n.serialize(&mut writer)?;
736        self.vert_off_d.serialize(&mut writer)?;
737        Ok(())
738    }
739}
740
741impl IsoSized for CleanApertureEntry {
742    fn size(&self) -> usize {
743        4 + 4 + 4 + 4 + 4 + 4 + 4 + 4 // clean_aperture_width_n + clean_aperture_width_d + clean_aperture_height_n + clean_aperture_height_d + horiz_off_n + horiz_off_d + vert_off_n + vert_off_d
744    }
745}