Recorder: write seek index and track durations into fMP4 segments - #6014
Open
Auburn wants to merge 5 commits into
Open
Recorder: write seek index and track durations into fMP4 segments#6014Auburn wants to merge 5 commits into
Auburn wants to merge 5 commits into
Conversation
Write one sidx box per track at the start of each segment file, with references grouped so that each one begins at a sync sample of the video track, and patch mvhd/tkhd/mdhd durations when the segment closes. This lets players report the full duration of long recordings immediately and seek without scanning the whole file. Space for the index is reserved with a free box when the segment is created; recordings interrupted by a crash keep the placeholder and remain readable.
Author
|
This code was written with the help of Claude Fable |
There was a problem hiding this comment.
Pull request overview
This PR improves fMP4 recording usability by writing a per-track segment index (sidx) into each recorded segment and patching track/movie durations in the init header, so players can report full duration and seek without scanning the entire file.
Changes:
- Reserve space on segment creation and overwrite it on clean close with one version-1
sidxbox per track (with sync-sample-aligned references and coalescing when needed). - Patch
mvhd/tkhd/mdhddurations on segment close (v0/v1 aware, clamped for v0). - Update the playback segment scanner to skip the seek index (or placeholder) when walking
moof/mdatpairs.
Reviewed changes
Copilot reviewed 5 out of 5 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| internal/recorder/format_fmp4_segment.go | Adds duration patch helpers; reserves/writes seek index on segment lifecycle; records per-part sizes for indexing. |
| internal/recorder/format_fmp4_seekindex.go | New seek-index implementation that reserves placeholder space and writes per-track sidx boxes on close. |
| internal/recorder/format_fmp4_seekindex_test.go | New tests validating written index placement/fields and merge/coalesce behavior. |
| internal/recorder/format_fmp4_part.go | Returns the number of bytes written for each part, enabling accurate referenced-size accounting. |
| internal/playback/segment_fmp4.go | Skips free/sidx boxes while scanning parts to determine duration. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
…arge memory usage with crazy segment/fragment length configs
There was a problem hiding this comment.
Pull request overview
Copilot reviewed 5 out of 5 changed files in this pull request and generated no new comments.
Comments suppressed due to low confidence (1)
internal/recorder/format_fmp4_seekindex.go:276
SubsegmentDurationinsidxis a 32-bit field;uint32(starts[i+1]-starts[i])can overflow for long segments (segment duration is allowed up to 24h in config) if references get coalesced into large groups (e.g., very sparse sync samples). This would silently write a broken index. Consider bailing out (like the referenced_size overflow check) when a reference duration exceedsmath.MaxUint32.
for i, e := range entries {
ref := amp4.SidxReference{
ReferencedSize: uint32(e.size),
SubsegmentDuration: uint32(starts[i+1] - starts[i]),
}
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
fMP4 recordings are written as a plain sequence of moof/mdat pairs. On segment close only the mvhd duration is patched; tkhd and mdhd stay at zero and there is no index, so the timing and location of every fragment exist only in the fragment headers spread across the whole file. Players therefore cannot report the duration of a recording without reading it end to end, and cannot seek without scanning. In practice a browser given a multi-hour recording shows a timeline of a few minutes that slowly grows as the download progresses, making it hard to scrub through until the video is fully indexed. Both Chrome and Firefox behave this way.
When opening the video file with VLC or MPC-HC there is a 10 second delay before the video starts playing as it indexes the segments. If the file is on a network share this takes even longer and seems to eventually give up and caps the video duration at 1 hour instead of the full length. FFmpeg-based tools have the same problem.
Change
freebox is written between the header and the first fragment, reserving room for one sidx per track withsegmentDuration / partDuration + 2references (capped at the 16-bit reference count).sidxbox per track, and the mvhd/tkhd/mdhd durations are patched (version 0/1 aware, clamped for v0).Design points, driven by how FFmpeg-based players consume the index:
freebox placed before the sidx boxes, so the last sidx ends exactly where the first moof starts: players treat the index as complete only when the byte ranges it references begin right after it and extend to the end of the file.Edge cases
freeplaceholder and remains readable exactly as before, the index is only ever written on a clean close.referenced_size, no index is written rather than a broken one.Testing