4

I'm trying to create a custom camera in Swift. I've found many posts on this, and managed to open the camera, have a "Start recording" button. However, when clicking on the "Start recording" button, I have this error:

 Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] No active/enabled connections'

When looking up this error, it's supposed to be caused by not setting the quality of the camera which I did in the beginSession function:

func beginSession(captureDevice: AVCaptureDevice) {
        var err : NSError? = nil

        var input = AVCaptureDeviceInput?()
        do {
            input = try AVCaptureDeviceInput(device: captureDevice)
        } catch _ {
            //Error handling, if needed
        }
         captureSession.addInput(input)
         captureSession.sessionPreset = AVCaptureSessionPresetHigh;

        if err != nil {
            print("error: \(err?.localizedDescription)")
        }

        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        self.view.layer.addSublayer(previewLayer!)
        previewLayer?.frame = self.view.layer.frame
        captureSession.startRunning()
        let btn: UIButton = UIButton(frame: CGRectMake((self.view.frame.size.width-350)/2, (self.view.frame.size.height-80), 350, 70))
        btn.backgroundColor = hexStringToUIColor("#E53935")
        btn.setTitle("Start Recording", forState: UIControlState.Normal)
        btn.addTarget(self, action: "takeVideoAction", forControlEvents: UIControlEvents.TouchUpInside)
        btn.tag = 1               // change tag property
        self.view.addSubview(btn)

    }

Here is the full code:

   class ViewController: UIViewController, UIImagePickerControllerDelegate, UINavigationControllerDelegate, CLLocationManagerDelegate,AVCaptureFileOutputRecordingDelegate {


    let imagePicker: UIImagePickerController! = UIImagePickerController()
    let saveFileName = "/test.mp4"
    var locationManager : CLLocationManager! = CLLocationManager()
    var startLocation: CLLocation!
    //let captureSession = AVCaptureSession()
    var stillImageOutput: AVCaptureStillImageOutput?
    var previewLayer: AVCaptureVideoPreviewLayer?
    let captureSession = AVCaptureSession()

    // MARK: ViewController methods
    override func viewDidLoad() {
        super.viewDidLoad()
        //var timer = NSTimer.scheduledTimerWithTimeInterval(1, target: self, selector: #selector(ViewController.update), userInfo: nil, repeats: true)
        // Do any additional setup after loading the view, typically from a nib.
        let btn: UIButton = UIButton(frame: CGRectMake((self.view.frame.size.width-350)/2, (self.view.frame.size.height-80), 350, 70))
        btn.backgroundColor = hexStringToUIColor("#E53935")
        btn.setTitle("Go to recording", forState: UIControlState.Normal)
        btn.addTarget(self, action: "recordVideo:", forControlEvents: UIControlEvents.TouchUpInside)
        btn.tag = 1               // change tag property
        self.view.addSubview(btn) // add to view as subview
        if CLLocationManager.locationServicesEnabled() {
            locationManager.requestAlwaysAuthorization()
            locationManager.requestWhenInUseAuthorization()
            locationManager.delegate = self
            locationManager.desiredAccuracy = kCLLocationAccuracyBest
            locationManager.startUpdatingLocation()
            print(locationManager.location)
        }

    }


    func startCameraFromViewController(viewController: UIViewController, withDelegate delegate: protocol<UIImagePickerControllerDelegate, UINavigationControllerDelegate>) -> Bool {
        if UIImagePickerController.isSourceTypeAvailable(.Camera) == false {
            return false
        }

        var cameraController = UIImagePickerController()
        cameraController.sourceType = .Camera
        cameraController.mediaTypes = [kUTTypeMovie as NSString as String]
        cameraController.allowsEditing = false
        cameraController.delegate = delegate

        presentViewController(cameraController, animated: true, completion: nil)
        return true
    }

    func imagePickerController(picker: UIImagePickerController, didFinishPickingMediaWithInfo info: [String : AnyObject]) {
        let mediaType = info[UIImagePickerControllerMediaType] as! NSString
        dismissViewControllerAnimated(true, completion: nil)
        // Handle a movie capture
        if mediaType == kUTTypeMovie {
            guard let path = (info[UIImagePickerControllerMediaURL] as! NSURL).path else { return }
            if UIVideoAtPathIsCompatibleWithSavedPhotosAlbum(path) {
                UISaveVideoAtPathToSavedPhotosAlbum(path, self, #selector(ViewController.video(_:didFinishSavingWithError:contextInfo:)), nil)
            }
        }
    }

    func video(videoPath: NSString, didFinishSavingWithError error: NSError?, contextInfo info: AnyObject) {
        var title = "Success"
        var message = "Video was saved"
        if let _ = error {
            title = "Error"
            message = "Video failed to save"
        }
        let alert = UIAlertController(title: title, message: message, preferredStyle: .Alert)
        alert.addAction(UIAlertAction(title: "OK", style: UIAlertActionStyle.Cancel, handler: nil))
        presentViewController(alert, animated: true, completion: nil)
    }

    // MARK: Button handlers
    // Record a video
    @IBAction func recordVideo(sender: AnyObject) {
         let devices = AVCaptureDevice.devices()
        captureSession.sessionPreset = AVCaptureSessionPresetHigh
       // captureSession.sessionPreset = kCaptureSessionPresetVideo
        for device in devices {
            // Make sure this particular device supports video
            if (device.hasMediaType(AVMediaTypeVideo)) {
                // Finally check the position and confirm we've got the back camera
                if(device.position == AVCaptureDevicePosition.Back) {
                    var captureDevice = device as? AVCaptureDevice
                    if captureDevice != nil {
                        beginSession(captureDevice!)
                    }
                }
            }
        }
    }

    func beginSession(captureDevice: AVCaptureDevice) {
        var err : NSError? = nil

        var input = AVCaptureDeviceInput?()
        do {
            input = try AVCaptureDeviceInput(device: captureDevice)
        } catch _ {
            //Error handling, if needed
        }
         captureSession.addInput(input)
         captureSession.sessionPreset = AVCaptureSessionPresetHigh;

        if err != nil {
            print("error: \(err?.localizedDescription)")
        }

        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        self.view.layer.addSublayer(previewLayer!)
        previewLayer?.frame = self.view.layer.frame
        captureSession.startRunning()
        let btn: UIButton = UIButton(frame: CGRectMake((self.view.frame.size.width-350)/2, (self.view.frame.size.height-80), 350, 70))
        btn.backgroundColor = hexStringToUIColor("#E53935")
        btn.setTitle("Start Recording", forState: UIControlState.Normal)
        btn.addTarget(self, action: "takeVideoAction", forControlEvents: UIControlEvents.TouchUpInside)
        btn.tag = 1               // change tag property
        self.view.addSubview(btn)

    }

    func takeVideoAction() {
        var fileName = "mysavefile.mp4";
        let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
        var filePath = documentsURL.URLByAppendingPathComponent(fileName)

        let videoFileOutput = AVCaptureMovieFileOutput()

        let recordingDelegate:AVCaptureFileOutputRecordingDelegate? = self
        videoFileOutput.startRecordingToOutputFileURL(filePath, recordingDelegate: recordingDelegate)
        self.captureSession.addOutput(videoFileOutput)
        startCameraFromViewController(self, withDelegate: self)
    }

    func captureOutput(captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL!, fromConnections connections: [AnyObject]!, error: NSError!) {
        return
    }

    func captureOutput(captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAtURL fileURL: NSURL!, fromConnections connections: [AnyObject]!) {
        return
    }

}

Any input will be much appreciated

Graham Slick
  • 6,692
  • 9
  • 51
  • 87

1 Answers1

6

You need to add the video file output to the capture session before you call startRecordingToOutputFileURL:

self.captureSession.addOutput(videoFileOutput)
videoFileOutput.startRecordingToOutputFileURL(filePath, recordingDelegate: recordingDelegate)
Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
  • I have done exactly that, but I still get the error. Do you know a way to record video without setting a preset? Sometimes you need to directly set the activeFormat, as Apple suggests. – Kartick Vaddadi Sep 11 '17 at 04:18
  • Can you start a new question and show some code, or even better, link to some runnable code? – Rhythmic Fistman Sep 11 '17 at 05:48
  • https://stackoverflow.com/questions/46160407/how-do-i-record-a-video-on-ios-without-using-a-preset Here you go. – Kartick Vaddadi Sep 11 '17 at 16:34
  • Same goes for `AVCapturePhotoOutput`... Make sure to call `session.addOutput(capturePhotoOutput)` before calling `capturePhotoOutput.capturePhoto()` – spencer.sm Apr 18 '18 at 05:02