1
0
Fork 0
mirror of https://github.com/alfg/mp4-rust.git synced 2024-06-11 01:19:21 +00:00

Support writing fmp4 for Media Source Extensions

This commit is contained in:
Hans Elias B. Josephsen 2020-12-20 02:18:50 +01:00
parent b63522186e
commit 132becd8bf
50 changed files with 1909 additions and 654 deletions

View file

@ -11,17 +11,24 @@ repository = "https://github.com/alfg/mp4-rust"
keywords = ["mp4", "iso-mp4", "isobmff", "video", "multimedia"]
license = "MIT"
[features]
default = ["use_serde"]
use_serde = ["serde", "serde_json"]
[dependencies]
thiserror = "^1.0"
byteorder = "1"
bytes = "0.5"
num-rational = { version = "0.3", features = ["serde"] }
serde = { version = "1.0", features = ["derive"] }
serde_json = "1.0"
serde = { version = "1.0", features = ["derive"], optional = true }
serde_json = { version = "1.0", optional = true }
num-traits = "0.2"
log = "0.4"
[dev-dependencies]
criterion = "0.3"
[[bench]]
name = "bench_main"
harness = false
harness = false

View file

@ -13,10 +13,14 @@ fn read_mp4(filename: &str) -> u64 {
fn criterion_benchmark(c: &mut Criterion) {
let filename = "tests/samples/minimal.mp4";
c.bench_with_input(BenchmarkId::new("input_example", filename), &filename, |b, &s| {
b.iter(|| read_mp4(s));
});
c.bench_with_input(
BenchmarkId::new("input_example", filename),
&filename,
|b, &s| {
b.iter(|| read_mp4(s));
},
);
}
criterion_group!(benches, criterion_benchmark);
criterion_main!(benches);
criterion_main!(benches);

View file

@ -5,15 +5,9 @@ use std::io::{self, BufReader, BufWriter};
use std::path::Path;
use mp4::{
AacConfig,
AvcConfig,
HevcConfig,
AacConfig, AvcConfig, HevcConfig, MediaConfig, MediaType, Mp4Config, Result, TrackConfig,
TtxtConfig,
MediaConfig,
MediaType,
Mp4Config,
Result,
TrackConfig};
};
fn main() {
let args: Vec<String> = env::args().collect();

View file

@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
use mp4::{Result, Mp4Box};
use mp4::{Mp4Box, Result};
fn main() {
let args: Vec<String> = env::args().collect();
@ -25,7 +25,10 @@ fn dump<P: AsRef<Path>>(filename: &P) -> Result<()> {
// print out boxes
for b in boxes.iter() {
println!("[{}] size={} {}", b.name, b.size, b.summary);
for _ in 0..b.indent {
print!(" ");
}
println!("[{}] size={} {}", b.name, b.size, b.summary);
}
Ok(())
@ -48,82 +51,83 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
let mut boxes = Vec::new();
// ftyp, moov, mvhd
boxes.push(build_box(&mp4.ftyp));
boxes.push(build_box(&mp4.moov));
boxes.push(build_box(&mp4.moov.mvhd));
boxes.push(build_box(&mp4.ftyp, 0));
boxes.push(build_box(&mp4.moov, 0));
boxes.push(build_box(&mp4.moov.mvhd, 1));
if let Some(ref mvex) = &mp4.moov.mvex {
boxes.push(build_box(mvex));
boxes.push(build_box(&mvex.mehd));
boxes.push(build_box(&mvex.trex));
boxes.push(build_box(mvex, 1));
boxes.push(build_box(&mvex.mehd, 2));
boxes.push(build_box(&mvex.trex, 2));
}
// trak.
for track in mp4.tracks().iter() {
boxes.push(build_box(&track.trak));
boxes.push(build_box(&track.trak.tkhd));
boxes.push(build_box(&track.trak, 1));
boxes.push(build_box(&track.trak.tkhd, 2));
if let Some(ref edts) = track.trak.edts {
boxes.push(build_box(edts));
boxes.push(build_box(edts, 2));
if let Some(ref elst) = edts.elst {
boxes.push(build_box(elst));
boxes.push(build_box(elst, 3));
}
}
// trak.mdia
let mdia = &track.trak.mdia;
boxes.push(build_box(mdia));
boxes.push(build_box(&mdia.mdhd));
boxes.push(build_box(&mdia.hdlr));
boxes.push(build_box(&track.trak.mdia.minf));
boxes.push(build_box(mdia, 2));
boxes.push(build_box(&mdia.mdhd, 3));
boxes.push(build_box(&mdia.hdlr, 3));
boxes.push(build_box(&mdia.minf, 3));
// trak.mdia.minf
let minf = &track.trak.mdia.minf;
boxes.push(descr_box("minf", 3));
if let Some(ref vmhd) = &minf.vmhd {
boxes.push(build_box(vmhd));
boxes.push(build_box(vmhd, 4));
}
if let Some(ref smhd) = &minf.smhd {
boxes.push(build_box(smhd));
boxes.push(build_box(smhd, 4));
}
// trak.mdia.minf.stbl
let stbl = &track.trak.mdia.minf.stbl;
boxes.push(build_box(stbl));
boxes.push(build_box(&stbl.stsd));
boxes.push(build_box(stbl, 4));
boxes.push(build_box(&stbl.stsd, 5));
if let Some(ref avc1) = &stbl.stsd.avc1 {
boxes.push(build_box(avc1));
boxes.push(build_box(avc1, 6));
}
if let Some(ref hev1) = &stbl.stsd.hev1 {
boxes.push(build_box(hev1));
boxes.push(build_box(hev1, 6));
}
if let Some(ref mp4a) = &stbl.stsd.mp4a {
boxes.push(build_box(mp4a));
boxes.push(build_box(mp4a, 6));
}
boxes.push(build_box(&stbl.stts));
boxes.push(build_box(&stbl.stts, 5));
if let Some(ref ctts) = &stbl.ctts {
boxes.push(build_box(ctts));
boxes.push(build_box(ctts, 5));
}
if let Some(ref stss) = &stbl.stss {
boxes.push(build_box(stss));
boxes.push(build_box(stss, 5));
}
boxes.push(build_box(&stbl.stsc));
boxes.push(build_box(&stbl.stsz));
boxes.push(build_box(&stbl.stsc, 5));
boxes.push(build_box(&stbl.stsz, 5));
if let Some(ref stco) = &stbl.stco {
boxes.push(build_box(stco));
boxes.push(build_box(stco, 5));
}
if let Some(ref co64) = &stbl.co64 {
boxes.push(build_box(co64));
boxes.push(build_box(co64, 5));
}
}
// If fragmented, add moof boxes.
for moof in mp4.moofs.iter() {
boxes.push(build_box(moof));
boxes.push(build_box(&moof.mfhd));
boxes.push(build_box(moof, 0));
boxes.push(build_box(&moof.mfhd, 1));
for traf in moof.trafs.iter() {
boxes.push(build_box(traf));
boxes.push(build_box(&traf.tfhd));
boxes.push(build_box(traf, 2));
boxes.push(build_box(&traf.tfhd, 3));
if let Some(ref trun) = &traf.trun {
boxes.push(build_box(trun));
boxes.push(build_box(trun, 3));
}
}
}
@ -131,11 +135,20 @@ fn get_boxes(file: File) -> Result<Vec<Box>> {
Ok(boxes)
}
fn build_box<M: Mp4Box + std::fmt::Debug>(ref m: &M) -> Box {
return Box{
fn descr_box(name: &str, indent: u32) -> Box {
return Box {
name: name.to_string(),
size: 0,
summary: "".to_string(),
indent,
};
}
fn build_box<M: Mp4Box + std::fmt::Debug>(ref m: &M, indent: u32) -> Box {
return Box {
name: m.box_type().to_string(),
size: m.box_size(),
summary: m.summary().unwrap(),
indent: 0,
indent: indent,
};
}
}

View file

@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
use mp4::{Mp4Track, Result, TrackType, Error};
use mp4::{Error, Mp4Track, Result, TrackType};
fn main() {
let args: Vec<String> = env::args().collect();
@ -38,7 +38,10 @@ fn info<P: AsRef<Path>>(filename: &P) -> Result<()> {
println!("Movie:");
println!(" version: {}", mp4.moov.mvhd.version);
println!(" creation time: {}", creation_time(mp4.moov.mvhd.creation_time));
println!(
" creation time: {}",
creation_time(mp4.moov.mvhd.creation_time)
);
println!(" duration: {:?}", mp4.duration());
println!(" fragments: {:?}", mp4.is_fragmented());
println!(" timescale: {:?}\n", mp4.timescale());
@ -89,7 +92,6 @@ fn video_info(track: &Mp4Track) -> Result<String> {
fn audio_info(track: &Mp4Track) -> Result<String> {
if let Some(ref mp4a) = track.trak.mdia.minf.stbl.stsd.mp4a {
if mp4a.esds.is_some() {
let profile = match track.audio_profile() {
Ok(val) => val.to_string(),
_ => "-".to_string(),
@ -124,11 +126,7 @@ fn audio_info(track: &Mp4Track) -> Result<String> {
fn subtitle_info(track: &Mp4Track) -> Result<String> {
if track.trak.mdia.minf.stbl.stsd.tx3g.is_some() {
Ok(format!(
"{} ({:?})",
track.media_type()?,
track.box_type()?,
))
Ok(format!("{} ({:?})", track.media_type()?, track.box_type()?,))
} else {
Err(Error::InvalidData("tx3g box not found"))
}
@ -141,4 +139,4 @@ fn creation_time(creation_time: u64) -> u64 {
} else {
creation_time
}
}
}

View file

@ -4,7 +4,7 @@ use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
use mp4::{Result};
use mp4::Result;
fn main() {
let args: Vec<String> = env::args().collect();
@ -35,13 +35,14 @@ fn samples<P: AsRef<Path>>(filename: &P) -> Result<()> {
let sample = mp4.read_sample(track_id, sample_id);
if let Some(ref samp) = sample.unwrap() {
println!("[{}] start_time={} duration={} rendering_offset={} size={} is_sync={}",
sample_id,
samp.start_time,
samp.duration,
samp.rendering_offset,
samp.bytes.len(),
samp.is_sync,
println!(
"[{}] start_time={} duration={} rendering_offset={} size={} is_sync={}",
sample_id,
samp.start_time,
samp.duration,
samp.rendering_offset,
samp.bytes.len(),
samp.is_sync,
);
}
}

192
examples/mp4tree.rs Normal file
View file

@ -0,0 +1,192 @@
use std::env;
use std::fs::File;
use std::io::prelude::*;
use std::io::{self, BufReader};
use std::path::Path;
use mp4::mp4box::RootBox;
use mp4::{Mp4Box, Mp4BoxReader, Result};
fn main() {
let args: Vec<String> = env::args().collect();
if args.len() < 2 {
println!("Usage: mp4fragdump <filename>");
std::process::exit(1);
}
if let Err(err) = dump(&args[1]) {
let _ = writeln!(io::stderr(), "{}", err);
}
}
fn dump<P: AsRef<Path>>(filename: &P) -> Result<()> {
let f = File::open(filename)?;
let boxes = get_boxes(f)?;
// print out boxes
for b in boxes.iter() {
for _ in 0..b.indent {
print!(" ");
}
println!("[{}] size={} {}", b.name, b.size, b.summary);
}
Ok(())
}
#[derive(Debug, Clone, PartialEq, Default)]
pub struct Box {
name: String,
size: u64,
summary: String,
indent: u32,
}
fn get_boxes(file: File) -> Result<Vec<Box>> {
let size = file.metadata()?.len();
let reader = BufReader::new(file);
let mp4 = Mp4BoxReader::read(reader, size)?;
// collect known boxes
let mut boxes = Vec::new();
for root_box in mp4.root_boxes() {
match root_box {
RootBox::FtypBox(ftyp) => {
println!("{}", ftyp.to_json().unwrap());
boxes.push(build_box(ftyp, 0));
}
RootBox::FreeBox(size) => {
boxes.push(Box {
name: "free".into(),
size: *size as u64,
summary: "".into(),
indent: 0,
});
}
RootBox::MdatBox(data) => {
boxes.push(Box {
name: "mdat".into(),
size: 0,
summary: format!("{}", data),
indent: 0,
});
}
RootBox::MoovBox(moov) => {
println!("{}", moov.to_json().unwrap());
boxes.push(build_box(moov, 0));
boxes.push(build_box(&moov.mvhd, 1));
if let Some(ref mvex) = &moov.mvex {
boxes.push(build_box(mvex, 1));
if let Some(mehd) = &mvex.mehd {
boxes.push(build_box(mehd, 2));
}
boxes.push(build_box(&mvex.trex, 2));
}
// trak.
for trak in moov.traks.iter() {
boxes.push(build_box(trak, 1));
boxes.push(build_box(&trak.tkhd, 2));
if let Some(ref edts) = trak.edts {
boxes.push(build_box(edts, 2));
if let Some(ref elst) = edts.elst {
boxes.push(build_box(elst, 3));
}
}
// trak.mdia
let mdia = &trak.mdia;
boxes.push(build_box(mdia, 2));
boxes.push(build_box(&mdia.mdhd, 3));
boxes.push(build_box(&mdia.hdlr, 3));
// trak.mdia.minf
let minf = &trak.mdia.minf;
boxes.push(build_box(minf, 3));
if let Some(ref vmhd) = &minf.vmhd {
boxes.push(build_box(vmhd, 4));
}
if let Some(ref smhd) = &minf.smhd {
boxes.push(build_box(smhd, 4));
}
let dinf = &minf.dinf;
boxes.push(build_box(dinf, 4));
boxes.push(build_box(&dinf.dref, 5));
for entry in dinf.dref.data_entries.iter() {
boxes.push(build_box(entry, 6));
}
// trak.mdia.minf.stbl
let stbl = &trak.mdia.minf.stbl;
boxes.push(build_box(stbl, 4));
boxes.push(build_box(&stbl.stsd, 5));
if let Some(ref avc1) = &stbl.stsd.avc1 {
boxes.push(build_box(avc1, 6));
boxes.push(build_box(&avc1.avcc, 7));
}
if let Some(ref avc2) = &stbl.stsd.avc2 {
boxes.push(build_box(avc2, 6));
boxes.push(build_box(&avc2.avcc, 7));
}
if let Some(ref avc3) = &stbl.stsd.avc3 {
boxes.push(build_box(avc3, 6));
boxes.push(build_box(&avc3.avcc, 7));
}
if let Some(ref hev1) = &stbl.stsd.hev1 {
boxes.push(build_box(hev1, 6));
}
if let Some(ref mp4a) = &stbl.stsd.mp4a {
boxes.push(build_box(mp4a, 6));
}
boxes.push(build_box(&stbl.stts, 5));
if let Some(ref ctts) = &stbl.ctts {
boxes.push(build_box(ctts, 5));
}
if let Some(ref stss) = &stbl.stss {
boxes.push(build_box(stss, 5));
}
boxes.push(build_box(&stbl.stsc, 5));
boxes.push(build_box(&stbl.stsz, 5));
if let Some(ref stco) = &stbl.stco {
boxes.push(build_box(stco, 5));
}
if let Some(ref co64) = &stbl.co64 {
boxes.push(build_box(co64, 5));
}
}
}
RootBox::MoofBox(moof) => {
println!("{}", moof.to_json().unwrap());
boxes.push(build_box(moof, 0));
boxes.push(build_box(&moof.mfhd, 1));
for traf in moof.trafs.iter() {
boxes.push(build_box(traf, 1));
boxes.push(build_box(&traf.tfhd, 2));
if let Some(ref tfdt) = &traf.tfdt {
boxes.push(build_box(tfdt, 2));
}
if let Some(ref trun) = &traf.trun {
println!("{:#?}", trun);
boxes.push(build_box(trun, 2));
}
}
}
RootBox::Unknown(typ, data) => panic!("unknown fragment!! {:?} ({})", typ, data),
}
}
Ok(boxes)
}
fn build_box<M: Mp4Box + std::fmt::Debug>(ref m: &M, indent: u32) -> Box {
return Box {
name: m.box_type().to_string(),
size: m.box_size(),
summary: m.summary().unwrap(),
indent,
};
}

View file

@ -17,11 +17,12 @@ fn main() {
println!("Major Brand: {}", mp4.major_brand());
for track in mp4.tracks().iter() {
println!("Track: #{}({}) {} {}",
println!(
"Track: #{}({}) {} {}",
track.track_id(),
track.language(),
track.track_type().unwrap(),
track.box_type().unwrap(),
);
}
}
}

View file

@ -1,14 +1,14 @@
//! `mp4` is a Rust library to read and write ISO-MP4 files.
//!
//!
//! This package contains MPEG-4 specifications defined in parts:
//! * ISO/IEC 14496-12 - ISO Base Media File Format (QuickTime, MPEG-4, etc)
//! * ISO/IEC 14496-14 - MP4 file format
//! * ISO/IEC 14496-17 - Streaming text format
//!
//!
//! See: [mp4box] for supported MP4 atoms.
//!
//!
//! ### Example
//!
//!
//! ```
//! use std::fs::File;
//! use std::io::{BufReader};
@ -49,9 +49,9 @@
//! Ok(())
//! }
//! ```
//!
//!
//! See [examples] for more examples.
//!
//!
//! # Installation
//!
//! Add the following to your `Cargo.toml` file:
@ -60,14 +60,13 @@
//! [dependencies]
//! mp4 = "0.7.0"
//! ```
//!
//!
//! [mp4box]: https://github.com/alfg/mp4-rust/blob/master/src/mp4box/mod.rs
//! [examples]: https://github.com/alfg/mp4-rust/blob/master/src/examples
#![doc(html_root_url = "https://docs.rs/mp4/*")]
use std::io::{BufReader};
use std::fs::File;
use std::io::BufReader;
mod error;
pub use error::Error;
@ -77,21 +76,24 @@ pub type Result<T> = std::result::Result<T, Error>;
mod types;
pub use types::*;
mod mp4box;
pub use mp4box::{Mp4Box};
pub mod mp4box;
pub use mp4box::Mp4Box;
mod track;
pub use track::{Mp4Track, TrackConfig};
mod reader;
pub use reader::Mp4Reader;
pub use reader::{Mp4BoxReader, Mp4Reader};
mod writer;
pub use writer::{Mp4Config, Mp4Writer};
mod sample_flags;
pub use sample_flags::*;
pub fn read_mp4(f: File) -> Result<Mp4Reader<BufReader<File>>> {
let size = f.metadata()?.len();
let reader = BufReader::new(f);
let mp4 = reader::Mp4Reader::read_header(reader, size)?;
Ok(mp4)
}
}

View file

@ -1,33 +1,65 @@
use std::marker::PhantomData;
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
pub struct Avc1Box {
pub trait AvcVariant {
const TYPE: BoxType;
}
/// SPS and PPS stored out of band from main video stream.
#[derive(Debug, Clone, PartialEq)]
pub enum Avc1Variant {}
impl AvcVariant for Avc1Variant {
const TYPE: BoxType = BoxType::Avc1Box;
}
#[derive(Debug, Clone, PartialEq)]
pub enum Avc2Variant {}
impl AvcVariant for Avc2Variant {
const TYPE: BoxType = BoxType::Avc2Box;
}
/// SPS and PPS stored inline in main video stream,
/// `sequence_parameter_sets` and `picture_parameter_sets` expected
/// empty.
#[derive(Debug, Clone, PartialEq)]
pub enum Avc3Variant {}
impl AvcVariant for Avc3Variant {
const TYPE: BoxType = BoxType::Avc3Box;
}
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct AvcNBox<Variant: AvcVariant> {
#[cfg_attr(feature = "use_serde", serde(skip))]
pub variant: PhantomData<Variant>,
pub data_reference_index: u16,
pub width: u16,
pub height: u16,
#[serde(with = "value_u32")]
pub horizresolution: FixedPointU16,
pub horizresolution: FixedPointU32,
pub vertresolution: FixedPointU32,
#[serde(with = "value_u32")]
pub vertresolution: FixedPointU16,
pub frame_count: u16,
pub depth: u16,
pub avcc: AvcCBox,
}
impl Default for Avc1Box {
impl<Variant: AvcVariant> Default for AvcNBox<Variant> {
fn default() -> Self {
Avc1Box {
AvcNBox {
variant: PhantomData,
data_reference_index: 0,
width: 0,
height: 0,
horizresolution: FixedPointU16::new(0x48),
vertresolution: FixedPointU16::new(0x48),
horizresolution: FixedPointU32::new_whole(0x48),
vertresolution: FixedPointU32::new_whole(0x48),
frame_count: 1,
depth: 0x0018,
avcc: AvcCBox::default(),
@ -35,14 +67,15 @@ impl Default for Avc1Box {
}
}
impl Avc1Box {
impl<Variant: AvcVariant> AvcNBox<Variant> {
pub fn new(config: &AvcConfig) -> Self {
Avc1Box {
AvcNBox {
variant: PhantomData,
data_reference_index: 1,
width: config.width,
height: config.height,
horizresolution: FixedPointU16::new(0x48),
vertresolution: FixedPointU16::new(0x48),
horizresolution: FixedPointU32::new_whole(0x48),
vertresolution: FixedPointU32::new_whole(0x48),
frame_count: 1,
depth: 0x0018,
avcc: AvcCBox::new(&config.seq_param_set, &config.pic_param_set),
@ -50,7 +83,7 @@ impl Avc1Box {
}
pub fn get_type(&self) -> BoxType {
BoxType::Avc1Box
Variant::TYPE
}
pub fn get_size(&self) -> u64 {
@ -58,7 +91,7 @@ impl Avc1Box {
}
}
impl Mp4Box for Avc1Box {
impl<Variant: AvcVariant> Mp4Box for AvcNBox<Variant> {
fn box_type(&self) -> BoxType {
return self.get_type();
}
@ -67,18 +100,21 @@ impl Mp4Box for Avc1Box {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("data_reference_index={} width={} height={} frame_count={}",
self.data_reference_index, self.width, self.height, self.frame_count);
let s = format!(
"data_reference_index={} width={} height={} frame_count={}",
self.data_reference_index, self.width, self.height, self.frame_count
);
Ok(s)
}
}
impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
impl<R: Read + Seek, Variant: AvcVariant> ReadBox<&mut R> for AvcNBox<Variant> {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let start = box_start(reader)?;
@ -91,8 +127,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
reader.read_u32::<BigEndian>()?; // pre-defined
let width = reader.read_u16::<BigEndian>()?;
let height = reader.read_u16::<BigEndian>()?;
let horizresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let vertresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let horizresolution = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
let vertresolution = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
reader.read_u32::<BigEndian>()?; // reserved
let frame_count = reader.read_u16::<BigEndian>()?;
skip_bytes(reader, 32)?; // compressorname
@ -106,7 +142,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
skip_bytes_to(reader, start + size)?;
Ok(Avc1Box {
Ok(AvcNBox {
variant: PhantomData,
data_reference_index,
width,
height,
@ -122,7 +159,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Avc1Box {
}
}
impl<W: Write> WriteBox<&mut W> for Avc1Box {
impl<W: Write, Variant: AvcVariant> WriteBox<&mut W> for AvcNBox<Variant> {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
@ -151,7 +188,8 @@ impl<W: Write> WriteBox<&mut W> for Avc1Box {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct AvcCBox {
pub configuration_version: u8,
pub avc_profile_indication: u8,
@ -192,13 +230,18 @@ impl Mp4Box for AvcCBox {
size
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("avc_profile_indication={}",
self.avc_profile_indication);
let s = format!(
"avc_profile_indication={} num_sps={} num_pps={}",
self.avc_profile_indication,
self.sequence_parameter_sets.len(),
self.picture_parameter_sets.len()
);
Ok(s)
}
}
@ -261,7 +304,8 @@ impl<W: Write> WriteBox<&mut W> for AvcCBox {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct NalUnit {
pub bytes: Vec<u8>,
}

View file

@ -1,15 +1,17 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct Co64Box {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub entries: Vec<u64>,
}
@ -32,6 +34,7 @@ impl Mp4Box for Co64Box {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,15 +1,17 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct CttsBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub entries: Vec<CttsEntry>,
}
@ -23,7 +25,8 @@ impl CttsBox {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct CttsEntry {
pub sample_count: u32,
pub sample_offset: i32,
@ -38,6 +41,8 @@ impl Mp4Box for CttsBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,11 +1,13 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct DinfBox {
dref: DrefBox,
pub dref: DrefBox,
}
impl DinfBox {
@ -27,6 +29,8 @@ impl Mp4Box for DinfBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -84,13 +88,13 @@ impl<W: Write> WriteBox<&mut W> for DinfBox {
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct DrefBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub url: Option<UrlBox>,
pub data_entries: Vec<UrlBox>,
}
impl Default for DrefBox {
@ -98,7 +102,7 @@ impl Default for DrefBox {
DrefBox {
version: 0,
flags: 0,
url: Some(UrlBox::default()),
data_entries: vec![UrlBox::default()],
}
}
}
@ -110,8 +114,8 @@ impl DrefBox {
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 4;
if let Some(ref url) = self.url {
size += url.box_size();
for entry in self.data_entries.iter() {
size += entry.box_size();
}
size
}
@ -126,6 +130,7 @@ impl Mp4Box for DrefBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -145,7 +150,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for DrefBox {
let (version, flags) = read_box_header_ext(reader)?;
let end = start + size;
let mut url = None;
let mut data_entries = vec![];
let entry_count = reader.read_u32::<BigEndian>()?;
for _i in 0..entry_count {
@ -159,7 +164,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for DrefBox {
match name {
BoxType::UrlBox => {
url = Some(UrlBox::read_box(reader, s)?);
data_entries.push(UrlBox::read_box(reader, s)?);
}
_ => {
skip_box(reader, s)?;
@ -174,7 +179,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for DrefBox {
Ok(DrefBox {
version,
flags,
url,
data_entries,
})
}
}
@ -188,15 +193,16 @@ impl<W: Write> WriteBox<&mut W> for DrefBox {
writer.write_u32::<BigEndian>(1)?;
if let Some(ref url) = self.url {
url.write_box(writer)?;
for entry in self.data_entries.iter() {
entry.write_box(writer)?;
}
Ok(size)
}
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct UrlBox {
pub version: u8,
pub flags: u32,
@ -221,7 +227,7 @@ impl UrlBox {
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
if ! self.location.is_empty() {
if !self.location.is_empty() {
size += self.location.bytes().len() as u64 + 1;
}
@ -238,6 +244,7 @@ impl Mp4Box for UrlBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -254,8 +261,12 @@ impl<R: Read + Seek> ReadBox<&mut R> for UrlBox {
let (version, flags) = read_box_header_ext(reader)?;
let location = if size - HEADER_SIZE - HEADER_EXT_SIZE > 0 {
let buf_size = size - HEADER_SIZE - HEADER_EXT_SIZE - 1;
println!("FOOBAR");
let rem_size = size - HEADER_SIZE - HEADER_EXT_SIZE;
let location = if rem_size > 0 {
let buf_size = rem_size - 1;
let mut buf = vec![0u8; buf_size as usize];
reader.read_exact(&mut buf)?;
match String::from_utf8(buf) {
@ -266,7 +277,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for UrlBox {
_ => String::default(),
}
} else {
String::default()
String::new()
};
skip_bytes_to(reader, start + size)?;
@ -286,7 +297,7 @@ impl<W: Write> WriteBox<&mut W> for UrlBox {
write_box_header_ext(writer, self.version, self.flags)?;
if ! self.location.is_empty() {
if !self.location.is_empty() {
writer.write(self.location.as_bytes())?;
writer.write_u8(0)?;
}

View file

@ -1,10 +1,12 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::elst::ElstBox;
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct EdtsBox {
pub elst: Option<ElstBox>,
}
@ -36,6 +38,8 @@ impl Mp4Box for EdtsBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,19 +1,22 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct ElstBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub entries: Vec<ElstEntry>,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct ElstEntry {
pub segment_duration: u64,
pub media_time: u64,
@ -47,6 +50,8 @@ impl Mp4Box for ElstBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,10 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct FtypBox {
pub major_brand: FourCC,
pub minor_version: u32,
@ -30,6 +32,7 @@ impl Mp4Box for FtypBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -39,8 +42,12 @@ impl Mp4Box for FtypBox {
for brand in self.compatible_brands.iter() {
compatible_brands.push(brand.to_string());
}
let s = format!("major_brand={} minor_version={} compatible_brands={}",
self.major_brand, self.minor_version, compatible_brands.join("-"));
let s = format!(
"major_brand={} minor_version={} compatible_brands={}",
self.major_brand,
self.minor_version,
compatible_brands.join("-")
);
Ok(s)
}
}

View file

@ -1,10 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct HdlrBox {
pub version: u8,
pub flags: u32,
@ -31,12 +33,17 @@ impl Mp4Box for HdlrBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("handler_type={} name={}", self.handler_type.to_string(), self.name);
let s = format!(
"handler_type={} name={}",
self.handler_type.to_string(),
self.name
);
Ok(s)
}
}

View file

@ -1,20 +1,20 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct Hev1Box {
pub data_reference_index: u16,
pub width: u16,
pub height: u16,
#[serde(with = "value_u32")]
pub horizresolution: FixedPointU16,
pub horizresolution: FixedPointU32,
pub vertresolution: FixedPointU32,
#[serde(with = "value_u32")]
pub vertresolution: FixedPointU16,
pub frame_count: u16,
pub depth: u16,
pub hvcc: HvcCBox,
@ -26,8 +26,8 @@ impl Default for Hev1Box {
data_reference_index: 0,
width: 0,
height: 0,
horizresolution: FixedPointU16::new(0x48),
vertresolution: FixedPointU16::new(0x48),
horizresolution: FixedPointU32::new_whole(0x48),
vertresolution: FixedPointU32::new_whole(0x48),
frame_count: 1,
depth: 0x0018,
hvcc: HvcCBox::default(),
@ -41,8 +41,8 @@ impl Hev1Box {
data_reference_index: 1,
width: config.width,
height: config.height,
horizresolution: FixedPointU16::new(0x48),
vertresolution: FixedPointU16::new(0x48),
horizresolution: FixedPointU32::new_whole(0x48),
vertresolution: FixedPointU32::new_whole(0x48),
frame_count: 1,
depth: 0x0018,
hvcc: HvcCBox::new(),
@ -67,13 +67,17 @@ impl Mp4Box for Hev1Box {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("data_reference_index={} width={} height={} frame_count={}",
self.data_reference_index, self.width, self.height, self.frame_count);
let s = format!(
"data_reference_index={} width={} height={} frame_count={}",
self.data_reference_index, self.width, self.height, self.frame_count
);
Ok(s)
}
}
@ -91,8 +95,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for Hev1Box {
reader.read_u32::<BigEndian>()?; // pre-defined
let width = reader.read_u16::<BigEndian>()?;
let height = reader.read_u16::<BigEndian>()?;
let horizresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let vertresolution = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let horizresolution = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
let vertresolution = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
reader.read_u32::<BigEndian>()?; // reserved
let frame_count = reader.read_u16::<BigEndian>()?;
skip_bytes(reader, 32)?; // compressorname
@ -151,7 +155,8 @@ impl<W: Write> WriteBox<&mut W> for Hev1Box {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct HvcCBox {
pub configuration_version: u8,
}
@ -174,13 +179,13 @@ impl Mp4Box for HvcCBox {
size
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("configuration_version={}",
self.configuration_version);
let s = format!("configuration_version={}", self.configuration_version);
Ok(s)
}
}

View file

@ -1,11 +1,13 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::char::{decode_utf16, REPLACEMENT_CHARACTER};
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MdhdBox {
pub version: u8,
pub flags: u32,
@ -58,13 +60,17 @@ impl Mp4Box for MdhdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("creation_time={} timescale={} duration={} language={}",
self.creation_time, self.timescale, self.duration, self.language);
let s = format!(
"creation_time={} timescale={} duration={} language={}",
self.creation_time, self.timescale, self.duration, self.language
);
Ok(s)
}
}

View file

@ -1,10 +1,12 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{hdlr::HdlrBox, mdhd::MdhdBox, minf::MinfBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MdiaBox {
pub mdhd: MdhdBox,
pub hdlr: HdlrBox,
@ -30,6 +32,8 @@ impl Mp4Box for MdiaBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,10 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MehdBox {
pub version: u8,
pub flags: u32,
@ -48,6 +50,8 @@ impl Mp4Box for MehdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -104,7 +108,6 @@ mod tests {
use crate::mp4box::BoxHeader;
use std::io::Cursor;
#[test]
fn test_mehd32() {
let src_box = MehdBox {

View file

@ -1,10 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MfhdBox {
pub version: u8,
pub flags: u32,
@ -27,7 +29,7 @@ impl MfhdBox {
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE + 4
HEADER_SIZE + HEADER_EXT_SIZE + 4
}
}
@ -40,6 +42,7 @@ impl Mp4Box for MfhdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,15 +1,17 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{dinf::DinfBox, smhd::SmhdBox, stbl::StblBox, vmhd::VmhdBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MinfBox {
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub vmhd: Option<VmhdBox>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub smhd: Option<SmhdBox>,
pub dinf: DinfBox,
@ -44,6 +46,7 @@ impl Mp4Box for MinfBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,13 +1,13 @@
//! 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
//!
//!
//! http://developer.apple.com/documentation/QuickTime/QTFF/index.html
//! http://www.adobe.com/devnet/video/articles/mp4_movie_atom.html
//! http://mp4ra.org/#/atoms
//!
//! http://mp4ra.org/#/atoms
//!
//! Supported Atoms:
//! ftyp
//! moov
@ -47,34 +47,35 @@
//! trun
//! mdat
//! free
//!
//!
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::borrow::Cow;
use std::convert::TryInto;
use std::fmt::{Display, Formatter};
use std::io::{Read, Seek, SeekFrom, Write};
use crate::*;
pub(crate) mod avc1;
pub(crate) mod avcn;
pub(crate) mod co64;
pub(crate) mod ctts;
pub(crate) mod dinf;
pub(crate) mod edts;
pub(crate) mod elst;
pub(crate) mod ftyp;
pub(crate) mod hev1;
pub(crate) mod hdlr;
pub(crate) mod hev1;
pub(crate) mod mdhd;
pub(crate) mod mdia;
pub(crate) mod minf;
pub(crate) mod moov;
pub(crate) mod mvex;
pub(crate) mod mehd;
pub(crate) mod trex;
pub(crate) mod moof;
pub(crate) mod mp4a;
pub(crate) mod mvhd;
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;
@ -83,17 +84,43 @@ pub(crate) mod stsd;
pub(crate) mod stss;
pub(crate) mod stsz;
pub(crate) mod stts;
pub(crate) mod tkhd;
pub(crate) mod tfdt;
pub(crate) mod tfhd;
pub(crate) mod trak;
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 use avcn::{AvcCBox, AvcNBox};
pub use dinf::{DinfBox, DrefBox, UrlBox};
pub use ftyp::FtypBox;
pub use moov::MoovBox;
pub use hdlr::HdlrBox;
pub use mdhd::MdhdBox;
pub use mdia::MdiaBox;
pub use mehd::MehdBox;
pub use mfhd::MfhdBox;
pub use minf::MinfBox;
pub use moof::MoofBox;
pub use moov::MoovBox;
pub use mvex::MvexBox;
pub use mvhd::MvhdBox;
pub use stbl::StblBox;
pub use stco::StcoBox;
pub use stsc::StscBox;
pub use stsd::StsdBox;
pub use stsz::StszBox;
pub use stts::SttsBox;
pub use tfdt::TfdtBox;
pub use tfhd::TfhdBox;
pub use tkhd::TkhdBox;
pub use traf::TrafBox;
pub use trak::TrakBox;
pub use trex::TrexBox;
pub use trun::TrunBox;
pub use vmhd::VmhdBox;
pub const HEADER_SIZE: u64 = 8;
// const HEADER_LARGE_SIZE: u64 = 16;
@ -140,6 +167,7 @@ boxtype! {
MoofBox => 0x6d6f6f66,
TkhdBox => 0x746b6864,
TfhdBox => 0x74666864,
TfdtBox => 0x74666474,
EdtsBox => 0x65647473,
MdiaBox => 0x6d646961,
ElstBox => 0x656c7374,
@ -165,6 +193,8 @@ boxtype! {
UrlBox => 0x75726C20,
SmhdBox => 0x736d6864,
Avc1Box => 0x61766331,
Avc2Box => 0x61766332,
Avc3Box => 0x61766333,
AvcCBox => 0x61766343,
Hev1Box => 0x68657631,
HvcCBox => 0x68766343,
@ -176,6 +206,7 @@ boxtype! {
pub trait Mp4Box: Sized {
fn box_type(&self) -> BoxType;
fn box_size(&self) -> u64;
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String>;
fn summary(&self) -> Result<String>;
}
@ -188,6 +219,46 @@ pub trait WriteBox<T>: Sized {
fn write_box(&self, _: T) -> Result<u64>;
}
#[derive(Debug, Clone)]
pub enum DataOrOffset<'a> {
Data(Cow<'a, [u8]>),
OffsetSize(usize, usize),
}
impl<'a> Display for DataOrOffset<'a> {
fn fmt(&self, fmt: &mut Formatter) -> std::fmt::Result {
match self {
DataOrOffset::Data(Cow::Borrowed(data)) => write!(fmt, "[borrowed len={}]", data.len()),
DataOrOffset::Data(Cow::Owned(data)) => write!(fmt, "[owned len={}]", data.len()),
DataOrOffset::OffsetSize(offset, size) => {
write!(fmt, "[reader offset={} size={}]", offset, size)
}
}
}
}
#[derive(Debug, Clone)]
pub enum RootBox<'a> {
FtypBox(FtypBox),
FreeBox(usize),
MdatBox(DataOrOffset<'a>),
MoovBox(MoovBox),
MoofBox(MoofBox),
Unknown(BoxType, DataOrOffset<'a>),
}
impl<'a> RootBox<'a> {
pub fn box_type(&self) -> BoxType {
match self {
RootBox::FtypBox(_) => BoxType::FtypBox,
RootBox::FreeBox(_) => BoxType::FreeBox,
RootBox::MdatBox(_) => BoxType::MdatBox,
RootBox::MoovBox(_) => BoxType::MoovBox,
RootBox::MoofBox(_) => BoxType::MoofBox,
RootBox::Unknown(typ, _) => *typ,
}
}
}
#[derive(Debug, Clone, Copy)]
pub struct BoxHeader {
pub name: BoxType,
@ -267,7 +338,12 @@ pub fn skip_bytes<S: Seek>(seeker: &mut S, size: u64) -> Result<()> {
}
pub fn skip_bytes_to<S: Seek>(seeker: &mut S, pos: u64) -> Result<()> {
seeker.seek(SeekFrom::Start(pos))?;
let old = seeker.seek(SeekFrom::Start(pos))?;
println!("check {} {}", pos, old);
if pos != old {
println!("WARNING: box length mismatch");
}
assert!(old <= pos);
Ok(())
}
@ -286,50 +362,46 @@ pub fn write_zeros<W: Write>(writer: &mut W, size: u64) -> Result<()> {
mod value_u32 {
use crate::types::FixedPointU16;
#[cfg(feature = "use_serde")]
use serde::{self, Serializer};
pub fn serialize<S>(
fixed: &FixedPointU16,
serializer: S,
) -> Result<S::Ok, S::Error>
#[cfg(feature = "use_serde")]
pub fn serialize<S>(fixed: &FixedPointU16, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u16(fixed.value())
}
{
serializer.serialize_u16(fixed.value())
}
}
mod value_i16 {
use crate::types::FixedPointI8;
#[cfg(feature = "use_serde")]
use serde::{self, Serializer};
pub fn serialize<S>(
fixed: &FixedPointI8,
serializer: S,
) -> Result<S::Ok, S::Error>
#[cfg(feature = "use_serde")]
pub fn serialize<S>(fixed: &FixedPointI8, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_i8(fixed.value())
}
{
serializer.serialize_i8(fixed.value())
}
}
mod value_u8 {
use crate::types::FixedPointU8;
#[cfg(feature = "use_serde")]
use serde::{self, Serializer};
pub fn serialize<S>(
fixed: &FixedPointU8,
serializer: S,
) -> Result<S::Ok, S::Error>
#[cfg(feature = "use_serde")]
pub fn serialize<S>(fixed: &FixedPointU8, serializer: S) -> Result<S::Ok, S::Error>
where
S: Serializer,
{
serializer.serialize_u8(fixed.value())
}
{
serializer.serialize_u8(fixed.value())
}
}
#[cfg(test)]
mod tests {
use super::*;

View file

@ -1,14 +1,16 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{mfhd::MfhdBox, traf::TrafBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MoofBox {
pub mfhd: MfhdBox,
#[serde(rename = "traf")]
#[cfg_attr(feature = "use_serde", serde(rename = "traf"))]
pub trafs: Vec<TrafBox>,
}
@ -35,6 +37,7 @@ impl Mp4Box for MoofBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -68,7 +71,12 @@ impl<R: Read + Seek> ReadBox<&mut R> for MoofBox {
trafs.push(traf);
}
_ => {
// XXX warn!()
if log::log_enabled!(log::Level::Warn) {
let num: u32 = name.into();
let name_str = FourCC::from(num);
log::warn!("unknown box in `moof`: {}", name_str);
}
skip_box(reader, s)?;
}
}

View file

@ -1,17 +1,19 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{mvhd::MvhdBox, mvex::MvexBox, trak::TrakBox};
use crate::mp4box::{mvex::MvexBox, mvhd::MvhdBox, trak::TrakBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MoovBox {
pub mvhd: MvhdBox,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub mvex: Option<MvexBox>,
#[serde(rename = "trak")]
#[cfg_attr(feature = "use_serde", serde(rename = "trak"))]
pub traks: Vec<TrakBox>,
}
@ -25,6 +27,9 @@ impl MoovBox {
for trak in self.traks.iter() {
size += trak.box_size();
}
if let Some(mvex) = &self.mvex {
size += mvex.box_size();
}
size
}
}
@ -38,6 +43,7 @@ impl Mp4Box for MoovBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -110,6 +116,9 @@ impl<W: Write> WriteBox<&mut W> for MoovBox {
for trak in self.traks.iter() {
trak.write_box(writer)?;
}
if let Some(mvex) = &self.mvex {
mvex.write_box(writer)?;
}
Ok(0)
}
}

View file

@ -1,17 +1,18 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct Mp4aBox {
pub data_reference_index: u16,
pub channelcount: u16,
pub samplesize: u16,
#[serde(with = "value_u32")]
pub samplerate: FixedPointU16,
pub samplerate: FixedPointU32,
pub esds: Option<EsdsBox>,
}
@ -21,7 +22,7 @@ impl Default for Mp4aBox {
data_reference_index: 0,
channelcount: 2,
samplesize: 16,
samplerate: FixedPointU16::new(48000),
samplerate: FixedPointU32::new_whole(48000),
esds: Some(EsdsBox::default()),
}
}
@ -33,7 +34,7 @@ impl Mp4aBox {
data_reference_index: 1,
channelcount: config.chan_conf as u16,
samplesize: 16,
samplerate: FixedPointU16::new(config.freq_index.freq() as u16),
samplerate: FixedPointU32::new_whole(config.freq_index.freq() as u32),
esds: Some(EsdsBox::new(config)),
}
}
@ -60,13 +61,18 @@ impl Mp4Box for Mp4aBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("channel_count={} sample_size={} sample_rate={}",
self.channelcount, self.samplesize, self.samplerate.value());
let s = format!(
"channel_count={} sample_size={} sample_rate={}",
self.channelcount,
self.samplesize,
self.samplerate.value()
);
Ok(s)
}
}
@ -83,7 +89,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for Mp4aBox {
let channelcount = reader.read_u16::<BigEndian>()?;
let samplesize = reader.read_u16::<BigEndian>()?;
reader.read_u32::<BigEndian>()?; // pre-defined, reserved
let samplerate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let samplerate = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
let header = BoxHeader::read(reader)?;
let BoxHeader { name, size: s } = header;
@ -127,7 +133,8 @@ impl<W: Write> WriteBox<&mut W> for Mp4aBox {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct EsdsBox {
pub version: u8,
pub flags: u32,
@ -150,10 +157,14 @@ impl Mp4Box for EsdsBox {
}
fn box_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE
+ 1 + size_of_length(ESDescriptor::desc_size()) as u64 + ESDescriptor::desc_size() as u64
HEADER_SIZE
+ HEADER_EXT_SIZE
+ 1
+ size_of_length(ESDescriptor::desc_size()) as u64
+ ESDescriptor::desc_size() as u64
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -269,7 +280,8 @@ fn write_desc<W: Write>(writer: &mut W, tag: u8, size: u32) -> Result<u64> {
Ok(1 + nbytes as u64)
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct ESDescriptor {
pub es_id: u16,
@ -293,9 +305,12 @@ impl Descriptor for ESDescriptor {
}
fn desc_size() -> u32 {
3
+ 1 + size_of_length(DecoderConfigDescriptor::desc_size()) + DecoderConfigDescriptor::desc_size()
+ 1 + size_of_length(SLConfigDescriptor::desc_size()) + SLConfigDescriptor::desc_size()
3 + 1
+ size_of_length(DecoderConfigDescriptor::desc_size())
+ DecoderConfigDescriptor::desc_size()
+ 1
+ size_of_length(SLConfigDescriptor::desc_size())
+ SLConfigDescriptor::desc_size()
}
}
@ -350,7 +365,8 @@ impl<W: Write> WriteDesc<&mut W> for ESDescriptor {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct DecoderConfigDescriptor {
pub object_type_indication: u8,
pub stream_type: u8,
@ -382,7 +398,9 @@ impl Descriptor for DecoderConfigDescriptor {
}
fn desc_size() -> u32 {
13 + 1 + size_of_length(DecoderSpecificDescriptor::desc_size()) + DecoderSpecificDescriptor::desc_size()
13 + 1
+ size_of_length(DecoderSpecificDescriptor::desc_size())
+ DecoderSpecificDescriptor::desc_size()
}
}
@ -444,7 +462,8 @@ impl<W: Write> WriteDesc<&mut W> for DecoderConfigDescriptor {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct DecoderSpecificDescriptor {
pub profile: u8,
pub freq_index: u8,
@ -499,7 +518,8 @@ impl<W: Write> WriteDesc<&mut W> for DecoderSpecificDescriptor {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct SLConfigDescriptor {}
impl SLConfigDescriptor {

View file

@ -1,22 +1,24 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{mehd::MehdBox, trex::TrexBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MvexBox {
pub mehd: MehdBox,
pub mehd: Option<MehdBox>,
pub trex: TrexBox,
}
impl MvexBox {
pub fn get_type(&self) -> BoxType {
BoxType::MdiaBox
BoxType::MvexBox
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + self.mehd.box_size() + self.trex.box_size()
HEADER_SIZE + self.mehd.as_ref().map(|v| v.box_size()).unwrap_or(0) + self.trex.box_size()
}
}
@ -29,6 +31,7 @@ impl Mp4Box for MvexBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -69,9 +72,6 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
current = reader.seek(SeekFrom::Current(0))?;
}
if mehd.is_none() {
return Err(Error::BoxNotFound(BoxType::MehdBox));
}
if trex.is_none() {
return Err(Error::BoxNotFound(BoxType::TrexBox));
}
@ -79,7 +79,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvexBox {
skip_bytes_to(reader, start + size)?;
Ok(MvexBox {
mehd: mehd.unwrap(),
mehd,
trex: trex.unwrap(),
})
}
@ -90,7 +90,9 @@ impl<W: Write> WriteBox<&mut W> for MvexBox {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
self.mehd.write_box(writer)?;
if let Some(mehd) = &self.mehd {
mehd.write_box(writer)?;
}
self.trex.write_box(writer)?;
Ok(size)

View file

@ -1,10 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct MvhdBox {
pub version: u8,
pub flags: u32,
@ -13,8 +15,10 @@ pub struct MvhdBox {
pub timescale: u32,
pub duration: u64,
#[serde(with = "value_u32")]
pub rate: FixedPointU16,
pub rate: FixedPointU32,
pub volume: FixedPointU16,
pub matrix: Matrix,
pub next_track_id: u32,
}
impl MvhdBox {
@ -44,7 +48,10 @@ impl Default for MvhdBox {
modification_time: 0,
timescale: 1000,
duration: 0,
rate: FixedPointU16::new(1),
rate: FixedPointU32::new_whole(1),
volume: FixedPointU16::new_whole(0),
matrix: Matrix::default(),
next_track_id: 0,
}
}
}
@ -58,13 +65,19 @@ impl Mp4Box for MvhdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("creation_time={} timescale={} duration={} rate={}",
self.creation_time, self.timescale, self.duration, self.rate.value());
let s = format!(
"creation_time={} timescale={} duration={} rate={}",
self.creation_time,
self.timescale,
self.duration,
self.rate.value()
);
Ok(s)
}
}
@ -91,7 +104,18 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
reader.read_u32::<BigEndian>()? as u64,
)
};
let rate = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let rate = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
let volume = FixedPointU16::new_raw(reader.read_u16::<BigEndian>()?);
// Reserved
reader.seek(SeekFrom::Current(10))?;
let matrix = Matrix::read_from(reader)?;
// Pre-defined (?)
reader.seek(SeekFrom::Current(36))?;
let next_track_id = reader.read_u32::<BigEndian>()?;
skip_bytes_to(reader, start + size)?;
@ -103,6 +127,9 @@ impl<R: Read + Seek> ReadBox<&mut R> for MvhdBox {
timescale,
duration,
rate,
volume,
matrix,
next_track_id,
})
}
}
@ -127,9 +154,11 @@ impl<W: Write> WriteBox<&mut W> for MvhdBox {
writer.write_u32::<BigEndian>(self.duration as u32)?;
}
writer.write_u32::<BigEndian>(self.rate.raw_value())?;
// XXX volume, ...
write_zeros(writer, 76)?;
writer.write_u16::<BigEndian>(self.volume.raw_value())?;
writer.write(&[0u8; 10])?;
self.matrix.write_to(writer)?;
writer.write(&[0u8; 24])?;
writer.write_u32::<BigEndian>(self.next_track_id)?;
Ok(size)
}
@ -150,7 +179,10 @@ mod tests {
modification_time: 200,
timescale: 1000,
duration: 634634,
rate: FixedPointU16::new(1),
rate: FixedPointU32::new_whole(1),
volume: FixedPointU16::new_whole(0),
matrix: Matrix::default(),
next_track_id: 0,
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();
@ -174,7 +206,10 @@ mod tests {
modification_time: 200,
timescale: 1000,
duration: 634634,
rate: FixedPointU16::new(1),
rate: FixedPointU32::new_whole(1),
volume: FixedPointU16::new_whole(0),
matrix: Matrix::default(),
next_track_id: 0,
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();

View file

@ -1,16 +1,17 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct SmhdBox {
pub version: u8,
pub flags: u32,
#[serde(with = "value_i16")]
pub balance: FixedPointI8,
pub balance: FixedPointI16,
}
impl SmhdBox {
@ -28,7 +29,7 @@ impl Default for SmhdBox {
SmhdBox {
version: 0,
flags: 0,
balance: FixedPointI8::new_raw(0),
balance: FixedPointI16::new_raw(0),
}
}
}
@ -42,6 +43,7 @@ impl Mp4Box for SmhdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -58,7 +60,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for SmhdBox {
let (version, flags) = read_box_header_ext(reader)?;
let balance = FixedPointI8::new_raw(reader.read_i16::<BigEndian>()?);
let balance = FixedPointI16::new_raw(reader.read_i16::<BigEndian>()?);
skip_bytes_to(reader, start + size)?;
@ -95,7 +97,7 @@ mod tests {
let src_box = SmhdBox {
version: 0,
flags: 0,
balance: FixedPointI8::new_raw(-1),
balance: FixedPointI16::new_raw(-1),
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();

View file

@ -1,35 +1,31 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{
co64::Co64Box,
ctts::CttsBox,
stco::StcoBox,
stsc::StscBox,
stsd::StsdBox,
stss::StssBox,
stsz::StszBox,
stts::SttsBox,
co64::Co64Box, ctts::CttsBox, stco::StcoBox, stsc::StscBox, stsd::StsdBox, stss::StssBox,
stsz::StszBox, stts::SttsBox,
};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct StblBox {
pub stsd: StsdBox,
pub stts: SttsBox,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub ctts: Option<CttsBox>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub stss: Option<StssBox>,
pub stsc: StscBox,
pub stsz: StszBox,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub stco: Option<StcoBox>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub co64: Option<Co64Box>,
}
@ -69,6 +65,7 @@ impl Mp4Box for StblBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,15 +1,17 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct StcoBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub entries: Vec<u32>,
}
@ -32,6 +34,7 @@ impl Mp4Box for StcoBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,15 +1,17 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct StscBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub entries: Vec<StscEntry>,
}
@ -23,7 +25,8 @@ impl StscBox {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct StscEntry {
pub first_chunk: u32,
pub samples_per_chunk: u32,
@ -40,6 +43,7 @@ impl Mp4Box for StscBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,25 +1,34 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::avcn::{Avc1Variant, Avc2Variant, Avc3Variant};
use crate::mp4box::*;
use crate::mp4box::{avc1::Avc1Box, hev1::Hev1Box, mp4a::Mp4aBox, tx3g::Tx3gBox};
use crate::mp4box::{avcn::AvcNBox, hev1::Hev1Box, mp4a::Mp4aBox, tx3g::Tx3gBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct StsdBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing_if = "Option::is_none")]
pub avc1: Option<Avc1Box>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub avc1: Option<AvcNBox<Avc1Variant>>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub avc2: Option<AvcNBox<Avc2Variant>>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub avc3: Option<AvcNBox<Avc3Variant>>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub hev1: Option<Hev1Box>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub mp4a: Option<Mp4aBox>,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub tx3g: Option<Tx3gBox>,
}
@ -32,6 +41,10 @@ impl StsdBox {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE + 4;
if let Some(ref avc1) = self.avc1 {
size += avc1.box_size();
} else if let Some(ref avc2) = self.avc2 {
size += avc2.box_size();
} else if let Some(ref avc3) = self.avc3 {
size += avc3.box_size();
} else if let Some(ref hev1) = self.hev1 {
size += hev1.box_size();
} else if let Some(ref mp4a) = self.mp4a {
@ -52,6 +65,7 @@ impl Mp4Box for StsdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -71,6 +85,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
reader.read_u32::<BigEndian>()?; // XXX entry_count
let mut avc1 = None;
let mut avc2 = None;
let mut avc3 = None;
let mut hev1 = None;
let mut mp4a = None;
let mut tx3g = None;
@ -81,7 +97,13 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
match name {
BoxType::Avc1Box => {
avc1 = Some(Avc1Box::read_box(reader, s)?);
avc1 = Some(AvcNBox::read_box(reader, s)?);
}
BoxType::Avc2Box => {
avc2 = Some(AvcNBox::read_box(reader, s)?);
}
BoxType::Avc3Box => {
avc3 = Some(AvcNBox::read_box(reader, s)?);
}
BoxType::Hev1Box => {
hev1 = Some(Hev1Box::read_box(reader, s)?);
@ -101,6 +123,8 @@ impl<R: Read + Seek> ReadBox<&mut R> for StsdBox {
version,
flags,
avc1,
avc2,
avc3,
hev1,
mp4a,
tx3g,
@ -119,6 +143,10 @@ impl<W: Write> WriteBox<&mut W> for StsdBox {
if let Some(ref avc1) = self.avc1 {
avc1.write_box(writer)?;
} else if let Some(ref avc2) = self.avc2 {
avc2.write_box(writer)?;
} else if let Some(ref avc3) = self.avc3 {
avc3.write_box(writer)?;
} else if let Some(ref hev1) = self.hev1 {
hev1.write_box(writer)?;
} else if let Some(ref mp4a) = self.mp4a {

View file

@ -1,15 +1,17 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct StssBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub entries: Vec<u32>,
}
@ -32,6 +34,7 @@ impl Mp4Box for StssBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,17 +1,19 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct StszBox {
pub version: u8,
pub flags: u32,
pub sample_size: u32,
pub sample_count: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub sample_sizes: Vec<u32>,
}
@ -34,13 +36,18 @@ impl Mp4Box for StszBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("sample_size={} sample_count={} sample_sizes={}",
self.sample_size, self.sample_count, self.sample_sizes.len());
let s = format!(
"sample_size={} sample_count={} sample_sizes={}",
self.sample_size,
self.sample_count,
self.sample_sizes.len()
);
Ok(s)
}
}

View file

@ -1,15 +1,17 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct SttsBox {
pub version: u8,
pub flags: u32,
#[serde(skip_serializing)]
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub entries: Vec<SttsEntry>,
}
@ -23,7 +25,8 @@ impl SttsBox {
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct SttsEntry {
pub sample_count: u32,
pub sample_delta: u32,
@ -38,6 +41,7 @@ impl Mp4Box for SttsBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

96
src/mp4box/tfdt.rs Normal file
View file

@ -0,0 +1,96 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::convert::TryInto;
use std::io::{Read, Seek, Write};
use crate::mp4box::*;
// Track Fragment Decode Time box
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct TfdtBox {
pub version: u8,
pub base_media_decode_time: u64,
}
impl Default for TfdtBox {
fn default() -> Self {
Self {
version: 0,
base_media_decode_time: 0,
}
}
}
impl TfdtBox {
pub fn get_type(&self) -> BoxType {
BoxType::TfdtBox
}
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
match self.version {
0 => size += 4,
1 => size += 8,
_ => panic!(),
}
size
}
}
impl Mp4Box for TfdtBox {
fn box_type(&self) -> BoxType {
self.get_type()
}
fn box_size(&self) -> u64 {
self.get_size()
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("decode_time={}", self.base_media_decode_time);
Ok(s)
}
}
impl<R: Read + Seek> ReadBox<&mut R> for TfdtBox {
fn read_box(reader: &mut R, size: u64) -> Result<Self> {
let _start = box_start(reader)?;
let (version, _flags) = read_box_header_ext(reader)?;
let base_media_decode_time = match version {
0 => reader.read_u32::<BigEndian>()? as u64,
1 => reader.read_u64::<BigEndian>()?,
_ => panic!(),
};
Ok(TfdtBox {
version,
base_media_decode_time,
})
}
}
impl<W: Write> WriteBox<&mut W> for TfdtBox {
fn write_box(&self, writer: &mut W) -> Result<u64> {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
write_box_header_ext(writer, self.version, 0)?;
match self.version {
0 => writer.write_u32::<BigEndian>(self.base_media_decode_time.try_into().unwrap())?,
1 => writer.write_u64::<BigEndian>(self.base_media_decode_time)?,
_ => panic!(),
}
Ok(size)
}
}

View file

@ -1,24 +1,43 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct TfhdBox {
pub version: u8,
pub flags: u32,
pub track_id: u32,
pub base_data_offset: u64,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub base_data_offset: Option<u64>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub sample_description_index: Option<u32>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub default_sample_duration: Option<u32>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub default_sample_size: Option<u32>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub default_sample_flags: Option<u32>,
pub duration_is_empty: bool,
pub default_base_is_moof: bool,
}
impl Default for TfhdBox {
fn default() -> Self {
TfhdBox {
version: 0,
flags: 0,
track_id: 0,
base_data_offset: 0,
base_data_offset: Some(0),
sample_description_index: None,
default_sample_duration: None,
default_sample_size: None,
default_sample_flags: None,
duration_is_empty: false,
default_base_is_moof: false,
}
}
}
@ -29,7 +48,24 @@ impl TfhdBox {
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE + 4 + 8
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
size += 4;
if self.base_data_offset.is_some() {
size += 8;
}
if self.sample_description_index.is_some() {
size += 4;
}
if self.default_sample_duration.is_some() {
size += 4;
}
if self.default_sample_size.is_some() {
size += 4;
}
if self.default_sample_flags.is_some() {
size += 4;
}
size
}
}
@ -42,6 +78,7 @@ impl Mp4Box for TfhdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -57,16 +94,57 @@ impl<R: Read + Seek> ReadBox<&mut R> for TfhdBox {
let start = box_start(reader)?;
let (version, flags) = read_box_header_ext(reader)?;
let base_data_offset_present = (flags & 0x000001) != 0;
let sample_description_index_present = (flags & 0x000002) != 0;
let default_sample_duration_present = (flags & 0x000008) != 0;
let default_sample_size_present = (flags & 0x000010) != 0;
let default_sample_flags_present = (flags & 0x000020) != 0;
let duration_is_empty = (flags & 0x010000) != 0;
let default_base_is_moof = (flags & 0x020000) != 0;
let track_id = reader.read_u32::<BigEndian>()?;
let base_data_offset = reader.read_u64::<BigEndian>()?;
let base_data_offset = if base_data_offset_present {
Some(reader.read_u64::<BigEndian>()?)
} else {
None
};
let sample_description_index = if sample_description_index_present {
Some(reader.read_u32::<BigEndian>()?)
} else {
None
};
let default_sample_duration = if default_sample_duration_present {
Some(reader.read_u32::<BigEndian>()?)
} else {
None
};
let default_sample_size = if default_sample_size_present {
Some(reader.read_u32::<BigEndian>()?)
} else {
None
};
let default_sample_flags = if default_sample_flags_present {
Some(reader.read_u32::<BigEndian>()?)
} else {
None
};
skip_bytes_to(reader, start + size)?;
Ok(TfhdBox {
version,
flags,
track_id,
base_data_offset,
sample_description_index,
default_sample_duration,
default_sample_size,
default_sample_flags,
duration_is_empty,
default_base_is_moof,
})
}
}
@ -76,9 +154,48 @@ impl<W: Write> WriteBox<&mut W> for TfhdBox {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
let mut flags = 0;
if self.base_data_offset.is_some() {
flags |= 0x000001;
}
if self.sample_description_index.is_some() {
flags |= 0x000002;
}
if self.default_sample_duration.is_some() {
flags |= 0x000008;
}
if self.default_sample_size.is_some() {
flags |= 0x000010;
}
if self.default_sample_flags.is_some() {
flags |= 0x000020;
}
if self.duration_is_empty {
flags |= 0x010000;
}
if self.default_base_is_moof {
flags |= 0x020000;
}
write_box_header_ext(writer, self.version, flags)?;
writer.write_u32::<BigEndian>(self.track_id)?;
writer.write_u64::<BigEndian>(self.base_data_offset)?;
if let Some(val) = self.base_data_offset {
writer.write_u64::<BigEndian>(val)?;
}
if let Some(val) = self.sample_description_index {
writer.write_u32::<BigEndian>(val)?;
}
if let Some(val) = self.default_sample_duration {
writer.write_u32::<BigEndian>(val)?;
}
if let Some(val) = self.default_sample_size {
writer.write_u32::<BigEndian>(val)?;
}
if let Some(val) = self.default_sample_flags {
writer.write_u32::<BigEndian>(val)?;
}
Ok(size)
}
@ -94,9 +211,14 @@ mod tests {
fn test_tfhd() {
let src_box = TfhdBox {
version: 0,
flags: 0,
track_id: 1,
base_data_offset: 0,
base_data_offset: Some(0),
sample_description_index: None,
default_sample_duration: None,
default_sample_size: None,
default_sample_flags: None,
duration_is_empty: false,
default_base_is_moof: false,
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();

View file

@ -1,6 +1,7 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
@ -10,7 +11,8 @@ pub enum TrackFlag {
// TrackInPreview = 0x000004,
}
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct TkhdBox {
pub version: u8,
pub flags: u32,
@ -21,15 +23,11 @@ pub struct TkhdBox {
pub layer: u16,
pub alternate_group: u16,
#[serde(with = "value_u8")]
pub volume: FixedPointU8,
pub volume: FixedPointU16,
pub matrix: Matrix,
#[serde(with = "value_u32")]
pub width: FixedPointU16,
#[serde(with = "value_u32")]
pub height: FixedPointU16,
pub width: FixedPointU32,
pub height: FixedPointU32,
}
impl Default for TkhdBox {
@ -43,27 +41,14 @@ impl Default for TkhdBox {
duration: 0,
layer: 0,
alternate_group: 0,
volume: FixedPointU8::new(1),
volume: FixedPointU16::new_whole(1),
matrix: Matrix::default(),
width: FixedPointU16::new(0),
height: FixedPointU16::new(0),
width: FixedPointU32::new_raw(0),
height: FixedPointU32::new_raw(0),
}
}
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
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 TkhdBox {
pub fn get_type(&self) -> BoxType {
BoxType::TkhdBox
@ -82,11 +67,11 @@ impl TkhdBox {
}
pub fn set_width(&mut self, width: u16) {
self.width = FixedPointU16::new(width);
self.width = FixedPointU32::new_whole(width as u32);
}
pub fn set_height(&mut self, height: u16) {
self.height = FixedPointU16::new(height);
self.height = FixedPointU32::new_whole(height as u32);
}
}
@ -99,14 +84,22 @@ impl Mp4Box for TkhdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("creation_time={} track_id={} duration={} layer={} volume={} width={} height={}",
self.creation_time, self.track_id, self.duration, self.layer,
self.volume.value(), self.width.value(), self.height.value());
let s = format!(
"creation_time={} track_id={} duration={} layer={} volume={:?} width={:?} height={:?}",
self.creation_time,
self.track_id,
self.duration,
self.layer,
self.volume,
self.width,
self.height
);
Ok(s)
}
}
@ -138,23 +131,13 @@ impl<R: Read + Seek> ReadBox<&mut R> for TkhdBox {
reader.read_u64::<BigEndian>()?; // reserved
let layer = reader.read_u16::<BigEndian>()?;
let alternate_group = reader.read_u16::<BigEndian>()?;
let volume = FixedPointU8::new_raw(reader.read_u16::<BigEndian>()?);
let volume = FixedPointU16::new_raw(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 matrix = Matrix::read_from(reader)?;
let width = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let height = FixedPointU16::new_raw(reader.read_u32::<BigEndian>()?);
let width = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
let height = FixedPointU32::new_raw(reader.read_u32::<BigEndian>()?);
skip_bytes_to(reader, start + size)?;
@ -204,15 +187,7 @@ impl<W: Write> WriteBox<&mut W> for TkhdBox {
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)?;
self.matrix.write_to(writer)?;
writer.write_u32::<BigEndian>(self.width.raw_value())?;
writer.write_u32::<BigEndian>(self.height.raw_value())?;

View file

@ -1,12 +1,15 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{tfhd::TfhdBox, trun::TrunBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct TrafBox {
pub tfhd: TfhdBox,
pub tfdt: Option<TfdtBox>,
pub trun: Option<TrunBox>,
}
@ -18,6 +21,9 @@ impl TrafBox {
pub fn get_size(&self) -> u64 {
let mut size = HEADER_SIZE;
size += self.tfhd.box_size();
if let Some(ref tfdt) = self.tfdt {
size += tfdt.box_size();
}
if let Some(ref trun) = self.trun {
size += trun.box_size();
}
@ -34,6 +40,7 @@ impl Mp4Box for TrafBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -50,6 +57,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
let mut tfhd = None;
let mut trun = None;
let mut tfdt = None;
let mut current = reader.seek(SeekFrom::Current(0))?;
let end = start + size;
@ -65,8 +73,12 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
BoxType::TrunBox => {
trun = Some(TrunBox::read_box(reader, s)?);
}
BoxType::TfdtBox => {
tfdt = Some(TfdtBox::read_box(reader, s)?);
}
_ => {
// XXX warn!()
println!("WARNING: unexpected box {:?}", name);
skip_box(reader, s)?;
}
}
@ -82,6 +94,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrafBox {
Ok(TrafBox {
tfhd: tfhd.unwrap(),
tfdt,
trun,
})
}
@ -94,6 +107,13 @@ impl<W: Write> WriteBox<&mut W> for TrafBox {
self.tfhd.write_box(writer)?;
if let Some(tfdt) = &self.tfdt {
tfdt.write_box(writer)?;
}
if let Some(trun) = &self.trun {
trun.write_box(writer)?;
}
Ok(size)
}
}

View file

@ -1,14 +1,16 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, SeekFrom, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::mp4box::{edts::EdtsBox, mdia::MdiaBox, tkhd::TkhdBox};
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct TrakBox {
pub tkhd: TkhdBox,
#[serde(skip_serializing_if = "Option::is_none")]
#[cfg_attr(feature = "use_serde", serde(skip_serializing_if = "Option::is_none"))]
pub edts: Option<EdtsBox>,
pub mdia: MdiaBox,
@ -39,6 +41,7 @@ impl Mp4Box for TrakBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}

View file

@ -1,18 +1,21 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::SampleFlags;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct TrexBox {
pub version: u8,
pub flags: u32,
pub track_id: u32,
pub default_sample_description_index: u32,
pub default_sample_duration: u32,
pub default_sample_size: u32,
pub default_sample_flags: u32,
pub default_sample_description_index: u32,
pub default_sample_duration: u32,
pub default_sample_size: u32,
pub default_sample_flags: SampleFlags,
}
impl TrexBox {
@ -34,13 +37,16 @@ impl Mp4Box for TrexBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("track_id={} default_sample_duration={}",
self.track_id, self.default_sample_duration);
let s = format!(
"track_id={} default_sample_duration={}",
self.track_id, self.default_sample_duration
);
Ok(s)
}
}
@ -55,7 +61,7 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrexBox {
let default_sample_description_index = reader.read_u32::<BigEndian>()?;
let default_sample_duration = reader.read_u32::<BigEndian>()?;
let default_sample_size = reader.read_u32::<BigEndian>()?;
let default_sample_flags = reader.read_u32::<BigEndian>()?;
let default_sample_flags = SampleFlags::new(reader.read_u32::<BigEndian>()?);
skip_bytes_to(reader, start + size)?;
@ -82,7 +88,7 @@ impl<W: Write> WriteBox<&mut W> for TrexBox {
writer.write_u32::<BigEndian>(self.default_sample_description_index)?;
writer.write_u32::<BigEndian>(self.default_sample_duration)?;
writer.write_u32::<BigEndian>(self.default_sample_size)?;
writer.write_u32::<BigEndian>(self.default_sample_flags)?;
writer.write_u32::<BigEndian>(self.default_sample_flags.to_u32())?;
Ok(size)
}

View file

@ -1,18 +1,28 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
use crate::SampleFlags;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
#[derive(Debug, Clone, PartialEq, Default)]
pub struct TrunBox {
pub version: u8,
pub flags: u32,
pub sample_count: u32,
pub data_offset: i32,
#[serde(skip_serializing)]
pub sample_sizes: Vec<u32>,
pub data_offset: Option<i32>,
pub first_sample_flags: Option<SampleFlags>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub sample_durations: Option<Vec<u32>>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub sample_sizes: Option<Vec<u32>>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub sample_flags: Option<Vec<SampleFlags>>,
#[cfg_attr(feature = "use_serde", serde(skip_serializing))]
pub sample_composition_time_offsets: Option<Vec<i64>>,
}
impl TrunBox {
@ -21,7 +31,27 @@ impl TrunBox {
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + HEADER_EXT_SIZE + 8 + (4 * self.sample_sizes.len() as u64)
let mut size = HEADER_SIZE + HEADER_EXT_SIZE;
size += 4;
if self.data_offset.is_some() {
size += 4;
}
if self.first_sample_flags.is_some() {
size += 4;
}
if self.sample_durations.is_some() {
size += 4 * self.sample_count as u64;
}
if self.sample_sizes.is_some() {
size += 4 * self.sample_count as u64;
}
if self.sample_flags.is_some() {
size += 4 * self.sample_count as u64;
}
if self.sample_composition_time_offsets.is_some() {
size += 4 * self.sample_count as u64;
}
size
}
}
@ -34,13 +64,13 @@ impl Mp4Box for TrunBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("sample_size={}",
self.sample_count);
let s = format!("sample_size={}", self.sample_count);
Ok(s)
}
}
@ -51,23 +81,77 @@ impl<R: Read + Seek> ReadBox<&mut R> for TrunBox {
let (version, flags) = read_box_header_ext(reader)?;
let sample_count = reader.read_u32::<BigEndian>()?;
let data_offset = reader.read_i32::<BigEndian>()?;
let data_offset_present = (flags & 0x000001) != 0;
let first_sample_flags_present = (flags & 0x000004) != 0;
let sample_duration_present = (flags & 0x000100) != 0;
let sample_size_present = (flags & 0x000200) != 0;
let sample_flags_present = (flags & 0x000400) != 0;
let sample_composition_time_offsets_present = (flags & 0x000800) != 0;
let sample_count = reader.read_u32::<BigEndian>()?;
let data_offset = if data_offset_present {
Some(reader.read_i32::<BigEndian>()?)
} else {
None
};
let first_sample_flags = if first_sample_flags_present {
Some(SampleFlags::new(reader.read_u32::<BigEndian>()?))
} else {
None
};
let mut sample_durations = if sample_duration_present {
Some(Vec::with_capacity(sample_count as usize))
} else {
None
};
let mut sample_sizes = if sample_size_present {
Some(Vec::with_capacity(sample_count as usize))
} else {
None
};
let mut sample_flags = if sample_flags_present {
Some(Vec::with_capacity(sample_count as usize))
} else {
None
};
let mut sample_composition_time_offsets = if sample_composition_time_offsets_present {
Some(Vec::with_capacity(sample_count as usize))
} else {
None
};
let mut sample_sizes = Vec::with_capacity(sample_count as usize);
for _ in 0..sample_count {
let sample_size = reader.read_u32::<BigEndian>()?;
sample_sizes.push(sample_size);
if let Some(vec) = &mut sample_durations {
vec.push(reader.read_u32::<BigEndian>()?);
}
if let Some(vec) = &mut sample_sizes {
vec.push(reader.read_u32::<BigEndian>()?);
}
if let Some(vec) = &mut sample_flags {
vec.push(SampleFlags::new(reader.read_u32::<BigEndian>()?));
}
if let Some(vec) = &mut sample_composition_time_offsets {
if version == 0 {
vec.push(reader.read_u32::<BigEndian>()?.into());
} else {
vec.push(reader.read_i32::<BigEndian>()?.into());
}
}
}
skip_bytes_to(reader, start + size)?;
Ok(TrunBox {
version,
flags,
sample_count,
data_offset,
first_sample_flags,
sample_durations,
sample_sizes,
sample_flags,
sample_composition_time_offsets,
})
}
}
@ -77,13 +161,57 @@ impl<W: Write> WriteBox<&mut W> for TrunBox {
let size = self.box_size();
BoxHeader::new(self.box_type(), size).write(writer)?;
write_box_header_ext(writer, self.version, self.flags)?;
let mut flags = 0;
if self.data_offset.is_some() {
flags |= 0x000001;
}
if self.first_sample_flags.is_some() {
flags |= 0x000004;
}
if let Some(vec) = &self.sample_durations {
assert_eq!(self.sample_count, vec.len() as u32);
flags |= 0x000100;
}
if let Some(vec) = &self.sample_sizes {
assert_eq!(self.sample_count, vec.len() as u32);
flags |= 0x000200;
}
if let Some(vec) = &self.sample_flags {
assert_eq!(self.sample_count, vec.len() as u32);
flags |= 0x000400;
}
if let Some(vec) = &self.sample_composition_time_offsets {
assert_eq!(self.sample_count, vec.len() as u32);
flags |= 0x000800;
}
write_box_header_ext(writer, self.version, flags)?;
writer.write_u32::<BigEndian>(self.sample_count)?;
writer.write_i32::<BigEndian>(self.data_offset)?;
assert_eq!(self.sample_count, self.sample_sizes.len() as u32);
for sample_number in self.sample_sizes.iter() {
writer.write_u32::<BigEndian>(*sample_number)?;
if let Some(val) = self.data_offset {
writer.write_i32::<BigEndian>(val)?;
}
if let Some(val) = self.first_sample_flags {
writer.write_u32::<BigEndian>(val.to_u32())?;
}
for n in 0..(self.sample_count as usize) {
if let Some(vec) = &self.sample_durations {
writer.write_u32::<BigEndian>(vec[n])?;
}
if let Some(vec) = &self.sample_sizes {
writer.write_u32::<BigEndian>(vec[n])?;
}
if let Some(vec) = &self.sample_flags {
writer.write_u32::<BigEndian>(vec[n].to_u32())?;
}
if let Some(vec) = &self.sample_composition_time_offsets {
if self.version == 0 {
writer.write_u32::<BigEndian>(vec[n].try_into().unwrap())?;
} else {
writer.write_i32::<BigEndian>(vec[n].try_into().unwrap())?;
}
}
}
Ok(size)
@ -100,10 +228,13 @@ mod tests {
fn test_trun_same_size() {
let src_box = TrunBox {
version: 0,
flags: 0,
data_offset: 0,
sample_count: 0,
sample_sizes: vec![],
data_offset: Some(0),
first_sample_flags: None,
sample_durations: None,
sample_sizes: Some(vec![]),
sample_flags: None,
sample_composition_time_offsets: None,
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();
@ -122,10 +253,13 @@ mod tests {
fn test_trun_many_sizes() {
let src_box = TrunBox {
version: 0,
flags: 0,
data_offset: 0,
data_offset: Some(0),
first_sample_flags: None,
sample_count: 9,
sample_durations: None,
sample_sizes: vec![1165, 11, 11, 8545, 10126, 10866, 9643, 9351, 7730],
sample_flags: None,
sample_composition_time_offsets: None,
};
let mut buf = Vec::new();
src_box.write_box(&mut buf).unwrap();

View file

@ -1,10 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Serialize)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct Tx3gBox {
pub data_reference_index: u16,
pub display_flags: u32,
@ -15,12 +17,13 @@ pub struct Tx3gBox {
pub style_record: [u8; 12],
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct RgbaColor {
pub red: u8,
pub green: u8,
pub blue: u8,
pub alpha: u8
pub alpha: u8,
}
impl Default for Tx3gBox {
@ -30,7 +33,7 @@ impl Default for Tx3gBox {
display_flags: 0,
horizontal_justification: 1,
vertical_justification: -1,
bg_color_rgba: RgbaColor{
bg_color_rgba: RgbaColor {
red: 0,
green: 0,
blue: 0,
@ -48,7 +51,7 @@ impl Tx3gBox {
}
pub fn get_size(&self) -> u64 {
HEADER_SIZE + 6 + 32
HEADER_SIZE + 6 + 32
}
}
@ -61,6 +64,7 @@ impl Mp4Box for Tx3gBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
@ -165,7 +169,7 @@ mod tests {
display_flags: 0,
horizontal_justification: 1,
vertical_justification: -1,
bg_color_rgba: RgbaColor{
bg_color_rgba: RgbaColor {
red: 0,
green: 0,
blue: 0,

View file

@ -1,10 +1,12 @@
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::io::{Read, Seek, Write};
use serde::{Serialize};
use crate::mp4box::*;
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct VmhdBox {
pub version: u8,
pub flags: u32,
@ -12,12 +14,18 @@ pub struct VmhdBox {
pub op_color: RgbColor,
}
#[derive(Debug, Clone, PartialEq, Default, Serialize)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct RgbColor {
pub red: u16,
pub green: u16,
pub blue: u16,
}
impl From<(u16, u16, u16)> for RgbColor {
fn from((red, green, blue): (u16, u16, u16)) -> Self {
RgbColor { red, green, blue }
}
}
impl VmhdBox {
pub fn get_type(&self) -> BoxType {
@ -38,16 +46,15 @@ impl Mp4Box for VmhdBox {
return self.get_size();
}
#[cfg(feature = "use_serde")]
fn to_json(&self) -> Result<String> {
Ok(serde_json::to_string(&self).unwrap())
}
fn summary(&self) -> Result<String> {
let s = format!("graphics_mode={} op_color={}{}{}",
self.graphics_mode,
self.op_color.red,
self.op_color.green,
self.op_color.blue
let s = format!(
"graphics_mode={} op_color={}{}{}",
self.graphics_mode, self.op_color.red, self.op_color.green, self.op_color.blue
);
Ok(s)
}

View file

@ -4,6 +4,70 @@ use std::time::Duration;
use crate::mp4box::*;
use crate::*;
#[derive(Debug)]
pub struct Mp4BoxReader<R> {
reader: R,
root: Vec<RootBox<'static>>,
}
impl<R: Read + Seek> Mp4BoxReader<R> {
pub fn read(mut reader: R, size: u64) -> Result<Self> {
let start = reader.seek(SeekFrom::Current(0))?;
let mut boxes = Vec::new();
let mut current = start;
while current < size {
// Get box header.
let header = BoxHeader::read(&mut reader)?;
let BoxHeader { name, size: s } = header;
println!("{:?} {:?}", name, s);
let inner_size = (s - 8) as usize;
match name {
BoxType::FtypBox => {
let ftyp = FtypBox::read_box(&mut reader, s)?;
boxes.push(RootBox::FtypBox(ftyp));
}
BoxType::FreeBox => {
skip_box(&mut reader, s)?;
boxes.push(RootBox::FreeBox(inner_size));
}
BoxType::MdatBox => {
skip_box(&mut reader, s)?;
let mdat = DataOrOffset::OffsetSize(current as usize, inner_size);
boxes.push(RootBox::MdatBox(mdat));
}
BoxType::MoovBox => {
let moov = MoovBox::read_box(&mut reader, s)?;
boxes.push(RootBox::MoovBox(moov));
}
BoxType::MoofBox => {
let moof = MoofBox::read_box(&mut reader, s)?;
boxes.push(RootBox::MoofBox(moof));
}
_ => {
skip_box(&mut reader, s)?;
let data = DataOrOffset::OffsetSize(current as usize, inner_size);
boxes.push(RootBox::Unknown(name, data));
}
}
current = reader.seek(SeekFrom::Current(0))?;
}
Ok(Self {
reader,
root: boxes,
})
}
pub fn root_boxes(&self) -> &[RootBox<'static>] {
&self.root
}
}
#[derive(Debug)]
pub struct Mp4Reader<R> {
reader: R,
@ -146,15 +210,15 @@ impl<R: Read + Seek> Mp4Reader<R> {
}
}
pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result<Option<Mp4Sample>> {
if track_id == 0 {
return Err(Error::TrakNotFound(track_id));
}
//pub fn read_sample(&mut self, track_id: u32, sample_id: u32) -> Result<Option<Mp4Sample>> {
// if track_id == 0 {
// return Err(Error::TrakNotFound(track_id));
// }
if let Some(ref track) = self.tracks.get(track_id as usize - 1) {
track.read_sample(&mut self.reader, sample_id)
} else {
Err(Error::TrakNotFound(track_id))
}
}
// if let Some(ref track) = self.tracks.get(track_id as usize - 1) {
// track.read_sample(&mut self.reader, sample_id)
// } else {
// Err(Error::TrakNotFound(track_id))
// }
//}
}

172
src/sample_flags.rs Normal file
View file

@ -0,0 +1,172 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
#[cfg_attr(feature = "use_serde", derive(Serialize))]
#[cfg_attr(feature = "use_serde", serde(rename_all = "snake_case"))]
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u8)]
pub enum IsLeading {
Unknown = 0,
LeadingDep = 1,
NotLeading = 2,
Leading = 3,
}
impl Default for IsLeading {
fn default() -> Self {
IsLeading::Unknown
}
}
impl IsLeading {
pub fn is_unknown(&self) -> bool {
*self == Self::Unknown
}
}
#[cfg_attr(feature = "use_serde", derive(Serialize))]
#[cfg_attr(feature = "use_serde", serde(rename_all = "snake_case"))]
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u8)]
pub enum DependsOn {
Unknown = 0,
Depends = 1,
NotDepends = 2,
}
impl Default for DependsOn {
fn default() -> Self {
DependsOn::Unknown
}
}
impl DependsOn {
pub fn is_unknown(&self) -> bool {
*self == Self::Unknown
}
}
#[cfg_attr(feature = "use_serde", derive(Serialize))]
#[cfg_attr(feature = "use_serde", serde(rename_all = "snake_case"))]
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u8)]
pub enum DependedOn {
Unknown = 0,
Depended = 1,
NotDepended = 2,
}
impl Default for DependedOn {
fn default() -> Self {
DependedOn::Unknown
}
}
impl DependedOn {
pub fn is_unknown(&self) -> bool {
*self == Self::Unknown
}
}
#[cfg_attr(feature = "use_serde", derive(Serialize))]
#[cfg_attr(feature = "use_serde", serde(rename_all = "snake_case"))]
#[derive(Debug, Copy, Clone, PartialEq)]
#[repr(u8)]
pub enum HasRedundancy {
Unknown = 0,
Redundant = 1,
NotRedundant = 2,
}
impl Default for HasRedundancy {
fn default() -> Self {
HasRedundancy::Unknown
}
}
impl HasRedundancy {
pub fn is_unknown(&self) -> bool {
*self == Self::Unknown
}
}
#[cfg_attr(feature = "use_serde", derive(Serialize))]
#[derive(Debug, Copy, Clone, PartialEq, Default)]
pub struct SampleFlags {
#[cfg_attr(
feature = "use_serde",
serde(default, skip_serializing_if = "IsLeading::is_unknown")
)]
pub is_leading: IsLeading,
#[cfg_attr(
feature = "use_serde",
serde(default, skip_serializing_if = "DependsOn::is_unknown")
)]
pub depends_on: DependsOn,
#[cfg_attr(
feature = "use_serde",
serde(default, skip_serializing_if = "DependedOn::is_unknown")
)]
pub depended_on: DependedOn,
#[cfg_attr(
feature = "use_serde",
serde(default, skip_serializing_if = "HasRedundancy::is_unknown")
)]
pub has_redundancy: HasRedundancy,
pub padding_value: u8,
pub is_non_sync: bool,
pub degredation_priority: u16,
}
impl SampleFlags {
pub fn new(flags: u32) -> Self {
let is_leading = match (flags >> 26) & 0b11 {
0 => IsLeading::Unknown,
1 => IsLeading::LeadingDep,
2 => IsLeading::NotLeading,
3 => IsLeading::Leading,
_ => unreachable!(),
};
let depends_on = match (flags >> 24) & 0b11 {
0 => DependsOn::Unknown,
1 => DependsOn::Depends,
2 => DependsOn::NotDepends,
3 => panic!("got reserved"),
_ => unreachable!(),
};
let depended_on = match (flags >> 22) & 0b11 {
0 => DependedOn::Unknown,
1 => DependedOn::Depended,
2 => DependedOn::NotDepended,
3 => panic!("got reserved"),
_ => unreachable!(),
};
let has_redundancy = match (flags >> 20) & 0b11 {
0 => HasRedundancy::Unknown,
1 => HasRedundancy::Redundant,
2 => HasRedundancy::NotRedundant,
3 => panic!("got reserved"),
_ => unreachable!(),
};
let padding_value = ((flags >> 17) & 0b111) as u8;
let is_non_sync = (flags >> 16) != 0;
SampleFlags {
is_leading,
depends_on,
depended_on,
has_redundancy,
padding_value,
is_non_sync,
degredation_priority: flags as u16,
}
}
pub fn to_u32(&self) -> u32 {
let mut flags = 0;
flags |= (self.is_leading as u32) << 26;
flags |= (self.depends_on as u32) << 24;
flags |= (self.depended_on as u32) << 22;
flags |= (self.has_redundancy as u32) << 20;
flags |= (self.padding_value as u32 & 0b111) << 17;
flags |= (self.is_non_sync as u32) << 16;
flags |= self.degredation_priority as u32;
flags
}
}

View file

@ -4,22 +4,12 @@ use std::convert::TryFrom;
use std::io::{Read, Seek, SeekFrom, Write};
use std::time::Duration;
use crate::mp4box::trak::TrakBox;
use crate::mp4box::traf::TrafBox;
use crate::mp4box::trak::TrakBox;
use crate::mp4box::*;
use crate::mp4box::{
avc1::Avc1Box,
hev1::Hev1Box,
ctts::CttsBox,
ctts::CttsEntry,
mp4a::Mp4aBox,
smhd::SmhdBox,
stco::StcoBox,
stsc::StscEntry,
stss::StssBox,
stts::SttsEntry,
tx3g::Tx3gBox,
vmhd::VmhdBox,
avcn::AvcNBox, ctts::CttsBox, ctts::CttsEntry, hev1::Hev1Box, mp4a::Mp4aBox, smhd::SmhdBox,
stco::StcoBox, stsc::StscEntry, stss::StssBox, stts::SttsEntry, tx3g::Tx3gBox, vmhd::VmhdBox,
};
use crate::*;
@ -98,7 +88,11 @@ pub struct Mp4Track {
impl Mp4Track {
pub(crate) fn from(trak: &TrakBox) -> Self {
let trak = trak.clone();
Self { trak, trafs: Vec::new(), default_sample_duration: 0, }
Self {
trak,
trafs: Vec::new(),
default_sample_duration: 0,
}
}
pub fn track_id(&self) -> u32 {
@ -137,21 +131,21 @@ impl Mp4Track {
}
}
pub fn width(&self) -> u16 {
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
avc1.width
} else {
self.trak.tkhd.width.value()
}
}
//pub fn width(&self) -> u16 {
// if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
// avc1.width
// } else {
// self.trak.tkhd.width.value()
// }
//}
pub fn height(&self) -> u16 {
if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
avc1.height
} else {
self.trak.tkhd.height.value()
}
}
//pub fn height(&self) -> u16 {
// if let Some(ref avc1) = self.trak.mdia.minf.stbl.stsd.avc1 {
// avc1.height
// } else {
// self.trak.tkhd.height.value()
// }
//}
pub fn frame_rate(&self) -> f64 {
let dur_msec = self.duration().as_millis() as u64;
@ -200,24 +194,24 @@ impl Mp4Track {
)
}
pub fn bitrate(&self) -> u32 {
if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
if let Some(ref esds) = mp4a.esds {
esds.es_desc.dec_config.avg_bitrate
} else {
0
}
// mp4a.esds.es_desc.dec_config.avg_bitrate
} else {
let dur_sec = self.duration().as_secs();
if dur_sec > 0 {
let bitrate = self.total_sample_size() * 8 / dur_sec;
bitrate as u32
} else {
0
}
}
}
//pub fn bitrate(&self) -> u32 {
// if let Some(ref mp4a) = self.trak.mdia.minf.stbl.stsd.mp4a {
// if let Some(ref esds) = mp4a.esds {
// esds.es_desc.dec_config.avg_bitrate
// } else {
// 0
// }
// // mp4a.esds.es_desc.dec_config.avg_bitrate
// } else {
// let dur_sec = self.duration().as_secs();
// if dur_sec > 0 {
// let bitrate = self.total_sample_size() * 8 / dur_sec;
// bitrate as u32
// } else {
// 0
// }
// }
//}
pub fn sample_count(&self) -> u32 {
if self.trafs.len() > 0 {
@ -344,85 +338,89 @@ impl Mp4Track {
));
}
fn sample_size(&self, sample_id: u32) -> Result<u32> {
if self.trafs.len() > 0 {
let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
let traf_idx = (sample_id - 1) / sample_sizes_count;
if let Some(trun) = &self.trafs[traf_idx as usize].trun {
if let Some(size) = trun.sample_sizes.get((sample_id - (sample_sizes_count * traf_idx)) as usize - 1) {
Ok(*size)
} else {
return Err(Error::EntryInTrunNotFound(
self.track_id(),
BoxType::TrunBox,
sample_id,
));
}
} else {
return Err(Error::BoxInTrafNotFound(
self.track_id(),
BoxType::TrafBox,
));
}
} else {
let stsz = &self.trak.mdia.minf.stbl.stsz;
if stsz.sample_size > 0 {
return Ok(stsz.sample_size);
}
if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
Ok(*size)
} else {
return Err(Error::EntryInStblNotFound(
self.track_id(),
BoxType::StszBox,
sample_id,
));
}
}
}
//fn sample_size(&self, sample_id: u32) -> Result<u32> {
// if self.trafs.len() > 0 {
// let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
// let traf_idx = (sample_id - 1) / sample_sizes_count;
// if let Some(trun) = &self.trafs[traf_idx as usize].trun {
// if let Some(size) = trun
// .sample_sizes
// .get((sample_id - (sample_sizes_count * traf_idx)) as usize - 1)
// {
// Ok(*size)
// } else {
// return Err(Error::EntryInTrunNotFound(
// self.track_id(),
// BoxType::TrunBox,
// sample_id,
// ));
// }
// } else {
// return Err(Error::BoxInTrafNotFound(self.track_id(), BoxType::TrafBox));
// }
// } else {
// let stsz = &self.trak.mdia.minf.stbl.stsz;
// if stsz.sample_size > 0 {
// return Ok(stsz.sample_size);
// }
// if let Some(size) = stsz.sample_sizes.get(sample_id as usize - 1) {
// Ok(*size)
// } else {
// return Err(Error::EntryInStblNotFound(
// self.track_id(),
// BoxType::StszBox,
// sample_id,
// ));
// }
// }
//}
fn total_sample_size(&self) -> u64 {
let stsz = &self.trak.mdia.minf.stbl.stsz;
if stsz.sample_size > 0 {
stsz.sample_size as u64 * self.sample_count() as u64
} else {
let mut total_size = 0;
for size in stsz.sample_sizes.iter() {
total_size += *size as u64;
}
total_size
}
}
//fn total_sample_size(&self) -> u64 {
// let stsz = &self.trak.mdia.minf.stbl.stsz;
// if stsz.sample_size > 0 {
// stsz.sample_size as u64 * self.sample_count() as u64
// } else {
// let mut total_size = 0;
// for size in stsz.sample_sizes.iter() {
// total_size += *size as u64;
// }
// total_size
// }
//}
fn sample_offset(&self, sample_id: u32) -> Result<u64> {
if self.trafs.len() > 0 {
let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
let traf_idx = (sample_id - 1) / sample_sizes_count;
Ok(self.trafs[(sample_id - (sample_sizes_count * traf_idx)) as usize].tfhd.base_data_offset as u64)
} else {
let stsc_index = self.stsc_index(sample_id);
//fn sample_offset(&self, sample_id: u32) -> Result<u64> {
// if self.trafs.len() > 0 {
// let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
// let traf_idx = (sample_id - 1) / sample_sizes_count;
// Ok(
// self.trafs[(sample_id - (sample_sizes_count * traf_idx)) as usize]
// .tfhd
// .base_data_offset as u64,
// )
// } else {
// let stsc_index = self.stsc_index(sample_id);
let stsc = &self.trak.mdia.minf.stbl.stsc;
let stsc_entry = stsc.entries.get(stsc_index).unwrap();
// let stsc = &self.trak.mdia.minf.stbl.stsc;
// let stsc_entry = stsc.entries.get(stsc_index).unwrap();
let first_chunk = stsc_entry.first_chunk;
let first_sample = stsc_entry.first_sample;
let samples_per_chunk = stsc_entry.samples_per_chunk;
// let first_chunk = stsc_entry.first_chunk;
// let first_sample = stsc_entry.first_sample;
// let samples_per_chunk = stsc_entry.samples_per_chunk;
let chunk_id = first_chunk + (sample_id - first_sample) / samples_per_chunk;
// let chunk_id = first_chunk + (sample_id - first_sample) / samples_per_chunk;
let chunk_offset = self.chunk_offset(chunk_id)?;
// let chunk_offset = self.chunk_offset(chunk_id)?;
let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk;
// let first_sample_in_chunk = sample_id - (sample_id - first_sample) % samples_per_chunk;
let mut sample_offset = 0;
for i in first_sample_in_chunk..sample_id {
sample_offset += self.sample_size(i)?;
}
// let mut sample_offset = 0;
// for i in first_sample_in_chunk..sample_id {
// sample_offset += self.sample_size(i)?;
// }
Ok(chunk_offset + sample_offset as u64)
}
}
// Ok(chunk_offset + sample_offset as u64)
// }
//}
fn sample_time(&self, sample_id: u32) -> Result<(u64, u32)> {
let stts = &self.trak.mdia.minf.stbl.stts;
@ -431,8 +429,8 @@ impl Mp4Track {
let mut elapsed = 0;
if self.trafs.len() > 0 {
let start_time = ((sample_id - 1) * self.default_sample_duration) as u64;
return Ok((start_time, self.default_sample_duration))
let start_time = ((sample_id - 1) * self.default_sample_duration) as u64;
return Ok((start_time, self.default_sample_duration));
} else {
for entry in stts.entries.iter() {
if sample_id <= sample_count + entry.sample_count - 1 {
@ -466,7 +464,7 @@ impl Mp4Track {
fn is_sync_sample(&self, sample_id: u32) -> bool {
if self.trafs.len() > 0 {
let sample_sizes_count = self.sample_count() / self.trafs.len() as u32;
return sample_id == 1 || sample_id % sample_sizes_count == 0
return sample_id == 1 || sample_id % sample_sizes_count == 0;
}
if let Some(ref stss) = self.trak.mdia.minf.stbl.stss {
@ -479,34 +477,34 @@ impl Mp4Track {
}
}
pub(crate) fn read_sample<R: Read + Seek>(
&self,
reader: &mut R,
sample_id: u32,
) -> Result<Option<Mp4Sample>> {
let sample_offset = match self.sample_offset(sample_id) {
Ok(offset) => offset,
Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
Err(err) => return Err(err),
};
let sample_size = self.sample_size(sample_id).unwrap();
//pub(crate) fn read_sample<R: Read + Seek>(
// &self,
// reader: &mut R,
// sample_id: u32,
//) -> Result<Option<Mp4Sample>> {
// let sample_offset = match self.sample_offset(sample_id) {
// Ok(offset) => offset,
// Err(Error::EntryInStblNotFound(_, _, _)) => return Ok(None),
// Err(err) => return Err(err),
// };
// let sample_size = self.sample_size(sample_id).unwrap();
let mut buffer = vec![0x0u8; sample_size as usize];
reader.seek(SeekFrom::Start(sample_offset))?;
reader.read_exact(&mut buffer)?;
// let mut buffer = vec![0x0u8; sample_size as usize];
// reader.seek(SeekFrom::Start(sample_offset))?;
// reader.read_exact(&mut buffer)?;
let (start_time, duration) = self.sample_time(sample_id).unwrap(); // XXX
let rendering_offset = self.sample_rendering_offset(sample_id);
let is_sync = self.is_sync_sample(sample_id);
// let (start_time, duration) = self.sample_time(sample_id).unwrap(); // XXX
// let rendering_offset = self.sample_rendering_offset(sample_id);
// let is_sync = self.is_sync_sample(sample_id);
Ok(Some(Mp4Sample {
start_time,
duration,
rendering_offset,
is_sync,
bytes: Bytes::from(buffer),
}))
}
// Ok(Some(Mp4Sample {
// start_time,
// duration,
// rendering_offset,
// is_sync,
// bytes: Bytes::from(buffer),
// }))
//}
}
// TODO creation_time, modification_time
@ -542,8 +540,20 @@ impl Mp4TrackWriter {
let vmhd = VmhdBox::default();
trak.mdia.minf.vmhd = Some(vmhd);
let avc1 = Avc1Box::new(avc_config);
trak.mdia.minf.stbl.stsd.avc1 = Some(avc1);
match avc_config.variant {
AvcVariant::Avc1 => {
let avc1 = AvcNBox::new(avc_config);
trak.mdia.minf.stbl.stsd.avc1 = Some(avc1);
}
AvcVariant::Avc2 => {
let avc2 = AvcNBox::new(avc_config);
trak.mdia.minf.stbl.stsd.avc2 = Some(avc2);
}
AvcVariant::Avc3 => {
let avc3 = AvcNBox::new(avc_config);
trak.mdia.minf.stbl.stsd.avc3 = Some(avc3);
}
}
}
MediaConfig::HevcConfig(ref hevc_config) => {
trak.tkhd.set_width(hevc_config.width);

View file

@ -1,73 +1,190 @@
#[cfg(feature = "use_serde")]
use serde::Serialize;
use std::convert::TryFrom;
use std::fmt;
use serde::{Serialize};
use std::marker::PhantomData;
use crate::mp4box::*;
use crate::*;
pub use bytes::Bytes;
pub use num_rational::Ratio;
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub struct FixedPointU8(Ratio<u16>);
pub trait FixedPointKind {
const POINT: usize;
type Carrier;
}
impl FixedPointU8 {
pub fn new(val: u8) -> Self {
Self(Ratio::new_raw(val as u16 * 0x100, 0x100))
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FPi4_4 {}
impl FixedPointKind for FPi4_4 {
const POINT: usize = 4;
type Carrier = i8;
}
pub fn new_raw(val: u16) -> Self {
Self(Ratio::new_raw(val, 0x100))
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FPu4_4 {}
impl FixedPointKind for FPu4_4 {
const POINT: usize = 4;
type Carrier = u8;
}
pub fn value(&self) -> u8 {
self.0.to_integer() as u8
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FPi8_8 {}
impl FixedPointKind for FPi8_8 {
const POINT: usize = 8;
type Carrier = i16;
}
pub fn raw_value(&self) -> u16 {
*self.0.numer()
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FPu8_8 {}
impl FixedPointKind for FPu8_8 {
const POINT: usize = 8;
type Carrier = u16;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FPi16_16 {}
impl FixedPointKind for FPi16_16 {
const POINT: usize = 16;
type Carrier = i32;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FPu16_16 {}
impl FixedPointKind for FPu16_16 {
const POINT: usize = 16;
type Carrier = u32;
}
#[derive(Debug, Copy, Clone, PartialEq, Eq)]
pub enum FPi2_30 {}
impl FixedPointKind for FPi2_30 {
const POINT: usize = 30;
type Carrier = i32;
}
#[derive(Copy, Clone, PartialEq, Eq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
#[cfg_attr(feature = "use_serde", serde(transparent))]
pub struct FixedPoint<T: FixedPointKind>(T::Carrier, PhantomData<T>);
impl<T: FixedPointKind> fmt::Debug for FixedPoint<T>
where
T::Carrier: Into<f64> + Copy,
{
fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
let raw_float: f64 = self.0.into();
let float = raw_float / (1 << T::POINT) as f64;
write!(
f,
"{}fp{}.{}",
float,
std::any::type_name::<T::Carrier>(),
T::POINT
)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub struct FixedPointI8(Ratio<i16>);
impl FixedPointI8 {
pub fn new(val: i8) -> Self {
Self(Ratio::new_raw(val as i16 * 0x100, 0x100))
impl<T: FixedPointKind> FixedPoint<T> {
pub fn new_whole(val: T::Carrier) -> Self
where
T::Carrier: std::ops::Shl<Output = T::Carrier> + TryFrom<usize>,
{
let point: T::Carrier = TryFrom::try_from(T::POINT).map_err(|_| ()).unwrap();
Self(val << point, PhantomData)
}
pub fn new_raw(val: i16) -> Self {
Self(Ratio::new_raw(val, 0x100))
}
pub fn value(&self) -> i8 {
self.0.to_integer() as i8
}
pub fn raw_value(&self) -> i16 {
*self.0.numer()
pub fn new_raw(val: T::Carrier) -> Self {
Self(val, PhantomData)
}
}
#[derive(Debug, Clone, Copy, PartialEq, Serialize)]
pub struct FixedPointU16(Ratio<u32>);
impl FixedPointU16 {
pub fn new(val: u16) -> Self {
Self(Ratio::new_raw(val as u32 * 0x10000, 0x10000))
impl<T: FixedPointKind> FixedPoint<T>
where
T::Carrier: Copy,
{
pub fn value(&self) -> T::Carrier {
self.0
}
pub fn new_raw(val: u32) -> Self {
Self(Ratio::new_raw(val, 0x10000))
pub fn raw_value(&self) -> T::Carrier {
self.0
}
}
pub fn value(&self) -> u16 {
self.0.to_integer() as u16
impl<T: FixedPointKind> Default for FixedPoint<T>
where
T::Carrier: Default,
{
fn default() -> Self {
Self(Default::default(), PhantomData)
}
}
pub fn raw_value(&self) -> u32 {
*self.0.numer()
pub type FixedPointU8 = FixedPoint<FPu4_4>;
pub type FixedPointI8 = FixedPoint<FPi4_4>;
pub type FixedPointU16 = FixedPoint<FPu8_8>;
pub type FixedPointI16 = FixedPoint<FPi8_8>;
pub type FixedPointU32 = FixedPoint<FPu16_16>;
pub type FixedPointI32 = FixedPoint<FPi16_16>;
pub type FixedPointI2_30 = FixedPoint<FPi2_30>;
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct Matrix {
pub a: FixedPointI32,
pub b: FixedPointI32,
pub u: FixedPointI2_30,
pub c: FixedPointI32,
pub d: FixedPointI32,
pub v: FixedPointI2_30,
pub x: FixedPointI32,
pub y: FixedPointI32,
pub w: FixedPointI2_30,
}
impl Default for Matrix {
fn default() -> Self {
Matrix {
a: FixedPointI32::new_whole(1),
b: FixedPointI32::new_whole(0),
u: FixedPointI2_30::new_whole(0),
c: FixedPointI32::new_whole(0),
d: FixedPointI32::new_whole(1),
v: FixedPointI2_30::new_whole(0),
x: FixedPointI32::new_whole(0),
y: FixedPointI32::new_whole(0),
w: FixedPointI2_30::new_whole(1),
}
}
}
use byteorder::{BigEndian, ReadBytesExt, WriteBytesExt};
use std::io::{Read, Write};
impl Matrix {
pub fn read_from<R: Read>(reader: &mut R) -> std::io::Result<Self> {
Ok(Matrix {
a: FixedPointI32::new_raw(reader.read_i32::<BigEndian>()?),
b: FixedPointI32::new_raw(reader.read_i32::<BigEndian>()?),
u: FixedPointI2_30::new_raw(reader.read_i32::<BigEndian>()?),
c: FixedPointI32::new_raw(reader.read_i32::<BigEndian>()?),
d: FixedPointI32::new_raw(reader.read_i32::<BigEndian>()?),
v: FixedPointI2_30::new_raw(reader.read_i32::<BigEndian>()?),
x: FixedPointI32::new_raw(reader.read_i32::<BigEndian>()?),
y: FixedPointI32::new_raw(reader.read_i32::<BigEndian>()?),
w: FixedPointI2_30::new_raw(reader.read_i32::<BigEndian>()?),
})
}
pub fn write_to<W: Write>(&self, writer: &mut W) -> std::io::Result<()> {
writer.write_i32::<BigEndian>(self.a.raw_value())?;
writer.write_i32::<BigEndian>(self.b.raw_value())?;
writer.write_i32::<BigEndian>(self.u.raw_value())?;
writer.write_i32::<BigEndian>(self.c.raw_value())?;
writer.write_i32::<BigEndian>(self.d.raw_value())?;
writer.write_i32::<BigEndian>(self.v.raw_value())?;
writer.write_i32::<BigEndian>(self.x.raw_value())?;
writer.write_i32::<BigEndian>(self.y.raw_value())?;
writer.write_i32::<BigEndian>(self.w.raw_value())?;
Ok(())
}
}
@ -85,7 +202,8 @@ impl fmt::Display for BoxType {
}
}
#[derive(Default, PartialEq, Clone, Serialize)]
#[derive(Default, PartialEq, Clone)]
#[cfg_attr(feature = "use_serde", derive(Serialize))]
pub struct FourCC {
pub value: String,
}
@ -461,8 +579,16 @@ impl fmt::Display for ChannelConfig {
}
}
#[derive(Debug, PartialEq, Clone, Default)]
#[derive(Debug, PartialEq, Clone)]
pub enum AvcVariant {
Avc1,
Avc2,
Avc3,
}
#[derive(Debug, PartialEq, Clone)]
pub struct AvcConfig {
pub variant: AvcVariant,
pub width: u16,
pub height: u16,
pub seq_param_set: Vec<u8>,