/** * selectCaptureDevice — capability-aware back-camera selection. * * Replaces the single-physical-device request that caused two * user-visible bugs (see docs/plans/2026-06-01-v0.13.2-multilens- * device-selection.md): * * 1. 0.5× silently showed the wide-angle FOV on phones where the * ultra-wide is only exposed inside a multi-cam logical device — * vision-camera's single-lens filter mis-scored and fell back to * a plain wide-angle device. * 2. flash threw `flash-not-available` on 0.5× because the standalone * ultra-wide device has no torch unit. * * Both stem from mounting ONE standalone physical device per lens. The * fix: prefer a MULTI-CAM device that carries the ultra-wide (so a * single mounted device spans both FOVs via zoom AND carries the torch * through its wide-angle member). Fall back to standalone devices for * phones — common on Android — where the ultra-wide has no multi-cam * grouping, so we don't regress those. * * Pure + synchronous: takes a plain device list (the structural subset * of vision-camera's `CameraDevice` we need) and returns the choice. * No React, no vision-camera hooks — unit-tested directly. */ export type LensType = 'ultra-wide-angle-camera' | 'wide-angle-camera' | 'telephoto-camera'; /** * The structural subset of vision-camera's `CameraDevice` this selector * reads. Declared locally (not imported) so tests can build synthetic * devices without the full vision-camera type, and so the SDK doesn't * couple its selection logic to vision-camera's evolving shape. */ export interface DeviceLike { id: string; position: 'front' | 'back' | 'external'; physicalDevices: LensType[]; isMultiCam: boolean; hasTorch: boolean; minZoom: number; neutralZoom: number; maxZoom: number; } export type CaptureDeviceMode = /** One multi-cam device spans wide + ultra-wide; switch lenses via zoom. */ 'multicam' /** * Ultra-wide reached by remounting a dedicated ultra-wide device on 0.5x * (the 1x primary may be a multi-cam *or* a standalone wide). Used when * no multi-cam device can reach the ultra-wide by zoom. */ | 'standalone-uw' /** No ultra-wide anywhere; wide-angle only (no 0.5× chip). */ | 'wide-only'; export interface CaptureDeviceSelection { /** The device to mount for the `1×` lens (and for `multicam`, all lenses). */ device: D | null; /** * The device to mount when the user picks `0.5×` in `standalone-uw` * mode (a separate physical ultra-wide). Null in `multicam` (same * device, zoom instead) and `wide-only` (no ultra-wide). */ ultraWideDevice: D | null; mode: CaptureDeviceMode; /** Whether a 0.5× chooser should be offered at all. */ has0_5x: boolean; /** Whether the `1×`/primary mounted device can flash (drives flash UI). */ hasTorch: boolean; /** * The device's REAL ultra-wide zoom factor, for the lens chip's LABEL only * (the `CameraLens` identifier stays `'0.5x'` — it is also the stitcher's * warper-tree zoom signal, so it must not become device-dependent). * `0.6` on a Galaxy S24 Ultra, `0.5` on a typical iPhone. * * `null` when no back device advertises a sub-1× zoom range, i.e. the * factor is genuinely unknowable from what the platform reports — the UI * falls back to its historical `0.5×` label rather than inventing a number. * * See {@link ultraWideFactorOf} for why this is NOT read off the mounted * device. */ ultraWideFactor: number | null; } /** Options for {@link selectCaptureDevice}. */ export interface SelectCaptureDeviceOptions { /** * `captureDepthData` (iOS): prefer a DEPTH-CAPABLE 1× mount. AVDepthData * only flows from a virtual multi-lens device (or LiDAR) — a plain * physical wide-angle never delivers it. Effect on the pick: * - multicam mode (virtual wide+ultra-wide) already qualifies — depth * comes from the wide+uw overlap at FULL wide FOV. Unchanged. * - standalone-uw / wide-only: the 1× primary becomes the best * depth-capable VIRTUAL device, ranked by the FOV its depth (and * depth-biased formats) actually cover — see `depthMountRank`: * 1. wide-only virtual (`Back LiDAR Depth Camera`) — sensor depth * at the full wide FOV; nothing visible changes at 1×. * 2. ultra-wide-containing virtual (Dual Wide / Triple) — the * stereo pair is uw+wide, whose overlap IS the wide FOV; 1× * still looks normal. * 3. wide+tele virtual (`Back Dual Camera`) — LAST RESORT: the * overlap is the TELE FOV, so 1× reads ~2× tighter (field * finding 2026-07-10). Only phones with no better depth * source pay this, knowingly. * Falls through to the normal pick when no depth-capable device exists. */ preferDepth?: boolean; /** * `Platform.OS` at the call site. Threaded in (not read directly) so * this module stays pure/synchronous and unit-testable without a * react-native mock — see the file header. * * ANDROID FIELD FINDING (Galaxy S24 Ultra / SCG26, 2026-07-27): the * logical multi-cam device reported `minZoom: 0.6` — inside * `UW_ZOOM_REACH_MAX`, so the multicam branch trusted it — but the * Samsung camera HAL never actually crossed physical sensors: a captured * `adb logcat` showed the identical vendor stream id * (`MultiCameraRealtime1_IFE0_cam0`) at both 1× and the "0.5×"-zoomed * request, and the captured frame's FOV visibly did not widen. Android's * Camera2 `CONTROL_ZOOM_RATIO` cross-physical-camera switch is * documented as OEM-inconsistent for non-first-party apps, so on * `'android'` we do NOT trust the zoom-reach claim when a genuine * standalone ultra-wide id exists to swap to instead — see the multicam * qualification check below. * * iOS is UNCHANGED: AVFoundation's virtual multi-cam devices are the * OS-native mechanism this file's original fix relies on (multicam * keeps flash working on 0.5× — see the SYMPTOM 1/2 tests), so iOS keeps * preferring multicam whenever it qualifies, even when a standalone * ultra-wide ALSO happens to be enumerated (real iPhones enumerate * both simultaneously). * * Omitted / any value other than `'android'` → today's behaviour * (prefer multicam), so an untested platform can't silently regress. */ platform?: string; } /** * Choose the back-camera device(s) for capture. * * Priority: * 1. multicam — a multi-cam device containing BOTH wide + ultra-wide * (best: one device, zoom-switch, torch via the wide member). * SKIPPED on `platform: 'android'` when a standalone ultra-wide is * ALSO enumerated — the zoom-reach claim is not trustworthy there * (see {@link SelectCaptureDeviceOptions.platform}). * 2. standalone-uw — a standalone wide AND a standalone ultra-wide * exist as separate devices (device-swap on lens change; flash * hidden on the torchless ultra-wide). * 3. wide-only — no ultra-wide reachable; wide-angle only. * * @param devices All enumerated camera devices (any position). * @param opts See {@link SelectCaptureDeviceOptions}. */ export declare function selectCaptureDevice(devices: readonly D[], opts?: SelectCaptureDeviceOptions): CaptureDeviceSelection; /** * Map a UI lens label to a vision-camera `zoom` value for the * `multicam` mode (where lens switching is zoom, not device swap). * * - `1×` → the device's `neutralZoom` (wide-angle baseline; vision- * camera docs: "where the camera is in wide-angle mode and hasn't * switched to ultra-wide or telephoto yet"). * - `0.5×` → `minZoom` (the ultra-wide end of the zoom range). * * Returns `neutralZoom` for any non-0.5× label as a safe default. * Only meaningful in `multicam` mode; the standalone path swaps devices * and ignores this. */ export declare function zoomForLens(device: Pick, lens: '1x' | '0.5x'): number; //# sourceMappingURL=selectCaptureDevice.d.ts.map