2020-01-12 09:19:31 +00:00
|
|
|
extern crate byteorder;
|
|
|
|
|
2020-01-08 05:34:01 +00:00
|
|
|
use std::io::prelude::*;
|
|
|
|
use std::io::{BufReader, Read, SeekFrom};
|
|
|
|
use std::fs::File;
|
|
|
|
use std::convert::TryInto;
|
2020-02-04 05:52:23 +00:00
|
|
|
|
|
|
|
mod atoms;
|
|
|
|
use crate::atoms::*;
|
2020-01-12 03:34:29 +00:00
|
|
|
|
|
|
|
const HEADER_SIZE: u32 = 8;
|
|
|
|
|
|
|
|
#[derive(Debug)]
|
|
|
|
pub enum Error {
|
|
|
|
InvalidData(&'static str),
|
|
|
|
}
|
2020-01-08 05:34:01 +00:00
|
|
|
|
2020-01-22 05:41:51 +00:00
|
|
|
#[derive(Debug, PartialEq)]
|
|
|
|
pub enum TrackType {
|
|
|
|
Audio,
|
|
|
|
Video,
|
|
|
|
Metadata,
|
|
|
|
Unknown,
|
|
|
|
}
|
|
|
|
|
2020-01-12 08:00:30 +00:00
|
|
|
#[derive(Debug, Default)]
|
2020-01-13 03:33:26 +00:00
|
|
|
pub struct BMFF {
|
2020-01-22 05:41:51 +00:00
|
|
|
pub ftyp: FtypBox,
|
|
|
|
pub moov: Option<MoovBox>,
|
|
|
|
pub size: u64,
|
2020-01-12 08:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
impl BMFF {
|
|
|
|
fn new() -> BMFF {
|
|
|
|
Default::default()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2020-02-04 05:52:23 +00:00
|
|
|
#[derive(Debug, Clone, Copy)]
|
2020-01-12 03:34:29 +00:00
|
|
|
struct BoxHeader {
|
2020-02-04 05:52:23 +00:00
|
|
|
name: BoxType,
|
2020-01-12 03:34:29 +00:00
|
|
|
size: u64,
|
2020-01-08 06:51:21 +00:00
|
|
|
}
|
2020-01-08 05:34:01 +00:00
|
|
|
|
2020-01-13 03:33:26 +00:00
|
|
|
pub fn read_mp4(f: File) -> Result<BMFF> {
|
2020-01-08 05:34:01 +00:00
|
|
|
|
2020-01-12 08:00:30 +00:00
|
|
|
// Open file and read boxes.
|
2020-01-13 03:33:26 +00:00
|
|
|
let bmff = read_boxes(f).unwrap();
|
2020-01-12 08:00:30 +00:00
|
|
|
|
2020-01-13 03:33:26 +00:00
|
|
|
Ok(bmff)
|
2020-01-12 08:00:30 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
fn read_boxes(f: File) -> Result<BMFF> {
|
2020-01-08 05:34:01 +00:00
|
|
|
let filesize = f.metadata().unwrap().len();
|
|
|
|
let mut reader = BufReader::new(f);
|
2020-01-12 08:00:30 +00:00
|
|
|
let mut bmff = BMFF::new();
|
2020-01-22 05:41:51 +00:00
|
|
|
bmff.size = filesize;
|
2020-01-08 05:34:01 +00:00
|
|
|
|
2020-01-12 03:34:29 +00:00
|
|
|
let mut start = 0u64;
|
|
|
|
while start < filesize {
|
2020-01-08 05:34:01 +00:00
|
|
|
|
2020-01-12 08:00:30 +00:00
|
|
|
// Get box header.
|
|
|
|
let header = read_box_header(&mut reader, start).unwrap();
|
2020-07-26 03:11:47 +00:00
|
|
|
let BoxHeader{ name, size } = header;
|
2020-01-08 05:34:01 +00:00
|
|
|
|
2020-02-04 05:52:23 +00:00
|
|
|
// Match and parse the atom boxes.
|
|
|
|
match name {
|
|
|
|
BoxType::FtypBox => {
|
2020-07-26 03:11:47 +00:00
|
|
|
let ftyp = FtypBox::read_box(&mut reader, size as u32).unwrap();
|
2020-01-12 08:00:30 +00:00
|
|
|
bmff.ftyp = ftyp;
|
2020-01-12 03:34:29 +00:00
|
|
|
}
|
2020-02-04 05:52:23 +00:00
|
|
|
BoxType::FreeBox => {
|
2020-01-12 03:34:29 +00:00
|
|
|
start = 0;
|
|
|
|
}
|
2020-02-04 05:52:23 +00:00
|
|
|
BoxType::MdatBox => {
|
2020-01-12 08:00:30 +00:00
|
|
|
start = (size as u32 - HEADER_SIZE) as u64;
|
2020-01-12 03:34:29 +00:00
|
|
|
}
|
2020-02-04 05:52:23 +00:00
|
|
|
BoxType::MoovBox => {
|
2020-07-26 03:11:47 +00:00
|
|
|
let moov = MoovBox::read_box(&mut reader, size as u32).unwrap();
|
2020-01-16 06:53:42 +00:00
|
|
|
bmff.moov = Some(moov);
|
2020-01-12 03:34:29 +00:00
|
|
|
}
|
2020-02-04 05:52:23 +00:00
|
|
|
BoxType::MoofBox => {
|
2020-01-12 08:00:30 +00:00
|
|
|
start = (size as u32 - HEADER_SIZE) as u64;
|
2020-01-12 03:34:29 +00:00
|
|
|
}
|
2020-05-28 01:29:41 +00:00
|
|
|
_ => {
|
|
|
|
// Skip over unsupported boxes, but stop if the size is zero,
|
|
|
|
// meaning the last box has been reached.
|
|
|
|
if size == 0 {
|
|
|
|
break;
|
|
|
|
} else {
|
|
|
|
start = (size as u32 - HEADER_SIZE) as u64;
|
|
|
|
}
|
|
|
|
}
|
2020-01-10 06:26:08 +00:00
|
|
|
};
|
2020-01-08 06:51:21 +00:00
|
|
|
}
|
2020-01-12 08:00:30 +00:00
|
|
|
Ok(bmff)
|
|
|
|
}
|
2020-01-08 06:51:21 +00:00
|
|
|
|
2020-07-26 03:11:47 +00:00
|
|
|
// TODO: if size is 0, then this box is the last one in the file
|
2020-07-25 02:57:46 +00:00
|
|
|
fn read_box_header<R: Read + Seek>(reader: &mut BufReader<R>, start: u64) -> Result<BoxHeader> {
|
2020-01-12 08:00:30 +00:00
|
|
|
// Seek to offset.
|
|
|
|
let _r = reader.seek(SeekFrom::Current(start as i64));
|
|
|
|
|
|
|
|
// Create and read to buf.
|
|
|
|
let mut buf = [0u8;8]; // 8 bytes for box header.
|
2020-02-04 05:52:23 +00:00
|
|
|
reader.read(&mut buf).unwrap();
|
2020-01-12 08:00:30 +00:00
|
|
|
|
|
|
|
// Get size.
|
|
|
|
let s = buf[0..4].try_into().unwrap();
|
|
|
|
let size = u32::from_be_bytes(s);
|
|
|
|
|
|
|
|
// Get box type string.
|
|
|
|
let t = buf[4..8].try_into().unwrap();
|
2020-02-04 05:52:23 +00:00
|
|
|
let typ = u32::from_be_bytes(t);
|
2020-01-12 08:00:30 +00:00
|
|
|
|
2020-07-26 03:11:47 +00:00
|
|
|
// Get largesize if size is 1
|
|
|
|
if size == 1 {
|
|
|
|
reader.read(&mut buf).unwrap();
|
|
|
|
let s = buf.try_into().unwrap();
|
|
|
|
let largesize = u64::from_be_bytes(s);
|
|
|
|
|
|
|
|
Ok(BoxHeader {
|
|
|
|
name: BoxType::from(typ),
|
|
|
|
size: largesize,
|
|
|
|
})
|
|
|
|
} else {
|
|
|
|
Ok(BoxHeader {
|
|
|
|
name: BoxType::from(typ),
|
|
|
|
size: size as u64,
|
|
|
|
})
|
|
|
|
}
|
2020-01-10 06:26:08 +00:00
|
|
|
}
|
|
|
|
|
2020-01-28 05:32:53 +00:00
|
|
|
|