captureOutput не вызывается

Я слишком долго занимался этим.

Я пытаюсь получить данные веб-камеры MacOS и запустить CIDetect для кадров, выводимых веб-камерой.

Я знаю, что мне нужно:

  • подключите AVCaptureDevice (как на входе) к AVCaptureSession

  • подключите AVCaptureVideoDataOutput (как выход к) в AVCaptureSession

  • позвонить .setSampleBufferDelegate(AVCaptureVideoDataOutputSampleBufferDelegate, DelegateQueue)

По какой-то причине после вызова .setSampleBufferDelegate(...) (и, конечно, после вызова .startRunning() в экземпляре AVCaptureSession) мой AVCaptureVideoDataOutputSampleBufferDelegate captureOutput не вызывается.

Я обнаружил, что у многих людей возникают проблемы с этим в Интернете, но я не смог найти никакого решения.

Мне кажется, это связано с DispatchQueue.

MyDelegate.swift:

class MyDelegate : NSObject {


    var context: CIContext?;
    var detector : CIDetector?;

    override init() {
        context = CIContext();
        detector = CIDetector(ofType: CIDetectorTypeFace, context: context);
        print("set up!");

    }

}
extension MyDelegate : AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection) {
        print("success?");
        var pixelBuffer : CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!;
        var image : CIImage = CIImage(cvPixelBuffer: pixelBuffer);
        var features : [CIFeature] = detector!.features(in: image);
        for feature in features {
            print(feature.type);
            print(feature.bounds);
        }
    }

    func captureOutput(_ : AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection) {
        print("fail?");
    }
}

ViewController.swift:

var captureSession : AVCaptureSession;
var captureDevice : AVCaptureDevice?
var previewLayer : AVCaptureVideoPreviewLayer?

var vdo : AVCaptureVideoDataOutput;

var videoDataOutputQueue : DispatchQueue;

override func viewDidLoad() {
    super.viewDidLoad()

    camera.layer = CALayer()

    // Do any additional setup after loading the view, typically from a nib.
    captureSession.sessionPreset = AVCaptureSessionPresetLow

    // Get all audio and video devices on this machine
    let devices = AVCaptureDevice.devices()

    // Find the FaceTime HD camera object
    for device in devices! {
        print(device)

        // Camera object found and assign it to captureDevice
        if ((device as AnyObject).hasMediaType(AVMediaTypeVideo)) {
            print(device)
            captureDevice = device as? AVCaptureDevice
        }
    }

    if captureDevice != nil {
        do {   
            try captureSession.addInput(AVCaptureDeviceInput(device: captureDevice))
            // vdo : AVCaptureVideoDataOutput;
            vdo.videoSettings = [kCVPixelBufferPixelFormatTypeKey as AnyHashable: NSNumber(value: kCVPixelFormatType_32BGRA)]

            try captureDevice!.lockForConfiguration()
            captureDevice!.activeVideoMinFrameDuration = CMTimeMake(1, 30)
            captureDevice!.unlockForConfiguration()

            videoDataOutputQueue.sync{
                vdo.setSampleBufferDelegate(
                    MyDelegate,
                    queue: videoDataOutputQueue
                );
                vdo.alwaysDiscardsLateVideoFrames = true
                captureSession.addOutput(vdo)   
                captureSession.startRunning();
            }
        } catch {
            print(AVCaptureSessionErrorKey.description)
        }
    }

Все необходимые переменные внутри viewDidLoad, относящиеся к AVFoundation, были созданы внутри Viewcontroller init(). Я опустил это для ясности.

Любые идеи?

Спасибо, ТАК!

Ковек

РЕДАКТИРОВАТЬ: - Фиксированная настройка делегата от self до MyDelegate.

И вот как я инициализирую videoDataOutputQueue:

    videoDataOutputQueue = DispatchQueue(
        label: "VideoDataOutputQueue"   
    );

person Slackware    schedule 09.07.2017    source источник


Ответы (2)


Вы допустили ошибку в объявлении требуемого метода делегата буфера выборки:

captureOutput(_:didOutputSampleBuffer:from:).

Пожалуйста, проверьте это и убедитесь, что это:

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

PS: Обратите внимание, как объявлены параметры этого метода. Все параметры имеют знак "!" что означает автоматическое разворачивание.

person ninjaproger    schedule 10.07.2017
comment
моя консоль прямо сейчас: ›успех? > успех? > успех? ... Успех! Спасибо! В качестве продолжения, почему XCode не уведомляет меня о том, что объявление неверно? - person Slackware; 10.07.2017
comment
XCode не уведомляет вас, потому что captureOutput (_: didOutputSampleBuffer: from :) является дополнительным методом протокола AVCaptureVideoDataOutputSampleBufferDelegate - person ninjaproger; 10.07.2017
comment
Я использую 10.15.6 с быстрым 5.3. У меня такая же проблема со Slackware, однако, когда я использовал подпись (с автоматическим развертыванием параметров), предоставленную в этом ответе, быстрый компилятор пожаловался, что параметры 'captureOutput (_: didOutput: from :)' имеют другие возможности, чем ожидалось по протоколу 'AVCaptureVideoDataOutputSampleBufferDelegate' Есть идеи? Похоже, Apple снова что-то меняет? - person psksvp; 18.09.2020
comment
Просто продолжение моего предыдущего комментария: если я реализую протокол (AVCaptureVideoDataOutputSampleBufferDelegate) в классе, производном от NSViewController, обычный метод captureOutput (_: didOutput: from :) без параметров автоматической развертки будет работать нормально. Это просто не сработает, если у меня будет отдельный класс, производный от NSObject, для реализации протокола, как это сделал Slackware. - person psksvp; 18.09.2020

У меня была аналогичная проблема: в моем случае проблема заключалась в том, что при написании на Swift 4 вам нужно было реализовать следующий метод:

func metadataOutput(_ output: AVCaptureMetadataOutput, didOutput metadataObjects: [AVMetadataObject], from connection: AVCaptureConnection) 

вместо того:

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

Надеюсь, это поможет.

ИЗМЕНИТЬ

Этот метод должен быть реализован AVCaptureMetadataOutputObjectsDelegate (например, вашим контроллером представления). Чтобы начать сеанс захвата QRCode, вы можете попробовать что-то вроде этого:

    captureSession = AVCaptureSession()

    let videoCaptureDevice = AVCaptureDevice.default(for: AVMediaType.video);
    var videoInput:AVCaptureDeviceInput? =  nil;

    do {
        if let v = videoCaptureDevice{
            videoInput = try AVCaptureDeviceInput(device: v)
        }
        else{
            print("Error: can't find videoCaptureDevice");
        }

    } catch {
        let ac = UIAlertController(title: "Error", message: error.localizedDescription, preferredStyle: .alert)
        ac.addAction(UIAlertAction(title: "Ok", style: .default))
        present(ac, animated: true)
        return
    }

    if let videoInput = videoInput{
        if (captureSession.canAddInput(videoInput)) {
            captureSession.addInput(videoInput)
        } else {
            //Show error
            return;
        }
    }
    else{
        //Show error
        return;
    }

    let metadataOutput = AVCaptureMetadataOutput()

    if (captureSession.canAddOutput(metadataOutput)) {
        captureSession.addOutput(metadataOutput);

        metadataOutput.setMetadataObjectsDelegate(/*YOUR DELEGATE*/, queue: DispatchQueue.main);
        metadataOutput.metadataObjectTypes = [AVMetadataObject.ObjectType.qr, AVMetadataObject.ObjectType.code128];
    } else {
        //Show error
        return;
    }

    previewLayer = AVCaptureVideoPreviewLayer(session: captureSession);
    previewLayer.frame = view.layer.bounds;

    previewLayer.videoGravity = AVLayerVideoGravity.resizeAspectFill;
    view.layer.addSublayer(previewLayer);

    captureSession.startRunning();
person Andrea Gorrieri    schedule 07.12.2017
comment
Не могли бы вы предоставить дополнительную информацию о том, как это сделать? Как добавить AVCaptureMetadataOutput к объекту AVCaptureSession. Xcode 9.2 жалуется на все, что я пробовал. - person gone; 18.02.2018
comment
Отредактировал свой ответ, использую XCode 9.2. Надеюсь, это поможет - person Andrea Gorrieri; 21.02.2018