0

I've written the code below based on several examples I found online, in order to capture video data from the built-in camera on macOS. No fatal errors are thrown while setting up the AVCaptureSession, yet the delegate is not being called, and some apparent errors are logged as soon as I call captureSession.startRunning().

So far, I've tried making small adjustments to my code (e.g. removing videoSettings on the output or the sessionPreset from the session), as well as checking in the systems preferences that my test app has camera access permission. However, the logs remain, and the delegate is not getting called.

CameraInput class (includes delegate):

final class CameraInput: NSObject {
    private lazy var sampleBufferDelegateQueue = DispatchQueue(label: "CameraInput")
    private lazy var captureSession: AVCaptureSession = {
        let session = AVCaptureSession()
        session.sessionPreset = .hd1280x720

        let device = AVCaptureDevice.default(for: .video)!
        let input = try! AVCaptureDeviceInput(device: device)
        session.addInput(input)

        let output = AVCaptureVideoDataOutput()
        output.videoSettings = [
            kCVPixelBufferPixelFormatTypeKey as String: kCVPixelFormatType_32BGRA,
            kCVPixelBufferMetalCompatibilityKey as String: true
        ]
        output.alwaysDiscardsLateVideoFrames = true
        output.setSampleBufferDelegate(self, queue: self.sampleBufferDelegateQueue)
        session.addOutput(output)

        return session
    }()
}

extension CameraInput: CVInput {
    func start() {
        guard !self.captureSession.isRunning else {
            return
        }

        self.captureSession.startRunning()
    }

    func stop() {
        guard self.captureSession.isRunning else {
            return
        }

        self.captureSession.stopRunning()
    }
}

extension CameraInput: AVCaptureVideoDataOutputSampleBufferDelegate {
    private func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        // Handle captured frame
    }
}

ViewController (default viewcontroller in new cocoa app):

class ViewController: NSViewController {
    lazy var cameraInput = CameraInput()

    override func viewDidLoad() {
        super.viewDidLoad()

        self.cameraInput.start()
    }
}

The logged information:

2019-01-02 16:27:15.821858+0100 TestApp[14182:1492243] [] CMIO_Unit_ScopeElement.h:200:SafeGetElement Throwing err: -67454
2019-01-02 16:27:15.823248+0100 TestApp[14182:1492243] [] CMIOUnitFigBaseObjectImpl.c:246:CMIOUnitCreateFromDescription Invalid paramater
2019-01-02 16:27:15.840190+0100 TestApp[14182:1492243] [] CMIO_Unit_Input_Device.cpp:244:GetPropertyInfo CMIOUInputFromProcs::GetPropertyInfo() failed for id 102, Error: -67456

Any help in getting this to work is greatly appreciated! Also, please inform me if more information is required.

EDIT After running into this question, I've ruled out that it is a problem regarding sandboxing.

Gori
  • 347
  • 2
  • 13

3 Answers3

2

You need to remove the keyword private from your did captureOutput:didOutput method.

I guess it changes the signature enough for your method to not be called.

Rhythmic Fistman
  • 34,352
  • 5
  • 87
  • 159
  • This was the problem! However, without the `private`, Xcode suggests to make it private which is the reason I did that in the first place. Making it explicitly `public` solves the warning, and makes it work – Gori Jan 03 '19 at 12:55
  • Mine didn't do that. There's something going on with your project, which is surely more complicated than my toy example. – Rhythmic Fistman Jan 03 '19 at 12:59
  • Well, at least the problem is solved. Thanks for pointing this out! – Gori Jan 03 '19 at 13:00
0

Your code seems to be ok. Try to

  • create a new "CocoaApp/Swift" project in Xcode
  • paste both of your code snippets inside the AppDelegate
  • add AVCaptureVideoDataOutputSampleBufferDelegate to the app delegate declaration
  • add self.captureSession.startRunning() to the applicationDidFinishLaunching function of the AppDelegate
  • enabling camera usage in the sandbox settings
  • add a NSCameraUsageDescription to the Info.plist (macOS 10.14 and later)

I've just tested this, the delegate function is called repeatedly. I see the mentioned log output on macOS 10.14 / XCode 10 (it's not showing up on macOS 10.13 / Xcode 9), but it doesn't prevent capturing.

So the problem is not in the code given in the question. Make sure that all objects are actually created as intended, that the capture session setup runs, and that no objects - especially the output delegate - are released prematurely.

NoHalfBits
  • 604
  • 1
  • 5
  • 10
  • This works (using the code of @RhythmicFistman), which leaves me to wonder why it stops working when I put the code in a separate class. I've put a print in the `deinit` of `CameraInput`, but it's not called. – Gori Jan 03 '19 at 12:40
  • Weird - do you nil your lazy cameraInput? – Rhythmic Fistman Jan 03 '19 at 13:00
0

I ran into this exact error. The marked answer helped me start down the right trail but I feel my problem is divergent enough to merit another answer

My code was this when it wasn't working.

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

That is what it used to be. I used Xcode autocomplete to get the current version of that.

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

So moral of the story... Conversion to Swift <insert version> is Available doesn't always work perfectly.

Lucas Goossen
  • 527
  • 7
  • 15