From 5aef86645ef52b470fb3d2967ddf762b814e4873 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?B=C5=82az=CC=87ej=20Pankowski?= <86720177+pblazej@users.noreply.github.com> Date: Fri, 10 Apr 2026 12:39:35 +0200 Subject: [PATCH] fix(camera): preserve camera position across full reconnects (#960) After a full reconnect, the camera could switch to the wrong device and switchCameraPosition() would stop working. Two issues in CameraCapturer: 1. options.position was never updated after device selection via fallback, so reconnects re-ran fallback logic instead of re-selecting the same camera. 2. The position property returned .unspecified when device was nil (between stop/start), causing switchCameraPosition() to always fail after reconnect. Fix: persist the resolved device position in options after startCapture, and fall back to options.position instead of .unspecified when device is nil. Co-Authored-By: Claude Opus 4.6 (1M context) --- Sources/LiveKit/Track/Capturers/CameraCapturer.swift | 10 ++++++++-- 1 file changed, 8 insertions(+), 2 deletions(-) diff --git a/Sources/LiveKit/Track/Capturers/CameraCapturer.swift b/Sources/LiveKit/Track/Capturers/CameraCapturer.swift index 4288bacc5..ef5d768ae 100644 --- a/Sources/LiveKit/Track/Capturers/CameraCapturer.swift +++ b/Sources/LiveKit/Track/Capturers/CameraCapturer.swift @@ -31,7 +31,7 @@ public class CameraCapturer: VideoCapturer, @unchecked Sendable { public var device: AVCaptureDevice? { _cameraCapturerState.device } /// Current position of the device - public var position: AVCaptureDevice.Position { _cameraCapturerState.device?.position ?? .unspecified } + public var position: AVCaptureDevice.Position { _cameraCapturerState.device?.position ?? _cameraCapturerState.options.position } @objc public var options: CameraCaptureOptions { _cameraCapturerState.options } @@ -288,7 +288,13 @@ public class CameraCapturer: VideoCapturer, @unchecked Sendable { try await capturer.startCapture(with: device, format: selectedFormat.format, fps: selectedFps) // Update internal vars - _cameraCapturerState.mutate { $0.device = device } + _cameraCapturerState.mutate { + $0.device = device + // Persist the resolved position so reconnects re-select the same camera. + if device.position != .unspecified, $0.options.position != device.position { + $0.options = $0.options.copyWith(position: .value(device.position)) + } + } return true }