Skip to content

OS: Auto-exposure restarts pinned at max ISO on every session start (regression from v4) #4016

Description

@stevengoldberg

Prerequisites

Reproduction

https://github.com/mrousavy/react-native-vision-camera/pull/4015/changes

Steps to reproduce

Reproduction: PR #4015 adds two failing harness tests to visioncamera.controller.harness.ts:

  • keeps an exposure lock applied between configure() and start() — scene-independent: start() resets an explicit setExposureLocked(...) back to continuous auto-exposure at the format's max ISO.
  • resumes auto-exposure near previously converged values on a fresh session — a fresh session restarts AE at max ISO instead of resuming near the previously converged exposure.

What did you expect to happen?

The preview should appear at (or very near) the correct exposure, with no visible exposure ramp — matching both VisionCamera v4 (same app, same device — no visible adjustment on camera open, including cold starts) and the native iOS Camera app.

Auto-exposure should start from a neutral mid-range default — or, when the AVCaptureDevice still holds exposure values from a previous session (which it demonstrably does right up until start() is called, see logs), from those retained values — rather than from the format's maximum ISO.

What actually happened?

On every CameraSession.configure() + start(), auto-exposure begins pinned at the format's maximum ISO (5184 on this format) and converges down to the correct value over ~1–1.5s. The preview is severely overexposed for that entire period. No crash.

Two additional data points:

  • When a previous session had run, polling controller.iso before start() returns that session's correct converged value (e.g. ISO 258). The first sample after start() jumps to ISO 5184 (max). So the start sequence itself resets exposure to max, discarding valid AE state.
  • Applying controller.setExposureLocked(duration, iso) with known-good values inside onConfigured (after configure, before start) does not help: the session start still resets exposure to max ISO, overriding the lock.

Affected platforms

iOS (device)

Device(s) affected

iPhone 17 Pro

VisionCamera version

5.0.11

React Native version

0.83.6

React Native architecture

New Architecture (Fabric / bridgeless)

Features being used

  • Preview
  • Photo capture
  • Video capture
  • Frame Processors (worklets)
  • Skia Frame Processors
  • Code/Barcode Scanner
  • Location metadata
  • Multi-cam
  • Depth data
  • HDR / custom dynamic range
  • Custom format / FPS / resolution

Relevant logs / stack trace

AE telemetry polled from the CameraController every ~100ms (iso, exposureDuration, exposureBias, exposureMode, zoom; timestamps relative to isActive=true; "session configured" / "session started" logged from onConfigured / onStarted):

[AE] session configured
[AE] +127ms  iso: 0    shutter: 1/NaN bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] +234ms  iso: 0    shutter: 1/NaN bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] session started
[AE] +381ms  iso: 5184 shutter: 1/30  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00   <-- starts at max ISO
[AE] +484ms  iso: 3027 shutter: 1/30  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] +601ms  iso: 1768 shutter: 1/30  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] +718ms  iso: 1768 shutter: 1/30  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] +834ms  iso: 798  shutter: 1/36  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] +951ms  iso: 319  shutter: 1/36  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] +1051ms iso: 156  shutter: 1/36  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] +1151ms iso: 108  shutter: 1/38  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00   <-- converged
[AE] +1268ms iso: 108  shutter: 1/50  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
A separate run showing the device still holding a previous session's correct exposure until start() resets it:

[AE] session configured
[AE] +113ms  iso: 258  shutter: 1/38  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00   <-- retained from previous session (correct)
[AE] +300ms  iso: 0    shutter: 1/NaN bias: 0.00 mode: continuous-auto-exposure zoom: 2.00
[AE] session started
[AE] +411ms  iso: 5184 shutter: 1/30  bias: 0.00 mode: continuous-auto-exposure zoom: 2.00   <-- reset to max ISO
The negotiated session config (from onSessionConfigSelected):

CameraSessionConfig(format: <AVCaptureDeviceFormat: 'vide'/'420f' 3264x2448, { 1- 30 fps},
photo dims:{3264x2448,4032x3024}, fov:103.625, gdc fov:106.201, max zoom:189.00 (upscales @1.24),
secondaries:{4.00,16.00}, system zoom range:1.0-24.0, AF System:2, ISO:54.0-5184.0,
SS:0.000014-1.000000, system exposure bias range:-2.0-2.0, supports HDR, supports wide color>,
selectedFPS: Optional(30.0), selectedVideoStabilizationMode: nil,
selectedPreviewStabilizationMode: nil, selectedVideoDynamicRange: Optional(...sdr...),
isPhotoHDREnabled: false)

Additional context

  • exposureBias is 0.00 throughout (the exposure prop is not involved), and zoom is correct (2.00) from the first sample (no lens-switch involved).

  • No effect on the behavior:

    • Adding/removing constraints: { fps: 30 }, { videoDynamicRange: CommonDynamicRanges.ANY_SDR }, { resolutionBias: videoOutput }
    • Adding/removing a video output alongside the photo output
    • Removing the zoom / exposure props entirely
    • setExposureLocked(...) in onConfigured as described above

Submission

  • The reproduction I linked is either (preferred) a PR against this repo that adds a failing harness test following the harness-tests README, or (fallback) a public repo that reproduces the bug on a fresh clone. I understand the issue will be closed without one.
  • I pasted logs as text (not screenshots).
  • I wrote this report in my own words. I did not paste AI-generated descriptions of the bug.

Metadata

Metadata

Assignees

No one assigned

    Labels

    🐛 bugSomething isn't working

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions