Skip to content

fix(recording): flush Studio video to disk every 2 s of capture time - #2646

Merged
richiemcilroy merged 11 commits into
mainfrom
fix/studio-timed-fragments
Oct 16, 2026
Merged

richiemcilroy merged 11 commits into
mainfrom
fix/studio-timed-fragments

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 16, 2026 •

Copy link
Copy Markdown
Member

A Studio recording of a mostly static screen could lose more than 10 seconds of video when Cap crashed, even though segments target 2 seconds.

Cause

flowchart LR
    A[ScreenCaptureKit delivers 3 fps on a static screen] --> B[keyframe every 60 frames = 20 s]
    B --> C[DASH cuts segments only at keyframes]
    C --> D[the whole 20 s segment sits in the writer's memory]
    D --> E[crash: nothing of it is on disk]
Loading
  • Recording encoders set the keyframe interval as a frame count (2 s at the nominal frame rate). When the screen delivers fewer frames, a segment spans far longer than 2 s.
  • The DASH writer keeps the open segment in memory until the segment ends, so a crash loses all of it.
  • Forcing more keyframes would fix the loss, but a keyframe every 2 s of capture time grows a 1 fps recording 20x. Keyframes stay as they are.

Fix

Studio's fragmented writers now flush a complete fragment (moof + mdat) every 2 s of capture time inside the current segment:

set_opt("frag_type", "duration");
set_opt("frag_duration", "2");
set_opt("streaming", "1"); // write each finished fragment to the open .m4s.tmp
  • Scope. Studio display and camera writers on macOS, Windows and Linux (timed_fragments: true). Every Instant writer passes false and keeps its options, because Instant segments are uploaded live.
  • Metadata. Streaming segments have no sidx. read_fragment_prefix (crates/enc-ffmpeg/src/mux/fragment_prefix.rs) reads the duration from the fragments' own tfhd/tfdt/trun against init.mp4. It checks that every run lies inside its mdat and that each fragment starts where the previous one ended. read_fragment_metadata falls back to it only when the sidx read fails.
  • Recovery. For an interrupted .m4s.tmp, recovery copies the verified complete prefix to the final segment name, from the segment's keyframe through the last whole fragment. The copy is synced, then published with an exclusive rename (renamex_np/renameat2/MoveFileW), falling back to a create-new copy. The original .tmp bytes are never modified. A storage error leaves the source retryable instead of marking it corrupt.
  • The out-of-process muxer (an opt-in setting, off by default) keeps its current writer.

Measured

Identical encoded packets replayed through the production writer, 60 s at each delivered frame rate:

Delivered fps Encoded payload Container size vs main Frames on disk before the trailer, main / this PR
30 identical -0.02% 1,740 / 1,740 of 1,800
15 identical +0.02% 840 / 870 of 900
3 identical +0.32% 120 / 174 of 180
1 identical +1.71% 0 / 58 of 60

Recorder killed during a static-screen Studio recording (Engine verify crash suite, macOS, Windows and Linux):

main This PR
Recovered frames none 30 of 34, timestamps exact
Video lost at the end 11.03 s 1.03 s
  • Decoded frames and timestamps of finalized recordings match main's.
  • The extra muxer CPU is at most 0.06 ms per minute of recording (1 fps).

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no new blocking issue was found.

Summary

Studio writers flush complete video fragments every two seconds of capture time without forcing more keyframes. Recovery preserves the original temporary files and copies their complete fragments into staged files.

  • Studio writers flush video in timed fragments.
  • Streaming fragments keep their duration without an index.
  • Studio recovery keeps complete fragments from interrupted recordings.

Reviews (8) · Last reviewed commit: "fix: keep an unreadable pending fragment..." · Reviewed by Greptile

@vercel

vercel Bot commented Oct 16, 2026 •

Copy link
Copy Markdown
Contributor

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
cap-status Ready Ready Preview Oct 16, 2026 4:18pm UTC

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

Comment thread crates/recording/src/recovery.rs Outdated
Comment thread crates/recording/src/recovery.rs Outdated
@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

@richiemcilroy richiemcilroy changed the title fix: preserve timed Studio fragments across recording crashes fix(recording): flush Studio video every 2 s so a crash loses at most one fragment Oct 16, 2026
@richiemcilroy richiemcilroy changed the title fix(recording): flush Studio video every 2 s so a crash loses at most one fragment fix(recording): flush Studio video to disk every 2 s of capture time Oct 16, 2026
@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Capy found no issues.

View 2 other findings in Capy.

Open in Capy

Comment thread crates/enc-ffmpeg/src/mux/fragment_prefix.rs
read_fragment_prefix returned the fragments verified before any later
error. A drive error then recovered a shorter video instead of leaving the
source retryable. Only a malformed or truncated tail ends the prefix now;
other I/O errors reach the caller.
@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Capy found 1 potential issue (1 high).

Open in Capy

Comment thread crates/enc-ffmpeg/src/mux/fragment_prefix.rs
…ryable

Studio fragment discovery skipped any .m4s.tmp whose prefix read failed,
so a transient storage error let staged finalization publish a shorter
recording. Only a malformed or empty tail is skipped now; any other read
error keeps the fragment listed, and staged finalization refuses to
publish while a pending fragment remains.
@richiemcilroy

Copy link
Copy Markdown
Member Author

@greptileai review

@capy-ai capy-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Capy found no issues.

Open in Capy

@richiemcilroy
richiemcilroy merged commit fb6652e into main Oct 16, 2026
54 checks passed

This branch was successfully deployed

2 active deployments
Preview – cap-web — d2b8ea8d Deployed Oct 16, 2026 by vercel[bot]
Preview – cap-status — d2b8ea8d Deployed Oct 16, 2026 by vercel[bot]
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant