Refactoring of FrameHandler.

This commit is contained in:
Maxime Delporte 2025-05-06 19:01:34 +02:00
parent 98257dc480
commit 16ca93918d

View File

@ -0,0 +1,84 @@
//
// FrameHandler.swift
// macamera
//
// Created by Maxime on 06/05/2025.
//
// Original Repository : https://github.com/daved01/LiveCameraSwiftUI/tree/main
import AVFoundation
import CoreImage
class FrameHandler: NSObject, ObservableObject {
@Published var frame: CGImage?
private var permissionGranted: Bool = true
private let captureSession = AVCaptureSession()
private let context = CIContext()
override init() {
super.init()
Task.detached(priority: .background) {
self.permissionGranted = await self.checkPermission()
self.setupCaptureSession()
}
}
}
// MARK: - Convenience Methods
extension FrameHandler {
private func checkPermission() async -> Bool {
switch AVCaptureDevice.authorizationStatus(for: .video) {
case .notDetermined:
return await AVCaptureDevice.requestAccess(for: .video)
case .authorized:
return true
default:
return false
}
}
private func setupCaptureSession() {
let videoOutput = AVCaptureVideoDataOutput()
guard permissionGranted else { return }
guard let videoDevice = AVCaptureDevice.default(.builtInDualWideCamera, for: .video, position: .back) else { return }
guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoDevice) else { return }
guard captureSession.canAddInput(videoDeviceInput) else { return }
captureSession.addInput(videoDeviceInput)
videoOutput.setSampleBufferDelegate(self, queue: DispatchQueue(label: "sampleBufferQueue"))
captureSession.addOutput(videoOutput)
videoOutput.connection(with: .video)?.videoRotationAngle = 90
captureSession.startRunning()
}
}
// MARK: - AVCaptureVideoDataOutputSampleBufferDelegate
extension FrameHandler: AVCaptureVideoDataOutputSampleBufferDelegate {
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
guard let cgImage = imageFromSampleBuffer(sampleBuffer: sampleBuffer) else { return }
// All UI updates should be/ must be performed on the main queue.
DispatchQueue.main.async { [unowned self] in
self.frame = cgImage
}
}
private func imageFromSampleBuffer(sampleBuffer: CMSampleBuffer) -> CGImage? {
guard let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer) else { return nil }
let ciImage = CIImage(cvPixelBuffer: imageBuffer)
guard let cgImage = context.createCGImage(ciImage, from: ciImage.extent) else { return nil }
return cgImage
}
}