[format][python] Add video keyframe index format for efficient random reads - #9831
Draft
XiaoHongbo-Hope wants to merge 11 commits into
Draft
XiaoHongbo-Hope wants to merge 11 commits into
XiaoHongbo-Hope wants to merge 11 commits into
Conversation
XiaoHongbo-Hope
force-pushed
the
codex/video-frame-mapping-index
branch
4 times, most recently
from
September 19, 2026 09:15
d8fffbd to
12e689e
Compare
XiaoHongbo-Hope
force-pushed
the
codex/video-frame-mapping-index
branch
8 times, most recently
from
September 19, 2026 10:20
0db216c to
405600d
Compare
XiaoHongbo-Hope
force-pushed
the
codex/video-frame-mapping-index
branch
2 times, most recently
from
September 19, 2026 10:30
75ba154 to
a8b3b9f
Compare
XiaoHongbo-Hope
force-pushed
the
codex/video-frame-mapping-index
branch
from
September 19, 2026 11:00
a8b3b9f to
9f115c2
Compare
XiaoHongbo-Hope
marked this pull request as ready for review
September 19, 2026 13:26
JingsongLi
reviewed
Sep 20, 2026
| descriptor = frame.keyframe_index_descriptor | ||
| if descriptor is None: | ||
| return b'' | ||
| mapping = Blob.from_descriptor(blob.uri_reader, descriptor).to_data() |
Contributor
There was a problem hiding this comment.
[P1] Avoid materializing unbounded keyframe indexes
This materializes the entire compressed index before the chunked validator runs, and _keyframe_indexes retains every block until the writer closes. BlobRef.to_data() issues a single read for the descriptor length, while rolling is checked only after add_element; therefore a valid or crafted large index can allocate beyond the worker heap despite the 64 KiB decompression chunks. The Java path has the same behavior in VideoFormatWriter.java. Please validate from a bounded stream into spillable storage, or enforce explicit per-index and cumulative limits, instead of materializing unbounded bytes.
XiaoHongbo-Hope
marked this pull request as draft
September 20, 2026 02:21
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.
Purpose
Persist optional sparse video seek indexes in
.video, so a cold random reader can fetch the video metadata and needed GOP range instead of probing or scanning the video. This PR establishes the Java/Python format and rewrite path; index generation and reader integration follow separately.Design
.video (in file order) |-- Complete video payloads A, B, ... |-- NEW: Video seek index blocks A, B, ... | |-- Video metadata ranges: (offset, length) pairs | `-- Compressed keyframe entries: (frame ordinal, PTS, packet byte position) |-- Video payload lengths |-- NEW: Video seek index block lengths |-- Consecutive-row counts / video references / starting frame ordinals `-- Footer |-- Five index byte lengths: 5 x 4 bytes |-- Magic: 4 bytes `-- Format version: 1 byteEach video has one block-length entry; zero means no persisted seek index. All seek-index offsets are relative to the first byte of that encoded video.
moovbox containing track and timing informationThe keyframe-entry list is compressed as one block. A cold random read fetches the video metadata, finds the preceding keyframe by ordinal, reads an adjacent GOP range from its packet byte position, then seeks to its PTS and decodes forward. Readers may extend the range through the following GOP for reordered frames. GOP index and in-GOP frame index are derived from the target ordinal and preceding keyframe, so they are not stored separately. This keeps seek metadata proportional to keyframes rather than rows.
Payload length is already available from the video descriptor. The index describes the first video stream; its time base remains in the encoded video.
Current open-source Lance comparison
The current open-source LeRobot-Lance implementation stores each source MP4 and its seek metadata as one row in
videos.lance:moov_offset,moov_sizekf_indiceskf_positionsPaimon stores this metadata in the same
.videofile and compresses the keyframe entries.Reader follow-up
A separate PR will integrate index generation and consumption into
PaimonLeRobotDataset/PyAV. It will group requests by payload, merge byte ranges, cache container data and decoders, and select the prefetch size at runtime. The stored metadata supports low-request object-store reads without requiring a fixed 64 KiB over-read in every environment.Reference
TorchCodec frame mappings also motivated the persisted positioning metadata.
Validation
moov; six random frames matched full PyAV decoding after the index was written to and read from.video.git diff --checkpassed.