将效果应用于iPhone相机预览“视频”

时间:2011-02-04 01:18:32

标签: iphone grand-central-dispatch avcapturesession

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

  1. 可以使用背面和前置摄像头拍摄所有四种界面方向的照片。
  2. 正确旋转并缩放预览“视频”以及全分辨率照片。
  3. 允许(简单)效果同时应用于预览“视频”和全分辨率照片。
  4. 实施(在iOS 4.2 / Xcode 3.2.5上):

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

    我从Technical Q&A QA1702开始并进行了这些更改:

    1. 将sessionPreset更改为AVCaptureSessionPresetPhoto。
    2. 在开始会话之前添加了AVCaptureStillImageOutput作为附加输出。
    3. 我遇到的问题是处理预览图像的效果(预览“视频”的一帧)。

      首先,我从imageFromSampleBuffer:获取样本缓冲区的captureOutput:didOutputSampleBuffer:fromConnection:的UIImage结果。然后,我使用CGGraphicsContext对屏幕进行缩放和旋转。

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

      我已经取得了一些成功,将iPhone 4的帧速率降至9 FPS,iPod Touch(第4代)的帧速率降至8 FPS。

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

      此时我甚至不介意低帧速率,但显然我们无法提供低内存崩溃。任何人都知道如何采取措施来防止这种情况下的内存不足(和/或更好地“刷新”调度队列)?

2 个答案:

答案 0 :(得分: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代)上没有内存警告的情况下运行。< / p>

OLD SOLUTION:

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

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

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

setupCaptureSession中,更改FROM:

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

TO:

// we want our dispatch to be on the main thread
[output setSampleBufferDelegate:self queue:dispatch_get_main_queue()];

答案 1 :(得分:2)

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

虽然在处理帧时遇到内存累积似乎很奇怪(根据我的经验,如果你不能足够快地处理它们就停止获取它们),Grand Central Dispatch队列可能会因为它们而被堵塞等待I / O.

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