1

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:

  1. Start session
  2. Init input, output variables and settings
  3. Start recording and add delegate
  4. Stop recording and save to unique URL
Dhara
  • 31
  • 3

0 Answers0