Search code examples
iosswiftavcapturesession

Takling simple photo with AVCaptureSession : Getting error "No active and enabled video connection"


I'm getting started taking simple photos with my iPhone using Xcode and swift. I've boiled down a couple of sample projects to a simple class below, derived from AVCapturePhotoCaptureDelegate, with Initialize, TakePhoto methods, and a callback photoOutput -- i.e., dead simple. To test, I created a simple app with "Initialize" and "Take Photo" buttons.

When I take the photo and the "photoOutput.capturePhoto" is called, it halts with an exception "No active and enabled video connection."

Apparently I've missed something fundamental in the boil down. One thing I left out from the sample project is the preview layer... does this require an active preview to function? Is that what it means by lacking a "video" connection? why would it require that?

Or is there something else I'm missing?

Thanks very much.


import UIKit
import AVFoundation

class cPhotoCaptureNormal: NSObject, AVCapturePhotoCaptureDelegate
{
    private let photoOutput = AVCapturePhotoOutput()
    
    public func Initialize()
    {
        
        let captureSession = AVCaptureSession()
        
        if let captureDevice = AVCaptureDevice.default(for: AVMediaType.video) {
            do {
                let input = try AVCaptureDeviceInput(device: captureDevice)
                if captureSession.canAddInput(input) {
                    captureSession.addInput(input)
                }
            } catch let error {
                print("Failed to set input device with error: \(error)")
            }
            
            if captureSession.canAddOutput(photoOutput) {
                captureSession.addOutput(photoOutput)
            }
            else
            {
                print("Failed to add output device")
            }

            captureSession.startRunning()
        }
    }
    
    public func TakePhoto()
    {
        let photoSettings = AVCapturePhotoSettings()
        if let photoPreviewType = photoSettings.availablePreviewPhotoPixelFormatTypes.first {
            photoSettings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: photoPreviewType]
// EXCEPTION OCCURS ON FOLLOwING LINE
            photoOutput.capturePhoto(with: photoSettings, delegate: self)
        }
    }
    
    func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
        guard let imageData = photo.fileDataRepresentation() else { return }
        let previewImage = UIImage(data: imageData)
        
        // ... post process and save UIImage
        

    }
}




Solution

  • Assuming you are calling your Initialize() function prior to calling TakePhoto(), the most likely cause of your issue that your AVCaptureSession is going out of scope at the end of Initialize. Make captureSession a property instead of a local variable. This will keep the session alive and allow you take a photo.

    You should also call captureSession.stopRunning() at some point.

    class cPhotoCaptureNormal: NSObject, AVCapturePhotoCaptureDelegate {
        private let photoOutput = AVCapturePhotoOutput()
        private let captureSession = AVCaptureSession()
                
        public func initialize() {
            if let captureDevice = AVCaptureDevice.default(for: AVMediaType.video) {
                do {
                    let input = try AVCaptureDeviceInput(device: captureDevice)
                    if captureSession.canAddInput(input) {
                        captureSession.addInput(input)
                    }
                } catch let error {
                    print("Failed to set input device with error: \(error)")
                }
                
                if captureSession.canAddOutput(photoOutput) {
                    captureSession.addOutput(photoOutput)
                } else {
                    print("Failed to add output device")
                }
    
                captureSession.startRunning()
            }
        }
        
        public func takePhoto() {
            let photoSettings = AVCapturePhotoSettings()
            if let photoPreviewType = photoSettings.availablePreviewPhotoPixelFormatTypes.first {
                photoSettings.previewPhotoFormat = [kCVPixelBufferPixelFormatTypeKey as String: photoPreviewType]
                photoOutput.capturePhoto(with: photoSettings, delegate: self)
            }
        }
        
        func photoOutput(_ output: AVCapturePhotoOutput, didFinishProcessingPhoto photo: AVCapturePhoto, error: Error?) {
            guard let imageData = photo.fileDataRepresentation() else { return }
            let previewImage = UIImage(data: imageData)
            
            // ... post process and save UIImage
        }
    }
    

    On an unrelated note, it is standard practice in Swift to begin function names with lowercase letters. Only class, struct, and enum names start with uppercase letters.