背景:
我正在与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帖子,其中答案使用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)
}
//...
}
}
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函数。有任何想法吗?
答案 0 :(得分:0)
好的,我已经找到了遭受酷刑的理由。因此,我正在使用AVCaptureMetadataOutput
,AVCaptureDepthDataOutput
,AVCaptureAudioDataOutput
和AVCaptureVideoDataOutput
,并使用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() }
}
这将按预期的每一帧进行调用,我可以获取单独的数据集,音频,视频等,并根据自己的喜好进行操作。