fix: Fix torch not working on iOS (#2031)
				
					
				
			* fix: Fix `torch` not working on iOS * Format * fix: Use `withSessionLock` and `withDeviceLock` * Update CameraSession.swift * Update RecordingSession.swift
This commit is contained in:
		| @@ -70,6 +70,7 @@ class CameraConfiguration { | |||||||
|     let orientationChanged: Bool |     let orientationChanged: Bool | ||||||
|     let formatChanged: Bool |     let formatChanged: Bool | ||||||
|     let sidePropsChanged: Bool |     let sidePropsChanged: Bool | ||||||
|  |     let torchChanged: Bool | ||||||
|     let zoomChanged: Bool |     let zoomChanged: Bool | ||||||
|  |  | ||||||
|     let audioSessionChanged: Bool |     let audioSessionChanged: Bool | ||||||
| @@ -100,7 +101,9 @@ class CameraConfiguration { | |||||||
|       // format (depends on cameraId) |       // format (depends on cameraId) | ||||||
|       formatChanged = inputChanged || left?.format != right.format |       formatChanged = inputChanged || left?.format != right.format | ||||||
|       // side-props (depends on format) |       // side-props (depends on format) | ||||||
|       sidePropsChanged = formatChanged || left?.fps != right.fps || left?.enableLowLightBoost != right.enableLowLightBoost || left?.torch != right.torch |       sidePropsChanged = formatChanged || left?.fps != right.fps || left?.enableLowLightBoost != right.enableLowLightBoost | ||||||
|  |       // torch (depends on isActive) | ||||||
|  |       torchChanged = left?.isActive != right.isActive || left?.torch != right.torch | ||||||
|       // zoom (depends on format) |       // zoom (depends on format) | ||||||
|       zoomChanged = formatChanged || left?.zoom != right.zoom |       zoomChanged = formatChanged || left?.zoom != right.zoom | ||||||
|  |  | ||||||
|   | |||||||
| @@ -175,16 +175,13 @@ extension CameraSession { | |||||||
|   /** |   /** | ||||||
|    Configures the active format (`format`) |    Configures the active format (`format`) | ||||||
|    */ |    */ | ||||||
|   func configureFormat(configuration: CameraConfiguration) throws { |   func configureFormat(configuration: CameraConfiguration, device: AVCaptureDevice) throws { | ||||||
|     guard let targetFormat = configuration.format else { |     guard let targetFormat = configuration.format else { | ||||||
|       // No format was set, just use the default. |       // No format was set, just use the default. | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     ReactLogger.log(level: .info, message: "Configuring Format (\(targetFormat))...") |     ReactLogger.log(level: .info, message: "Configuring Format (\(targetFormat))...") | ||||||
|     guard let device = videoDeviceInput?.device else { |  | ||||||
|       throw CameraError.session(.cameraNotReady) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     let currentFormat = CameraDeviceFormat(fromFormat: device.activeFormat) |     let currentFormat = CameraDeviceFormat(fromFormat: device.activeFormat) | ||||||
|     if currentFormat == targetFormat { |     if currentFormat == targetFormat { | ||||||
| @@ -207,13 +204,9 @@ extension CameraSession { | |||||||
|   // pragma MARK: Side-Props |   // pragma MARK: Side-Props | ||||||
|  |  | ||||||
|   /** |   /** | ||||||
|    Configures format-dependant "side-props" (`fps`, `lowLightBoost`, `torch`) |    Configures format-dependant "side-props" (`fps`, `lowLightBoost`) | ||||||
|    */ |    */ | ||||||
|   func configureSideProps(configuration: CameraConfiguration) throws { |   func configureSideProps(configuration: CameraConfiguration, device: AVCaptureDevice) throws { | ||||||
|     guard let device = videoDeviceInput?.device else { |  | ||||||
|       throw CameraError.session(.cameraNotReady) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     // Configure FPS |     // Configure FPS | ||||||
|     if let fps = configuration.fps { |     if let fps = configuration.fps { | ||||||
|       let supportsGivenFps = device.activeFormat.videoSupportedFrameRateRanges.contains { range in |       let supportsGivenFps = device.activeFormat.videoSupportedFrameRateRanges.contains { range in | ||||||
| @@ -238,13 +231,20 @@ extension CameraSession { | |||||||
|       } |       } | ||||||
|       device.automaticallyEnablesLowLightBoostWhenAvailable = configuration.enableLowLightBoost |       device.automaticallyEnablesLowLightBoostWhenAvailable = configuration.enableLowLightBoost | ||||||
|     } |     } | ||||||
|  |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    Configures the torch. | ||||||
|  |    The CaptureSession has to be running for the Torch to work. | ||||||
|  |    */ | ||||||
|  |   func configureTorch(configuration: CameraConfiguration, device: AVCaptureDevice) throws { | ||||||
|     // Configure Torch |     // Configure Torch | ||||||
|     let torchMode = configuration.torch.toTorchMode() |     let torchMode = configuration.torch.toTorchMode() | ||||||
|     if device.torchMode != torchMode { |     if device.torchMode != torchMode { | ||||||
|       guard device.hasTorch else { |       guard device.hasTorch else { | ||||||
|         throw CameraError.device(.flashUnavailable) |         throw CameraError.device(.flashUnavailable) | ||||||
|       } |       } | ||||||
|  |  | ||||||
|       device.torchMode = torchMode |       device.torchMode = torchMode | ||||||
|       if torchMode == .on { |       if torchMode == .on { | ||||||
|         try device.setTorchModeOn(level: 1.0) |         try device.setTorchModeOn(level: 1.0) | ||||||
| @@ -257,10 +257,7 @@ extension CameraSession { | |||||||
|   /** |   /** | ||||||
|    Configures zoom (`zoom`) |    Configures zoom (`zoom`) | ||||||
|    */ |    */ | ||||||
|   func configureZoom(configuration: CameraConfiguration) throws { |   func configureZoom(configuration: CameraConfiguration, device: AVCaptureDevice) { | ||||||
|     guard let device = videoDeviceInput?.device else { |  | ||||||
|       throw CameraError.session(.cameraNotReady) |  | ||||||
|     } |  | ||||||
|     guard let zoom = configuration.zoom else { |     guard let zoom = configuration.zoom else { | ||||||
|       return |       return | ||||||
|     } |     } | ||||||
|   | |||||||
| @@ -114,56 +114,50 @@ 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 { | ||||||
|           // Lock Capture Session for configuration |           try self.withSessionLock { | ||||||
|           ReactLogger.log(level: .info, message: "Beginning CameraSession configuration...") |             // 1. Update input device | ||||||
|           self.captureSession.beginConfiguration() |             if difference.inputChanged { | ||||||
|  |               try self.configureDevice(configuration: config) | ||||||
|           // 1. Update input device |             } | ||||||
|           if difference.inputChanged { |             // 2. Update outputs | ||||||
|             try self.configureDevice(configuration: config) |             if difference.outputsChanged { | ||||||
|  |               try self.configureOutputs(configuration: config) | ||||||
|  |             } | ||||||
|  |             // 3. Update output orientation | ||||||
|  |             if difference.orientationChanged { | ||||||
|  |               self.configureOrientation(configuration: config) | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|           // 2. Update outputs |  | ||||||
|           if difference.outputsChanged { |  | ||||||
|             try self.configureOutputs(configuration: config) |  | ||||||
|           } |  | ||||||
|           // 3. Update output orientation |  | ||||||
|           if difference.orientationChanged { |  | ||||||
|             self.configureOrientation(configuration: config) |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           // Unlock Capture Session again and submit configuration to Hardware |  | ||||||
|           self.captureSession.commitConfiguration() |  | ||||||
|           ReactLogger.log(level: .info, message: "Committed CameraSession configuration!") |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 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 { | ||||||
|           guard let device = self.videoDeviceInput?.device else { |           try self.withDeviceLock { device in | ||||||
|             throw CameraError.session(.cameraNotReady) |             // 4. Configure format | ||||||
|  |             if difference.formatChanged { | ||||||
|  |               try self.configureFormat(configuration: config, device: device) | ||||||
|  |             } | ||||||
|  |             // 5. Configure side-props (fps, lowLightBoost) | ||||||
|  |             if difference.sidePropsChanged { | ||||||
|  |               try self.configureSideProps(configuration: config, device: device) | ||||||
|  |             } | ||||||
|  |             // 6. Configure zoom | ||||||
|  |             if difference.zoomChanged { | ||||||
|  |               self.configureZoom(configuration: config, device: device) | ||||||
|  |             } | ||||||
|           } |           } | ||||||
|           ReactLogger.log(level: .info, message: "Beginning CaptureDevice configuration...") |  | ||||||
|           try device.lockForConfiguration() |  | ||||||
|  |  | ||||||
|           // 4. Configure format |  | ||||||
|           if difference.formatChanged { |  | ||||||
|             try self.configureFormat(configuration: config) |  | ||||||
|           } |  | ||||||
|           // 5. Configure side-props (fps, lowLightBoost) |  | ||||||
|           if difference.sidePropsChanged { |  | ||||||
|             try self.configureSideProps(configuration: config) |  | ||||||
|           } |  | ||||||
|           // 6. Configure zoom |  | ||||||
|           if difference.zoomChanged { |  | ||||||
|             try self.configureZoom(configuration: config) |  | ||||||
|           } |  | ||||||
|  |  | ||||||
|           device.unlockForConfiguration() |  | ||||||
|           ReactLogger.log(level: .info, message: "Committed CaptureDevice configuration!") |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         // 6. Start or stop the session if needed |         // 7. Start or stop the session if needed | ||||||
|         self.checkIsActive(configuration: config) |         self.checkIsActive(configuration: config) | ||||||
|  |  | ||||||
|  |         // 8. 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) | ||||||
|  |           } | ||||||
|  |         } | ||||||
|  |  | ||||||
|         // Update successful, set the new configuration! |         // Update successful, set the new configuration! | ||||||
|         self.configuration = config |         self.configuration = config | ||||||
|       } catch { |       } catch { | ||||||
| @@ -191,6 +185,41 @@ 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`) | ||||||
|    */ |    */ | ||||||
|   | |||||||
| @@ -36,10 +36,16 @@ class RecordingSession { | |||||||
|   private var hasWrittenFirstVideoFrame = false |   private var hasWrittenFirstVideoFrame = false | ||||||
|   private var isFinishing = false |   private var isFinishing = false | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    Gets the file URL of the recorded video. | ||||||
|  |    */ | ||||||
|   var url: URL { |   var url: URL { | ||||||
|     return assetWriter.outputURL |     return assetWriter.outputURL | ||||||
|   } |   } | ||||||
|  |  | ||||||
|  |   /** | ||||||
|  |    Get the duration (in seconds) of the recorded video. | ||||||
|  |    */ | ||||||
|   var duration: Double { |   var duration: Double { | ||||||
|     guard let latestTimestamp = latestTimestamp, |     guard let latestTimestamp = latestTimestamp, | ||||||
|           let initialTimestamp = initialTimestamp else { |           let initialTimestamp = initialTimestamp else { | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user