-
Notifications
You must be signed in to change notification settings - Fork 289
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
camera: add PhotoCaptureProcessor and VideoCaptureProcessor
- Loading branch information
1 parent
cdf86e8
commit c0d775b
Showing
3 changed files
with
176 additions
and
0 deletions.
There are no files selected for viewing
This file contains 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
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,91 @@ | ||
// | ||
// PhotoCaptureProcessor.swift | ||
// damus | ||
// | ||
// Created by Suhail Saqan on 8/5/23. | ||
// | ||
|
||
import Foundation | ||
import Photos | ||
|
||
class PhotoCaptureProcessor: NSObject { | ||
private(set) var requestedPhotoSettings: AVCapturePhotoSettings | ||
private(set) var photoOutput: AVCapturePhotoOutput? | ||
|
||
lazy var context = CIContext() | ||
var photoData: Data? | ||
private var maxPhotoProcessingTime: CMTime? | ||
|
||
private let willCapturePhotoAnimation: () -> Void | ||
private let completionHandler: (PhotoCaptureProcessor) -> Void | ||
private let photoProcessingHandler: (Bool) -> Void | ||
|
||
init(with requestedPhotoSettings: AVCapturePhotoSettings, | ||
photoOutput: AVCapturePhotoOutput?, | ||
willCapturePhotoAnimation: @escaping () -> Void, | ||
completionHandler: @escaping (PhotoCaptureProcessor) -> Void, | ||
photoProcessingHandler: @escaping (Bool) -> Void) { | ||
self.requestedPhotoSettings = requestedPhotoSettings | ||
self.willCapturePhotoAnimation = willCapturePhotoAnimation | ||
self.completionHandler = completionHandler | ||
self.photoProcessingHandler = photoProcessingHandler | ||
self.photoOutput = photoOutput | ||
} | ||
|
||
func capturePhoto(settings: AVCapturePhotoSettings) { | ||
if let photoOutput = self.photoOutput { | ||
photoOutput.capturePhoto(with: settings, delegate: self) | ||
} | ||
} | ||
} | ||
|
||
extension PhotoCaptureProcessor: AVCapturePhotoCaptureDelegate { | ||
func photoOutput(_ output: AVCapturePhotoOutput, willBeginCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings) { | ||
maxPhotoProcessingTime = resolvedSettings.photoProcessingTimeRange.start + resolvedSettings.photoProcessingTimeRange.duration | ||
} | ||
|
||
func photoOutput(_ output: AVCapturePhotoOutput, willCapturePhotoFor resolvedSettings: AVCaptureResolvedPhotoSettings) { | ||
DispatchQueue.main.async { | ||
self.willCapturePhotoAnimation() | ||
} | ||
|
||
guard let maxPhotoProcessingTime = maxPhotoProcessingTime else { | ||
return | ||
} | ||
|
||
DispatchQueue.main.async { | ||
self.photoProcessingHandler(true) | ||
} | ||
|
||
let oneSecond = CMTime(seconds: 2, preferredTimescale: 1) | ||
if maxPhotoProcessingTime > oneSecond { | ||
DispatchQueue.main.async { | ||
self.photoProcessingHandler(true) | ||
} | ||
} | ||
} | ||
|
||
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) { | ||
DispatchQueue.main.async { | ||
self.photoProcessingHandler(false) | ||
} | ||
|
||
if let error = error { | ||
print("Error capturing photo: \(error)") | ||
} else { | ||
photoData = photo.fileDataRepresentation() | ||
|
||
} | ||
} | ||
|
||
func photoOutput(_ output: AVCapturePhotoOutput, didFinishCaptureFor resolvedSettings: AVCaptureResolvedPhotoSettings, error: Error?) { | ||
if let error = error { | ||
print("Error capturing photo: \(error)") | ||
return | ||
} | ||
|
||
DispatchQueue.main.async { | ||
self.completionHandler(self) | ||
} | ||
} | ||
} |
This file contains 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,77 @@ | ||
// | ||
// VideoCaptureProcessor.swift | ||
// damus | ||
// | ||
// Created by Suhail Saqan on 8/5/23. | ||
// | ||
|
||
import Foundation | ||
import AVFoundation | ||
import Photos | ||
|
||
class VideoCaptureProcessor: NSObject { | ||
private(set) var movieOutput: AVCaptureMovieFileOutput? | ||
|
||
private let beginHandler: () -> Void | ||
private let completionHandler: (VideoCaptureProcessor, URL) -> Void | ||
private let videoProcessingHandler: (Bool) -> Void | ||
private var session: AVCaptureSession? | ||
|
||
init(movieOutput: AVCaptureMovieFileOutput?, | ||
beginHandler: @escaping () -> Void, | ||
completionHandler: @escaping (VideoCaptureProcessor, URL) -> Void, | ||
videoProcessingHandler: @escaping (Bool) -> Void) { | ||
self.beginHandler = beginHandler | ||
self.completionHandler = completionHandler | ||
self.videoProcessingHandler = videoProcessingHandler | ||
self.movieOutput = movieOutput | ||
} | ||
|
||
func startCapture(session: AVCaptureSession) { | ||
if let movieOutput = self.movieOutput, session.isRunning { | ||
let outputFileURL = uniqueOutputFileURL() | ||
movieOutput.startRecording(to: outputFileURL, recordingDelegate: self) | ||
} | ||
} | ||
|
||
func stopCapture() { | ||
if let movieOutput = self.movieOutput { | ||
if movieOutput.isRecording { | ||
movieOutput.stopRecording() | ||
} | ||
} | ||
} | ||
|
||
private func uniqueOutputFileURL() -> URL { | ||
let tempDirectory = FileManager.default.temporaryDirectory | ||
let fileName = UUID().uuidString + ".mov" | ||
return tempDirectory.appendingPathComponent(fileName) | ||
} | ||
} | ||
|
||
extension VideoCaptureProcessor: AVCaptureFileOutputRecordingDelegate { | ||
|
||
func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) { | ||
DispatchQueue.main.async { | ||
self.beginHandler() | ||
} | ||
} | ||
|
||
func fileOutput(_ output: AVCaptureFileOutput, willFinishRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) { | ||
DispatchQueue.main.async { | ||
self.videoProcessingHandler(true) | ||
} | ||
} | ||
|
||
func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) { | ||
if let error = error { | ||
print("Error capturing video: \(error)") | ||
return | ||
} | ||
|
||
DispatchQueue.main.async { | ||
self.completionHandler(self, outputFileURL) | ||
self.videoProcessingHandler(false) | ||
} | ||
} | ||
} |