Skip to content

feat(mp4): resolve overlapping fragments during defragmentation - #560

Draft
nchitkara-xai wants to merge 2 commits into
Eyevinn:masterfrom
nchitkara-xai:defragment-overlaps
Draft

feat(mp4): resolve overlapping fragments during defragmentation#560
nchitkara-xai wants to merge 2 commits into
Eyevinn:masterfrom
nchitkara-xai:defragment-overlaps

Conversation

@nchitkara-xai

Copy link
Copy Markdown

Stacked on #557 — the first commit is #557; please review the last commit only.

Problem

Fragmented files from segmented capture or upload pipelines can re-declare
time ranges: a fragment is re-sent after a partial write, or a restart
re-declares part of the previous timeline with a backward tfdt. #557 rejects
such files outright, which makes the converter useless for exactly the files
that most need converting to a clean progressive form.

Fix

Overlap resolution as the default behavior, with no new API surface: a
fragment appearing later in the file whose tfdt re-declares an earlier decode
time wins (retransmission semantics). Whole re-sent fragments disappear,
superseded tails are trimmed at sample granularity, and the timeline
continues from the surviving declaration.

The guarantee that keeps this fail-closed in spirit: no declared time range
is ever silently dropped
. Every abandoned range must be re-declared by
surviving fragments — a shorter resend, a transitively voided declaration, or
wrong-order concatenation (corruption rather than retransmission) all reject
with clear errors. Cuts landing inside a sample reject too, unless they only
shrink duration padding that an earlier tfdt gap introduced. Inputs that #557
already accepted convert byte-identically, since resolution only activates on
backward tfdts.

The declared-payload bound from #557 now applies after resolution, so a
legitimately re-sent fragment does not count twice against the input size.

Tests

Full resend, superseded tail, reset chains (covered resolves, shortening
rejects), byte-identical resend, multi-track with one overlapping track
(other track byte-verbatim), gap-padding shrink, mid-sample cuts, abandoned
ranges, wrong-order concatenation, and the payload-bound interaction. All
fragmented testdata files convert byte-identically to #557 output. go test ./..., go vet, gofmt, golangci-lint pass.

Add mp4.Defragment and mp4.DefragmentTracks, which convert a fragmented
file to a progressive one: sample tables (stts, ctts, stsc, stsz, stss,
stco/co64) are synthesized from the fragment metadata while payload
bytes, sample descriptions, and fragment-declared timing are preserved.
Every track is rebased so that its first tfdt becomes media time zero:
edit-list media times shift by the same origin, and a track starting
later than the earliest one keeps its presentation alignment through an
empty edit (merged into an existing leading one, prepended, or as a
synthesized edit list), with at most half a movie-timescale tick of
rounding. Encrypted fragments (senc or traf-level saiz/saio), backward
tfdt jumps, edits that cannot be shifted exactly, truncated byte
ranges, declared payloads exceeding the input file size, and kept
tracks without any samples (whose empty sample tables would classify
the output as fragmented again) fail closed. The new mp4ff-defragment
tool exposes the conversion with optional track selection.

Files with an initial progressive part before the first fragment are
rejected for now; support may come as a follow-up.
A fragment whose tfdt re-declares an earlier decode time supersedes the
earlier samples of its track (a retransmission): the fragment appearing
later in the file wins, whole re-sent fragments disappear, superseded
tails are trimmed at sample granularity, and the timeline continues
from the declaring fragment, whether or not the re-sent bytes are
identical. Non-overlapping input converts byte-identically to before.

Ambiguous overlaps keep failing closed instead of being guessed at: an
overlap that starts inside a sample (unless it only shrinks earlier
tfdt-gap padding), any abandoned time range that no surviving later
fragment declares again (whether the superseded fragment was trimmed or
dropped whole, and with voided declarations not counting as coverage),
and overlapping files whose fragments use absolute base data offsets
are rejected, so no declared content is ever silently dropped. The
payload size bound now applies to the surviving samples, so re-sent
declarations larger than the input file no longer reject a file that
resolves cleanly.
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