Skip to content
Open
Show file tree
Hide file tree
Changes from 6 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 = "0.9", 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 = "0.9", 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
20 changes: 17 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,25 @@ 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);
}
}
}

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

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

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