Skip to content

Commit

Permalink
fix: AVCaptureSession start running in between configuration updates …
Browse files Browse the repository at this point in the history
…race condition
  • Loading branch information
phantumcode committed Dec 18, 2023
1 parent bebdb60 commit f731a7e
Show file tree
Hide file tree
Showing 3 changed files with 62 additions and 49 deletions.
101 changes: 52 additions & 49 deletions Sources/FaceLiveness/AV/LivenessCaptureSession.swift
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,12 @@ import AVFoundation
class LivenessCaptureSession {
let captureDevice: LivenessCaptureDevice
private let captureQueue = DispatchQueue(label: "com.amazonaws.faceliveness.cameracapturequeue")
private let configurationQueue = DispatchQueue(label: "com.amazonaws.faceliveness.sessionconfiguration", qos: .userInitiated)
let outputDelegate: AVCaptureVideoDataOutputSampleBufferDelegate
var captureSession: AVCaptureSession?
private var deviceInput: AVCaptureDeviceInput?
private var videoOutput: AVCaptureVideoDataOutput?
private var previewLayer: AVCaptureVideoPreviewLayer?

var outputSampleBufferCapturer: OutputSampleBufferCapturer? {
return outputDelegate as? OutputSampleBufferCapturer
Expand All @@ -34,81 +38,80 @@ class LivenessCaptureSession {
frame: frame,
for: captureSession
)

self.previewLayer = previewLayer
return previewLayer
}

func startSession() throws {
teardownCurrentSession()
guard let camera = captureDevice.avCaptureDevice
else { throw LivenessCaptureSessionError.cameraUnavailable }

let cameraInput = try AVCaptureDeviceInput(device: camera)

teardownExistingSession(input: cameraInput)
captureSession = AVCaptureSession()
deviceInput = try AVCaptureDeviceInput(device: camera)
videoOutput = AVCaptureVideoDataOutput()

guard let captureSession = captureSession else {
throw LivenessCaptureSessionError.captureSessionUnavailable
}

try setupInput(cameraInput, for: captureSession)
captureSession.sessionPreset = captureDevice.preset

let videoOutput = AVCaptureVideoDataOutput()
try setupOutput(videoOutput, for: captureSession)

guard let input = deviceInput, captureSession.canAddInput(input) else {
throw LivenessCaptureSessionError.captureSessionInputUnavailable
}
guard let output = videoOutput, captureSession.canAddOutput(output) else {
throw LivenessCaptureSessionError.captureSessionOutputUnavailable
}
try captureDevice.configure()

DispatchQueue.global().async {

configureOutput(output)

configurationQueue.async {
captureSession.beginConfiguration()
captureSession.sessionPreset = self.captureDevice.preset
captureSession.addInput(input)
captureSession.addOutput(output)
captureSession.commitConfiguration()
captureSession.startRunning()
}

videoOutput.setSampleBufferDelegate(
outputDelegate,
queue: captureQueue
)
}

func stopRunning() {
if captureSession?.isRunning == true {
captureSession?.stopRunning()
}
}

private func teardownExistingSession(input: AVCaptureDeviceInput) {
stopRunning()
captureSession?.removeInput(input)
}

private func setupInput(
_ input: AVCaptureDeviceInput,
for captureSession: AVCaptureSession
) throws {
if captureSession.canAddInput(input) {
captureSession.addInput(input)
} else {
throw LivenessCaptureSessionError.captureSessionInputUnavailable
}
teardownCurrentSession()
}

private func setupOutput(
_ output: AVCaptureVideoDataOutput,
for captureSession: AVCaptureSession
) throws {
if captureSession.canAddOutput(output) {
captureSession.addOutput(output)
} else {
throw LivenessCaptureSessionError.captureSessionOutputUnavailable
}

private func configureOutput(_ output: AVCaptureVideoDataOutput) {
output.videoSettings = [
kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA
]

output.connections
.filter(\.isVideoOrientationSupported)
.forEach {
$0.videoOrientation = .portrait
}

output.setSampleBufferDelegate(
outputDelegate,
queue: captureQueue
)
}

private func teardownCurrentSession() {
if captureSession?.isRunning == true {
captureSession?.stopRunning()
}

if let output = videoOutput {
captureSession?.removeOutput(output)
videoOutput = nil
}
if let input = deviceInput {
captureSession?.removeInput(input)
deviceInput = nil
}

previewLayer?.removeFromSuperlayer()
previewLayer?.session = nil
previewLayer = nil
captureSession = nil
}

private func previewLayer(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -172,6 +172,9 @@ public struct FaceLivenessDetectorView: View {
UIScreen.main.brightness = 1.0
}
}
.onDisappear() {
viewModel.stopRecording()
}
.onReceive(viewModel.$livenessState) { output in
switch output.state {
case .completed:
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,12 @@ final class _LivenessViewController: UIViewController {
}
}
}

deinit {
self.previewLayer.removeFromSuperlayer()
(self.previewLayer as? AVCaptureVideoPreviewLayer)?.session = nil
self.previewLayer = nil
}

override func viewDidLoad() {
super.viewDidLoad()
Expand Down Expand Up @@ -110,6 +116,7 @@ extension _LivenessViewController: FaceLivenessViewControllerPresenter {
imageView.frame = self.previewLayer.frame
self.view.addSubview(imageView)
self.previewLayer.removeFromSuperlayer()
(self.previewLayer as? AVCaptureVideoPreviewLayer)?.session = nil
self.viewModel.stopRecording()
}
}
Expand Down

0 comments on commit f731a7e

Please sign in to comment.