Use Front Camera for AVCaptureDevice Preview Layer Automatically Like Snapchat or Houseparty (Swift 3)

9k Views Asked by At

Essentially what I'm trying to accomplish is having the front camera of the AVCaptureDevice be the first and only option on a application during an AVCaptureSession.

I've looked around StackOverflow and all the methods and answers provided are deprecated as of iOS 10, Swift 3 and Xcode 8.

I know you're supposed to enumerate the devices with AVCaptureDeviceDiscoverySession and look at them to distinguish front from back, but I'm unsure of how to do so.

Could anyone help? It would amazing if so!

Here's my code:

    override func viewDidAppear(_ animated: Bool) {
    super.viewDidAppear(animated)

    previewLayer.frame = singleViewCameraSlot.bounds
    self.singleViewCameraSlot.layer.addSublayer(previewLayer)
    captureSession.startRunning()

}



lazy var captureSession: AVCaptureSession = {
    let capture = AVCaptureSession()
    capture.sessionPreset = AVCaptureSessionPreset1920x1080
    return capture
}()

lazy var previewLayer: AVCaptureVideoPreviewLayer = {
    let preview =  AVCaptureVideoPreviewLayer(session: self.captureSession)

    preview?.videoGravity = AVLayerVideoGravityResizeAspect
    preview?.connection.videoOrientation = AVCaptureVideoOrientation.portrait
    preview?.bounds = CGRect(x: 0, y: 0, width: self.view.bounds.width, height: self.view.bounds.height)
    preview?.position = CGPoint(x: self.view.bounds.midX, y: self.view.bounds.midY)

    return preview!
}()


func setupCameraSession() {


    let frontCamera = AVCaptureDevice.defaultDevice(withMediaType: AVMediaTypeVideo) as AVCaptureDevice

    do {
        let deviceInput = try AVCaptureDeviceInput(device: frontCamera)

        captureSession.beginConfiguration()

        if (captureSession.canAddInput(deviceInput) == true) {
            captureSession.addInput(deviceInput)
        }

        let dataOutput = AVCaptureVideoDataOutput()

        dataOutput.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) : NSNumber(value: kCVPixelFormatType_420YpCbCr8BiPlanarFullRange as UInt32)]
        dataOutput.alwaysDiscardsLateVideoFrames = true

        if (captureSession.canAddOutput(dataOutput) == true) {
            captureSession.addOutput(dataOutput)
        }

        captureSession.commitConfiguration()

        let queue = DispatchQueue(label: "io.goodnight.videoQueue")
        dataOutput.setSampleBufferDelegate(self, queue: queue)

    }
    catch let error as NSError {
        NSLog("\(error), \(error.localizedDescription)")
    }

}
2

There are 2 best solutions below

0
On

If you explicitly need the front camera, you can use AVCaptureDeviceDiscoverySession as specified here.

https://developer.apple.com/reference/avfoundation/avcapturedevicediscoverysession/2361539-init

This allows you to specify the types of devices you want to search for. The following (untested) should give you the front facing camera.

let deviceSessions = AVCaptureDeviceDiscoverySession(deviceTypes: [AVCaptureDeviceType.builtInWideAngleCamera], mediaType: AVMediaTypeVideo, position: AVCaptureDevicePosition.front)

This deviceSessions has a devices property which is an array of AVCaptureDevice types containing only the devices matching that search criteria.

deviceSessions?.devices

That should be either 0 or 1 depending on if the device has a front facing camera or not (some iPods won't for example).

0
On

If you just need to find a single device based on simple characteristics (like a front-facing camera that can shoot video), just use AVCaptureDevice.default(_:for:position:). For example:

guard let device = AVCaptureDevice.default(.builtInWideAngleCamera,
    for: .video,
    position: .front)
    else { fatalError("no front camera. but don't all iOS 10 devices have them?")

// then use the device: captureSession.addInput(device) or whatever

Really that's all there is to it for most use cases.


There's also AVCaptureDeviceDiscoverySession as a replacement for the old method of iterating through the devices array. However, most of the things you'd usually iterate through the devices array for can be found using the new default(_:for:position:) method, so you might as well use that and write less code.

The cases where AVCaptureDeviceDiscoverySession is worth using are the less common, more complicated cases: say you want to find all the devices that support a certain frame rate, or use key-value observing to see when the set of available devices changes.


By the way...

I've looked around StackOverflow and all the methods and answers provided are deprecated as of iOS 10, Swift 3 and Xcode 8.

If you read Apple's docs for those methods (at least this one, this one, and this one), you'll see along with those deprecation warnings some recommendations for what to use instead. There's also a guide to the iOS 10 / Swift 3 photo capture system and some sample code that both show current best practices for these APIs.