-
Notifications
You must be signed in to change notification settings - Fork 31
Fix: media stream resource leak + lazy idle cleanup #4
Description
Problem
The app creates system-level media resources (ScreenCaptureKit sessions, camera streams, AudioContext, WebSocket connections, main-process timers) during recording but never cleans them up on window close, app quit, or view transitions.
On macOS, ScreenCaptureKit sessions are managed by WindowServer — outside the Electron process boundary. When the process terminates without calling .stop() on stream tracks, those sessions become orphans. Over multiple dev restarts this accumulates, eventually starving the GPU readback path that captureStream + MediaRecorder depend on — causing recordings to produce only a handful of frames. The only recovery is a full system reboot.
Resources that were leaking
| Resource | Created in | Cleanup existed? |
|---|---|---|
screenStream tracks |
updateScreenStream() |
Only when switching source — not on quit/close |
cameraStream tracks |
updateCameraStream() |
Only when switching source — not on quit/close |
audioStream tracks |
updateAudioStream() |
Only when switching source — not on quit/close |
AudioContext |
startAudioMeter() |
On source change — not on quit/close |
MediaRecorder × 2 |
startRecording() |
On stopRecording() — not on quit during recording |
scribeWs WebSocket |
startRecording() |
On stopRecording() — not on quit during recording |
| Mouse trail timer | start-mouse-trail IPC |
On stop-mouse-trail — not on main process quit |
Fix: cleanup on quit
Added a centralized cleanupAllMedia() function that synchronously stops all media resources. Called from:
beforeunloadin the renderer — releases all streams, AudioContext, MediaRecorder, WebSocket, intervals, and animation frames on window close / Cmd+Qbefore-quitin the main process — clears the mouse trail timer independently
Verified via macOS unified logging (/usr/bin/log stream) that every SCStream startCapture gets a matching stopCapture on normal quit.
Improvement: lazy idle cleanup (30s timeout)
Beyond the leak fix, streams were also staying alive unnecessarily. The camera and screen capture sessions remain active even when the user is in the timeline/editor view — where they're not needed at all (the editor plays back recorded files, not live streams).
New behavior: When the user leaves the recording view, a 30-second idle timer starts. If they don't return to recording within that window, all media streams are released. When they navigate back to recording, streams are re-acquired automatically.
This means:
- Quick back-and-forth (record → review → record): streams stay alive, no re-acquisition cost
- Extended editing session: streams cleaned after 30s idle, freeing camera/screen/GPU resources
- Leak window shrinks from "forever until app close" to "30 seconds max"
Verification
Confirmed via macOS ScreenCaptureKit system logs:
- Normal quit (Cmd+Q): all sessions paired (start → stop) ✅
- Idle cleanup: sessions released after timeout ✅
- Force kill (Ctrl+C / terminal): sessions leak (expected — OS limitation,
beforeunloaddoesn't fire)
Related
Fixes included in PR #3
Related commits
0b04bc2— feat: add media stream cleanup on quit and lazy idle release