Muneeb Jan
Muneeb Jan

Reputation: 55

How to switch camera using AVFoundation

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.

Upvotes: 2

Views: 3286

Answers (2)

Sham Dhiman
Sham Dhiman

Reputation: 1566

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
}

Upvotes: 4

Yannick Loriot
Yannick Loriot

Reputation: 7136

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.

Upvotes: 0

Related Questions