3

我的目标是编写一个自定义的相机视图控制器:

  1. 可以使用后置摄像头和前置摄像头(如果有的话)在所有四个界面方向上拍照。
  2. 正确旋转和缩放预览“视频”以及全分辨率照片。
  3. 允许将(简单)效果应用于预览“视频”和全分辨率照片。

实现(在 iOS 4.2 / Xcode 3.2.5 上):

由于要求(3),我需要下拉到 AVFoundation。

我从技术问答 QA1702开始并进行了以下更改:

  1. 将 sessionPreset 更改为 AVCaptureSessionPresetPhoto。
  2. 在开始会话之前添加了 AVCaptureStillImageOutput 作为附加输出。

我遇到的问题是处理预览图像(预览“视频”的帧)的性能。

首先,我imageFromSampleBuffer:captureOutput:didOutputSampleBuffer:fromConnection:. 然后,我使用 CGGraphicsContext 为屏幕缩放和旋转它。

此时,帧速率已经低于会话视频输出中指定的 15 FPS,当我添加效果时,它会下降到 10 以下或左右。由于内存不足,应用程序很快就会崩溃。

我在将 iPhone 4 的帧速率降低到 9 FPS 并将 iPod Touch(第 4 代)的帧速率降低到 8 FPS 方面取得了一些成功。

我还添加了一些代码来“刷新”调度队列,但我不确定它实际上有多大帮助。基本上,每 8-10 帧,就会设置一个标志,指示captureOutput:didOutputSampleBuffer:fromConnection:立即返回而不是处理该帧。在输出调度队列上的同步操作完成后,该标志被重置。

在这一点上,我什至不介意低帧率,但显然我们不能在低内存崩溃的情况下发货。任何人都知道在这种情况下如何采取措施来防止内存不足的情况(和/或“刷新”调度队列的更好方法)?

4

2 回答 2

4

为了防止内存问题,只需在captureOutput:didOutputSampleBuffer:fromConnection:.

这是有道理的,因为imageFromSampleBuffer:返回一个自动释放的 UIImage 对象。此外,它会立即释放由图像处理代码创建的任何自动释放对象。

// Delegate routine that is called when a sample buffer was written
- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
fromConnection:(AVCaptureConnection *)connection
{ 
    NSAutoreleasePool *pool = [[NSAutoreleasePool alloc] init];

    // Create a UIImage from the sample buffer data
    UIImage *image = [self imageFromSampleBuffer:sampleBuffer];

    < Add your code here that uses the image >

    [pool release];
}

我的测试表明,即使请求的 FPS 非常高(例如 60)并且图像处理速度非常慢(例如 0.5+ 秒),它也会在 iPhone 4 或 iPod Touch(第 4 代)上运行而不会出现内存警告。

旧解决方案:

正如 Brad 指出的那样,Apple 建议在后台线程上进行图像处理,以免干扰 UI 响应。在这种情况下我没有注意到太多的滞后,但最佳实践就是最佳实践,因此请使用上述带有自动释放池的解决方案,而不是在主调度队列/主线程上运行它。

为了防止内存问题,只需使用主调度队列而不是创建一个新队列。

captureOutput:didOutputSampleBuffer:fromConnection:这也意味着当您想要更新 UI 时,您不必切换到主线程。

setupCaptureSession中,从:

// Configure your output.
dispatch_queue_t queue = dispatch_queue_create("myQueue", NULL);
[output setSampleBufferDelegate:self queue:queue];
dispatch_release(queue);

到:

// we want our dispatch to be on the main thread
[output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];
于 2011-02-04T21:47:17.537 回答
2

一个从根本上更好的方法是使用 OpenGL 为您处理与图像相关的繁重工作(正如我看到您在最近的尝试中所做的那样)。但是,即使这样,您也可能在构建要处理的帧时遇到问题。

虽然在处理帧时您会遇到内存积累似乎很奇怪(根据我的经验,如果您不能足够快地处理它们,您就会停止获取它们),但如果 Grand Central Dispatch 队列正在等待,它们可能会堵塞输入/输出。

也许调度信号量可以让您限制将新项目添加到处理队列中。有关这方面的更多信息,我强烈推荐 Mike Ash 的“ GCD Practicum ”文章,其中他着眼于使用调度信号量优化 I/O 绑定的缩略图处理操作。

于 2011-02-04T04:40:37.763 回答