Skip to content

Instantly share code, notes, and snippets.

@LucasAbijmil
Created May 11, 2024 13:19
Show Gist options
  • Select an option

  • Save LucasAbijmil/b613c9ca4cff664a188e222535f17795 to your computer and use it in GitHub Desktop.

Select an option

Save LucasAbijmil/b613c9ca4cff664a188e222535f17795 to your computer and use it in GitHub Desktop.
Capture Live Photo
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