Skip to content

Conversation

@richiemcilroy
Copy link
Member

@richiemcilroy richiemcilroy commented Oct 18, 2025

This pull request introduces improved error handling, performance and timing precision for video playback in the editor. The main changes include adding a new error type to handle invalid FPS values, updating the playback start logic to return a result instead of panicking, and refining the frame scheduling to ensure smoother and more accurate playback.

Improves performance of playback in the editor by tightening the playback loop so we only render once per frame interval instead of hammering the decoders continuously.

Summary by CodeRabbit

  • Bug Fixes
    • Added validation for frame rate values to prevent playback startup errors
    • Enhanced playback timing system with improved frame synchronization

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 18, 2025

Walkthrough

The playback system adds FPS validation during startup with explicit error handling, returning early on invalid FPS. The playback loop is refactored from time-based calculations to a frame-driven scheduling model using tokio::select! to synchronize frame progression with a computed deadline.

Changes

Cohort / File(s) Summary
Playback System Core
crates/editor/src/playback.rs
Introduces public PlaybackStartError::InvalidFps enum; changes Playback::start() return type from PlaybackHandle to Result<PlaybackHandle, PlaybackStartError>; adds FPS validation (checks finite and > 0 after f64 conversion); refactors playback loop to frame-driven model with frame-based deadline calculation; replaces time-based frame calculations with frame_number-derived playback_time; uses tokio::select! to wait on stop signals or deadline; synchronizes frame_number to catch up with elapsed time.
Call Site Integration
crates/editor/src/editor_instance.rs
Adds import of PlaybackStartError from playback module; expands tracing imports to include warn macro; wraps playback .start() invocation in match expression that logs warning and returns early on PlaybackStartError::InvalidFps.

Sequence Diagram

sequenceDiagram
    participant Editor
    participant Playback
    participant Timing

    Editor->>Playback: start(fps, resolution)
    
    rect rgb(200, 220, 255)
    Note over Playback: Validation Phase
    Playback->>Playback: Convert fps to f64
    Playback->>Playback: Check finite & > 0
    alt Invalid FPS
        Playback-->>Editor: Err(InvalidFps)
        Editor->>Editor: Log warning, abort
    else Valid FPS
        Playback->>Timing: Calculate frame_duration
        Playback->>Timing: Initialize next_deadline
    end
    end
    
    rect rgb(220, 240, 220)
    Note over Playback: Frame-Driven Loop
    loop Per Frame
        Playback->>Timing: tokio::select! {<br/>stop_signal | deadline}
        alt Deadline Reached
            Playback->>Playback: Increment frame_number
            Playback->>Playback: Calculate playback_time<br/>(frame_number / fps)
            Playback->>Playback: Fetch segment by playback_time
            Playback->>Playback: Render frame
            Playback->>Timing: Compute next_deadline
        else Stop Signal Received
            break Exit loop
            end
        end
    end
    end
    
    Playback-->>Editor: Ok(PlaybackHandle)
Loading

Estimated Code Review Effort

🎯 4 (Complex) | ⏱️ ~45 minutes

The review requires understanding a significant refactoring of timing-critical code from time-based to frame-driven scheduling, including new FPS validation logic, error propagation changes, and frame synchronization mechanisms that affect playback accuracy and behavior.

Poem

🐰 Frame by frame, precise and true,
Validation guards what we do,
Deadlines tick in perfect time,
Error caught at the start's first chime,
Playback hops through timing's rhyme! 🎬

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The pull request title "Stabilize editor playback pacing and validate fps" directly and accurately captures the two primary changes in this changeset. The title reflects the main objectives: stabilizing playback pacing through frame-driven scheduling refinements (as seen in the reworked playback loop using calculated deadlines and frame progression), and validating FPS through the new PlaybackStartError type and validation logic that checks for valid FPS values before playback starts. The title is concise, clear, avoids vague terminology, and provides sufficient specificity for a developer reviewing the commit history to understand the core purpose without being misleading or overly broad.
✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch editor-performance-fixes

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@richiemcilroy richiemcilroy changed the title feat: Handle invalid FPS in playback start Stabilize editor playback pacing and validate fps Oct 18, 2025
Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
crates/editor/src/playback.rs (2)

23-26: LGTM! Simple error type for FPS validation.

The error enum is appropriate for internal error handling. Optionally, consider implementing std::error::Error and Display if this error needs to be propagated or formatted in user-facing contexts.


55-60: Simplify the FPS validation check.

Since fps is a u32, converting it to f64 will always produce a finite value. The is_finite() check is redundant. The real concern is whether fps is zero.

Apply this diff to simplify the validation:

-    let fps_f64 = fps as f64;
-
-    if !(fps_f64.is_finite() && fps_f64 > 0.0) {
+    if fps == 0 {
         warn!(fps, "Invalid FPS provided for playback start");
         return Err(PlaybackStartError::InvalidFps);
     }
+
+    let fps_f64 = fps as f64;
📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8eda1a3 and d81c4ce.

📒 Files selected for processing (2)
  • crates/editor/src/editor_instance.rs (3 hunks)
  • crates/editor/src/playback.rs (3 hunks)
🧰 Additional context used
📓 Path-based instructions (2)
**/*.rs

📄 CodeRabbit inference engine (AGENTS.md)

**/*.rs: Format Rust code using rustfmt and ensure all Rust code passes workspace-level clippy lints.
Rust modules should be named with snake_case, and crate directories should be in kebab-case.

Files:

  • crates/editor/src/editor_instance.rs
  • crates/editor/src/playback.rs
crates/*/src/**/*

📄 CodeRabbit inference engine (AGENTS.md)

Rust crates should place tests within the src/ and/or a sibling tests/ directory for each crate inside crates/*.

Files:

  • crates/editor/src/editor_instance.rs
  • crates/editor/src/playback.rs
🧬 Code graph analysis (1)
crates/editor/src/playback.rs (3)
crates/media-info/src/lib.rs (1)
  • fps (221-223)
apps/desktop/src-tauri/src/export.rs (1)
  • fps (17-22)
crates/rendering/src/project_recordings.rs (1)
  • fps (50-52)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (3)
  • GitHub Check: Build Desktop (x86_64-pc-windows-msvc, windows-latest)
  • GitHub Check: Build Desktop (aarch64-apple-darwin, macos-latest)
  • GitHub Check: Analyze (rust)
🔇 Additional comments (8)
crates/editor/src/editor_instance.rs (2)

2-2: LGTM! Import additions support the error handling.

The new imports enable explicit error pattern matching and warning logs for invalid FPS scenarios.

Also applies to: 13-13


148-163: LGTM! Proper error handling for invalid FPS.

The error handling correctly matches on the Result type, logs a warning for invalid FPS, and aborts the playback start via early return. This prevents invalid playback from proceeding.

crates/editor/src/playback.rs (6)

91-96: LGTM! Frame-driven scheduling improves timing precision.

Calculating the deadline from the frame offset relative to start time prevents accumulating timing errors, which is superior to repeatedly adding frame durations.


98-105: LGTM! Proper synchronization for frame timing and cancellation.

The tokio::select! correctly prioritizes the stop signal, and the subsequent stop check at line 103 is good defensive programming to catch signals that arrive as the sleep completes.


107-117: LGTM! Clean playback time calculation and segment retrieval.

The frame-based playback time calculation is correct, and the let-else pattern provides clean error handling for segment retrieval.


119-132: LGTM! Proper clip offset retrieval and cancellable frame fetching.

The clip offset lookup with fallback to default is correct, and using tokio::select! to make frame retrieval cancellable ensures responsive shutdown.


134-159: LGTM! Rendering logic with frame catch-up for drift prevention.

The rendering is correctly conditioned on frame availability. The catch-up logic (lines 154-159) ensures the playback doesn't fall behind by skipping frames when rendering is slow—appropriate behavior for real-time playback to maintain synchronization.


167-167: LGTM! Correct return type.

The Ok(handle) return matches the new Result signature.

@richiemcilroy richiemcilroy merged commit 50898da into main Oct 18, 2025
15 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.

1 participant