1

I have implemented the preview camera using AVFoundation, its working fine. But I have a hard time to switch the camera back and front. I have added a switch button at the bottom bar. By default, its the back camera, I want to switch it to front. How can I do that?

class FifteenSecsViewController: UIViewController, AVCaptureFileOutputRecordingDelegate {

    @IBOutlet weak var camPreview: UIView!
    let captureSession = AVCaptureSession()
    let movieOutput = AVCaptureMovieFileOutput()
    var previewLayer: AVCaptureVideoPreviewLayer!
    var activeInput: AVCaptureDeviceInput!
    var outputURL: URL!

    override func viewDidLoad() {
    super.viewDidLoad()

        if setupSession() {
            setupPreview()
            startSession()
        }
        self.switchCameraButton.addTarget(self, action: #selector(switchButtonTapped), for: .touchUpInside)
    }

    func setupSession() -> Bool {

    captureSession.sessionPreset = AVCaptureSession.Preset.high

    // Setup Camera
    let camera: AVCaptureDevice?

    camera = AVCaptureDevice.default(for: .video)
    do {
        let input = try AVCaptureDeviceInput(device: camera!)
        if captureSession.canAddInput(input) {
            captureSession.addInput(input)
            activeInput = input
        }
    } catch {
        print("Error setting device video input: \(error)")
        return false
    }

    // Setup Microphone
    let microphone = AVCaptureDevice.default(for: .audio)

    do {
        let micInput = try AVCaptureDeviceInput(device: microphone!)
        if captureSession.canAddInput(micInput) {
            captureSession.addInput(micInput)
        }
    } catch {
        print("Error setting device audio input: \(error)")
        return false
    }


    // Movie output
    let seconds : Int64 = 3
    let maxDuration = CMTime(seconds: Double(seconds), 
    preferredTimescale: 1)
    movieOutput.maxRecordedDuration = maxDuration
    if captureSession.canAddOutput(movieOutput) {
        captureSession.addOutput(movieOutput)
    }

    return true
    }

    func setupPreview() {
    // Configure previewLayer
        previewLayer = AVCaptureVideoPreviewLayer(session: 
        captureSession)
        previewLayer.frame = camPreview.bounds
        previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill
        camPreview.layer.addSublayer(previewLayer)
    }

        //MARK:- Camera Session
    func startSession() {      
        if !captureSession.isRunning {
            videoQueue().async {
                self.captureSession.startRunning()
            }
        }
    }

    @objc func switchButtonTapped(){
    // what to write here??
    }

}

Function switchButtonTapped is an actionTarget of UIButton. If I add this code in this button:

@objc func switchButtonTapped(){

    if setupSession() {
        setupPreview()
        startSession()
    }
}

Camerapreview screen shows a white screen and got stuck.

Yannick Loriot
  • 7,107
  • 2
  • 33
  • 56
Muneeb Jan
  • 45
  • 1
  • 7
  • checkout this link may be it helps you https://stackoverflow.com/questions/20864372/switch-cameras-with-avcapturesession – jignesh Vadadoriya Jan 01 '19 at 11:56
  • Supplying only a link as an answer is discouraged. The link can break if the page moves. Please consider explaining the basics of the answer you are providing since we’re not here to do basic searches that the OP could do themselves. – Magnas Jan 01 '19 at 11:58

2 Answers2

3

Try this code:

func switchCamera() {
    session?.beginConfiguration()
    let currentInput = session?.inputs.first as? AVCaptureDeviceInput
    session?.removeInput(currentInput!)
    let newCameraDevice = currentInput?.device.position == .back ? getCamera(with: .front) : getCamera(with: .back)
    let newVideoInput = try? AVCaptureDeviceInput(device: newCameraDevice!)
    session?.addInput(newVideoInput!)
    session?.commitConfiguration()
}

func getCamera(with position: AVCaptureDevice.Position) -> AVCaptureDevice? {
    guard let devices = AVCaptureDevice.devices(for: AVMediaType.video) as? [AVCaptureDevice] else {
        return nil
    }
    
    return devices.filter {
        $0.position == position
        }.first
}
Sham Dhiman
  • 1,348
  • 1
  • 21
  • 59
0

To begin create a device input for the front camera:

let frontDevice: AVCaptureDevice? = {
  for device in AVCaptureDevice.devices(for: AVMediaType.video) {
    if device.position == .front {
      return device
    }
  }

  return nil
}()

lazy var frontDeviceInput: AVCaptureDeviceInput? = {
  if let _frontDevice = self.frontDevice {
    return try? AVCaptureDeviceInput(device: _frontDevice)
  }

  return nil
}()

Then in your switchButtonTapped, if there is a front camera you can do the switch between the front and the ones:

func switchButtonTapped() {
  if let _frontDeviceInput = frontDeviceInput {
    captureSession.beginConfiguration()

    if let _currentInput = captureSession.inputs.first as? AVCaptureDeviceInput {
      captureSession.removeInput(_currentInput)

      let newDeviceInput = (_currentInput.device.position == .front) ? activeInput : _frontDeviceInput
      captureSession.addInput(newDeviceInput!)
    }

    captureSession.commitConfiguration()
  }
}

If you need more details, don't hesitate.

Yannick Loriot
  • 7,107
  • 2
  • 33
  • 56
  • Hi, thankyou so much it worked. but when i tapped the switchbutton again, the app crashes with an error ;"An AVCaptureInput instance may not be added to more than one session'" – Muneeb Jan Jan 02 '19 at 09:32
  • Are you sure you don't add the `activeInput` elsewhere? You can check whether the `activeInput` is already present in the `captureSession.inputs` and add/remove it accordingly. – Yannick Loriot Jan 02 '19 at 15:54