Skip to content

refactor(ffmpeg): extract seek/prefetch decisions as pure predicates + tests#2014

Merged
yuto-trd merged 4 commits into
mainfrom
yuto-trd/ffmpeg-seek-eof-predicates
Jun 25, 2026
Merged

refactor(ffmpeg): extract seek/prefetch decisions as pure predicates + tests#2014
yuto-trd merged 4 commits into
mainfrom
yuto-trd/ffmpeg-seek-eof-predicates

Conversation

@yuto-trd

@yuto-trd yuto-trd commented Jun 25, 2026

Copy link
Copy Markdown
Member

Description

Extracts the reseek/prefetch branch conditions in the FFmpeg direct-call decoder into a small pure helper so they can be unit-tested without spinning up FFmpeg, and adds the seek-past-EOF regression coverage requested as a PR #1985 follow-up.

  • New FFmpegSeekDecision (in Beutl.Extensions.FFmpeg/Decoding, namespace-switched to Beutl.FFmpegWorker.Decoding under BEUTL_FFMPEG_WORKER): ShouldReseek(bool currentUsable, long skip) and HasPrefetchTarget(...) capture the exact inline conditions from FFmpegReader.ReadVideoCore / VideoRingBuffer.
  • FFmpegReader.cs and VideoRingBuffer.cs now call the helper; behavior is preserved (a baseFrame < 0 invariant note added where the predicate is shared).
  • Build: Beutl.FFmpegWorker.csproj and Beutl.FFmpegBenchmarks.csproj (which source-links FFmpegReader.cs directly) both source-link the new file.

Affected areas

  • Beutl.FFmpegIpc / Beutl.FFmpegWorker (media IPC boundary)
  • Build / CI / docs only (benchmark source-link)

Breaking changes

None — behavior-preserving extraction.

Test plan

  • tests/Beutl.UnitTests/Extensions/FFmpeg/FFmpegSeekDecisionTests.cs (new, 17 table-driven cases) covering the reseek thresholds (currentUsable, large/negative skip, MaxSequentialSkip boundary) and prefetch-target selection.
  • Green in the full-solution integration build + test alongside the other FFmpeg branches.

Fixed issues / References

Summary by CodeRabbit

  • New Features
    • Enhanced video seeking and prefetching logic for smoother playback.
  • Bug Fixes
    • Reduced unnecessary re-seeks when the current frame can be reused.
    • Improved behavior for backward skips and large forward jumps.
    • Prevented prefetching beyond the end of available frames.
  • Tests
    • Added unit test coverage for the seek/reseeking and prefetch target decision logic.

yuto-trd added 3 commits June 24, 2026 16:37
…+ tests

Hoist two FFmpeg worker failure-path decisions into a pure, native-free
helper so they get direct NUnit coverage without spawning the worker or
loading FFmpeg natives, following the existing FFmpegFrameValidation
dual-compile pattern (#if BEUTL_FFMPEG_WORKER namespace switch +
<Compile Include Link> into the GPL worker csproj):

- FFmpegSeekDecision.ShouldReseek(currentUsable, skip): mirrors the inline
  re-seek condition in FFmpegReader.ReadVideoCore (drop the active frame and
  force a fresh seek when it is unusable or the skip is out of range).
- FFmpegSeekDecision.HasPrefetchTarget(baseFrame, cachedAhead, totalFrames,
  out nextFrame): mirrors the inline prefetch-advance condition in
  VideoRingBuffer.StartPrefetch (next target exists below EOF for a
  non-negative base).

Behavior-preserving extraction; no FFmpeg-native call is moved. Adds
table-driven MIT NUnit tests over the decision boundaries.

Refs: Project #9 "AI Review" item "Add direct NUnit coverage for PR #1985 seek-past-EOF failure-path decisions in the GPL worker"
…tchTarget

Address review nit: document that the fast-path early break and the extracted
HasPrefetchTarget predicate intentionally re-check the same baseFrame<0 guard,
so the predicate stays self-contained and unit-testable.

Refs: Project #9 "AI Review" item 246
…call mode

Beutl.FFmpegBenchmarks compiles the worker decoder directly by source-linking
FFmpegReader.cs (with BEUTL_FFMPEG_WORKER defined). The predicate extraction
moved the reseek/prefetch logic into a new FFmpegSeekDecision.cs that
FFmpegReader.cs now references, so the benchmark project must source-link that
file too or it fails with CS0103 (FFmpegSeekDecision not found).
Copilot AI review requested due to automatic review settings June 25, 2026 00:09
@coderabbitai

coderabbitai Bot commented Jun 25, 2026

Copy link
Copy Markdown

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: 7457a062-bd87-42ee-a27b-f02b69a1e49e

📥 Commits

Reviewing files that changed from the base of the PR and between 116a4d7 and 182db9b.

📒 Files selected for processing (2)
  • src/Beutl.Extensions.FFmpeg/Decoding/FFmpegSeekDecision.cs
  • src/Beutl.FFmpegWorker/Handlers/VideoRingBuffer.cs
💤 Files with no reviewable changes (1)
  • src/Beutl.FFmpegWorker/Handlers/VideoRingBuffer.cs
🚧 Files skipped from review as they are similar to previous changes (1)
  • src/Beutl.Extensions.FFmpeg/Decoding/FFmpegSeekDecision.cs

📝 Walkthrough

Walkthrough

A shared FFmpegSeekDecision helper is added for reseek and prefetch target decisions. FFmpegReader and VideoRingBuffer call it, and the worker and benchmark projects link the shared source file. New unit tests cover both helper methods.

Changes

FFmpeg seek decision extraction

Layer / File(s) Summary
Seek decision helper
src/Beutl.Extensions.FFmpeg/Decoding/FFmpegSeekDecision.cs
FFmpegSeekDecision defines the reseek predicate and prefetch target lookup, with a conditional namespace and MaxSequentialSkip limit.
Worker wiring
src/Beutl.FFmpegWorker/Beutl.FFmpegWorker.csproj, src/Beutl.FFmpegWorker/Decoding/FFmpegReader.cs, src/Beutl.FFmpegWorker/Handlers/VideoRingBuffer.cs
The worker project links the shared source file, ReadVideoCore calls ShouldReseek, and StartPrefetch calls HasPrefetchTarget.
Decision coverage
tests/Beutl.FFmpegBenchmarks/Beutl.FFmpegBenchmarks.csproj, tests/Beutl.UnitTests/Extensions/FFmpeg/FFmpegSeekDecisionTests.cs
The benchmark project links the shared source file, and the NUnit fixture covers ShouldReseek and HasPrefetchTarget outcomes.

Sequence Diagram(s)

sequenceDiagram
  participant Reader as FFmpegReader.ReadVideoCore
  participant Prefetch as VideoRingBuffer.StartPrefetch
  participant Decision as FFmpegSeekDecision

  Reader->>Decision: ShouldReseek(currentUsable, skip)
  Decision-->>Reader: reseek decision

  Prefetch->>Decision: HasPrefetchTarget(baseFrame, cachedAhead, totalFrames, out nextFrame)
  Decision-->>Prefetch: target availability and nextFrame
Loading

Estimated code review effort

🎯 3 (Moderate) | ⏱️ ~20 minutes

Possibly related PRs

  • b-editor/beutl#1985: Also changes FFmpegReader.ReadVideoCore and VideoRingBuffer.StartPrefetch around FFmpeg frame-seek handling, making it the closest code-level match.

Poem

🐰 I hopped through frames with careful care,
And shared my seek decisions in the air.
When skips go long, I turn and peek,
When caches hold a frame, I prefetch sleek.
Thump-thump! The decoder’s paths now leap.

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 37.50% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the refactor and added tests.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch yuto-trd/ffmpeg-seek-eof-predicates

Comment @coderabbitai help to get the list of available commands.

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

Pull request overview

This PR refactors FFmpeg direct-call decoding by extracting the reseek and prefetch branching logic into a small pure helper (FFmpegSeekDecision) so it can be unit-tested without invoking FFmpeg, and adds regression coverage around seek/prefetch edge conditions (including seek-past-EOF–adjacent behavior from the #1985 follow-up context).

Changes:

  • Introduces FFmpegSeekDecision with pure predicates (ShouldReseek, HasPrefetchTarget) and dual-namespace compilation across MIT extension vs GPL worker via #if BEUTL_FFMPEG_WORKER.
  • Updates FFmpegReader.ReadVideoCore and VideoRingBuffer.StartPrefetch to call the extracted predicates (behavior-preserving).
  • Adds table-driven unit tests and source-links the new helper into the worker and benchmark projects.

Reviewed changes

Copilot reviewed 6 out of 6 changed files in this pull request and generated no comments.

Show a summary per file
File Description
tests/Beutl.UnitTests/Extensions/FFmpeg/FFmpegSeekDecisionTests.cs Adds unit coverage for reseek threshold logic and prefetch target selection.
tests/Beutl.FFmpegBenchmarks/Beutl.FFmpegBenchmarks.csproj Source-links the new helper so linked worker reader code continues to compile in benchmarks.
src/Beutl.FFmpegWorker/Handlers/VideoRingBuffer.cs Replaces inline prefetch EOF/negative-base branching with FFmpegSeekDecision.HasPrefetchTarget.
src/Beutl.FFmpegWorker/Decoding/FFmpegReader.cs Replaces inline reseek condition with FFmpegSeekDecision.ShouldReseek.
src/Beutl.FFmpegWorker/Beutl.FFmpegWorker.csproj Source-links the helper into the GPL worker build (consistent with existing boundary approach).
src/Beutl.Extensions.FFmpeg/Decoding/FFmpegSeekDecision.cs New internal helper implementing the extracted pure predicates with boundary-safe namespace switching.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

…icate comments

FFmpegReader and VideoRingBuffer now call ShouldReseek / HasPrefetchTarget, so
the "mirrors the inline condition in X" framing on those docs is stale (X no
longer holds an inline condition). Trim it, keeping the per-condition rationale
and the out-param contract. Drop the VideoRingBuffer fast-path note, which only
justified a behavior-preserving duplicate baseFrame<0 guard.
@github-actions

Copy link
Copy Markdown
Contributor

No TODO comments were found.

@github-actions

Copy link
Copy Markdown
Contributor

Code Coverage

Package Line Rate Branch Rate Complexity Health
Beutl.Api 9% 4% 1171
Beutl.Configuration 41% 22% 350
Beutl.Controls 0% 0% 5513
Beutl.Core 62% 56% 3069
Beutl.Editor 78% 70% 1742
Beutl.Editor.Components 1% 0% 8552
Beutl.Embedding.MediaFoundation 6% 8% 1376
Beutl.Engine 63% 52% 18123
Beutl.Engine.SourceGenerators 59% 44% 540
Beutl.Extensibility 38% 59% 112
Beutl.Extensions.AVFoundation 5% 12% 246
Beutl.Extensions.FFmpeg 7% 8% 861
Beutl.FFmpegIpc 25% 33% 825
Beutl.Language 7% 50% 1349
Beutl.NodeGraph 24% 15% 2474
Beutl.ProjectSystem 58% 43% 1061
Beutl.Threading 100% 90% 137
Beutl.Utilities 94% 87% 358
Summary 35% (43591 / 124700) 31% (10867 / 35516) 47859

Minimum allowed line rate is 0%

@yuto-trd yuto-trd merged commit 76ead1e into main Jun 25, 2026
4 checks passed
@yuto-trd yuto-trd deleted the yuto-trd/ffmpeg-seek-eof-predicates branch June 25, 2026 06:22
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