Skip to content

Camera-only SDK recordings cannot be saved after their source ends #2970

Closed

Description

@AksharP5

Description

@cap/sdk-recorder cannot save a camera-only recording after its camera source ends while the microphone is disabled. The native MediaRecorder has already emitted the final video and stopped, but the SDK still reports recording or paused. Calling the SDK's Stop switches it to stopping forever. No upload begins, and destroying the recorder discards the buffered take.

CapRecorder.stop() installs its native stop listener only when called, then invokes native stop() unconditionally. An already-inactive MediaRecorder does nothing and emits no second stop event, so the new promise has no path to settle.

Expected: Stop saves the already-buffered recording and completes.

Reproduction

Use the class API or the exported React hook with:

const recorder = new CapRecorder({
	publicKey,
	mode: "camera",
	microphone: { enabled: false },
});
await recorder.start();
  1. Record camera video, then end the camera source, for example by disconnecting an external webcam or revoking its permission.
  2. After native recording has ended, invoke await recorder.stop().
  3. Observe the SDK remain in stopping, with no upload requests or completed result. The paused case is also affected.

The source-ending transition was reproduced deterministically in Chromium by returning a real canvas.captureStream(30) from the camera-acquisition boundary, recording moving frames, ending its sole video track, waiting for native completion, then invoking public Stop. The SDK, MediaRecorder, media events, chunks and multipart client were real; camera acquisition and HTTP responses were local fixtures. A physical webcam disconnect was not exercised.

Same short video fixture Main Proposed fix
Source ends during recording 9,343 buffered bytes; Stop pending after 800 ms; zero HTTP calls Stop resolves; all 9,343 bytes uploaded; phase complete
Source ends while paused Same pending Stop and zero HTTP calls Stop resolves and uploads the buffered video
Normal manual Stop Completes Completes

The final patch was also replayed with Stop running while native final events were still pending. All six output files decode successfully as 320×180 VP9 video with 21 or 51 frames, and each upload matches all native chunks byte for byte. A 1.7 s replay preserves all 25,672 bytes, including its final 11,107-byte chunk. The baseline's otherwise-stranded artifacts were recovered from buffered chunks by the diagnostic fixture; there is no consumer-facing recovery feature.

Additional Context

Relevant sources: Stop implementation, camera/display track handling, React hook Stop. The MediaRecorder stop algorithm specifies the inactive no-op.

Activity

  1. linear commented on Oct 23, 2026

    @linear
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions