| #![allow(dead_code)] |
| |
| #[macro_use] |
| extern crate nom; |
| |
| use nom::{ |
| IResult, Needed, Err, |
| error::ErrorKind, |
| number::streaming::{be_u16, be_u32, be_u64, be_f32} |
| }; |
| |
| use std::str; |
| |
| fn mp4_box(input: &[u8]) -> IResult<&[u8], &[u8]> { |
| match be_u32(input) { |
| Ok((i, offset)) => { |
| let sz: usize = offset as usize; |
| if i.len() >= sz - 4 { |
| Ok((&i[(sz - 4)..], &i[0..(sz - 4)])) |
| } else { |
| Err(Err::Incomplete(Needed::Size(offset as usize + 4))) |
| } |
| } |
| Err(e) => Err(e), |
| } |
| } |
| |
| #[cfg_attr(rustfmt, rustfmt_skip)] |
| #[derive(PartialEq,Eq,Debug)] |
| struct FileType<'a> { |
| major_brand: &'a str, |
| major_brand_version: &'a [u8], |
| compatible_brands: Vec<&'a str> |
| } |
| |
| #[cfg_attr(rustfmt, rustfmt_skip)] |
| #[allow(non_snake_case)] |
| #[derive(Debug,Clone)] |
| pub struct Mvhd32 { |
| version_flags: u32, // actually: |
| // version: u8, |
| // flags: u24 // 3 bytes |
| created_date: u32, |
| modified_date: u32, |
| scale: u32, |
| duration: u32, |
| speed: f32, |
| volume: u16, // actually a 2 bytes decimal |
| /* 10 bytes reserved */ |
| scaleA: f32, |
| rotateB: f32, |
| angleU: f32, |
| rotateC: f32, |
| scaleD: f32, |
| angleV: f32, |
| positionX: f32, |
| positionY: f32, |
| scaleW: f32, |
| preview: u64, |
| poster: u32, |
| selection: u64, |
| current_time: u32, |
| track_id: u32 |
| } |
| |
| #[cfg_attr(rustfmt, rustfmt_skip)] |
| #[allow(non_snake_case)] |
| #[derive(Debug,Clone)] |
| pub struct Mvhd64 { |
| version_flags: u32, // actually: |
| // version: u8, |
| // flags: u24 // 3 bytes |
| created_date: u64, |
| modified_date: u64, |
| scale: u32, |
| duration: u64, |
| speed: f32, |
| volume: u16, // actually a 2 bytes decimal |
| /* 10 bytes reserved */ |
| scaleA: f32, |
| rotateB: f32, |
| angleU: f32, |
| rotateC: f32, |
| scaleD: f32, |
| angleV: f32, |
| positionX: f32, |
| positionY: f32, |
| scaleW: f32, |
| preview: u64, |
| poster: u32, |
| selection: u64, |
| current_time: u32, |
| track_id: u32 |
| } |
| |
| #[allow(non_snake_case)] |
| named!(mvhd32 <&[u8], MvhdBox>, |
| do_parse!( |
| version_flags: be_u32 >> |
| created_date: be_u32 >> |
| modified_date: be_u32 >> |
| scale: be_u32 >> |
| duration: be_u32 >> |
| speed: be_f32 >> |
| volume: be_u16 >> // actually a 2 bytes decimal |
| take!(10) >> |
| scale_a: be_f32 >> |
| rotate_b: be_f32 >> |
| angle_u: be_f32 >> |
| rotate_c: be_f32 >> |
| scale_d: be_f32 >> |
| angle_v: be_f32 >> |
| position_x: be_f32 >> |
| position_y: be_f32 >> |
| scale_w: be_f32 >> |
| preview: be_u64 >> |
| poster: be_u32 >> |
| selection: be_u64 >> |
| current_time: be_u32 >> |
| track_id: be_u32 >> |
| ( |
| MvhdBox::M32(Mvhd32 { |
| version_flags: version_flags, |
| created_date: created_date, |
| modified_date: modified_date, |
| scale: scale, |
| duration: duration, |
| speed: speed, |
| volume: volume, |
| scaleA: scale_a, |
| rotateB: rotate_b, |
| angleU: angle_u, |
| rotateC: rotate_c, |
| scaleD: scale_d, |
| angleV: angle_v, |
| positionX: position_x, |
| positionY: position_y, |
| scaleW: scale_w, |
| preview: preview, |
| poster: poster, |
| selection: selection, |
| current_time: current_time, |
| track_id: track_id |
| }) |
| )) |
| ); |
| |
| #[allow(non_snake_case)] |
| named!(mvhd64 <&[u8], MvhdBox>, |
| do_parse!( |
| version_flags: be_u32 >> |
| created_date: be_u64 >> |
| modified_date: be_u64 >> |
| scale: be_u32 >> |
| duration: be_u64 >> |
| speed: be_f32 >> |
| volume: be_u16 >> // actually a 2 bytes decimal |
| take!(10) >> |
| scale_a: be_f32 >> |
| rotate_b: be_f32 >> |
| angle_u: be_f32 >> |
| rotate_c: be_f32 >> |
| scale_d: be_f32 >> |
| angle_v: be_f32 >> |
| position_x: be_f32 >> |
| position_y: be_f32 >> |
| scale_w: be_f32 >> |
| preview: be_u64 >> |
| poster: be_u32 >> |
| selection: be_u64 >> |
| current_time: be_u32 >> |
| track_id: be_u32 >> |
| ( |
| MvhdBox::M64(Mvhd64 { |
| version_flags: version_flags, |
| created_date: created_date, |
| modified_date: modified_date, |
| scale: scale, |
| duration: duration, |
| speed: speed, |
| volume: volume, |
| scaleA: scale_a, |
| rotateB: rotate_b, |
| angleU: angle_u, |
| rotateC: rotate_c, |
| scaleD: scale_d, |
| angleV: angle_v, |
| positionX: position_x, |
| positionY: position_y, |
| scaleW: scale_w, |
| preview: preview, |
| poster: poster, |
| selection: selection, |
| current_time: current_time, |
| track_id: track_id |
| }) |
| )) |
| ); |
| |
| #[derive(Debug, Clone)] |
| pub enum MvhdBox { |
| M32(Mvhd32), |
| M64(Mvhd64), |
| } |
| |
| #[derive(Debug, Clone)] |
| pub enum MoovBox { |
| Mdra, |
| Dref, |
| Cmov, |
| Rmra, |
| Iods, |
| Mvhd(MvhdBox), |
| Clip, |
| Trak, |
| Udta, |
| } |
| |
| #[derive(Debug)] |
| enum MP4BoxType { |
| Ftyp, |
| Moov, |
| Mdat, |
| Free, |
| Skip, |
| Wide, |
| Mdra, |
| Dref, |
| Cmov, |
| Rmra, |
| Iods, |
| Mvhd, |
| Clip, |
| Trak, |
| Udta, |
| Unknown, |
| } |
| |
| #[derive(Debug)] |
| struct MP4BoxHeader { |
| length: u32, |
| tag: MP4BoxType, |
| } |
| |
| named!(brand_name<&[u8],&str>, map_res!(take!(4), str::from_utf8)); |
| |
| named!(filetype_parser<&[u8], FileType>, |
| do_parse!( |
| m: brand_name >> |
| v: take!(4) >> |
| c: many0!(brand_name) >> |
| (FileType{ major_brand: m, major_brand_version:v, compatible_brands: c }) |
| ) |
| ); |
| |
| fn mvhd_box(input: &[u8]) -> IResult<&[u8], MvhdBox> { |
| let res = if input.len() < 100 { |
| Err(Err::Incomplete(Needed::Size(100))) |
| } else if input.len() == 100 { |
| mvhd32(input) |
| } else if input.len() == 112 { |
| mvhd64(input) |
| } else { |
| Err(Err::Error(error_position!(input, ErrorKind::TooLarge))) |
| }; |
| println!("res: {:?}", res); |
| res |
| } |
| |
| fn unknown_box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType> { |
| Ok((input, MP4BoxType::Unknown)) |
| } |
| |
| //named!(box_type<&[u8], MP4BoxType>, |
| fn box_type(input: &[u8]) -> IResult<&[u8], MP4BoxType> { |
| alt!(input, |
| tag!("ftyp") => { |_| MP4BoxType::Ftyp } | |
| tag!("moov") => { |_| MP4BoxType::Moov } | |
| tag!("mdat") => { |_| MP4BoxType::Mdat } | |
| tag!("free") => { |_| MP4BoxType::Free } | |
| tag!("skip") => { |_| MP4BoxType::Skip } | |
| tag!("wide") => { |_| MP4BoxType::Wide } | |
| unknown_box_type |
| ) |
| } |
| |
| // warning, an alt combinator with 9 branches containing a tag combinator |
| // can make the compilation very slow. Use functions as sub parsers, |
| // or split into multiple alt! parsers if it gets slow |
| named!(moov_type<&[u8], MP4BoxType>, |
| alt!( |
| tag!("mdra") => { |_| MP4BoxType::Mdra } | |
| tag!("dref") => { |_| MP4BoxType::Dref } | |
| tag!("cmov") => { |_| MP4BoxType::Cmov } | |
| tag!("rmra") => { |_| MP4BoxType::Rmra } | |
| tag!("iods") => { |_| MP4BoxType::Iods } | |
| tag!("mvhd") => { |_| MP4BoxType::Mvhd } | |
| tag!("clip") => { |_| MP4BoxType::Clip } | |
| tag!("trak") => { |_| MP4BoxType::Trak } | |
| tag!("udta") => { |_| MP4BoxType::Udta } |
| ) |
| ); |
| |
| named!(box_header<&[u8],MP4BoxHeader>, |
| do_parse!( |
| length: be_u32 >> |
| tag: box_type >> |
| (MP4BoxHeader{ length: length, tag: tag}) |
| ) |
| ); |
| |
| named!(moov_header<&[u8],MP4BoxHeader>, |
| do_parse!( |
| length: be_u32 >> |
| tag: moov_type >> |
| (MP4BoxHeader{ length: length, tag: tag}) |
| ) |
| ); |
| |