Skip to content

Recorder: write seek index and track durations into fMP4 segments - #6014

Open
Auburn wants to merge 5 commits into
bluenviron:mainfrom
Auburn:main
Open

Recorder: write seek index and track durations into fMP4 segments#6014
Auburn wants to merge 5 commits into
bluenviron:mainfrom
Auburn:main

Conversation

@Auburn

@Auburn Auburn commented Jul 30, 2026

Copy link
Copy Markdown

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

  • When a segment file is created, a free box is written between the header and the first fragment, reserving room for one sidx per track with segmentDuration / partDuration + 2 references (capped at the 16-bit reference count).
  • When the segment closes, the placeholder is overwritten with one version-1 sidx box per track, and the mvhd/tkhd/mdhd durations are patched (version 0/1 aware, clamped for v0).
  • The playback server skips the index or its placeholder when scanning segments.

Design points, driven by how FFmpeg-based players consume the index:

  • One sidx per track, because such players only seek through the index on streams that a sidx references.
  • References are grouped to begin at a sync sample of the video track: on a seek, players jump to the start of the reference containing the target and can only start decoding from a sync sample. Otherwise they fall back to scanning from the beginning.
  • Unused reserved space becomes a free box 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.
  • Reference start times are taken from the DTS of the first sample so they match the tfdt of the corresponding moof, references with no samples of a track inherit the start of the following one.

Edge cases

  • A recording interrupted by a crash keeps the free placeholder and remains readable exactly as before, the index is only ever written on a clean close.
  • If a segment ends up with more parts than the reservation allows for, entries are coalesced to fit.
  • If a reference would overflow the 31-bit referenced_size, no index is written rather than a broken one.
  • The reservation costs 40 bytes plus 12 per reference per track . Under 100 KB for a one-hour segment with one-second parts and two tracks.

Testing

  • Unit tests cover the written index (placement, per-track boxes, sync-sample grouping, reference sizes and durations) and the merge and coalescing behaviour.
  • A multi-hour recording now opens in browser with its full duration shown immediately and seeks land directly, where it previously reported a creeping duration and scanned on every seek.
  • VLC and MPC-HC also open the video instantly and report the full video duration even when the file is on a network share.

Auburn and others added 3 commits July 29, 2026 15:26
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.
Copilot AI review requested due to automatic review settings July 30, 2026 10:10
@Auburn

Auburn commented Jul 30, 2026

Copy link
Copy Markdown
Author

This code was written with the help of Claude Fable

Copilot AI 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.

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 sidx box per track (with sync-sample-aligned references and coalescing when needed).
  • Patch mvhd/tkhd/mdhd durations on segment close (v0/v1 aware, clamped for v0).
  • Update the playback segment scanner to skip the seek index (or placeholder) when walking moof/mdat pairs.

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.

Comment thread internal/recorder/format_fmp4_seekindex.go
Comment thread internal/recorder/format_fmp4_seekindex_test.go
Copilot AI review requested due to automatic review settings July 30, 2026 10:42

Copilot AI 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.

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

  • SubsegmentDuration in sidx is 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 exceeds math.MaxUint32.
		for i, e := range entries {
			ref := amp4.SidxReference{
				ReferencedSize:     uint32(e.size),
				SubsegmentDuration: uint32(starts[i+1] - starts[i]),
			}

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