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