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 file_path(item: &str) -> PathBuf {
122 if let Some(env) = std::env::var_os("MP4_ASSETS_DIR") {
123 PathBuf::from(env).join(item)
124 } else {
125 PathBuf::from(env!("CARGO_MANIFEST_DIR")).join(format!("../../assets/{item}"))
126 }
127 }
128
129 fn transmux_sample(sample_name: &str, skip_insta: bool) -> io::Result<()> {
130 let test_name = sample_name.split('.').next().unwrap();
131
132 let data = std::fs::read(file_path(sample_name))?;
133 let mut reader = scuffle_bytes_util::zero_copy::Slice::from(&data[..]);
134 let og_file = IsobmffFile::deserialize(&mut reader)?;
135 if !skip_insta {
136 insta::assert_debug_snapshot!(test_name, og_file);
137 }
138 assert_eq!(og_file.size(), data.len());
139
140 let mut out_data = Vec::new();
141 og_file.serialize(&mut out_data)?;
142 assert_eq!(out_data.len(), data.len());
143
144 let mut reader = scuffle_bytes_util::zero_copy::Slice::from(&out_data[..]);
145 let file = IsobmffFile::deserialize(&mut reader)?;
146 if !skip_insta {
147 insta::assert_debug_snapshot!(test_name, file);
148 }
149
150 Ok(())
151 }
152
153 #[test]
154 fn avc_aac_sample() {
155 transmux_sample("avc_aac.mp4", false).unwrap();
156 }
157
158 #[test]
159 fn avc_aac_large_sample() {
160 transmux_sample("avc_aac_large.mp4", false).unwrap();
161 }
162
163 #[test]
164 fn avc_aac_fragmented_sample() {
165 transmux_sample("avc_aac_fragmented.mp4", false).unwrap();
166 }
167
168 #[test]
169 fn avc_aac_keyframes_sample() {
170 transmux_sample("avc_aac_keyframes.mp4", false).unwrap();
171 }
172
173 #[test]
174 fn hevc_aac_fragmented_sample() {
175 transmux_sample("hevc_aac_fragmented.mp4", true).unwrap();
177 }
178
179 #[test]
180 fn av1_aac_fragmented_sample() {
181 transmux_sample("av1_aac_fragmented.mp4", true).unwrap();
183 }
184}