1use std::fmt::Debug;
2
3use scuffle_bytes_util::zero_copy::{Deserialize, DeserializeSeed};
4
5use crate::boxes::{
6 ExtendedTypeBox, FileTypeBox, IdentifiedMediaDataBox, MediaDataBox, MetaBox, MovieBox, MovieFragmentBox,
7 MovieFragmentRandomAccessBox, OriginalFileTypeBox, ProducerReferenceTimeBox, ProgressiveDownloadInfoBox,
8 SegmentIndexBox, SegmentTypeBox, SubsegmentIndexBox,
9};
10use crate::{BoxHeader, BoxSize, BoxType, IsoBox, UnknownBox};
11
12#[derive(IsoBox, Debug, PartialEq, Eq)]
17#[iso_box(skip_impl(iso_box, deserialize), crate_path = crate)]
18pub struct IsobmffFile<'a> {
19 #[iso_box(nested_box(collect))]
24 pub ftyp: Option<FileTypeBox>,
25 #[iso_box(nested_box(collect))]
27 pub etyp: Vec<ExtendedTypeBox<'a>>,
28 #[iso_box(nested_box(collect))]
30 pub otyp: Vec<OriginalFileTypeBox<'a>>,
31 #[iso_box(nested_box(collect))]
33 pub pdin: Option<ProgressiveDownloadInfoBox>,
34 #[iso_box(nested_box(collect))]
43 pub moov: Option<MovieBox<'a>>,
44 #[iso_box(nested_box(collect))]
46 pub moof: Vec<MovieFragmentBox<'a>>,
47 #[iso_box(nested_box(collect))]
49 pub mdat: Vec<MediaDataBox<'a>>,
50 #[iso_box(nested_box(collect))]
52 pub imda: Vec<IdentifiedMediaDataBox<'a>>,
53 #[iso_box(nested_box(collect))]
54 pub meta: Option<MetaBox<'a>>,
56 #[iso_box(nested_box(collect))]
58 pub styp: Vec<SegmentTypeBox>,
59 #[iso_box(nested_box(collect))]
61 pub sidx: Vec<SegmentIndexBox>,
62 #[iso_box(nested_box(collect))]
64 pub ssix: Vec<SubsegmentIndexBox>,
65 #[iso_box(nested_box(collect))]
67 pub prft: Vec<ProducerReferenceTimeBox>,
68 #[iso_box(nested_box(collect_unknown))]
70 pub unknown_boxes: Vec<UnknownBox<'a>>,
71 #[iso_box(nested_box(collect))]
73 pub mfra: Option<MovieFragmentRandomAccessBox>,
74}
75
76impl<'a> Deserialize<'a> for IsobmffFile<'a> {
77 fn deserialize<R>(reader: R) -> std::io::Result<Self>
78 where
79 R: scuffle_bytes_util::zero_copy::ZeroCopyReader<'a>,
80 {
81 Self::deserialize_seed(
82 reader,
83 BoxHeader {
84 size: BoxSize::ToEnd,
85 box_type: BoxType::FourCc(*b"root"),
86 },
87 )
88 }
89}
90
91impl IsoBox for IsobmffFile<'_> {
94 const TYPE: BoxType = BoxType::Uuid(uuid::Uuid::nil());
95
96 fn add_header_size(payload_size: usize) -> usize {
97 payload_size
99 }
100
101 fn serialize_box_header<W>(&self, _writer: W) -> std::io::Result<()>
102 where
103 W: std::io::Write,
104 {
105 Ok(())
107 }
108}
109
110#[cfg(test)]
111#[cfg_attr(all(test, coverage_nightly), coverage(off))]
112mod tests {
113 use std::io;
114 use std::path::PathBuf;
115
116 use scuffle_bytes_util::zero_copy::{Deserialize, Serialize};
117
118 use super::IsobmffFile;
119 use crate::IsoSized;
120
121 fn transmux_sample(sample_name: &str, skip_insta: bool) -> io::Result<()> {
122 let test_name = sample_name.split('.').next().unwrap();
123
124 let dir = PathBuf::from(env!("CARGO_MANIFEST_DIR")).join("../../assets");
125 let data = std::fs::read(dir.join(sample_name))?;
126 let mut reader = scuffle_bytes_util::zero_copy::Slice::from(&data[..]);
127 let og_file = IsobmffFile::deserialize(&mut reader)?;
128 if !skip_insta {
129 insta::assert_debug_snapshot!(test_name, og_file);
130 }
131 assert_eq!(og_file.size(), data.len());
132
133 let mut out_data = Vec::new();
134 og_file.serialize(&mut out_data)?;
135 assert_eq!(out_data.len(), data.len());
136
137 let mut reader = scuffle_bytes_util::zero_copy::Slice::from(&out_data[..]);
138 let file = IsobmffFile::deserialize(&mut reader)?;
139 if !skip_insta {
140 insta::assert_debug_snapshot!(test_name, file);
141 }
142
143 Ok(())
144 }
145
146 #[test]
147 fn avc_aac_sample() {
148 transmux_sample("avc_aac.mp4", false).unwrap();
149 }
150
151 #[test]
152 fn avc_aac_large_sample() {
153 transmux_sample("avc_aac_large.mp4", false).unwrap();
154 }
155
156 #[test]
157 fn avc_aac_fragmented_sample() {
158 transmux_sample("avc_aac_fragmented.mp4", false).unwrap();
159 }
160
161 #[test]
162 fn avc_aac_keyframes_sample() {
163 transmux_sample("avc_aac_keyframes.mp4", false).unwrap();
164 }
165
166 #[test]
167 fn hevc_aac_fragmented_sample() {
168 transmux_sample("hevc_aac_fragmented.mp4", true).unwrap();
170 }
171
172 #[test]
173 fn av1_aac_fragmented_sample() {
174 transmux_sample("av1_aac_fragmented.mp4", true).unwrap();
176 }
177}