iosswiftswiftuiavfoundation

How to switch from front camera to back camera in AVFoundation


I have CaptureDelegate that has a method setupCaptureDevice that I call from .task:

class CaptureDelegate: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate, ObservableObject {
    
    
    let captureSession = AVCaptureSession()         // For capturing what comes in from the device's camera
    let captureSessionQueue = DispatchQueue(label: "captureSessionQueue")   // For processing images
    

    @Published var cameraIsActive: Bool = false 


    func setupCaptureSession(captureDevice: AVCaptureDevice?) {

        
        let videoCaptureDevice: AVCaptureDevice
        
        if let captureDevice {
            videoCaptureDevice = captureDevice
        } else {
            //will pick the best camera or the only camera(for iphone SE)
            if let device = AVCaptureDevice.default(.builtInDualCamera,
                                                    for: .video, position: .back) {
                videoCaptureDevice = device
            } else if let device = AVCaptureDevice.default(.builtInWideAngleCamera,
                                                           for: .video, position: .back) {
                videoCaptureDevice = device
            } else {
                fatalError("Missing expected back camera device.")
            }

        }
                
        guard let videoDeviceInput = try? AVCaptureDeviceInput(device: videoCaptureDevice) else {      //input from the back Camera
            print( "Failed to create AVCaptureDeviceInput")
            return
        }
        
        let videoOutput = AVCaptureVideoDataOutput()                //the output that camera catches
        videoOutput.setSampleBufferDelegate(self, queue: captureSessionQueue)
        
        captureSession.beginConfiguration( )
        
        if captureSession.canAddInput(videoDeviceInput) {
            captureSession.addInput(videoDeviceInput)
        } else {
            print("Failed to add input to capture session")
        }
        
        if captureSession.canAddOutput(videoOutput) {
            captureSession.addOutput(videoOutput)
        } else {
            print("Failed to add output to capture session")
        }
        
        captureSession.sessionPreset = .high
        
        captureSession.commitConfiguration()    //commit the new frames to CaptureSession
        
        
        DispatchQueue.global().async {
            self.captureSession.startRunning()
        }
        

it takes an optional AVCaptureDevice so that the first time the app runs it initiates with the back camera. I want to allow the user to toggle from back to front and vice verse so I am attempting to have a published property that the user can toggle via a method call In CaptureDelegate:

@Published var currentCamera: AVCaptureDevice?


//Switch from front to back camera etc
func toggleCaptureDevice() {
captureSession.stopRunning()

//Now define the AVCapturedevice, switch to back cam if it is front and switch to front cam if its back

//Now call setupCaptureSession but this time actually pass in that device
}
    }

My problem is how do I know if the App is currently using the front or back camera to be able to toggle it and update my currentCamera?


Solution

  • I think what you need is: AVCaptureDeviceInput.position

    @Published var currentCameraPosition: AVCaptureDevice.Position = .back
    
    func toggleCaptureDevice() {
        captureSessionQueue.async { [weak self] in
            guard let self = self else { return }
            
            self.captureSession.beginConfiguration()
            
            if let currentInput = self.captureSession.inputs.first as? AVCaptureDeviceInput {
                self.captureSession.removeInput(currentInput)
            }
            
            let newPosition: AVCaptureDevice.Position = (self.currentCameraPosition == .back) ? .front : .back
            
            guard let newDevice = AVCaptureDevice.default(.builtInWideAngleCamera, for: .video, position: newPosition) else {
                print("Failed to get new camera device")
                return
            }
            
            do {
                let newInput = try AVCaptureDeviceInput(device: newDevice)
                
                if self.captureSession.canAddInput(newInput) {
                    self.captureSession.addInput(newInput)
                    self.currentCameraPosition = newPosition // Update camera state
                } else {
                    print("Failed to add new input to capture session")
                }
            } catch {
                print("Error switching camera: \(error)")
            }
            
            self.captureSession.commitConfiguration()
            self.captureSession.startRunning()
        }
    }