1 #![cfg(feature = "stream")]
2 #![allow(dead_code)]
3 
4 #[macro_use]
5 extern crate nom;
6 
7 use nom::{IResult, Needed, be_f32, be_u16, be_u32, be_u64};
8 //use nom::{Consumer,ConsumerState,Move,Input,Producer,FileProducer,FileProducerState};
9 //use nom::IResult;
10 use nom::{Err, ErrorKind};
11 
12 use std::str;
13 
mp4_box(input: &[u8]) -> IResult<&[u8], &[u8]>14 fn mp4_box(input: &[u8]) -> IResult<&[u8], &[u8]> {
15   match be_u32(input) {
16     Ok((i, offset)) => {
17       let sz: usize = offset as usize;
18       if i.len() >= sz - 4 {
19         Ok((&i[(sz - 4)..], &i[0..(sz - 4)]))
20       } else {
21         Err(Err::Incomplete(Needed::Size(offset as usize + 4)))
22       }
23     }
24     Err(e) => Err(e),
25   }
26 }
27 
28 #[cfg_attr(rustfmt, rustfmt_skip)]
29 #[derive(PartialEq,Eq,Debug)]
30 struct FileType<'a> {
31   major_brand:         &'a str,
32   major_brand_version: &'a [u8],
33   compatible_brands:   Vec<&'a str>
34 }
35 
36 #[cfg_attr(rustfmt, rustfmt_skip)]
37 #[allow(non_snake_case)]
38 #[derive(Debug,Clone)]
39 pub struct Mvhd32 {
40   version_flags: u32, // actually:
41   // version: u8,
42   // flags: u24       // 3 bytes
43   created_date:  u32,
44   modified_date: u32,
45   scale:         u32,
46   duration:      u32,
47   speed:         f32,
48   volume:        u16, // actually a 2 bytes decimal
49   /* 10 bytes reserved */
50   scaleA:        f32,
51   rotateB:       f32,
52   angleU:        f32,
53   rotateC:       f32,
54   scaleD:        f32,
55   angleV:        f32,
56   positionX:     f32,
57   positionY:     f32,
58   scaleW:        f32,
59   preview:       u64,
60   poster:        u32,
61   selection:     u64,
62   current_time:  u32,
63   track_id:      u32
64 }
65 
66 #[cfg_attr(rustfmt, rustfmt_skip)]
67 #[allow(non_snake_case)]
68 #[derive(Debug,Clone)]
69 pub struct Mvhd64 {
70   version_flags: u32, // actually:
71   // version: u8,
72   // flags: u24       // 3 bytes
73   created_date:  u64,
74   modified_date: u64,
75   scale:         u32,
76   duration:      u64,
77   speed:         f32,
78   volume:        u16, // actually a 2 bytes decimal
79   /* 10 bytes reserved */
80   scaleA:        f32,
81   rotateB:       f32,
82   angleU:        f32,
83   rotateC:       f32,
84   scaleD:        f32,
85   angleV:        f32,
86   positionX:     f32,
87   positionY:     f32,
88   scaleW:        f32,
89   preview:       u64,
90   poster:        u32,
91   selection:     u64,
92   current_time:  u32,
93   track_id:      u32
94 }
95 
96 #[allow(non_snake_case)]
97 named!(mvhd32 <&[u8], MvhdBox>,
98   do_parse!(
99   version_flags: be_u32 >>
100   created_date:  be_u32 >>
101   modified_date: be_u32 >>
102   scale:         be_u32 >>
103   duration:      be_u32 >>
104   speed:         be_f32 >>
105   volume:        be_u16 >> // actually a 2 bytes decimal
106               take!(10) >>
107   scale_a:       be_f32 >>
108   rotate_b:      be_f32 >>
109   angle_u:       be_f32 >>
110   rotate_c:      be_f32 >>
111   scale_d:       be_f32 >>
112   angle_v:       be_f32 >>
113   position_x:    be_f32 >>
114   position_y:    be_f32 >>
115   scale_w:       be_f32 >>
116   preview:       be_u64 >>
117   poster:        be_u32 >>
118   selection:     be_u64 >>
119   current_time:  be_u32 >>
120   track_id:      be_u32 >>
121   (
122     MvhdBox::M32(Mvhd32 {
123       version_flags: version_flags,
124       created_date:  created_date,
125       modified_date: modified_date,
126       scale:         scale,
127       duration:      duration,
128       speed:         speed,
129       volume:        volume,
130       scaleA:        scale_a,
131       rotateB:       rotate_b,
132       angleU:        angle_u,
133       rotateC:       rotate_c,
134       scaleD:        scale_d,
135       angleV:        angle_v,
136       positionX:     position_x,
137       positionY:     position_y,
138       scaleW:        scale_w,
139       preview:       preview,
140       poster:        poster,
141       selection:     selection,
142       current_time:  current_time,
143       track_id:      track_id
144     })
145   ))
146 );
147 
148 #[allow(non_snake_case)]
149 named!(mvhd64 <&[u8], MvhdBox>,
150   do_parse!(
151   version_flags: be_u32 >>
152   created_date:  be_u64 >>
153   modified_date: be_u64 >>
154   scale:         be_u32 >>
155   duration:      be_u64 >>
156   speed:         be_f32 >>
157   volume:        be_u16 >> // actually a 2 bytes decimal
158               take!(10) >>
159   scale_a:       be_f32 >>
160   rotate_b:      be_f32 >>
161   angle_u:       be_f32 >>
162   rotate_c:      be_f32 >>
163   scale_d:       be_f32 >>
164   angle_v:       be_f32 >>
165   position_x:    be_f32 >>
166   position_y:    be_f32 >>
167   scale_w:       be_f32 >>
168   preview:       be_u64 >>
169   poster:        be_u32 >>
170   selection:     be_u64 >>
171   current_time:  be_u32 >>
172   track_id:      be_u32 >>
173   (
174     MvhdBox::M64(Mvhd64 {
175       version_flags: version_flags,
176       created_date:  created_date,
177       modified_date: modified_date,
178       scale:         scale,
179       duration:      duration,
180       speed:         speed,
181       volume:        volume,
182       scaleA:        scale_a,
183       rotateB:       rotate_b,
184       angleU:        angle_u,
185       rotateC:       rotate_c,
186       scaleD:        scale_d,
187       angleV:        angle_v,
188       positionX:     position_x,
189       positionY:     position_y,
190       scaleW:        scale_w,
191       preview:       preview,
192       poster:        poster,
193       selection:     selection,
194       current_time:  current_time,
195       track_id:      track_id
196     })
197   ))
198 );
199 
200 #[derive(Debug, Clone)]
201 pub enum MvhdBox {
202   M32(Mvhd32),
203   M64(Mvhd64),
204 }
205 
206 #[derive(Debug, Clone)]
207 pub enum MoovBox {
208   Mdra,
209   Dref,
210   Cmov,
211   Rmra,
212   Iods,
213   Mvhd(MvhdBox),
214   Clip,
215   Trak,
216   Udta,
217 }
218 
219 #[derive(Debug)]
220 enum MP4BoxType {
221   Ftyp,
222   Moov,
223   Mdat,
224   Free,
225   Skip,
226   Wide,
227   Mdra,
228   Dref,
229   Cmov,
230   Rmra,
231   Iods,
232   Mvhd,
233   Clip,
234   Trak,
235   Udta,
236   Unknown,
237 }
238 
239 #[derive(Debug)]
240 struct MP4BoxHeader {
241   length: u32,
242   tag: MP4BoxType,
243 }
244 
245 named!(brand_name<&[u8],&str>, map_res!(take!(4), str::from_utf8));
246 
247 named!(filetype_parser<&[u8], FileType>,
248   do_parse!(
249     m: brand_name          >>
250     v: take!(4)            >>
251     c: many0!(brand_name)  >>
252     (FileType{ major_brand: m, major_brand_version:v, compatible_brands: c })
253   )
254 );
255 
mvhd_box(input: &[u8]) -> IResult<&[u8], MvhdBox>256 fn mvhd_box(input: &[u8]) -> IResult<&[u8], MvhdBox> {
257   let res = if input.len() < 100 {
258     Err(Err::Incomplete(Needed::Size(100)))
259   } else if input.len() == 100 {
260     mvhd32(input)
261   } else if input.len() == 112 {
262     mvhd64(input)
263   } else {
264     Err(Err::Error(error_position!(input, ErrorKind::Custom(32u32))))
265   };
266   println!("res: {:?}", res);
267   res
268 }
269 
unknown_box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType>270 fn unknown_box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType> {
271   Ok((input, MP4BoxType::Unknown))
272 }
273 
274 //named!(box_type<&[u8], MP4BoxType>,
box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType, u32>275 fn box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType, u32> {
276   alt!(input,
277     tag!("ftyp") => { |_| MP4BoxType::Ftyp } |
278     tag!("moov") => { |_| MP4BoxType::Moov } |
279     tag!("mdat") => { |_| MP4BoxType::Mdat } |
280     tag!("free") => { |_| MP4BoxType::Free } |
281     tag!("skip") => { |_| MP4BoxType::Skip } |
282     tag!("wide") => { |_| MP4BoxType::Wide } |
283     unknown_box_type
284   )
285 }
286 
287 // warning, an alt combinator with 9 branches containing a tag combinator
288 // can make the compilation very slow. Use functions as sub parsers,
289 // or split into multiple alt! parsers if it gets slow
290 named!(moov_type<&[u8], MP4BoxType>,
291   alt!(
292     tag!("mdra") => { |_| MP4BoxType::Mdra } |
293     tag!("dref") => { |_| MP4BoxType::Dref } |
294     tag!("cmov") => { |_| MP4BoxType::Cmov } |
295     tag!("rmra") => { |_| MP4BoxType::Rmra } |
296     tag!("iods") => { |_| MP4BoxType::Iods } |
297     tag!("mvhd") => { |_| MP4BoxType::Mvhd } |
298     tag!("clip") => { |_| MP4BoxType::Clip } |
299     tag!("trak") => { |_| MP4BoxType::Trak } |
300     tag!("udta") => { |_| MP4BoxType::Udta }
301   )
302 );
303 
304 named!(box_header<&[u8],MP4BoxHeader>,
305   do_parse!(
306     length: be_u32 >>
307     tag: box_type  >>
308     (MP4BoxHeader{ length: length, tag: tag})
309   )
310 );
311 
312 named!(moov_header<&[u8],MP4BoxHeader>,
313   do_parse!(
314     length: be_u32 >>
315     tag: moov_type >>
316     (MP4BoxHeader{ length: length, tag: tag})
317   )
318 );
319 
320 /*
321 #[derive(Debug,PartialEq,Eq)]
322 enum MP4State {
323   Main,
324   Moov,
325   Mvhd(usize)
326 }
327 
328 pub struct MP4Consumer {
329   state:      MP4State,
330   moov_bytes: usize,
331   c_state:    ConsumerState<(), (), Move>
332 }
333 
334 impl MP4Consumer {
335   fn new() -> MP4Consumer {
336     MP4Consumer { state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0)) }
337   }
338 
339   fn consume_main(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
340     //println!("\nparsing box header:\n{}", input.to_hex(8));
341     match input {
342       Input::Eof(None) => ConsumerState::Done(Move::Consume(0), ()),
343       Input::Empty     => ConsumerState::Continue(Move::Consume(0)),
344       Input::Element(sl) |  Input::Eof(Some(sl)) => {
345         match box_header(sl) {
346           Ok((i, header)) => {
347             match header.tag {
348               MP4BoxType::Ftyp    => {
349                 println!("-> FTYP");
350                 match filetype_parser(&i[0..(header.length as usize - 8)]) {
351                   Ok((rest, filetype_header)) => {
352                     println!("filetype header: {:?}", filetype_header);
353                     //return ConsumerState::Await(header.length as usize, header.length as usize - 8);
354                     return ConsumerState::Continue(Move::Consume(sl.offset(rest)));
355                   }
356                   Err(Err::Error(a)) => {
357                     println!("ftyp parsing error: {:?}", a);
358                     assert!(false);
359                     return ConsumerState::Error(());
360                   },
361                   Err(Err::Incomplete(n)) => {
362                     println!("ftyp incomplete -> await: {}", sl.len());
363                     return ConsumerState::Continue(Move::Await(n));
364                     //return ConsumerState::Await(0, input.len() + 100);
365                   }
366                 }
367               },
368               MP4BoxType::Moov    => {
369                 println!("-> MOOV");
370                 self.state = MP4State::Moov;
371                 self.moov_bytes = header.length as usize - 8;
372                 return ConsumerState::Continue(Move::Consume(sl.offset(i)));
373               },
374               MP4BoxType::Mdat    => println!("-> MDAT"),
375               MP4BoxType::Free    => println!("-> FREE"),
376               MP4BoxType::Skip    => println!("-> SKIP"),
377               MP4BoxType::Wide    => println!("-> WIDE"),
378               MP4BoxType::Unknown => {
379                 println!("-> UNKNOWN");
380                 println!("bytes:\n{}", (sl).to_hex(8));
381                 //return ConsumerState::Continue(Move::Consume(sl.offset(i)));
382               },
383               _                   => { println!("invalid"); return ConsumerState::Error(())}
384             }
385             return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
386           },
387           Err(Err::Error(a)) => {
388             println!("mp4 parsing error: {:?}", a);
389             assert!(false);
390             return ConsumerState::Error(());
391           },
392           Err(Err::Incomplete(i)) => {
393             // FIXME: incomplete should send the required size
394             println!("mp4 incomplete -> await: {}", sl.len());
395             return ConsumerState::Continue(Move::Await(i));
396           }
397         }
398       }
399     }
400   }
401 
402   fn consume_moov(&mut self, input: Input<&[u8]>) -> ConsumerState<(), (), Move> {
403     //println!("\nparsing moov box(remaining {} bytes):\n{}", self.moov_bytes, input.to_hex(8));
404     match input {
405       Input::Eof(None) => return ConsumerState::Error(()),
406       Input::Empty => return ConsumerState::Continue(Move::Consume(0)),
407       Input::Element(sl) |  Input::Eof(Some(sl)) => {
408         if self.moov_bytes == 0 {
409           //println!("finished parsing moov atom, continuing with main parser");
410           self.state = MP4State::Main;
411           return ConsumerState::Continue(Move::Consume(0));
412         }
413         match moov_header(sl) {
414           Ok((i, header)) => {
415             match header.tag {
416               MP4BoxType::Mvhd    => {
417                 println!("-> MVHD");
418                 self.state = MP4State::Mvhd(header.length as usize - 8);
419                 // TODO: check for overflow here
420                 self.moov_bytes = self.moov_bytes - (sl.len() - i.len());
421                 println!("remaining moov_bytes: {}", self.moov_bytes);
422                 return ConsumerState::Continue(Move::Consume(sl.offset(i)));
423               },
424               MP4BoxType::Wide    => println!("-> WIDE"),
425               MP4BoxType::Mdra    => println!("-> MDRA"),
426               MP4BoxType::Dref    => println!("-> DREF"),
427               MP4BoxType::Cmov    => println!("-> CMOV"),
428               MP4BoxType::Rmra    => println!("-> RMRA"),
429               MP4BoxType::Iods    => println!("-> IODS"),
430               MP4BoxType::Clip    => println!("-> CLIP"),
431               MP4BoxType::Trak    => println!("-> TRAK"),
432               MP4BoxType::Udta    => println!("-> UDTA"),
433               MP4BoxType::Unknown => println!("-> MOOV UNKNOWN"),
434               _                   => { println!("invalid header here: {:?}", header.tag); return ConsumerState::Error(());}
435             };
436             // TODO: check for overflow here
437             self.moov_bytes = self.moov_bytes - header.length as usize;
438             println!("remaining moov_bytes: {}", self.moov_bytes);
439             return ConsumerState::Continue(Move::Seek(SeekFrom::Current((header.length) as i64)))
440           },
441           Err(Err::Error(a)) => {
442             println!("moov parsing error: {:?}", a);
443             println!("data:\n{}", sl.to_hex(8));
444             assert!(false);
445             return ConsumerState::Error(());
446           },
447           Err(Err::Incomplete(i)) => {
448             println!("moov incomplete -> await: {}", sl.len());
449             return ConsumerState::Continue(Move::Await(i));
450           }
451         }
452       }
453     };
454   }
455 
456 }
457 
458 consumer_from_parser!(MvhdConsumer<MvhdBox>, mvhd_box);
459 
460 impl<'a> Consumer<&'a[u8], (), (), Move> for MP4Consumer {
461   fn handle(&mut self, input: Input<&[u8]>) -> &ConsumerState<(), (), Move> {
462     match self.state {
463       MP4State::Main     => {
464         self.c_state = self.consume_main(input);
465       },
466       MP4State::Moov     => {
467         self.c_state = self.consume_moov(input);
468       },
469       MP4State::Mvhd(sz) => {
470         match input {
471           Input::Eof(None) => self.c_state = ConsumerState::Error(()),
472           Input::Empty     => self.c_state = ConsumerState::Continue(Move::Consume(0)),
473           Input::Element(sl) |  Input::Eof(Some(sl)) => {
474             let mut c = MvhdConsumer{ state:ConsumerState::Continue(Move::Consume(0)) };
475             self.c_state = c.handle(Input::Element(&sl[..sz])).flat_map(|m, _| {
476               self.state = MP4State::Moov;
477               ConsumerState::Continue(m)
478             });
479             println!("found mvhd?: {:?}", c.state());
480             match self.c_state {
481               ConsumerState::Continue(Move::Consume(sz)) => self.moov_bytes = self.moov_bytes - sz,
482               ConsumerState::Continue(Move::Seek(SeekFrom::Current(sz))) => self.moov_bytes = self.moov_bytes - (sz as usize),
483               _ => ()
484             };
485             println!("remaining moov_bytes: {}", self.moov_bytes);
486           }
487         }
488       }
489     };
490     &self.c_state
491   }
492 
493   fn state(&self) -> &ConsumerState<(), (), Move> {
494     &self.c_state
495   }
496 }
497 
498 #[allow(unused_must_use)]
499 fn explore_mp4_file(filename: &str) {
500   let mut p = FileProducer::new(filename, 400).unwrap();
501   let mut c = MP4Consumer{state: MP4State::Main, moov_bytes: 0, c_state: ConsumerState::Continue(Move::Consume(0))};
502   //c.run(&mut p);
503   while let &ConsumerState::Continue(mv) = p.apply(&mut c) {
504     println!("move: {:?}", mv);
505   }
506   println!("last consumer state: {:?} | last state: {:?}", c.c_state, c.state);
507 
508   if let ConsumerState::Done(Move::Consume(0), ()) = c.c_state {
509     println!("consumer state ok");
510   } else {
511     assert!(false, "consumer should have reached Done state");
512   }
513   assert_eq!(c.state, MP4State::Main);
514   assert_eq!(p.state(), FileProducerState::Eof);
515   //assert!(false);
516 }
517 
518 
519 #[test]
520 fn small_test() {
521   explore_mp4_file("assets/small.mp4");
522 }
523 
524 
525 #[test]
526 fn big_bunny_test() {
527   explore_mp4_file("assets/bigbuckbunny.mp4");
528 }
529 */
530