使用AVCaptureVideoDataOutput

时间:2019-07-15 23:53:05

标签: swift delegates avcapturesession avcapture avcapturevideodataoutput

背景

我正在与AVCaptureVideoDataOutput和其他各种AVCaptureSession工具配合使用AV来创建视频会话,以便可以创建相机会话。我实时获得了相机在屏幕上看到的内容。

我使用AVCaptureVideoDataOutput而不是AVCaptureMovieFileOutput,因为通过实时供稿获取的图像是使用CIFilter处理的。现在,我想记录按下按钮时显示给用户的内容。我的想法是使用以下功能,因为我认为此功能可以捕获每一帧。我从Apple's Page推断出这一点:

  

只要输出捕获并输出新的视频帧,并按照其videoSettings属性的指定对其进行解码或重新编码,代表就会收到此消息。代表们可以将提供的视频帧与其他API结合使用以进行进一步处理。

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

我希望每个帧都在这里,以便可以使用AVAssetWriter并将缓冲区添加到videoWriterInput。可以在here中看到,答案表明方法func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)每次将sampleBuffer添加到videoWriterInput

我的努力:

我尝试模拟上述SO帖子,其中答案使用AVAssetWriterAVCaptureVideoDataOutput写入文件。但是,如下所示,我的代码未调用AVCaptureVideoDataOutputSampleBufferDelegate func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)委托方法。

注意:这还不是全部代码-我仅添加相关部分-如果您需要缺少某些内容,请告诉我

VideoCapture.swift

class VideoCapture: NSObject, AVCaptureVideoDataOutputSampleBufferDelegate {

    private let captureSession = AVCaptureSession()
    private let videoDataOutput = AVCaptureVideoDataOutput()
    private let dataOutputQueue = DispatchQueue(label: "com.Camera.dataOutputQueue")
    private var videoConnection: AVCaptureConnection!

    //NEVER GETS CALLED
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        print("Buf: \(sampleBuffer)")
    }

    func captureOutput(_ output: AVCaptureOutput, didDrop sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        print("Drop Buff: \(sampleBuffer)")
    }

    init() {
        //Some Setup
        captureSession.sessionPreset = AVCaptureSession.Preset.high

        //...
        do {
            // video output
            videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey as String: Int(kCVPixelFormatType_32BGRA)]
            videoDataOutput.alwaysDiscardsLateVideoFrames = true
            videoDataOutput.setSampleBufferDelegate(self, queue: dataOutputQueue)
            guard captureSession.canAddOutput(videoDataOutput) else { fatalError() }
            captureSession.addOutput(videoDataOutput)
            videoConnection = videoDataOutput.connection(with: .video)
        }
        //...
    }
}

AnotherFile.swift

class VC: UIViewController {

    private var videoCapture: VideoCapture!

    init() {
        self.videoCapture = VideoCapture()
    }

    public override func viewWillAppear(_ animated: Bool) {
        super.viewWillAppear(animated)
        guard let videoCapture = videoCapture else {return}
        videoCapture.startCapture()
    }
}

我的期望:

我希望该方法

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

被调用,并至少打印出上面示例中的缓冲区。这没有发生,因此我无法继续开发并将缓冲区插入videoWriteInput进行记录。

实际发生的情况:

它从未被调用过。我确保使用videoDataOutput.setSampleBufferDelegate(self, queue: dataOutputQueue)设置委托,并明确指出已完成委托方法。我确定要使用自动完成功能,以便该方法是从XCode创建的,因此我不会弄乱诸如this SO post之类的方法名称。

问题:

我如何适当地调用该方法-假设我的直觉是正确的,即每帧都调用此方法,并且该方法是我可以插入videoWriterInput的缓冲区-这样我就可以记录一个我可以在屏幕上看到AVCaptureSession的视频吗?

值得注意:

This项目 在呼叫

方面有效
func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection)

this 确实 正常工作。

编辑:

我发现由于某种原因,AVCaptureDataOutputSynchronizer使其不调用Delegate函数。有任何想法吗?

1 个答案:

答案 0 :(得分:0)

好的,我已经找到了遭受酷刑的理由。因此,我正在使用AVCaptureMetadataOutputAVCaptureDepthDataOutputAVCaptureAudioDataOutputAVCaptureVideoDataOutput,并使用AVCaptureDataOutputSynchronizer将它们组合在一起。

由于我使用的是AVCaptureDataOutputSynchronizer,因此它捕获了这里所有的委托调用。例如,我可以同步我的数据,它将调用AVCaptureDataOutputSynchronizer Delegate方法,而不是其他单独的委托。

dataOutputSynchronizer = AVCaptureDataOutputSynchronizer(dataOutputs: [videoDataOutput, depthDataOutput, metadataOutput])
dataOutputSynchronizer.setDelegate(self, queue: dataOutputQueue)

//... Some Code Later...

func dataOutputSynchronizer(_ synchronizer: AVCaptureDataOutputSynchronizer, didOutput synchronizedDataCollection: AVCaptureSynchronizedDataCollection) {

    guard let syncedVideoData = synchronizedDataCollection.synchronizedData(for: videoDataOutput) as? AVCaptureSynchronizedSampleBufferData else { return }
    guard !syncedVideoData.sampleBufferWasDropped else {
        print("dropped video:\(syncedVideoData)")
        return
    }
    let videoSampleBuffer = syncedVideoData.sampleBuffer
    print(videoSampleBuffer)

    let syncedDepthData = synchronizedDataCollection.synchronizedData(for: depthDataOutput) as? AVCaptureSynchronizedDepthData
    var depthData = syncedDepthData?.depthData
    if let syncedDepthData = syncedDepthData, syncedDepthData.depthDataWasDropped {
        print("dropped depth:\(syncedDepthData)")
        depthData = nil
    }

    // 顔のある位置のしきい値を求める
    let syncedMetaData = synchronizedDataCollection.synchronizedData(for: metadataOutput) as? AVCaptureSynchronizedMetadataObjectData
    var face: AVMetadataObject? = nil
    if let firstFace = syncedMetaData?.metadataObjects.first {
        face = videoDataOutput.transformedMetadataObject(for: firstFace, connection: videoConnection)
    }
    guard let imagePixelBuffer = CMSampleBufferGetImageBuffer(videoSampleBuffer) else { fatalError() }
}

这将按预期的每一帧进行调用,我可以获取单独的数据集,音频,视频等,并根据自己的喜好进行操作。