Skip to content

fix(video): stream canonical remux with bounded memory - #581

Merged
kstonekuan merged 2 commits into
Hebbian-Robotics:mainfrom
Kaileshwar16:fix/canonical-video-remux-memory
Sep 21, 2026
Merged

kstonekuan merged 2 commits into
Hebbian-Robotics:mainfrom
Kaileshwar16:fix/canonical-video-remux-memory

Conversation

@Kaileshwar16

Copy link
Copy Markdown
Contributor

Canonical video remuxing previously retained the entire camera channel and joined all
H.264 payloads before invoking ffmpeg, creating multiple full-video allocations.

Use two batch-reading passes: estimate FPS with an exact disk-backed median, then stream
decoded access units into ffmpeg stdin. Preserve H.264/B-frame validation and atomic
output publication, with subprocess and temporary-file cleanup on failures.

Add regressions for incremental consumption, remuxing without channel materialization,
validation failures, broken pipes, and preservation of existing outputs.

Validation:

  • 66 directly affected tests passed on the current branch.
  • Broader tests stopped during collection because the environment lacks httpx.
  • Before rebasing onto latest upstream, the full suite passed: 2,206 passed, 9 skipped;
    Ruff and type checks also passed.
  • Streaming 16 MiB and 512 MiB produced identical traced Python memory peaks of 1,053,657
    bytes using a subprocess sink.

The change requires two reads and temporary disk storage. Memory measurements cover HFlow
allocations, not ffmpeg’s internal muxer metadata.

@greptile-apps

greptile-apps Bot commented Sep 21, 2026

Copy link
Copy Markdown

RetriggerConfidence Score: 5/5

The PR appears safe to merge; the previous cache-rescan concern is resolved and no new actionable failures were identified.

Summary

This PR changes canonical H.264 remuxing from whole-channel materialization to a bounded-memory, two-pass streaming flow.

  • Estimates frame rate using a disk-backed exact median of timestamp intervals.
  • Streams decoded access units directly into ffmpeg while incrementally validating picture coding types.
  • Preserves atomic output publication and cleans up subprocesses and temporary files on failures.
  • Adds coverage for incremental consumption, cache reuse, validation failures, broken pipes, and output preservation.
  • The change since the previous review adds a safe cache fast path when both the frame rate and completed MP4 are already available.
Diagram
%%{init: {'theme': 'neutral'}}%%
flowchart TD
    A[Episode.video] --> B{FPS cached and MP4 exists?}
    B -- Yes --> C[Return cached MP4]
    B -- No --> D[First batch-reading pass]
    D --> E[Store timestamp intervals in temporary SQLite index]
    E --> F[Calculate exact median and FPS]
    F --> G{Completed MP4 exists?}
    G -- Yes --> C
    G -- No --> H[Second batch-reading pass]
    H --> I[Decode and validate H.264 access units]
    I --> J[Incrementally scan picture coding types]
    I --> K[Stream units to ffmpeg stdin]
    J --> L{Validation and ffmpeg succeed?}
    K --> L
    L -- Yes --> M[Atomically replace final MP4]
    L -- No --> N[Reap subprocess and remove temporary output]
Loading

Reviews (2) · Last reviewed commit: "fix(video): avoid rescanning cached remu..."

Comment thread src/hflow/episode.py

@kstonekuan kstonekuan left a comment

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.

Both halves of #580, including the cache-hit one, and iter_decoded_batches is the right accessor to land on since it is already the documented bounded-memory path.

The SQLite median is the part I wanted to be sure about, because a remux silently changing its declared frame rate is the kind of drift nobody notices for months. Ran it differentially against estimate_fps_from_log_times across both median parities, uniform and jittered streams, streams with a long stall, and the degenerate inputs: exact agreement on all ten, and all three error contracts match word for word.

Guards hold. Removing the cache-hit early return reddens 1, dropping the h264 check reddens 1, and replacing the median with the first interval reddens 2.

Guarding the early return on topic in self._video_fps as well as output.exists() is the detail that makes it correct rather than just fast, since _video_fps is a side effect of the old ordering.

2300 passed on the merge with main.

@kstonekuan
kstonekuan merged commit c929c6b into Hebbian-Robotics:main Sep 21, 2026
7 checks passed
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