Skip to content
Open
7 changes: 7 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,13 @@ Using this library does require some additional knowledge of the format otherwis

See the [documentation](https://docs.rs/mp4-atom).

## Fault-Tolerant Parsing
This library implements fault-tolerant parsing for container boxes. When decoding a container box (such as `moov`, `trak`, `mdia`, etc.), if an unexpected child box is encountered, instead of failing the entire parsing operation, the unexpected box is collected in an `unexpected` field as an `Any` atom.

All container box structures include an `unexpected: Vec<Any>` field that collects these unrecognized boxes.

Note that when encoding, the `unexpected` boxes are **not** written back - only the explicitly defined fields are encoded.

## Examples
### Decoding/encoding a byte buffer
```rust
Expand Down
9 changes: 8 additions & 1 deletion src/atom.rs
Original file line number Diff line number Diff line change
Expand Up @@ -175,6 +175,9 @@ macro_rules! nested {
$( let mut [<$optional:lower>] = None;)*
$( let mut [<$multiple:lower>] = Vec::new();)*

// aggregate unexpected boxes, if any
let mut unexpected = Vec::new();

while let Some(atom) = Any::decode_maybe(buf)? {
match atom {
$(Any::$required(atom) => {
Expand All @@ -195,14 +198,18 @@ macro_rules! nested {
Any::Unknown(kind, _) => {
tracing::warn!("unknown box: {:?}", kind);
},
_ => return Err(Error::UnexpectedBox(atom.kind())),
_ => {
tracing::warn!("unexpected box: {:?}", atom.kind());
unexpected.push(atom);
}
}
}

Ok(Self {
$([<$required:lower>]: [<$required:lower>].ok_or(Error::MissingBox($required::KIND))? ,)*
$([<$optional:lower>],)*
$([<$multiple:lower>],)*
unexpected
})
}

Expand Down
7 changes: 7 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,13 @@
//! The simplest way to decode with this library is with [Any::decode], returning any supported atom in a giant enum.
//! For encoding you will call encode on the atom directly, ex: [Moov::encode].
//!
//! ## Fault-Tolerant Parsing
//! This library implements fault-tolerant parsing for container boxes. When decoding a container box (such as `moov`, `trak`, `mdia`, etc.), if an unexpected child box is encountered, instead of failing the entire parsing operation, the unexpected box is collected in an `unexpected` field as an `Any` atom.
//!
//! All container box structures include an `unexpected: Vec<Any>` field that collects these unrecognized boxes.
//!
//! Note that when encoding, the `unexpected` boxes are **not** written back - only the explicitly defined fields are encoded.
//!
//! ## Traits
//! This library gates functionality behind quite a few traits:
//!
Expand Down
1 change: 1 addition & 0 deletions src/meta/iprp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ use crate::*;
pub struct Iprp {
pub ipco: Ipco,
pub ipma: Vec<Ipma>,
pub unexpected: Vec<Any>,
}

impl Atom for Iprp {
Expand Down
2 changes: 2 additions & 0 deletions src/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,7 @@ mod tests {
location: "".into(),
}],
},
unexpected: vec![],
});
expected.push(Iloc {
item_locations: vec![ItemLocation {
Expand Down Expand Up @@ -229,6 +230,7 @@ mod tests {
},
],
}],
unexpected: vec![],
});
expected.push(Iref {
references: vec![Reference {
Expand Down
3 changes: 2 additions & 1 deletion src/moof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ pub use traf::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Moof {
pub mfhd: Mfhd,
pub traf: Vec<Traf>,
pub unexpected: Vec<Any>,
}

impl Atom for Moof {
Expand Down
3 changes: 2 additions & 1 deletion src/moof/traf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ pub use trun::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Traf {
pub tfhd: Tfhd,
Expand All @@ -21,6 +21,7 @@ pub struct Traf {
pub saio: Vec<Saio>,
pub meta: Option<Meta>,
pub udta: Option<Udta>,
pub unexpected: Vec<Any>,
}

impl Atom for Traf {
Expand Down
20 changes: 14 additions & 6 deletions src/moov/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@ pub struct Moov {
pub mvex: Option<Mvex>,
pub trak: Vec<Trak>,
pub udta: Option<Udta>,
pub unexpected: Vec<Any>,
}

impl Atom for Moov {
Expand Down Expand Up @@ -137,7 +138,8 @@ mod test {
default_sample_description_index: 1,
default_sample_duration: 3000,
..Default::default()
}]
}],
unexpected: vec![],
}),
trak: vec![Trak {
tkhd: Tkhd {
Expand All @@ -156,7 +158,8 @@ mod test {
media_rate: 1,
..Default::default()
}]
})
}),
unexpected: vec![],
}),
meta: None,
mdia: Mdia {
Expand Down Expand Up @@ -184,7 +187,8 @@ mod test {
dinf: Dinf {
dref: Dref {
urls: vec![Url::default()]
}
},
unexpected: vec![],
},
stbl: Stbl {
stsd: Stsd {
Expand Down Expand Up @@ -224,12 +228,16 @@ mod test {
},
stco: Some(Stco::default()),
..Default::default()
}
}
},
unexpected: vec![],
},
unexpected: vec![],
},
udta: None
udta: None,
unexpected: vec![],
}],
udta: None,
unexpected: vec![],
}
)
}
Expand Down
3 changes: 2 additions & 1 deletion src/moov/mvex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,12 @@ pub use trex::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Mvex {
pub mehd: Option<Mehd>,
pub trex: Vec<Trex>,
pub unexpected: Vec<Any>,
}

impl Atom for Mvex {
Expand Down
3 changes: 2 additions & 1 deletion src/moov/trak/edts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ pub use elst::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Edts {
pub elst: Option<Elst>,
pub unexpected: Vec<Any>,
}

impl Atom for Edts {
Expand Down
3 changes: 2 additions & 1 deletion src/moov/trak/mdia/minf/dinf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,11 @@ pub use dref::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Dinf {
pub dref: Dref,
pub unexpected: Vec<Any>,
}

impl Atom for Dinf {
Expand Down
3 changes: 2 additions & 1 deletion src/moov/trak/mdia/minf/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,14 @@ pub use vmhd::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Minf {
pub vmhd: Option<Vmhd>,
pub smhd: Option<Smhd>,
pub dinf: Dinf,
pub stbl: Stbl,
pub unexpected: Vec<Any>,
}

impl Atom for Minf {
Expand Down
3 changes: 2 additions & 1 deletion src/moov/trak/mdia/minf/stbl/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -26,7 +26,7 @@ pub use subs::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Stbl {
pub stsd: Stsd,
Expand All @@ -42,6 +42,7 @@ pub struct Stbl {
pub subs: Vec<Subs>,
pub saiz: Vec<Saiz>,
pub saio: Vec<Saio>,
pub unexpected: Vec<Any>,
}

impl Atom for Stbl {
Expand Down
3 changes: 2 additions & 1 deletion src/moov/trak/mdia/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,12 +8,13 @@ pub use minf::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Mdia {
pub mdhd: Mdhd,
pub hdlr: Hdlr,
pub minf: Minf,
pub unexpected: Vec<Any>,
}

impl Atom for Mdia {
Expand Down
3 changes: 2 additions & 1 deletion src/moov/trak/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,14 +8,15 @@ pub use tkhd::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Trak {
pub tkhd: Tkhd,
pub edts: Option<Edts>,
pub meta: Option<Meta>, // TODO is this suppose to be here?
pub mdia: Mdia,
pub udta: Option<Udta>,
pub unexpected: Vec<Any>,
}

impl Atom for Trak {
Expand Down
5 changes: 4 additions & 1 deletion src/moov/udta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -4,11 +4,12 @@ pub use skip::*;

use crate::*;

#[derive(Debug, Clone, PartialEq, Eq, Default)]
#[derive(Debug, Clone, PartialEq, Default)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Udta {
pub meta: Option<Meta>,
pub skip: Option<Skip>,
pub unexpected: Vec<Any>,
}

impl Atom for Udta {
Expand All @@ -30,6 +31,7 @@ mod tests {
let expected = Udta {
meta: None,
skip: None,
unexpected: vec![],
};

let mut buf = Vec::new();
Expand All @@ -51,6 +53,7 @@ mod tests {
items: vec![],
}),
skip: None,
unexpected: vec![],
};

let mut buf = Vec::new();
Expand Down
10 changes: 10 additions & 0 deletions src/test/av1.rs
Original file line number Diff line number Diff line change
Expand Up @@ -111,6 +111,7 @@ fn av1() {
assert_eq!(
moov,
Moov {
unexpected: vec![],
mvhd: Mvhd {
creation_time: 0,
modification_time: 0,
Expand All @@ -133,6 +134,7 @@ fn av1() {
},
meta: None,
mvex: Some(Mvex {
unexpected: vec![],
mehd: None,
trex: vec![Trex {
track_id: 1,
Expand All @@ -143,6 +145,7 @@ fn av1() {
}]
}),
trak: vec![Trak {
unexpected: vec![],
tkhd: Tkhd {
creation_time: 0,
modification_time: 0,
Expand All @@ -169,6 +172,7 @@ fn av1() {
edts: None,
meta: None,
mdia: Mdia {
unexpected: vec![],
mdhd: Mdhd {
creation_time: 0,
modification_time: 0,
Expand All @@ -181,6 +185,7 @@ fn av1() {
name: "[email protected]_master".into()
},
minf: Minf {
unexpected: vec![],
vmhd: Some(Vmhd {
graphics_mode: 0,
op_color: RgbColor {
Expand All @@ -191,13 +196,15 @@ fn av1() {
}),
smhd: None,
dinf: Dinf {
unexpected: vec![],
dref: Dref {
urls: vec![Url {
location: "".into()
}]
}
},
stbl: Stbl {
unexpected: vec![],
stsd: Stsd {
codecs: vec![Av01 {
visual: Visual {
Expand Down Expand Up @@ -254,6 +261,7 @@ fn av1() {
udta: None,
}],
udta: Some(Udta {
unexpected: vec![],
meta: Some(Meta {
hdlr: Hdlr {
handler: FourCC::new(b"mdir"),
Expand All @@ -270,8 +278,10 @@ fn av1() {
assert_eq!(
moof,
Moof {
unexpected: vec![],
mfhd: Mfhd { sequence_number: 1 },
traf: vec![Traf {
unexpected: vec![],
tfhd: Tfhd {
track_id: 1,
base_data_offset: None,
Expand Down
Loading