Skip to content

Commit

Permalink
perf: Fix double configuration flicker on fast device change (#2537)
Browse files Browse the repository at this point in the history
* 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`
  • Loading branch information
mrousavy authored Feb 13, 2024
1 parent 919e6c9 commit b3a8827
Showing 1 changed file with 56 additions and 76 deletions.
132 changes: 56 additions & 76 deletions package/ios/Core/CameraSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -117,61 +117,76 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
do {
// If needed, configure the AVCaptureSession (inputs, outputs)
if difference.isSessionConfigurationDirty {
try self.withSessionLock {
// 1. Update input device
if difference.inputChanged {
try self.configureDevice(configuration: config)
}
// 2. Update outputs
if difference.outputsChanged {
try self.configureOutputs(configuration: config)
}
// 3. Update Video Stabilization
if difference.videoStabilizationChanged {
self.configureVideoStabilization(configuration: config)
}
// 4. Update output orientation
if difference.orientationChanged {
self.configureOrientation(configuration: config)
}
self.captureSession.beginConfiguration()

// 1. Update input device
if difference.inputChanged {
try self.configureDevice(configuration: config)
}
// 2. Update outputs
if difference.outputsChanged {
try self.configureOutputs(configuration: config)
}
// 3. Update Video Stabilization
if difference.videoStabilizationChanged {
self.configureVideoStabilization(configuration: config)
}
// 4. Update output orientation
if difference.orientationChanged {
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 difference.isDeviceConfigurationDirty {
try self.withDeviceLock { device in
// 4. Configure format
if difference.formatChanged {
try self.configureFormat(configuration: config, device: device)
}
// 5. After step 2. and 4., we also need to configure the PixelFormat.
// This needs to be done AFTER we updated the `format`, as this controls the supported PixelFormats.
if difference.outputsChanged || difference.formatChanged {
try self.configurePixelFormat(configuration: config)
}
// 6. Configure side-props (fps, lowLightBoost)
if difference.sidePropsChanged {
try self.configureSideProps(configuration: config, device: device)
}
// 7. Configure zoom
if difference.zoomChanged {
self.configureZoom(configuration: config, device: device)
}
// 8. Configure exposure bias
if difference.exposureChanged {
self.configureExposure(configuration: config, device: device)
}
try device.lockForConfiguration()
defer {
device.unlockForConfiguration()
}

// 4. Configure format
if difference.formatChanged {
try self.configureFormat(configuration: config, device: device)
}
// 5. After step 2. and 4., we also need to configure the PixelFormat.
// This needs to be done AFTER we updated the `format`, as this controls the supported PixelFormats.
if difference.outputsChanged || difference.formatChanged {
try self.configurePixelFormat(configuration: config)
}
// 6. Configure side-props (fps, lowLightBoost)
if difference.sidePropsChanged {
try self.configureSideProps(configuration: config, device: device)
}
// 7. Configure zoom
if difference.zoomChanged {
self.configureZoom(configuration: config, device: device)
}
// 8. Configure exposure bias
if difference.exposureChanged {
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
self.checkIsActive(configuration: config)

// 10. Enable or disable the Torch if needed (requires session to be running)
if difference.torchChanged {
try self.withDeviceLock { device in
try self.configureTorch(configuration: config, device: device)
try device.lockForConfiguration()
defer {
device.unlockForConfiguration()
}
try self.configureTorch(configuration: config, device: device)
}

// Notify about Camera initialization
Expand Down Expand Up @@ -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`)
*/
Expand Down

0 comments on commit b3a8827

Please sign in to comment.