3

背景:

我正在使用AVCaptureVideoDataOutput其他AVCaptureSession各种AV工具来创建视频会话,以便我可以创建相机会话。我有相机在屏幕上看到的实时信息。

我使用AVCaptureVideoDataOutput而不是AVCaptureMovieFileOutput因为我通过实时提要获得的图像是使用CIFilter's. 现在,我想记录按下按钮时向用户显示的内容。我这样做的想法是使用下面的函数,因为我认为这个函数会捕获每一帧。我从Apple 的页面中推断出这一点,该页面指出:

每当输出捕获并输出新的视频帧、解码或重新编码它的 videoSettings 属性时,代表就会收到此消息。代表可以将提供的视频帧与其他 API 结合使用以进行进一步处理。

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

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

我的努力:

我试图模拟上面的 SO 帖子,其中答案AVAssetWriter用于写入AVCaptureVideoDataOutput文件。但是,我的代码(如下所示)没有调用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)
        }
        //...
    }
}

另一个文件.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 创建该方法,所以我没有弄乱方法名称,例如这个 SO post

问题:

我怎样才能适当地获取要调用的方法——假设我的直觉是正确的,这个方法是为每一帧调用的,并且是我可以插入到我的缓冲区中videoWriterInput——这样我就可以从AVCaptureSession屏幕上看到的视频录制?

值得注意的是:

这个项目在调用方面不起作用

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

虽然 确实有效。

编辑:

我发现由于某种原因,AVCaptureDataOutputSynchronizer导致它不调用委托函数。有任何想法吗?

4

1 回答 1

3

好吧,我找到了我折磨的理由。所以,我使用AVCaptureMetadataOutput, AVCaptureDepthDataOutput, AVCaptureAudioDataOutput, 并AVCaptureVideoDataOutput使用AVCaptureDataOutputSynchronizer.

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

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() }
}

这会按预期调用每一帧,我可以获得单独的数据集、音频、视频等,并用它们做我想做的事。

于 2019-07-16T19:56:34.880 回答