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

“将效果应用于iPhone相机预览”;“视频”;,iphone,grand-central-dispatch,avcapturesession,Iphone,Grand Central Dispatch,Avcapturesession,我的目标是编写一个自定义摄影机视图控制器,该控制器: 可以在所有四个界面方向上使用背面和(如果可用)正面相机拍照 正确旋转和缩放预览“视频”以及全分辨率照片 允许对预览“视频”和全分辨率照片应用(简单)效果 实施(在iOS 4.2/Xcode 3.2.5上): 由于要求(3),我需要转到AVFoundation 我从开始做了以下更改: 将sessionPreset更改为AVCaptureSessionPresetPhoto 在启动会话之前,添加了AVCaptureStillImageOutput

我的目标是编写一个自定义摄影机视图控制器,该控制器:

  • 可以在所有四个界面方向上使用背面和(如果可用)正面相机拍照
  • 正确旋转和缩放预览“视频”以及全分辨率照片
  • 允许对预览“视频”和全分辨率照片应用(简单)效果
  • 实施(在iOS 4.2/Xcode 3.2.5上):

    由于要求(3),我需要转到AVFoundation

    我从开始做了以下更改:

  • 将sessionPreset更改为AVCaptureSessionPresetPhoto
  • 在启动会话之前,添加了AVCaptureStillImageOutput作为附加输出
  • 我遇到的问题是处理预览图像(预览“视频”的一帧)的性能

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

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

    在iPhone4和iPodtouch(第四代)上,我成功地将帧速率分别降低到9 FPS和8 FPS

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


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

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

    虽然在处理帧时会遇到内存累积的情况似乎很奇怪(根据我的经验,如果处理速度不够快,就停止获取帧),但如果Grand Central调度队列正在等待I/O,它们可能会被阻塞


    也许调度信号可以让您限制向处理队列中添加新项。关于这方面的更多信息,我强烈推荐Mike Ash的“”文章,在这篇文章中,他研究了如何使用调度信号优化I/O绑定的缩略图处理操作

    为了防止内存问题,只需在
    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];
    }
    
    致:


    OpenGL提供了很好的帧速率,但这不是正确的预设(尽管照片预设视频帧的分辨率仅略高于示例)。是的,很奇怪,但很容易复制。您可以抓取苹果的示例代码,将其放入新的iPhone应用程序中,在
    captureOutput:didOutputSampleBuffer:fromConnection:
    中添加一个短延迟,然后在几秒钟内看到它崩溃。这与我尚未探讨的OpenGL示例之间的一个区别是后者使用了主调度队列(主线程),这可能最终类似于信号量方法。我将研究这两个问题。结果证明,这和使用主调度队列一样简单(参见我的答案)。到目前为止,最好的解决方案是添加自动释放池。我已经更新了我的答案。是的,我猜这会导致会话在事情跟不上的时候丢弃帧。然而,我认为苹果不鼓励这种做法,因为它会对界面的响应性产生影响(因为你在主线程上进行的处理)。似乎仍然有一种方法可以在不过载的情况下处理非主队列上的帧。到目前为止,它的响应速度已经足够快了,但是我可能会尝试在
    captureOutput:didOutputSampleBuffer:fromConnection:
    之后在后台启动实际处理,然后返回主线程更新UI。这种方法确实解决了这个问题,而这正是苹果WWDC的GLVideoFrame示例所做的(当然,OpenGL处理速度非常快)。在后台进行处理将帧速率降低到了不可接受的水平,并使应用程序的响应性大大降低,即使UI的响应性稍高。在这种情况下,在主线程上执行所有操作似乎是最好的方法。实际上,仅添加自动释放似乎已经解决了内存问题,同时允许处理保留在后台队列/线程上。我已经更新了答案。@gerry3-您当时是在控制台上看到关于丢失自动释放池的错误,还是需要更频繁地排空池?
    // 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()];