Skip to content

feat(mp4): add the rsot box for redundant sample original timing - #589

Merged
tobbee merged 1 commit into
masterfrom
feat/rsot-box
Sep 14, 2026
Merged

tobbee merged 1 commit into
masterfrom
feat/rsot-box

Conversation

@tobbee

@tobbee tobbee commented Sep 13, 2026

Copy link
Copy Markdown
Collaborator

Adds the Redundant Sample Original Timing Box (rsot), new in ISO/IEC 14496-12:2026
§8.8.18, as a traf child.

What it is

Box Type:  'rsot'
Container: TrackFragmentBox
Quantity:  Zero or one

aligned(8) class RedundantSampleOriginalTimingBox extends FullBox('rsot', 0, flags) {
   if (flags & rsot_original_duration)  unsigned int(32) original_duration;
   if (flags & rsot_elapsed_duration)   unsigned int(32) elapsed_duration;
}

It documents two things about the edges of a track fragment:

  • elapsed_duration (flag 0x2) — the first sample of the fragment is a copy of the
    previous sample, and has already been presented for this long. A receiver that already
    has the previous sample extends that sample by this one's duration and ignores
    elapsed_duration; one that tunes in here instead presents it as if it had already run
    for elapsed_duration. When this flag is set, that first sample shall carry
    sample_depends_on = 2 and sample_has_redundancy = 1.
  • original_duration (flag 0x1) — the last sample of the fragment was truncated to
    respect fragmentation constraints, and was originally authored this long.

The spec's NOTE 1 names the case it exists for: "In adaptive streaming context where media
segments of fixed duration are not aligned with variable frame rate media such as text,
possible duplicated redundant samples may happen at segment boundaries."
That is the
subtitle-cue-across-a-chunk-boundary case — a wvtt cue or stpp document restated in the
next fragment, where the container previously had no way to say "this is a continuation,
and it really runs until T".

API

rsot := mp4.CreateRsotBox(originalDuration, elapsedDuration) // 0 means "not signalled"
rsot.HasOriginalDuration()
rsot.HasElapsedDuration()
traf.AddChild(rsot)
traf.Rsot // named link, like Tfdt and Senc

Info renders:

[rsot] size=20 version=0 flags=000003
 - originalDuration: 2000
 - elapsedDuration: 500

Two decisions worth a look

  1. CreateRsotBox derives the flags from its non-zero arguments rather than taking a
    separate flags argument. §8.8.18.3 says a signalled duration "shall be different from
    0", so zero cannot mean anything else, and this makes an inconsistent flags/field pair
    unconstructible. Both zero gives a legal 12-byte box with no flags set.

  2. Decode rejects a box whose size does not match its flags. Since both fields are
    optional and independent, the size follows exactly from the flags once they are read, so
    any other size means a malformed box. Without the check a truncated rsot decoded
    silently with a zeroed duration. This follows the clli convention
    (invalid box size %d) rather than tfhd, which does no size check.

Files

File Change
mp4/rsot.go new RsotBox with the full Box interface, DecodeRsot/DecodeRsotSR, CreateRsotBox, HasOriginalDuration, HasElapsedDuration
mp4/box.go, mp4/boxsr.go "rsot" in decoders and decodersSR
mp4/traf.go TrafBox.Rsot plus the AddChild case
mp4/rsot_test.go all four flag combinations through boxDiffAfterEncodeAndDecode, byte-exact hex goldens, changeBoxSizeAndAssertError, and a traf round trip checking the named link
CHANGELOG.md Unreleased / Added

Follows the "How to implement a new box" guide in the README. go test ./..., gofmt and
golangci-lint are clean.

🤖 Generated with Claude Code

ISO/IEC 14496-12:2026 Section 8.8.18 adds the RedundantSampleOriginalTimingBox, a traf child that documents that the first sample of a track fragment is a copy of the previous sample and how long that sample has already been presented, and that the fragment's last sample was truncated to fit the fragment and how long it was meant to last.

Its NOTE 1 names the case it is meant for: adaptive streaming where fixed-duration segments are not aligned with variable frame rate media such as text, so a cue that spans a boundary is restated as a duplicated redundant sample. With rsot_elapsed_duration set, that first sample shall carry sample_depends_on=2 and sample_has_redundancy=1, and a receiver that already has the previous sample extends it rather than presenting the copy again.

Both durations are optional and independent, so the box size follows from the flags; decode rejects any other size rather than reading as far as it goes.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
@tobbee
tobbee merged commit 32e5d30 into master Sep 14, 2026
9 checks passed
@tobbee
tobbee deleted the feat/rsot-box branch September 14, 2026 12:10
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.

1 participant