Skip to content

Fix: media stream resource leak + lazy idle cleanup #4

@amitayks

Description

@amitayks

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:

  • beforeunload in the renderer — releases all streams, AudioContext, MediaRecorder, WebSocket, intervals, and animation frames on window close / Cmd+Q
  • before-quit in 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, beforeunload doesn't fire)

Related

Fixes included in PR #3

Related commits

  • 0b04bc2 — feat: add media stream cleanup on quit and lazy idle release

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions