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 formatChanged: Bool | ||||
|     let sidePropsChanged: Bool | ||||
|     let torchChanged: Bool | ||||
|     let zoomChanged: Bool | ||||
|  | ||||
|     let audioSessionChanged: Bool | ||||
| @@ -100,7 +101,9 @@ class CameraConfiguration { | ||||
|       // format (depends on cameraId) | ||||
|       formatChanged = inputChanged || left?.format != right.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) | ||||
|       zoomChanged = formatChanged || left?.zoom != right.zoom | ||||
|  | ||||
|   | ||||
| @@ -175,16 +175,13 @@ extension CameraSession { | ||||
|   /** | ||||
|    Configures the active format (`format`) | ||||
|    */ | ||||
|   func configureFormat(configuration: CameraConfiguration) throws { | ||||
|   func configureFormat(configuration: CameraConfiguration, device: AVCaptureDevice) throws { | ||||
|     guard let targetFormat = configuration.format else { | ||||
|       // No format was set, just use the default. | ||||
|       return | ||||
|     } | ||||
|  | ||||
|     ReactLogger.log(level: .info, message: "Configuring Format (\(targetFormat))...") | ||||
|     guard let device = videoDeviceInput?.device else { | ||||
|       throw CameraError.session(.cameraNotReady) | ||||
|     } | ||||
|  | ||||
|     let currentFormat = CameraDeviceFormat(fromFormat: device.activeFormat) | ||||
|     if currentFormat == targetFormat { | ||||
| @@ -207,13 +204,9 @@ extension CameraSession { | ||||
|   // pragma MARK: Side-Props | ||||
|  | ||||
|   /** | ||||
|    Configures format-dependant "side-props" (`fps`, `lowLightBoost`, `torch`) | ||||
|    Configures format-dependant "side-props" (`fps`, `lowLightBoost`) | ||||
|    */ | ||||
|   func configureSideProps(configuration: CameraConfiguration) throws { | ||||
|     guard let device = videoDeviceInput?.device else { | ||||
|       throw CameraError.session(.cameraNotReady) | ||||
|     } | ||||
|  | ||||
|   func configureSideProps(configuration: CameraConfiguration, device: AVCaptureDevice) throws { | ||||
|     // Configure FPS | ||||
|     if let fps = configuration.fps { | ||||
|       let supportsGivenFps = device.activeFormat.videoSupportedFrameRateRanges.contains { range in | ||||
| @@ -238,13 +231,20 @@ extension CameraSession { | ||||
|       } | ||||
|       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 | ||||
|     let torchMode = configuration.torch.toTorchMode() | ||||
|     if device.torchMode != torchMode { | ||||
|       guard device.hasTorch else { | ||||
|         throw CameraError.device(.flashUnavailable) | ||||
|       } | ||||
|  | ||||
|       device.torchMode = torchMode | ||||
|       if torchMode == .on { | ||||
|         try device.setTorchModeOn(level: 1.0) | ||||
| @@ -257,10 +257,7 @@ extension CameraSession { | ||||
|   /** | ||||
|    Configures zoom (`zoom`) | ||||
|    */ | ||||
|   func configureZoom(configuration: CameraConfiguration) throws { | ||||
|     guard let device = videoDeviceInput?.device else { | ||||
|       throw CameraError.session(.cameraNotReady) | ||||
|     } | ||||
|   func configureZoom(configuration: CameraConfiguration, device: AVCaptureDevice) { | ||||
|     guard let zoom = configuration.zoom else { | ||||
|       return | ||||
|     } | ||||
|   | ||||
| @@ -114,56 +114,50 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC | ||||
|       do { | ||||
|         // If needed, configure the AVCaptureSession (inputs, outputs) | ||||
|         if difference.isSessionConfigurationDirty { | ||||
|           // Lock Capture Session for configuration | ||||
|           ReactLogger.log(level: .info, message: "Beginning CameraSession configuration...") | ||||
|           self.captureSession.beginConfiguration() | ||||
|  | ||||
|           // 1. Update input device | ||||
|           if difference.inputChanged { | ||||
|             try self.configureDevice(configuration: config) | ||||
|           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 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 difference.isDeviceConfigurationDirty { | ||||
|           guard let device = self.videoDeviceInput?.device else { | ||||
|             throw CameraError.session(.cameraNotReady) | ||||
|           try self.withDeviceLock { device in | ||||
|             // 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) | ||||
|  | ||||
|         // 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! | ||||
|         self.configuration = config | ||||
|       } 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`) | ||||
|    */ | ||||
|   | ||||
| @@ -36,10 +36,16 @@ class RecordingSession { | ||||
|   private var hasWrittenFirstVideoFrame = false | ||||
|   private var isFinishing = false | ||||
|  | ||||
|   /** | ||||
|    Gets the file URL of the recorded video. | ||||
|    */ | ||||
|   var url: URL { | ||||
|     return assetWriter.outputURL | ||||
|   } | ||||
|  | ||||
|   /** | ||||
|    Get the duration (in seconds) of the recorded video. | ||||
|    */ | ||||
|   var duration: Double { | ||||
|     guard let latestTimestamp = latestTimestamp, | ||||
|           let initialTimestamp = initialTimestamp else { | ||||
|   | ||||
		Reference in New Issue
	
	Block a user