Goal: Record video with custom SwiftUI Camera using AVFoundation and save to Firestore
Problem: No error messages appear, session is starting, fileOutput is not writing metadata to URL
I have a CameraView
that calls CameraModel
when the button is pressed and held:
var recordButton: some View {
ZStack{
if(model.isRecording){
RoundedRectangle(cornerRadius: 12)
.fill(Color.accentColor)
.frame(width: 50, height: 50)
Circle()
.stroke(Color.accentColor, lineWidth: 2)
.frame(width: 78, height: 78)
}else{
Circle()
.fill(Color.accentColor)
.frame(width: 70, height: 70)
Circle()
.stroke(Color.accentColor, lineWidth: 2)
.frame(width: 78, height: 78)
}
}
.onLongPressGesture(minimumDuration: 0.1){
model.captureVideo()
}
.simultaneousGesture(
DragGesture(minimumDistance: 0)
.onEnded{ _ in
model.stopVideo()
}
)
.onTapGesture {
model.captureVideo()
}
}
The CameraModel
calls CameraService
which starts and stops recording:
func captureVideo() {
print("Capturing video...")
service.startRecording()
self.isRecording = true
}
func stopVideo() {
print("Stopping video...")
service.stopRecording()
self.isRecording = false
}
The startRecording()
function in CameraService
looks like this:
public func startRecording(){
//TODO: Eventually, we want to stich recordings, so if isRecording, add to the existing session.
if self.setupResult != .configurationFailed {
sessionQueue.async {
if let videoOutputConnection = self.videoOutput.connection(with: .video) {
self.isRecording = true
print("Is starting recording... \(self.isRecording)")
videoOutputConnection.videoOrientation = .portrait
self.videoOutput.setOutputSettings([AVVideoCodecKey: AVVideoCodecType.hevc], for: videoOutputConnection)
// Fetch the download URL
self.video?.fileStorage.downloadURL { url, error in
if let error = error {
print(error)
} else {
if(url != nil){
self.videoOutput.startRecording(to: url!, recordingDelegate: self.delegate!)
}else{
print("Video storage URL is nil.")
}
}
}
}else{
print("Could not get videoOutput connection.")
self.stopRecording()
self.isRecording = false
}
}
}
}
My AVCaptureFileOutputRecordingDelegate
is an extension of CameraService
like this:
extension CameraService: AVCaptureFileOutputRecordingDelegate {
/// - Tag: DidStartRecording
public func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {
print("Did Start Recording...")
}
/// - Tag: DidFinishRecording
public func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
print("Did finish recording...")
// Note: Because we use a unique file path for each recording, a new recording won't overwrite a recording mid-save.
func cleanup() {
let path = outputFileURL.path
if FileManager.default.fileExists(atPath: path) {
do {
try FileManager.default.removeItem(atPath: path)
} catch {
print("Could not remove file at url: \(outputFileURL)")
}
}
}
var success = true
if error != nil {
print("Movie file finishing error: \(String(describing: error))")
success = false
}
if success {
// Check the authorization status.
PHPhotoLibrary.requestAuthorization { status in
if status == .authorized {
// Save the movie file to the photo library and cleanup.
PHPhotoLibrary.shared().performChanges({
let options = PHAssetResourceCreationOptions()
options.shouldMoveFile = true
let creationRequest = PHAssetCreationRequest.forAsset()
creationRequest.addResource(with: .video, fileURL: outputFileURL, options: options)
}, completionHandler: { success, error in
if !success {
print("Couldn't save the movie to your photo library: \(String(describing: error))")
}
cleanup()
}
)
} else {
cleanup()
}
}
} else {
cleanup()
}
}
I would really appreciate any help! I've looked everywhere :(
I tried to follow this pattern:
- Start session
- Init input, output variables and settings
- Start recording and add delegate
- Stop recording and save to unique URL