1#[allow(unused_variables)]
2#[allow(unused_imports)]
3pub mod base;
4pub mod r#macro;
5
6pub use base::Either;
7
8pub mod boxes {
9 use crate::r#macro::mp4box_gen;
10
11 mp4box_gen! { version flags;
12 Moof : Container,
13 Mfhd : Full {
14 seq_num: u32,
15 },
16 Traf : Container,
17 Tfhd : Full {
18 track_id: u32,
19 base_data_offset: u64 [if flags & 0x000001 != 0],
20 sample_description_index: u32 [if flags & 0x000002 != 0],
21 default_sample_duration: u32 [if flags & 0x000008 != 0],
22 default_sample_size: u32 [if flags & 0x000010 != 0],
23 default_sample_flags: u32 [if flags & 0x000020 != 0]
24 },
25 Tfdt : Full {
26 base_media_decode_time: u64,
27 },
28 Senc : Full {
29 sample_count: u32,
30 samples: [sample_count] {
31 iv: [u8; 8],
32 subsample_count: u16 [if flags & 0x000002 != 0],
33 subsamples: [subsample_count.unwrap()] {
34 clear_bytes: u16,
35 cipher_bytes: u32,
36 } [if flags & 0x000002 != 0]
37 }
38 },
39 Saiz : Full {
40 aux_info_type: u32 [if flags & 0x000001 != 0],
41 aux_info_type_parameter: u32 [if flags & 0x000001 != 0],
42
43 default_sample_info_size: u8,
44 sample_count: u32,
45 sample_info_size: u8 [if default_sample_info_size == 0]
46 },
47 Saio : Full {
48 aux_info_type: u32 [if flags & 0x000001 != 0],
49 aux_info_type_parameter: u32 [if flags & 0x000001 != 0],
50
51 offset_count: u32,
52 offsets: [offset_count] {
53 offset: [u32, u64] [if version == 1], },
55 },
56 Trun : Full {
57 sample_count: u32,
58
59 data_offset: i32 [if flags & 0x000001 != 0],
60 first_sample_flags: u32 [if flags & 0x000004 != 0],
61
62 samples: [sample_count] {
63 sample_duration: u32 [if flags & 0x000100 != 0],
64 sample_size: u32 [if flags & 0x000200 != 0],
65 sample_flags: u32 [if flags & 0x000400 != 0],
66 sample_composition_time_offset: [u32, i32] [if version == 1] [if flags & 0x000800 != 0],
69 }
70 },
71 Mdat : Container = u8,
72 Ftyp {
73 major_brand: [u8; 4],
74 minor_version: u32,
75 compatible_brands: Vec<[u8; 4]>,
76 },
77 Moov : Container,
78 Mvhd : Full {
79 creation_time: [u32, u64] [if version == 1],
80 modification_time: [u32, u64] [if version == 1],
81 timescale: u32,
82 duration: [u32, u64] [if version == 1],
83
84 rate: i32,
85 volume: i16,
86
87 _reserved: [u8; 10],
89
90 matrix: [i32; 9],
91 pre_defined: [u32; 6],
92 next_track_id: u32,
93 },
94 Trak : Container,
95 Tkhd : Full {
96 creation_time: [u32, u64] [if version == 1],
97 modification_time: [u32, u64] [if version == 1],
98 track_id: u32,
99
100 _reserved: [u8; 4],
102
103 duration: [u32, u64] [if version == 1],
104
105 _reserved1: [u8; 8],
107
108 layer: i16,
109 alternate_group: i16,
110 volume: i16,
111
112 _reserved2: [u8; 2],
114
115 matrix: [i32; 9],
116 width: u32,
117 height: u32,
118 },
119 Mdia : Container,
120 Mdhd : Full {
121 creation_time: [u32, u64] [if version == 1],
122 modification_time: [u32, u64] [if version == 1],
123 timescale: u32,
124 duration: [u32, u64] [if version == 1],
125
126 language: u16,
128
129 _reserved1: [u8; 2],
131 },
132 Hdlr : Full {
133 _reserved1: [u8; 4],
135
136 handler_type: [u8; 4], _reserved2: [u8; 12],
140
141 name: String,
142 },
143 Minf : Container,
144 Smhd : Full {
145 balance: i16,
146
147 _reserved1: [u8; 2],
149 },
150 Dinf : Container,
151 Dref : Skip,
152 Stbl : Container,
153 Stsd : Skip,
168 Stts : Full {
169 entry_count: u32,
170 entries: [entry_count] {
171 sample_count: u32,
172 sample_delta: u32,
173 },
174 },
175 Stsc : Full {
176 entry_count: u32,
177 entries: [entry_count] {
178 first_chunk: u32,
179 samples_per_chunk: u32,
180 sample_description_index: u32,
181 },
182 },
183 Stsz : Full {
185 sample_size: u32,
186 sample_count: u32,
187 entry_size: [sample_count] {
188 size: u32,
189 } [if sample_size == 0],
190 },
191 Stco : Full {
192 entry_count: u32,
193 chunk_offset: [entry_count] {
194 offset: u32,
195 },
196 },
197 Udta : Skip,
198 Mvex : Container,
199 Trex : Full {
200 track_id: u32,
201 default_sample_description_index: u32,
202 default_sample_duration: u32,
203 default_sample_size: u32,
204 default_sample_flags: u32,
205 },
206 Pssh : Skip,
207 Free : Skip,
208 Edts : Skip,
209 Sgpd : Skip,
210 Sbgp : Skip,
211 Vmhd : Skip,
212 }
213}
214
215use base::*;
216use boxes::*;
217
218pub use boxes::Mp4Box;
219
220pub fn parse_mp4(input: &[u8]) -> Vec<Mp4Box> {
221 let mut state = ParserState { offset: 0 };
222 let mut boxes = vec![];
223
224 while !is_empty(input, &state) {
225 if let Some(box_) = parse_box(input, &mut state) {
226 boxes.push(box_);
227 }
228 }
229
230 boxes
231}
232
233pub fn write_mp4(boxes: &[Mp4Box]) -> Vec<u8> {
234 let mut buf = vec![];
235
236 for box_ in boxes {
237 box_.write(&mut buf);
238 }
239
240 buf
241}
242
243pub fn find_box_mut<'a>(boxes: &'a mut [Mp4Box], box_type: &'a [u8; 4]) -> Option<&'a mut Mp4Box> {
245 let box_type = u32::from_ne_bytes(*box_type);
246
247 let mut next_search = vec![boxes];
248
249 while let Some(boxes) = next_search.pop() {
250 for box_ in boxes {
251 if is_box_type(box_, box_type) {
252 return Some(box_);
253 }
254
255 match box_ {
257 Mp4Box::Moof(box_) => {
258 next_search.push(box_.data.as_mut_slice());
259 }
260 Mp4Box::Traf(box_) => {
261 next_search.push(box_.data.as_mut_slice());
262 }
263 Mp4Box::Moov(box_) => {
264 next_search.push(box_.data.as_mut_slice());
265 }
266 Mp4Box::Trak(box_) => {
267 next_search.push(box_.data.as_mut_slice());
268 }
269 Mp4Box::Mdia(box_) => {
270 next_search.push(box_.data.as_mut_slice());
271 }
272 Mp4Box::Minf(box_) => {
273 next_search.push(box_.data.as_mut_slice());
274 }
275 Mp4Box::Dinf(box_) => {
276 next_search.push(box_.data.as_mut_slice());
277 }
278 Mp4Box::Stbl(box_) => {
279 next_search.push(box_.data.as_mut_slice());
280 }
281 Mp4Box::Mvex(box_) => {
282 next_search.push(box_.data.as_mut_slice());
283 }
284 _ => {}
285 }
286 }
287 }
288
289 None
290}
291
292pub fn find_box<'a>(boxes: &'a [Mp4Box], box_type: &'a [u8; 4]) -> Option<&'a Mp4Box> {
293 let box_type = u32::from_ne_bytes(*box_type);
294
295 let mut next_search = vec![boxes];
296
297 while let Some(boxes) = next_search.pop() {
298 for box_ in boxes {
299 if is_box_type(box_, box_type) {
300 return Some(box_);
301 }
302
303 match box_ {
305 Mp4Box::Moof(box_) => {
306 next_search.push(box_.data.as_slice());
307 }
308 Mp4Box::Traf(box_) => {
309 next_search.push(box_.data.as_slice());
310 }
311 Mp4Box::Moov(box_) => {
312 next_search.push(box_.data.as_slice());
313 }
314 Mp4Box::Trak(box_) => {
315 next_search.push(box_.data.as_slice());
316 }
317 Mp4Box::Mdia(box_) => {
318 next_search.push(box_.data.as_slice());
319 }
320 Mp4Box::Minf(box_) => {
321 next_search.push(box_.data.as_slice());
322 }
323 Mp4Box::Dinf(box_) => {
324 next_search.push(box_.data.as_slice());
325 }
326 Mp4Box::Stbl(box_) => {
327 next_search.push(box_.data.as_slice());
328 }
329 Mp4Box::Mvex(box_) => {
330 next_search.push(box_.data.as_slice());
331 }
332 _ => {}
333 }
334 }
335 }
336
337 None
338}
339
340pub fn list_box_tree(boxes: &[Mp4Box], indent: usize) {
341 for box_ in boxes {
342 let name = get_box_type(box_);
343 println!("{:indent$}{}", "", name, indent = indent * 2);
344
345 match box_ {
346 Mp4Box::Moof(box_) => {
347 list_box_tree(&box_.data, indent + 1);
348 }
349 Mp4Box::Traf(box_) => {
350 list_box_tree(&box_.data, indent + 1);
351 }
352 Mp4Box::Moov(box_) => {
353 list_box_tree(&box_.data, indent + 1);
354 }
355 Mp4Box::Trak(box_) => {
356 list_box_tree(&box_.data, indent + 1);
357 }
358 Mp4Box::Mdia(box_) => {
359 list_box_tree(&box_.data, indent + 1);
360 }
361 Mp4Box::Minf(box_) => {
362 list_box_tree(&box_.data, indent + 1);
363 }
364 Mp4Box::Dinf(box_) => {
365 list_box_tree(&box_.data, indent + 1);
366 }
367 Mp4Box::Stbl(box_) => {
368 list_box_tree(&box_.data, indent + 1);
369 }
370 Mp4Box::Mvex(box_) => {
371 list_box_tree(&box_.data, indent + 1);
372 }
373 _ => {}
374 }
375 }
376}