16

I'm starting to develop an iOS app and this is my first SO post. I'm trying to implement a UI view which can show the preview video of the rear camera and process the captured frames. My preview layer works perfectly and I can see the picture display in my UI view. However, the captureOutput function is never called.

I have searched online for silimar issues and solutions for a while and tried to tweak different things including the output, connection, and dispatch queue settings, but none has worked. Can anyone help me out or share some insights and directions? Thanks a lot in advance!

Here is my code, I'm using Xcode 11 beta with iOS 10 as build target.

class ThreeDScanningViewController: UIViewController, 
AVCaptureVideoDataOutputSampleBufferDelegate {

    @IBOutlet weak var imageView: UIImageView!

    var session : AVCaptureSession!
    var device : AVCaptureDevice!
    var output : AVCaptureVideoDataOutput!
    var previewLayer : AVCaptureVideoPreviewLayer!

    override func viewDidLoad() {
        super.viewDidLoad()
                //NotificationCenter.default.addObserver(self, selector: #selector(self.startedNotif), name: NSNotification.name.CaptureSessionDidStartRunningNotification, object: nil)

    func initCamera() -> Bool {
        session = AVCaptureSession()
        session.sessionPreset = AVCaptureSession.Preset.medium

        let devices = AVCaptureDevice.devices()

        for d in devices { 
            if ((d as AnyObject).position == AVCaptureDevice.Position.back) {
                device = d as! AVCaptureDevice
            }
        }
        if device == nil {
            return false
        }

        do {
            // Set up the input

            let input : AVCaptureDeviceInput!
            try input = AVCaptureDeviceInput(device: device)

            if session.canAddInput(input) {
                session.addInput(input)
            } else {
                return false
            }

            // Set up the device

            try device.lockForConfiguration()
            device.activeVideoMinFrameDuration = CMTimeMake(1, 15)
            device.unlockForConfiguration()

            // Set up the preview layer

            previewLayer = AVCaptureVideoPreviewLayer(session: session)
            previewLayer.frame = imageView.bounds
            imageView.layer.addSublayer(previewLayer)

            // Set up the output

            output = AVCaptureVideoDataOutput()
            output.videoSettings = [(kCVPixelBufferPixelFormatTypeKey as NSString) as String: kCVPixelFormatType_32BGRA]

            let queue = DispatchQueue(label: "myqueue")
            output!.setSampleBufferDelegate(self, queue: queue)

            output.alwaysDiscardsLateVideoFrames = true

            if session.canAddOutput(output) {
                session.addOutput(output)
            } else {
                return false
            }

            for connection in output.connections {
                if let conn = connection as? AVCaptureConnection {
                    if conn.isVideoOrientationSupported {
                        conn.videoOrientation = AVCaptureVideoOrientation.portrait
                    }
                }
            }

            session.startRunning()

        } catch let error as NSError {
            print(error)
            return false
        }

        return true
    }

    func captureOutput (captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {
        print("captureOutput!\n");
        DispatchQueue.main.async(execute: {
            // Do stuff
        })
    }
}

Here are some links I've looked into, none is relevant to solve my issue:

Zayn Ali
  • 4,765
  • 1
  • 30
  • 40
CMao
  • 591
  • 1
  • 4
  • 7

6 Answers6

43

I have finally managed to find the cause of the issue. You need to make sure to use the correct function signature for the captureOutput function for the Swift 3 syntax.

func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)

NOT

func captureOutput(_ output: AVCaptureOutput, didOutputSampleBuffer sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)

I was using older version of the Swift syntax and the compiler did not warn me of the issue! After correcting the function signatures, the captureOutput function gets called beautifully:-)

Wukerplank
  • 4,156
  • 2
  • 28
  • 45
CMao
  • 591
  • 1
  • 4
  • 7
  • I thank you for posting the solution for this, even if you found it yourself. This way others might find the answer they need as well! Great job! – Canis Aug 12 '17 at 14:53
  • 1
    Thank you Canis, it's very kind of you! :-) – CMao Aug 12 '17 at 19:54
  • 2
    This should be marked as correct answer. Thank you @CMao . I am still in shock that Apple did not mark the method as `deprecated`, took me ages to find the problem. – Victor Jan 18 '18 at 19:12
  • Oh my word, this is bad! It cost me a few hours of poking aimlessly at the AV Framework (given its propensity for general cargo culting), and then this answer saved me. Thanks! – llude Feb 11 '20 at 08:30
  • This solved it for me too! I had copied in some sample code and this time the compiler didn't seem to mind. I guess because it's an optional method. – danwood May 15 '20 at 03:02
13

From Swift 4:

func captureOutput(_ captureOutput: AVCaptureOutput!, 
didOutputMetadataObjects metadataObjects: [Any]!, from connection: 
AVCaptureConnection!)  

won't be called as it no longer exists.

It has been changed to the following :

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) 
Ajay Reddy
  • 1,475
  • 1
  • 16
  • 20
4

I had a similar issue, however my issue was due to the way I was creating the AVCaptureSession.

I had opted to create the AVCaptureSession in a function and return it to the caller, this part is fine, however, I neglected to hold a reference to the session in a class variable which was causing the AVCaptureSession to go out of scope and be collected by ARC before the delegate could get invoked.

Once I set the class variable so that the AVCaptureSession wasn't released when the calling function exited everything started to work.

Wanted to share just in case someone else ran into the same issue.

James Jones
  • 1,486
  • 1
  • 12
  • 22
4

If previous answers cannot solve your problem for swift 4 and newer, correct the function name by add public can fix the problem.

public func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {}
Tony TRAN
  • 2,118
  • 1
  • 15
  • 16
  • This was my problem, and oddly enough it was working without `public` for audio sample buffers, but video buffers weren't coming through, and only on some devices. This fixed it, thanks! – charmingToad Oct 12 '22 at 20:14
2

According to this tutorial you need to commit your configuration before starting to run the session.

I also see that you have multiple points where you return false before the session can start to run. Hav you checked to see if you are exiting prematurely in one of these locations? Simply a console output, or a break point on the return statements can give you some info.

Canis
  • 4,130
  • 1
  • 23
  • 27
  • 1
    Hi Canis, thanks for your reply! I have tested and can confirm that the function doesn't return false before the session can start to run. I will looking into commit the configuration before starting to run the session shortly and see how it works. Thanks again! – CMao Aug 11 '17 at 07:56
  • 1
    I have tried to use the startConfiguration and commitConfiguration functions and it didn't solve the issue... – CMao Aug 12 '17 at 14:46
  • @CMao If following that tutorial does not work, then I'm at a loss too. My dev environment does not allow me to test your code myself at the moment either... Could you try to follow the tutorial step by step in a separate viewcontroller perhaps? Oh, another though, you are testing on a real device and not in the simulator? – Canis Aug 12 '17 at 14:50
0

The problem got fixed when i changed dualCamera to AVCaptureDeviceType.builtInWideAngleCamera swift 4. Hope it helps anyone in need.

Aragunz
  • 511
  • 5
  • 18