4

我正在使用 AV Foundation 处理来自摄像机的帧(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 帧持续时间?如何?

谢谢。

4

3 回答 3

4

虽然有两个AVCaptureConnections 是正确的,但这并不意味着它们可以独立设置最小和最大帧持续时间。这是因为它们共享相同的物理硬件

如果连接#1 以(比如说)5 帧/秒的速率激活卷帘快门,帧持续时间为 1/5 秒,则连接#2不可能同时以 30 次/秒的速度激活快门。持续时间 1/30 秒。

要获得您想要的效果,需要两台相机!

接近你想要的唯一方法是遵循 Kaelin Colclasure 在 3 月 22 日的回答中概述的方法。

但是,您确实可以选择在该方法中变得更复杂一些。例如,您可以使用计数器来决定丢弃哪些帧,而不是让线程休眠。您可以使该计数器响应通过的实际帧速率(您可以从captureOutput:didOutputSampleBuffer:fromConnection:与图像数据一起进入代理的元数据中获取,或者您可以通过手动计时帧来计算自己)。你甚至可以通过合成帧而不是丢弃它们来非常合理地模拟更长的曝光——就像 App Store 中的许多“慢快门”应用程序所做的那样(撇开细节——例如不同的滚动快门伪影——没有真正以 1/5 秒扫描的一帧与以 1/25 秒扫描然后粘合在一起的五帧之间的差异很大)。

是的,这有点工作,但您正试图让一台摄像机像两台摄像机一样实时运行——这绝非易事。

于 2013-03-29T10:58:54.953 回答
2

可以这样想:您要求捕获设备限制帧持续时间,以便获得更好的曝光。美好的。您想以更高的帧速率进行预览。如果您要以更高的速率进行预览,那么捕获设备(相机)将没有足够的时间来曝光帧,因此您可以在捕获的帧处获得更好的曝光。这就像要求在预览中查看与捕获的帧不同的帧。

我认为,如果可能的话,这也会是一种负面的用户体验。

于 2013-03-28T13:00:14.403 回答
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.
}

希望这可以帮助。

于 2013-03-22T15:55:37.570 回答