From e9936e5f78f2e80920b710885016a19b6a0d6e18 Mon Sep 17 00:00:00 2001 From: Alfred Gutierrez Date: Mon, 27 Jan 2020 21:32:53 -0800 Subject: [PATCH] Sample boxes and mp4info example updates (#1) * more sample boxes * stts and sample box parsing. Update mp4info example with more details. --- .gitignore | 2 + examples/mp4info.rs | 35 +++++- src/lib.rs | 287 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 320 insertions(+), 4 deletions(-) diff --git a/.gitignore b/.gitignore index 250314e..acbda4d 100644 --- a/.gitignore +++ b/.gitignore @@ -3,3 +3,5 @@ *.exe *.pdb *.mp4 +.idea/ +.vscode/ diff --git a/examples/mp4info.rs b/examples/mp4info.rs index cb9cb6e..f827aaf 100644 --- a/examples/mp4info.rs +++ b/examples/mp4info.rs @@ -3,7 +3,9 @@ extern crate mp4; use std::env; use std::fs::File; use std::any::Any; -use mp4::{FourCC, TrackType}; +use std::borrow::Borrow; +use std::fmt::Debug; +use mp4::{TrackType}; fn main() { let args: Vec = env::args().collect(); @@ -17,7 +19,6 @@ fn main() { let moov = bmff.moov.unwrap(); // Print results. -// println!("{:?}", bmff.unwrap()); println!("File:"); println!(" file size: {}", bmff.size); println!(" brands: {:?} {:?}\n", bmff.ftyp.major_brand, bmff.ftyp.compatible_brands); @@ -32,14 +33,29 @@ fn main() { let mdia = trak.mdia.as_ref().unwrap(); let hdlr = mdia.hdlr.as_ref().unwrap(); let mdhd = mdia.mdhd.as_ref().unwrap(); + let stts= mdia.minf.as_ref().unwrap() + .stbl.as_ref().unwrap() + .stts.as_ref().unwrap(); + println!("Track: {:?}", tkhd.track_id); println!(" flags: {:?}", tkhd.flags); println!(" id: {:?}", tkhd.track_id); println!(" type: {:?}", get_handler_type(hdlr.handler_type.value.as_ref())); println!(" duration: {:?}", tkhd.duration); println!(" language: {:?}", mdhd.language_string); - println!(" width: {:?}", tkhd.width); - println!(" height: {:?}\n", tkhd.height); + + println!(" media:"); + println!(" sample count: {:?}", stts.sample_counts[0]); + println!(" timescale: {:?}", mdhd.timescale); + println!(" duration: {:?} (media timescale units)", mdhd.duration); + println!(" duration: {:?} (ms)", getDurationMS(mdhd.duration, mdhd.timescale)); + if tkhd.width != 0 && tkhd.height != 0 { + println!(" width: {:?}", tkhd.width); + println!(" height: {:?}", tkhd.height); + } + if get_handler_type(hdlr.handler_type.value.as_ref()) == TrackType::Video { + println!(" frame rate: (computed): {:?}", getFramerate(&stts.sample_counts, mdhd.duration, mdhd.timescale)); + } } }, _ => { @@ -57,4 +73,15 @@ fn get_handler_type(handler: &str) -> TrackType { _ => (), } return typ; +} + +fn getDurationMS(duration: u32, timescale: u32) -> String { + let ms = (duration as f64 / timescale as f64) * 1000.0; + return format!("{:.2}", ms.floor()); +} + +fn getFramerate(sample_counts: &Vec, duration: u32, timescale: u32) -> String { + let sc = (sample_counts[0] as f64) * 1000.0; + let ms = (duration as f64 / timescale as f64) * 1000.0; + return format!("{:.2}", sc / ms.floor()); } \ No newline at end of file diff --git a/src/lib.rs b/src/lib.rs index cf2d0ce..a879e5c 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -163,6 +163,7 @@ pub struct ElstEntry { pub struct MdiaBox { pub mdhd: Option, pub hdlr: Option, + pub minf: Option, } impl MdiaBox { @@ -203,6 +204,71 @@ impl HdlrBox { } } +#[derive(Debug, Default)] +pub struct MinfBox { + pub vmhd: Option, + pub stbl: Option, +} + +impl MinfBox { + fn new() -> MinfBox { + Default::default() + } +} + +#[derive(Debug, Default)] +pub struct VmhdBox { + pub version: u8, + pub flags: u32, + pub graphics_mode: u16, + pub op_color: u16, +} + +impl VmhdBox { + fn new() -> VmhdBox { + Default::default() + } +} + +#[derive(Debug, Default)] +pub struct StblBox { + pub stts: Option, + pub stsd: Option, +} + +impl StblBox { + fn new() -> StblBox { + Default::default() + } +} + +#[derive(Debug, Default)] +pub struct SttsBox { + pub version: u8, + pub flags: u32, + pub entry_count: u32, + pub sample_counts: Vec, + pub sample_deltas: Vec, +} + +impl SttsBox { + fn new() -> SttsBox { + Default::default() + } +} + +#[derive(Debug, Default)] +pub struct StsdBox { + pub version: u8, + pub flags: u32, +} + +impl StsdBox { + fn new() -> StsdBox { + Default::default() + } +} + #[derive(Default, PartialEq, Clone)] pub struct FourCC { @@ -597,6 +663,10 @@ fn parse_mdia_box(f: &mut BufReader, _offset: u64, size: u32) -> Result { + let minf = parse_minf_box(f, 0, s as u32).unwrap(); + mdia.minf = Some(minf); + } _ => break } } @@ -688,3 +758,220 @@ fn parse_hdlr_box(f: &mut BufReader, _offset: u64, size: u32) -> Result, _offset: u64, size: u32) -> Result { + println!("size: {:?}", size); + let current = f.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position. + let mut minf = MinfBox::new(); + + let mut start = 0u64; + while start < size as u64 { + // Get box header. + let header = read_box_header(f, start).unwrap(); + let BoxHeader{ name, size: s, offset } = header; + + let mut b = BMFFBox::new(); + b.head = BoxHeader{ + name: name.try_into().unwrap(), + size: s as u64, + offset: offset as u64, + }; + + match b.head.name.as_ref() { + "vmhd" => { + println!("found vmhd"); + let vmhd = parse_vmhd_box(f, 0, s as u32).unwrap(); + minf.vmhd = Some(vmhd); +// start = (s as u32 - HEADER_SIZE) as u64; + } + "smhd" => { + println!("found smhd"); +//// let vmhd = parse_smhd_box(f, 0, s as u32).unwrap(); +//// minf.smhd = Some(vmhd); + start = (s as u32 - HEADER_SIZE) as u64; + } + "dinf" => { + println!("found dinf"); + start = (s as u32 - HEADER_SIZE) as u64; + } + "stbl" => { + println!("found stbl"); + let stbl = parse_stbl_box(f, 0, s as u32).unwrap(); + minf.stbl = Some(stbl); + // start = (s as u32 - HEADER_SIZE) as u64; + } + _ => break + } + } + + // Skip remaining bytes. + let after = f.seek(SeekFrom::Current(0)).unwrap(); + let remaining_bytes = (size as u64 - (after - current)) as i64; + f.seek(SeekFrom::Current(remaining_bytes - HEADER_SIZE as i64)).unwrap(); + Ok(minf) +} + +fn parse_vmhd_box(f: &mut BufReader, _offset: u64, size: u32) -> Result { + let current = f.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position. + + let version = f.read_u8().unwrap(); + let flags_a = f.read_u8().unwrap(); + let flags_b = f.read_u8().unwrap(); + let flags_c = f.read_u8().unwrap(); + let flags = u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c); +// let flags = f.read_u32::().unwrap(); + let graphics_mode = f.read_u16::().unwrap(); + let op_color = f.read_u16::().unwrap(); + + // Skip remaining bytes. + let after = f.seek(SeekFrom::Current(0)).unwrap(); + let remaining_bytes = (size as u64 - (after - current)) as i64; + f.seek(SeekFrom::Current(remaining_bytes - HEADER_SIZE as i64)).unwrap(); + + Ok(VmhdBox { + version, + flags, + graphics_mode, + op_color, + }) +} + +fn parse_stbl_box(f: &mut BufReader, _offset: u64, size: u32) -> Result { + println!("stbl size: {:?}", size); + let current = f.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position. + let mut stbl = StblBox::new(); + + let mut start = 0u64; + while start < size as u64 { + // Get box header. + let header = read_box_header(f, start).unwrap(); + let BoxHeader{ name, size: s, offset } = header; + + let mut b = BMFFBox::new(); + b.head = BoxHeader{ + name: name.try_into().unwrap(), + size: s as u64, + offset: offset as u64, + }; + + match b.head.name.as_ref() { + "stsd" => { + println!("found stsd: {:?}", s); +// let stsd = parse_stsd_box(f, 0, s as u32).unwrap(); + start = (s as u32 - HEADER_SIZE) as u64; + } + "stts" => { + let stts = parse_stts_box(f, 0, s as u32).unwrap(); + stbl.stts = Some(stts); + } + "stss" => { + println!("found stss"); + start = (s as u32 - HEADER_SIZE) as u64; + } + "ctts" => { + println!("found ctts"); + start = (s as u32 - HEADER_SIZE) as u64; + } + "stsc" => { + println!("found stsc"); + start = (s as u32 - HEADER_SIZE) as u64; + } + "stsz" => { + println!("found stsz"); + start = (s as u32 - HEADER_SIZE) as u64; + } + "stco" => { + println!("found stco"); + start = (s as u32 - HEADER_SIZE) as u64; + } + _ => break + } + } + + // Skip remaining bytes. +// let after = f.seek(SeekFrom::Current(0)).unwrap(); +// let remaining_bytes = (size as u64 - (after - current)) as i64; +// f.seek(SeekFrom::Current(remaining_bytes - HEADER_SIZE as i64)).unwrap(); + Ok(stbl) +} + +fn parse_stts_box(f: &mut BufReader, _offset: u64, size: u32) -> Result { + let current = f.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position. + + let version = f.read_u8().unwrap(); + let flags_a = f.read_u8().unwrap(); + let flags_b = f.read_u8().unwrap(); + let flags_c = f.read_u8().unwrap(); + let flags = u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c); + let entry_count = f.read_u32::().unwrap(); + let mut sample_counts = Vec::new(); + let mut sample_deltas = Vec::new(); + + for _i in 0..entry_count { + let sc = f.read_u32::().unwrap(); + let sd = f.read_u32::().unwrap(); + sample_counts.push(sc); + sample_deltas.push(sd); + } + + // Skip remaining bytes. + let after = f.seek(SeekFrom::Current(0)).unwrap(); + let remaining_bytes = (size as u64 - (after - current)) as i64; + f.seek(SeekFrom::Current(remaining_bytes - HEADER_SIZE as i64)).unwrap(); + + Ok(SttsBox { + version, + flags, + entry_count, + sample_counts, + sample_deltas, + }) +} + +fn parse_stsd_box(f: &mut BufReader, _offset: u64, size: u32) -> Result { + let current = f.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position. + + let version = f.read_u8().unwrap(); + let flags_a = f.read_u8().unwrap(); + let flags_b = f.read_u8().unwrap(); + let flags_c = f.read_u8().unwrap(); + let flags = u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c); + f.read_u32::().unwrap(); // skip. + + + let mut start = 0u64; + while start < size as u64 { + // Get box header. + let header = read_box_header(f, start).unwrap(); + let BoxHeader{ name, size: s, offset } = header; + + let mut b = BMFFBox::new(); + b.head = BoxHeader{ + name: name.try_into().unwrap(), + size: s as u64, + offset: offset as u64, + }; + + match b.head.name.as_ref() { + "avc1" => { + println!("found avc1"); + start = (s as u32 - HEADER_SIZE) as u64; + } + "mp4a" => { + println!("found mp4a"); + start = (s as u32 - HEADER_SIZE) as u64; + } + _ => break + } + } + + // Skip remaining bytes. +// let after = f.seek(SeekFrom::Current(0)).unwrap(); +// let remaining_bytes = (size as u64 - (after - current)) as i64; +// f.seek(SeekFrom::Current(remaining_bytes - HEADER_SIZE as i64)).unwrap(); + + Ok(StsdBox { + version, + flags, + }) +}