Skip to content

fix(episode): align sampled JPEGs with decoded source frames - #732

Merged
kstonekuan merged 3 commits into
Hebbian-Robotics:mainfrom
victorwon2001:fix/issue-731
Oct 28, 2026
Merged

kstonekuan merged 3 commits into
Hebbian-Robotics:mainfrom
victorwon2001:fix/issue-731

Conversation

@victorwon2001

@victorwon2001 victorwon2001 commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Summary

Episode.frames() now labels extracted JPEGs with the source messages represented by their pixels instead of assigning later pictures to earlier sampling ticks. JPEGs for source frames missing from the decoder output are omitted without shifting subsequent sample labels.

Why

FFmpeg's default nearest-frame sampling can choose pictures recorded after a requested tick and can repeat the last decoded picture across a missing keyframe. Using fps=...:round=up, an unfiltered framecrc trace from the same decode, and the trace's actual PTS time base separates present source frames from padding; a versioned cache avoids reusing the older inaccurate JPEGs. The transform and frames_at_indices() are unchanged.

Validation

  • uv run --no-sync pytest -q tests/test_episode.py tests/test_end_to_end.py::test_canonical_episode_accessors tests/test_video.py — 57 passed.
  • uv run --no-sync ruff check --fix src/hflow/episode.py tests/test_episode.py, uv run --no-sync ruff format src/hflow/episode.py tests/test_episode.py, and uv run --no-sync ty check — passed; subsequent scoped Ruff lint and format checks passed.
  • uv run --no-sync pytest -q — 2,567 passed, eight skipped, one unrelated cache-migration failure due to the task-required XDG_CACHE_HOME; the failing case passed separately with XDG_CACHE_HOME unset and HOME redirected to an isolated task-local directory.

Verified on Linux with FFmpeg 6.1.1; the report's macOS/FFmpeg 8.1.1 environment was not available.

Checklist

  • I added or updated outcome-focused tests for changed business logic.
  • I updated documentation for changed behavior, flags, formats, or requirements.
  • I ran uv run ruff check --fix, uv run ruff format, and uv run ty check.
  • I ran the relevant pytest suite.
  • I did not add recordings, generated media, credentials, private URLs, or runtime artifacts.
  • I preserved stored-data compatibility or documented an explicit version change.

Fixes #731


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need.

@greptile-apps

greptile-apps Bot commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

RetriggerView in GreptileConfidence Score: 5/5

[Medium impact] The PR appears safe to merge; both previous findings are fixed, and no new actionable issue was established.

Summary

Episode.frames() now uses decoded timestamps to omit samples whose source pictures are missing, while keeping later sample labels in place.

  • Sampled JPEGs keep the source positions and log times of the pictures they show.

Diagram

%%{init: {'theme': 'neutral'}}%%
flowchart LR
    A[Camera MP4] --> B[Shared decode]
    B --> C[Sample JPEGs]
    B --> D[Trace decoded timestamps]
    D --> E[Recover source positions]
    C --> F[Remove samples with missing source pictures]
    E --> F
    F --> G[Publish versioned cache]
    G --> H[Return JPEGs with source message times]
Loading

Reviews (2) · Last reviewed commit: "Merge remote-tracking branch 'origin/mai..." · Reviewed by Greptile

Comment thread src/hflow/episode.py Outdated
Comment thread src/hflow/episode.py Outdated

@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.

Could you add coverage for sparse decoder output and fix the recovered source indices? I exercised _decoded_frame_indices with a 1/1000000 time base and surviving PTS values 0, 1000000, 2000000 at 30 fps: it returns {0, 1, 2}, so frames(fps=1) drops the usable pictures at source indices 30 and 60.

Could you also bound the decoded trace to the requested window? On a three-second, 5 fps clip, frames(fps=5, end_s=0.5) returns three JPEGs but writes all 15 decoded pictures to the trace.

@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.

Thanks for the frame-alignment fix. I added c1522fc for sparse timestamp recovery and bounded trace output; 12764d3 brings in main. Ruff, ty, and the full suite pass (2,556 passed), and both new regressions fail with the old behavior. Merging.

@kstonekuan
kstonekuan merged commit 8dd2a41 into Hebbian-Robotics:main Oct 28, 2026
2 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.

[Bug]: frames(fps=…) can return pictures from after their sample time, and repeats the last picture across a lost keyframe

2 participants