Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
38 changes: 36 additions & 2 deletions package/ios/Core/CameraSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,18 @@ final class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegat
NotificationCenter.default.removeObserver(self,
name: AVAudioSession.interruptionNotification,
object: AVAudioSession.sharedInstance)
let cameraCaptureSession = captureSession
CameraQueues.cameraQueue.async {
if cameraCaptureSession.isRunning {
cameraCaptureSession.stopRunning()
}
}
let cameraAudioSession = audioCaptureSession
CameraQueues.audioQueue.async {
if cameraAudioSession.isRunning {
cameraAudioSession.stopRunning()
}
}
}

/**
Expand All @@ -108,13 +120,20 @@ final class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegat
Any changes in here will be re-configured only if required, and under a lock (in this case, the serial cameraQueue DispatchQueue).
The `configuration` object is a copy of the currently active configuration that can be modified by the caller in the lambda.
*/
func configure(_ lambda: @escaping (_ configuration: CameraConfiguration) throws -> Void) {
func configure(_ lambda: @escaping (_ configuration: CameraConfiguration) throws -> Void,
completion: (() -> Void)? = nil) {
initialize()

VisionLogger.log(level: .info, message: "configure { ... }: Waiting for lock...")

let completionBlock = completion

// Set up Camera (Video) Capture Session (on camera queue, acts like a lock)
CameraQueues.cameraQueue.async {
defer {
completionBlock?()
}

// Let caller configure a new configuration for the Camera.
let config = CameraConfiguration(copyOf: self.configuration)
do {
Expand Down Expand Up @@ -247,6 +266,21 @@ final class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegat
}
}

/**
Gracefully stop streaming and tear down any active outputs. Completion executes on the camera queue.
*/
func shutdown(completion: (() -> Void)? = nil) {
configure({ config in
config.photo = .disabled
config.video = .disabled
config.audio = .disabled
config.codeScanner = .disabled
config.enableLocation = false
config.torch = .off
config.isActive = false
}, completion: completion)
}

/**
Starts or stops the CaptureSession if needed (`isActive`)
*/
Expand All @@ -265,7 +299,7 @@ final class CameraSession: NSObject, AVCaptureVideoDataOutputSampleBufferDelegat
}
}

public final func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
final func captureOutput(_ captureOutput: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
switch captureOutput {
case is AVCaptureVideoDataOutput:
onVideoFrame(sampleBuffer: sampleBuffer, orientation: connection.orientation, isMirrored: connection.isVideoMirrored)
Expand Down
28 changes: 28 additions & 0 deletions package/ios/React/CameraView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,7 @@ public final class CameraView: UIView, CameraSessionDelegate, PreviewViewDelegat
var isMounted = false
private var currentConfigureCall: DispatchTime?
private let fpsSampleCollector = FpsSampleCollector()
private var didScheduleShutdown = false

// CameraView+Zoom
var pinchGestureRecognizer: UIPinchGestureRecognizer?
Expand All @@ -129,12 +130,14 @@ public final class CameraView: UIView, CameraSessionDelegate, PreviewViewDelegat
super.willMove(toSuperview: newSuperview)

if newSuperview != nil {
didScheduleShutdown = false
fpsSampleCollector.start()
if !isMounted {
isMounted = true
onViewReadyEvent?(nil)
}
} else {
shutdownCameraSession()
fpsSampleCollector.stop()
}
}
Expand Down Expand Up @@ -283,6 +286,31 @@ public final class CameraView: UIView, CameraSessionDelegate, PreviewViewDelegat
UIApplication.shared.isIdleTimerDisabled = isActive
}

private func shutdownCameraSession() {
if didScheduleShutdown {
return
}
didScheduleShutdown = true

UIApplication.shared.isIdleTimerDisabled = false

#if VISION_CAMERA_ENABLE_FRAME_PROCESSORS
frameProcessor = nil
#endif

let logSlowShutdownWarning = DispatchWorkItem {
VisionLogger.log(level: .warning, message: "CameraSession shutdown is still running after 2 seconds.")
}
CameraQueues.cameraQueue.asyncAfter(deadline: .now() + .seconds(2), execute: logSlowShutdownWarning)

cameraSession.shutdown { [weak self] in
logSlowShutdownWarning.cancel()
DispatchQueue.main.async {
self?.didScheduleShutdown = false
}
}
}

func updatePreview() {
if preview && previewView == nil {
// Create PreviewView and add it
Expand Down