37

I've been looking around on Stack and I have found similar questions to this, but none have worked for me. I am a complete novice to Swift 3.0. Essentially what I'm trying to do is record a video using AVFoundation. So far I have managed to capture a still image, and this is the code I have so far

func beginSession() {
    do {
        let deviceInput = try  AVCaptureDeviceInput(device: captureDevice) as AVCaptureDeviceInput
        if captureSession.inputs.isEmpty {
            self.captureSession.addInput(deviceInput)
        }
        stillImageOutput.outputSettings = [AVVideoCodecKey:AVVideoCodecJPEG]

        if captureSession.canAddOutput(stillImageOutput) {
            captureSession.addOutput(stillImageOutput)
        }

    }
    catch {
        print("error: \(error.localizedDescription)")
    }

    guard let previewLayer = AVCaptureVideoPreviewLayer(session: captureSession) else {
        print("no preview layer")
        return
    }

    self.view.layer.addSublayer(previewLayer)
    previewLayer.frame = self.view.layer.frame
    captureSession.startRunning()

    // Subviews
    self.view.addSubview(imgOverlay)
    self.view.addSubview(blur)
    self.view.addSubview(label)
    self.view.addSubview(Flip)
    self.view.addSubview(btnCapture)
}

and

 // SAVE PHOTO
func saveToCamera() {
    if let videoConnection = stillImageOutput.connection(withMediaType: AVMediaTypeVideo) {
        stillImageOutput.captureStillImageAsynchronously(from: videoConnection, completionHandler: { (CMSampleBuffer, Error) in
            if let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(CMSampleBuffer) {
                if let cameraImage = UIImage(data: imageData) {
                    self.flippedImage = UIImage(cgImage: cameraImage.cgImage!, scale: cameraImage.scale, orientation: UIImageOrientation.rightMirrored)
                    UIImageWriteToSavedPhotosAlbum(self.flippedImage, nil, nil, nil)

                }
            }
        })
    }

}
Adam Allard
  • 403
  • 1
  • 5
  • 11

3 Answers3

132

I am going to make it easy for you by posting the entire code you need to make a video recorder in AVFoundation. This code should work if you simply copy and paste it as is.

The only things you need to do are:

  1. In the storyboard, connect the camPreview outlet to a UIView in your view controller. This UIView should take up the entire contents of the screen.
  2. Attach the relevant privacy permissions to Info.plist (or else you will only see a black screen)
    • Privacy - Microphone Usage Description
    • Privacy - Camera Usage Description

NOTE: Right at the bottom, I've added how to play the recorded video under the title "Playing the Recorded Video".
EDIT: I forgot two things which made it crash during recording but I have added them now.

Swift 4

import UIKit

import AVFoundation

class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {

    @IBOutlet weak var camPreview: UIView!

    let cameraButton = UIView()

    let captureSession = AVCaptureSession()

    let movieOutput = AVCaptureMovieFileOutput()

    var previewLayer: AVCaptureVideoPreviewLayer!

    var activeInput: AVCaptureDeviceInput!

    var outputURL: URL!

    override func viewDidLoad() {
        super.viewDidLoad()
    
        if setupSession() {
            setupPreview()
            startSession()
        }
    
        cameraButton.isUserInteractionEnabled = true
    
        let cameraButtonRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.startCapture))
    
        cameraButton.addGestureRecognizer(cameraButtonRecognizer)
    
        cameraButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    
        cameraButton.backgroundColor = UIColor.red
    
        camPreview.addSubview(cameraButton)
    
    }

    func setupPreview() {
        // Configure previewLayer
        previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
        previewLayer.frame = camPreview.bounds
        previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        camPreview.layer.addSublayer(previewLayer)
    }

    //MARK:- Setup Camera

    func setupSession() -> Bool {
    
        captureSession.sessionPreset = AVCaptureSession.Preset.high
    
        // Setup Camera
        let camera = AVCaptureDevice.default(for: AVMediaType.video)!
    
        do {
        
            let input = try AVCaptureDeviceInput(device: camera)
        
            if captureSession.canAddInput(input) {
                captureSession.addInput(input)
                activeInput = input
            }
        } catch {
            print("Error setting device video input: \(error)")
            return false
        }
    
        // Setup Microphone
        let microphone = AVCaptureDevice.default(for: AVMediaType.audio)!
    
        do {
            let micInput = try AVCaptureDeviceInput(device: microphone)
            if captureSession.canAddInput(micInput) {
                captureSession.addInput(micInput)
            }
        } catch {
            print("Error setting device audio input: \(error)")
            return false
        }
    
    
        // Movie output
        if captureSession.canAddOutput(movieOutput) {
            captureSession.addOutput(movieOutput)
        }
    
        return true
    }

    func setupCaptureMode(_ mode: Int) {
        // Video Mode
    
    }

    //MARK:- Camera Session
    func startSession() {
    
        if !captureSession.isRunning {
            videoQueue().async {
                self.captureSession.startRunning()
            }
        }
    }

    func stopSession() {
        if captureSession.isRunning {
            videoQueue().async {
                self.captureSession.stopRunning()
            }
        }
    }

    func videoQueue() -> DispatchQueue {
        return DispatchQueue.main
    }

    func currentVideoOrientation() -> AVCaptureVideoOrientation {
        var orientation: AVCaptureVideoOrientation
    
        switch UIDevice.current.orientation {
            case .portrait:
                orientation = AVCaptureVideoOrientation.portrait
            case .landscapeRight:
                orientation = AVCaptureVideoOrientation.landscapeLeft
            case .portraitUpsideDown:
                orientation = AVCaptureVideoOrientation.portraitUpsideDown
            default:
                 orientation = AVCaptureVideoOrientation.landscapeRight
         }
    
         return orientation
     }

    @objc func startCapture() {
    
        startRecording()
    
    }

    //EDIT 1: I FORGOT THIS AT FIRST

    func tempURL() -> URL? {
        let directory = NSTemporaryDirectory() as NSString
    
        if directory != "" {
            let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
            return URL(fileURLWithPath: path)
        }
    
        return nil
    }

    override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    
        let vc = segue.destination as! VideoPlaybackViewController
    
        vc.videoURL = sender as? URL
    
    }

    func startRecording() {
    
        if movieOutput.isRecording == false {
        
            let connection = movieOutput.connection(with: AVMediaType.video)
        
            if (connection?.isVideoOrientationSupported)! {
                connection?.videoOrientation = currentVideoOrientation()
            }
        
            if (connection?.isVideoStabilizationSupported)! {
                connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
            }
        
            let device = activeInput.device
        
            if (device.isSmoothAutoFocusSupported) {
            
                do {
                    try device.lockForConfiguration()
                    device.isSmoothAutoFocusEnabled = false
                    device.unlockForConfiguration()
                } catch {
                   print("Error setting configuration: \(error)")
                }
            
            }
        
            //EDIT2: And I forgot this
            outputURL = tempURL()
            movieOutput.startRecording(to: outputURL, recordingDelegate: self)
        
            }
            else {
                stopRecording()
            }
    
       }    

   func stopRecording() {
    
       if movieOutput.isRecording == true {
           movieOutput.stopRecording()
        }
   }

    func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) {
    
    }

    func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
    
        if (error != nil) {
        
            print("Error recording movie: \(error!.localizedDescription)")
        
        } else {
        
            let videoRecorded = outputURL! as URL
        
            performSegue(withIdentifier: "showVideo", sender: videoRecorded)
        
        }
    
    }

}

Swift 3

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {

@IBOutlet weak var camPreview: UIView!

let cameraButton = UIView()

let captureSession = AVCaptureSession()

let movieOutput = AVCaptureMovieFileOutput()

var previewLayer: AVCaptureVideoPreviewLayer!

var activeInput: AVCaptureDeviceInput!

var outputURL: URL!

override func viewDidLoad() {
    super.viewDidLoad()
    
    if setupSession() {
        setupPreview()
        startSession()
    }
    
    cameraButton.isUserInteractionEnabled = true
    
    let cameraButtonRecognizer = UITapGestureRecognizer(target: self, action: #selector(ViewController.startCapture))
    
    cameraButton.addGestureRecognizer(cameraButtonRecognizer)
    
    cameraButton.frame = CGRect(x: 0, y: 0, width: 100, height: 100)
    
    cameraButton.backgroundColor = UIColor.red
    
    camPreview.addSubview(cameraButton)
    
}

func setupPreview() {
    // Configure previewLayer
    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.frame = camPreview.bounds
    previewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
    camPreview.layer.addSublayer(previewLayer)
}

//MARK:- Setup Camera

func setupSession() -> Bool {
    
    captureSession.sessionPreset = AVCaptureSessionPresetHigh
    
    // Setup Camera
    let camera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo)
    
    do {
        let input = try AVCaptureDeviceInput(device: camera)
        if captureSession.canAddInput(input) {
            captureSession.addInput(input)
            activeInput = input
        }
    } catch {
        print("Error setting device video input: \(error)")
        return false
    }
    
    // Setup Microphone
    let microphone = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeAudio)
    
    do {
        let micInput = try AVCaptureDeviceInput(device: microphone)
        if captureSession.canAddInput(micInput) {
            captureSession.addInput(micInput)
        }
    } catch {
        print("Error setting device audio input: \(error)")
        return false
    }
    
    
    // Movie output
    if captureSession.canAddOutput(movieOutput) {
        captureSession.addOutput(movieOutput)
    }
    
    return true
}

func setupCaptureMode(_ mode: Int) {
        // Video Mode
    
}

//MARK:- Camera Session
func startSession() {
    
    
    if !captureSession.isRunning {
        videoQueue().async {
            self.captureSession.startRunning()
        }
    }
}

func stopSession() {
    if captureSession.isRunning {
        videoQueue().async {
            self.captureSession.stopRunning()
        }
    }
}

func videoQueue() -> DispatchQueue {
    return DispatchQueue.main
}



func currentVideoOrientation() -> AVCaptureVideoOrientation {
    var orientation: AVCaptureVideoOrientation
    
    switch UIDevice.current.orientation {
    case .portrait:
        orientation = AVCaptureVideoOrientation.portrait
    case .landscapeRight:
        orientation = AVCaptureVideoOrientation.landscapeLeft
    case .portraitUpsideDown:
        orientation = AVCaptureVideoOrientation.portraitUpsideDown
    default:
        orientation = AVCaptureVideoOrientation.landscapeRight
    }
    
    return orientation
}

func startCapture() {
    
    startRecording()
    
}

//EDIT 1: I FORGOT THIS AT FIRST

func tempURL() -> URL? {
    let directory = NSTemporaryDirectory() as NSString
    
    if directory != "" {
        let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
        return URL(fileURLWithPath: path)
    }
    
    return nil
}


func startRecording() {
    
    if movieOutput.isRecording == false {
        
        let connection = movieOutput.connection(withMediaType: AVMediaTypeVideo)
        if (connection?.isVideoOrientationSupported)! {
            connection?.videoOrientation = currentVideoOrientation()
        }
        
        if (connection?.isVideoStabilizationSupported)! {
            connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
        }
        
        let device = activeInput.device
        if (device?.isSmoothAutoFocusSupported)! {
            do {
                try device?.lockForConfiguration()
                device?.isSmoothAutoFocusEnabled = false
                device?.unlockForConfiguration()
            } catch {
                print("Error setting configuration: \(error)")
            }
            
        }
        
        //EDIT2: And I forgot this
        outputURL = tempURL()
        movieOutput.startRecording(toOutputFileURL: outputURL, recordingDelegate: self)
        
    }
    else {
        stopRecording()
    }
    
}

func stopRecording() {
    
    if movieOutput.isRecording == true {
        movieOutput.stopRecording()
    }
}

func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) {
    
}

func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
    if (error != nil) {
        print("Error recording movie: \(error!.localizedDescription)")
    } else {
        
        _ = outputURL as URL
        
    }
    outputURL = nil
}



}

This is How You Should Have Setup Your View Controller

Setup your View Controller with campPreview

Permissions for Your Info.plist

plist permissions

Setting Up the Recording Delegates

You need to conform to AVCaptureFileOutputRecordingDelegate. According to Apple docs, it defines an interface for delegates of AVCaptureFileOutput to respond to events that occur in the process of recording a single file. It comes with two methods you need to implement and these are the last two methods at the bottom of the code. The first is,

func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) {
}

You can add any logic to this when the video starts recording. In the code example I have given, the video starts recording when you tap the red square button in the left hand corner. The second is,

func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
    if (error != nil) {
        print("Error recording movie: \(error!.localizedDescription)")
    } else {
        
        _ = outputURL as URL
        
    }
    outputURL = nil
}

This is called when the video has finished recording. In the code example I have given the video stops recording after you tap the red square button a second time. When the video has stopped recording, you get an output file URL. This represents your video. You can use this to perhaps segue to another View Controller to play the video in a AVPlayer. Or you can save it. In this example you will notice I have not done much with the output URL though.

To start recording a video I have used a programmatically created button which appears as a red square in the left hand corner and responds to a UITapGesture. You can make a better button in your app.

Setting Up The Session

The video recorder needs a capture session which I have setup in setupSession(). Here you add the AVCapture input devices which include the camera and the microphone. According to Apple, AVCaptureDeviceInput is a concrete sub-class of AVCaptureInput you use to capture data from an AVCaptureDevice object. However, the user needs to grant you access to use these so in you info.plist you should add Privacy - Microphone Usage Description and Privacy - Camera Usage Description and give a reason why you want to use the the video recorder and microphone. If you do not do this, you will only get a black screen. The session preset is a constant value indicating the quality level or bitrate of the output. I have set this to high but there are other options you can explore. The movieOutput is of type AVCaptureMovieFileOutput which according to Apple, is a concrete sub-class of AVCaptureFileOutput you use to capture data to a QuickTime movie. This is what actually allows you to record and save the video.

Setting Up The Preview

This is where you setup the camera preview layer which is done in setupPreview(). You setup the preview layer with the capture session you have created with the following AVCaptureVideoPreviewLayer(session: captureSession).

Starting the Session

The final step is to start the session which is done in startSession(). You check if a session is already running and if it is not then you start one.

if !captureSession.isRunning {
    videoQueue().async {
        self.captureSession.startRunning()
    }
}

Starting the Recording

When you tap the red button, the startRecording() method is called. Here I have added methods to handle video orientation and video stabilization. Finally, we see the movieOutput variable again which we setup earlier with our session. We call it to record our movie to outputURL and tell it our delegate methods to handle the start and end of recording are in the same view controller (those last two methods).

Stop Recording

It just so happens that when you tap the red button again, startRecoding is called again but it will notice that some thing is being recorded and call stopRecording.

Playing the Recorded Video

I'm being generous today so I'll throw this in too.

Create a new view controller and call it VideoPlayback. Connect it with your first ViewController using a segue in Storyboard. Give the segue an identifier of "showVideo". Create a UIView and fills up the VideoPlayback's screen and create an outlet to its view controller called videoView. Add the following code to your new VideoPlayback view controller:

Swift 4

import UIKit
import AVFoundation

class VideoPlayback: UIViewController {

    let avPlayer = AVPlayer()
    var avPlayerLayer: AVPlayerLayer!

    var videoURL: URL!
    //connect this to your uiview in storyboard
    @IBOutlet weak var videoView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        avPlayerLayer = AVPlayerLayer(player: avPlayer)
        avPlayerLayer.frame = view.bounds
        avPlayerLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        videoView.layer.insertSublayer(avPlayerLayer, at: 0)
    
        view.layoutIfNeeded()
    
        let playerItem = AVPlayerItem(url: videoURL as URL)
        avPlayer.replaceCurrentItem(with: playerItem)
    
        avPlayer.play()
    }
}

Swift 3

import UIKit
import AVFoundation

class VideoPlayback: UIViewController {

    let avPlayer = AVPlayer()
    var avPlayerLayer: AVPlayerLayer!

    var videoURL: URL!
    //connect this to your uiview in storyboard
    @IBOutlet weak var videoView: UIView!

    override func viewDidLoad() {
        super.viewDidLoad()

        avPlayerLayer = AVPlayerLayer(player: avPlayer)
        avPlayerLayer.frame = view.bounds
        avPlayerLayer.videoGravity = AVLayerVideoGravityResizeAspectFill
        videoView.layer.insertSublayer(avPlayerLayer, at: 0)
    
        view.layoutIfNeeded()
    
        let playerItem = AVPlayerItem(url: videoURL as URL)
        avPlayer.replaceCurrentItem(with: playerItem)
    
        avPlayer.play()
    }
}

Now go back to your last delegate method and modify it as follows:

func capture(_ captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAt outputFileURL: URL!, fromConnections connections: [Any]!, error: Error!) {
    
    if (error != nil) {
        print("Error recording movie: \(error!.localizedDescription)")
    } else {
        
        let videoRecorded = outputURL! as URL
        
        performSegue(withIdentifier: "showVideo", sender: videoRecorded)
    }
}

Finally, create a prepare for segue method that will initialize the videoURL that will play with the AVPlayer.

override func prepare(for segue: UIStoryboardSegue, sender: Any?) {
    
    let vc = segue.destination as! VideoPlayback
    vc.videoURL = sender as! URL
}

Now to test, go back and start recording a video. On the second tap of the red square, the segue will be performed and you will see the recorded video being played back automatically.

aheze
  • 24,434
  • 8
  • 68
  • 125
gwinyai
  • 2,560
  • 2
  • 15
  • 17
  • 2
    This is an insane answer! Thank you ever so much! I definitely understand the subject more now. I have done what you said, however on run, when I tap the red square I get this error `Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '*** -[AVCaptureMovieFileOutput startRecordingToOutputFileURL:recordingDelegate:] Nil URL passed'` – Adam Allard Jan 17 '17 at 14:22
  • Let us see if we can diagnose the problem, because I have just run the code and it is working from my end. Can you also check you implemented the correct delegate method. I know some of their method signatures can look slightly similar. You should have one that says didStartRecordingToOutputFileAt and not startRecordingToOutputFileURL. – gwinyai Jan 17 '17 at 14:32
  • In other words, I think you let the autocomplete give you the wrong delegate method when it should be func capture(_ captureOutput: AVCaptureFileOutput!, didStartRecordingToOutputFileAt fileURL: URL!, fromConnections connections: [Any]!) { } – gwinyai Jan 17 '17 at 14:40
  • Sorry. It is my fault. I forgot to add two things in the code I wrote and the code I was using to test. Please see my amended answer. – gwinyai Jan 17 '17 at 15:03
  • This is all brilliant, and I really appreciate your efforts. Your edit did fix my problem, however now I'm having a slightly different issue, when tap the red square, the app isnt recording the video. Any idea? I get this message in the debugger `2017-01-17 17:06:10.337923 CustomCamera[1364:351538] [MC] System group container for systemgroup.com.apple.configurationprofiles path is /private/var/containers/Shared/SystemGroup/systemgroup.com.apple.configurationprofiles 2017-01-17 17:06:10.338804 CustomCamera[1364:351538] [MC] Reading from public effective user settings.` – Adam Allard Jan 17 '17 at 17:08
  • That message is normal, it is not a warning or error. The video is being recorded. Like I said, it is up to you do something with the recorded video which comes out as a URL. You could play it back immediately, you could save it and display a thumbnail etc. I'll be generous with you today. I have added an explanation right at the bottom which covers how to play the video you record in an AVPlayer. It's not pretty but it works. Now it is up to you to figure out how to make this whole thing work according to your app's needs. That is all part of learning :) – gwinyai Jan 17 '17 at 18:12
  • Thank you ever so much for this! Really is helpful! Best response I've ever come across on stack! – Adam Allard Jan 17 '17 at 18:23
  • I just wanted to chime in and let you know you've helped a lot of people including myself better understand AV Foundation and the world will be a better place when we produce cool apps using your knowledge! – Peter Li Dec 14 '17 at 16:45
  • Thanks for amazing answer but i have a question. Are you sure this code creates an MP4 encoded file? Because i seem to get a video with "Format profile : QuickTime, Codec ID : qt 0000.00 (qt )" encoding information. It feels like all we are doing is renaming the fiile with MP4 extension, not actually transcoding it. – Numan Karaaslan Dec 24 '17 at 14:54
  • @NumanKaraaslan you are correct, I attempted no transcoding. QuickTime mov. containers and .MP4 are pretty much identical but the extension matters. Simply renaming a QuickTime video from .mov to .mp4 should make it work like a .mp4 but if you would like to be safe depending on your use case then feel free to use AV Foundation media transcoding. – gwinyai Dec 24 '17 at 16:52
  • thanks for clearifying ;) I am streaming the mp4 file and android 6.0 can play it but 4.4.2 can not. That is why i asked about encoding. Looks like it is a problem with old android versions. – Numan Karaaslan Dec 24 '17 at 17:14
  • When pressing a button, camera jerks, anyone knows how to get rid of that? – Yaroslav Dukal May 30 '18 at 02:17
  • @gwinyai can we record the video in SUPER SLOW MOTION ALSO? – user1629977 Nov 13 '18 at 17:19
  • Thank you this answer is more helpful for a beginner like me than AVCam tutorial of Apple. https://developer.apple.com/documentation/avfoundation/cameras_and_media_capture/avcam_building_a_camera_app – MJ Studio Dec 20 '19 at 08:45
  • is there something similar for SwiftUI . I am struggling a bit to replace the storyboard elements. – thenakulchawla Feb 01 '20 at 18:28
  • The line " let videoRecorded = outputURL! as URL " should be " let videoRecordedURL = outputFileURL as URL " – user3528623 Jul 29 '20 at 18:18
  • is there a way to be able to switch it to the front camera? – Noah Iarrobino Sep 01 '20 at 19:07
5

Based of @gwinyai amazing answer I made a similar Camera framework. https://github.com/eonist/HybridCamera This also supports taking photos, and doesn't have the glitch problem described by @Maksim Kniazev which was caused by this line:

if (connection?.isVideoStabilizationSupported)! {
    connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
}
Sentry.co
  • 5,355
  • 43
  • 38
  • 1
    Amazing work @eonist is it possible to adapt it to a UI story board because some perefer storyboards to pure programmatic codes. Cheers – King Sep 27 '18 at 00:01
4

From @gwinyai 's answer updated to latest version of (swift 4)

import UIKit
import AVFoundation


class ViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {

@IBOutlet weak var camPreview: UIView!
let captureSession = AVCaptureSession()
let movieOutput = AVCaptureMovieFileOutput()

var previewLayer: AVCaptureVideoPreviewLayer!
var activeInput: AVCaptureDeviceInput!
var outputURL: URL!

override func viewDidLoad() {
    super.viewDidLoad()
    // Do any additional setup after loading the view, typically from a nib.
    if setupSession() {
        setupPreview()
        startSession()
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}
func setupPreview() {
    // Configure previewLayer
    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession)
    previewLayer.frame = camPreview.bounds
    previewLayer.videoGravity = .resizeAspectFill
    camPreview.layer.addSublayer(previewLayer)
}

//MARK:- Setup Camera

func setupSession() -> Bool {
    captureSession.sessionPreset = AVCaptureSession.Preset.high

    // Setup Camera
    let camera = AVCaptureDevice.default(for: .video)

    do {
        let input = try AVCaptureDeviceInput(device: camera!)
        if captureSession.canAddInput(input) {
            captureSession.addInput(input)
            activeInput = input
        }
    } catch {
        print("Error setting device video input: \(error)")
        return false
    }

    // Setup Microphone
    let microphone = AVCaptureDevice.default(for: .audio)

    do {
        let micInput = try AVCaptureDeviceInput(device: microphone!)
        if captureSession.canAddInput(micInput) {
            captureSession.addInput(micInput)
        }
    } catch {
        print("Error setting device audio input: \(error)")
        return false
    }


    // Movie output
    if captureSession.canAddOutput(movieOutput) {
        captureSession.addOutput(movieOutput)
    }

    return true
}

func setupCaptureMode(_ mode: Int) {
    // Video Mode

}

//MARK:- Camera Session
func startSession() {


    if !captureSession.isRunning {
        videoQueue().async {
            self.captureSession.startRunning()
        }
    }
}

func stopSession() {
    if captureSession.isRunning {
        videoQueue().async {
            self.captureSession.stopRunning()
        }
    }
}

func videoQueue() -> DispatchQueue {
    return DispatchQueue.main
}



func currentVideoOrientation() -> AVCaptureVideoOrientation {
    var orientation: AVCaptureVideoOrientation

    switch UIDevice.current.orientation {
    case .portrait:
        orientation = AVCaptureVideoOrientation.portrait
    case .landscapeRight:
        orientation = AVCaptureVideoOrientation.landscapeLeft
    case .portraitUpsideDown:
        orientation = AVCaptureVideoOrientation.portraitUpsideDown
    default:
        orientation = AVCaptureVideoOrientation.landscapeRight
    }

    return orientation
}

func startCapture() {

    startRecording()

}

//EDIT 1: I FORGOT THIS AT FIRST

func tempURL() -> URL? {
    let directory = NSTemporaryDirectory() as NSString

    if directory != "" {
        let path = directory.appendingPathComponent(NSUUID().uuidString + ".mp4")
        return URL(fileURLWithPath: path)
    }

    return nil
}


func startRecording() {

    if movieOutput.isRecording == false {

        let connection = movieOutput.connection(with: .video)
        if (connection?.isVideoOrientationSupported)! {
            connection?.videoOrientation = currentVideoOrientation()
        }

        if (connection?.isVideoStabilizationSupported)! {
            connection?.preferredVideoStabilizationMode = AVCaptureVideoStabilizationMode.auto
        }

        let device = activeInput.device
        if (device.isSmoothAutoFocusSupported) {
            do {
                try device.lockForConfiguration()
                device.isSmoothAutoFocusEnabled = false
                device.unlockForConfiguration()
            } catch {
                print("Error setting configuration: \(error)")
            }

        }

        //EDIT2: And I forgot this
        outputURL = tempURL()
        movieOutput.startRecording(to: outputURL, recordingDelegate: self)

    }
    else {
        stopRecording()
    }

}

func stopRecording() {

    if movieOutput.isRecording == true {
        movieOutput.stopRecording()
    }
}

func fileOutput(_ output: AVCaptureFileOutput, didStartRecordingTo fileURL: URL, from connections: [AVCaptureConnection]) {

}

func fileOutput(_ output: AVCaptureFileOutput, didFinishRecordingTo outputFileURL: URL, from connections: [AVCaptureConnection], error: Error?) {
    if (error != nil) {
        print("Error recording movie: \(error!.localizedDescription)")
    } else {
        _ = outputURL as URL
    }
    outputURL = nil
}

}
mert
  • 1,090
  • 13
  • 26