bmf_parse/
lib.rs

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], // u64 if version == 1, u32 if version == 0
54            },
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                // i32 if version == 1, u32 if version == 0, only present if flags & 0x000800
67                // [A, B, C..] [C.. condition] [BC condition] [ABC/present condition]
68                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            // 10 reserved bytes
88            _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            // 4 reserved bytes
101            _reserved: [u8; 4],
102
103            duration: [u32, u64] [if version == 1],
104
105            // 8 reserved bytes
106            _reserved1: [u8; 8],
107
108            layer: i16,
109            alternate_group: i16,
110            volume: i16,
111
112            // 2 reserved bytes
113            _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            // Ignore first bit of language
127            language: u16,
128
129            // 2 reserved bytes
130            _reserved1: [u8; 2],
131        },
132        Hdlr : Full {
133            // 4 reserved bytes
134            _reserved1: [u8; 4],
135
136            handler_type: [u8; 4], // String
137
138            // 12 reserved bytes
139            _reserved2: [u8; 12],
140
141            name: String,
142        },
143        Minf : Container,
144        Smhd : Full {
145            balance: i16,
146
147            // 2 reserved bytes
148            _reserved1: [u8; 2],
149        },
150        Dinf : Container,
151        Dref : Skip,
152        Stbl : Container,
153        /* TODO: Implement this properly
154        Stsd : Full {
155            entry_count: u32,
156            entries: [entry_count] {
157                _size: u32,
158                format: [u8; 4], // String
159
160                // 6 reserved bytes
161                _reserved: [u8; 6],
162
163                data_reference_index: u16,
164            },
165        },
166        */
167        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        // TODO: Make this not an Optional vec & just have it be a vec of size 0 if cond not met
184        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
243// recursive search for box_type
244pub 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            // TODO: macro-ify this part
256            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            // TODO: macro-ify this part
304            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}