Skip to content

fix(camera): recover browser preview after permission is granted#41

Open
cn0303 wants to merge 1 commit into
huggingface:mainfrom
cn0303:fix/camera-stream-retry
Open

fix(camera): recover browser preview after permission is granted#41
cn0303 wants to merge 1 commit into
huggingface:mainfrom
cn0303:fix/camera-stream-retry

Conversation

@cn0303

@cn0303 cn0303 commented Jun 19, 2026

Copy link
Copy Markdown
Contributor

Problem

The camera preview (useCameraStream) makes a single getUserMedia attempt. On first
use that attempt can fail because:

  • the page has not been granted camera permission yet (the grant is driven separately by
    useAvailableCameras' probe getUserMedia), or
  • the device is briefly held right after that probe releases it (transient NotReadableError).

Because the bound deviceId doesn't change once permission lands, the hook stays stuck in the
error state until a full page reload. This is visible today on:

  • Calibration → camera previews
  • Recording → camera configuration previews

both showing Preview failed / No browser match on the first visit, only fixed by reloading.

Steps to reproduce

  1. Reset camera permission for the site (or use a fresh incognito window).
  2. Open the Calibration page and enable cameras (or open Recording's camera config).
  3. Grant the camera permission when prompted.
  4. The previews stay on "Preview failed" / "No browser match" until you reload the page.

Fix

  • Error-aware retry. Only transient failures (NotReadableError / AbortError, i.e. the device
    is briefly held) are retried, with exponential backoff (300ms → 600ms → 1200ms → ...).
    Permanent-at-this-instant errors; permission denial (NotAllowedError), missing device
    (NotFoundError), unsatisfiable constraints (OverconstrainedError) — are not spin-retried.
  • Event-driven recovery. Those non-transient cases recover when the browser fires devicechange
    which can follow a permission grant or a device-exposure change (the browser doesn't guarantee
    timing) and only while already in the error state, so a healthy stream is never torn down by an
    unrelated device change (e.g. plugging in a microphone).

The preview now recovers on its own, no reload needed. Behaviour while paused is unchanged
(stream is still released for cv2 during recording).

Testing

  • Manual: cold-start permission flow on Calibration and Recording; previews come up first-try
    after granting, no reload.
  • npx tsc --noEmit clean; ESLint clean on the changed file (note: the repo's full npm run lint
    already reports pre-existing errors/warnings unrelated to this change); npm run build succeeds.

The camera preview video element made a single getUserMedia attempt. On
first use it can fail before the page has been granted camera permission
(the grant is driven separately by useAvailableCameras' probe) or while the
device is briefly held right after that probe releases it. Because the
deviceId does not change once permission lands, the hook stayed stuck on the
error state until a full page reload -- visible on the calibration and
recording camera previews as a failed preview / "No browser match".

Retry the stream a few times with backoff, and re-attempt when the media
device list changes (granting permission fires a devicechange event) but
only while in the error state, so a healthy stream is never torn down. The
preview now recovers on its own without a reload.
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