1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-12-22 20:16:27 +00:00

Feature/writebox trait (#11)

* Add ReadBox trait

* Add boxtype macro

* Remove offset in BoxHeader

* Fix parsing error when box has largesize

* Remove duplicated codes reading version and flags

* Simplify all box size types as largesize

* Add WriteBox trait and improve compatibility with large box

* Split large atoms file into smaller ones

* Refator Error

Co-authored-by: Byungwan Jun <unipro.kr@gmail.com>
This commit is contained in:
Ian Jun 2020-07-29 01:36:17 +09:00 committed by GitHub
parent 95fc64b811
commit 8f56200dd0
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
22 changed files with 1747 additions and 792 deletions

56
Cargo.lock generated
View file

@ -10,7 +10,63 @@ name = "mp4"
version = "0.4.3" version = "0.4.3"
dependencies = [ dependencies = [
"byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)", "byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)",
"thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
] ]
[[package]]
name = "proc-macro2"
version = "1.0.19"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "quote"
version = "1.0.7"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "syn"
version = "1.0.36"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "thiserror-impl"
version = "1.0.20"
source = "registry+https://github.com/rust-lang/crates.io-index"
dependencies = [
"proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)",
"quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)",
"syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)",
]
[[package]]
name = "unicode-xid"
version = "0.2.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
[metadata] [metadata]
"checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5" "checksum byteorder 1.3.2 (registry+https://github.com/rust-lang/crates.io-index)" = "a7c3dd8985a7111efc5c80b44e23ecdd8c007de8ade3b96595387e812b957cf5"
"checksum proc-macro2 1.0.19 (registry+https://github.com/rust-lang/crates.io-index)" = "04f5f085b5d71e2188cb8271e5da0161ad52c3f227a661a3c135fdf28e258b12"
"checksum quote 1.0.7 (registry+https://github.com/rust-lang/crates.io-index)" = "aa563d17ecb180e500da1cfd2b028310ac758de548efdd203e18f283af693f37"
"checksum syn 1.0.36 (registry+https://github.com/rust-lang/crates.io-index)" = "4cdb98bcb1f9d81d07b536179c269ea15999b5d14ea958196413869445bb5250"
"checksum thiserror 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "7dfdd070ccd8ccb78f4ad66bf1982dc37f620ef696c6b5028fe2ed83dd3d0d08"
"checksum thiserror-impl 1.0.20 (registry+https://github.com/rust-lang/crates.io-index)" = "bd80fc12f73063ac132ac92aceea36734f04a1d93c1240c6944e23a3b8841793"
"checksum unicode-xid 0.2.1 (registry+https://github.com/rust-lang/crates.io-index)" = "f7fe0bb3479651439c9112f72b6c505038574c9fbb575ed1bf3b797fa39dd564"

View file

@ -18,4 +18,5 @@ keywords = ["mp4", "isobmff"]
license = "MIT" license = "MIT"
[dependencies] [dependencies]
thiserror = "^1.0"
byteorder = "1" byteorder = "1"

View file

@ -58,7 +58,7 @@ fn main() {
println!(" media:"); println!(" media:");
if let Some(ref s) = stts { if let Some(ref s) = stts {
println!(" sample count: {:?}", s.sample_counts[0]); println!(" sample count: {:?}", s.entries[0].sample_count);
} }
println!(" timescale: {:?}", mdhd.timescale); println!(" timescale: {:?}", mdhd.timescale);
println!( println!(
@ -73,7 +73,7 @@ fn main() {
if let Some(ref s) = stts { if let Some(ref s) = stts {
println!( println!(
" frame rate: (computed): {:?}", " frame rate: (computed): {:?}",
get_framerate(&s.sample_counts, mdhd.duration, mdhd.timescale) get_framerate(s.entries[0].sample_count, mdhd.duration, mdhd.timescale)
); );
} }
} }
@ -97,18 +97,18 @@ fn get_handler_type(handler: &str) -> mp4::TrackType {
return typ; return typ;
} }
fn get_duration_ms(duration: u32, timescale: u32) -> String { fn get_duration_ms(duration: u64, timescale: u32) -> String {
let ms = (duration as f64 / timescale as f64) * 1000.0; let ms = (duration as f64 / timescale as f64) * 1000.0;
return format!("{:.2}", ms.floor()); return format!("{:.2}", ms.floor());
} }
fn get_framerate(sample_counts: &Vec<u32>, duration: u32, timescale: u32) -> String { fn get_framerate(sample_count: u32, duration: u64, timescale: u32) -> String {
let sc = (sample_counts[0] as f64) * 1000.0; let sc = (sample_count as f64) * 1000.0;
let ms = (duration as f64 / timescale as f64) * 1000.0; let ms = (duration as f64 / timescale as f64) * 1000.0;
return format!("{:.2}", sc / ms.floor()); return format!("{:.2}", sc / ms.floor());
} }
fn creation_time(creation_time: u32) -> u32 { fn creation_time(creation_time: u64) -> u64 {
// convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01) // convert from MP4 epoch (1904-01-01) to Unix epoch (1970-01-01)
if creation_time >= 2082844800 { if creation_time >= 2082844800 {
creation_time - 2082844800 creation_time - 2082844800

View file

@ -1,767 +0,0 @@
use std::fmt;
use std::io::{BufReader, SeekFrom, Seek, Read};
use byteorder::{BigEndian, ReadBytesExt};
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
use crate::{Error, read_box_header, BoxHeader, HEADER_SIZE};
pub type Result<T> = std::result::Result<T, Error>;
macro_rules! boxtype {
($( $name:ident => $value:expr ),*) => {
#[derive(Clone, Copy, PartialEq)]
pub enum BoxType {
$( $name, )*
UnknownBox(u32),
}
impl From<u32> for BoxType {
fn from(t: u32) -> BoxType {
match t {
$( $value => BoxType::$name, )*
_ => BoxType::UnknownBox(t),
}
}
}
impl Into<u32> for BoxType {
fn into(self) -> u32 {
match self {
$( BoxType::$name => $value, )*
BoxType::UnknownBox(t) => t,
}
}
}
}
}
macro_rules! read_box_header_ext {
($r:ident, $v:ident, $ f:ident) => {
let $v = $r.read_u8().unwrap();
let flags_a = $r.read_u8().unwrap();
let flags_b = $r.read_u8().unwrap();
let flags_c = $r.read_u8().unwrap();
let $f = u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c);
}
}
boxtype!{
FtypBox => 0x66747970,
MvhdBox => 0x6d766864,
FreeBox => 0x66726565,
MdatBox => 0x6d646174,
MoovBox => 0x6d6f6f76,
MoofBox => 0x6d6f6f66,
TkhdBox => 0x746b6864,
EdtsBox => 0x65647473,
MdiaBox => 0x6d646961,
ElstBox => 0x656c7374,
MdhdBox => 0x6d646864,
HdlrBox => 0x68646c72,
MinfBox => 0x6d696e66,
VmhdBox => 0x766d6864,
StblBox => 0x7374626c,
StsdBox => 0x73747364,
SttsBox => 0x73747473,
TrakBox => 0x7472616b,
UdtaBox => 0x75647461,
DinfBox => 0x64696e66,
SmhdBox => 0x736d6864,
Avc1Box => 0x61766331,
Mp4aBox => 0x6d703461
}
impl fmt::Debug for BoxType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let fourcc: FourCC = From::from(self.clone());
write!(f, "{}", fourcc)
}
}
#[derive(Default, PartialEq, Clone)]
pub struct FourCC {
pub value: String
}
impl From<u32> for FourCC {
fn from(number: u32) -> FourCC {
let mut box_chars = Vec::new();
for x in 0..4 {
let c = (number >> (x * 8) & 0x0000_00FF) as u8;
box_chars.push(c);
}
box_chars.reverse();
let box_string = match String::from_utf8(box_chars) {
Ok(t) => t,
_ => String::from("null"), // error to retrieve fourcc
};
FourCC {
value: box_string
}
}
}
impl From<BoxType> for FourCC {
fn from(t: BoxType) -> FourCC {
let box_num: u32 = Into::into(t);
From::from(box_num)
}
}
impl fmt::Debug for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl fmt::Display for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
pub trait ReadBox<T>: Sized {
fn read_box(_: T, size: u32) -> Result<Self>;
}
#[derive(Debug, Default)]
pub struct FtypBox {
pub major_brand: FourCC,
pub minor_version: u32,
pub compatible_brands: Vec<FourCC>,
}
#[derive(Debug, Default)]
pub struct MoovBox {
pub mvhd: MvhdBox,
pub traks: Vec<TrakBox>,
}
impl MoovBox {
pub(crate) fn new() -> MoovBox {
Default::default()
}
}
#[derive(Debug, Default)]
pub struct MvhdBox {
pub version: u8,
pub flags: u32,
pub creation_time: u32,
pub modification_time: u32,
pub timescale: u32,
pub duration: u32,
pub rate: u32,
}
#[derive(Debug, Default)]
pub struct TrakBox {
pub tkhd: Option<TkhdBox>,
pub edts: Option<EdtsBox>,
pub mdia: Option<MdiaBox>,
}
impl TrakBox {
pub(crate) fn new() -> TrakBox {
Default::default()
}
}
#[derive(Debug, Default)]
pub struct TkhdBox {
pub version: u8,
pub flags: u32,
pub creation_time: u32,
pub modification_time: u32,
pub track_id: u32,
pub duration: u64,
pub layer: u16,
pub alternate_group: u16,
pub volume: u16,
pub matrix: Matrix,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Default)]
pub struct Matrix {
pub a: i32,
pub b: i32,
pub u: i32,
pub c: i32,
pub d: i32,
pub v: i32,
pub x: i32,
pub y: i32,
pub w: i32,
}
#[derive(Debug, Default)]
pub struct EdtsBox {
pub elst: Option<ElstBox>,
}
impl EdtsBox {
pub(crate) fn new() -> EdtsBox {
Default::default()
}
}
#[derive(Debug, Default)]
pub struct ElstBox {
pub version: u32,
pub entry_count: u32,
pub entries: Vec<ElstEntry>,
}
#[derive(Debug, Default)]
pub struct ElstEntry {
pub segment_duration: u32,
pub media_time: u32,
pub media_rate: u16,
pub media_rate_fraction: u16,
}
#[derive(Debug, Default)]
pub struct MdiaBox {
pub mdhd: Option<MdhdBox>,
pub hdlr: Option<HdlrBox>,
pub minf: Option<MinfBox>,
}
impl MdiaBox {
pub(crate) fn new() -> MdiaBox {
Default::default()
}
}
#[derive(Debug, Default)]
pub struct MdhdBox {
pub version: u8,
pub flags: u32,
pub creation_time: u32,
pub modification_time: u32,
pub timescale: u32,
pub duration: u32,
pub language: u16,
pub language_string: String,
}
#[derive(Debug, Default)]
pub struct HdlrBox {
pub version: u8,
pub flags: u32,
pub handler_type: FourCC,
pub name: String,
}
#[derive(Debug, Default)]
pub struct MinfBox {
pub vmhd: Option<VmhdBox>,
pub stbl: Option<StblBox>,
}
impl MinfBox {
pub(crate) 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,
}
#[derive(Debug, Default)]
pub struct StblBox {
pub stts: Option<SttsBox>,
pub stsd: Option<StsdBox>,
}
impl StblBox {
pub(crate) 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>,
}
#[derive(Debug, Default)]
pub struct StsdBox {
pub version: u8,
pub flags: u32,
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for FtypBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let major = reader.read_u32::<BigEndian>().unwrap();
let minor = reader.read_u32::<BigEndian>().unwrap();
if size % 4 != 0 {
return Err(Error::InvalidData("invalid ftyp size"));
}
let brand_count = (size - 16) / 4; // header + major + minor
let mut brands = Vec::new();
for _ in 0..brand_count {
let b = reader.read_u32::<BigEndian>().unwrap();
brands.push(From::from(b));
}
Ok(FtypBox {
major_brand: From::from(major),
minor_version: minor,
compatible_brands: brands,
})
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MoovBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let mut moov = MoovBox::new();
let mut start = 0u64;
while start < size as u64 {
// Get box header.
let header = read_box_header(reader, start).unwrap();
let BoxHeader{ name, size: s } = header;
match name {
BoxType::MvhdBox => {
moov.mvhd = MvhdBox::read_box(reader, s as u32).unwrap();
}
BoxType::TrakBox => {
let trak = TrakBox::read_box(reader, s as u32).unwrap();
moov.traks.push(trak);
}
BoxType::UdtaBox => {
start = (s as u32 - HEADER_SIZE) as u64;
}
_ => break
}
}
Ok(moov)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MvhdBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
read_box_header_ext!(reader, version, flags);
let creation_time = reader.read_u32::<BigEndian>().unwrap();
let modification_time = reader.read_u32::<BigEndian>().unwrap();
let timescale = reader.read_u32::<BigEndian>().unwrap();
let duration = reader.read_u32::<BigEndian>().unwrap();
let rate = reader.read_u32::<BigEndian>().unwrap();
skip(reader, current, size);
Ok(MvhdBox{
version,
flags,
creation_time,
modification_time,
timescale,
duration,
rate,
})
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TrakBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
let mut trak = TrakBox::new();
let start = 0u64;
while start < size as u64 {
// Get box header.
let header = read_box_header(reader, start).unwrap();
let BoxHeader{ name, size: s } = header;
match name {
BoxType::TkhdBox => {
let tkhd = TkhdBox::read_box(reader, s as u32).unwrap();
trak.tkhd = Some(tkhd);
}
BoxType::EdtsBox => {
let edts = EdtsBox::read_box(reader, s as u32).unwrap();
trak.edts = Some(edts);
}
BoxType::MdiaBox => {
let mdia = MdiaBox::read_box(reader, s as u32).unwrap();
trak.mdia = Some(mdia);
}
_ => break
}
}
skip(reader, current, size);
Ok(trak)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TkhdBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
read_box_header_ext!(reader, version, flags);
let creation_time = reader.read_u32::<BigEndian>().unwrap();
let modification_time = reader.read_u32::<BigEndian>().unwrap();
let track_id = reader.read_u32::<BigEndian>().unwrap();
let duration = reader.read_u64::<BigEndian>().unwrap();
reader.read_u64::<BigEndian>().unwrap(); // skip.
let layer = reader.read_u16::<BigEndian>().unwrap();
let alternate_group = reader.read_u16::<BigEndian>().unwrap();
let volume = reader.read_u16::<BigEndian>().unwrap() >> 8;
reader.read_u8().unwrap(); // skip.
let matrix = Matrix{
a: reader.read_i32::<byteorder::LittleEndian>().unwrap(),
b: reader.read_i32::<BigEndian>().unwrap(),
u: reader.read_i32::<BigEndian>().unwrap(),
c: reader.read_i32::<BigEndian>().unwrap(),
d: reader.read_i32::<BigEndian>().unwrap(),
v: reader.read_i32::<BigEndian>().unwrap(),
x: reader.read_i32::<BigEndian>().unwrap(),
y: reader.read_i32::<BigEndian>().unwrap(),
w: reader.read_i32::<BigEndian>().unwrap(),
};
let width = reader.read_u32::<BigEndian>().unwrap() >> 8;
let height = reader.read_u32::<BigEndian>().unwrap() >> 8;
skip(reader, current, size);
Ok(TkhdBox {
version,
flags,
creation_time,
modification_time,
track_id,
duration,
layer,
alternate_group,
volume,
matrix,
width,
height,
})
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for EdtsBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
let mut edts = EdtsBox::new();
let start = 0u64;
while start < size as u64 {
// Get box header.
let header = read_box_header(reader, start).unwrap();
let BoxHeader{ name, size: s } = header;
match name {
BoxType::ElstBox => {
let elst = ElstBox::read_box(reader, s as u32).unwrap();
edts.elst = Some(elst);
}
_ => break
}
}
skip(reader, current, size);
Ok(edts)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for ElstBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
let version = reader.read_u32::<BigEndian>().unwrap();
let entry_count = reader.read_u32::<BigEndian>().unwrap();
let mut entries = Vec::new();
for _i in 0..entry_count {
let entry = ElstEntry{
segment_duration: reader.read_u32::<BigEndian>().unwrap(),
media_time: reader.read_u32::<BigEndian>().unwrap(),
media_rate: reader.read_u16::<BigEndian>().unwrap(),
media_rate_fraction: reader.read_u16::<BigEndian>().unwrap(),
};
entries.push(entry);
}
skip(reader, current, size);
Ok(ElstBox {
version,
entry_count,
entries,
})
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdiaBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
let mut mdia = MdiaBox::new();
let start = 0u64;
while start < size as u64 {
// Get box header.
let header = read_box_header(reader, start).unwrap();
let BoxHeader{ name, size: s } = header;
match name {
BoxType::MdhdBox => {
let mdhd = MdhdBox::read_box(reader, s as u32).unwrap();
mdia.mdhd = Some(mdhd);
}
BoxType::HdlrBox => {
let hdlr = HdlrBox::read_box(reader, s as u32).unwrap();
mdia.hdlr = Some(hdlr);
}
BoxType::MinfBox => {
let minf = MinfBox::read_box(reader, s as u32).unwrap();
mdia.minf = Some(minf);
}
_ => break
}
}
skip(reader, current, size);
Ok(mdia)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdhdBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
read_box_header_ext!(reader, version, flags);
let creation_time = reader.read_u32::<BigEndian>().unwrap();
let modification_time = reader.read_u32::<BigEndian>().unwrap();
let timescale = reader.read_u32::<BigEndian>().unwrap();
let duration = reader.read_u32::<BigEndian>().unwrap();
let language = reader.read_u16::<BigEndian>().unwrap();
let language_string = get_language_string(language);
skip(reader, current, size);
Ok(MdhdBox {
version,
flags,
creation_time,
modification_time,
timescale,
duration,
language,
language_string,
})
}
}
fn get_language_string(language: u16) -> String {
let mut lang: [u16; 3] = [0; 3];
lang[0] = ((language >> 10) & 0x1F) + 0x60;
lang[1] = ((language >> 5) & 0x1F) + 0x60;
lang[2] = ((language) & 0x1F) + 0x60;
// Decode utf-16 encoded bytes into a string.
let lang_str = decode_utf16(lang.iter().cloned())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
.collect::<String>();
return lang_str;
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for HdlrBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
read_box_header_ext!(reader, version, flags);
reader.read_u32::<BigEndian>().unwrap(); // skip.
let handler = reader.read_u32::<BigEndian>().unwrap();
let n = reader.seek(SeekFrom::Current(12)).unwrap(); // 12 bytes reserved.
let buf_size = (size as u64 - (n - current)) - HEADER_SIZE as u64;
let mut buf = vec![0u8; buf_size as usize];
reader.read_exact(&mut buf).unwrap();
let handler_string = match String::from_utf8(buf) {
Ok(t) => t,
_ => String::from("null"),
};
skip(reader, current, size);
Ok(HdlrBox {
version,
flags,
handler_type: From::from(handler),
name: handler_string,
})
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MinfBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.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(reader, start).unwrap();
let BoxHeader{ name, size: s } = header;
match name {
BoxType::VmhdBox => {
let vmhd = VmhdBox::read_box(reader, s as u32).unwrap();
minf.vmhd = Some(vmhd);
}
BoxType::SmhdBox => {
start = (s as u32 - HEADER_SIZE) as u64;
}
BoxType::DinfBox => {
start = (s as u32 - HEADER_SIZE) as u64;
}
BoxType::StblBox => {
let stbl = StblBox::read_box(reader, s as u32).unwrap();
minf.stbl = Some(stbl);
}
_ => break
}
}
skip(reader, current, size);
Ok(minf)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for VmhdBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
read_box_header_ext!(reader, version, flags);
let graphics_mode = reader.read_u16::<BigEndian>().unwrap();
let op_color = reader.read_u16::<BigEndian>().unwrap();
skip(reader, current, size);
Ok(VmhdBox {
version,
flags,
graphics_mode,
op_color,
})
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for StblBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let mut stbl = StblBox::new();
let start = 0u64;
while start < size as u64 {
// Get box header.
let header = read_box_header(reader, start).unwrap();
let BoxHeader{ name, size: s } = header;
match name {
BoxType::StsdBox => {
let stsd = StsdBox::read_box(reader, s as u32).unwrap();
stbl.stsd = Some(stsd);
}
BoxType::SttsBox => {
let stts = SttsBox::read_box(reader, s as u32).unwrap();
stbl.stts = Some(stts);
}
_ => break
}
}
Ok(stbl)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for SttsBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
read_box_header_ext!(reader, version, flags);
let entry_count = reader.read_u32::<BigEndian>().unwrap();
let mut sample_counts = Vec::new();
let mut sample_deltas = Vec::new();
for _i in 0..entry_count {
let sc = reader.read_u32::<BigEndian>().unwrap();
let sd = reader.read_u32::<BigEndian>().unwrap();
sample_counts.push(sc);
sample_deltas.push(sd);
}
skip(reader, current, size);
Ok(SttsBox {
version,
flags,
entry_count,
sample_counts,
sample_deltas,
})
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for StsdBox {
fn read_box(reader: &mut BufReader<R>, size: u32) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0)).unwrap(); // Current cursor position.
read_box_header_ext!(reader, version, flags);
reader.read_u32::<BigEndian>().unwrap(); // skip.
let mut start = 0u64;
while start < size as u64 {
// Get box header.
let header = read_box_header(reader, start).unwrap();
let BoxHeader{ name, size: s } = header;
match name {
BoxType::Avc1Box => {
start = (s as u32 - HEADER_SIZE) as u64;
}
BoxType::Mp4aBox => {
start = (s as u32 - HEADER_SIZE) as u64;
}
_ => break
}
}
skip(reader, current, size);
Ok(StsdBox {
version,
flags,
})
}
}
fn skip<R: Read + Seek>(reader: &mut BufReader<R>, current: u64, size: u32) {
let after = reader.seek(SeekFrom::Current(0)).unwrap();
let remaining_bytes = (size as u64 - (after - current)) as i64;
reader.seek(SeekFrom::Current(remaining_bytes - HEADER_SIZE as i64)).unwrap();
}

68
src/atoms/edts.rs Normal file
View file

@ -0,0 +1,68 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use crate::*;
use crate::atoms::elst::ElstBox;
#[derive(Debug, Default)]
pub struct EdtsBox {
pub elst: Option<ElstBox>,
}
impl EdtsBox {
pub(crate) fn new() -> EdtsBox {
Default::default()
}
}
impl Mp4Box for EdtsBox {
fn box_type(&self) -> BoxType {
BoxType::EdtsBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(elst) = &self.elst {
size += elst.box_size();
}
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for EdtsBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let mut edts = EdtsBox::new();
let start = 0u64;
while start < size {
// Get box header.
let header = read_box_header(reader, start)?;
let BoxHeader{ name, size: s } = header;
match name {
BoxType::ElstBox => {
let elst = ElstBox::read_box(reader, s)?;
edts.elst = Some(elst);
}
_ => break
}
}
skip_read(reader, current, size)?;
Ok(edts)
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for EdtsBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
if let Some(elst) = &self.elst {
elst.write_box(writer)?;
}
Ok(size)
}
}

104
src/atoms/elst.rs Normal file
View file

@ -0,0 +1,104 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default)]
pub struct ElstBox {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub entries: Vec<ElstEntry>,
}
#[derive(Debug, Default)]
pub struct ElstEntry {
pub segment_duration: u64,
pub media_time: u64,
pub media_rate: u16,
pub media_rate_fraction: u16,
}
impl Mp4Box for ElstBox {
fn box_type(&self) -> BoxType {
BoxType::ElstBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
if self.version == 1 {
size += self.entry_count as u64 * 20;
} else {
assert_eq!(self.version, 0);
size += self.entry_count as u64 * 12;
}
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for ElstBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
let entry_count = reader.read_u32::<BigEndian>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
for _ in 0..entry_count {
let (segment_duration, media_time)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
)
};
let entry = ElstEntry{
segment_duration,
media_time,
media_rate: reader.read_u16::<BigEndian>()?,
media_rate_fraction: reader.read_u16::<BigEndian>()?,
};
entries.push(entry);
}
skip_read(reader, current, size)?;
Ok(ElstBox {
version,
flags,
entry_count,
entries,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for ElstBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
assert_eq!(self.entry_count as usize, self.entries.len());
writer.write_u32::<BigEndian>(self.entry_count)?;
for entry in self.entries.iter() {
if self.version == 1 {
writer.write_u64::<BigEndian>(entry.segment_duration)?;
writer.write_u64::<BigEndian>(entry.media_time)?;
} else {
writer.write_u32::<BigEndian>(entry.segment_duration as u32)?;
writer.write_u32::<BigEndian>(entry.media_time as u32)?;
}
writer.write_u16::<BigEndian>(entry.media_rate)?;
writer.write_u16::<BigEndian>(entry.media_rate_fraction)?;
}
Ok(size)
}
}

97
src/atoms/ftyp.rs Normal file
View file

@ -0,0 +1,97 @@
use std::io::{BufReader, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default, PartialEq)]
pub struct FtypBox {
pub major_brand: FourCC,
pub minor_version: u32,
pub compatible_brands: Vec<FourCC>,
}
impl Mp4Box for FtypBox {
fn box_type(&self) -> BoxType {
BoxType::FtypBox
}
fn box_size(&self) -> u64 {
HEADER_SIZE + 8 + (4 * self.compatible_brands.len() as u64)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for FtypBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let major = reader.read_u32::<BigEndian>()?;
let minor = reader.read_u32::<BigEndian>()?;
if size % 4 != 0 {
return Err(Error::InvalidData("invalid ftyp size"));
}
let brand_count = (size - 16) / 4; // header + major + minor
let mut brands = Vec::new();
for _ in 0..brand_count {
let b = reader.read_u32::<BigEndian>()?;
brands.push(From::from(b));
}
Ok(FtypBox {
major_brand: From::from(major),
minor_version: minor,
compatible_brands: brands,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for FtypBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
writer.write_u32::<BigEndian>((&self.major_brand).into())?;
writer.write_u32::<BigEndian>(self.minor_version)?;
for b in self.compatible_brands.iter() {
writer.write_u32::<BigEndian>(b.into())?;
}
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::read_box_header;
use std::io::Cursor;
#[test]
fn test_ftyp() {
let src_box = FtypBox {
major_brand: FourCC { value: String::from("isom") },
minor_version: 0,
compatible_brands: vec![
FourCC { value: String::from("isom") },
FourCC { value: String::from("iso2") },
FourCC { value: String::from("avc1") },
FourCC { value: String::from("mp41") },
]
};
let mut buf = Vec::new();
{
let mut writer = BufWriter::new(&mut buf);
src_box.write_box(&mut writer).unwrap();
}
assert_eq!(buf.len(), src_box.box_size() as usize);
{
let mut reader = BufReader::new(Cursor::new(&buf));
let header = read_box_header(&mut reader, 0).unwrap();
assert_eq!(header.name, BoxType::FtypBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = FtypBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}
}

79
src/atoms/hdlr.rs Normal file
View file

@ -0,0 +1,79 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default)]
pub struct HdlrBox {
pub version: u8,
pub flags: u32,
pub handler_type: FourCC,
pub name: String,
}
impl Mp4Box for HdlrBox {
fn box_type(&self) -> BoxType {
BoxType::HdlrBox
}
fn box_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE + 20 + self.name.len() as u64 + 1
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for HdlrBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
reader.read_u32::<BigEndian>()?; // pre-defined
let handler = reader.read_u32::<BigEndian>()?;
let n = reader.seek(SeekFrom::Current(12))?; // 12 bytes reserved.
let buf_size = (size - (n - current)) - HEADER_SIZE;
let mut buf = vec![0u8; buf_size as usize];
reader.read_exact(&mut buf)?;
let handler_string = match String::from_utf8(buf) {
Ok(t) => {
assert_eq!(t.len(), buf_size as usize);
t
},
_ => String::from("null"),
};
skip_read(reader, current, size)?;
Ok(HdlrBox {
version,
flags,
handler_type: From::from(handler),
name: handler_string,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for HdlrBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
writer.write_u32::<BigEndian>(0)?; // pre-defined
writer.write_u32::<BigEndian>((&self.handler_type).into())?;
// 12 bytes reserved
for _ in 0..3 {
writer.write_u32::<BigEndian>(0)?;
}
writer.write(self.name.as_bytes())?;
writer.write_u8(0)?;
Ok(size)
}
}

119
src/atoms/mdhd.rs Normal file
View file

@ -0,0 +1,119 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default)]
pub struct MdhdBox {
pub version: u8,
pub flags: u32,
pub creation_time: u64,
pub modification_time: u64,
pub timescale: u32,
pub duration: u64,
pub language: u16,
pub language_string: String,
}
impl Mp4Box for MdhdBox {
fn box_type(&self) -> BoxType {
BoxType::MdhdBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
if self.version == 1 {
size += 28;
} else {
assert_eq!(self.version, 0);
size += 16;
}
size += 4;
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdhdBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, timescale, duration)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
let language = reader.read_u16::<BigEndian>()?;
let language_string = get_language_string(language);
skip_read(reader, current, size)?;
Ok(MdhdBox {
version,
flags,
creation_time,
modification_time,
timescale,
duration,
language,
language_string,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for MdhdBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
if self.version == 1 {
writer.write_u64::<BigEndian>(self.creation_time)?;
writer.write_u64::<BigEndian>(self.modification_time)?;
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u64::<BigEndian>(self.duration)?;
} else {
assert_eq!(self.version, 0);
writer.write_u32::<BigEndian>(self.creation_time as u32)?;
writer.write_u32::<BigEndian>(self.modification_time as u32)?;
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u32::<BigEndian>(self.duration as u32)?;
}
writer.write_u16::<BigEndian>(self.language)?;
writer.write_u16::<BigEndian>(0)?; // pre-defined
Ok(size)
}
}
fn get_language_string(language: u16) -> String {
let mut lang: [u16; 3] = [0; 3];
lang[0] = ((language >> 10) & 0x1F) + 0x60;
lang[1] = ((language >> 5) & 0x1F) + 0x60;
lang[2] = ((language) & 0x1F) + 0x60;
// Decode utf-16 encoded bytes into a string.
let lang_str = decode_utf16(lang.iter().cloned())
.map(|r| r.unwrap_or(REPLACEMENT_CHARACTER))
.collect::<String>();
return lang_str;
}

90
src/atoms/mdia.rs Normal file
View file

@ -0,0 +1,90 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use crate::*;
use crate::atoms::{mdhd::MdhdBox, hdlr::HdlrBox, minf::MinfBox};
#[derive(Debug, Default)]
pub struct MdiaBox {
pub mdhd: Option<MdhdBox>,
pub hdlr: Option<HdlrBox>,
pub minf: Option<MinfBox>,
}
impl MdiaBox {
pub(crate) fn new() -> MdiaBox {
Default::default()
}
}
impl Mp4Box for MdiaBox {
fn box_type(&self) -> BoxType {
BoxType::MdiaBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(mdhd) = &self.mdhd {
size += mdhd.box_size();
}
if let Some(hdlr) = &self.hdlr {
size += hdlr.box_size();
}
if let Some(minf) = &self.minf {
size += minf.box_size();
}
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MdiaBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let mut mdia = MdiaBox::new();
let start = 0u64;
while start < size {
// Get box header.
let header = read_box_header(reader, start)?;
let BoxHeader{ name, size: s } = header;
match name {
BoxType::MdhdBox => {
let mdhd = MdhdBox::read_box(reader, s)?;
mdia.mdhd = Some(mdhd);
}
BoxType::HdlrBox => {
let hdlr = HdlrBox::read_box(reader, s)?;
mdia.hdlr = Some(hdlr);
}
BoxType::MinfBox => {
let minf = MinfBox::read_box(reader, s)?;
mdia.minf = Some(minf);
}
_ => break
}
}
skip_read(reader, current, size)?;
Ok(mdia)
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for MdiaBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
if let Some(mdhd) = &self.mdhd {
mdhd.write_box(writer)?;
}
if let Some(hdlr) = &self.hdlr {
hdlr.write_box(writer)?;
}
if let Some(minf) = &self.minf {
minf.write_box(writer)?;
}
Ok(size)
}
}

85
src/atoms/minf.rs Normal file
View file

@ -0,0 +1,85 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use crate::*;
use crate::atoms::{vmhd::VmhdBox, stbl::StblBox};
#[derive(Debug, Default)]
pub struct MinfBox {
pub vmhd: Option<VmhdBox>,
pub stbl: Option<StblBox>,
}
impl MinfBox {
pub(crate) fn new() -> MinfBox {
Default::default()
}
}
impl Mp4Box for MinfBox {
fn box_type(&self) -> BoxType {
BoxType::MinfBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(vmhd) = &self.vmhd {
size += vmhd.box_size();
}
if let Some(stbl) = &self.stbl {
size += stbl.box_size();
}
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MinfBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let mut minf = MinfBox::new();
let mut start = 0u64;
while start < size {
// Get box header.
let header = read_box_header(reader, start)?;
let BoxHeader{ name, size: s } = header;
match name {
BoxType::VmhdBox => {
let vmhd = VmhdBox::read_box(reader, s)?;
minf.vmhd = Some(vmhd);
}
BoxType::SmhdBox => {
start = s - HEADER_SIZE;
}
BoxType::DinfBox => {
start = s - HEADER_SIZE;
}
BoxType::StblBox => {
let stbl = StblBox::read_box(reader, s)?;
minf.stbl = Some(stbl);
}
_ => break
}
}
skip_read(reader, current, size)?;
Ok(minf)
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for MinfBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
if let Some(vmhd) = &self.vmhd {
vmhd.write_box(writer)?;
}
if let Some(stbl) = &self.stbl {
stbl.write_box(writer)?;
}
Ok(size)
}
}

217
src/atoms/mod.rs Normal file
View file

@ -0,0 +1,217 @@
use std::fmt;
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
mod ftyp;
mod moov;
mod mvhd;
mod trak;
mod tkhd;
mod edts;
mod elst;
mod mdia;
mod mdhd;
mod hdlr;
mod minf;
mod vmhd;
mod stbl;
mod stts;
mod stsd;
pub use ftyp::FtypBox;
pub use moov::MoovBox;
pub const HEADER_EXT_SIZE: u64 = 4;
macro_rules! boxtype {
($( $name:ident => $value:expr ),*) => {
#[derive(Clone, Copy, PartialEq)]
pub enum BoxType {
$( $name, )*
UnknownBox(u32),
}
impl From<u32> for BoxType {
fn from(t: u32) -> BoxType {
match t {
$( $value => BoxType::$name, )*
_ => BoxType::UnknownBox(t),
}
}
}
impl Into<u32> for BoxType {
fn into(self) -> u32 {
match self {
$( BoxType::$name => $value, )*
BoxType::UnknownBox(t) => t,
}
}
}
}
}
boxtype!{
FtypBox => 0x66747970,
MvhdBox => 0x6d766864,
FreeBox => 0x66726565,
MdatBox => 0x6d646174,
MoovBox => 0x6d6f6f76,
MoofBox => 0x6d6f6f66,
TkhdBox => 0x746b6864,
EdtsBox => 0x65647473,
MdiaBox => 0x6d646961,
ElstBox => 0x656c7374,
MdhdBox => 0x6d646864,
HdlrBox => 0x68646c72,
MinfBox => 0x6d696e66,
VmhdBox => 0x766d6864,
StblBox => 0x7374626c,
StsdBox => 0x73747364,
SttsBox => 0x73747473,
TrakBox => 0x7472616b,
UdtaBox => 0x75647461,
DinfBox => 0x64696e66,
SmhdBox => 0x736d6864,
Avc1Box => 0x61766331,
Mp4aBox => 0x6d703461
}
impl fmt::Debug for BoxType {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let fourcc: FourCC = From::from(self.clone());
write!(f, "{}", fourcc)
}
}
#[derive(Default, PartialEq, Clone)]
pub struct FourCC {
pub value: String
}
impl From<u32> for FourCC {
fn from(number: u32) -> Self {
let mut box_chars = Vec::new();
for x in 0..4 {
let c = (number >> (x * 8) & 0x0000_00FF) as u8;
box_chars.push(c);
}
box_chars.reverse();
let box_string = match String::from_utf8(box_chars) {
Ok(t) => t,
_ => String::from("null"), // error to retrieve fourcc
};
FourCC {
value: box_string
}
}
}
impl From<FourCC> for u32 {
fn from(fourcc: FourCC) -> u32 {
(&fourcc).into()
}
}
impl From<&FourCC> for u32 {
fn from(fourcc: &FourCC) -> u32 {
let mut b: [u8; 4] = Default::default();
b.copy_from_slice(fourcc.value.as_bytes());
u32::from_be_bytes(b)
}
}
impl From<BoxType> for FourCC {
fn from(t: BoxType) -> FourCC {
let box_num: u32 = Into::into(t);
From::from(box_num)
}
}
impl fmt::Debug for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
impl fmt::Display for FourCC {
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
write!(f, "{}", self.value)
}
}
pub trait Mp4Box: Sized {
fn box_type(&self) -> BoxType;
fn box_size(&self) -> u64;
}
pub trait ReadBox<T>: Sized {
fn read_box(_: T, size: u64) -> Result<Self>;
}
pub trait WriteBox<T>: Sized {
fn write_box(&self, _: T) -> Result<u64>;
}
pub fn read_box_header_ext<R: Read>(reader: &mut BufReader<R>) -> Result<(u8, u32)> {
let version = reader.read_u8()?;
let flags_a = reader.read_u8()?;
let flags_b = reader.read_u8()?;
let flags_c = reader.read_u8()?;
let flags = u32::from(flags_a) << 16 | u32::from(flags_b) << 8 | u32::from(flags_c);
Ok((version, flags))
}
pub fn write_box_header_ext<W: Write>(w: &mut BufWriter<W>, v: u8, f: u32) -> Result<u64> {
let d = u32::from(v) << 24 | f;
w.write_u32::<BigEndian>(d)?;
Ok(4)
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for BoxHeader {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
if self.size > u32::MAX as u64 {
writer.write_u32::<BigEndian>(1)?;
writer.write_u32::<BigEndian>(self.name.into())?;
writer.write_u64::<BigEndian>(self.size)?;
Ok(16)
} else {
writer.write_u32::<BigEndian>(self.size as u32)?;
writer.write_u32::<BigEndian>(self.name.into())?;
Ok(8)
}
}
}
pub fn skip_read<R: Read + Seek>(reader: &mut BufReader<R>, current: u64, size: u64) -> Result<i64> {
let after = reader.seek(SeekFrom::Current(0))?;
let remaining_bytes = (size - (after - current)) as i64;
let size = remaining_bytes - HEADER_SIZE as i64;
reader.seek(SeekFrom::Current(size))?;
Ok(size)
}
pub fn skip_write<W: Write>(writer: &mut BufWriter<W>, size: u64) -> Result<u64> {
for _ in 0..size {
writer.write_u8(0)?;
}
Ok(size)
}
#[cfg(test)]
mod tests {
use super::*;
#[test]
fn test_fourcc() {
let ftyp_fcc = 0x66747970;
let ftyp_value = FourCC::from(ftyp_fcc);
assert_eq!(ftyp_value.value, "ftyp");
let ftyp_fcc2 = ftyp_value.into();
assert_eq!(ftyp_fcc, ftyp_fcc2);
}
}

73
src/atoms/moov.rs Normal file
View file

@ -0,0 +1,73 @@
use std::io::{BufReader, Seek, Read, BufWriter, Write};
use crate::*;
use crate::atoms::{mvhd::MvhdBox, trak::TrakBox};
#[derive(Debug, Default)]
pub struct MoovBox {
pub mvhd: MvhdBox,
pub traks: Vec<TrakBox>,
}
impl MoovBox {
pub(crate) fn new() -> MoovBox {
Default::default()
}
}
impl Mp4Box for MoovBox {
fn box_type(&self) -> BoxType {
BoxType::MoovBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE + self.mvhd.box_size();
for trak in self.traks.iter() {
size += trak.box_size();
}
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MoovBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let mut moov = MoovBox::new();
let mut start = 0u64;
while start < size {
// Get box header.
let header = read_box_header(reader, start)?;
let BoxHeader{ name, size: s } = header;
match name {
BoxType::MvhdBox => {
moov.mvhd = MvhdBox::read_box(reader, s)?;
}
BoxType::TrakBox => {
let trak = TrakBox::read_box(reader, s)?;
moov.traks.push(trak);
}
BoxType::UdtaBox => {
start = s - HEADER_SIZE;
}
_ => break
}
}
Ok(moov)
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for MoovBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
self.mvhd.write_box(writer)?;
for trak in self.traks.iter() {
trak.write_box(writer)?;
}
Ok(0)
}
}

137
src/atoms/mvhd.rs Normal file
View file

@ -0,0 +1,137 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default, PartialEq)]
pub struct MvhdBox {
pub version: u8,
pub flags: u32,
pub creation_time: u64,
pub modification_time: u64,
pub timescale: u32,
pub duration: u64,
pub rate: u32,
}
impl Mp4Box for MvhdBox {
fn box_type(&self) -> BoxType {
BoxType::MvhdBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
if self.version == 1 {
size += 28;
} else {
assert_eq!(self.version, 0);
size += 16;
}
size += 80;
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for MvhdBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, timescale, duration)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
let rate = reader.read_u32::<BigEndian>()?;
skip_read(reader, current, size)?;
Ok(MvhdBox{
version,
flags,
creation_time,
modification_time,
timescale,
duration,
rate,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for MvhdBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
if self.version == 1 {
writer.write_u64::<BigEndian>(self.creation_time)?;
writer.write_u64::<BigEndian>(self.modification_time)?;
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u64::<BigEndian>(self.duration)?;
} else {
assert_eq!(self.version, 0);
writer.write_u32::<BigEndian>(self.creation_time as u32)?;
writer.write_u32::<BigEndian>(self.modification_time as u32)?;
writer.write_u32::<BigEndian>(self.timescale)?;
writer.write_u32::<BigEndian>(self.duration as u32)?;
}
writer.write_u32::<BigEndian>(self.rate)?;
// XXX volume, ...
skip_write(writer, 76)?;
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::read_box_header;
use std::io::Cursor;
#[test]
fn test_mvhd() {
let src_box = MvhdBox {
version: 0,
flags: 0,
creation_time: 100,
modification_time: 200,
timescale: 1000,
duration: 634634,
rate: 0x00010000,
};
let mut buf = Vec::new();
{
let mut writer = BufWriter::new(&mut buf);
src_box.write_box(&mut writer).unwrap();
}
assert_eq!(buf.len(), src_box.box_size() as usize);
{
let mut reader = BufReader::new(Cursor::new(&buf));
let header = read_box_header(&mut reader, 0).unwrap();
assert_eq!(header.name, BoxType::MvhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = MvhdBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}
}

76
src/atoms/stbl.rs Normal file
View file

@ -0,0 +1,76 @@
use std::io::{BufReader, Seek, Read, BufWriter, Write};
use crate::*;
use crate::atoms::{stts::SttsBox, stsd::StsdBox};
#[derive(Debug, Default)]
pub struct StblBox {
pub stts: Option<SttsBox>,
pub stsd: Option<StsdBox>,
}
impl StblBox {
pub(crate) fn new() -> StblBox {
Default::default()
}
}
impl Mp4Box for StblBox {
fn box_type(&self) -> BoxType {
BoxType::StblBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(stts) = &self.stts {
size += stts.box_size();
}
if let Some(stsd) = &self.stsd {
size += stsd.box_size();
}
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for StblBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let mut stbl = StblBox::new();
let start = 0u64;
while start < size {
// Get box header.
let header = read_box_header(reader, start)?;
let BoxHeader{ name, size: s } = header;
match name {
BoxType::SttsBox => {
let stts = SttsBox::read_box(reader, s)?;
stbl.stts = Some(stts);
}
BoxType::StsdBox => {
let stsd = StsdBox::read_box(reader, s)?;
stbl.stsd = Some(stsd);
}
_ => break
}
}
Ok(stbl)
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for StblBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
if let Some(stts) = &self.stts {
stts.write_box(writer)?;
}
if let Some(stsd) = &self.stsd {
stsd.write_box(writer)?;
}
Ok(size)
}
}

61
src/atoms/stsd.rs Normal file
View file

@ -0,0 +1,61 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt};
use crate::*;
#[derive(Debug)]
pub struct StsdBox {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
}
impl Mp4Box for StsdBox {
fn box_type(&self) -> BoxType {
BoxType::StsdBox
}
fn box_size(&self) -> u64 {
// TODO
0
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for StsdBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
let entry_count = reader.read_u32::<BigEndian>()?;
let mut start = 0u64;
while start < size {
// Get box header.
let header = read_box_header(reader, start)?;
let BoxHeader{ name, size: s } = header;
match name {
BoxType::Avc1Box => {}
BoxType::Mp4aBox => {}
_ => break
}
start += s - HEADER_SIZE;
}
skip_read(reader, current, size)?;
Ok(StsdBox {
version,
flags,
entry_count,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for StsdBox {
fn write_box(&self, _writer: &mut BufWriter<W>) -> Result<u64> {
// TODO
Ok(0)
}
}

72
src/atoms/stts.rs Normal file
View file

@ -0,0 +1,72 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default)]
pub struct SttsBox {
pub version: u8,
pub flags: u32,
pub entry_count: u32,
pub entries: Vec<SttsEntry>,
}
#[derive(Debug, Default)]
pub struct SttsEntry {
pub sample_count: u32,
pub sample_delta: u32,
}
impl Mp4Box for SttsBox {
fn box_type(&self) -> BoxType {
BoxType::SttsBox
}
fn box_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE + 4 + (8 * self.entry_count as u64)
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for SttsBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
let entry_count = reader.read_u32::<BigEndian>()?;
let mut entries = Vec::with_capacity(entry_count as usize);
for _i in 0..entry_count {
let entry = SttsEntry {
sample_count: reader.read_u32::<BigEndian>()?,
sample_delta: reader.read_u32::<BigEndian>()?,
};
entries.push(entry);
}
skip_read(reader, current, size)?;
Ok(SttsBox {
version,
flags,
entry_count,
entries,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for SttsBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
writer.write_u32::<BigEndian>(self.entry_count)?;
for entry in self.entries.iter() {
writer.write_u32::<BigEndian>(entry.sample_count)?;
writer.write_u32::<BigEndian>(entry.sample_delta)?;
}
Ok(size)
}
}

215
src/atoms/tkhd.rs Normal file
View file

@ -0,0 +1,215 @@
use std::io::{BufReader, Seek, SeekFrom, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default, PartialEq)]
pub struct TkhdBox {
pub version: u8,
pub flags: u32,
pub creation_time: u64,
pub modification_time: u64,
pub track_id: u32,
pub duration: u64,
pub layer: u16,
pub alternate_group: u16,
pub volume: u16,
pub matrix: Matrix,
pub width: u32,
pub height: u32,
}
#[derive(Debug, Default, PartialEq)]
pub struct Matrix {
pub a: i32,
pub b: i32,
pub u: i32,
pub c: i32,
pub d: i32,
pub v: i32,
pub x: i32,
pub y: i32,
pub w: i32,
}
impl Mp4Box for TkhdBox {
fn box_type(&self) -> BoxType {
BoxType::TkhdBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
if self.version == 1 {
size += 32;
} else {
assert_eq!(self.version, 0);
size += 20;
}
size += 60;
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TkhdBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
let (creation_time, modification_time, track_id, _, duration)
= if version == 1 {
(
reader.read_u64::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u64::<BigEndian>()?,
)
} else {
assert_eq!(version, 0);
(
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()? as u64,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()?,
reader.read_u32::<BigEndian>()? as u64,
)
};
reader.read_u64::<BigEndian>()?; // reserved
let layer = reader.read_u16::<BigEndian>()?;
let alternate_group = reader.read_u16::<BigEndian>()?;
let volume = reader.read_u16::<BigEndian>()?;
reader.read_u16::<BigEndian>()?; // reserved
let matrix = Matrix{
a: reader.read_i32::<byteorder::LittleEndian>()?,
b: reader.read_i32::<BigEndian>()?,
u: reader.read_i32::<BigEndian>()?,
c: reader.read_i32::<BigEndian>()?,
d: reader.read_i32::<BigEndian>()?,
v: reader.read_i32::<BigEndian>()?,
x: reader.read_i32::<BigEndian>()?,
y: reader.read_i32::<BigEndian>()?,
w: reader.read_i32::<BigEndian>()?,
};
let width = reader.read_u32::<BigEndian>()? >> 16;
let height = reader.read_u32::<BigEndian>()? >> 16;
skip_read(reader, current, size)?;
Ok(TkhdBox {
version,
flags,
creation_time,
modification_time,
track_id,
duration,
layer,
alternate_group,
volume,
matrix,
width,
height,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for TkhdBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
if self.version == 1 {
writer.write_u64::<BigEndian>(self.creation_time)?;
writer.write_u64::<BigEndian>(self.modification_time)?;
writer.write_u32::<BigEndian>(self.track_id)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u64::<BigEndian>(self.duration)?;
} else {
assert_eq!(self.version, 0);
writer.write_u32::<BigEndian>(self.creation_time as u32)?;
writer.write_u32::<BigEndian>(self.modification_time as u32)?;
writer.write_u32::<BigEndian>(self.track_id)?;
writer.write_u32::<BigEndian>(0)?; // reserved
writer.write_u32::<BigEndian>(self.duration as u32)?;
}
writer.write_u64::<BigEndian>(0)?; // reserved
writer.write_u16::<BigEndian>(self.layer)?;
writer.write_u16::<BigEndian>(self.alternate_group)?;
writer.write_u16::<BigEndian>(self.volume)?;
writer.write_u16::<BigEndian>(0)?; // reserved
writer.write_i32::<byteorder::LittleEndian>(self.matrix.a)?;
writer.write_i32::<BigEndian>(self.matrix.b)?;
writer.write_i32::<BigEndian>(self.matrix.u)?;
writer.write_i32::<BigEndian>(self.matrix.c)?;
writer.write_i32::<BigEndian>(self.matrix.d)?;
writer.write_i32::<BigEndian>(self.matrix.v)?;
writer.write_i32::<BigEndian>(self.matrix.x)?;
writer.write_i32::<BigEndian>(self.matrix.y)?;
writer.write_i32::<BigEndian>(self.matrix.w)?;
writer.write_u32::<BigEndian>(self.width << 16)?;
writer.write_u32::<BigEndian>(self.height << 16)?;
Ok(size)
}
}
#[cfg(test)]
mod tests {
use super::*;
use crate::read_box_header;
use std::io::Cursor;
#[test]
fn test_tkhd() {
let src_box = TkhdBox {
version: 0,
flags: 0,
creation_time: 100,
modification_time: 200,
track_id: 1,
duration: 634634,
layer: 0,
alternate_group: 0,
volume: 0x0100,
matrix: Matrix {
a: 0x00010000,
b: 0,
u: 0,
c: 0,
d: 0x00010000,
v: 0,
x: 0,
y: 0,
w: 0x40000000,
},
width: 512,
height: 288,
};
let mut buf = Vec::new();
{
let mut writer = BufWriter::new(&mut buf);
src_box.write_box(&mut writer).unwrap();
}
assert_eq!(buf.len(), src_box.box_size() as usize);
{
let mut reader = BufReader::new(Cursor::new(&buf));
let header = read_box_header(&mut reader, 0).unwrap();
assert_eq!(header.name, BoxType::TkhdBox);
assert_eq!(src_box.box_size(), header.size);
let dst_box = TkhdBox::read_box(&mut reader, header.size).unwrap();
assert_eq!(src_box, dst_box);
}
}
}

90
src/atoms/trak.rs Normal file
View file

@ -0,0 +1,90 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use crate::*;
use crate::atoms::{tkhd::TkhdBox, edts::EdtsBox, mdia::MdiaBox};
#[derive(Debug, Default)]
pub struct TrakBox {
pub tkhd: Option<TkhdBox>,
pub edts: Option<EdtsBox>,
pub mdia: Option<MdiaBox>,
}
impl TrakBox {
pub(crate) fn new() -> TrakBox {
Default::default()
}
}
impl Mp4Box for TrakBox {
fn box_type(&self) -> BoxType {
BoxType::TrakBox
}
fn box_size(&self) -> u64 {
let mut size = HEADER_SIZE;
if let Some(tkhd) = &self.tkhd {
size += tkhd.box_size();
}
if let Some(edts) = &self.edts {
size += edts.box_size();
}
if let Some(mdia) = &self.mdia {
size += mdia.box_size();
}
size
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for TrakBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let mut trak = TrakBox::new();
let start = 0u64;
while start < size {
// Get box header.
let header = read_box_header(reader, start)?;
let BoxHeader{ name, size: s } = header;
match name {
BoxType::TkhdBox => {
let tkhd = TkhdBox::read_box(reader, s)?;
trak.tkhd = Some(tkhd);
}
BoxType::EdtsBox => {
let edts = EdtsBox::read_box(reader, s)?;
trak.edts = Some(edts);
}
BoxType::MdiaBox => {
let mdia = MdiaBox::read_box(reader, s)?;
trak.mdia = Some(mdia);
}
_ => break
}
}
skip_read(reader, current, size)?;
Ok(trak)
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for TrakBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
if let Some(tkhd) = &self.tkhd {
tkhd.write_box(writer)?;
}
if let Some(edts) = &self.edts {
edts.write_box(writer)?;
}
if let Some(mdia) = &self.mdia {
mdia.write_box(writer)?;
}
Ok(size)
}
}

69
src/atoms/vmhd.rs Normal file
View file

@ -0,0 +1,69 @@
use std::io::{BufReader, SeekFrom, Seek, Read, BufWriter, Write};
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use crate::*;
#[derive(Debug, Default)]
pub struct VmhdBox {
pub version: u8,
pub flags: u32,
pub graphics_mode: u16,
pub op_color: RgbColor,
}
#[derive(Debug, Default)]
pub struct RgbColor {
pub red: u16,
pub green: u16,
pub blue: u16,
}
impl Mp4Box for VmhdBox {
fn box_type(&self) -> BoxType {
BoxType::VmhdBox
}
fn box_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE + 8
}
}
impl<R: Read + Seek> ReadBox<&mut BufReader<R>> for VmhdBox {
fn read_box(reader: &mut BufReader<R>, size: u64) -> Result<Self> {
let current = reader.seek(SeekFrom::Current(0))?; // Current cursor position.
let (version, flags) = read_box_header_ext(reader)?;
let graphics_mode = reader.read_u16::<BigEndian>()?;
let op_color = RgbColor {
red: reader.read_u16::<BigEndian>()?,
green: reader.read_u16::<BigEndian>()?,
blue: reader.read_u16::<BigEndian>()?,
};
skip_read(reader, current, size)?;
Ok(VmhdBox {
version,
flags,
graphics_mode,
op_color,
})
}
}
impl<W: Write> WriteBox<&mut BufWriter<W>> for VmhdBox {
fn write_box(&self, writer: &mut BufWriter<W>) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write_box(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
writer.write_u16::<BigEndian>(self.graphics_mode)?;
writer.write_u16::<BigEndian>(self.op_color.red)?;
writer.write_u16::<BigEndian>(self.op_color.green)?;
writer.write_u16::<BigEndian>(self.op_color.blue)?;
Ok(size)
}
}

9
src/error.rs Normal file
View file

@ -0,0 +1,9 @@
use thiserror::Error;
#[derive(Error, Debug)]
pub enum Error {
#[error("{0}")]
IoError(#[from] std::io::Error),
#[error("{0}")]
InvalidData(&'static str),
}

View file

@ -1,19 +1,17 @@
extern crate byteorder; use std::io::{BufReader, Read, SeekFrom, Seek};
use std::io::prelude::*;
use std::io::{BufReader, Read, SeekFrom};
use std::fs::File; use std::fs::File;
use std::convert::TryInto; use std::convert::TryInto;
mod atoms; mod atoms;
use crate::atoms::*; use crate::atoms::*;
const HEADER_SIZE: u32 = 8; mod error;
pub use error::Error;
#[derive(Debug)] pub type Result<T> = std::result::Result<T, Error>;
pub enum Error {
InvalidData(&'static str), // XXX if box has largesize
} const HEADER_SIZE: u64 = 8;
#[derive(Debug, PartialEq)] #[derive(Debug, PartialEq)]
pub enum TrackType { pub enum TrackType {
@ -42,16 +40,22 @@ struct BoxHeader {
size: u64, size: u64,
} }
impl BoxHeader {
fn new(name: BoxType, size: u64) -> Self {
Self { name, size }
}
}
pub fn read_mp4(f: File) -> Result<BMFF> { pub fn read_mp4(f: File) -> Result<BMFF> {
// Open file and read boxes. // Open file and read boxes.
let bmff = read_boxes(f).unwrap(); let bmff = read_boxes(f)?;
Ok(bmff) Ok(bmff)
} }
fn read_boxes(f: File) -> Result<BMFF> { fn read_boxes(f: File) -> Result<BMFF> {
let filesize = f.metadata().unwrap().len(); let filesize = f.metadata()?.len();
let mut reader = BufReader::new(f); let mut reader = BufReader::new(f);
let mut bmff = BMFF::new(); let mut bmff = BMFF::new();
bmff.size = filesize; bmff.size = filesize;
@ -60,27 +64,27 @@ fn read_boxes(f: File) -> Result<BMFF> {
while start < filesize { while start < filesize {
// Get box header. // Get box header.
let header = read_box_header(&mut reader, start).unwrap(); let header = read_box_header(&mut reader, start)?;
let BoxHeader{ name, size } = header; let BoxHeader{ name, size } = header;
// Match and parse the atom boxes. // Match and parse the atom boxes.
match name { match name {
BoxType::FtypBox => { BoxType::FtypBox => {
let ftyp = FtypBox::read_box(&mut reader, size as u32).unwrap(); let ftyp = FtypBox::read_box(&mut reader, size)?;
bmff.ftyp = ftyp; bmff.ftyp = ftyp;
} }
BoxType::FreeBox => { BoxType::FreeBox => {
start = 0; start = 0;
} }
BoxType::MdatBox => { BoxType::MdatBox => {
start = (size as u32 - HEADER_SIZE) as u64; start = size - HEADER_SIZE;
} }
BoxType::MoovBox => { BoxType::MoovBox => {
let moov = MoovBox::read_box(&mut reader, size as u32).unwrap(); let moov = MoovBox::read_box(&mut reader, size)?;
bmff.moov = Some(moov); bmff.moov = Some(moov);
} }
BoxType::MoofBox => { BoxType::MoofBox => {
start = (size as u32 - HEADER_SIZE) as u64; start = size - HEADER_SIZE;
} }
_ => { _ => {
// Skip over unsupported boxes, but stop if the size is zero, // Skip over unsupported boxes, but stop if the size is zero,
@ -88,7 +92,7 @@ fn read_boxes(f: File) -> Result<BMFF> {
if size == 0 { if size == 0 {
break; break;
} else { } else {
start = (size as u32 - HEADER_SIZE) as u64; start = size - HEADER_SIZE;
} }
} }
}; };
@ -103,7 +107,7 @@ fn read_box_header<R: Read + Seek>(reader: &mut BufReader<R>, start: u64) -> Res
// Create and read to buf. // Create and read to buf.
let mut buf = [0u8;8]; // 8 bytes for box header. let mut buf = [0u8;8]; // 8 bytes for box header.
reader.read(&mut buf).unwrap(); reader.read(&mut buf)?;
// Get size. // Get size.
let s = buf[0..4].try_into().unwrap(); let s = buf[0..4].try_into().unwrap();
@ -115,7 +119,7 @@ fn read_box_header<R: Read + Seek>(reader: &mut BufReader<R>, start: u64) -> Res
// Get largesize if size is 1 // Get largesize if size is 1
if size == 1 { if size == 1 {
reader.read(&mut buf).unwrap(); reader.read(&mut buf)?;
let s = buf.try_into().unwrap(); let s = buf.try_into().unwrap();
let largesize = u64::from_be_bytes(s); let largesize = u64::from_be_bytes(s);