diff --git a/Cargo.toml b/Cargo.toml index af01891..71d2a04 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -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" diff --git a/README.md b/README.md index 3c1f636..4d71313 100644 --- a/README.md +++ b/README.md @@ -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 = "", 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` 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 diff --git a/src/atom.rs b/src/atom.rs index 01fb508..9cc8524 100644 --- a/src/atom.rs +++ b/src/atom.rs @@ -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) => { @@ -195,7 +198,17 @@ 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())); + } + } } } @@ -203,6 +216,8 @@ macro_rules! nested { $([<$required:lower>]: [<$required:lower>].ok_or(Error::MissingBox($required::KIND))? ,)* $([<$optional:lower>],)* $([<$multiple:lower>],)* + #[cfg(feature = "fault-tolerant")] + unexpected }) } diff --git a/src/lib.rs b/src/lib.rs index 4b403ca..0a6f3e5 100644 --- a/src/lib.rs +++ b/src/lib.rs @@ -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 = "", 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` 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: //! diff --git a/src/meta/iprp.rs b/src/meta/iprp.rs index 160338c..0650f66 100644 --- a/src/meta/iprp.rs +++ b/src/meta/iprp.rs @@ -8,6 +8,8 @@ use crate::*; pub struct Iprp { pub ipco: Ipco, pub ipma: Vec, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Iprp { diff --git a/src/meta/mod.rs b/src/meta/mod.rs index ede274a..b10fc6d 100644 --- a/src/meta/mod.rs +++ b/src/meta/mod.rs @@ -158,6 +158,8 @@ mod tests { location: "".into(), }], }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }); expected.push(Iloc { item_locations: vec![ItemLocation { @@ -229,6 +231,8 @@ mod tests { }, ], }], + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }); expected.push(Iref { references: vec![Reference { diff --git a/src/moof/mod.rs b/src/moof/mod.rs index 3e127a6..84ba924 100644 --- a/src/moof/mod.rs +++ b/src/moof/mod.rs @@ -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, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Moof { diff --git a/src/moof/traf/mod.rs b/src/moof/traf/mod.rs index 8c520b8..2304102 100644 --- a/src/moof/traf/mod.rs +++ b/src/moof/traf/mod.rs @@ -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, @@ -21,6 +21,8 @@ pub struct Traf { pub saio: Vec, pub meta: Option, pub udta: Option, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Traf { diff --git a/src/moov/mod.rs b/src/moov/mod.rs index 0e43a5a..5f0021a 100644 --- a/src/moov/mod.rs +++ b/src/moov/mod.rs @@ -18,6 +18,8 @@ pub struct Moov { pub mvex: Option, pub trak: Vec, pub udta: Option, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Moov { @@ -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 { @@ -156,7 +160,9 @@ mod test { media_rate: 1, ..Default::default() }] - }) + }), + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }), meta: None, mdia: Mdia { @@ -184,7 +190,9 @@ mod test { dinf: Dinf { dref: Dref { urls: vec![Url::default()] - } + }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }, stbl: Stbl { stsd: Stsd { @@ -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![], } ) } diff --git a/src/moov/mvex/mod.rs b/src/moov/mvex/mod.rs index 1ae169c..43031c9 100644 --- a/src/moov/mvex/mod.rs +++ b/src/moov/mvex/mod.rs @@ -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, pub trex: Vec, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Mvex { diff --git a/src/moov/trak/edts/mod.rs b/src/moov/trak/edts/mod.rs index ebf5c2f..c0966c2 100644 --- a/src/moov/trak/edts/mod.rs +++ b/src/moov/trak/edts/mod.rs @@ -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, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Edts { diff --git a/src/moov/trak/mdia/minf/dinf/mod.rs b/src/moov/trak/mdia/minf/dinf/mod.rs index 5e65768..7b77ea2 100644 --- a/src/moov/trak/mdia/minf/dinf/mod.rs +++ b/src/moov/trak/mdia/minf/dinf/mod.rs @@ -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, } impl Atom for Dinf { diff --git a/src/moov/trak/mdia/minf/mod.rs b/src/moov/trak/mdia/minf/mod.rs index c57ef11..a85d433 100644 --- a/src/moov/trak/mdia/minf/mod.rs +++ b/src/moov/trak/mdia/minf/mod.rs @@ -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, pub smhd: Option, pub dinf: Dinf, pub stbl: Stbl, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Minf { diff --git a/src/moov/trak/mdia/minf/stbl/mod.rs b/src/moov/trak/mdia/minf/stbl/mod.rs index 0f9ab03..01fc384 100644 --- a/src/moov/trak/mdia/minf/stbl/mod.rs +++ b/src/moov/trak/mdia/minf/stbl/mod.rs @@ -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, @@ -42,6 +42,8 @@ pub struct Stbl { pub subs: Vec, pub saiz: Vec, pub saio: Vec, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Stbl { diff --git a/src/moov/trak/mdia/minf/stbl/stsd/ac3.rs b/src/moov/trak/mdia/minf/stbl/stsd/ac3.rs index e764864..83f29b8 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/ac3.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/ac3.rs @@ -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, } impl Atom for Ac3 { @@ -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, }) } @@ -120,7 +133,9 @@ mod tests { acmod: 2, lfeon: false, bit_rate_code: 10 - } + }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } ); } @@ -142,6 +157,8 @@ mod tests { lfeon: false, bit_rate_code: 10, }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; let mut buf = Vec::new(); diff --git a/src/moov/trak/mdia/minf/stbl/stsd/av01.rs b/src/moov/trak/mdia/minf/stbl/stsd/av01.rs index efc30d2..741bef8 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/av01.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/av01.rs @@ -3,7 +3,7 @@ use crate::{Any, Atom, Buf, BufMut, DecodeMaybe, Error, FourCC, Result}; use super::{Btrt, Colr, Pasp, Taic, Visual}; -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Av01 { pub visual: Visual, @@ -12,6 +12,8 @@ pub struct Av01 { pub colr: Option, pub pasp: Option, pub taic: Option, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Av01 { @@ -25,6 +27,9 @@ impl Atom for Av01 { let mut colr = None; let mut pasp = None; let mut taic = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Av1c(atom) => av1c = atom.into(), @@ -32,7 +37,14 @@ impl Atom for Av01 { Any::Colr(atom) => colr = atom.into(), Any::Pasp(atom) => pasp = atom.into(), Any::Taic(atom) => taic = 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())); + } } } @@ -43,6 +55,8 @@ impl Atom for Av01 { colr, pasp, taic, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } diff --git a/src/moov/trak/mdia/minf/stbl/stsd/eac3.rs b/src/moov/trak/mdia/minf/stbl/stsd/eac3.rs index a2316f6..e629d8d 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/eac3.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/eac3.rs @@ -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 Eac3 { pub audio: Audio, pub dec3: Ec3SpecificBox, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Eac3 { @@ -16,17 +18,28 @@ impl Atom for Eac3 { let audio = Audio::decode(buf)?; let mut dec3 = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Ec3SpecificBox(atom) => dec3 = 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, dec3: dec3.ok_or(Error::MissingBox(Ec3SpecificBox::KIND))?, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } @@ -180,7 +193,9 @@ mod tests { num_dep_sub: 0, chan_loc: None }] - } + }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } ); } @@ -207,6 +222,8 @@ mod tests { chan_loc: None, }], }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; let mut buf = Vec::new(); @@ -238,6 +255,8 @@ mod tests { chan_loc: Some(0x1FF), }], }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; // Encode @@ -287,6 +306,8 @@ mod tests { }, ], }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; // Encode @@ -342,7 +363,9 @@ mod tests { num_dep_sub: 0, chan_loc: None }] - } + }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } ); } diff --git a/src/moov/trak/mdia/minf/stbl/stsd/flac.rs b/src/moov/trak/mdia/minf/stbl/stsd/flac.rs index 082a40a..52d95d0 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/flac.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/flac.rs @@ -1,10 +1,12 @@ use crate::*; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Flac { pub audio: Audio, pub dfla: Dfla, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Flac { @@ -14,16 +16,28 @@ impl Atom for Flac { let audio = Audio::decode(buf)?; let mut dfla = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Dfla(atom) => dfla = 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, dfla: dfla.ok_or(Error::MissingBox(Dfla::KIND))?, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } diff --git a/src/moov/trak/mdia/minf/stbl/stsd/h264/avc1.rs b/src/moov/trak/mdia/minf/stbl/stsd/h264/avc1.rs index ad0417e..c4732b1 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/h264/avc1.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/h264/avc1.rs @@ -1,6 +1,6 @@ use crate::*; -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Avc1 { pub visual: Visual, @@ -9,6 +9,9 @@ pub struct Avc1 { pub colr: Option, pub pasp: Option, pub taic: Option, + + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Avc1 { @@ -22,6 +25,10 @@ impl Atom for Avc1 { let mut colr = None; let mut pasp = None; let mut taic = None; + + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Avcc(atom) => avcc = atom.into(), @@ -29,7 +36,14 @@ impl Atom for Avc1 { Any::Colr(atom) => colr = atom.into(), Any::Pasp(atom) => pasp = atom.into(), Any::Taic(atom) => taic = 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())); + } } } @@ -40,6 +54,8 @@ impl Atom for Avc1 { colr, pasp, taic, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } @@ -96,6 +112,8 @@ mod tests { colr: None, pasp: None, taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: Vec::new(), }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); @@ -147,6 +165,8 @@ mod tests { clock_drift_rate: i32::MAX, clock_type: ClockType::CanSync, }), + #[cfg(feature = "fault-tolerant")] + unexpected: Vec::new(), }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); diff --git a/src/moov/trak/mdia/minf/stbl/stsd/hevc/hev1.rs b/src/moov/trak/mdia/minf/stbl/stsd/hevc/hev1.rs index bd726de..b7d021c 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/hevc/hev1.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/hevc/hev1.rs @@ -1,6 +1,6 @@ use crate::*; -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Hev1 { pub visual: Visual, @@ -9,6 +9,9 @@ pub struct Hev1 { pub colr: Option, pub pasp: Option, pub taic: Option, + + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Hev1 { @@ -22,6 +25,10 @@ impl Atom for Hev1 { let mut colr = None; let mut pasp = None; let mut taic = None; + + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Hvcc(atom) => hvcc = atom.into(), @@ -29,7 +36,14 @@ impl Atom for Hev1 { Any::Colr(atom) => colr = atom.into(), Any::Pasp(atom) => pasp = atom.into(), Any::Taic(atom) => taic = 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())); + } } } @@ -40,6 +54,8 @@ impl Atom for Hev1 { colr, pasp, taic, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } @@ -84,10 +100,7 @@ mod tests { configuration_version: 1, ..Default::default() }, - btrt: None, - colr: None, - pasp: None, - taic: None, + ..Default::default() }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); diff --git a/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvc1.rs b/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvc1.rs index 6c7b2c9..a93fba4 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvc1.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvc1.rs @@ -1,6 +1,6 @@ use crate::*; -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Hvc1 { pub visual: Visual, @@ -10,6 +10,9 @@ pub struct Hvc1 { pub colr: Option, pub pasp: Option, pub taic: Option, + + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Hvc1 { @@ -23,6 +26,10 @@ impl Atom for Hvc1 { let mut colr = None; let mut pasp = None; let mut taic = None; + + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Hvcc(atom) => hvcc = atom.into(), @@ -30,7 +37,13 @@ impl Atom for Hvc1 { Any::Colr(atom) => colr = atom.into(), Any::Pasp(atom) => pasp = atom.into(), Any::Taic(atom) => taic = 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())); + } } } @@ -41,6 +54,9 @@ impl Atom for Hvc1 { colr, pasp, taic, + + #[cfg(feature = "fault-tolerant")] + unexpected, }) } diff --git a/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvcc.rs b/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvcc.rs index 9fb8f9d..e45eb70 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvcc.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/hevc/hvcc.rs @@ -169,10 +169,7 @@ mod tests { configuration_version: 1, ..Default::default() }, - btrt: None, - colr: None, - pasp: None, - taic: None, + ..Default::default() }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); diff --git a/src/moov/trak/mdia/minf/stbl/stsd/mod.rs b/src/moov/trak/mdia/minf/stbl/stsd/mod.rs index afa9d78..46fda96 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/mod.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/mod.rs @@ -39,14 +39,14 @@ pub use vp9::*; use crate::*; use derive_more::From; -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Stsd { pub codecs: Vec, } /// Called a "sample entry" in the ISOBMFF specification. -#[derive(Debug, Clone, PartialEq, Eq, From)] +#[derive(Debug, Clone, PartialEq, From)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub enum Codec { // H264 diff --git a/src/moov/trak/mdia/minf/stbl/stsd/mp4a/mod.rs b/src/moov/trak/mdia/minf/stbl/stsd/mp4a/mod.rs index c06662b..c43d8ae 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/mp4a/mod.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/mp4a/mod.rs @@ -4,13 +4,15 @@ use crate::*; pub mod esds; pub use esds::Esds; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Mp4a { pub audio: Audio, pub esds: Esds, pub btrt: Option, pub taic: Option, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Mp4a { @@ -23,13 +25,23 @@ impl Atom for Mp4a { let mut esds = None; let mut taic = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + // Find esds in mp4a or wave while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Btrt(atom) => btrt = atom.into(), Any::Esds(atom) => esds = atom.into(), Any::Taic(atom) => taic = 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())); + } } } @@ -38,6 +50,8 @@ impl Atom for Mp4a { esds: esds.ok_or(Error::MissingBox(Esds::KIND))?, btrt, taic, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } @@ -90,6 +104,8 @@ mod tests { avg_bitrate: 3, }), taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); diff --git a/src/moov/trak/mdia/minf/stbl/stsd/opus.rs b/src/moov/trak/mdia/minf/stbl/stsd/opus.rs index ad36ec0..d3e5046 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/opus.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/opus.rs @@ -1,10 +1,12 @@ use crate::*; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Opus { pub audio: Audio, pub dops: Dops, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Opus { @@ -14,18 +16,29 @@ impl Atom for Opus { let audio = Audio::decode(buf)?; let mut dops = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); // Find esds in mp4a or wave while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Dops(atom) => dops = 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, dops: dops.ok_or(Error::MissingBox(Dops::KIND))?, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } diff --git a/src/moov/trak/mdia/minf/stbl/stsd/uncv.rs b/src/moov/trak/mdia/minf/stbl/stsd/uncv.rs index 9eca10c..45ea9eb 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/uncv.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/uncv.rs @@ -2,7 +2,7 @@ use crate::*; use super::{Btrt, Pasp, Visual}; -#[derive(Debug, Clone, PartialEq, Eq)] +#[derive(Debug, Clone, PartialEq)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Uncv { pub visual: Visual, @@ -11,6 +11,8 @@ pub struct Uncv { pub btrt: Option, pub ccst: Option, pub pasp: Option, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Uncv { @@ -24,6 +26,9 @@ impl Atom for Uncv { let mut uncc = None; let mut btrt = None; let mut pasp = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::Cmpd(atom) => cmpd = atom.into(), @@ -31,7 +36,14 @@ impl Atom for Uncv { Any::Btrt(atom) => btrt = atom.into(), Any::Ccst(atom) => ccst = atom.into(), Any::Pasp(atom) => pasp = 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())); + } } } @@ -42,6 +54,8 @@ impl Atom for Uncv { btrt, ccst, pasp, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } diff --git a/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp08.rs b/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp08.rs index dea5e65..77a7204 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp08.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp08.rs @@ -1,11 +1,13 @@ use crate::*; // https://www.webmproject.org/vp9/mp4/ -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Vp08 { pub visual: Visual, pub vpcc: VpcC, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Vp08 { @@ -15,16 +17,28 @@ impl Atom for Vp08 { let visual = Visual::decode(buf)?; let mut vpcc = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::VpcC(atom) => vpcc = 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 { visual, vpcc: vpcc.ok_or(Error::MissingBox(VpcC::KIND))?, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } diff --git a/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp09.rs b/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp09.rs index 6b7b41f..147bfbf 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp09.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/vp9/vp09.rs @@ -1,11 +1,13 @@ use crate::*; // https://www.webmproject.org/vp9/mp4/ -#[derive(Debug, Clone, PartialEq, Eq, Default)] +#[derive(Debug, Clone, PartialEq, Default)] #[cfg_attr(feature = "serde", derive(serde::Serialize, serde::Deserialize))] pub struct Vp09 { pub visual: Visual, pub vpcc: VpcC, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Vp09 { @@ -15,16 +17,28 @@ impl Atom for Vp09 { let visual = Visual::decode(buf)?; let mut vpcc = None; + #[cfg(feature = "fault-tolerant")] + let mut unexpected = Vec::new(); + while let Some(atom) = Any::decode_maybe(buf)? { match atom { Any::VpcC(atom) => vpcc = 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 { visual, vpcc: vpcc.ok_or(Error::MissingBox(VpcC::KIND))?, + #[cfg(feature = "fault-tolerant")] + unexpected, }) } @@ -49,6 +63,8 @@ mod tests { ..Default::default() }, vpcc: VpcC::default(), + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); diff --git a/src/moov/trak/mdia/mod.rs b/src/moov/trak/mdia/mod.rs index 4923b39..4594b68 100644 --- a/src/moov/trak/mdia/mod.rs +++ b/src/moov/trak/mdia/mod.rs @@ -8,12 +8,14 @@ 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, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Mdia { diff --git a/src/moov/trak/mod.rs b/src/moov/trak/mod.rs index 2772f63..a0f6182 100644 --- a/src/moov/trak/mod.rs +++ b/src/moov/trak/mod.rs @@ -8,7 +8,7 @@ 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, @@ -16,6 +16,8 @@ pub struct Trak { pub meta: Option, // TODO is this suppose to be here? pub mdia: Mdia, pub udta: Option, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Trak { diff --git a/src/moov/udta/mod.rs b/src/moov/udta/mod.rs index 2a550f0..3324b19 100644 --- a/src/moov/udta/mod.rs +++ b/src/moov/udta/mod.rs @@ -4,11 +4,13 @@ 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, pub skip: Option, + #[cfg(feature = "fault-tolerant")] + pub unexpected: Vec, } impl Atom for Udta { @@ -30,6 +32,8 @@ mod tests { let expected = Udta { meta: None, skip: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; let mut buf = Vec::new(); @@ -51,6 +55,8 @@ mod tests { items: vec![], }), skip: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], }; let mut buf = Vec::new(); diff --git a/src/test/av1.rs b/src/test/av1.rs index 7b00be3..e93d336 100644 --- a/src/test/av1.rs +++ b/src/test/av1.rs @@ -111,6 +111,8 @@ fn av1() { assert_eq!( moov, Moov { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mvhd: Mvhd { creation_time: 0, modification_time: 0, @@ -133,6 +135,8 @@ fn av1() { }, meta: None, mvex: Some(Mvex { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mehd: None, trex: vec![Trex { track_id: 1, @@ -143,6 +147,8 @@ fn av1() { }] }), trak: vec![Trak { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tkhd: Tkhd { creation_time: 0, modification_time: 0, @@ -169,6 +175,8 @@ fn av1() { edts: None, meta: None, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { creation_time: 0, modification_time: 0, @@ -181,6 +189,8 @@ fn av1() { name: "obu@GPAC2.1-DEV-rev199-g8e29f6e8b-github_master".into() }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], vmhd: Some(Vmhd { graphics_mode: 0, op_color: RgbColor { @@ -191,6 +201,8 @@ fn av1() { }), smhd: None, dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".into() @@ -198,6 +210,8 @@ fn av1() { } }, stbl: Stbl { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], stsd: Stsd { codecs: vec![Av01 { visual: Visual { @@ -233,6 +247,8 @@ fn av1() { colr: None, pasp: None, taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into()], }, @@ -254,6 +270,8 @@ fn av1() { udta: None, }], udta: Some(Udta { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], meta: Some(Meta { hdlr: Hdlr { handler: FourCC::new(b"mdir"), @@ -270,8 +288,12 @@ fn av1() { assert_eq!( moof, Moof { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mfhd: Mfhd { sequence_number: 1 }, traf: vec![Traf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tfhd: Tfhd { track_id: 1, base_data_offset: None, diff --git a/src/test/bbb.rs b/src/test/bbb.rs index ed6382f..437ba3f 100644 --- a/src/test/bbb.rs +++ b/src/test/bbb.rs @@ -218,6 +218,8 @@ fn bbb() { ..Default::default() }, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { timescale: 24000, language: "und".into(), @@ -228,12 +230,16 @@ fn bbb() { name: "(C) 2007 Google Inc. v08.13.2007.".into(), }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], smhd: None, vmhd: Vmhd { ..Default::default() } .into(), dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".into(), @@ -270,6 +276,8 @@ fn bbb() { v_spacing: 1, }), taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into()], }, @@ -298,6 +306,8 @@ fn bbb() { ..Default::default() }, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { timescale: 44100, language: "und".into(), @@ -312,6 +322,8 @@ fn bbb() { ..Default::default() }), dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".into(), @@ -347,6 +359,8 @@ fn bbb() { }, btrt: Some(Btrt { buffer_size_db: 0, max_bitrate: 125587, avg_bitrate: 125587 }), taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into()], }, @@ -383,8 +397,12 @@ fn bbb() { assert_eq!( moof, Moof { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mfhd: Mfhd { sequence_number: 1 }, traf: vec![Traf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tfhd: Tfhd { track_id: 1, sample_description_index: 1.into(), @@ -443,8 +461,12 @@ fn bbb() { assert_eq!( moof, Moof { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mfhd: Mfhd { sequence_number: 2 }, traf: vec![Traf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tfhd: Tfhd { track_id: 2, sample_description_index: 1.into(), diff --git a/src/test/esds.rs b/src/test/esds.rs index 1d294f2..45f77dd 100644 --- a/src/test/esds.rs +++ b/src/test/esds.rs @@ -147,6 +147,8 @@ fn esds() { ..Default::default() }, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { timescale: 12288, language: "und".into(), @@ -157,12 +159,16 @@ fn esds() { name: "(C) 2007 Google Inc. v08.13.2007.".into(), }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], smhd: None, vmhd: Vmhd { ..Default::default() } .into(), dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".into(), @@ -199,6 +205,8 @@ fn esds() { v_spacing: 1 }), taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into()], }, @@ -227,6 +235,8 @@ fn esds() { ..Default::default() }, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { timescale: 44100, language: "und".into(), @@ -241,6 +251,8 @@ fn esds() { ..Default::default() }), dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".into(), @@ -276,6 +288,8 @@ fn esds() { }, btrt: Some(Btrt { buffer_size_db: 0, max_bitrate: 128000, avg_bitrate: 128000 }), taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into()], }, @@ -311,8 +325,12 @@ fn esds() { assert_eq!( moof, Moof { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mfhd: Mfhd { sequence_number: 1 }, traf: vec![Traf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tfhd: Tfhd { track_id: 1, sample_description_index: 1.into(), diff --git a/src/test/fault_tolerant.rs b/src/test/fault_tolerant.rs new file mode 100644 index 0000000..73e6754 --- /dev/null +++ b/src/test/fault_tolerant.rs @@ -0,0 +1,226 @@ +use crate::*; + +/// Test that fault-tolerant feature works as expected +/// When enabled: unexpected boxes are collected +/// When disabled: unexpected boxes cause an error +#[test] +fn test_fault_tolerant_behavior() { + // Create a buffer with a Moov box containing an unexpected child box + // Moov structure: mvhd (required) + an unknown/unexpected box + let mut buf = Vec::new(); + + // First encode a valid Moov with mvhd + let moov = Moov { + mvhd: Mvhd { + creation_time: 0, + modification_time: 0, + timescale: 1000, + duration: 0, + rate: 1.into(), + volume: 1.into(), + next_track_id: 1, + ..Default::default() + }, + meta: None, + mvex: None, + trak: vec![], + udta: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + }; + + // Encode the moov + moov.encode(&mut buf).unwrap(); + + // Now manually insert an unexpected box into the moov + // We'll use 'mdat' which is a known box type but NOT expected inside moov + // This should trigger the unexpected box behavior + let mut moov_buf = Vec::new(); + + // Encode mvhd + moov.mvhd.encode(&mut moov_buf).unwrap(); + + // Add an mdat box (which is NOT a child of moov) + // This should be treated as unexpected + let mdat_data = vec![ + 0x00, 0x00, 0x00, 0x10, // size: 16 bytes + b'm', b'd', b'a', b't', // type: 'mdat' + 0x01, 0x02, 0x03, 0x04, // some data + 0x05, 0x06, 0x07, 0x08, + ]; + moov_buf.extend_from_slice(&mdat_data); + + // Now wrap it in a moov box + let mut final_buf = Vec::new(); + let moov_size = (moov_buf.len() + 8) as u32; // +8 for size and type + final_buf.extend_from_slice(&moov_size.to_be_bytes()); + final_buf.extend_from_slice(b"moov"); + final_buf.extend_from_slice(&moov_buf); + + // Try to decode the moov + let result = Moov::decode(&mut final_buf.as_slice()); + + #[cfg(feature = "fault-tolerant")] + { + // With fault-tolerant feature enabled, it should succeed + let decoded = result.expect("should decode successfully with fault-tolerant feature"); + + // Check that the unexpected box was collected + assert_eq!(decoded.unexpected.len(), 1, "should have 1 unexpected box"); + + // Verify the unexpected box + match &decoded.unexpected[0] { + Any::Mdat(mdat) => { + // The mdat box should be collected as unexpected + assert_eq!(mdat.data.len(), 8, "mdat data should be 8 bytes"); + } + _ => panic!( + "unexpected box should be Any::Mdat variant, got: {:?}", + decoded.unexpected[0] + ), + } + + // Other fields should be decoded correctly + assert_eq!(decoded.mvhd.timescale, 1000); + assert_eq!(decoded.trak.len(), 0); + } + + #[cfg(not(feature = "fault-tolerant"))] + { + // Without fault-tolerant feature, it should return an error + assert!( + result.is_err(), + "should fail to decode without fault-tolerant feature" + ); + + // Check that it's specifically an UnexpectedBox error + match result.unwrap_err() { + Error::UnexpectedBox(kind) => { + assert_eq!( + kind, + FourCC::new(b"mdat"), + "error should report 'mdat' as unexpected" + ); + } + other => panic!("expected UnexpectedBox error, got: {:?}", other), + } + } +} + +/// Test that multiple unexpected boxes are collected correctly +#[test] +#[cfg(feature = "fault-tolerant")] +fn test_multiple_unexpected_boxes() { + // Create a moov with multiple unexpected boxes + let mut moov_buf = Vec::new(); + + // Encode mvhd + let mvhd = Mvhd { + creation_time: 0, + modification_time: 0, + timescale: 1000, + duration: 0, + rate: 1.into(), + volume: 1.into(), + next_track_id: 1, + ..Default::default() + }; + mvhd.encode(&mut moov_buf).unwrap(); + + // Add first unexpected box (ftyp - not expected in moov) + let unexpected1 = vec![ + 0x00, 0x00, 0x00, 0x14, // size: 20 bytes + b'f', b't', b'y', b'p', // type: 'ftyp' + b'i', b's', b'o', b'm', // major brand + 0x00, 0x00, 0x02, 0x00, // minor version + b'm', b'p', b'4', b'1', // compatible brand + ]; + moov_buf.extend_from_slice(&unexpected1); + + // Add second unexpected box (mdat - not expected in moov) + let unexpected2 = vec![ + 0x00, 0x00, 0x00, 0x10, // size: 16 bytes + b'm', b'd', b'a', b't', // type: 'mdat' + 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, + ]; + moov_buf.extend_from_slice(&unexpected2); + + // Wrap in moov box + let mut final_buf = Vec::new(); + let moov_size = (moov_buf.len() + 8) as u32; + final_buf.extend_from_slice(&moov_size.to_be_bytes()); + final_buf.extend_from_slice(b"moov"); + final_buf.extend_from_slice(&moov_buf); + + // Decode + let decoded = Moov::decode(&mut final_buf.as_slice()) + .expect("should decode successfully with multiple unexpected boxes"); + + // Verify both unexpected boxes were collected + assert_eq!( + decoded.unexpected.len(), + 2, + "should have 2 unexpected boxes" + ); + + match &decoded.unexpected[0] { + Any::Ftyp(ftyp) => { + assert_eq!(ftyp.major_brand, FourCC::new(b"isom")); + } + _ => panic!( + "first unexpected box should be Any::Ftyp, got: {:?}", + decoded.unexpected[0] + ), + } + + match &decoded.unexpected[1] { + Any::Mdat(mdat) => { + assert_eq!(mdat.data.len(), 8); + } + _ => panic!( + "second unexpected box should be Any::Mdat, got: {:?}", + decoded.unexpected[1] + ), + } +} + +/// Test that unexpected boxes are not encoded back +#[test] +#[cfg(feature = "fault-tolerant")] +fn test_unexpected_boxes_not_encoded() { + // Create a moov with an unexpected box + let moov = Moov { + mvhd: Mvhd { + creation_time: 0, + modification_time: 0, + timescale: 1000, + duration: 0, + rate: 1.into(), + volume: 1.into(), + next_track_id: 1, + ..Default::default() + }, + meta: None, + mvex: None, + trak: vec![], + udta: None, + unexpected: vec![Any::Mdat(Mdat { + data: vec![0x01, 0x02, 0x03, 0x04], + })], + }; + + // Encode the moov + let mut buf = Vec::new(); + moov.encode(&mut buf).unwrap(); + + // Decode it back + let decoded = Moov::decode(&mut buf.as_slice()).expect("should decode"); + + // The unexpected box should NOT be present in the decoded version + // because it wasn't encoded + assert_eq!( + decoded.unexpected.len(), + 0, + "unexpected boxes should not be encoded" + ); +} diff --git a/src/test/flac.rs b/src/test/flac.rs index b078e9d..2fd56ef 100644 --- a/src/test/flac.rs +++ b/src/test/flac.rs @@ -239,6 +239,8 @@ fn flac() { assert_eq!( moov, Moov { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mvhd: Mvhd { creation_time: 3840517353, modification_time: 3840517353, @@ -261,6 +263,8 @@ fn flac() { }, meta: None, mvex: Some(Mvex { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mehd: None, trex: vec![Trex { track_id: 1, @@ -271,6 +275,8 @@ fn flac() { }] }), trak: vec![Trak { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tkhd: Tkhd { creation_time: 3840517353, modification_time: 3840517353, @@ -297,6 +303,8 @@ fn flac() { edts: None, meta: None, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { creation_time: 3840517353, modification_time: 3840517353, @@ -309,9 +317,13 @@ fn flac() { name: "SoundHandler".into(), }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], vmhd: None, smhd: Some(Smhd { balance: 0.into() }), dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".into() @@ -319,6 +331,8 @@ fn flac() { } }, stbl: Stbl { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], stsd: Stsd { codecs: vec![Flac { audio: Audio { @@ -353,6 +367,8 @@ fn flac() { }, ], }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into(),] }, diff --git a/src/test/h264.rs b/src/test/h264.rs index 9f34a17..9f4a2d1 100644 --- a/src/test/h264.rs +++ b/src/test/h264.rs @@ -118,6 +118,8 @@ fn avcc_ext() { let moov = Moov::decode(buf).expect("failed to decode moov"); let expected = Moov { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mvhd: Mvhd { creation_time: 0, modification_time: 0, @@ -140,6 +142,8 @@ fn avcc_ext() { }, meta: None, mvex: Some(Mvex { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mehd: None, trex: vec![ Trex { @@ -160,6 +164,8 @@ fn avcc_ext() { }), trak: vec![ Trak { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tkhd: Tkhd { creation_time: 0, modification_time: 0, @@ -186,6 +192,8 @@ fn avcc_ext() { edts: None, meta: None, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { creation_time: 0, modification_time: 0, @@ -198,6 +206,8 @@ fn avcc_ext() { name: "L-SMASH Video Handler".to_string(), }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], vmhd: Some(Vmhd { graphics_mode: 0, op_color: RgbColor { @@ -208,11 +218,15 @@ fn avcc_ext() { }), smhd: None, dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url::default()], }, }, stbl: Stbl { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], stsd: Stsd { codecs: vec![Avc1 { visual: Visual { @@ -258,7 +272,7 @@ fn avcc_ext() { h_spacing: 1, v_spacing: 1, }), - taic: None, + ..Default::default() } .into()], }, @@ -280,6 +294,8 @@ fn avcc_ext() { udta: None, }, Trak { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tkhd: Tkhd { creation_time: 0, modification_time: 0, @@ -306,6 +322,8 @@ fn avcc_ext() { edts: None, meta: None, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { creation_time: 0, modification_time: 0, @@ -318,14 +336,20 @@ fn avcc_ext() { name: "L-SMASH Audio Handler".into(), }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], vmhd: None, smhd: Some(Smhd::default()), dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url::default()], }, }, stbl: Stbl { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], stsd: Stsd { codecs: vec![Mp4a { audio: Audio { @@ -359,6 +383,8 @@ fn avcc_ext() { avg_bitrate: 160000, }), taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into()], }, @@ -381,6 +407,8 @@ fn avcc_ext() { }, ], udta: Some(Udta { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], meta: None, skip: Some(Skip { zeroed: Zeroed { size: 81 }, @@ -670,10 +698,14 @@ fn avc_encrypted_segment() { assert_eq!( moof, Moof { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mfhd: Mfhd { sequence_number: 4382715 }, traf: vec![Traf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tfhd: Tfhd { track_id: 1, base_data_offset: None, diff --git a/src/test/heif.rs b/src/test/heif.rs index 05f1b91..0bd3f28 100644 --- a/src/test/heif.rs +++ b/src/test/heif.rs @@ -129,6 +129,8 @@ fn heif() { } .into(), Iprp { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], ipco: Ipco { properties: vec![ any::Any::Hvcc(Hvcc { @@ -410,6 +412,8 @@ fn avif() { } .into(), Iprp { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], ipco: Ipco { properties: vec![ Any::Ispe(Ispe { diff --git a/src/test/hevc.rs b/src/test/hevc.rs index d6bdcdf..4eae917 100644 --- a/src/test/hevc.rs +++ b/src/test/hevc.rs @@ -211,51 +211,30 @@ fn hevc() { } ); - let moov = Moov::decode(buf).expect("failed to decode moov"); - assert_eq!( - moov, - Moov { - mvhd: Mvhd { - creation_time: 0, - modification_time: 0, - timescale: 1000, - duration: 0, - rate: 1.into(), - volume: 1.into(), - matrix: Matrix { - a: 65536, - b: 0, - u: 0, - c: 0, - d: 65536, - v: 0, - x: 0, - y: 0, - w: 1073741824 - }, - next_track_id: 2 - }, - meta: None, - mvex: Some(Mvex { - mehd: None, - trex: vec![Trex { - track_id: 1, - default_sample_description_index: 1, - default_sample_duration: 0, - default_sample_size: 0, - default_sample_flags: 0 - }] - }), - trak: vec![Trak { - tkhd: Tkhd { + // unexpected box `fiel` + #[cfg(not(feature = "fault-tolerant"))] + { + let Error::UnexpectedBox(fourcc) = Moov::decode(buf).unwrap_err() else { + unreachable!() + }; + assert_eq!(fourcc, FourCC::new(b"fiel")); + } + + #[cfg(feature = "fault-tolerant")] + { + let moov = Moov::decode(buf).expect("failed to decode moov"); + assert_eq!( + moov, + Moov { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + mvhd: Mvhd { creation_time: 0, modification_time: 0, - track_id: 1, + timescale: 1000, duration: 0, - layer: 0, - alternate_group: 0, - enabled: true, - volume: 0.into(), + rate: 1.into(), + volume: 1.into(), matrix: Matrix { a: 65536, b: 0, @@ -267,335 +246,412 @@ fn hevc() { y: 0, w: 1073741824 }, - width: 1920.into(), - height: 1080.into() + next_track_id: 2 }, - edts: None, meta: None, - mdia: Mdia { - mdhd: Mdhd { + mvex: Some(Mvex { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + mehd: None, + trex: vec![Trex { + track_id: 1, + default_sample_description_index: 1, + default_sample_duration: 0, + default_sample_size: 0, + default_sample_flags: 0 + }] + }), + trak: vec![Trak { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + tkhd: Tkhd { creation_time: 0, modification_time: 0, - timescale: 15360, + track_id: 1, duration: 0, - language: "und".into() - }, - hdlr: Hdlr { - handler: b"vide".into(), - name: "VideoHandler".into() + layer: 0, + alternate_group: 0, + enabled: true, + volume: 0.into(), + matrix: Matrix { + a: 65536, + b: 0, + u: 0, + c: 0, + d: 65536, + v: 0, + x: 0, + y: 0, + w: 1073741824 + }, + width: 1920.into(), + height: 1080.into() }, - minf: Minf { - vmhd: Some(Vmhd { - graphics_mode: 0, - op_color: RgbColor { - red: 0, - green: 0, - blue: 0 - } - }), - smhd: None, - dinf: Dinf { - dref: Dref { - urls: vec![Url { - location: "".to_string() - }] - } + edts: None, + meta: None, + mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + mdhd: Mdhd { + creation_time: 0, + modification_time: 0, + timescale: 15360, + duration: 0, + language: "und".into() + }, + hdlr: Hdlr { + handler: b"vide".into(), + name: "VideoHandler".into() }, - stbl: Stbl { - stsd: Stsd { - codecs: vec![Hev1 { - visual: Visual { - data_reference_index: 1, - width: 1920, - height: 1080, - horizresolution: 72.into(), - vertresolution: 72.into(), - frame_count: 1, - compressor: "".into(), - depth: 24 - }, - hvcc: Hvcc { - configuration_version: 1, - general_profile_space: 0, - general_tier_flag: true, - general_profile_idc: 1, - general_profile_compatibility_flags: [96, 0, 0, 0], - general_constraint_indicator_flags: [144, 0, 0, 0, 0, 0], - general_level_idc: 120, - min_spatial_segmentation_idc: 0, - parallelism_type: 0, - chroma_format_idc: 1, - bit_depth_luma_minus8: 0, - bit_depth_chroma_minus8: 0, - avg_frame_rate: 0, - constant_frame_rate: 0, - num_temporal_layers: 1, - temporal_id_nested: true, - length_size_minus_one: 3, - arrays: vec![ - HvcCArray { - completeness: false, - nal_unit_type: 32, - nalus: vec![vec![ - 64, 1, 12, 1, 255, 255, 33, 96, 0, 0, 3, 0, - 144, 0, 0, 3, 0, 0, 3, 0, 120, 149, 152, 9 - ]] - }, - HvcCArray { - completeness: false, - nal_unit_type: 33, - nalus: vec![vec![ - 66, 1, 1, 33, 96, 0, 0, 3, 0, 144, 0, 0, 3, 0, - 0, 3, 0, 120, 160, 3, 192, 128, 16, 229, 150, - 86, 105, 36, 202, 240, 16, 16, 0, 0, 3, 0, 16, - 0, 0, 3, 1, 224, 128 - ]] - }, - HvcCArray { - completeness: false, - nal_unit_type: 34, - nalus: vec![vec![68, 1, 193, 114, 180, 98, 64]] - }, - HvcCArray { - completeness: false, - nal_unit_type: 39, - // It looks scary, but it's just the x265 CLI flags. - nalus: vec![vec![ - 78, 1, 5, 255, 255, 255, 255, 255, 255, 255, - 115, 44, 162, 222, 9, 181, 23, 71, 219, 187, - 85, 164, 254, 127, 194, 252, 78, 120, 50, 54, - 53, 32, 40, 98, 117, 105, 108, 100, 32, 49, 53, - 49, 41, 32, 45, 32, 50, 46, 54, 43, 52, 57, 45, - 55, 50, 49, 57, 51, 55, 54, 100, 101, 52, 50, - 97, 58, 91, 87, 105, 110, 100, 111, 119, 115, - 93, 91, 71, 67, 67, 32, 55, 46, 51, 46, 48, 93, - 91, 54, 52, 32, 98, 105, 116, 93, 32, 56, 98, - 105, 116, 43, 49, 48, 98, 105, 116, 32, 45, 32, - 72, 46, 50, 54, 53, 47, 72, 69, 86, 67, 32, 99, - 111, 100, 101, 99, 32, 45, 32, 67, 111, 112, - 121, 114, 105, 103, 104, 116, 32, 50, 48, 49, - 51, 45, 50, 48, 49, 56, 32, 40, 99, 41, 32, 77, - 117, 108, 116, 105, 99, 111, 114, 101, 119, 97, - 114, 101, 44, 32, 73, 110, 99, 32, 45, 32, 104, - 116, 116, 112, 58, 47, 47, 120, 50, 54, 53, 46, - 111, 114, 103, 32, 45, 32, 111, 112, 116, 105, - 111, 110, 115, 58, 32, 99, 112, 117, 105, 100, - 61, 49, 48, 53, 48, 49, 49, 49, 32, 102, 114, - 97, 109, 101, 45, 116, 104, 114, 101, 97, 100, - 115, 61, 51, 32, 110, 117, 109, 97, 45, 112, - 111, 111, 108, 115, 61, 56, 32, 119, 112, 112, - 32, 110, 111, 45, 112, 109, 111, 100, 101, 32, - 110, 111, 45, 112, 109, 101, 32, 110, 111, 45, - 112, 115, 110, 114, 32, 110, 111, 45, 115, 115, - 105, 109, 32, 108, 111, 103, 45, 108, 101, 118, - 101, 108, 61, 50, 32, 98, 105, 116, 100, 101, - 112, 116, 104, 61, 56, 32, 105, 110, 112, 117, - 116, 45, 99, 115, 112, 61, 49, 32, 102, 112, - 115, 61, 51, 48, 47, 49, 32, 105, 110, 112, - 117, 116, 45, 114, 101, 115, 61, 49, 57, 50, - 48, 120, 49, 48, 56, 48, 32, 105, 110, 116, - 101, 114, 108, 97, 99, 101, 61, 48, 32, 116, - 111, 116, 97, 108, 45, 102, 114, 97, 109, 101, - 115, 61, 48, 32, 108, 101, 118, 101, 108, 45, - 105, 100, 99, 61, 48, 32, 104, 105, 103, 104, - 45, 116, 105, 101, 114, 61, 49, 32, 117, 104, - 100, 45, 98, 100, 61, 48, 32, 114, 101, 102, - 61, 52, 32, 110, 111, 45, 97, 108, 108, 111, - 119, 45, 110, 111, 110, 45, 99, 111, 110, 102, - 111, 114, 109, 97, 110, 99, 101, 32, 110, 111, - 45, 114, 101, 112, 101, 97, 116, 45, 104, 101, - 97, 100, 101, 114, 115, 32, 97, 110, 110, 101, - 120, 98, 32, 110, 111, 45, 97, 117, 100, 32, - 110, 111, 45, 104, 114, 100, 32, 105, 110, 102, - 111, 32, 104, 97, 115, 104, 61, 48, 32, 110, - 111, 45, 116, 101, 109, 112, 111, 114, 97, 108, - 45, 108, 97, 121, 101, 114, 115, 32, 111, 112, - 101, 110, 45, 103, 111, 112, 32, 109, 105, 110, - 45, 107, 101, 121, 105, 110, 116, 61, 50, 53, - 32, 107, 101, 121, 105, 110, 116, 61, 50, 53, - 48, 32, 103, 111, 112, 45, 108, 111, 111, 107, - 97, 104, 101, 97, 100, 61, 48, 32, 98, 102, - 114, 97, 109, 101, 115, 61, 52, 32, 98, 45, 97, - 100, 97, 112, 116, 61, 50, 32, 98, 45, 112, - 121, 114, 97, 109, 105, 100, 32, 98, 102, 114, - 97, 109, 101, 45, 98, 105, 97, 115, 61, 48, 32, - 114, 99, 45, 108, 111, 111, 107, 97, 104, 101, - 97, 100, 61, 50, 53, 32, 108, 111, 111, 107, - 97, 104, 101, 97, 100, 45, 115, 108, 105, 99, - 101, 115, 61, 52, 32, 115, 99, 101, 110, 101, - 99, 117, 116, 61, 52, 48, 32, 114, 97, 100, - 108, 61, 48, 32, 110, 111, 45, 105, 110, 116, - 114, 97, 45, 114, 101, 102, 114, 101, 115, 104, - 32, 99, 116, 117, 61, 54, 52, 32, 109, 105, - 110, 45, 99, 117, 45, 115, 105, 122, 101, 61, - 56, 32, 114, 101, 99, 116, 32, 110, 111, 45, - 97, 109, 112, 32, 109, 97, 120, 45, 116, 117, - 45, 115, 105, 122, 101, 61, 51, 50, 32, 116, - 117, 45, 105, 110, 116, 101, 114, 45, 100, 101, - 112, 116, 104, 61, 49, 32, 116, 117, 45, 105, - 110, 116, 114, 97, 45, 100, 101, 112, 116, 104, - 61, 49, 32, 108, 105, 109, 105, 116, 45, 116, - 117, 61, 48, 32, 114, 100, 111, 113, 45, 108, - 101, 118, 101, 108, 61, 50, 32, 100, 121, 110, - 97, 109, 105, 99, 45, 114, 100, 61, 48, 46, 48, - 48, 32, 110, 111, 45, 115, 115, 105, 109, 45, - 114, 100, 32, 115, 105, 103, 110, 104, 105, - 100, 101, 32, 110, 111, 45, 116, 115, 107, 105, - 112, 32, 110, 114, 45, 105, 110, 116, 114, 97, - 61, 48, 32, 110, 114, 45, 105, 110, 116, 101, - 114, 61, 48, 32, 110, 111, 45, 99, 111, 110, - 115, 116, 114, 97, 105, 110, 101, 100, 45, 105, - 110, 116, 114, 97, 32, 115, 116, 114, 111, 110, - 103, 45, 105, 110, 116, 114, 97, 45, 115, 109, - 111, 111, 116, 104, 105, 110, 103, 32, 109, 97, - 120, 45, 109, 101, 114, 103, 101, 61, 51, 32, - 108, 105, 109, 105, 116, 45, 114, 101, 102, - 115, 61, 51, 32, 108, 105, 109, 105, 116, 45, - 109, 111, 100, 101, 115, 32, 109, 101, 61, 51, - 32, 115, 117, 98, 109, 101, 61, 51, 32, 109, - 101, 114, 97, 110, 103, 101, 61, 53, 55, 32, - 116, 101, 109, 112, 111, 114, 97, 108, 45, 109, - 118, 112, 32, 119, 101, 105, 103, 104, 116, - 112, 32, 110, 111, 45, 119, 101, 105, 103, 104, - 116, 98, 32, 110, 111, 45, 97, 110, 97, 108, - 121, 122, 101, 45, 115, 114, 99, 45, 112, 105, - 99, 115, 32, 100, 101, 98, 108, 111, 99, 107, - 61, 48, 58, 48, 32, 115, 97, 111, 32, 110, 111, - 45, 115, 97, 111, 45, 110, 111, 110, 45, 100, - 101, 98, 108, 111, 99, 107, 32, 114, 100, 61, - 52, 32, 110, 111, 45, 101, 97, 114, 108, 121, - 45, 115, 107, 105, 112, 32, 114, 115, 107, 105, - 112, 32, 110, 111, 45, 102, 97, 115, 116, 45, - 105, 110, 116, 114, 97, 32, 110, 111, 45, 116, - 115, 107, 105, 112, 45, 102, 97, 115, 116, 32, - 110, 111, 45, 99, 117, 45, 108, 111, 115, 115, - 108, 101, 115, 115, 32, 110, 111, 45, 98, 45, - 105, 110, 116, 114, 97, 32, 110, 111, 45, 115, - 112, 108, 105, 116, 114, 100, 45, 115, 107, - 105, 112, 32, 114, 100, 112, 101, 110, 97, 108, - 116, 121, 61, 48, 32, 112, 115, 121, 45, 114, - 100, 61, 50, 46, 48, 48, 32, 112, 115, 121, 45, - 114, 100, 111, 113, 61, 49, 46, 48, 48, 32, - 110, 111, 45, 114, 100, 45, 114, 101, 102, 105, - 110, 101, 32, 110, 111, 45, 108, 111, 115, 115, - 108, 101, 115, 115, 32, 99, 98, 113, 112, 111, - 102, 102, 115, 61, 48, 32, 99, 114, 113, 112, - 111, 102, 102, 115, 61, 48, 32, 114, 99, 61, - 97, 98, 114, 32, 98, 105, 116, 114, 97, 116, - 101, 61, 50, 51, 50, 48, 48, 32, 113, 99, 111, - 109, 112, 61, 48, 46, 54, 48, 32, 113, 112, - 115, 116, 101, 112, 61, 52, 32, 115, 116, 97, - 116, 115, 45, 119, 114, 105, 116, 101, 61, 48, - 32, 115, 116, 97, 116, 115, 45, 114, 101, 97, - 100, 61, 48, 32, 105, 112, 114, 97, 116, 105, - 111, 61, 49, 46, 52, 48, 32, 112, 98, 114, 97, - 116, 105, 111, 61, 49, 46, 51, 48, 32, 97, 113, - 45, 109, 111, 100, 101, 61, 49, 32, 97, 113, - 45, 115, 116, 114, 101, 110, 103, 116, 104, 61, - 49, 46, 48, 48, 32, 99, 117, 116, 114, 101, - 101, 32, 122, 111, 110, 101, 45, 99, 111, 117, - 110, 116, 61, 48, 32, 110, 111, 45, 115, 116, - 114, 105, 99, 116, 45, 99, 98, 114, 32, 113, - 103, 45, 115, 105, 122, 101, 61, 51, 50, 32, - 110, 111, 45, 114, 99, 45, 103, 114, 97, 105, - 110, 32, 113, 112, 109, 97, 120, 61, 54, 57, - 32, 113, 112, 109, 105, 110, 61, 48, 32, 110, - 111, 45, 99, 111, 110, 115, 116, 45, 118, 98, - 118, 32, 115, 97, 114, 61, 49, 32, 111, 118, - 101, 114, 115, 99, 97, 110, 61, 48, 32, 118, - 105, 100, 101, 111, 102, 111, 114, 109, 97, - 116, 61, 53, 32, 114, 97, 110, 103, 101, 61, - 48, 32, 99, 111, 108, 111, 114, 112, 114, 105, - 109, 61, 50, 32, 116, 114, 97, 110, 115, 102, - 101, 114, 61, 50, 32, 99, 111, 108, 111, 114, - 109, 97, 116, 114, 105, 120, 61, 50, 32, 99, - 104, 114, 111, 109, 97, 108, 111, 99, 61, 48, - 32, 100, 105, 115, 112, 108, 97, 121, 45, 119, - 105, 110, 100, 111, 119, 61, 48, 32, 109, 97, - 120, 45, 99, 108, 108, 61, 48, 44, 48, 32, 109, - 105, 110, 45, 108, 117, 109, 97, 61, 48, 32, - 109, 97, 120, 45, 108, 117, 109, 97, 61, 50, - 53, 53, 32, 108, 111, 103, 50, 45, 109, 97, - 120, 45, 112, 111, 99, 45, 108, 115, 98, 61, - 56, 32, 118, 117, 105, 45, 116, 105, 109, 105, - 110, 103, 45, 105, 110, 102, 111, 32, 118, 117, - 105, 45, 104, 114, 100, 45, 105, 110, 102, 111, - 32, 115, 108, 105, 99, 101, 115, 61, 49, 32, - 110, 111, 45, 111, 112, 116, 45, 113, 112, 45, - 112, 112, 115, 32, 110, 111, 45, 111, 112, 116, - 45, 114, 101, 102, 45, 108, 105, 115, 116, 45, - 108, 101, 110, 103, 116, 104, 45, 112, 112, - 115, 32, 110, 111, 45, 109, 117, 108, 116, 105, - 45, 112, 97, 115, 115, 45, 111, 112, 116, 45, - 114, 112, 115, 32, 115, 99, 101, 110, 101, 99, - 117, 116, 45, 98, 105, 97, 115, 61, 48, 46, 48, - 53, 32, 110, 111, 45, 111, 112, 116, 45, 99, - 117, 45, 100, 101, 108, 116, 97, 45, 113, 112, - 32, 110, 111, 45, 97, 113, 45, 109, 111, 116, - 105, 111, 110, 32, 110, 111, 45, 104, 100, 114, - 32, 110, 111, 45, 104, 100, 114, 45, 111, 112, - 116, 32, 110, 111, 45, 100, 104, 100, 114, 49, - 48, 45, 111, 112, 116, 32, 97, 110, 97, 108, - 121, 115, 105, 115, 45, 114, 101, 117, 115, - 101, 45, 108, 101, 118, 101, 108, 61, 53, 32, - 115, 99, 97, 108, 101, 45, 102, 97, 99, 116, - 111, 114, 61, 48, 32, 114, 101, 102, 105, 110, - 101, 45, 105, 110, 116, 114, 97, 61, 48, 32, - 114, 101, 102, 105, 110, 101, 45, 105, 110, - 116, 101, 114, 61, 48, 32, 114, 101, 102, 105, - 110, 101, 45, 109, 118, 61, 48, 32, 110, 111, - 45, 108, 105, 109, 105, 116, 45, 115, 97, 111, - 32, 99, 116, 117, 45, 105, 110, 102, 111, 61, - 48, 32, 110, 111, 45, 108, 111, 119, 112, 97, - 115, 115, 45, 100, 99, 116, 32, 114, 101, 102, - 105, 110, 101, 45, 109, 118, 45, 116, 121, 112, - 101, 61, 48, 32, 99, 111, 112, 121, 45, 112, - 105, 99, 61, 49, 128 - ]] - } - ] - }, - btrt: Some(Btrt { - buffer_size_db: 0, - max_bitrate: 25175730, - avg_bitrate: 25175730 - }), - colr: None, - pasp: Some(Pasp { - h_spacing: 1, - v_spacing: 1, - }), - taic: None, + minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + vmhd: Some(Vmhd { + graphics_mode: 0, + op_color: RgbColor { + red: 0, + green: 0, + blue: 0 + } + }), + smhd: None, + dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + dref: Dref { + urls: vec![Url { + location: "".to_string() + }] } - .into()], }, - stco: Some(Stco { entries: vec![] }), - ..Default::default() + stbl: Stbl { + stsd: Stsd { + codecs: vec![Hev1 { + visual: Visual { + data_reference_index: 1, + width: 1920, + height: 1080, + horizresolution: 72.into(), + vertresolution: 72.into(), + frame_count: 1, + compressor: "".into(), + depth: 24 + }, + hvcc: Hvcc { + configuration_version: 1, + general_profile_space: 0, + general_tier_flag: true, + general_profile_idc: 1, + general_profile_compatibility_flags: [96, 0, 0, 0], + general_constraint_indicator_flags: [ + 144, 0, 0, 0, 0, 0 + ], + general_level_idc: 120, + min_spatial_segmentation_idc: 0, + parallelism_type: 0, + chroma_format_idc: 1, + bit_depth_luma_minus8: 0, + bit_depth_chroma_minus8: 0, + avg_frame_rate: 0, + constant_frame_rate: 0, + num_temporal_layers: 1, + temporal_id_nested: true, + length_size_minus_one: 3, + arrays: vec![ + HvcCArray { + completeness: false, + nal_unit_type: 32, + nalus: vec![vec![ + 64, 1, 12, 1, 255, 255, 33, 96, 0, 0, 3, 0, + 144, 0, 0, 3, 0, 0, 3, 0, 120, 149, 152, 9 + ]] + }, + HvcCArray { + completeness: false, + nal_unit_type: 33, + nalus: vec![vec![ + 66, 1, 1, 33, 96, 0, 0, 3, 0, 144, 0, 0, 3, + 0, 0, 3, 0, 120, 160, 3, 192, 128, 16, 229, + 150, 86, 105, 36, 202, 240, 16, 16, 0, 0, + 3, 0, 16, 0, 0, 3, 1, 224, 128 + ]] + }, + HvcCArray { + completeness: false, + nal_unit_type: 34, + nalus: vec![vec![68, 1, 193, 114, 180, 98, 64]] + }, + HvcCArray { + completeness: false, + nal_unit_type: 39, + // It looks scary, but it's just the x265 CLI flags. + nalus: vec![vec![ + 78, 1, 5, 255, 255, 255, 255, 255, 255, + 255, 115, 44, 162, 222, 9, 181, 23, 71, + 219, 187, 85, 164, 254, 127, 194, 252, 78, + 120, 50, 54, 53, 32, 40, 98, 117, 105, 108, + 100, 32, 49, 53, 49, 41, 32, 45, 32, 50, + 46, 54, 43, 52, 57, 45, 55, 50, 49, 57, 51, + 55, 54, 100, 101, 52, 50, 97, 58, 91, 87, + 105, 110, 100, 111, 119, 115, 93, 91, 71, + 67, 67, 32, 55, 46, 51, 46, 48, 93, 91, 54, + 52, 32, 98, 105, 116, 93, 32, 56, 98, 105, + 116, 43, 49, 48, 98, 105, 116, 32, 45, 32, + 72, 46, 50, 54, 53, 47, 72, 69, 86, 67, 32, + 99, 111, 100, 101, 99, 32, 45, 32, 67, 111, + 112, 121, 114, 105, 103, 104, 116, 32, 50, + 48, 49, 51, 45, 50, 48, 49, 56, 32, 40, 99, + 41, 32, 77, 117, 108, 116, 105, 99, 111, + 114, 101, 119, 97, 114, 101, 44, 32, 73, + 110, 99, 32, 45, 32, 104, 116, 116, 112, + 58, 47, 47, 120, 50, 54, 53, 46, 111, 114, + 103, 32, 45, 32, 111, 112, 116, 105, 111, + 110, 115, 58, 32, 99, 112, 117, 105, 100, + 61, 49, 48, 53, 48, 49, 49, 49, 32, 102, + 114, 97, 109, 101, 45, 116, 104, 114, 101, + 97, 100, 115, 61, 51, 32, 110, 117, 109, + 97, 45, 112, 111, 111, 108, 115, 61, 56, + 32, 119, 112, 112, 32, 110, 111, 45, 112, + 109, 111, 100, 101, 32, 110, 111, 45, 112, + 109, 101, 32, 110, 111, 45, 112, 115, 110, + 114, 32, 110, 111, 45, 115, 115, 105, 109, + 32, 108, 111, 103, 45, 108, 101, 118, 101, + 108, 61, 50, 32, 98, 105, 116, 100, 101, + 112, 116, 104, 61, 56, 32, 105, 110, 112, + 117, 116, 45, 99, 115, 112, 61, 49, 32, + 102, 112, 115, 61, 51, 48, 47, 49, 32, 105, + 110, 112, 117, 116, 45, 114, 101, 115, 61, + 49, 57, 50, 48, 120, 49, 48, 56, 48, 32, + 105, 110, 116, 101, 114, 108, 97, 99, 101, + 61, 48, 32, 116, 111, 116, 97, 108, 45, + 102, 114, 97, 109, 101, 115, 61, 48, 32, + 108, 101, 118, 101, 108, 45, 105, 100, 99, + 61, 48, 32, 104, 105, 103, 104, 45, 116, + 105, 101, 114, 61, 49, 32, 117, 104, 100, + 45, 98, 100, 61, 48, 32, 114, 101, 102, 61, + 52, 32, 110, 111, 45, 97, 108, 108, 111, + 119, 45, 110, 111, 110, 45, 99, 111, 110, + 102, 111, 114, 109, 97, 110, 99, 101, 32, + 110, 111, 45, 114, 101, 112, 101, 97, 116, + 45, 104, 101, 97, 100, 101, 114, 115, 32, + 97, 110, 110, 101, 120, 98, 32, 110, 111, + 45, 97, 117, 100, 32, 110, 111, 45, 104, + 114, 100, 32, 105, 110, 102, 111, 32, 104, + 97, 115, 104, 61, 48, 32, 110, 111, 45, + 116, 101, 109, 112, 111, 114, 97, 108, 45, + 108, 97, 121, 101, 114, 115, 32, 111, 112, + 101, 110, 45, 103, 111, 112, 32, 109, 105, + 110, 45, 107, 101, 121, 105, 110, 116, 61, + 50, 53, 32, 107, 101, 121, 105, 110, 116, + 61, 50, 53, 48, 32, 103, 111, 112, 45, 108, + 111, 111, 107, 97, 104, 101, 97, 100, 61, + 48, 32, 98, 102, 114, 97, 109, 101, 115, + 61, 52, 32, 98, 45, 97, 100, 97, 112, 116, + 61, 50, 32, 98, 45, 112, 121, 114, 97, 109, + 105, 100, 32, 98, 102, 114, 97, 109, 101, + 45, 98, 105, 97, 115, 61, 48, 32, 114, 99, + 45, 108, 111, 111, 107, 97, 104, 101, 97, + 100, 61, 50, 53, 32, 108, 111, 111, 107, + 97, 104, 101, 97, 100, 45, 115, 108, 105, + 99, 101, 115, 61, 52, 32, 115, 99, 101, + 110, 101, 99, 117, 116, 61, 52, 48, 32, + 114, 97, 100, 108, 61, 48, 32, 110, 111, + 45, 105, 110, 116, 114, 97, 45, 114, 101, + 102, 114, 101, 115, 104, 32, 99, 116, 117, + 61, 54, 52, 32, 109, 105, 110, 45, 99, 117, + 45, 115, 105, 122, 101, 61, 56, 32, 114, + 101, 99, 116, 32, 110, 111, 45, 97, 109, + 112, 32, 109, 97, 120, 45, 116, 117, 45, + 115, 105, 122, 101, 61, 51, 50, 32, 116, + 117, 45, 105, 110, 116, 101, 114, 45, 100, + 101, 112, 116, 104, 61, 49, 32, 116, 117, + 45, 105, 110, 116, 114, 97, 45, 100, 101, + 112, 116, 104, 61, 49, 32, 108, 105, 109, + 105, 116, 45, 116, 117, 61, 48, 32, 114, + 100, 111, 113, 45, 108, 101, 118, 101, 108, + 61, 50, 32, 100, 121, 110, 97, 109, 105, + 99, 45, 114, 100, 61, 48, 46, 48, 48, 32, + 110, 111, 45, 115, 115, 105, 109, 45, 114, + 100, 32, 115, 105, 103, 110, 104, 105, 100, + 101, 32, 110, 111, 45, 116, 115, 107, 105, + 112, 32, 110, 114, 45, 105, 110, 116, 114, + 97, 61, 48, 32, 110, 114, 45, 105, 110, + 116, 101, 114, 61, 48, 32, 110, 111, 45, + 99, 111, 110, 115, 116, 114, 97, 105, 110, + 101, 100, 45, 105, 110, 116, 114, 97, 32, + 115, 116, 114, 111, 110, 103, 45, 105, 110, + 116, 114, 97, 45, 115, 109, 111, 111, 116, + 104, 105, 110, 103, 32, 109, 97, 120, 45, + 109, 101, 114, 103, 101, 61, 51, 32, 108, + 105, 109, 105, 116, 45, 114, 101, 102, 115, + 61, 51, 32, 108, 105, 109, 105, 116, 45, + 109, 111, 100, 101, 115, 32, 109, 101, 61, + 51, 32, 115, 117, 98, 109, 101, 61, 51, 32, + 109, 101, 114, 97, 110, 103, 101, 61, 53, + 55, 32, 116, 101, 109, 112, 111, 114, 97, + 108, 45, 109, 118, 112, 32, 119, 101, 105, + 103, 104, 116, 112, 32, 110, 111, 45, 119, + 101, 105, 103, 104, 116, 98, 32, 110, 111, + 45, 97, 110, 97, 108, 121, 122, 101, 45, + 115, 114, 99, 45, 112, 105, 99, 115, 32, + 100, 101, 98, 108, 111, 99, 107, 61, 48, + 58, 48, 32, 115, 97, 111, 32, 110, 111, 45, + 115, 97, 111, 45, 110, 111, 110, 45, 100, + 101, 98, 108, 111, 99, 107, 32, 114, 100, + 61, 52, 32, 110, 111, 45, 101, 97, 114, + 108, 121, 45, 115, 107, 105, 112, 32, 114, + 115, 107, 105, 112, 32, 110, 111, 45, 102, + 97, 115, 116, 45, 105, 110, 116, 114, 97, + 32, 110, 111, 45, 116, 115, 107, 105, 112, + 45, 102, 97, 115, 116, 32, 110, 111, 45, + 99, 117, 45, 108, 111, 115, 115, 108, 101, + 115, 115, 32, 110, 111, 45, 98, 45, 105, + 110, 116, 114, 97, 32, 110, 111, 45, 115, + 112, 108, 105, 116, 114, 100, 45, 115, 107, + 105, 112, 32, 114, 100, 112, 101, 110, 97, + 108, 116, 121, 61, 48, 32, 112, 115, 121, + 45, 114, 100, 61, 50, 46, 48, 48, 32, 112, + 115, 121, 45, 114, 100, 111, 113, 61, 49, + 46, 48, 48, 32, 110, 111, 45, 114, 100, 45, + 114, 101, 102, 105, 110, 101, 32, 110, 111, + 45, 108, 111, 115, 115, 108, 101, 115, 115, + 32, 99, 98, 113, 112, 111, 102, 102, 115, + 61, 48, 32, 99, 114, 113, 112, 111, 102, + 102, 115, 61, 48, 32, 114, 99, 61, 97, 98, + 114, 32, 98, 105, 116, 114, 97, 116, 101, + 61, 50, 51, 50, 48, 48, 32, 113, 99, 111, + 109, 112, 61, 48, 46, 54, 48, 32, 113, 112, + 115, 116, 101, 112, 61, 52, 32, 115, 116, + 97, 116, 115, 45, 119, 114, 105, 116, 101, + 61, 48, 32, 115, 116, 97, 116, 115, 45, + 114, 101, 97, 100, 61, 48, 32, 105, 112, + 114, 97, 116, 105, 111, 61, 49, 46, 52, 48, + 32, 112, 98, 114, 97, 116, 105, 111, 61, + 49, 46, 51, 48, 32, 97, 113, 45, 109, 111, + 100, 101, 61, 49, 32, 97, 113, 45, 115, + 116, 114, 101, 110, 103, 116, 104, 61, 49, + 46, 48, 48, 32, 99, 117, 116, 114, 101, + 101, 32, 122, 111, 110, 101, 45, 99, 111, + 117, 110, 116, 61, 48, 32, 110, 111, 45, + 115, 116, 114, 105, 99, 116, 45, 99, 98, + 114, 32, 113, 103, 45, 115, 105, 122, 101, + 61, 51, 50, 32, 110, 111, 45, 114, 99, 45, + 103, 114, 97, 105, 110, 32, 113, 112, 109, + 97, 120, 61, 54, 57, 32, 113, 112, 109, + 105, 110, 61, 48, 32, 110, 111, 45, 99, + 111, 110, 115, 116, 45, 118, 98, 118, 32, + 115, 97, 114, 61, 49, 32, 111, 118, 101, + 114, 115, 99, 97, 110, 61, 48, 32, 118, + 105, 100, 101, 111, 102, 111, 114, 109, 97, + 116, 61, 53, 32, 114, 97, 110, 103, 101, + 61, 48, 32, 99, 111, 108, 111, 114, 112, + 114, 105, 109, 61, 50, 32, 116, 114, 97, + 110, 115, 102, 101, 114, 61, 50, 32, 99, + 111, 108, 111, 114, 109, 97, 116, 114, 105, + 120, 61, 50, 32, 99, 104, 114, 111, 109, + 97, 108, 111, 99, 61, 48, 32, 100, 105, + 115, 112, 108, 97, 121, 45, 119, 105, 110, + 100, 111, 119, 61, 48, 32, 109, 97, 120, + 45, 99, 108, 108, 61, 48, 44, 48, 32, 109, + 105, 110, 45, 108, 117, 109, 97, 61, 48, + 32, 109, 97, 120, 45, 108, 117, 109, 97, + 61, 50, 53, 53, 32, 108, 111, 103, 50, 45, + 109, 97, 120, 45, 112, 111, 99, 45, 108, + 115, 98, 61, 56, 32, 118, 117, 105, 45, + 116, 105, 109, 105, 110, 103, 45, 105, 110, + 102, 111, 32, 118, 117, 105, 45, 104, 114, + 100, 45, 105, 110, 102, 111, 32, 115, 108, + 105, 99, 101, 115, 61, 49, 32, 110, 111, + 45, 111, 112, 116, 45, 113, 112, 45, 112, + 112, 115, 32, 110, 111, 45, 111, 112, 116, + 45, 114, 101, 102, 45, 108, 105, 115, 116, + 45, 108, 101, 110, 103, 116, 104, 45, 112, + 112, 115, 32, 110, 111, 45, 109, 117, 108, + 116, 105, 45, 112, 97, 115, 115, 45, 111, + 112, 116, 45, 114, 112, 115, 32, 115, 99, + 101, 110, 101, 99, 117, 116, 45, 98, 105, + 97, 115, 61, 48, 46, 48, 53, 32, 110, 111, + 45, 111, 112, 116, 45, 99, 117, 45, 100, + 101, 108, 116, 97, 45, 113, 112, 32, 110, + 111, 45, 97, 113, 45, 109, 111, 116, 105, + 111, 110, 32, 110, 111, 45, 104, 100, 114, + 32, 110, 111, 45, 104, 100, 114, 45, 111, + 112, 116, 32, 110, 111, 45, 100, 104, 100, + 114, 49, 48, 45, 111, 112, 116, 32, 97, + 110, 97, 108, 121, 115, 105, 115, 45, 114, + 101, 117, 115, 101, 45, 108, 101, 118, 101, + 108, 61, 53, 32, 115, 99, 97, 108, 101, 45, + 102, 97, 99, 116, 111, 114, 61, 48, 32, + 114, 101, 102, 105, 110, 101, 45, 105, 110, + 116, 114, 97, 61, 48, 32, 114, 101, 102, + 105, 110, 101, 45, 105, 110, 116, 101, 114, + 61, 48, 32, 114, 101, 102, 105, 110, 101, + 45, 109, 118, 61, 48, 32, 110, 111, 45, + 108, 105, 109, 105, 116, 45, 115, 97, 111, + 32, 99, 116, 117, 45, 105, 110, 102, 111, + 61, 48, 32, 110, 111, 45, 108, 111, 119, + 112, 97, 115, 115, 45, 100, 99, 116, 32, + 114, 101, 102, 105, 110, 101, 45, 109, 118, + 45, 116, 121, 112, 101, 61, 48, 32, 99, + 111, 112, 121, 45, 112, 105, 99, 61, 49, + 128 + ]] + } + ] + }, + btrt: Some(Btrt { + buffer_size_db: 0, + max_bitrate: 25175730, + avg_bitrate: 25175730 + }), + pasp: Some(Pasp { + h_spacing: 1, + v_spacing: 1, + }), + colr: None, + taic: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![Any::Unknown( + FourCC::new(b"fiel"), + vec![1, 0] + )], + } + .into()], + }, + stco: Some(Stco { entries: vec![] }), + ..Default::default() + } } - } - }, - udta: None, - }], - udta: Some(Udta { - meta: Some(Meta { - hdlr: Hdlr { - handler: FourCC::new(b"mdir"), - name: "".into() }, - items: vec![Ilst::default().into(),], - }), - skip: None, - }) - } - ); - - // Make sure the hev1 atom encodes/decodes to the exact same content. - let hev1 = &moov.trak[0].mdia.minf.stbl.stsd.codecs[0]; - hev1.assert_encode_decode(); + udta: None, + }], + udta: Some(Udta { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], + meta: Some(Meta { + hdlr: Hdlr { + handler: FourCC::new(b"mdir"), + name: "".into() + }, + items: vec![Ilst::default().into(),], + }), + skip: None, + }) + } + ); - let mut buf = Vec::new(); - ftyp.encode(&mut buf).expect("failed to encode ftyp"); - moov.encode(&mut buf).expect("failed to encode moov"); + // Make sure the hev1 atom encodes/decodes to the exact same content. + // Note: We need to clear unexpected atoms because they are not encoded back + let mut hev1_for_test = moov.trak[0].mdia.minf.stbl.stsd.codecs[0].clone(); + if let Codec::Hev1(ref mut hev1) = hev1_for_test { + hev1.unexpected.clear(); + } + hev1_for_test.assert_encode_decode(); - // assert_eq!(buf, ENCODED); + // Encode to verify encoding succeeds. Output will differ from ENCODED because + // unexpected boxes (like the `fiel` box) are not encoded back. + let mut buf = Vec::new(); + ftyp.encode(&mut buf).expect("failed to encode ftyp"); + moov.encode(&mut buf).expect("failed to encode moov"); + } } diff --git a/src/test/mod.rs b/src/test/mod.rs index 09e6e0c..4b59f69 100644 --- a/src/test/mod.rs +++ b/src/test/mod.rs @@ -1,6 +1,7 @@ mod av1; mod bbb; mod esds; +mod fault_tolerant; mod flac; mod h264; mod heif; diff --git a/src/test/uncompressed.rs b/src/test/uncompressed.rs index adbd22e..3416e82 100644 --- a/src/test/uncompressed.rs +++ b/src/test/uncompressed.rs @@ -89,6 +89,8 @@ fn uncompressed() { assert_eq!( moov, Moov { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mvhd: Mvhd { creation_time: 3827291266, modification_time: 3827291266, @@ -112,6 +114,8 @@ fn uncompressed() { meta: None, mvex: None, trak: [Trak { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tkhd: Tkhd { creation_time: 3827291266, modification_time: 3827291266, @@ -138,6 +142,8 @@ fn uncompressed() { edts: None, meta: None, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { creation_time: 3827291266, modification_time: 3827291266, @@ -150,6 +156,8 @@ fn uncompressed() { name: "GPAC ISO Video Handler".into(), }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], vmhd: Some(Vmhd { graphics_mode: 0, op_color: RgbColor { @@ -160,6 +168,8 @@ fn uncompressed() { }), smhd: None, dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".into() @@ -167,6 +177,8 @@ fn uncompressed() { } }, stbl: Stbl { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], stsd: Stsd { codecs: vec![Codec::Uncv(Uncv { visual: Visual { @@ -237,7 +249,9 @@ fn uncompressed() { avg_bitrate: 48 }), ccst: None, - pasp: None + pasp: None, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], })], }, stts: Stts { @@ -283,6 +297,8 @@ fn uncompressed() { }] .into(), udta: Some(Udta { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], meta: Some(Meta { hdlr: Hdlr { handler: FourCC::new(b"mdir"), diff --git a/src/test/vp9.rs b/src/test/vp9.rs index e42a137..24ed242 100644 --- a/src/test/vp9.rs +++ b/src/test/vp9.rs @@ -156,6 +156,8 @@ fn vp9() { assert_eq!( moov, Moov { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mvhd: Mvhd { creation_time: 3576083626, modification_time: 3576083626, @@ -193,6 +195,8 @@ fn vp9() { )] }), mvex: Some(Mvex { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mehd: Some(Mehd { fragment_duration: 2736000 }), @@ -205,6 +209,8 @@ fn vp9() { }] }), trak: vec![Trak { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], tkhd: Tkhd { creation_time: 3576083626, modification_time: 3576083626, @@ -231,6 +237,8 @@ fn vp9() { edts: None, meta: None, mdia: Mdia { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], mdhd: Mdhd { creation_time: 3576083626, modification_time: 3576083626, @@ -243,6 +251,8 @@ fn vp9() { name: "VideoHandler".to_string() }, minf: Minf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], vmhd: Some(Vmhd { graphics_mode: 0, op_color: RgbColor { @@ -253,6 +263,8 @@ fn vp9() { }), smhd: None, dinf: Dinf { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], dref: Dref { urls: vec![Url { location: "".to_string() @@ -260,6 +272,8 @@ fn vp9() { } }, stbl: Stbl { + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], stsd: Stsd { codecs: vec![Vp09 { visual: Visual { @@ -282,7 +296,9 @@ fn vp9() { transfer_characteristics: 2, matrix_coefficients: 2, codec_initialization_data: vec![] - } + }, + #[cfg(feature = "fault-tolerant")] + unexpected: vec![], } .into()], },