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:
parent
b63522186e
commit
132becd8bf
15
Cargo.toml
15
Cargo.toml
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
};
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
192
examples/mp4tree.rs
Normal 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,
|
||||
};
|
||||
}
|
|
@ -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(),
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
28
src/lib.rs
28
src/lib.rs
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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>,
|
||||
}
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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::*;
|
||||
|
|
|
@ -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)?;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
96
src/mp4box/tfdt.rs
Normal 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)
|
||||
}
|
||||
}
|
|
@ -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();
|
||||
|
|
|
@ -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())?;
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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())
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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
172
src/sample_flags.rs
Normal 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
|
||||
}
|
||||
}
|
302
src/track.rs
302
src/track.rs
|
@ -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);
|
||||
|
|
222
src/types.rs
222
src/types.rs
|
@ -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>,
|
||||
|
|
Loading…
Reference in a new issue