Search code examples
iosswiftcameraavfoundationiphone-xs

Settings for AVCaptureDevice are not being used when Photo is captured


I am trying to write a basic iOS app that captures RAW photos with some fixed settings. However I run into a Problem, while taking the photo. The settings that I chose in the configuration of the device (Telecamera of iPhone Xs) are shown in the Preview (fixed Focus, changing lighting, depending on the iso i choose in the code), are not applied when I press the capture button and the capturePhoto method is run. Then I can see the focus adjusting and the torch firing anyway. At first I suspected the session-preset to override my settings, as soon as I start the session, but locking the device until the session is running did not help. Thanks in advance!

My code:

import UIKit
import AVFoundation
import Photos

class ViewController: UIViewController {

    //instance variables:

    //dispatch-queue for session setup:
    var setupQueue  = DispatchQueue(label: "sessionSetupQueue")

    //session-related:
    var captureSession:     AVCaptureSession!
    var camera:             AVCaptureDevice!
    var captureInput:       AVCaptureInput!
    var photoOutput:        AVCapturePhotoOutput!
    var photoSettings:      AVCapturePhotoSettings!

    //view-model:
    var previewLayer:       AVCaptureVideoPreviewLayer!

    @IBOutlet weak var capturePreviewView: UIView!
    @IBOutlet weak var photoCaptureButton: UIButton!

}

//extension with methods and functions:

extension ViewController {

    override func viewDidLoad() {
        super.viewDidLoad()


        do {
            try self.configureSession()

                //Create a preview view:
            self.previewLayer = AVCaptureVideoPreviewLayer(session: self.captureSession)
            self.previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill

            //connect preview view to UIView object


            self.capturePreviewView.layer.insertSublayer(self.previewLayer, at: 0)
            self.previewLayer.frame = self.view.frame


        } catch {
            print("could not set up a CaptureSession!")
            return
        }
    }

    //configuration of the capture session
    func configureSession() throws {

        // create a capture session and set it up for configuration
        self.captureSession = AVCaptureSession()
        captureSession.sessionPreset = .photo
        captureSession.beginConfiguration()

        //find tele-camera and set it as input to the session
        let camera = AVCaptureDevice.default(.builtInTelephotoCamera, for: .video, position: .back)
        self.camera = camera

        //configure the device before adding it as an input to the session
        do {
            try configureDevice()
        }
        guard let captureInput = try? AVCaptureDeviceInput(device: self.camera!), captureSession.canAddInput(captureInput) else { return }
        captureSession.addInput(captureInput)

        //Set an output for the session:
        self.photoOutput = AVCapturePhotoOutput()
        photoOutput.isHighResolutionCaptureEnabled = true
        guard captureSession.canAddOutput(photoOutput) else { return }
        captureSession.addOutput(photoOutput)

        //commit the configuration:
        captureSession.commitConfiguration()

        //start the session:
        captureSession.startRunning()

    }

    //configure the device

    func configureDevice() throws {

        //lock the device for setup

        do {
            try camera.lockForConfiguration()
        } catch {
            print("could not lock device!")
            return
        }

        //set camera parameters
        camera.focusMode    = .locked
        if camera.isLockingFocusWithCustomLensPositionSupported {
            camera.setFocusModeLocked(lensPosition: 1.0, completionHandler: nil)
        }
        if camera.isExposureModeSupported(.custom) {
            camera.setExposureModeCustom(duration: AVCaptureDevice.currentExposureDuration, iso: 60, completionHandler: nil)
        }
        camera.torchMode    = .off

        //end setup by unlocking for configuration
        camera.unlockForConfiguration()

    }

    //capture fuction

    @IBAction func capturePhoto (_ sender: UIButton) {

        //settings configuration

        //setup settings for RAW capture
        self.photoSettings  = AVCapturePhotoSettings(rawPixelFormatType: self.photoOutput.availableRawPhotoPixelFormatTypes.first!)

        //disable red eye correction, image stabilization and flash
        photoSettings.isAutoRedEyeReductionEnabled = false
        photoSettings.isAutoStillImageStabilizationEnabled = false
        photoSettings.flashMode = .on

        //capture the photo
        self.photoOutput.capturePhoto(with: self.photoSettings, delegate: self)

        //visual effects:

        self.capturePreviewView.layer.opacity = 0
        UIView.animate(withDuration: 0.25) {
            self.capturePreviewView.layer.opacity = 1
        }

    }

}

//photoOutput delegate functions:

extension ViewController: AVCapturePhotoCaptureDelegate {

    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        if error != nil { print("Error capturing photo!"); return }

        PHPhotoLibrary.requestAuthorization { status in
            guard status == .authorized else { return }
            PHPhotoLibrary.shared().performChanges({
                //add captured photos data to asset
                let creationRequest =  PHAssetCreationRequest.forAsset()
                creationRequest.addResource(with: .photo, data: photo.fileDataRepresentation()!, options: nil)
            })
        }

    }

}

Solution

  • I found the solution:

    The problem was the flashMode in the photoSettings. Using flashMode = .on overrides most of the device settings i chose beforehand.

    In order to evaluate the flash intensity of the scene, the device uses some autofocus and torch.

    Without the flash, the custom Exposure was set correctly and the focus stayed fixed.