fix: preserve per-sample fields on trun encode roundtrip#160
Conversation
5892470 to
d7360f8
Compare
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: Organization UI Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
WalkthroughThe 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/moof/traf/trun.rs`:
- Around line 99-102: The current first_only_flags detection (using all_flags
and first().is_some_and(...)) is too broad and treats cases like [Some, Some,
None] as "first-only", causing later explicit flags to be dropped; change the
check to true only when the first entry has Some flags and every subsequent
entry has None (e.g., first_has = self.entries.first().map(|s|
s.flags.is_some()).unwrap_or(false) && self.entries.iter().skip(1).all(|s|
s.flags.is_none())); update usages of first_only_flags (the variable computed in
trun.rs and the encoding branches that write only the first flag vs per-entry
flags) so they rely on this stricter predicate and thus preserve any later
explicit flags.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: bbaa06aa-cb02-4ed7-a4bb-d3b003d7615f
📒 Files selected for processing (1)
src/moof/traf/trun.rs
1f35d97 to
c80d20f
Compare
| // nor sample_flags can represent this, so flags are dropped. | ||
| // This is a known limitation — callers should fill in defaults | ||
| // before encoding if they need all flags preserved. | ||
| assert_eq!(decoded.entries[0].flags, None); |
There was a problem hiding this comment.
This seems like a bug? Could we fix it?
There was a problem hiding this comment.
Good catch — fixed. The encoder now backfills None flags with 0 and emits per-sample sample_flags, so the [Some, Some, None] case roundtrips losslessly instead of silently dropping everything. Updated the test to match.
18f3e06 to
d51b778
Compare
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@src/moof/traf/trun.rs`:
- Around line 113-141: The encoder must not backfill tfhd-inherited None values
with zeroes; update trun.rs so that before encoding you detect mixed presence
for sample_duration, sample_size, and sample_flags (and ensure
first_sample_flags' entry exists) and return a Result::Err using the crate's
custom error type (defined in error.rs) when some entries have Some(...) and
others have None instead of silently writing 0. Concretely: instead of using
any(|s| s.field.is_some()) + unwrap_or(0), compute whether the field is
uniformly present (all entries have Some) or uniformly absent (all None); if
uniformly present, encode the values as now; if uniformly absent, do not mark
the field in the trun header; if mixed, return an appropriate error (e.g.
Error::TrunMixedInheritedField(...) or similar). Also ensure when
ext.first_sample_flags is set you verify entries.get(0).and_then(|e|
e.flags).is_some() else return an error rather than unwrap. Use the existing
encode(buf) call paths but only write values when the field is actually present
for all entries.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 300f6f2c-f358-4ab7-acf0-9d567362778e
📒 Files selected for processing (1)
src/moof/traf/trun.rs
The trun encoder used all-or-nothing semantics for per-sample fields (duration, size, flags, cts). If any entry had None for a field, the encoder would not write that field for any entry. After decode, entries that inherited defaults from tfhd have None, so a decode→encode roundtrip silently dropped those fields entirely. Fix: use any-or-nothing semantics. If at least one entry has a value for a field, set the trun flag and backfill None entries with 0. This fixes: - first_sample_flags being lost (entry[0] has flags, rest are None) - sample_duration being dropped (frag_every_frame produces one entry with duration, rest inherit from tfhd) - sample_size being dropped (same pattern) - sample_cts being dropped (same pattern)
d51b778 to
475f56d
Compare
|
Thanks for the PR. I haven't forgotten this, just need to get back to check what Ed. 8 says. |
bradh
left a comment
There was a problem hiding this comment.
Thanks. Sorry this took so long.
Problem
The trun encoder uses all-or-nothing semantics for per-sample fields (duration, size, flags, cts). If any entry has
Nonefor a field, the encoder does not write that field for any entry.After decode, entries that inherited defaults from
tfhd(default_sample_duration,default_sample_size,default_sample_flags) haveNonein the correspondingTrunEntryfield. A decode→encode roundtrip silently drops those fields entirely, producing invalid output.This manifests as:
first_sample_flagslost: entry[0] getsSome(keyframe_flags)fromfirst_sample_flags, entries[1..N] getNone. Encoder sees not-all-Some, drops flags for all entries. Keyframe signaling is lost.sample_durationdropped: Withfrag_every_frame, the first entry may have an explicit duration while the rest inherit from tfhd. Encoder drops duration entirely, producingduration=0in re-encoded output.sample_sizedropped: Same pattern as duration.Fix
Change from
all()toany()semantics: if at least one entry has a value for a field, set the trun flag and write the field for all entries, backfillingNoneentries with0.Special case preserved: when only entry[0] has flags and the rest are
None, usefirst_sample_flags(compact encoding matching the original).Testing
first_sample_flags_roundtrip— first-only flags pattern preservedmixed_flags_uses_per_sample— mixed Some/None flags backfilledall_flags_roundtrip— all-explicit flags unchangedduration_backfill_roundtrip— None duration backfilled to 0size_backfill_roundtrip— None size backfilled to 0all_none_fields_omitted— all-None fields still omitted (no wasted bytes)