Jojodmo
Jojodmo

Reputation: 23596

Inactive/invalid connection passed when attempting to take a photo

I am attempting to take a photo using a custom view (without using UIImagePickerController), but whenever I attempt to take the photo, the app crashes, and this error is thrown:

Terminating app due to uncaught exception 'NSInvalidArgumentException', reason: '-[AVCaptureStillImageOutput captureStillImageAsynchronouslyFromConnection:completionHandler:] - inactive/invalid connection passed.'

Here's my takePhoto() function, which causes the error:

func takePhoto(sender: UIButton!){
    var still: AVCaptureStillImageOutput = AVCaptureStillImageOutput()
    var connection: AVCaptureConnection = AVCaptureConnection(inputPorts: self.input.ports, output: still)

    if(connection.enabled){
        still.captureStillImageAsynchronouslyFromConnection(connection, completionHandler: {(buffer: CMSampleBuffer!, error: NSError!) -> Void in
            println("picture taken")//this never gets executed
        })
    }
}

I've also tried setting the connection variable in the takePhoto() function to:

self.output.connections[0] as AVCaptureConnection

as well as

(self.session.outputs[0] as AVCaptureOutput).connections[0] as AVCaptureConnection

and I get the same result.

On the same view as the take photo button, there is also a live preview of the camera (which works):

func setupCamera(){
    self.session = AVCaptureSession()
    self.session.sessionPreset = AVCaptureSessionPreset640x480
    self.session.beginConfiguration()
    self.session.commitConfiguration()

    var error: NSError?

    var devices: [AVCaptureDevice] = AVCaptureDevice.devices() as [AVCaptureDevice]
    for device in devices{
        if(device.hasMediaType(AVMediaTypeVideo) && device.supportsAVCaptureSessionPreset(AVCaptureSessionPreset640x480)){

            //the input variable is initialized here

            self.input = AVCaptureDeviceInput.deviceInputWithDevice(device as AVCaptureDevice, error: &error) as AVCaptureDeviceInput

            if(self.session.canAddInput(self.input)){
                self.session.addInput(self.input)
                break
            }
        }
    }

    var settings = [kCVPixelBufferPixelFormatTypeKey:kCVPixelFormatType_32BGRA]

    //the output variable is initialized here

    self.output = AVCaptureVideoDataOutput()
    self.output.videoSettings = settings
    self.output.alwaysDiscardsLateVideoFrames = true

    if(self.session.canAddOutput(self.output)){
        self.session.addOutput(self.output)
    }

    var captureLayer = AVCaptureVideoPreviewLayer(session: self.session)
    captureLayer.frame = CGRectMake(0, 64, self.view.frame.width, (self.view.frame.width * 4) / 3)
    self.view.layer.addSublayer(captureLayer)

    self.session.startRunning()
}

I'm testing this on my iPhone 5s, and everything works, including the preview, except for the takePhoto() function.

Is there any way to do this, or do I have to use UIImagePickerController?

Upvotes: 3

Views: 3008

Answers (2)

X.Y.
X.Y.

Reputation: 13906

Whoever reaches out to this question from crashlytics or something you cannot reproduce:

The user denied camera access. Check if the camera permission is granted. I got production crashes, and it turned out to be this case.

Upvotes: 2

gabbler
gabbler

Reputation: 13766

When you set up camera, add a stillImageOutput to your AVCaptureSession.

self.stillImageOutput = AVCaptureStillImageOutput()
let stillSettings = [AVVideoCodecJPEG:AVVideoCodecKey]
self.stillImageOutput.outputSettings = stillSettings

if(self.session.canAddOutput(self.stillImageOutput)){
    self.session.addOutput(self.stillImageOutput)
}

Then when taking photo, get the AVCaptureSession from stillImageOutput.

func takePhoto(sender: UIButton!){
    let connection = self.stillImageOutput.connectionWithMediaType(AVMediaTypeVideo)

    if(connection.enabled){
        self.stillImageOutput.captureStillImageAsynchronouslyFromConnection(connection, completionHandler: {(buffer: CMSampleBuffer!, error: NSError!) -> Void in
            println("picture taken") // Now, this is executed
        })
    }
}

Upvotes: 6

Related Questions