Skip to content
Open
Show file tree
Hide file tree
Changes from 10 commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,7 @@ serde = { version = "1", features = ["derive"], optional = true }
tokio = ["dep:tokio"]
bytes = ["dep:bytes"]
serde = ["dep:serde", "bytes/serde"]
fault-tolerant = []

[dev-dependencies]
anyhow = "1"
Expand Down
14 changes: 14 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,20 @@ Using this library does require some additional knowledge of the format otherwis

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

## Fault-Tolerant Parsing
Enable the `fault-tolerant` feature to support parsing files with unexpected boxes.

```toml
[dependencies]
mp4-atom = { version = "<VERSION>", features = ["fault-tolerant"] }
```

When this feature is enabled, if a container box (such as `moov`, `trak`, `mdia`, etc.) encounters an unexpected child box during decoding, instead of returning an error, the unknown box is collected in an `unexpected: Vec<Any>` field.

When the feature is **disabled** (default), encountering an unexpected box will return an `Error::UnexpectedBox` error, ensuring strict compliance with the expected structure.

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
17 changes: 16 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();)*

#[cfg(feature = "fault-tolerant")]
let mut unexpected = Vec::new();

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

Ok(Self {
$([<$required:lower>]: [<$required:lower>].ok_or(Error::MissingBox($required::KIND))? ,)*
$([<$optional:lower>],)*
$([<$multiple:lower>],)*
#[cfg(feature = "fault-tolerant")]
unexpected
})
}

Expand Down
13 changes: 13 additions & 0 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,19 @@
//! 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].
//!
//! Enable the `fault-tolerant` feature to support parsing files with unexpected boxes.
//!
//! ```toml
//! [dependencies]
//! mp4-atom = { version = "<VERSION>", features = ["fault-tolerant"] }
//! ```
//!
//! When this feature is enabled, if a container box (such as `moov`, `trak`, `mdia`, etc.) encounters an unexpected child box during decoding, instead of returning an error, the unknown box is collected in an `unexpected: Vec<Any>` field.
//!
//! When the feature is **disabled** (default), encountering an unexpected box will return an `Error::UnexpectedBox` error, ensuring strict compliance with the expected structure.
//!
//! 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
2 changes: 2 additions & 0 deletions src/meta/iprp.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use crate::*;
pub struct Iprp {
pub ipco: Ipco,
pub ipma: Vec<Ipma>,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Iprp {
Expand Down
4 changes: 4 additions & 0 deletions src/meta/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -158,6 +158,8 @@ mod tests {
location: "".into(),
}],
},
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
});
expected.push(Iloc {
item_locations: vec![ItemLocation {
Expand Down Expand Up @@ -229,6 +231,8 @@ mod tests {
},
],
}],
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
});
expected.push(Iref {
references: vec![Reference {
Expand Down
4 changes: 3 additions & 1 deletion src/moof/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ 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>,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Moof {
Expand Down
4 changes: 3 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,8 @@ pub struct Traf {
pub saio: Vec<Saio>,
pub meta: Option<Meta>,
pub udta: Option<Udta>,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Traf {
Expand Down
30 changes: 24 additions & 6 deletions src/moov/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ pub struct Moov {
pub mvex: Option<Mvex>,
pub trak: Vec<Trak>,
pub udta: Option<Udta>,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Moov {
Expand Down Expand Up @@ -137,7 +139,9 @@ mod test {
default_sample_description_index: 1,
default_sample_duration: 3000,
..Default::default()
}]
}],
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
}),
trak: vec![Trak {
tkhd: Tkhd {
Expand All @@ -156,7 +160,9 @@ mod test {
media_rate: 1,
..Default::default()
}]
})
}),
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
}),
meta: None,
mdia: Mdia {
Expand Down Expand Up @@ -184,7 +190,9 @@ mod test {
dinf: Dinf {
dref: Dref {
urls: vec![Url::default()]
}
},
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
},
stbl: Stbl {
stsd: Stsd {
Expand Down Expand Up @@ -219,17 +227,27 @@ mod test {
colr: None,
pasp: None,
taic: None,
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
}
.into()],
},
stco: Some(Stco::default()),
..Default::default()
}
}
},
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
},
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
},
udta: None
udta: None,
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
}],
udta: None,
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
}
)
}
Expand Down
4 changes: 3 additions & 1 deletion src/moov/mvex/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,13 @@ 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>,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Mvex {
Expand Down
4 changes: 3 additions & 1 deletion src/moov/trak/edts/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@ 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>,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Edts {
Expand Down
4 changes: 3 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,12 @@ 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,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Dinf {
Expand Down
4 changes: 3 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,15 @@ 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,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Minf {
Expand Down
4 changes: 3 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,8 @@ pub struct Stbl {
pub subs: Vec<Subs>,
pub saiz: Vec<Saiz>,
pub saio: Vec<Saio>,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Stbl {
Expand Down
23 changes: 20 additions & 3 deletions src/moov/trak/mdia/minf/stbl/stsd/ac3.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,13 @@ use crate::*;

// See ETSI TS 102 366 V1.4.1 (2017-09) for details of AC-3 and EAC-3

#[derive(Debug, Clone, PartialEq, Eq)]
#[derive(Debug, Clone, PartialEq)]
#[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))]
pub struct Ac3 {
pub audio: Audio,
pub dac3: Ac3SpecificBox,
#[cfg(feature = "fault-tolerant")]
pub unexpected: Vec<Any>,
}

impl Atom for Ac3 {
Expand All @@ -16,17 +18,28 @@ impl Atom for Ac3 {
let audio = Audio::decode(buf)?;

let mut dac3 = None;
#[cfg(feature = "fault-tolerant")]
let mut unexpected = Vec::new();

while let Some(atom) = Any::decode_maybe(buf)? {
match atom {
Any::Ac3SpecificBox(atom) => dac3 = atom.into(),
_ => tracing::warn!("unknown atom: {:?}", atom),
_ => {
tracing::warn!("unknown atom: {:?}", atom);
#[cfg(feature = "fault-tolerant")]
unexpected.push(atom);

#[cfg(not(feature = "fault-tolerant"))]
return Err(Error::UnexpectedBox(atom.kind()));
}
}
}

Ok(Self {
audio,
dac3: dac3.ok_or(Error::MissingBox(Ac3SpecificBox::KIND))?,
#[cfg(feature = "fault-tolerant")]
unexpected,
})
}

Expand Down Expand Up @@ -120,7 +133,9 @@ mod tests {
acmod: 2,
lfeon: false,
bit_rate_code: 10
}
},
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
}
);
}
Expand All @@ -142,6 +157,8 @@ mod tests {
lfeon: false,
bit_rate_code: 10,
},
#[cfg(feature = "fault-tolerant")]
unexpected: vec![],
};

let mut buf = Vec::new();
Expand Down
Loading