mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-06-02 13:39:54 +00:00
Sample boxes and mp4info example updates (#1)
* more sample boxes * stts and sample box parsing. Update mp4info example with more details.
This commit is contained in:
parent
b345450e19
commit
e9936e5f78
2
.gitignore
vendored
2
.gitignore
vendored
|
@ -3,3 +3,5 @@
|
||||||
*.exe
|
*.exe
|
||||||
*.pdb
|
*.pdb
|
||||||
*.mp4
|
*.mp4
|
||||||
|
.idea/
|
||||||
|
.vscode/
|
||||||
|
|
|
@ -3,7 +3,9 @@ extern crate mp4;
|
||||||
use std::env;
|
use std::env;
|
||||||
use std::fs::File;
|
use std::fs::File;
|
||||||
use std::any::Any;
|
use std::any::Any;
|
||||||
use mp4::{FourCC, TrackType};
|
use std::borrow::Borrow;
|
||||||
|
use std::fmt::Debug;
|
||||||
|
use mp4::{TrackType};
|
||||||
|
|
||||||
fn main() {
|
fn main() {
|
||||||
let args: Vec<String> = env::args().collect();
|
let args: Vec<String> = env::args().collect();
|
||||||
|
@ -17,7 +19,6 @@ fn main() {
|
||||||
let moov = bmff.moov.unwrap();
|
let moov = bmff.moov.unwrap();
|
||||||
|
|
||||||
// Print results.
|
// Print results.
|
||||||
// println!("{:?}", bmff.unwrap());
|
|
||||||
println!("File:");
|
println!("File:");
|
||||||
println!(" file size: {}", bmff.size);
|
println!(" file size: {}", bmff.size);
|
||||||
println!(" brands: {:?} {:?}\n", bmff.ftyp.major_brand, bmff.ftyp.compatible_brands);
|
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 mdia = trak.mdia.as_ref().unwrap();
|
||||||
let hdlr = mdia.hdlr.as_ref().unwrap();
|
let hdlr = mdia.hdlr.as_ref().unwrap();
|
||||||
let mdhd = mdia.mdhd.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!(" flags: {:?}", tkhd.flags);
|
||||||
println!(" id: {:?}", tkhd.track_id);
|
println!(" id: {:?}", tkhd.track_id);
|
||||||
println!(" type: {:?}", get_handler_type(hdlr.handler_type.value.as_ref()));
|
println!(" type: {:?}", get_handler_type(hdlr.handler_type.value.as_ref()));
|
||||||
println!(" duration: {:?}", tkhd.duration);
|
println!(" duration: {:?}", tkhd.duration);
|
||||||
println!(" language: {:?}", mdhd.language_string);
|
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;
|
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<u32>, 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());
|
||||||
}
|
}
|
287
src/lib.rs
287
src/lib.rs
|
@ -163,6 +163,7 @@ pub struct ElstEntry {
|
||||||
pub struct MdiaBox {
|
pub struct MdiaBox {
|
||||||
pub mdhd: Option<MdhdBox>,
|
pub mdhd: Option<MdhdBox>,
|
||||||
pub hdlr: Option<HdlrBox>,
|
pub hdlr: Option<HdlrBox>,
|
||||||
|
pub minf: Option<MinfBox>,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl MdiaBox {
|
impl MdiaBox {
|
||||||
|
@ -203,6 +204,71 @@ impl HdlrBox {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Default)]
|
||||||
|
pub struct MinfBox {
|
||||||
|
pub vmhd: Option<VmhdBox>,
|
||||||
|
pub stbl: Option<StblBox>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<SttsBox>,
|
||||||
|
pub stsd: Option<StsdBox>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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<u32>,
|
||||||
|
pub sample_deltas: Vec<u32>,
|
||||||
|
}
|
||||||
|
|
||||||
|
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)]
|
#[derive(Default, PartialEq, Clone)]
|
||||||
pub struct FourCC {
|
pub struct FourCC {
|
||||||
|
@ -597,6 +663,10 @@ fn parse_mdia_box(f: &mut BufReader<File>, _offset: u64, size: u32) -> Result<Md
|
||||||
let hdlr = parse_hdlr_box(f, 0, s as u32).unwrap();
|
let hdlr = parse_hdlr_box(f, 0, s as u32).unwrap();
|
||||||
mdia.hdlr = Some(hdlr);
|
mdia.hdlr = Some(hdlr);
|
||||||
}
|
}
|
||||||
|
"minf" => {
|
||||||
|
let minf = parse_minf_box(f, 0, s as u32).unwrap();
|
||||||
|
mdia.minf = Some(minf);
|
||||||
|
}
|
||||||
_ => break
|
_ => break
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -688,3 +758,220 @@ fn parse_hdlr_box(f: &mut BufReader<File>, _offset: u64, size: u32) -> Result<Hd
|
||||||
name: handler_string,
|
name: handler_string,
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn parse_minf_box(f: &mut BufReader<File>, _offset: u64, size: u32) -> Result<MinfBox> {
|
||||||
|
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<File>, _offset: u64, size: u32) -> Result<VmhdBox> {
|
||||||
|
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::<BigEndian>().unwrap();
|
||||||
|
let graphics_mode = f.read_u16::<BigEndian>().unwrap();
|
||||||
|
let op_color = f.read_u16::<BigEndian>().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<File>, _offset: u64, size: u32) -> Result<StblBox> {
|
||||||
|
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<File>, _offset: u64, size: u32) -> Result<SttsBox> {
|
||||||
|
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::<BigEndian>().unwrap();
|
||||||
|
let mut sample_counts = Vec::new();
|
||||||
|
let mut sample_deltas = Vec::new();
|
||||||
|
|
||||||
|
for _i in 0..entry_count {
|
||||||
|
let sc = f.read_u32::<BigEndian>().unwrap();
|
||||||
|
let sd = f.read_u32::<BigEndian>().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<File>, _offset: u64, size: u32) -> Result<StsdBox> {
|
||||||
|
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::<BigEndian>().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,
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
Loading…
Reference in a new issue