isobmff/boxes/
movie_fragments.rs

1use std::io;
2
3use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed, Serialize, U24Be, ZeroCopyReader};
4use scuffle_bytes_util::{BitReader, BitWriter};
5
6use super::{
7    CompositionToDecodeBox, MetaBox, SampleAuxiliaryInformationOffsetsBox, SampleAuxiliaryInformationSizesBox,
8    SampleGroupDescriptionBox, SampleToGroupBox, SubSampleInformationBox, UserDataBox,
9};
10use crate::{BoxHeader, FullBoxHeader, IsoBox, IsoSized, UnknownBox};
11
12/// Movie extends box
13///
14/// ISO/IEC 14496-12 - 8.8.1
15#[derive(IsoBox, Debug, PartialEq, Eq)]
16#[iso_box(box_type = b"mvex", crate_path = crate)]
17pub struct MovieExtendsBox {
18    /// The contained [`MovieExtendsHeaderBox`]. (optional)
19    #[iso_box(nested_box(collect))]
20    pub mehd: Option<MovieExtendsHeaderBox>,
21    /// The contained [`TrackExtendsBox`]es. (exactly one for each track in the [`MovieBox`](super::MovieBox))
22    #[iso_box(nested_box(collect))]
23    pub trex: Vec<TrackExtendsBox>,
24    /// The contained [`LevelAssignmentBox`]. (optional)
25    #[iso_box(nested_box(collect))]
26    pub leva: Option<LevelAssignmentBox>,
27}
28
29/// Movie extends header box
30///
31/// ISO/IEC 14496-12 - 8.8.2
32#[derive(IsoBox, Debug, PartialEq, Eq)]
33#[iso_box(box_type = b"mehd", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
34pub struct MovieExtendsHeaderBox {
35    /// The full box header.
36    pub full_header: FullBoxHeader,
37    /// A number associated with this fragment.
38    pub fragment_duration: u64,
39}
40
41impl<'a> DeserializeSeed<'a, BoxHeader> for MovieExtendsHeaderBox {
42    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
43    where
44        R: ZeroCopyReader<'a>,
45    {
46        let full_header = FullBoxHeader::deserialize(&mut reader)?;
47
48        let fragment_duration = if full_header.version == 1 {
49            u64::deserialize(&mut reader)?
50        } else {
51            u32::deserialize(&mut reader)? as u64
52        };
53
54        Ok(Self {
55            full_header,
56            fragment_duration,
57        })
58    }
59}
60
61impl Serialize for MovieExtendsHeaderBox {
62    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
63    where
64        W: std::io::Write,
65    {
66        self.serialize_box_header(&mut writer)?;
67        self.full_header.serialize(&mut writer)?;
68
69        if self.full_header.version == 1 {
70            self.fragment_duration.serialize(&mut writer)?;
71        } else {
72            (self.fragment_duration as u32).serialize(&mut writer)?;
73        }
74
75        Ok(())
76    }
77}
78
79impl IsoSized for MovieExtendsHeaderBox {
80    fn size(&self) -> usize {
81        let mut size = self.full_header.size(); // header
82        if self.full_header.version == 1 {
83            size += 8; // fragment_duration
84        } else {
85            size += 4; // fragment_duration
86        }
87        Self::add_header_size(size)
88    }
89}
90
91/// Sample flags
92///
93/// ISO/IEC 14496-12 - 8.8.3
94#[derive(Debug, PartialEq, Eq, Clone, Copy)]
95pub struct SampleFlags {
96    /// Reserved 4 bits, must be 0.
97    pub reserved: u8,
98    /// - `0`: The leading nature of this sample is unknown;
99    /// - `1`: This sample is a leading sample that has a dependency before the referenced I-picture (and is
100    ///   therefore not decodable);
101    /// - `2`: This sample is not a leading sample;
102    /// - `3`: This sample is a leading sample that has no dependency before the referenced I-picture (and is
103    ///   therefore decodable);
104    pub is_leading: u8,
105    /// - `0`: The dependency of this sample is unknown;
106    /// - `1`: This sample does depend on others (not an I picture);
107    /// - `2`: This sample does not depend on others (I picture);
108    /// - `3`: Reserved;
109    pub sample_depends_on: u8,
110    /// - `0`: The dependency of other samples on this sample is unknown;
111    /// - `1`: Other samples may depend on this one (not disposable);
112    /// - `2`: No other sample depends on this one (disposable);
113    /// - `3`: Reserved;
114    pub sample_is_depended_on: u8,
115    /// - `0`: It is unknown whether there is redundant coding in this sample;
116    /// - `1`: There is redundant coding in this sample;
117    /// - `2`: There is no redundant coding in this sample;
118    /// - `3`: Reserved;
119    pub sample_has_redundancy: u8,
120    /// A value from 0 to 7, indicating the number of padding bits at the end of sample.
121    pub sample_padding_value: u8,
122    /// Provides the same information as the sync sample table 8.6.2.
123    /// When this value is set to 0 for a sample, it is the same as if the sample were not in a movie fragment and
124    /// marked with an entry in the sync sample table (or, if all samples are sync samples, the sync sample table
125    /// were absent).
126    pub sample_is_non_sync_sample: bool,
127    /// An integer specifying the degradation priority for each sample.
128    pub sample_degradation_priority: u16,
129}
130
131impl<'a> Deserialize<'a> for SampleFlags {
132    fn deserialize<R>(mut reader: R) -> io::Result<Self>
133    where
134        R: ZeroCopyReader<'a>,
135    {
136        let mut reader = BitReader::new(reader.as_std());
137        let reserved = reader.read_bits(4)? as u8;
138        let is_leading = reader.read_bits(2)? as u8;
139        let sample_depends_on = reader.read_bits(2)? as u8;
140        let sample_is_depended_on = reader.read_bits(2)? as u8;
141        let sample_has_redundancy = reader.read_bits(2)? as u8;
142        let sample_padding_value = reader.read_bits(3)? as u8;
143        let sample_is_non_sync_sample = reader.read_bit()?;
144        let sample_degradation_priority = reader.read_bits(16)? as u16;
145
146        Ok(Self {
147            reserved,
148            is_leading,
149            sample_depends_on,
150            sample_is_depended_on,
151            sample_has_redundancy,
152            sample_padding_value,
153            sample_is_non_sync_sample,
154            sample_degradation_priority,
155        })
156    }
157}
158
159impl Serialize for SampleFlags {
160    fn serialize<W>(&self, writer: W) -> io::Result<()>
161    where
162        W: std::io::Write,
163    {
164        let mut bit_writer = BitWriter::new(writer);
165        bit_writer.write_bits(self.reserved as u64, 4)?;
166        bit_writer.write_bits(self.is_leading as u64, 2)?;
167        bit_writer.write_bits(self.sample_depends_on as u64, 2)?;
168        bit_writer.write_bits(self.sample_is_depended_on as u64, 2)?;
169        bit_writer.write_bits(self.sample_has_redundancy as u64, 2)?;
170        bit_writer.write_bits(self.sample_padding_value as u64, 3)?;
171        bit_writer.write_bit(self.sample_is_non_sync_sample)?;
172        bit_writer.write_bits(self.sample_degradation_priority as u64, 16)?;
173
174        Ok(())
175    }
176}
177
178impl IsoSized for SampleFlags {
179    fn size(&self) -> usize {
180        4
181    }
182}
183
184/// Track extends box
185///
186/// ISO/IEC 14496-12 - 8.8.3
187#[derive(IsoBox, Debug, PartialEq, Eq)]
188#[iso_box(box_type = b"trex", crate_path = crate)]
189pub struct TrackExtendsBox {
190    /// The full box header.
191    pub full_header: FullBoxHeader,
192    /// Identifies the track; this shall be the `track_ID` of a track in the [`MovieBox`](super::MovieBox).
193    pub track_id: u32,
194    /// Indicates the index of the sample entry that describes, by default,
195    /// the samples in the track fragments.
196    pub default_sample_description_index: u32,
197    /// Indicates the default duration of the samples in the track fragments.
198    pub default_sample_duration: u32,
199    /// Indicates the default size of the samples in the track fragments.
200    pub default_sample_size: u32,
201    /// Indicate the default flags values for the samples in the track fragments.
202    pub default_sample_flags: SampleFlags,
203}
204
205impl TrackExtendsBox {
206    /// Creates a new [`TrackExtendsBox`] with the given `track_id`.
207    pub fn new(track_id: u32) -> Self {
208        Self {
209            full_header: FullBoxHeader::default(),
210            track_id,
211            default_sample_description_index: 1,
212            default_sample_duration: 0,
213            default_sample_size: 0,
214            default_sample_flags: SampleFlags {
215                reserved: 0,
216                is_leading: 0,
217                sample_depends_on: 0,
218                sample_is_depended_on: 0,
219                sample_has_redundancy: 0,
220                sample_padding_value: 0,
221                sample_is_non_sync_sample: false,
222                sample_degradation_priority: 0,
223            },
224        }
225    }
226}
227
228/// Movie fragment box
229///
230/// ISO/IEC 14496-12 - 8.8.4
231#[derive(IsoBox, Debug, PartialEq, Eq)]
232#[iso_box(box_type = b"moof", crate_path = crate)]
233pub struct MovieFragmentBox<'a> {
234    /// The contained [`MovieFragmentHeaderBox`]. (mandatory)
235    #[iso_box(nested_box)]
236    pub mfhd: MovieFragmentHeaderBox,
237    /// The contained [`MovieExtendsBox`]. (optional)
238    #[iso_box(nested_box(collect))]
239    pub meta: Option<MetaBox<'a>>,
240    /// The contained [`TrackFragmentBox`]es. (any quantity)
241    #[iso_box(nested_box(collect))]
242    pub traf: Vec<TrackFragmentBox<'a>>,
243    /// The contained [`UserDataBox`]. (optional)
244    #[iso_box(nested_box(collect))]
245    pub udta: Option<UserDataBox<'a>>,
246}
247
248/// Movie fragment header box
249///
250/// ISO/IEC 14496-12 - 8.8.5
251#[derive(IsoBox, Debug, PartialEq, Eq)]
252#[iso_box(box_type = b"mfhd", crate_path = crate)]
253pub struct MovieFragmentHeaderBox {
254    /// The full box header.
255    pub full_header: FullBoxHeader,
256    /// A number associated with this fragment.
257    pub sequence_number: u32,
258}
259
260impl MovieFragmentHeaderBox {
261    /// Creates a new [`MovieFragmentHeaderBox`] with the given `sequence_number`.
262    pub fn new(sequence_number: u32) -> Self {
263        Self {
264            full_header: FullBoxHeader::default(),
265            sequence_number,
266        }
267    }
268}
269
270/// Track fragment box
271///
272/// ISO/IEC 14496-12 - 8.8.6
273#[derive(IsoBox, Debug, PartialEq, Eq)]
274#[iso_box(box_type = b"traf", crate_path = crate)]
275pub struct TrackFragmentBox<'a> {
276    /// The contained [`TrackFragmentHeaderBox`]. (mandatory)
277    #[iso_box(nested_box)]
278    pub tfhd: TrackFragmentHeaderBox,
279    /// The contained [`TrackRunBox`]es. (any quantity)
280    #[iso_box(nested_box(collect))]
281    pub trun: Vec<TrackRunBox>,
282    /// The contained [`SampleToGroupBox`]es. (any quantity)
283    #[iso_box(nested_box(collect))]
284    pub sbgp: Vec<SampleToGroupBox>,
285    /// The contained [`SampleGroupDescriptionBox`]es. (any quantity)
286    #[iso_box(nested_box(collect))]
287    pub sgpd: Vec<SampleGroupDescriptionBox<'a>>,
288    /// The contained [`SubSampleInformationBox`]es. (any quantity)
289    #[iso_box(nested_box(collect))]
290    pub subs: Vec<SubSampleInformationBox>,
291    /// The contained [`SampleAuxiliaryInformationSizesBox`]es. (any quantity)
292    #[iso_box(nested_box(collect))]
293    pub saiz: Vec<SampleAuxiliaryInformationSizesBox<'a>>,
294    /// The contained [`SampleAuxiliaryInformationOffsetsBox`]es. (any quantity)
295    #[iso_box(nested_box(collect))]
296    pub saio: Vec<SampleAuxiliaryInformationOffsetsBox>,
297    /// The contained [`TrackFragmentBaseMediaDecodeTimeBox`]es. (any quantity)
298    #[iso_box(nested_box(collect))]
299    pub tfdt: Option<TrackFragmentBaseMediaDecodeTimeBox>,
300    /// The contained [`MetaBox`]. (optional)
301    #[iso_box(nested_box(collect))]
302    pub meta: Option<MetaBox<'a>>,
303    /// The contained [`UserDataBox`]. (optional)
304    #[iso_box(nested_box(collect))]
305    pub udta: Option<UserDataBox<'a>>,
306}
307
308/// Track fragment header box
309///
310/// ISO/IEC 14496-12 - 8.8.7
311#[derive(IsoBox, Debug, PartialEq, Eq)]
312#[iso_box(box_type = b"tfhd", skip_impl(deserialize_seed, serialize), crate_path = crate)]
313pub struct TrackFragmentHeaderBox {
314    // full header:
315    /// The version of the box.
316    pub version: u8,
317    /// The flags of the box.
318    pub flags: TfFlags,
319    // body:
320    /// Identifies the track; this shall be the `track_ID` of a track in the [`MovieBox`](super::MovieBox).
321    pub track_id: u32,
322    /// The base offset to use when calculating data offsets.
323    ///
324    /// Present if the [`BaseDataOffsetPresent`](TfFlags::BaseDataOffsetPresent) flag is set.
325    pub base_data_offset: Option<u64>,
326    /// Indicates the index of the sample entry that describes, by default,
327    /// the samples in the track fragments.
328    ///
329    /// Present if the [`SampleDescriptionIndexPresent`](TfFlags::SampleDescriptionIndexPresent) flag is set.
330    pub sample_description_index: Option<u32>,
331    /// Indicates the default duration of the samples in the track fragments.
332    ///
333    /// Present if the [`DefaultSampleDurationPresent`](TfFlags::DefaultSampleDurationPresent) flag is set.
334    pub default_sample_duration: Option<u32>,
335    /// Indicates the default size of the samples in the track fragments.
336    ///
337    /// Present if the [`DefaultSampleSizePresent`](TfFlags::DefaultSampleSizePresent) flag is set.
338    pub default_sample_size: Option<u32>,
339    /// Indicate the default flags values for the samples in the track fragments.
340    ///
341    /// Present if the [`DefaultSampleFlagsPresent`](TfFlags::DefaultSampleFlagsPresent) flag is set.
342    pub default_sample_flags: Option<SampleFlags>,
343}
344
345impl TrackFragmentHeaderBox {
346    /// Creates a new [`TrackFragmentHeaderBox`] with the given parameters.
347    pub fn new(
348        track_id: u32,
349        base_data_offset: Option<u64>,
350        sample_description_index: Option<u32>,
351        default_sample_duration: Option<u32>,
352        default_sample_size: Option<u32>,
353        default_sample_flags: Option<SampleFlags>,
354    ) -> Self {
355        let mut flags = TfFlags::DefaultBaseIsMoof;
356
357        if base_data_offset.is_some() {
358            flags |= TfFlags::BaseDataOffsetPresent;
359        }
360
361        if sample_description_index.is_some() {
362            flags |= TfFlags::SampleDescriptionIndexPresent;
363        }
364
365        if default_sample_duration.is_some() {
366            flags |= TfFlags::DefaultSampleDurationPresent;
367        }
368
369        if default_sample_size.is_some() {
370            flags |= TfFlags::DefaultSampleSizePresent;
371        }
372
373        if default_sample_flags.is_some() {
374            flags |= TfFlags::DefaultSampleFlagsPresent;
375        }
376
377        Self {
378            version: 0,
379            flags,
380            track_id,
381            base_data_offset,
382            sample_description_index,
383            default_sample_duration,
384            default_sample_size,
385            default_sample_flags,
386        }
387    }
388}
389
390bitflags::bitflags! {
391    /// Track fragment header flags
392    #[derive(Debug, Clone, Copy, PartialEq, Eq)]
393    pub struct TfFlags: u32 {
394        /// Indicates the presence of the base-data-offset field. This
395        /// provides an explicit anchor for the data offsets in each track run.
396        const BaseDataOffsetPresent = 0x000001;
397        /// Indicates the presence of this field, which over-rides, in
398        /// this fragment, the default set up in the [`TrackExtendsBox`].
399        const SampleDescriptionIndexPresent = 0x000002;
400        /// Indicates the presence of the [`default_sample_duration`](TrackFragmentHeaderBox::default_sample_duration) field.
401        const DefaultSampleDurationPresent = 0x000008;
402        /// Indicates the presence of the [`default_sample_size`](TrackFragmentHeaderBox::default_sample_size) field.
403        const DefaultSampleSizePresent = 0x000010;
404        /// Indicates the presence of the [`default_sample_flags`](TrackFragmentHeaderBox::default_sample_flags) field.
405        const DefaultSampleFlagsPresent = 0x000020;
406        /// This indicates that the duration provided in either
407        /// [`default_sample_duration`](TrackFragmentHeaderBox::default_sample_duration), or by the
408        /// [`default_sample_duration`](TrackExtendsBox::default_sample_duration) in the [`TrackExtendsBox`],
409        /// is empty, i.e. that there are no samples for this time interval.
410        /// It is an error to make a presentation that has both edit lists in the [`MovieBox`](super::MovieBox),
411        /// and empty-duration fragments.
412        const DurationIsEmpty = 0x010000;
413        /// If [`BaseDataOffsetPresent`](Self::BaseDataOffsetPresent) is set, this flag is ignored. Support for the
414        /// [`DefaultBaseIsMoof`](Self::DefaultBaseIsMoof) is required under the 'iso5' brand,
415        /// and it shall not be used in brands or compatible brands earlier than 'iso5'.
416        const DefaultBaseIsMoof = 0x020000;
417    }
418}
419
420impl<'a> Deserialize<'a> for TfFlags {
421    fn deserialize<R>(reader: R) -> io::Result<Self>
422    where
423        R: ZeroCopyReader<'a>,
424    {
425        let flags = U24Be::deserialize(reader)?;
426        Ok(TfFlags::from_bits_truncate(*flags))
427    }
428}
429
430impl Serialize for TfFlags {
431    fn serialize<W>(&self, writer: W) -> io::Result<()>
432    where
433        W: std::io::Write,
434    {
435        U24Be(self.bits()).serialize(writer)
436    }
437}
438
439impl IsoSized for TfFlags {
440    fn size(&self) -> usize {
441        3
442    }
443}
444
445impl<'a> DeserializeSeed<'a, BoxHeader> for TrackFragmentHeaderBox {
446    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
447    where
448        R: ZeroCopyReader<'a>,
449    {
450        let version = u8::deserialize(&mut reader)?;
451        let flags = TfFlags::deserialize(&mut reader)?;
452
453        let track_id = u32::deserialize(&mut reader)?;
454        let base_data_offset = if flags.contains(TfFlags::BaseDataOffsetPresent) {
455            Some(u64::deserialize(&mut reader)?)
456        } else {
457            None
458        };
459        let sample_description_index = if flags.contains(TfFlags::SampleDescriptionIndexPresent) {
460            Some(u32::deserialize(&mut reader)?)
461        } else {
462            None
463        };
464        let default_sample_duration = if flags.contains(TfFlags::DefaultSampleDurationPresent) {
465            Some(u32::deserialize(&mut reader)?)
466        } else {
467            None
468        };
469        let default_sample_size = if flags.contains(TfFlags::DefaultSampleSizePresent) {
470            Some(u32::deserialize(&mut reader)?)
471        } else {
472            None
473        };
474        let default_sample_flags = if flags.contains(TfFlags::DefaultSampleFlagsPresent) {
475            Some(SampleFlags::deserialize(&mut reader)?)
476        } else {
477            None
478        };
479
480        Ok(Self {
481            version,
482            flags,
483            track_id,
484            base_data_offset,
485            sample_description_index,
486            default_sample_duration,
487            default_sample_size,
488            default_sample_flags,
489        })
490    }
491}
492
493impl Serialize for TrackFragmentHeaderBox {
494    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
495    where
496        W: std::io::Write,
497    {
498        self.serialize_box_header(&mut writer)?;
499        self.version.serialize(&mut writer)?;
500        self.flags.serialize(&mut writer)?;
501
502        self.track_id.serialize(&mut writer)?;
503        if self.flags.contains(TfFlags::BaseDataOffsetPresent) {
504            self.base_data_offset
505                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "base_data_offset is required"))?
506                .serialize(&mut writer)?;
507        }
508        if self.flags.contains(TfFlags::SampleDescriptionIndexPresent) {
509            self.sample_description_index
510                .ok_or(io::Error::new(
511                    io::ErrorKind::InvalidData,
512                    "sample_description_index is required",
513                ))?
514                .serialize(&mut writer)?;
515        }
516        if self.flags.contains(TfFlags::DefaultSampleDurationPresent) {
517            self.default_sample_duration
518                .ok_or(io::Error::new(
519                    io::ErrorKind::InvalidData,
520                    "default_sample_duration is required",
521                ))?
522                .serialize(&mut writer)?;
523        }
524        if self.flags.contains(TfFlags::DefaultSampleSizePresent) {
525            self.default_sample_size
526                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "default_sample_size is required"))?
527                .serialize(&mut writer)?;
528        }
529        if self.flags.contains(TfFlags::DefaultSampleFlagsPresent) {
530            self.default_sample_flags
531                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "default_sample_flags is required"))?
532                .serialize(&mut writer)?;
533        }
534
535        Ok(())
536    }
537}
538
539/// Track fragment run box
540///
541/// ISO/IEC 14496-12 - 8.8.8
542#[derive(IsoBox, Debug, PartialEq, Eq)]
543#[iso_box(box_type = b"trun", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
544pub struct TrackRunBox {
545    // full header:
546    /// The version of the box.
547    pub version: u8,
548    /// The flags of the box.
549    pub flags: TrFlags,
550    // body:
551    /// The number of samples being added in this run; also the number of rows in the [`samples`](Self::samples) vec.
552    pub sample_count: u32,
553    /// Added to the implicit or explicit `data_offset` established in the track fragment header.
554    pub data_offset: Option<i32>,
555    /// Provides a set of flags for the first sample only of this run.
556    pub first_sample_flags: Option<SampleFlags>,
557    /// The samples in this run.
558    pub samples: Vec<TrackRunBoxSample>,
559}
560
561impl TrackRunBox {
562    /// Creates a new [`TrackRunBox`] with the given samples and optional first sample flags.
563    pub fn new(samples: Vec<TrackRunBoxSample>, first_sample_flags: Option<SampleFlags>) -> Self {
564        let mut flags = TrFlags::DataOffsetPresent;
565
566        if let Some(first_sample) = samples.first() {
567            if first_sample.sample_duration.is_some() {
568                flags |= TrFlags::SampleDurationPresent;
569            }
570            if first_sample.sample_size.is_some() {
571                flags |= TrFlags::SampleSizePresent;
572            }
573            if first_sample.sample_flags.is_some() {
574                flags |= TrFlags::SampleFlagsPresent;
575            }
576            if first_sample.sample_composition_time_offset.is_some() {
577                flags |= TrFlags::SampleCompositionTimeOffsetsPresent;
578            }
579        }
580
581        let version = if samples
582            .iter()
583            .any(|s| s.sample_composition_time_offset.is_some_and(|o| o < 0))
584        {
585            1
586        } else {
587            0
588        };
589
590        Self {
591            version,
592            flags,
593            sample_count: samples.len() as u32,
594            data_offset: Some(0),
595            first_sample_flags,
596            samples,
597        }
598    }
599}
600
601impl<'a> DeserializeSeed<'a, BoxHeader> for TrackRunBox {
602    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
603    where
604        R: ZeroCopyReader<'a>,
605    {
606        let version = u8::deserialize(&mut reader)?;
607        let flags = TrFlags::deserialize(&mut reader)?;
608
609        let sample_count = u32::deserialize(&mut reader)?;
610        let data_offset = if flags.contains(TrFlags::DataOffsetPresent) {
611            Some(i32::deserialize(&mut reader)?)
612        } else {
613            None
614        };
615        let first_sample_flags = if flags.contains(TrFlags::FirstSampleFlagsPresent) {
616            Some(SampleFlags::deserialize(&mut reader)?)
617        } else {
618            None
619        };
620
621        let mut samples = Vec::with_capacity(sample_count as usize);
622        for _ in 0..sample_count {
623            samples.push(TrackRunBoxSample::deserialize_seed(&mut reader, (version, flags))?);
624        }
625
626        Ok(Self {
627            version,
628            flags,
629            sample_count,
630            data_offset,
631            first_sample_flags,
632            samples,
633        })
634    }
635}
636
637impl IsoSized for TrackRunBox {
638    fn size(&self) -> usize {
639        let mut size = 0;
640        size += self.version.size(); // version
641        size += self.flags.size(); // flags
642        size += 4; // sample_count
643        if self.flags.contains(TrFlags::DataOffsetPresent) {
644            size += 4; // data_offset
645        }
646        if self.flags.contains(TrFlags::FirstSampleFlagsPresent) {
647            size += 4; // first_sample_flags
648        }
649
650        size += self.samples.iter().map(|s| s.size(self.flags)).sum::<usize>();
651
652        Self::add_header_size(size)
653    }
654}
655
656impl Serialize for TrackRunBox {
657    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
658    where
659        W: std::io::Write,
660    {
661        self.serialize_box_header(&mut writer)?;
662        self.version.serialize(&mut writer)?;
663        self.flags.serialize(&mut writer)?;
664
665        self.sample_count.serialize(&mut writer)?;
666        if self.flags.contains(TrFlags::DataOffsetPresent) {
667            self.data_offset
668                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "data_offset is required"))?
669                .serialize(&mut writer)?;
670        }
671        if self.flags.contains(TrFlags::FirstSampleFlagsPresent) {
672            self.first_sample_flags
673                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "first_sample_flags is required"))?
674                .serialize(&mut writer)?;
675        }
676
677        for sample in &self.samples {
678            sample.serialize(&mut writer, (self.version, self.flags))?;
679        }
680
681        Ok(())
682    }
683}
684
685/// Sample in a [`TrackRunBox`].
686#[derive(Debug, PartialEq, Eq)]
687pub struct TrackRunBoxSample {
688    /// Duration of this sample.
689    ///
690    /// Present if the [`SampleDurationPresent`](TrFlags::SampleDurationPresent) flag is set.
691    pub sample_duration: Option<u32>,
692    /// Size of this sample.
693    ///
694    /// Present if the [`SampleSizePresent`](TrFlags::SampleSizePresent) flag is set.
695    pub sample_size: Option<u32>,
696    /// Flags for this sample.
697    ///
698    /// Present if the [`SampleFlagsPresent`](TrFlags::SampleFlagsPresent) flag is set.
699    pub sample_flags: Option<SampleFlags>,
700    /// Composition time offset for this sample.
701    /// Either a signed or unsigned 32-bit integer.
702    ///
703    /// Present if the [`SampleCompositionTimeOffsetsPresent`](TrFlags::SampleCompositionTimeOffsetsPresent) flag is set.
704    pub sample_composition_time_offset: Option<i64>,
705}
706
707bitflags::bitflags! {
708    /// Track run flags
709    #[derive(Debug, PartialEq, Eq, Clone, Copy)]
710    pub struct TrFlags: u32 {
711        /// Indicates the presence of the [`data_offset`](TrackRunBox::data_offset) field.
712        const DataOffsetPresent = 0x000001;
713        /// This overrides the default flags for the first sample only,
714        /// defined in [8.8.3.1](TrackExtendsBox).
715        /// This makes it possible to record a group of frames where the first is a key and the
716        /// rest are difference frames, without supplying explicit flags for every sample. If this flag and field
717        /// are used, [`SampleFlagsPresent`](Self::SampleFlagsPresent) shall not be set.
718        ///
719        /// If this flag is set, the [`first_sample_flags`](TrackRunBox::first_sample_flags) field is present.
720        const FirstSampleFlagsPresent = 0x000004;
721        /// Indicates that each sample has its own duration, otherwise the default is used.
722        ///
723        /// Indicates the presence of the [`sample_duration`](TrackRunBoxSample::sample_duration) field.
724        const SampleDurationPresent = 0x000100;
725        /// Each sample has its own size, otherwise the default is used.
726        ///
727        /// Indicates the presence of the [`sample_size`](TrackRunBoxSample::sample_size) field.
728        const SampleSizePresent = 0x000200;
729        /// Each sample has its own flags, otherwise the default is used.
730        ///
731        /// Indicates the presence of the [`sample_flags`](TrackRunBoxSample::sample_flags) field.
732        const SampleFlagsPresent = 0x000400;
733        /// Each sample has a composition time offset.
734        ///
735        /// Indicates the presence of the [`sample_composition_time_offset`](TrackRunBoxSample::sample_composition_time_offset)
736        /// field.
737        const SampleCompositionTimeOffsetsPresent = 0x000800;
738    }
739}
740
741impl<'a> Deserialize<'a> for TrFlags {
742    fn deserialize<R>(reader: R) -> io::Result<Self>
743    where
744        R: ZeroCopyReader<'a>,
745    {
746        let flags = U24Be::deserialize(reader)?;
747        Ok(TrFlags::from_bits_truncate(*flags))
748    }
749}
750
751impl Serialize for TrFlags {
752    fn serialize<W>(&self, writer: W) -> io::Result<()>
753    where
754        W: std::io::Write,
755    {
756        U24Be(self.bits()).serialize(writer)
757    }
758}
759
760impl IsoSized for TrFlags {
761    fn size(&self) -> usize {
762        3
763    }
764}
765
766impl<'a> DeserializeSeed<'a, (u8, TrFlags)> for TrackRunBoxSample {
767    fn deserialize_seed<R>(mut reader: R, seed: (u8, TrFlags)) -> std::io::Result<Self>
768    where
769        R: ZeroCopyReader<'a>,
770    {
771        let (version, flags) = seed;
772
773        let sample_duration = if flags.contains(TrFlags::SampleDurationPresent) {
774            Some(u32::deserialize(&mut reader)?)
775        } else {
776            None
777        };
778        let sample_size = if flags.contains(TrFlags::SampleSizePresent) {
779            Some(u32::deserialize(&mut reader)?)
780        } else {
781            None
782        };
783        let sample_flags = if flags.contains(TrFlags::SampleFlagsPresent) {
784            Some(SampleFlags::deserialize(&mut reader)?)
785        } else {
786            None
787        };
788        let sample_composition_time_offset = if flags.contains(TrFlags::SampleCompositionTimeOffsetsPresent) {
789            if version == 0 {
790                Some(u32::deserialize(&mut reader)? as i64)
791            } else {
792                Some(i32::deserialize(&mut reader)? as i64)
793            }
794        } else {
795            None
796        };
797
798        Ok(Self {
799            sample_duration,
800            sample_size,
801            sample_flags,
802            sample_composition_time_offset,
803        })
804    }
805}
806
807impl TrackRunBoxSample {
808    fn serialize<W>(&self, mut writer: W, full_header: (u8, TrFlags)) -> io::Result<()>
809    where
810        W: io::Write,
811    {
812        let (version, flags) = full_header;
813
814        if flags.contains(TrFlags::SampleDurationPresent) {
815            self.sample_duration
816                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "sample_duration is required"))?
817                .serialize(&mut writer)?;
818        }
819        if flags.contains(TrFlags::SampleSizePresent) {
820            self.sample_size
821                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "sample_size is required"))?
822                .serialize(&mut writer)?;
823        }
824        if flags.contains(TrFlags::SampleFlagsPresent) {
825            self.sample_flags
826                .ok_or(io::Error::new(io::ErrorKind::InvalidData, "sample_flags is required"))?
827                .serialize(&mut writer)?;
828        }
829        if flags.contains(TrFlags::SampleCompositionTimeOffsetsPresent) {
830            let sample_composition_time_offset = self.sample_composition_time_offset.ok_or(io::Error::new(
831                io::ErrorKind::InvalidData,
832                "sample_composition_time_offset is required",
833            ))?;
834            if version == 0 {
835                (sample_composition_time_offset as u32).serialize(&mut writer)?;
836            } else {
837                (sample_composition_time_offset as i32).serialize(&mut writer)?;
838            }
839        }
840
841        Ok(())
842    }
843}
844
845impl TrackRunBoxSample {
846    /// Returns the size of this sample in bytes, depending on the flags.
847    pub fn size(&self, flags: TrFlags) -> usize {
848        let mut size = 0;
849        if flags.contains(TrFlags::SampleDurationPresent) {
850            size += 4; // sample_duration
851        }
852        if flags.contains(TrFlags::SampleSizePresent) {
853            size += 4; // sample_size
854        }
855        if flags.contains(TrFlags::SampleFlagsPresent) {
856            size += 4; // sample_flags
857        }
858        if flags.contains(TrFlags::SampleCompositionTimeOffsetsPresent) {
859            size += 4; // sample_composition_time_offset
860        }
861
862        size
863    }
864}
865
866/// Movie fragment random access box
867///
868/// ISO/IEC 14496-12 - 8.8.9
869#[derive(IsoBox, Debug, PartialEq, Eq)]
870#[iso_box(box_type = b"mfra", crate_path = crate)]
871pub struct MovieFragmentRandomAccessBox {
872    /// The contained [`TrackFragmentRandomAccessBox`]es. (zero or one per track)
873    #[iso_box(nested_box(collect))]
874    pub tfra: Vec<TrackFragmentRandomAccessBox>,
875    /// The contained [`MovieFragmentRandomAccessOffsetBox`]. (mandatory)
876    #[iso_box(nested_box)]
877    pub mfro: MovieFragmentRandomAccessOffsetBox,
878}
879
880/// Track fragment random access box
881///
882/// ISO/IEC 14496-12 - 8.8.10
883#[derive(IsoBox, Debug, PartialEq, Eq)]
884#[iso_box(box_type = b"tfra", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
885pub struct TrackFragmentRandomAccessBox {
886    /// The full box header.
887    pub full_header: FullBoxHeader,
888    /// An integer providing the track identifier for which random access information is provided.
889    pub track_id: u32,
890    /// Indicates the length in bytes of the traf_number field minus one.
891    pub length_size_of_traf_num: u8,
892    /// Indicates the length in bytes of the trun_number field minus one.
893    pub length_size_of_trun_num: u8,
894    /// Indicates the length in bytes of the sample_number field minus one.
895    pub length_size_of_sample_num: u8,
896    /// An integer that gives the number of the entries for this track. If this value is zero, it
897    /// indicates that every sample is a sync sample and no table entry follows.
898    pub number_of_entry: u32,
899    /// `time`, `moof_offset`, `traf_number`, `trun_number`, and `sample_number`.
900    pub entries: Vec<TrackFragmentRandomAccessBoxEntry>,
901}
902
903impl<'a> DeserializeSeed<'a, BoxHeader> for TrackFragmentRandomAccessBox {
904    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
905    where
906        R: ZeroCopyReader<'a>,
907    {
908        let full_header = FullBoxHeader::deserialize(&mut reader)?;
909
910        let track_id = u32::deserialize(&mut reader)?;
911        // 00000000 00000000 00000000 00xxxxxx
912        let bytes = u32::deserialize(&mut reader)?;
913        let length_size_of_traf_num = ((bytes >> 4) & 0b11) as u8;
914        let length_size_of_trun_num = ((bytes >> 2) & 0b11) as u8;
915        let length_size_of_sample_num = (bytes & 0b11) as u8;
916        let number_of_entry = u32::deserialize(&mut reader)?;
917
918        let mut entries = Vec::with_capacity(number_of_entry as usize);
919        for _ in 0..number_of_entry {
920            let time = if full_header.version == 1 {
921                u64::deserialize(&mut reader)?
922            } else {
923                u32::deserialize(&mut reader)? as u64
924            };
925            let moof_offset = if full_header.version == 1 {
926                u64::deserialize(&mut reader)?
927            } else {
928                u32::deserialize(&mut reader)? as u64
929            };
930
931            // The length of the following fields is bound to 3 bytes because the length fields are all 2 bits
932            let traf_number = reader.try_read(length_size_of_traf_num as usize + 1)?.pad_to_u32_be();
933            let trun_number = reader.try_read(length_size_of_trun_num as usize + 1)?.pad_to_u32_be();
934            let sample_number = reader.try_read(length_size_of_sample_num as usize + 1)?.pad_to_u32_be();
935
936            entries.push(TrackFragmentRandomAccessBoxEntry {
937                time,
938                moof_offset,
939                traf_number,
940                trun_number,
941                sample_delta: sample_number,
942            });
943        }
944
945        Ok(Self {
946            full_header,
947            track_id,
948            length_size_of_traf_num,
949            length_size_of_trun_num,
950            length_size_of_sample_num,
951            number_of_entry,
952            entries,
953        })
954    }
955}
956
957impl Serialize for TrackFragmentRandomAccessBox {
958    fn serialize<W>(&self, writer: W) -> io::Result<()>
959    where
960        W: std::io::Write,
961    {
962        let mut bit_writer = BitWriter::new(writer);
963
964        self.serialize_box_header(&mut bit_writer)?;
965        self.full_header.serialize(&mut bit_writer)?;
966
967        self.track_id.serialize(&mut bit_writer)?;
968        bit_writer.write_bits(0, 26)?;
969        bit_writer.write_bits(self.length_size_of_traf_num as u64, 2)?;
970        bit_writer.write_bits(self.length_size_of_trun_num as u64, 2)?;
971        bit_writer.write_bits(self.length_size_of_sample_num as u64, 2)?;
972        self.number_of_entry.serialize(&mut bit_writer)?;
973
974        for entry in &self.entries {
975            entry.serialize(&mut bit_writer, self)?;
976        }
977
978        Ok(())
979    }
980}
981
982impl IsoSized for TrackFragmentRandomAccessBox {
983    fn size(&self) -> usize {
984        let mut size = self.full_header.size(); // header
985        size += 4; // track_id
986        size += 4; // length_size_of_traf_num + length_size_of_trun_num + length_size_of_sample_num
987        size += 4; // number_of_entry
988        size += self.entries.iter().map(|e| e.size(self)).sum::<usize>();
989
990        Self::add_header_size(size)
991    }
992}
993
994/// Entry in a [`TrackFragmentRandomAccessBox`].
995#[derive(Debug, PartialEq, Eq)]
996pub struct TrackFragmentRandomAccessBoxEntry {
997    /// An integer that indicates the presentation time of the sync sample in units defined in
998    /// the [`MediaHeaderBox`](super::MediaHeaderBox) of the associated track.
999    pub time: u64,
1000    /// An integer that gives the offset of the ['moof'](MovieFragmentBox) used in this entry. Offset is the
1001    /// byte-offset between the beginning of the file and the beginning of the ['moof'](MovieFragmentBox).
1002    pub moof_offset: u64,
1003    /// Indicates the ['traf'](TrackFragmentBox) number that contains the sync sample. The number ranges from 1
1004    /// (the first ['traf'](TrackFragmentBox) is numbered 1) in each ['moof'](MovieFragmentBox).
1005    pub traf_number: u32,
1006    /// Indicates the ['trun'](TrackRunBox) number that contains the sync sample. The number ranges from 1 in
1007    /// each ['traf'](TrackFragmentBox).
1008    pub trun_number: u32,
1009    /// Indicates the sample number of the sync sample. It is coded as one plus the desired sample
1010    /// number minus the sample number of the first sample in the [`TrackRunBox`].
1011    pub sample_delta: u32,
1012}
1013
1014impl TrackFragmentRandomAccessBoxEntry {
1015    fn serialize<W>(&self, writer: W, parent: &TrackFragmentRandomAccessBox) -> io::Result<()>
1016    where
1017        W: std::io::Write,
1018    {
1019        let mut bit_writer = BitWriter::new(writer);
1020
1021        if parent.full_header.version == 1 {
1022            self.time.serialize(&mut bit_writer)?;
1023            self.moof_offset.serialize(&mut bit_writer)?;
1024        } else {
1025            (self.time as u32).serialize(&mut bit_writer)?;
1026            (self.moof_offset as u32).serialize(&mut bit_writer)?;
1027        }
1028
1029        bit_writer.write_bits(self.traf_number as u64, (parent.length_size_of_traf_num + 1) * 8)?;
1030        bit_writer.write_bits(self.trun_number as u64, (parent.length_size_of_trun_num + 1) * 8)?;
1031        bit_writer.write_bits(self.sample_delta as u64, (parent.length_size_of_sample_num + 1) * 8)?;
1032
1033        Ok(())
1034    }
1035}
1036
1037impl TrackFragmentRandomAccessBoxEntry {
1038    /// Returns the size of this entry in bytes, depending on the parent box.
1039    pub fn size(&self, parent: &TrackFragmentRandomAccessBox) -> usize {
1040        let mut size = 0;
1041        if parent.full_header.version == 1 {
1042            size += 8;
1043            size += 8;
1044        } else {
1045            size += 4;
1046            size += 4;
1047        }
1048        size += parent.length_size_of_traf_num as usize + 1;
1049        size += parent.length_size_of_trun_num as usize + 1;
1050        size += parent.length_size_of_sample_num as usize + 1;
1051        size
1052    }
1053}
1054
1055/// Movie fragment random access offset box
1056///
1057/// ISO/IEC 14496-12 - 8.8.11
1058#[derive(IsoBox, Debug, PartialEq, Eq)]
1059#[iso_box(box_type = b"mfro", crate_path = crate)]
1060pub struct MovieFragmentRandomAccessOffsetBox {
1061    /// The full box header.
1062    pub full_header: FullBoxHeader,
1063    /// An integer that gives the number of bytes of the enclosing [`MovieFragmentRandomAccessBox`]
1064    /// box. This field is placed last in the enclosing box to assist readers scanning from the end of the file
1065    /// in finding the [`MovieFragmentRandomAccessBox`].
1066    pub parent_size: u32,
1067}
1068
1069/// Track fragment decode time box
1070///
1071/// ISO/IEC 14496-12 - 8.8.12
1072#[derive(IsoBox, Debug, PartialEq, Eq)]
1073#[iso_box(box_type = b"tfdt", skip_impl(deserialize_seed, serialize, sized), crate_path = crate)]
1074pub struct TrackFragmentBaseMediaDecodeTimeBox {
1075    /// The full box header.
1076    pub full_header: FullBoxHeader,
1077    /// An integer equal to the sum of the decode durations of all earlier samples in the
1078    /// media, expressed in the media's timescale. It does not include the samples added in the enclosing
1079    /// track fragment.
1080    pub base_media_decode_time: u64,
1081}
1082
1083impl TrackFragmentBaseMediaDecodeTimeBox {
1084    /// Creates a new [`TrackFragmentBaseMediaDecodeTimeBox`] with the given base media decode time.
1085    pub fn new(base_media_decode_time: u64) -> Self {
1086        let version = if base_media_decode_time > u32::MAX as u64 { 1 } else { 0 };
1087
1088        Self {
1089            full_header: FullBoxHeader {
1090                version,
1091                flags: U24Be(0),
1092            },
1093            base_media_decode_time,
1094        }
1095    }
1096}
1097
1098impl<'a> DeserializeSeed<'a, BoxHeader> for TrackFragmentBaseMediaDecodeTimeBox {
1099    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
1100    where
1101        R: ZeroCopyReader<'a>,
1102    {
1103        let full_header = FullBoxHeader::deserialize(&mut reader)?;
1104
1105        let base_media_decode_time = if full_header.version == 1 {
1106            u64::deserialize(&mut reader)?
1107        } else {
1108            u32::deserialize(&mut reader)? as u64
1109        };
1110
1111        Ok(Self {
1112            full_header,
1113            base_media_decode_time,
1114        })
1115    }
1116}
1117
1118impl Serialize for TrackFragmentBaseMediaDecodeTimeBox {
1119    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
1120    where
1121        W: std::io::Write,
1122    {
1123        self.serialize_box_header(&mut writer)?;
1124        self.full_header.serialize(&mut writer)?;
1125
1126        if self.full_header.version == 1 {
1127            self.base_media_decode_time.serialize(&mut writer)?;
1128        } else {
1129            (self.base_media_decode_time as u32).serialize(&mut writer)?;
1130        }
1131
1132        Ok(())
1133    }
1134}
1135
1136impl IsoSized for TrackFragmentBaseMediaDecodeTimeBox {
1137    fn size(&self) -> usize {
1138        let mut size = self.full_header.size(); // header
1139        if self.full_header.version == 1 {
1140            size += 8; // base_media_decode_time
1141        } else {
1142            size += 4; // base_media_decode_time
1143        }
1144        Self::add_header_size(size)
1145    }
1146}
1147
1148/// Level assignment box
1149///
1150/// ISO/IEC 14496-12 - 8.8.13
1151#[derive(IsoBox, Debug, PartialEq, Eq)]
1152#[iso_box(box_type = b"leva", crate_path = crate)]
1153pub struct LevelAssignmentBox {
1154    /// The full box header.
1155    pub full_header: FullBoxHeader,
1156    /// Specifies the number of levels each fraction is grouped into. `level_count` shall be greater
1157    /// than or equal to 2.
1158    pub level_count: u8,
1159    /// `track_ID`, `padding_flag`, `assignment_type` and [`LevelAssignmentBoxLevelAssignmentType`].
1160    #[iso_box(repeated)]
1161    pub levels: Vec<LevelAssignmentBoxLevel>,
1162}
1163
1164/// Level in a [`LevelAssignmentBox`].
1165#[derive(Debug, PartialEq, Eq)]
1166pub struct LevelAssignmentBoxLevel {
1167    /// For loop entry `j` specifies the track identifier of the track assigned to level `j`.
1168    pub track_id: u32,
1169    /// Equal to 1 indicates that a conforming fraction can be formed by concatenating any
1170    /// positive integer number of levels within a fraction and padding the last [`MediaDataBox`](super::MediaDataBox)
1171    /// by zero bytes up to the full size that is indicated in the header of the last [`MediaDataBox`](super::MediaDataBox).
1172    /// When `padding_flag` is equal to 0 this is not assured.
1173    pub padding_flag: bool,
1174    /// Specifies the type of level assignment.
1175    pub assignment_type: LevelAssignmentBoxLevelAssignmentType,
1176}
1177
1178impl<'a> Deserialize<'a> for LevelAssignmentBoxLevel {
1179    fn deserialize<R>(mut reader: R) -> io::Result<Self>
1180    where
1181        R: ZeroCopyReader<'a>,
1182    {
1183        let track_id = u32::deserialize(&mut reader)?;
1184        let byte = u8::deserialize(&mut reader)?;
1185        let padding_flag = (byte >> 7) == 1;
1186        let assignment_type = byte & 0b01111111;
1187        let assignment_type = match assignment_type {
1188            0 => LevelAssignmentBoxLevelAssignmentType::Type0 {
1189                grouping_type: <[u8; 4]>::deserialize(&mut reader)?,
1190            },
1191            1 => LevelAssignmentBoxLevelAssignmentType::Type1 {
1192                grouping_type: <[u8; 4]>::deserialize(&mut reader)?,
1193                grouping_type_parameter: u32::deserialize(&mut reader)?,
1194            },
1195            2 => LevelAssignmentBoxLevelAssignmentType::Type2,
1196            3 => LevelAssignmentBoxLevelAssignmentType::Type3,
1197            4 => LevelAssignmentBoxLevelAssignmentType::Type4 {
1198                sub_track_id: u32::deserialize(&mut reader)?,
1199            },
1200            _ => LevelAssignmentBoxLevelAssignmentType::Other(assignment_type),
1201        };
1202
1203        Ok(Self {
1204            track_id,
1205            padding_flag,
1206            assignment_type,
1207        })
1208    }
1209}
1210
1211impl Serialize for LevelAssignmentBoxLevel {
1212    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
1213    where
1214        W: std::io::Write,
1215    {
1216        self.track_id.serialize(&mut writer)?;
1217
1218        let mut byte = (self.padding_flag as u8) << 7;
1219        byte |= self.assignment_type.assignment_type() & 0b01111111;
1220        byte.serialize(&mut writer)?;
1221
1222        match self.assignment_type {
1223            LevelAssignmentBoxLevelAssignmentType::Type0 { grouping_type } => {
1224                grouping_type.serialize(&mut writer)?;
1225            }
1226            LevelAssignmentBoxLevelAssignmentType::Type1 {
1227                grouping_type,
1228                grouping_type_parameter,
1229            } => {
1230                grouping_type.serialize(&mut writer)?;
1231                grouping_type_parameter.serialize(&mut writer)?;
1232            }
1233            LevelAssignmentBoxLevelAssignmentType::Type4 { sub_track_id } => {
1234                sub_track_id.serialize(&mut writer)?;
1235            }
1236            _ => {}
1237        }
1238
1239        Ok(())
1240    }
1241}
1242
1243impl IsoSized for LevelAssignmentBoxLevel {
1244    fn size(&self) -> usize {
1245        let mut size = 4; // track_id
1246        size += 1; // padding_flag + assignment_type
1247
1248        match self.assignment_type {
1249            LevelAssignmentBoxLevelAssignmentType::Type0 { .. } => size += 4,
1250            LevelAssignmentBoxLevelAssignmentType::Type1 { .. } => size += 8,
1251            LevelAssignmentBoxLevelAssignmentType::Type4 { .. } => size += 4,
1252            _ => {}
1253        }
1254
1255        size
1256    }
1257}
1258
1259/// Type of level assignment in a [`LevelAssignmentBox`].
1260#[derive(Debug, PartialEq, Eq)]
1261pub enum LevelAssignmentBoxLevelAssignmentType {
1262    /// Type 0: sample groups are used to specify levels, i.e., samples mapped to different sample group
1263    /// description indexes of a particular sample grouping lie in different levels within the identified
1264    /// track; other tracks are not affected and shall have all their data in precisely one level;
1265    Type0 {
1266        /// Specifies the sample grouping used to
1267        /// map sample group description entries in the [`SampleGroupDescriptionBox`] to levels. Level `n`
1268        /// contains the samples that are mapped to the [`SampleGroupDescriptionEntry`](super::SampleGroupDescriptionEntry)
1269        /// having index `n` in grouping_type the [`SampleGroupDescriptionBox`] having the same values of
1270        /// `grouping_type` and `grouping_type_parameter`, if present, as those provided in this box.
1271        grouping_type: [u8; 4],
1272    },
1273    /// Type 1: as for [`Type0`](Self::Type0) except assignment is by a parameterized sample group;
1274    Type1 {
1275        /// Specifies the sample grouping used to
1276        /// map sample group description entries in the [`SampleGroupDescriptionBox`] to levels. Level `n`
1277        /// contains the samples that are mapped to the [`SampleGroupDescriptionEntry`](super::SampleGroupDescriptionEntry)
1278        /// having index `n` in grouping_type the [`SampleGroupDescriptionBox`] having the same values of
1279        /// `grouping_type` and `grouping_type_parameter`, if present, as those provided in this box.
1280        grouping_type: [u8; 4],
1281        /// Specifies the sample grouping used to
1282        /// map sample group description entries in the [`SampleGroupDescriptionBox`] to levels. Level `n`
1283        /// contains the samples that are mapped to the [`SampleGroupDescriptionEntry`](super::SampleGroupDescriptionEntry)
1284        /// having index `n` in grouping_type the [`SampleGroupDescriptionBox`] having the same values of
1285        /// `grouping_type` and `grouping_type_parameter`, if present, as those provided in this box.
1286        grouping_type_parameter: u32,
1287    },
1288    /// Type 2: level assignment is by track (see the [`SubsegmentIndexBox`](super::SubsegmentIndexBox)
1289    /// for the difference in processing of these levels)
1290    Type2,
1291    /// Type 3: level assignment is by track (see the [`SubsegmentIndexBox`](super::SubsegmentIndexBox)
1292    /// for the difference in processing of these levels)
1293    Type3,
1294    /// Type 4: the respective level contains the samples for a sub-track. The sub-tracks are specified through
1295    /// the [`SubTrackBox`](super::SubTrackBox);
1296    /// other tracks are not affected and shall have all their data in precisely one level;
1297    Type4 {
1298        /// Specifies that the sub-track identified by `sub_track_ID` within loop entry `j` is mapped to level `j`.
1299        sub_track_id: u32,
1300    },
1301    /// Other assignment type.
1302    Other(u8),
1303}
1304
1305impl LevelAssignmentBoxLevelAssignmentType {
1306    /// Returns the assignment type as a u8.
1307    pub fn assignment_type(&self) -> u8 {
1308        match self {
1309            LevelAssignmentBoxLevelAssignmentType::Type0 { .. } => 0,
1310            LevelAssignmentBoxLevelAssignmentType::Type1 { .. } => 1,
1311            LevelAssignmentBoxLevelAssignmentType::Type2 => 2,
1312            LevelAssignmentBoxLevelAssignmentType::Type3 => 3,
1313            LevelAssignmentBoxLevelAssignmentType::Type4 { .. } => 4,
1314            LevelAssignmentBoxLevelAssignmentType::Other(v) => *v,
1315        }
1316    }
1317}
1318
1319/// Track Extension Properties box
1320///
1321/// ISO/IEC 14496-12 - 8.8.15
1322#[derive(IsoBox, Debug, PartialEq, Eq)]
1323#[iso_box(box_type = b"trep", crate_path = crate)]
1324pub struct TrackExtensionPropertiesBox<'a> {
1325    /// The full box header.
1326    pub full_header: FullBoxHeader,
1327    /// Indicates the track for which the track extension properties are provided in this box.
1328    pub track_id: u32,
1329    /// The contained [`CompositionToDecodeBox`]. (optional)
1330    #[iso_box(nested_box(collect))]
1331    pub cslg: Option<CompositionToDecodeBox>,
1332    /// The contained [`AlternativeStartupSequencePropertiesBox`]. (optional)
1333    #[iso_box(nested_box(collect))]
1334    pub assp: Option<AlternativeStartupSequencePropertiesBox>,
1335    /// Any other boxes that were not recognized during deserialization.
1336    #[iso_box(nested_box(collect_unknown))]
1337    pub unknown_boxes: Vec<UnknownBox<'a>>,
1338}
1339
1340/// Alternative startup sequence properties box
1341///
1342/// ISO/IEC 14496-12 - 8.8.16
1343#[derive(IsoBox, Debug, PartialEq, Eq)]
1344#[iso_box(box_type = b"assp", skip_impl(deserialize_seed, serialize), crate_path = crate)]
1345pub struct AlternativeStartupSequencePropertiesBox {
1346    /// The full box header.
1347    pub full_header: FullBoxHeader,
1348    /// `min_initial_alt_startup_offset` or `grouping_type_parameter` and `min_initial_alt_startup_offset`
1349    /// depending on the version.
1350    pub version: AlternativeStartupSequencePropertiesBoxVersion,
1351}
1352
1353impl<'a> DeserializeSeed<'a, BoxHeader> for AlternativeStartupSequencePropertiesBox {
1354    fn deserialize_seed<R>(mut reader: R, _seed: BoxHeader) -> io::Result<Self>
1355    where
1356        R: ZeroCopyReader<'a>,
1357    {
1358        let full_header = FullBoxHeader::deserialize(&mut reader)?;
1359
1360        let version = match full_header.version {
1361            0 => AlternativeStartupSequencePropertiesBoxVersion::Version0 {
1362                min_initial_alt_startup_offset: i32::deserialize(&mut reader)?,
1363            },
1364            1 => {
1365                let num_entries = u32::deserialize(&mut reader)?;
1366                AlternativeStartupSequencePropertiesBoxVersion::Version1 {
1367                    num_entries,
1368                    entries: {
1369                        let mut entries = Vec::with_capacity(num_entries as usize);
1370
1371                        for _ in 0..num_entries {
1372                            entries.push(AlternativeStartupSequencePropertiesBoxVersion1Entry::deserialize(
1373                                &mut reader,
1374                            )?);
1375                        }
1376
1377                        entries
1378                    },
1379                }
1380            }
1381            v => AlternativeStartupSequencePropertiesBoxVersion::Other(v),
1382        };
1383
1384        Ok(Self { full_header, version })
1385    }
1386}
1387
1388impl Serialize for AlternativeStartupSequencePropertiesBox {
1389    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
1390    where
1391        W: std::io::Write,
1392    {
1393        self.serialize_box_header(&mut writer)?;
1394        self.full_header.serialize(&mut writer)?;
1395
1396        match &self.version {
1397            AlternativeStartupSequencePropertiesBoxVersion::Version0 {
1398                min_initial_alt_startup_offset,
1399            } => {
1400                min_initial_alt_startup_offset.serialize(&mut writer)?;
1401            }
1402            AlternativeStartupSequencePropertiesBoxVersion::Version1 { num_entries, entries } => {
1403                num_entries.serialize(&mut writer)?;
1404                for entry in entries {
1405                    entry.serialize(&mut writer)?;
1406                }
1407            }
1408            _ => {}
1409        }
1410
1411        Ok(())
1412    }
1413}
1414
1415/// Version of the [`AlternativeStartupSequencePropertiesBox`].
1416#[derive(Debug, PartialEq, Eq)]
1417pub enum AlternativeStartupSequencePropertiesBoxVersion {
1418    /// Version 0.
1419    Version0 {
1420        /// No value of `sample_offset`(3GPP) of the referred sample group
1421        /// description entries of the alternative startup sequence sample grouping shall be smaller than
1422        /// min_initial_alt_startup_offset. In version 0 of this box, the alternative startup sequence sample
1423        /// grouping using version 0 of the Sample to Group box is referred to. In version 1 of this box, the
1424        /// alternative startup sequence sample grouping using version 1 of the [`SampleToGroupBox`] is referred
1425        /// to as further constrained by `grouping_type_parameter`.
1426        min_initial_alt_startup_offset: i32,
1427    },
1428    /// Version 1.
1429    Version1 {
1430        /// Indicates the number of alternative startup sequence sample groupings documented in this box.
1431        num_entries: u32,
1432        /// `grouping_type_parameter` and `min_initial_alt_startup_offset`.
1433        entries: Vec<AlternativeStartupSequencePropertiesBoxVersion1Entry>,
1434    },
1435    /// Any other version.
1436    Other(u8),
1437}
1438
1439impl IsoSized for AlternativeStartupSequencePropertiesBoxVersion {
1440    fn size(&self) -> usize {
1441        match self {
1442            AlternativeStartupSequencePropertiesBoxVersion::Version0 { .. } => 4,
1443            AlternativeStartupSequencePropertiesBoxVersion::Version1 { entries, .. } => 4 + entries.size(),
1444            _ => 0,
1445        }
1446    }
1447}
1448
1449/// Entry in a [`AlternativeStartupSequencePropertiesBox`] version 1.
1450///
1451/// See [`AlternativeStartupSequencePropertiesBoxVersion`].
1452#[derive(Debug, PartialEq, Eq)]
1453pub struct AlternativeStartupSequencePropertiesBoxVersion1Entry {
1454    /// Indicates which one of the alternative sample groupings this loop entry applies to.
1455    pub grouping_type_parameter: u32,
1456    /// No value of `sample_offset`(3GPP) of the referred sample group
1457    /// description entries of the alternative startup sequence sample grouping shall be smaller than
1458    /// min_initial_alt_startup_offset. In version 0 of this box, the alternative startup sequence sample
1459    /// grouping using version 0 of the Sample to Group box is referred to. In version 1 of this box, the
1460    /// alternative startup sequence sample grouping using version 1 of the [`SampleToGroupBox`] is referred
1461    /// to as further constrained by `grouping_type_parameter`.
1462    pub min_initial_alt_startup_offset: i32,
1463}
1464
1465impl<'a> Deserialize<'a> for AlternativeStartupSequencePropertiesBoxVersion1Entry {
1466    fn deserialize<R>(mut reader: R) -> io::Result<Self>
1467    where
1468        R: ZeroCopyReader<'a>,
1469    {
1470        let grouping_type_parameter = u32::deserialize(&mut reader)?;
1471        let min_initial_alt_startup_offset = i32::deserialize(&mut reader)?;
1472
1473        Ok(Self {
1474            grouping_type_parameter,
1475            min_initial_alt_startup_offset,
1476        })
1477    }
1478}
1479
1480impl Serialize for AlternativeStartupSequencePropertiesBoxVersion1Entry {
1481    fn serialize<W>(&self, mut writer: W) -> io::Result<()>
1482    where
1483        W: std::io::Write,
1484    {
1485        self.grouping_type_parameter.serialize(&mut writer)?;
1486        self.min_initial_alt_startup_offset.serialize(&mut writer)?;
1487        Ok(())
1488    }
1489}
1490
1491impl IsoSized for AlternativeStartupSequencePropertiesBoxVersion1Entry {
1492    fn size(&self) -> usize {
1493        4 + 4
1494    }
1495}