Skip to content

[format][python] Add packed video frame storage - #9461

Open
JingsongLi wants to merge 4 commits into
apache:masterfrom
JingsongLi:codex/video-file-format
Open

[format][python] Add packed video frame storage#9461
JingsongLi wants to merge 4 commits into
apache:masterfrom
JingsongLi:codex/video-file-format

Conversation

@JingsongLi

Copy link
Copy Markdown
Contributor

Purpose

Add a self-contained video file format for append/data-evolution tables whose logical rows represent frames while the physical decoding unit is a complete encoded video.

This avoids storing one MP4 payload per frame, avoids a sidecar object per video, and leaves the existing .blob format unchanged.

Design

  • Add the video-frame-field table option for one scalar BLOB column.
  • Add .video files with:
    • a data region containing one or more complete encoded video payloads;
    • embedded delta-varint indexes for physical video lengths, logical run lengths, run-to-video references, and first frame ordinals;
    • NULL and data-evolution placeholder run references.
  • Add VideoFrameDescriptor, extending the normal BLOB descriptor with a presentation-order frame ordinal.
  • Keep rolling aligned to physical video groups so a complete video is not split across files.
  • Compact by byte-copying complete video ranges and rebuilding the embedded indexes; compaction never decodes or re-encodes video.
  • Keep .video packs self-contained. They never reference payloads owned by another Paimon data file.
  • Restrict the initial version to append/data-evolution tables without primary keys, one scalar video field, and stride-one frame ordinals.

PyPaimon

  • Add add_video and add_videos for complete-video ingestion.
  • Add replace_video for updating the video backing rows selected by a predicate.
  • Reuse the existing table update path for ordinary frame columns without rewriting the existing .video file.
  • Reject generic update() assignments to the video field and direct callers to replace_video().
  • Reuse the existing scan and BLOB APIs. Video reads always return descriptors, including data-evolution fallback and blob-as-descriptor=false.
  • Add VideoFrameCollator for PyTorch DataLoader workers with a process-local bounded decoder cache.

Compatibility

  • The ordinary .blob format and its readers/writers are unchanged.
  • Primary-key managed BLOB storage remains separate and does not use .video.
  • Version 1 stores frame ordinals, not PTS or codec/container metadata.

Tests

  • Java targeted format/common/core tests: 79 passed.
  • Ordinary Java BLOB format regression tests: 39 passed.
  • Relevant PyPaimon video, multimodal, data-evolution, and ordinary BLOB tests: 211 passed, 3 skipped, 51 subtests passed.
  • Python compileall and git diff --check passed.
  • PyTorch-dependent integration test collection was not available locally because the optional torch dependency is not installed; the decoder/collator unit tests pass.

# retain count-only sentinels and let VideoFileMeta resolve them.
self.blob_lengths = [0] * self._video_meta.record_count
self.blob_offsets = [0] * self._video_meta.record_count
return

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We can expose record_count directly from _video_meta instead of allocating these two O(frame count) lists?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 1c0708d. FormatBlobReader now exposes record_count directly from VideoFileMeta for .video files, BlobFallbackBatchReader uses it, and the two O(frame count) sentinel lists are no longer allocated. Added a regression test for selected video rows.

return;
}
ordinal = physicalVideoLengths.size();
physicalVideoLengths.add(length);

@XiaoHongbo-Hope XiaoHongbo-Hope Aug 29, 2026

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

PayloadWriter.write() may return zero, but VideoFileMeta rejects non-positive lengths. Python reject empty payloads here.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Fixed in 99060ef. RawVideoPayloadWriter now rejects zero-length encoded video payloads, matching the Python writer and VideoFileMeta contract. Added a Java regression test.

self._ensure_process_local_cache()
single_row = isinstance(rows, Mapping)
input_rows = [rows] if single_row else list(rows)
decoded_rows = [self._decode_row(row) for row in input_rows]

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: VideoFrameCollator adds ~4.6% sequential decode overhead on droid_100, while direct .video range decoding matches raw MP4. video? We can create. a batch fast path PR as follow-up.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Agreed. I kept the current collator API unchanged and will leave batch/range decode optimization to a focused follow-up PR so this format PR stays scoped.

@XiaoHongbo-Hope

Copy link
Copy Markdown
Contributor

+1

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