简体   繁体   中英

App crashes on switching to front camera

I am creating a snapchat like camera application to swap cameras on double tap in screen. Code compiles with no error but I am getting an error when I double tap on screen to get to front camera. Here is the error:

2018-03-30 18:44:46.717257+0530 Camera[369:36651] * Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '* -[AVCaptureSession addOutput:] Cannot add output to capture session -> because more than one output of the same type is unsupported' *** First throw call stack: (0x18564ed8c 0x1848085ec 0x18b13ca74 0x104b3c140 0x104b3b75c 0x104b3b794 0x18f3fe750 0x18f96b2a4 0x18f560e6c 0x18f3fd7a8 0x18f95cac4 0x18f3f7540 0x18f3f7078 0x18f3f68dc 0x18f3f5238 0x18fbd6c0c 0x18fbd91b8 0x18fbd2258 0x1855f7404 0x1855f6c2c 0x1855f479c 0x185514da8 0x1874f7020 0x18f4f578c 0x104b40ca8 0x184fa5fc0) libc++abi.dylib: terminating with uncaught exception of type NSException

Here is my ViewController file:

import UIKit
import AVFoundation

class ViewController: UIViewController, AVCapturePhotoCaptureDelegate {

@IBOutlet weak var previewView: UIView!
override var prefersStatusBarHidden: Bool { return true }
var session = AVCaptureSession()
var device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
var frontCameraMode = false
var photoOutput : AVCapturePhotoOutput?

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

    loadCamera()
    let tap = UITapGestureRecognizer(target: self, action: #selector(swapCamera))
    tap.numberOfTapsRequired = 2
    previewView.addGestureRecognizer(tap)
}

@objc func swapCamera() {
    if(frontCameraMode == true){
        frontCameraMode = false
    }
    else if(frontCameraMode == false){
        frontCameraMode = true
    }
    loadCamera()
}

func loadCamera() {

    if(frontCameraMode == false){
        var device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
    }
    else if (frontCameraMode == true){
        var device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
    }
    do{
        var deviceInput = try AVCaptureDeviceInput(device: device!)
        print(deviceInput)
        session.sessionPreset = AVCaptureSession.Preset.high
        /*if let inputs = session.inputs as? [AVCaptureDeviceInput] {
            for input in inputs {
                session.removeInput(input)
            }
        }*/

        if session.inputs.isEmpty {
            session.addInput(deviceInput)
        }

        var previewLayer = AVCaptureVideoPreviewLayer(session: session)

        previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        previewLayer.frame = view.layer.bounds

        previewView.layer.addSublayer(previewLayer)

        session.startRunning()

        photoOutput = AVCapturePhotoOutput()
        session.addOutput(photoOutput!)
    }
    catch{
        print(error)
    }
}

@IBAction func onShootButtonTap(_ sender: Any) {
    // Make sure capturePhotoOutput is valid
    guard let photoOutput = self.photoOutput else { return }
    // Get an instance of AVCapturePhotoSettings class

    let photoSettings = AVCapturePhotoSettings()
    // Set photo settings for our need

    photoSettings.isAutoStillImageStabilizationEnabled = true

    //photoSettings.isHighResolutionPhotoEnabled = true
    photoSettings.flashMode = .auto
    // Call capturePhoto method by passing our photo settings and a
    // delegate implementing AVCapturePhotoCaptureDelegate

    photoOutput.capturePhoto(with: photoSettings, delegate: self)

}


@available(iOS 11.0, *)
func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
    let imageData = photo.fileDataRepresentation()

    let capturedImage = UIImage.init(data: imageData! , scale: 1.0)
    if let image = capturedImage {
        // Save our captured image to photos album
        UIImageWriteToSavedPhotosAlbum(image, nil, nil, nil)
    }
}

override func didReceiveMemoryWarning() {
    super.didReceiveMemoryWarning()
    // Dispose of any resources that can be recreated.
}


}

You need to check session's canAddInput and canAddOutput before adding inputs and outputs

session.beginConfiguration()
session.sessionPreset = AVCaptureSession.Preset.photo
let cameraPosition: AVCaptureDevice.Position = self.configuration.usesFrontCamera ? .front : .back
let aDevice = deviceForPosition(cameraPosition)
if let d = aDevice {
   videoInput = try? AVCaptureDeviceInput(device: d)
}
if let videoInput = videoInput {
   if session.canAddInput(videoInput) {
      session.addInput(videoInput)
   }
   if session.canAddOutput(imageOutput) {
      session.addOutput(imageOutput)
   }
}
session.commitConfiguration()

//A helper function for device position
func deviceForPosition(_ p: AVCaptureDevice.Position) -> AVCaptureDevice? {
    for device in AVCaptureDevice.devices(for: AVMediaType.video) where device.position == p {
        return device
    }
    return nil
}

I would suggest you to not reinvent the wheel and use this resource instead - https://github.com/Yummypets/YPImagePicker

确保在请求相机访问权限时为Info.plist文件中的Privacy - Camera Usage Description键设置一个值。

Every time you press swap button, you create new camera preview layer with already used camera session, this makes crash. to fix:

declare new variable for current device input

var deviceInput: AVCaptureDeviceInput?

change your swapCamera function to change device input back/front and save input in variable declared above, to remove in second swap.

@objc func swapCamera() {
    frontCameraMode = !frontCameraMode
    let newDeviceInput = try! AVCaptureDeviceInput(device: device!)

    session.beginConfiguration()
    if(frontCameraMode == false){
        device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .back)
        session.removeInput(deviceInput!)
        session.addInput(newDeviceInput)
    }
    else if (frontCameraMode == true){
        device = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: .front)
        session.removeInput(deviceInput!)
        session.addInput(newDeviceInput)
    }
    session.commitConfiguration();

    deviceInput = newDeviceInput
}

remove local let variable in loadCamera function

change

let deviceInput = try AVCaptureDeviceInput(device: device!)

to

deviceInput = try AVCaptureDeviceInput(device: device!)

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM