diff --git a/src/moov/trak/mdia/minf/stbl/stsd/mod.rs b/src/moov/trak/mdia/minf/stbl/stsd/mod.rs index f7144f4f..01f85ea8 100644 --- a/src/moov/trak/mdia/minf/stbl/stsd/mod.rs +++ b/src/moov/trak/mdia/minf/stbl/stsd/mod.rs @@ -125,7 +125,7 @@ pub enum Codec { Samr(Samr), // Unknown - Unknown(FourCC), + Unknown(FourCC, Vec), } impl Decode for Codec { @@ -157,9 +157,23 @@ impl Decode for Codec { Any::S16l(atom) => atom.into(), Any::Wvtt(atom) => atom.into(), Any::Samr(atom) => atom.into(), + unknown @ Any::Unknown(..) => { + crate::decode_unknown(&unknown, Stsd::KIND)?; + + let Any::Unknown(kind, body) = unknown else { + unreachable!() + }; + Self::Unknown(kind, body) + } unknown => { crate::decode_unknown(&unknown, Stsd::KIND)?; - Self::Unknown(unknown.kind()) + + // The atom kind is known elsewhere in the hierarchy, but is not a supported + // sample entry. Re-encode its body so the unknown entry remains a valid box. + let kind = unknown.kind(); + let mut encoded = Vec::new(); + unknown.encode(&mut encoded)?; + Self::Unknown(kind, encoded.split_off(8)) } }) } @@ -168,7 +182,19 @@ impl Decode for Codec { impl Encode for Codec { fn encode(&self, buf: &mut B) -> Result<()> { match self { - Self::Unknown(kind) => kind.encode(buf), + Self::Unknown(kind, body) => { + let start = buf.len(); + 0u32.encode(buf)?; + kind.encode(buf)?; + body.encode(buf)?; + + let size: u32 = (buf.len() - start) + .try_into() + .map_err(|_| Error::TooLarge(*kind))?; + buf.set_slice(start, &size.to_be_bytes()); + + Ok(()) + } Self::Avc1(atom) => atom.encode(buf), Self::Hev1(atom) => atom.encode(buf), Self::Hvc1(atom) => atom.encode(buf), diff --git a/tests/stsd.rs b/tests/stsd.rs new file mode 100644 index 00000000..51aac39c --- /dev/null +++ b/tests/stsd.rs @@ -0,0 +1,20 @@ +#![cfg(not(feature = "strict"))] + +use mp4_atom::{Codec, Decode, Encode, FourCC, Stsd}; + +#[test] +fn unknown_codec_round_trips() { + let input = b"\0\0\0\x1cstsd\0\0\0\0\0\0\0\x01\0\0\0\x0cdvh1\x01\x02\x03\x04"; + let mut buf = input.as_slice(); + + let stsd = Stsd::decode(&mut buf).unwrap(); + assert_eq!( + stsd.codecs, + vec![Codec::Unknown(FourCC::new(b"dvh1"), vec![1, 2, 3, 4])] + ); + + let mut output = Vec::new(); + stsd.encode(&mut output).unwrap(); + + assert_eq!(output, input); +}