Skip to content

udta: dispatch children by namespace - #222

Merged
bradh merged 3 commits into
kixelated:mainfrom
AdrianEddy:udta-namespace
Jul 28, 2026
Merged

udta: dispatch children by namespace#222
bradh merged 3 commits into
kixelated:mainfrom
AdrianEddy:udta-namespace

Conversation

@AdrianEddy

@AdrianEddy AdrianEddy commented Jul 22, 2026

Copy link
Copy Markdown
Contributor

udta is a free-form user-data container whose children are context-specific. QuickTime writes a track-name name box in a track's udta, and its fourcc collides with the iTunes ilst Name item in the global atom table; routing udta children through that table misparses the QuickTime name (its string decoder stops at the first NUL of an empty name) and fails the moov with UnderDecode(name).

Udta::decode_body now dispatches children by header against the udta namespace only (cprt, kind, meta, rtng; free/skip treated as padding; everything else via decode_unknown), the same shape as Ilst::decode_body from #172. A child whose declared size exceeds what remains is rejected as truncated.

Review follow-up (breaking): child multiplicity now follows the specs — cprt and kind are quantity "zero or more" (ISO/IEC 14496-12 §8.10.2 / §8.10.4: one copyright notice per language, one kind box per role value) and rtng may repeat per language (3GPP TS 26.244 clause 8), so those Udta fields changed from Option<T> to Vec<T> and repeats accumulate. meta stays "zero or one" (§8.11.1): a second one is rejected with Error::DuplicateBox. The old nested! decode rejected repeats of all four, failing whole moovs over spec-valid files such as bilingual copyright notices.

Split out of the trailing-padding PR (#173) and stacked on it — the diff shows the trailing-padding commit until #173 merges.

QuickTime muxers append a few bytes of padding — commonly a 4-byte zero
terminator — after the child boxes of a container atom or sample entry
(e.g. `avc1 → avcC + colr + 00000000`). The remainder is shorter than an
8-byte box header, so it cannot be a box; ffmpeg, GPAC and others skip it.
The strict remaining-bytes check otherwise fails the whole `moov` with
`UnderDecode`.

`skip_trailing_padding` drains a sub-header (`< 8` byte) remainder — 8+ zero
bytes already decode as a null-fourcc box, so only a 1–7 byte remainder is
affected — and is now called after the child-box loop everywhere one occurs:
the `nested!` container macro (moov, trak, mdia, minf, stbl, dinf, edts, mvex,
moof, traf, iprp, udta), the hand-written `meta` and `mfra` containers, and
every sample entry. A remainder of 8+ bytes is left untouched so genuine
trailing corruption is still reported.
`udta` is a free-form user-data container whose children are context-specific.
QuickTime writes a track-name `name` box in a track's `udta`, and its fourcc
collides with the iTunes `ilst` Name item in the global atom table; routing
`udta` children through that table misparses the QuickTime `name` (its string
decoder stops at the first NUL of an empty name) and fails the whole `moov`
with `UnderDecode(name)`.

`Udta::decode_body` now dispatches children by header against the udta
namespace only (`cprt`, `kind`, `meta`, `rtng`; `free`/`skip` dropped as
padding; everything else via `decode_unknown`), the same shape as
`Ilst::decode_body`. A child whose declared size exceeds what remains is
rejected as truncated.
@coderabbitai

coderabbitai Bot commented Jul 22, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@AdrianEddy, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 55 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: 8c3f4001-51fb-4f16-a841-542bdac68f71

📥 Commits

Reviewing files that changed from the base of the PR and between dbeae0e and 09daed6.

📒 Files selected for processing (1)
  • src/moov/udta/mod.rs

Walkthrough

The decoder gains shared handling for 1–7 trailing padding bytes after child atoms. Nested containers, metadata, movie fragments, and multiple audio/video/text sample entries now invoke this handling. Udta uses explicit child dispatch with size validation, known-field encoding, padding-box omission, and unknown-box routing. Tests cover trailing padding for metadata, editing lists, Avc1, and Udta, including rejection of a QuickTime track-name box.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 59.38% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
Title check ✅ Passed The title is concise and accurately summarizes the main change to udta child dispatch.
Description check ✅ Passed The description clearly matches the changeset and explains the namespace dispatch and multiplicity updates.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

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.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@bradh

bradh commented Jul 23, 2026

Copy link
Copy Markdown
Collaborator

@CodeRabbit review

@coderabbitai

coderabbitai Bot commented Jul 23, 2026

Copy link
Copy Markdown
✅ Action performed

Review finished.

Note: CodeRabbit is an incremental review system and does not re-review already reviewed commits. This command is applicable only when automatic reviews are paused.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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/moov/udta/mod.rs`:
- Around line 44-47: Update the known-child handling in the udta nested decoder
to reject a second Cprt, Kind, Meta, or Rtng when its corresponding option is
already populated, returning the existing Error::DuplicateBox instead of
overwriting it. Preserve normal first-child decoding and add a regression test
covering duplicate known children and the expected error.
🪄 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 Plus

Run ID: d91f962a-0874-439f-a90f-bdb540778228

📥 Commits

Reviewing files that changed from the base of the PR and between 7e113de and e305f08.

📒 Files selected for processing (21)
  • src/atom.rs
  • src/buf.rs
  • src/meta/mod.rs
  • src/mfra/mod.rs
  • src/moov/trak/edts/mod.rs
  • src/moov/trak/mdia/minf/stbl/stsd/ac3.rs
  • src/moov/trak/mdia/minf/stbl/stsd/amr/samr.rs
  • src/moov/trak/mdia/minf/stbl/stsd/av01.rs
  • src/moov/trak/mdia/minf/stbl/stsd/eac3.rs
  • src/moov/trak/mdia/minf/stbl/stsd/flac.rs
  • src/moov/trak/mdia/minf/stbl/stsd/h264/avc1.rs
  • src/moov/trak/mdia/minf/stbl/stsd/hevc/hev1.rs
  • src/moov/trak/mdia/minf/stbl/stsd/hevc/hvc1.rs
  • src/moov/trak/mdia/minf/stbl/stsd/mp4a/mod.rs
  • src/moov/trak/mdia/minf/stbl/stsd/opus.rs
  • src/moov/trak/mdia/minf/stbl/stsd/tx3g.rs
  • src/moov/trak/mdia/minf/stbl/stsd/uncv.rs
  • src/moov/trak/mdia/minf/stbl/stsd/vp9/vp08.rs
  • src/moov/trak/mdia/minf/stbl/stsd/vp9/vp09.rs
  • src/moov/trak/mdia/minf/stbl/stsd/wvtt.rs
  • src/moov/udta/mod.rs

Comment thread src/moov/udta/mod.rs Outdated
Comment on lines +44 to +47
Cprt::KIND => cprt = Some(Cprt::decode_atom(&header, buf)?),
Kind::KIND => kind = Some(Kind::decode_atom(&header, buf)?),
Meta::KIND => meta = Some(Meta::decode_atom(&header, buf)?),
Rtng::KIND => rtng = Some(Rtng::decode_atom(&header, buf)?),

@coderabbitai coderabbitai Bot Jul 23, 2026

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Preserve duplicate-child rejection.

nested! previously returned Error::DuplicateBox for repeated known children. These assignments silently overwrite the first atom, so decoding and re-encoding a malformed udta loses data. Reject duplicates for cprt, kind, meta, and rtng before assignment, and add a regression test.

🤖 Prompt for 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.

In `@src/moov/udta/mod.rs` around lines 44 - 47, Update the known-child handling
in the udta nested decoder to reject a second Cprt, Kind, Meta, or Rtng when its
corresponding option is already populated, returning the existing
Error::DuplicateBox instead of overwriting it. Preserve normal first-child
decoding and add a regression test covering duplicate known children and the
expected error.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AdrianEddy I think checking for duplicates (i.e. that each of cprt, kind, meta and rtng is None before assigning) is a valid suggestion. Your view?

@AdrianEddy AdrianEddy Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch that the rewrite dropped the nested! duplicate check — but restoring DuplicateBox for all four children would reject spec-valid files. The specs disagree with the old behavior for three of the four boxes:

  • cprt — ISO/IEC 14496-12 §8.10.2, quantity zero or more: "There may be multiple copyright boxes using different language codes" (that's why the box carries a language field).
  • kind — ISO/IEC 14496-12 §8.10.4, quantity zero or more: a track carrying several roles gets one KindBox per role value, and multiple schemes may each contribute boxes (DASH role signaling relies on this).
  • rtng — 3GPP TS 26.244 clause 8 (asset information): the udta may hold "zero or more sub-boxes of each kind, zero or one for each language".
  • meta — ISO/IEC 14496-12 §8.11.1, quantity zero or one per container: a duplicate here genuinely is malformed.

So the old nested! behavior was already wrong — it failed a whole moov over e.g. a bilingual copyright notice, which is exactly the class of over-strictness this PR is about, and keep-last silently dropped data.

Fixed in 09daed6: cprt/kind/rtng are now Vec fields that accumulate repeats (encode iterates them), while a second meta is rejected with Error::DuplicateBox. Regression tests cover both directions: repeated children round-trip losslessly, duplicate meta errors. Note this is a breaking change to the Udta field types (Option<T>Vec<T>).

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Seems like the humans are having a chat. I'll hop back into my burrow for now. If you need me again, just tag @coderabbitai in a new comment, and I'll come hopping out!

@bradh bradh left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Otherwise LGTM.

Comment thread src/moov/udta/mod.rs Outdated
Comment on lines +44 to +47
Cprt::KIND => cprt = Some(Cprt::decode_atom(&header, buf)?),
Kind::KIND => kind = Some(Kind::decode_atom(&header, buf)?),
Meta::KIND => meta = Some(Meta::decode_atom(&header, buf)?),
Rtng::KIND => rtng = Some(Rtng::decode_atom(&header, buf)?),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@AdrianEddy I think checking for duplicates (i.e. that each of cprt, kind, meta and rtng is None before assigning) is a valid suggestion. Your view?

CopyrightBox and KindBox are quantity "zero or more" (ISO/IEC 14496-12
§8.10.2 and §8.10.4 — one copyright notice per language, one kind box
per role value), and 3GPP TS 26.244 clause 8 admits "zero or more
sub-boxes of each kind, zero or one for each language" of the asset
boxes, rtng included. The previous nested!-based decode rejected such
spec-valid repeats with DuplicateBox, and the first namespace-dispatch
revision silently kept only the last one; store them as Vec instead.

MetaBox stays quantity "zero or one" per container (§8.11.1), so a
second meta is rejected with DuplicateBox rather than overwritten.
@bradh
bradh merged commit a307827 into kixelated:main Jul 28, 2026
1 check passed
@bradh

bradh commented Jul 28, 2026

Copy link
Copy Markdown
Collaborator

Thanks.

@github-actions github-actions Bot mentioned this pull request Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants