Caps-iOS/Caps/Camera/CameraManager.swift

173 lines
4.6 KiB
Swift
Raw Normal View History

2022-06-10 21:20:49 +02:00
import Foundation
import AVFoundation
class CameraManager: ObservableObject {
enum Status {
case unconfigured
case configured
case unauthorized
case failed
}
static let shared = CameraManager()
@Published var error: CameraError?
let session = AVCaptureSession()
private let sessionQueue = DispatchQueue(label: "de.christophhagen.cam")
private let videoOutput = AVCaptureVideoDataOutput()
private let photoOutput = AVCapturePhotoOutput()
private var status = Status.unconfigured
private init() {
configure()
}
private func set(error: CameraError?) {
DispatchQueue.main.async {
self.error = error
}
}
private func checkPermissions() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .notDetermined:
sessionQueue.suspend()
AVCaptureDevice.requestAccess(for: .video) { authorized in
if !authorized {
self.status = .unauthorized
self.set(error: .deniedAuthorization)
}
self.sessionQueue.resume()
}
case .restricted:
status = .unauthorized
set(error: .restrictedAuthorization)
case .denied:
status = .unauthorized
set(error: .deniedAuthorization)
case .authorized:
break
@unknown default:
status = .unauthorized
set(error: .unknownAuthorization)
}
}
private func configureCaptureSession() {
guard status == .unconfigured else {
return
}
session.beginConfiguration()
session.sessionPreset = .photo
defer {
session.commitConfiguration()
}
let device = AVCaptureDevice.default(
.builtInWideAngleCamera,
for: .video,
position: .back)
guard let camera = device else {
set(error: .cameraUnavailable)
status = .failed
return
}
let cameraInput: AVCaptureDeviceInput
do {
cameraInput = try AVCaptureDeviceInput(device: camera)
} catch {
set(error: .createCaptureInput(error))
status = .failed
return
}
guard session.canAddInput(cameraInput) else {
set(error: .cannotAddInput)
status = .failed
return
}
session.addInput(cameraInput)
if session.canAddOutput(videoOutput) {
session.addOutput(videoOutput)
videoOutput.videoSettings =
[kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA]
let videoConnection = videoOutput.connection(with: .video)
videoConnection?.videoOrientation = .portrait
} else {
set(error: .cannotAddOutput)
status = .failed
return
}
guard session.canAddOutput(photoOutput) else {
set(error: .cannotAddOutput)
status = .failed
return
}
session.addOutput(photoOutput)
photoOutput.isHighResolutionCaptureEnabled = true
photoOutput.isDepthDataDeliveryEnabled = false
photoOutput.isLivePhotoCaptureEnabled = false
status = .configured
}
private func configure() {
checkPermissions()
sessionQueue.async {
self.configureCaptureSession()
self.session.startRunning()
}
}
func setVideoDelegate(_ delegate: AVCaptureVideoDataOutputSampleBufferDelegate,
queue: DispatchQueue) {
sessionQueue.async {
self.videoOutput.setSampleBufferDelegate(delegate, queue: queue)
}
}
func stopVideoCaptureSession() {
sessionQueue.async {
guard self.status == .configured else {
return
}
guard self.session.isRunning else {
return
}
self.session.stopRunning()
}
}
func startVideoCapture() {
guard status == .configured else {
return
}
sessionQueue.async {
guard !self.session.isRunning else {
return
}
self.session.startRunning()
}
}
// MARK: Photo Capture
func capturePhoto(delegate: AVCapturePhotoCaptureDelegate) {
sessionQueue.async {
let photoSettings = AVCapturePhotoSettings()
photoSettings.flashMode = .off
self.photoOutput.capturePhoto(with: photoSettings, delegate: delegate)
}
}
}