mirror of
https://github.com/alfg/mp4-rust.git
synced 2024-05-08 19:43:03 +00:00
3ecfd0cc9d
* Run cargo clippy --fix * Run cargo fmt --all
345 lines
8.5 KiB
Rust
345 lines
8.5 KiB
Rust
//! All ISO-MP4 boxes (atoms) and operations.
|
|
//!
|
|
//! * [ISO/IEC 14496-12](https://en.wikipedia.org/wiki/MPEG-4_Part_14) - ISO Base Media File Format (QuickTime, MPEG-4, etc)
|
|
//! * [ISO/IEC 14496-14](https://en.wikipedia.org/wiki/MPEG-4_Part_14) - MP4 file format
|
|
//! * ISO/IEC 14496-17 - Streaming text format
|
|
//! * [ISO 23009-1](https://www.iso.org/standard/79329.html) -Dynamic adaptive streaming over HTTP (DASH)
|
|
//!
|
|
//! http://developer.apple.com/documentation/QuickTime/QTFF/index.html
|
|
//! http://www.adobe.com/devnet/video/articles/mp4_movie_atom.html
|
|
//! http://mp4ra.org/#/atoms
|
|
//!
|
|
//! Supported Atoms:
|
|
//! ftyp
|
|
//! moov
|
|
//! mvhd
|
|
//! trak
|
|
//! tkhd
|
|
//! mdia
|
|
//! mdhd
|
|
//! hdlr
|
|
//! minf
|
|
//! stbl
|
|
//! stsd
|
|
//! avc1
|
|
//! hev1
|
|
//! mp4a
|
|
//! tx3g
|
|
//! stts
|
|
//! stsc
|
|
//! stsz
|
|
//! stss
|
|
//! stco
|
|
//! co64
|
|
//! ctts
|
|
//! dinf
|
|
//! dref
|
|
//! smhd
|
|
//! vmhd
|
|
//! edts
|
|
//! elst
|
|
//! mvex
|
|
//! mehd
|
|
//! trex
|
|
//! emsg
|
|
//! moof
|
|
//! mfhd
|
|
//! traf
|
|
//! tfhd
|
|
//! trun
|
|
//! mdat
|
|
//! free
|
|
//!
|
|
|
|
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
|
|
use std::convert::TryInto;
|
|
use std::io::{Read, Seek, SeekFrom, Write};
|
|
|
|
use crate::*;
|
|
|
|
pub(crate) mod avc1;
|
|
pub(crate) mod co64;
|
|
pub(crate) mod ctts;
|
|
pub(crate) mod dinf;
|
|
pub(crate) mod edts;
|
|
pub(crate) mod elst;
|
|
pub(crate) mod emsg;
|
|
pub(crate) mod ftyp;
|
|
pub(crate) mod hdlr;
|
|
pub(crate) mod hev1;
|
|
pub(crate) mod mdhd;
|
|
pub(crate) mod mdia;
|
|
pub(crate) mod mehd;
|
|
pub(crate) mod mfhd;
|
|
pub(crate) mod minf;
|
|
pub(crate) mod moof;
|
|
pub(crate) mod moov;
|
|
pub(crate) mod mp4a;
|
|
pub(crate) mod mvex;
|
|
pub(crate) mod mvhd;
|
|
pub(crate) mod smhd;
|
|
pub(crate) mod stbl;
|
|
pub(crate) mod stco;
|
|
pub(crate) mod stsc;
|
|
pub(crate) mod stsd;
|
|
pub(crate) mod stss;
|
|
pub(crate) mod stsz;
|
|
pub(crate) mod stts;
|
|
pub(crate) mod tfhd;
|
|
pub(crate) mod tkhd;
|
|
pub(crate) mod traf;
|
|
pub(crate) mod trak;
|
|
pub(crate) mod trex;
|
|
pub(crate) mod trun;
|
|
pub(crate) mod tx3g;
|
|
pub(crate) mod vmhd;
|
|
pub(crate) mod vp09;
|
|
pub(crate) mod vpcc;
|
|
|
|
pub use emsg::EmsgBox;
|
|
pub use ftyp::FtypBox;
|
|
pub use moof::MoofBox;
|
|
pub use moov::MoovBox;
|
|
|
|
pub const HEADER_SIZE: u64 = 8;
|
|
// const HEADER_LARGE_SIZE: u64 = 16;
|
|
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,
|
|
MfhdBox => 0x6d666864,
|
|
FreeBox => 0x66726565,
|
|
MdatBox => 0x6d646174,
|
|
MoovBox => 0x6d6f6f76,
|
|
MvexBox => 0x6d766578,
|
|
MehdBox => 0x6d656864,
|
|
TrexBox => 0x74726578,
|
|
EmsgBox => 0x656d7367,
|
|
MoofBox => 0x6d6f6f66,
|
|
TkhdBox => 0x746b6864,
|
|
TfhdBox => 0x74666864,
|
|
EdtsBox => 0x65647473,
|
|
MdiaBox => 0x6d646961,
|
|
ElstBox => 0x656c7374,
|
|
MdhdBox => 0x6d646864,
|
|
HdlrBox => 0x68646c72,
|
|
MinfBox => 0x6d696e66,
|
|
VmhdBox => 0x766d6864,
|
|
StblBox => 0x7374626c,
|
|
StsdBox => 0x73747364,
|
|
SttsBox => 0x73747473,
|
|
CttsBox => 0x63747473,
|
|
StssBox => 0x73747373,
|
|
StscBox => 0x73747363,
|
|
StszBox => 0x7374737A,
|
|
StcoBox => 0x7374636F,
|
|
Co64Box => 0x636F3634,
|
|
TrakBox => 0x7472616b,
|
|
TrafBox => 0x74726166,
|
|
TrunBox => 0x7472756E,
|
|
UdtaBox => 0x75647461,
|
|
DinfBox => 0x64696e66,
|
|
DrefBox => 0x64726566,
|
|
UrlBox => 0x75726C20,
|
|
SmhdBox => 0x736d6864,
|
|
Avc1Box => 0x61766331,
|
|
AvcCBox => 0x61766343,
|
|
Hev1Box => 0x68657631,
|
|
HvcCBox => 0x68766343,
|
|
Mp4aBox => 0x6d703461,
|
|
EsdsBox => 0x65736473,
|
|
Tx3gBox => 0x74783367,
|
|
VpccBox => 0x76706343,
|
|
Vp09Box => 0x76703039
|
|
}
|
|
|
|
pub trait Mp4Box: Sized {
|
|
fn box_type(&self) -> BoxType;
|
|
fn box_size(&self) -> u64;
|
|
fn to_json(&self) -> Result<String>;
|
|
fn summary(&self) -> Result<String>;
|
|
}
|
|
|
|
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>;
|
|
}
|
|
|
|
#[derive(Debug, Clone, Copy)]
|
|
pub struct BoxHeader {
|
|
pub name: BoxType,
|
|
pub size: u64,
|
|
}
|
|
|
|
impl BoxHeader {
|
|
pub fn new(name: BoxType, size: u64) -> Self {
|
|
Self { name, size }
|
|
}
|
|
|
|
// TODO: if size is 0, then this box is the last one in the file
|
|
pub fn read<R: Read>(reader: &mut R) -> Result<Self> {
|
|
// Create and read to buf.
|
|
let mut buf = [0u8; 8]; // 8 bytes for box header.
|
|
reader.read(&mut buf)?;
|
|
|
|
// 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();
|
|
let typ = u32::from_be_bytes(t);
|
|
|
|
// Get largesize if size is 1
|
|
if size == 1 {
|
|
reader.read(&mut buf)?;
|
|
let s = buf.try_into().unwrap();
|
|
let largesize = u64::from_be_bytes(s);
|
|
|
|
Ok(BoxHeader {
|
|
name: BoxType::from(typ),
|
|
size: largesize - HEADER_SIZE,
|
|
})
|
|
} else {
|
|
Ok(BoxHeader {
|
|
name: BoxType::from(typ),
|
|
size: size as u64,
|
|
})
|
|
}
|
|
}
|
|
|
|
pub fn write<W: Write>(&self, writer: &mut 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 read_box_header_ext<R: Read>(reader: &mut R) -> Result<(u8, u32)> {
|
|
let version = reader.read_u8()?;
|
|
let flags = reader.read_u24::<BigEndian>()?;
|
|
Ok((version, flags))
|
|
}
|
|
|
|
pub fn write_box_header_ext<W: Write>(w: &mut W, v: u8, f: u32) -> Result<u64> {
|
|
w.write_u8(v)?;
|
|
w.write_u24::<BigEndian>(f)?;
|
|
Ok(4)
|
|
}
|
|
|
|
pub fn box_start<R: Seek>(seeker: &mut R) -> Result<u64> {
|
|
Ok(seeker.seek(SeekFrom::Current(0))? - HEADER_SIZE)
|
|
}
|
|
|
|
pub fn skip_bytes<S: Seek>(seeker: &mut S, size: u64) -> Result<()> {
|
|
seeker.seek(SeekFrom::Current(size as i64))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn skip_bytes_to<S: Seek>(seeker: &mut S, pos: u64) -> Result<()> {
|
|
seeker.seek(SeekFrom::Start(pos))?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn skip_box<S: Seek>(seeker: &mut S, size: u64) -> Result<()> {
|
|
let start = box_start(seeker)?;
|
|
skip_bytes_to(seeker, start + size)?;
|
|
Ok(())
|
|
}
|
|
|
|
pub fn write_zeros<W: Write>(writer: &mut W, size: u64) -> Result<()> {
|
|
for _ in 0..size {
|
|
writer.write_u8(0)?;
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
mod value_u32 {
|
|
use crate::types::FixedPointU16;
|
|
use serde::{self, Serializer};
|
|
|
|
pub fn serialize<S>(fixed: &FixedPointU16, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
serializer.serialize_u16(fixed.value())
|
|
}
|
|
}
|
|
|
|
mod value_i16 {
|
|
use crate::types::FixedPointI8;
|
|
use serde::{self, Serializer};
|
|
|
|
pub fn serialize<S>(fixed: &FixedPointI8, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
serializer.serialize_i8(fixed.value())
|
|
}
|
|
}
|
|
|
|
mod value_u8 {
|
|
use crate::types::FixedPointU8;
|
|
use serde::{self, Serializer};
|
|
|
|
pub fn serialize<S>(fixed: &FixedPointU8, serializer: S) -> Result<S::Ok, S::Error>
|
|
where
|
|
S: Serializer,
|
|
{
|
|
serializer.serialize_u8(fixed.value())
|
|
}
|
|
}
|
|
|
|
#[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[..], b"ftyp");
|
|
let ftyp_fcc2: u32 = ftyp_value.into();
|
|
assert_eq!(ftyp_fcc, ftyp_fcc2);
|
|
}
|
|
}
|