1
0
mirror of git://jb55.com/damus synced 2024-09-30 00:40:45 +00:00

camera: add PhotoCaptureProcessor and VideoCaptureProcessor

This commit is contained in:
Suhail Saqan 2023-09-19 13:29:40 -07:00 committed by William Casarin
parent 476f52562a
commit 88b3c6fe8d
3 changed files with 176 additions and 0 deletions

View File

@ -416,6 +416,8 @@
9C83F89329A937B900136C08 /* TextViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C83F89229A937B900136C08 /* TextViewWrapper.swift */; };
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759892ABCCDE30018D73B /* ImageResizer.swift */; };
BA37598D2ABCCE500018D73B /* PhotoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */; };
BA37598E2ABCCE500018D73B /* VideoCaptureProcessor.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */; };
BA4AB0AE2A63B9270070A32A /* AddEmojiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */; };
BA4AB0B02A63B94D0070A32A /* EmojiListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */; };
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.swift */; };
@ -1096,6 +1098,8 @@
9C83F89229A937B900136C08 /* TextViewWrapper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TextViewWrapper.swift; sourceTree = "<group>"; };
9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AttachMediaUtility.swift; sourceTree = "<group>"; };
BA3759892ABCCDE30018D73B /* ImageResizer.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ImageResizer.swift; sourceTree = "<group>"; };
BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = PhotoCaptureProcessor.swift; sourceTree = "<group>"; };
BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoCaptureProcessor.swift; sourceTree = "<group>"; };
BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddEmojiView.swift; sourceTree = "<group>"; };
BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiListItemView.swift; sourceTree = "<group>"; };
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
@ -2258,6 +2262,8 @@
isa = PBXGroup;
children = (
BA3759892ABCCDE30018D73B /* ImageResizer.swift */,
BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */,
BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */,
);
path = Camera;
sourceTree = "<group>";
@ -2713,6 +2719,7 @@
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
4CA352A22A76AEC5003BB08B /* LikedNotify.swift in Sources */,
BA37598D2ABCCE500018D73B /* PhotoCaptureProcessor.swift in Sources */,
4C9146FD2A2A87C200DDEA40 /* wasm.c in Sources */,
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
@ -2877,6 +2884,7 @@
4C9AA14A2A4587A6003F49FD /* NotificationStatusModel.swift in Sources */,
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
4CE4F0F429D779B5005914DB /* PostBox.swift in Sources */,
BA37598E2ABCCE500018D73B /* VideoCaptureProcessor.swift in Sources */,
4C9B0DF32A65C46800CBDA21 /* ProfileEditButton.swift in Sources */,
4C32B95F2A9AD44700DC3548 /* Enum.swift in Sources */,
4C2859622A12A7F0004746F7 /* GoldSupportGradient.swift in Sources */,

View File

@ -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)
}
}
}

View File

@ -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)
}
}
}