diff --git a/.changes/camera-switching b/.changes/camera-switching new file mode 100644 index 000000000..b7b78a766 --- /dev/null +++ b/.changes/camera-switching @@ -0,0 +1 @@ +patch type="fixed" "Camera position lost after full reconnect" diff --git a/Sources/LiveKit/Track/Capturers/CameraCapturer.swift b/Sources/LiveKit/Track/Capturers/CameraCapturer.swift index f939ffebc..675a2ec98 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 } diff --git a/Sources/LiveKit/Types/Options/CameraCaptureOptions+Copy.swift b/Sources/LiveKit/Types/Options/CameraCaptureOptions+Copy.swift index 2ca49fc41..9eed37084 100644 --- a/Sources/LiveKit/Types/Options/CameraCaptureOptions+Copy.swift +++ b/Sources/LiveKit/Types/Options/CameraCaptureOptions+Copy.swift @@ -17,6 +17,22 @@ @preconcurrency import AVFoundation public extension CameraCaptureOptions { + #if !os(visionOS) + func copyWith(deviceType: ValueOrAbsent = .absent, + device: ValueOrAbsent = .absent, + position: ValueOrAbsent = .absent, + preferredFormat: ValueOrAbsent = .absent, + dimensions: ValueOrAbsent = .absent, + fps: ValueOrAbsent = .absent) -> CameraCaptureOptions + { + CameraCaptureOptions(deviceType: deviceType.value(ifAbsent: self.deviceType), + device: device.value(ifAbsent: self.device), + position: position.value(ifAbsent: self.position), + preferredFormat: preferredFormat.value(ifAbsent: self.preferredFormat), + dimensions: dimensions.value(ifAbsent: self.dimensions), + fps: fps.value(ifAbsent: self.fps)) + } + #else func copyWith(device: ValueOrAbsent = .absent, position: ValueOrAbsent = .absent, preferredFormat: ValueOrAbsent = .absent, @@ -29,4 +45,5 @@ public extension CameraCaptureOptions { dimensions: dimensions.value(ifAbsent: self.dimensions), fps: fps.value(ifAbsent: self.fps)) } + #endif }