Skip to content

feat(mp4): convert fragmented files to progressive (defragmentation) - #557

Draft
nchitkara-xai wants to merge 1 commit into
Eyevinn:masterfrom
nchitkara-xai:defragment-core
Draft

feat(mp4): convert fragmented files to progressive (defragmentation)#557
nchitkara-xai wants to merge 1 commit into
Eyevinn:masterfrom
nchitkara-xai:defragment-core

Conversation

@nchitkara-xai

Copy link
Copy Markdown

Implements #548, following the approach discussed there.

Problem

There is no way to convert a fragmented file to a progressive one (#162,
#311, #548): the moov of the progressive file needs the metadata of every
sample, so all moofs must be read before anything can be written.

Fix

mp4.Defragment(f *File, rs io.ReadSeeker, w io.Writer) and
mp4.DefragmentTracks(f, rs, w, trackIDs...), plus a thin
cmd/mp4ff-defragment CLI (cloned from the mp4ff-crop skeleton, -t for
track selection). Two passes: sample metadata is collected from all moofs
(lazy mdat, payloads never materialized), the progressive sample tables
(stts/ctts/stsc/stsz/stss/stco/co64) are synthesized, the moov is written,
then the sample byte ranges are copied verbatim. Each traf becomes one chunk,
so the input interleaving is kept.

Timeline handling as discussed in #548: each track is rebased by its own
first tfdt, existing elst media times shift by the same amount, and differing
start offsets between tracks are absorbed with an empty edit in movie
timescale on the later track (merged into its elst or synthesized), worst
case half a movie tick of rounding. A forward tfdt gap extends the previous
sample; a backward tfdt is an error.

Fail-closed rather than silently wrong: rate != 1 and other odd edits,
encrypted tracks (senc, or saiz/saio without senc), kept tracks without
samples (init-only input), declared payload exceeding the input size, and
files that already carry progressive samples are all rejected with clear
errors — track selection doubles as the escape hatch for a single bad track.

Kept for follow-ups to stay small here: progressive-prefix (hybrid) inputs,
resolving overlapping fragments, and multi-file import (one file series per
track) as sketched in #548.

Tests

Exact elst outputs are pinned for differing origins with no/existing/leading
empty edits and for equal origins; conversion round-trips are verified
sample-by-sample against re-decoded output; all fragmented testdata files
(AAC, Opus, VVC, multi-segment, decrypted cbcs) convert and re-decode as
progressive; error paths cover encryption, backward tfdt, truncation,
amplification, hybrid input, and sample-less tracks. 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.
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