Categories: iOSSwiftTutorials

Implementing Camera with AVFoundation

Howdy!

Today I am going to write about how to implement custom Camera using AVFoundation.

Here I have created this storyboard consisting of two View Controllers. One is Main View Controller and attached to Navigation Controller and the other one is Camera View Controller in which I have a UIView (in gray color), a Button which will help us capture the image/video, an Activity Indicator which will show processing, and a Bar Button for switching camera.

In my assets I have.

Now lets open your Main View Controller and create IBActions for both of your buttons and set the identifier of its segue to Camera View Controller as ‘capture’ (you can give it any identifier  you like). Also create a variable of type String called keyForCamera.

Open your Camera View Controller now and create IBActions and IBOutlets, two functions for initiating Camera for Image and Video respectively and a variable named keyFromMenu of type String.

Create @IBOutlet for the UIView , Activity Indicator and @IBAction for the Capture Button and Switch Camera Bar Button.

Now come back to your Main View Controller and in your @IBActions paste the following code and override prepareForSegue.

   @IBAction func capturePicture(sender: UIButton) {
    self.keyForCamera = "Capture Picture"
        self.performSegueWithIdentifier("capture", sender: nil)
        
    }

    @IBAction func captureVideo(sender: UIButton) {
        self.keyForCamera = "Capture Video"
        self.performSegueWithIdentifier("capture", sender: nil)
        
    }
    
    override func prepareForSegue(segue: UIStoryboardSegue, sender: AnyObject?) {
        if segue.identifier == "capture"{
        let destination = segue.destinationViewController as! CameraViewController
            destination.keyFromMenu = self.keyForCamera
        
        
        }
    }

Just to check if our logic works build and run it, it should show you different titles on both of the buttons you created.

Now come back to your Camera View Controller class and declare the following variables in it.

    //Camera Session
    var session: AVCaptureSession?
    //Capturing Image
    var stillImageOutput: AVCaptureStillImageOutput?
    //Capturing Video
    var videoOutput :  AVCaptureMovieFileOutput?
    //Shows preview
    var videoPreviewLayer: AVCaptureVideoPreviewLayer?
    //Capturing Camera hardware
    var captureDevice:AVCaptureDevice! = nil
    //Switching to front/back camera
    var camera : Bool = true

Your initiatePictureCamera function will now look like this.

func initiatePictureCamera(){
        print("Picture Camera is Running")
        session = AVCaptureSession()
        session!.sessionPreset = AVCaptureSessionPresetPhoto
        var input : AVCaptureDeviceInput?
        var error: NSError?
        
        if (camera == false) {
            let videoDevices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
            
            
            for device in videoDevices{
                let device = device as! AVCaptureDevice
                if device.position == AVCaptureDevicePosition.Front {
                    captureDevice = device
                    
                    break
                }
            }
        } else {
            captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
        }
        
        
        do {
            input = try AVCaptureDeviceInput(device: captureDevice)
            
            if error == nil && session!.canAddInput(input) {
                session!.addInput(input)
                
                stillImageOutput = AVCaptureStillImageOutput()
                stillImageOutput!.outputSettings = [AVVideoCodecKey: AVVideoCodecJPEG]
                
                if session!.canAddOutput(stillImageOutput) {
                    session!.addOutput(stillImageOutput)
                    
                    videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
                    videoPreviewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
                    videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait
                    videoPreviewLayer!.frame = cameraOverlayView.bounds
                    
                    cameraOverlayView.layer.addSublayer(videoPreviewLayer!)
                    
                    session!.startRunning()
                }
            }
            
        }
        catch let err as NSError {
            error = err
            input = nil
            print(error!.localizedDescription)
            
        }
        
    }

And your initiateVideoCamera will be something like

 func initiateVideoCamera(){
        print("Video Camera is Running")
       
        session = AVCaptureSession()
        session!.sessionPreset = AVCaptureSessionPresetHigh
        //var captureDevice:AVCaptureDevice! = nil
        var input : AVCaptureDeviceInput?
        var error: NSError?

        if (camera == false) {
            let videoDevices = AVCaptureDevice.devicesWithMediaType(AVMediaTypeVideo)
            
            
            for device in videoDevices{
                let device = device as! AVCaptureDevice
                if device.position == AVCaptureDevicePosition.Front {
                    captureDevice = device
                    break
                }
                
            }
        } else {
            captureDevice = AVCaptureDevice.defaultDeviceWithMediaType(AVMediaTypeVideo)
        }
        
        
        do {
            
            input = try AVCaptureDeviceInput(device: captureDevice)
            
            if error == nil && session!.canAddInput(input) {
                session!.addInput(input)
                
                self.videoOutput = AVCaptureMovieFileOutput()
                
                if session!.canAddOutput(videoOutput) {
                    session!.addOutput(videoOutput)
                    
                    videoPreviewLayer = AVCaptureVideoPreviewLayer(session: session)
                    videoPreviewLayer!.videoGravity = AVLayerVideoGravityResizeAspectFill
                    videoPreviewLayer!.connection?.videoOrientation = AVCaptureVideoOrientation.Portrait
                    videoPreviewLayer!.frame = cameraOverlayView.bounds
                    cameraOverlayView.layer.addSublayer(videoPreviewLayer!)
                    
                    session!.startRunning()
                }
            }
            
        }
        catch let err as NSError {
            error = err
            input = nil
            print(error!.localizedDescription)
            
        }
    }
    //MARK: - Change Camera to Front or Back
    @IBAction func switchCamera(sender: UIBarButtonItem) {
  
        self.camera = !camera
        session!.stopRunning()
        videoPreviewLayer!.removeFromSuperlayer()
        initiatePictureCamera()
    
    }
    //MARK: - Capture Video or Picture
    @IBAction func capture(sender: AnyObject) {
        
        if let title = self.keyFromMenu{
        
        if title == "Capture Picture"{
            
        if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
            
            stillImageOutput!.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: { (sampleBuffer, error) -> Void in
                
                if sampleBuffer != nil {
                    
                    let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
                    let dataProvider = CGDataProviderCreateWithCFData(imageData)
                    let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
                    let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
                    
                    UIImageWriteToSavedPhotosAlbum(image, self,#selector(CameraViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
                }
                
                
            })
            
            
            
            
        }
        }
            //If not title is Capture Video
        else{
            
            let fileName = "video.mp4";
            let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
            let filePath = documentsURL.URLByAppendingPathComponent(fileName)
            
            if self.videoOutput!.recording{
            //Change camera button for video recording
            self.videoOutput!.stopRecording()
                self.captureOutlet.setImage(UIImage(named: "capture"), forState: .Normal)
            self.activityIndicator.hidden = false
            self.activityIndicator.startAnimating()
                
            return
            
            }
            else{
                
            //Change camera button for video recording
            self.captureOutlet.setImage(UIImage(named: "video_record"), forState: .Normal)
            //Start recording
            self.videoOutput!.startRecordingToOutputFileURL(filePath, recordingDelegate: self)
  

            }
            
            
            
            
            
            }
        }
        
    }

In your Capture button action

    if let title = self.keyFromMenu{
        
        if title == "Capture Picture"{
            
        if let videoConnection = stillImageOutput!.connectionWithMediaType(AVMediaTypeVideo) {
            
            stillImageOutput!.captureStillImageAsynchronouslyFromConnection(videoConnection, completionHandler: { (sampleBuffer, error) -> Void in
                
                if sampleBuffer != nil {
                    
                    let imageData = AVCaptureStillImageOutput.jpegStillImageNSDataRepresentation(sampleBuffer)
                    let dataProvider = CGDataProviderCreateWithCFData(imageData)
                    let cgImageRef = CGImageCreateWithJPEGDataProvider(dataProvider, nil, true, CGColorRenderingIntent.RenderingIntentDefault)
                    let image = UIImage(CGImage: cgImageRef!, scale: 1.0, orientation: UIImageOrientation.Right)
                    
                    UIImageWriteToSavedPhotosAlbum(image, self,#selector(CameraViewController.image(_:didFinishSavingWithError:contextInfo:)), nil)
                }
                
                
            })
            
            
            
            
        }
        }
            //If not title is Capture Video
        else{
            
            let fileName = "video.mp4";
            let documentsURL = NSFileManager.defaultManager().URLsForDirectory(.DocumentDirectory, inDomains: .UserDomainMask)[0]
            let filePath = documentsURL.URLByAppendingPathComponent(fileName)
            
            if self.videoOutput!.recording{
            //Change camera button for video recording
            self.videoOutput!.stopRecording()
                self.captureOutlet.setImage(UIImage(named: "capture"), forState: .Normal)
            self.activityIndicator.hidden = false
            self.activityIndicator.startAnimating()
                
            return
            
            }
            else{
                
            //Change camera button for video recording
            self.captureOutlet.setImage(UIImage(named: "video_record"), forState: .Normal)
            //Start recording
            self.videoOutput!.startRecordingToOutputFileURL(filePath, recordingDelegate: self)
  

            }
            
            
            
            
            
            }
        }

This will show you an alert when image is saved in the Photos.

 //MARK: - Shows alert when image is saved
    func image(image: UIImage, didFinishSavingWithError error: NSError?, contextInfo:UnsafePointer<Void>) {
        guard error == nil else {
            //Error saving image
            print(error)
            return
        }
        
        //Image saved successfully
        showAlert("Saved", message: "Image Saved to Photos")
        

    }

Conform your class  Camera View Controller to protocol AVCaptureFileOutputRecordingDelegate. It will provide the recorded video url which can be later saved into Photos, and paste the following code in your class.

 //MARK: - Get completed video path
    func captureOutput(captureOutput: AVCaptureFileOutput!, didFinishRecordingToOutputFileAtURL outputFileURL: NSURL!, fromConnections connections: [AnyObject]!, error: NSError!){
        
      //Saves the video in Photos
        doVideoProcessing(outputFileURL)

        
    }

Finally for saving Video in Photos.

 func doVideoProcessing(outputPath:NSURL){
        
        PHPhotoLibrary.sharedPhotoLibrary().performChanges({
            PHAssetChangeRequest.creationRequestForAssetFromVideoAtFileURL(outputPath)
            
            
            
        }) { (success, error) in
            
            
            
            if error == nil{
                
                print("Success:\(success)")
                dispatch_async(dispatch_get_main_queue(),{
                    self.activityIndicator!.stopAnimating()
                    self.showAlert("Saved", message: "Video saved to Photos")
                })
                
                
            }
            else{
                
                dispatch_async(dispatch_get_main_queue(),{
                    self.activityIndicator!.stopAnimating()

                    self.showAlert("Error", message: error!.localizedDescription)
                    
                })
                

                
            }
            
            
        }

Don’t forget to add the these frameworks in your Camera View Controller.

import AVFoundation
import Photos

At last, run at test it.
Check out this demo.


Here is the full Source Code to this project.

Good day!

Aaqib Hussain

Aaqib is an enthusiastic programmer with the love of Swift and anything that looks like Swift i.e Kotlin. He loves writing code in Swift, and exploring new technology and platforms. He likes to listen to old music. When he is not writing code, he's probably spend his time watching movies, tv-shows or anime, or either doing some research for writing the next article. He started Kode Snippets in 2015.

View Comments

  • Good evening, Aaqib Hussain!
    I'm looking for documentation that helps me create an app that allows me to record videos and save them in the gallery (like Snapchat or Instagram Histories).
    Same as the one you performed.
    I've reviewed the documentation available on the development apple, but it got a bit confusing for me.
    Could you help, give a look at this poor "lammer" who speaks to you?

    The "Swift Language Version" (SWIFT_VERSION) build setting must be set to a supported value for targets which uses Swift. This setting can be set in the build settings editor.

    • Hi, I think you're using two different versions of Swift in your project. By any case one of your installed library uses a different version of Swift than your current project. Make sure they both are the same.
      Hope this helps.
      Cheers.

      • Aaqib Hussain, thank you very much for the answer, it helped a bit, but as a young enthusiast I do not understand much of the language and I am still learning from what I find on the internet.
        I would like to create something like the Histories of Instagram or SnapChat.
        I could not replicate this code, I believe, for lack of knowledge and practice with language.
        Is there somewhere I can learn more about the audio and video capturing part?

        • Hi Wagenr, Can you explain more about what do you mean by Histories? and I couldn't really find a tutorial that shows you how to use video and audio together. I'm quite busy now adays, if I got some time I will try to code a sample project.

Recent Posts

Things to know when moving to Germany

This article covers some important things you must know when you are considering a move…

3 years ago

Unit Testing in Android for Dummies

What is Unit Testing? In its simplest term, unit testing is testing a small piece…

4 years ago

Factory Design Pattern

In this article, you will learn about a type of Creational Design Pattern which is…

5 years ago

Creating Target specific Theme in iOS

In this tutorial, you will go through the use of targets to achieve two separate…

5 years ago

Facade Design Pattern

In this article, you will learn about a type of Structural Design Pattern which is…

5 years ago

Singleton Design Pattern

In this article you will learn about a type of Creational Design Pattern which is…

5 years ago