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:
parent
6956fded2d
commit
89dfd351e0
@ -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,10 +114,7 @@ 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...")
|
|
||||||
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)
|
||||||
@ -130,40 +127,37 @@ class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, AVC
|
|||||||
if difference.orientationChanged {
|
if difference.orientationChanged {
|
||||||
self.configureOrientation(configuration: config)
|
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)
|
|
||||||
}
|
|
||||||
ReactLogger.log(level: .info, message: "Beginning CaptureDevice configuration...")
|
|
||||||
try device.lockForConfiguration()
|
|
||||||
|
|
||||||
// 4. Configure format
|
// 4. Configure format
|
||||||
if difference.formatChanged {
|
if difference.formatChanged {
|
||||||
try self.configureFormat(configuration: config)
|
try self.configureFormat(configuration: config, device: device)
|
||||||
}
|
}
|
||||||
// 5. Configure side-props (fps, lowLightBoost)
|
// 5. Configure side-props (fps, lowLightBoost)
|
||||||
if difference.sidePropsChanged {
|
if difference.sidePropsChanged {
|
||||||
try self.configureSideProps(configuration: config)
|
try self.configureSideProps(configuration: config, device: device)
|
||||||
}
|
}
|
||||||
// 6. Configure zoom
|
// 6. Configure zoom
|
||||||
if difference.zoomChanged {
|
if difference.zoomChanged {
|
||||||
try self.configureZoom(configuration: config)
|
self.configureZoom(configuration: config, device: device)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
device.unlockForConfiguration()
|
// 7. Start or stop the session if needed
|
||||||
ReactLogger.log(level: .info, message: "Committed CaptureDevice configuration!")
|
|
||||||
}
|
|
||||||
|
|
||||||
// 6. 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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user