应用程序在切换到前置摄像头时崩溃

时间:2018-03-30 13:17:21

标签: ios swift avfoundation

我正在创建一个类似于相机的应用程序来在屏幕上双击交换相机。代码编译没有错误,但是当我双击屏幕到达前置摄像头时,我收到错误。这是错误:

  

2018-03-30 18:44:46.717257 + 0530相机[369:36651] *终止应用   由于未捕获的异常'NSInvalidArgumentException',原因:'*    - [AVCaptureSession addOutput:]无法将输出添加到捕获会话          - >    因为不止一个输出   相同的类型不受支持'   ***首先抛出调用堆栈:(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:终止于   NSException类型的未捕获异常

这是我的ViewController文件:

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.
}


}

3 个答案:

答案 0 :(得分:1)

在添加输入和输出之前,您需要检查会话的canAddInput和canAddOutput

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
}

我建议你不要重新发明轮子并使用这个资源 - https://github.com/Yummypets/YPImagePicker

答案 1 :(得分:0)

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

答案 2 :(得分:0)

每次按下交换按钮,您都会使用已经使用过的相机会话创建新的相机预览图层,这会导致崩溃。修复:

为当前设备输入声明新变量

var deviceInput: AVCaptureDeviceInput?

更改swapCamera函数以更改设备输入前/后并将输入保存在上面声明的变量中,以在第二次交换中删除。

@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
}

在loadCamera函数中删除local let变量

更改

let deviceInput = try AVCaptureDeviceInput(device: device!)

deviceInput = try AVCaptureDeviceInput(device: device!)