perf: Fix double configuration flicker on fast device change (#2537)

* fix: Fix double configuration on device change

Fixes a situation that happened on every device change (or initial mount) where the device was configured after the session, separately, instead of just all at once causing an additonal delay/flicker of the prevjew.

* Try?

* Format

* Update CameraSession.swift

* Use `defer`

* Throw `.device`
This commit is contained in:
Marc Rousavy 2024-02-13 13:32:11 +01:00 committed by GitHub
parent 919e6c9fe8
commit b3a88278de
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194

View File

@ -117,7 +117,8 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
do { do {
// If needed, configure the AVCaptureSession (inputs, outputs) // If needed, configure the AVCaptureSession (inputs, outputs)
if difference.isSessionConfigurationDirty { if difference.isSessionConfigurationDirty {
try self.withSessionLock { self.captureSession.beginConfiguration()
// 1. Update input device // 1. Update input device
if difference.inputChanged { if difference.inputChanged {
try self.configureDevice(configuration: config) try self.configureDevice(configuration: config)
@ -135,11 +136,18 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
self.configureOrientation(configuration: config) self.configureOrientation(configuration: config)
} }
} }
guard let device = self.videoDeviceInput?.device else {
throw CameraError.device(.noDevice)
} }
// If needed, configure the AVCaptureDevice (format, zoom, low-light-boost, ..) // If needed, configure the AVCaptureDevice (format, zoom, low-light-boost, ..)
if difference.isDeviceConfigurationDirty { if difference.isDeviceConfigurationDirty {
try self.withDeviceLock { device in try device.lockForConfiguration()
defer {
device.unlockForConfiguration()
}
// 4. Configure format // 4. Configure format
if difference.formatChanged { if difference.formatChanged {
try self.configureFormat(configuration: config, device: device) try self.configureFormat(configuration: config, device: device)
@ -162,6 +170,11 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
self.configureExposure(configuration: config, device: device) self.configureExposure(configuration: config, device: device)
} }
} }
if difference.isSessionConfigurationDirty {
// We commit the session config updates AFTER the device config,
// that way we can also batch those changes into one update instead of doing two updates.
self.captureSession.commitConfiguration()
} }
// 9. Start or stop the session if needed // 9. Start or stop the session if needed
@ -169,9 +182,11 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
// 10. Enable or disable the Torch if needed (requires session to be running) // 10. Enable or disable the Torch if needed (requires session to be running)
if difference.torchChanged { if difference.torchChanged {
try self.withDeviceLock { device in try device.lockForConfiguration()
try self.configureTorch(configuration: config, device: device) defer {
device.unlockForConfiguration()
} }
try self.configureTorch(configuration: config, device: device)
} }
// Notify about Camera initialization // Notify about Camera initialization
@ -206,41 +221,6 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
} }
} }
/**
Runs the given [lambda] under an AVCaptureSession configuration lock (`beginConfiguration()`)
*/
private func withSessionLock(_ lambda: () throws -> Void) throws {
// Lock Capture Session for configuration
ReactLogger.log(level: .info, message: "Beginning CameraSession configuration...")
captureSession.beginConfiguration()
defer {
// Unlock Capture Session again and submit configuration to Hardware
self.captureSession.commitConfiguration()
ReactLogger.log(level: .info, message: "Committed CameraSession configuration!")
}
// Call lambda
try lambda()
}
/**
Runs the given [lambda] under an AVCaptureDevice configuration lock (`lockForConfiguration()`)
*/
private func withDeviceLock(_ lambda: (_ device: AVCaptureDevice) throws -> Void) throws {
guard let device = videoDeviceInput?.device else {
throw CameraError.session(.cameraNotReady)
}
ReactLogger.log(level: .info, message: "Beginning CaptureDevice configuration...")
try device.lockForConfiguration()
defer {
device.unlockForConfiguration()
ReactLogger.log(level: .info, message: "Committed CaptureDevice configuration!")
}
// Call lambda with Device
try lambda(device)
}
/** /**
Starts or stops the CaptureSession if needed (`isActive`) Starts or stops the CaptureSession if needed (`isActive`)
*/ */