【问题标题】:captureOutput not being called未调用 captureOutput
【发布时间】:2017-12-13 10:23:03
【问题描述】:

我已经研究这个太久了。

我正在尝试获取 MacOS 网络摄像头数据并在网络摄像头输出的帧上运行 CIDetect。

我知道我需要:

  • AVCaptureDevice(如输入)连接到AVCaptureSession

  • AVCaptureVideoDataOutput(作为输出)连接到AVCaptureSession

  • 致电.setSampleBufferDelegate(AVCaptureVideoDataOutputSampleBufferDelegate, DelegateQueue)

由于某种原因,在调用.setSampleBufferDelegate(...) 之后(当然还有在AVCaptureSession 实例上调用.startRunning() 之后),我的AVCaptureVideoDataOutputSampleBufferDelegatecaptureOutput 没有被调用。

我发现很多人在网上遇到了这个问题,但我找不到任何解决方案。

在我看来,这似乎与 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 相关的所有必要变量都已在Viewcontrollerinit() 中实例化。为了清楚起见,我省略了。

有什么想法吗?

谢谢!

科维克

编辑: - 修复了从 selfMyDelegate 的设置委托。

这就是我初始化videoDataOutputQueue的方式:

    videoDataOutputQueue = DispatchQueue(
        label: "VideoDataOutputQueue"   
    );

【问题讨论】:

    标签: avfoundation macos-sierra


    【解决方案1】:

    我有一个类似的问题:在我的情况下,问题是在 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();
    

    【讨论】:

    • 您能否提供有关如何执行此操作的更多信息?如何将AVCaptureMetadataOutput 添加到AVCaptureSession 对象。 Xcode 9.2 抱怨我尝试过的一切。
    • 我编辑了我的回复,我使用的是 XCode 9.2。希望对你有帮助
    【解决方案2】:

    您在声明所需的样本缓冲区委托方法时犯了一个错误:

    captureOutput(_:didOutputSampleBuffer:from:).

    请检查并确保它是:

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

    PS:注意该方法的参数是如何声明的。所有参数都有'!'这意味着自动展开。

    【讨论】:

    • 我的控制台现在:> 成功了吗? > 成功? > 成功? ... 成功!谢谢!作为后续,为什么 XCode 没有通知我声明不正确?
    • XCode 不会通知你,因为 captureOutput(_:didOutputSampleBuffer:from:) 是 AVCaptureVideoDataOutputSampleBufferDelegate 协议的可选方法
    • 我在 10.15.6 和 swift 5.3。我对 Slackware 有同样的问题,但是,当我使用此答案中提供的签名(参数中自动解包)时,swift 编译器抱怨“'captureOutput(_:didOutput:from:)' 的参数具有不同的可选性协议'AVCaptureVideoDataOutputSampleBufferDelegate'所期望的“任何想法?看起来 Apple 又要做出改变了?
    • 只是我之前评论的跟进,如果我在 NSViewController 派生类中实现协议 (AVCaptureVideoDataOutputSampleBufferDelegate),则没有自动解包参数的正常签名 captureOutput(_:didOutput:from:) 可以正常工作美好的。如果我有一个从 NSObject 派生的单独类来实现协议,就像 Slackware 所做的那样,它就行不通了。
    【解决方案3】:

    在我的情况下,没有调用委托方法,因为 AVCaptureMovieFileOutput 在“AVCaptureVideoDataOutput”之前添加到会话中。我猜只能将一个与视频相关的输出添加到会话中。仅添加 AVCaptureVideoDataOutput 即可解决问题。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2016-10-19
      • 2021-12-14
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2022-01-14
      • 1970-01-01
      • 2020-07-24
      相关资源
      最近更新 更多