Created
May 11, 2024 13:19
-
-
Save LucasAbijmil/b613c9ca4cff664a188e222535f17795 to your computer and use it in GitHub Desktop.
Capture Live Photo
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
| protocol CameraService { | |
| var delegate: CameraServiceDelegate? { get set } | |
| func start() async | |
| func capture() | |
| } | |
| @MainActor | |
| protocol CameraServiceDelegate: AnyObject { | |
| func didRecord(livePhoto: URL) | |
| func didReceive(error: Error) | |
| } | |
| final class DefaultCameraService: NSObject, CameraService { | |
| weak var delegate: CameraServiceDelegate? | |
| private var captureSession = AVCaptureSession() | |
| private let photoOutput = AVCapturePhotoOutput() | |
| func start() async { | |
| guard !captureSession.isRunning else { return } | |
| captureSession.startRunning() | |
| captureSession.beginConfiguration() | |
| captureSession.sessionPreset = .photo | |
| guard let frontCameraDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front), | |
| let frontCameraDeviceInput = try? AVCaptureDeviceInput(device: frontCameraDevice), | |
| let microphoneDevice = AVCaptureDevice.default(for: .audio), | |
| let microphoneDeviceInput = try? AVCaptureDeviceInput(device: microphoneDevice), | |
| captureSession.canAddInput(frontCameraDeviceInput), | |
| captureSession.canAddInput(microphoneDeviceInput) else { | |
| fatalError() | |
| } | |
| captureSession.addInput(frontCameraDeviceInput) | |
| captureSession.addInput(microphoneDeviceInput) | |
| guard captureSession.canAddOutput(photoOutput) else { | |
| fatalError() | |
| } | |
| captureSession.addOutput(photoOutput) | |
| photoOutput.isAutoDeferredPhotoDeliveryEnabled = photoOutput.isAutoDeferredPhotoDeliverySupported | |
| photoOutput.isFastCapturePrioritizationEnabled = photoOutput.isFastCapturePrioritizationSupported | |
| photoOutput.isResponsiveCaptureEnabled = photoOutput.isResponsiveCaptureSupported | |
| photoOutput.isZeroShutterLagEnabled = photoOutput.isZeroShutterLagSupported | |
| photoOutput.isAppleProRAWEnabled = photoOutput.isAppleProRAWSupported | |
| photoOutput.isContentAwareDistortionCorrectionEnabled = photoOutput.isContentAwareDistortionCorrectionSupported | |
| photoOutput.isLivePhotoCaptureEnabled = photoOutput.isLivePhotoCaptureSupported | |
| photoOutput.isLivePhotoAutoTrimmingEnabled = true | |
| photoOutput.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliverySupported | |
| photoOutput.isPortraitEffectsMatteDeliveryEnabled = photoOutput.isPortraitEffectsMatteDeliverySupported | |
| photoOutput.isVirtualDeviceConstituentPhotoDeliveryEnabled = photoOutput.isVirtualDeviceConstituentPhotoDeliverySupported | |
| captureSession.commitConfiguration() | |
| } | |
| func capture() { | |
| let photoSettings = AVCapturePhotoSettings() | |
| photoSettings.flashMode = .auto | |
| photoSettings.isAutoRedEyeReductionEnabled = photoOutput.isAutoRedEyeReductionSupported | |
| photoSettings.maxPhotoDimensions = photoOutput.maxPhotoDimensions | |
| photoSettings.photoQualityPrioritization = photoOutput.maxPhotoQualityPrioritization | |
| photoSettings.isCameraCalibrationDataDeliveryEnabled = photoOutput.isCameraCalibrationDataDeliverySupported | |
| photoSettings.isAutoContentAwareDistortionCorrectionEnabled = photoOutput.isContentAwareDistortionCorrectionSupported | |
| photoSettings.isDepthDataDeliveryEnabled = photoOutput.isDepthDataDeliverySupported | |
| photoSettings.isPortraitEffectsMatteDeliveryEnabled = photoOutput.isPortraitEffectsMatteDeliverySupported | |
| photoSettings.livePhotoMovieFileURL = URL.temporaryDirectory.appendingPathComponent(UUID().uuidString, conformingTo: .quickTimeMovie) | |
| photoSettings.livePhotoVideoCodecType = .hevc | |
| photoOutput.capturePhoto(with: photoSettings, delegate: self) | |
| } | |
| private func stop() { | |
| captureSession.stopRunning() | |
| captureSession = AVCaptureSession() | |
| } | |
| } | |
| extension DefaultCameraService: AVCapturePhotoCaptureDelegate { | |
| func photoOutput(_ output: AVCapturePhotoOutput, didFinishCapturingDeferredPhotoProxy deferredPhotoProxy: AVCaptureDeferredPhotoProxy?, error: Error?) {} | |
| func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {} | |
| func photoOutput( | |
| _ output: AVCapturePhotoOutput, | |
| didFinishProcessingLivePhotoToMovieFileAt outputFileURL: URL, | |
| duration: CMTime, | |
| photoDisplayTime: CMTime, | |
| resolvedSettings: AVCaptureResolvedPhotoSettings, | |
| error: Error? | |
| ) { | |
| MainActor.assumeIsolated { | |
| if let error { | |
| delegate?.didReceive(error: error) | |
| } else { | |
| delegate?.didRecord(livePhoto: outputFileURL) | |
| } | |
| } | |
| stop() | |
| } | |
| } |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment