From 7139232952341a0c7177ef95e554b4a0462d3a6c Mon Sep 17 00:00:00 2001 From: Alex Kirszenberg Date: Thu, 2 Apr 2026 12:38:23 +0200 Subject: [PATCH] expose duration_is_empty and default_base_is_moof flags on Tfhd These boolean flags were defined in the ext! macro but silently discarded during decoding and defaulted to false during encoding, making it impossible to set default_base_is_moof without manually patching encoded bytes. Closes #148 Co-Authored-By: Claude Opus 4.6 --- src/moof/traf/tfhd.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ src/test/av1.rs | 4 +++- src/test/bbb.rs | 2 ++ src/test/esds.rs | 1 + src/test/h264.rs | 2 ++ 5 files changed, 51 insertions(+), 1 deletion(-) diff --git a/src/moof/traf/tfhd.rs b/src/moof/traf/tfhd.rs index 4cbb369..cb3d5d3 100644 --- a/src/moof/traf/tfhd.rs +++ b/src/moof/traf/tfhd.rs @@ -23,6 +23,8 @@ pub struct Tfhd { pub default_sample_duration: Option, pub default_sample_size: Option, pub default_sample_flags: Option, + pub duration_is_empty: bool, + pub default_base_is_moof: bool, } impl AtomExt for Tfhd { @@ -65,6 +67,8 @@ impl AtomExt for Tfhd { default_sample_duration, default_sample_size, default_sample_flags, + duration_is_empty: ext.duration_is_empty, + default_base_is_moof: ext.default_base_is_moof, }) } @@ -75,6 +79,8 @@ impl AtomExt for Tfhd { default_sample_duration: self.default_sample_duration.is_some(), default_sample_size: self.default_sample_size.is_some(), default_sample_flags: self.default_sample_flags.is_some(), + duration_is_empty: self.duration_is_empty, + default_base_is_moof: self.default_base_is_moof, ..Default::default() }; @@ -102,6 +108,8 @@ mod tests { default_sample_duration: None, default_sample_size: None, default_sample_flags: None, + duration_is_empty: false, + default_base_is_moof: false, }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); @@ -120,6 +128,41 @@ mod tests { default_sample_duration: Some(512), default_sample_size: None, default_sample_flags: Some(0x1010000), + duration_is_empty: false, + default_base_is_moof: false, + }; + let mut buf = Vec::new(); + expected.encode(&mut buf).unwrap(); + + let mut buf = buf.as_ref(); + let decoded = Tfhd::decode(&mut buf).unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + fn test_tfhd_duration_is_empty() { + let expected = Tfhd { + track_id: 1, + duration_is_empty: true, + ..Default::default() + }; + let mut buf = Vec::new(); + expected.encode(&mut buf).unwrap(); + + let mut buf = buf.as_ref(); + let decoded = Tfhd::decode(&mut buf).unwrap(); + assert_eq!(decoded, expected); + } + + #[test] + fn test_tfhd_default_base_is_moof() { + let expected = Tfhd { + track_id: 1, + default_base_is_moof: true, + default_sample_duration: Some(512), + default_sample_size: Some(0), + default_sample_flags: Some(0x1010000), + ..Default::default() }; let mut buf = Vec::new(); expected.encode(&mut buf).unwrap(); diff --git a/src/test/av1.rs b/src/test/av1.rs index e947371..d56f933 100644 --- a/src/test/av1.rs +++ b/src/test/av1.rs @@ -200,7 +200,9 @@ fn av1() { sample_description_index: Some(1), default_sample_duration: Some(1000), default_sample_size: Some(252), - default_sample_flags: Some(16842752) + default_sample_flags: Some(16842752), + duration_is_empty: false, + default_base_is_moof: true, }, tfdt: Some(Tfdt { base_media_decode_time: 0 diff --git a/src/test/bbb.rs b/src/test/bbb.rs index b0a62da..b0c6fcc 100644 --- a/src/test/bbb.rs +++ b/src/test/bbb.rs @@ -226,6 +226,7 @@ fn bbb() { default_sample_duration: 1000.into(), default_sample_flags: 0x1010000.into(), default_sample_size: 215.into(), + default_base_is_moof: true, ..Default::default() }, tfdt: Some(Tfdt { @@ -287,6 +288,7 @@ fn bbb() { default_sample_duration: 1024.into(), default_sample_flags: 0x2000000.into(), default_sample_size: 9.into(), + default_base_is_moof: true, ..Default::default() }, tfdt: Some(Tfdt { diff --git a/src/test/esds.rs b/src/test/esds.rs index 2496a9a..0af4448 100644 --- a/src/test/esds.rs +++ b/src/test/esds.rs @@ -219,6 +219,7 @@ fn esds() { default_sample_duration: 512.into(), default_sample_flags: 0x1010000.into(), default_sample_size: 3377.into(), + default_base_is_moof: true, ..Default::default() }, tfdt: Some(Tfdt { diff --git a/src/test/h264.rs b/src/test/h264.rs index 66409d1..0ba4409 100644 --- a/src/test/h264.rs +++ b/src/test/h264.rs @@ -396,6 +396,8 @@ fn avc_encrypted_segment() { default_sample_duration: None, default_sample_size: None, default_sample_flags: None, + duration_is_empty: false, + default_base_is_moof: true, }, tfdt: Some(Tfdt { base_media_decode_time: 342202323002237