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