我正在使用AV基金会处理来自摄像机(iPhone 4s,iOS 6.1.2)的帧。我根据AV Foundation编程指南设置了AVCaptureSession,AVCaptureDeviceInput,AVCaptureVideoDataOutput。一切都按预期工作,我能够在captureOutput:didOutputSampleBuffer:fromConnection:
代表中收到框架。
我还有一个像这样的预览图层:
AVCaptureVideoPreviewLayer *videoPreviewLayer = [[AVCaptureVideoPreviewLayer alloc] initWithSession:_captureSession];
[videoPreviewLayer setFrame:self.view.bounds];
videoPreviewLayer.videoGravity = AVLayerVideoGravityResizeAspectFill;
[self.view.layer insertSublayer:videoPreviewLayer atIndex:0];
事情是,我在帧处理中不需要每秒30帧,但我无法如此快速地处理它们。所以我使用此代码来限制帧持续时间:
// videoOutput is AVCaptureVideoDataOutput set earlier
AVCaptureConnection *conn = [videoOutput connectionWithMediaType:AVMediaTypeVideo];
[conn setVideoMinFrameDuration:CMTimeMake(1, 10)];
[conn setVideoMaxFrameDuration:CMTimeMake(1, 2)];
这样可以正常工作并限制 captureOutput 委托收到的帧。
然而,这也限制了预览图层上的每秒帧数,预览视频变得非常无响应。
我从文档中了解到,帧持续时间是在连接上独立设置的,而预览层确实有不同的AVCaptureConnection。检查[videoPreviewLayer connection]
上的混合/最大帧持续时间表明它确实设置为默认值(1/30和1/24),并且与AVCaptureVideoDataOutput连接上设置的持续时间不同。
那么,是否可以仅在帧捕获输出上限制帧持续时间,并且仍然可以在预览视频上看到1 / 24-1 / 30帧持续时间?怎么样?
感谢。
答案 0 :(得分:4)
虽然你有两个AVCaptureConnection
是正确的,但这并不意味着他们可以独立设置最小和最大帧持续时间。这是因为他们正在共享相同的物理硬件。
如果连接#1以(例如)5帧/秒的速率激活卷帘快门,帧持续时间为1/5秒,则无法连接#2可以同时以1/30秒的帧持续时间激活快门30次/秒。
要获得你想要的效果需要两个相机!
接近你想要的唯一方法是遵循Kaelin Colclasure在3月22日回答中概述的方法。
然而,你做可以选择在这种方法中更复杂一些。例如,您可以使用计数器来决定丢弃哪些帧,而不是让线程休眠。您可以使该计数器响应即将到来的实际帧速率(您可以从captureOutput:didOutputSampleBuffer:fromConnection:
代理中携带的元数据中获取图像数据,或者您可以通过手动计时来自行计算帧)。您甚至可以通过合成帧而不是丢弃它来非常合理地模仿更长时间的曝光 - 就像App Store中的一些“慢速快门”应用程序一样(抛开不同的细节 - 例如不同的卷帘快门假象 - 实际上并不是这样) 1/5秒扫描的一帧与1/25秒扫描的五帧之间有很大差异然后粘在一起。
是的,这有点工作,但你正在尝试让一个摄像机实时像两个一样 - 而且这一切都不容易。
答案 1 :(得分:2)
这样想: 您要求捕捉设备限制帧持续时间,以便您获得更好的曝光。 精细。 您想以更高的帧速率预览。 如果您要以更高的速率进行预览,则捕捉设备(相机)将没有足够的时间来曝光帧,以便您在捕获的帧上获得更好的曝光。 这就像要求预览中的不同帧而不是捕获的帧。
我认为,如果可能的话,那也会是负面的用户体验。
答案 2 :(得分:1)
我的Cocoa(Mac OS X)应用程序遇到了同样的问题。这就是我解决它的方法:
首先,确保在单独的调度队列中处理捕获的帧。还要确保丢弃任何未准备好处理的帧;这是默认设置,但无论如何我都设置了下面的标志只是为了记录我依赖它。
videoQueue = dispatch_queue_create("com.ohmware.LabCam.videoQueue", DISPATCH_QUEUE_SERIAL);
videoOutput = [[AVCaptureVideoDataOutput alloc] init];
[videoOutput setAlwaysDiscardsLateVideoFrames:YES];
[videoOutput setSampleBufferDelegate:self
queue:videoQueue];
[session addOutput:videoOutput];
然后在处理委托中的帧时,您可以让线程休眠所需的时间间隔。代表没有醒来处理的框架被悄然丢弃。我实现了可选的方法来计算下面的丢帧,只是作为一个健全性检查;我的应用程序永远不会使用这种技术记录丢弃任何帧。
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didDropSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection;
{
OSAtomicAdd64(1, &videoSampleBufferDropCount);
}
- (void)captureOutput:(AVCaptureOutput *)captureOutput
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer
fromConnection:(AVCaptureConnection *)connection;
{
int64_t savedSampleBufferDropCount = videoSampleBufferDropCount;
if (savedSampleBufferDropCount && OSAtomicCompareAndSwap64(savedSampleBufferDropCount, 0, &videoSampleBufferDropCount)) {
NSLog(@"Dropped %lld video sample buffers!!!", savedSampleBufferDropCount);
}
// NSLog(@"%s", __func__);
@autoreleasepool {
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
CIImage * cameraImage = [CIImage imageWithCVImageBuffer:imageBuffer];
CIImage * faceImage = [self faceImage:cameraImage];
dispatch_sync(dispatch_get_main_queue(), ^ {
[_imageView setCIImage:faceImage];
});
}
[NSThread sleepForTimeInterval:0.5]; // Only want ~2 frames/sec.
}
希望这有帮助。