Skip to content

perf: faster Instant processing and early playback for everyone - #3530

Draft
richiemcilroy wants to merge 5 commits into
mainfrom
perf/instant-processing-speed
Draft

richiemcilroy wants to merge 5 commits into
mainfrom
perf/instant-processing-speed

Conversation

@richiemcilroy

@richiemcilroy richiemcilroy commented Oct 28, 2026 •

Copy link
Copy Markdown
Member

Makes an Instant recording reach its processed, verified MP4 sooner after Stop, and lets the owner's stopped tab start playing straight away for everyone.

flowchart LR
  subgraph before[media server, before]
    direction LR
    A1[download] --> B1[remux] --> C1["packet proof<br/>(SHA-256 per packet)"] --> D1[chunk decode] --> E1[PUT] --> F1["GET whole object<br/>+ SHA-256"] --> G1[upload previews]
  end
  subgraph after[media server, after]
    direction LR
    A2[download] --> B2[remux] --> C2["packet proof (murmur3)<br/>‖ chunk decode<br/>‖ SHA-256 + MD5 in own process"] --> E2["PUT ‖ previews"] --> F2["ETag == local MD5?<br/>yes: done · no: GET + SHA-256"]
  end
Loading

Media server (/video/mux-segments)

  • Upload proof without the read-back. hashRecordingUpload (apps/media-server/src/lib/recording-verification.ts) computes the SHA-256 plus the ETag S3 returns for the same bytes: "md5" for a single PUT, "md5(part md5s)-N" for multipart at the upload's own part size (apps/media-server/src/lib/upload-digest.ts). When the stored object's ETag (read with If-Match, size checked) equals it, the whole-object GET + hash is skipped. Any other ETag (SSE-KMS, Drive, other providers) still re-reads every byte exactly as before.
  • Hashing off the event loop. The output hash now runs in its own bun process (same pattern as recording-audio-timing.ts), so it no longer takes turns with the decoder output the verification parses on the main thread.
  • Packet proof uses murmur3 for video. readTrack (apps/media-server/src/lib/recording-packet-proof.ts) compared source and output packets by FFmpeg's plain-C SHA-256, the slowest read in a long recording's proof. Both sides are hashed by the same ffprobe and only compared with each other, so video packets now use murmur3 (128-bit, ~4x faster). Audio keeps SHA-256 because its tail is checked against a hash computed outside ffprobe.
  • Proof and decode run together. verifyRemuxedRecording starts the fragment decode at the same time as the packet proof. The decode only reads the proof for its final count and timing checks; a failed proof still wins (a mismatch is thrown, the decoders are stopped), and a proof error still falls back to the full source inspection.
  • Previews upload beside the recording. Thumbnail and preview GIF go to this attempt's own candidate keys, which nothing reads before the recording is published, so they upload while the recording uploads and is checked. The job still completes only after both.

Fixture runs on a g6.4xlarge (same type and env as the media workers), main and this branch alternating, recordings prepared with the desktop's 30 s preverification:

Fixture main total PR total main verify PR verify main upload + check PR upload + check
60 s real recording 2.21 s 1.63 s 1.24 s 0.99 s 0.52 s 0.29 s
5 min 720p 2.48 s 1.76 s 1.21 s 0.80 s 0.53 s 0.29 s
13 min 720p 3.98 s 2.86 s 1.95 s 1.45 s 0.77 s 0.27 s
28 min 720p 7.14 s 5.32 s 3.36 s 2.70 s 1.50 s 0.40 s

Medians of 3 alternating runs per fixture. Totals are the media server's own time from job start to the completion webhook.

Web

  • One listing per commit step (apps/web/lib/desktop-recording-source.ts). Plan, enumerate and copy share one bucket listing; nothing in those phases writes a copy before the copy phase does, and the only other writer (segment preparation) at worst leaves a copy unlisted and it is copied again. Verification still lists afresh, since it is what proves the originals did not change. Four listing rounds per commit become two.
  • Preverified run listing overlaps the inventory read in dispatchDesktopRecordingJob (apps/web/workflows/finalize-desktop-recording.ts).
  • Early playback for everyone (apps/web/lib/early-instant-playback.ts). The stopped owner tab plays the uploaded prefix while the tail uploads instead of waiting for the whole upload. CAP_EARLY_INSTANT_PLAYBACK=disabled turns it off; any other value (including the current owner list) now means on.

Source commit against real S3 from us-east-1, main and this branch alternating, 16 rounds each:

Recording main p50 PR p50
1 min 444 ms 356 ms
5 min 441 ms 378 ms

Rollout

  • Web and media server are independent: an old media server ignores nothing new, and a new one only changes its own work.
  • After deploy, CAP_EARLY_INSTANT_PLAYBACK can be removed from the production env.

RetriggerView in GreptileConfidence Score: 5/5

The PR appears safe to merge; no actionable issues were found.

Summary

Speeds up Instant recording completion and enables early playback by default.

  • Instant recordings finish verification with less waiting.
  • Preview images upload while the recording is checked.
  • Source commits reuse bucket listings during preparation.
  • Stopped segmented recordings can play their uploaded parts right away.

No actionable issues were found. Checks were based on source inspection; tests were not run.

Intentional trade-offs acknowledged by richiemcilroy: video packet hashes change to murmur3 for speed, and a copy created during segment preparation may be copied again when it misses the shared listing.

Reviews (1) · Last reviewed commit: "feat(web): play stopped Instant recordin..." · Reviewed by Greptile

@richiemcilroy

Copy link
Copy Markdown
Member Author

hey @greptileai, please re-review the PR

This branch was successfully deployed

1 active and 1 inactive deployments
Preview – cap-web — 2fcdcd9c Deployed Oct 28, 2026 by vercel[bot]
Preview – cap-status — 2fcdcd9c Deployed Oct 28, 2026 by vercel[bot]
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