camera: add PhotoCaptureProcessor and VideoCaptureProcessor
This commit is contained in:
committed by
William Casarin
parent
476f52562a
commit
88b3c6fe8d
@@ -416,6 +416,8 @@
|
|||||||
9C83F89329A937B900136C08 /* TextViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C83F89229A937B900136C08 /* TextViewWrapper.swift */; };
|
9C83F89329A937B900136C08 /* TextViewWrapper.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9C83F89229A937B900136C08 /* TextViewWrapper.swift */; };
|
||||||
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
|
9CA876E229A00CEA0003B9A3 /* AttachMediaUtility.swift in Sources */ = {isa = PBXBuildFile; fileRef = 9CA876E129A00CE90003B9A3 /* AttachMediaUtility.swift */; };
|
||||||
BA37598A2ABCCDE40018D73B /* ImageResizer.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA3759892ABCCDE30018D73B /* ImageResizer.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 */; };
|
BA4AB0AE2A63B9270070A32A /* AddEmojiView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AD2A63B9270070A32A /* AddEmojiView.swift */; };
|
||||||
BA4AB0B02A63B94D0070A32A /* EmojiListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */; };
|
BA4AB0B02A63B94D0070A32A /* EmojiListItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA4AB0AF2A63B94D0070A32A /* EmojiListItemView.swift */; };
|
||||||
BA693074295D649800ADDB87 /* UserSettingsStore.swift in Sources */ = {isa = PBXBuildFile; fileRef = BA693073295D649800ADDB87 /* UserSettingsStore.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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
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>"; };
|
BA693073295D649800ADDB87 /* UserSettingsStore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserSettingsStore.swift; sourceTree = "<group>"; };
|
||||||
@@ -2258,6 +2262,8 @@
|
|||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
BA3759892ABCCDE30018D73B /* ImageResizer.swift */,
|
BA3759892ABCCDE30018D73B /* ImageResizer.swift */,
|
||||||
|
BA37598B2ABCCE500018D73B /* PhotoCaptureProcessor.swift */,
|
||||||
|
BA37598C2ABCCE500018D73B /* VideoCaptureProcessor.swift */,
|
||||||
);
|
);
|
||||||
path = Camera;
|
path = Camera;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -2713,6 +2719,7 @@
|
|||||||
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
|
4C2859602A12A2BE004746F7 /* SupporterBadge.swift in Sources */,
|
||||||
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
|
4C1A9A2A29DDF54400516EAC /* DamusVideoPlayer.swift in Sources */,
|
||||||
4CA352A22A76AEC5003BB08B /* LikedNotify.swift in Sources */,
|
4CA352A22A76AEC5003BB08B /* LikedNotify.swift in Sources */,
|
||||||
|
BA37598D2ABCCE500018D73B /* PhotoCaptureProcessor.swift in Sources */,
|
||||||
4C9146FD2A2A87C200DDEA40 /* wasm.c in Sources */,
|
4C9146FD2A2A87C200DDEA40 /* wasm.c in Sources */,
|
||||||
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
4C75EFAF28049D350006080F /* NostrFilter.swift in Sources */,
|
||||||
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
4C3EA64C28FF59AC00C48A62 /* bech32_util.c in Sources */,
|
||||||
@@ -2877,6 +2884,7 @@
|
|||||||
4C9AA14A2A4587A6003F49FD /* NotificationStatusModel.swift in Sources */,
|
4C9AA14A2A4587A6003F49FD /* NotificationStatusModel.swift in Sources */,
|
||||||
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
|
4CB9D4A72992D02B00A9A7E4 /* ProfileNameView.swift in Sources */,
|
||||||
4CE4F0F429D779B5005914DB /* PostBox.swift in Sources */,
|
4CE4F0F429D779B5005914DB /* PostBox.swift in Sources */,
|
||||||
|
BA37598E2ABCCE500018D73B /* VideoCaptureProcessor.swift in Sources */,
|
||||||
4C9B0DF32A65C46800CBDA21 /* ProfileEditButton.swift in Sources */,
|
4C9B0DF32A65C46800CBDA21 /* ProfileEditButton.swift in Sources */,
|
||||||
4C32B95F2A9AD44700DC3548 /* Enum.swift in Sources */,
|
4C32B95F2A9AD44700DC3548 /* Enum.swift in Sources */,
|
||||||
4C2859622A12A7F0004746F7 /* GoldSupportGradient.swift in Sources */,
|
4C2859622A12A7F0004746F7 /* GoldSupportGradient.swift in Sources */,
|
||||||
|
|||||||
91
damus/Models/Camera/PhotoCaptureProcessor.swift
Normal file
91
damus/Models/Camera/PhotoCaptureProcessor.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
77
damus/Models/Camera/VideoCaptureProcessor.swift
Normal file
77
damus/Models/Camera/VideoCaptureProcessor.swift
Normal 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)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user