Caps-iOS/Caps/Capture/CameraView.swift
2022-04-28 15:54:13 +02:00

225 lines
6.5 KiB
Swift

//
// CameraView.swift
// CapFinder
//
// Created by User on 07.02.18.
// Copyright © 2018 User. All rights reserved.
//
import UIKit
import AVFoundation
class CameraView: UIView {
// MARK: UIView overrides
/**
Override for AVCapture
*/
override class var layerClass: AnyClass {
return AVCaptureVideoPreviewLayer.self
}
// MARK: Enums
private enum SessionSetupResult {
case success
case notAuthorized
case configurationFailed
}
// MARK: Variables
var delegate: PhotoCaptureHandlerDelegate? {
get {
return photoCaptureProcessor.delegate
}
set {
photoCaptureProcessor.delegate = newValue
}
}
private let session = AVCaptureSession()
private var isSessionRunning = false
/// Communicate with the session and other session objects on this queue.
private let sessionQueue = DispatchQueue(label: "session queue")
private var setupResult: SessionSetupResult = .success
var videoDeviceInput: AVCaptureDeviceInput!
private let photoOutput = AVCapturePhotoOutput()
private var cameraDevice: AVCaptureDevice?
private let photoCaptureProcessor = PhotoCaptureHandler()
var videoPreviewLayer: AVCaptureVideoPreviewLayer {
return layer as! AVCaptureVideoPreviewLayer
}
// MARK: Life cycle
func configure() {
videoPreviewLayer.session = session
checkPermission()
// Setup the capture session.
sessionQueue.async {
self.configureSession()
}
}
func launch(completionHandler: @escaping (Bool, String?) -> ()) {
sessionQueue.async {
switch self.setupResult {
case .success:
// Only setup observers and start the session running if setup succeeded.
self.session.startRunning()
self.isSessionRunning = self.session.isRunning
case .notAuthorized:
DispatchQueue.main.async {
completionHandler(false, "No camera access")
}
case .configurationFailed:
DispatchQueue.main.async {
completionHandler(false, "Camera error")
}
}
}
}
func complete() {
sessionQueue.async {
if self.setupResult == .success {
self.session.stopRunning()
self.isSessionRunning = self.session.isRunning
}
}
}
// MARK: Photo Capture
func capture() {
sessionQueue.async {
self.photoOutput.capturePhoto(
with: self.photoCaptureProcessor.photoSettings,
delegate: self.photoCaptureProcessor)
}
}
// MARK: Camera permissions
private func checkPermission() {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .authorized: break
case .notDetermined:
sessionQueue.suspend()
AVCaptureDevice.requestAccess(for: .video, completionHandler: { granted in
if !granted {
self.setupResult = .notAuthorized
}
self.sessionQueue.resume()
})
default:
// The user has previously denied access.
setupResult = .notAuthorized
}
}
// Call this on the session queue.
private func configureSession() {
if setupResult != .success {
return
}
session.beginConfiguration()
/*
We do not create an AVCaptureMovieFileOutput when setting up the session because the
AVCaptureMovieFileOutput does not support movie recording with AVCaptureSession.Preset.Photo.
*/
session.sessionPreset = .photo
// Add video input.
guard let backCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back) else {
error("No camera on device")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
let videoDeviceInput: AVCaptureDeviceInput
do {
videoDeviceInput = try AVCaptureDeviceInput(device: backCameraDevice)
} catch {
self.error("Could not create video device input: \(error)")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
guard session.canAddInput(videoDeviceInput) else {
error("Could not add video device input to the session")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
session.addInput(videoDeviceInput)
self.videoDeviceInput = videoDeviceInput
self.cameraDevice = backCameraDevice
DispatchQueue.main.async {
self.videoPreviewLayer.connection?.videoOrientation = .portrait
}
// Add photo output.
guard session.canAddOutput(photoOutput) else {
error("Could not add photo output to the session")
setupResult = .configurationFailed
session.commitConfiguration()
return
}
session.addOutput(photoOutput)
photoOutput.isHighResolutionCaptureEnabled = true
photoOutput.isDepthDataDeliveryEnabled = false
//photoOutput.isDualCameraDualPhotoDeliveryEnabled = false
photoOutput.isLivePhotoCaptureEnabled = false
session.commitConfiguration()
}
func didReceiveTouch(_ touch: UITouch) {
let screenSize = bounds.size
let location = touch.location(in: self)
let focusPoint = CGPoint(x: location.y / screenSize.height, y: 1.0 - location.x / screenSize.width)
log("Focusing on point (\(focusPoint.x),\(focusPoint.y))")
if let device = cameraDevice {
do {
try device.lockForConfiguration()
if device.isFocusPointOfInterestSupported {
device.focusPointOfInterest = focusPoint
device.focusMode = .autoFocus
}
// if device.isExposurePointOfInterestSupported {
// device.exposurePointOfInterest = focusPoint
// device.exposureMode = .autoExpose
// }
device.unlockForConfiguration()
} catch {
self.error("Could not lock device for configuration: \(error)")
}
}
}
}
extension CameraView: Logger { }