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.