如何修改GLCameraRipple示例以在后台线程上处理?

时间:2012-02-03 12:56:38

标签: iphone ios opengl-es opengl-es-2.0

我正在尝试修改Apple的GLCameraRipple示例应用程序,以便在后台线程上处理视频帧。在此示例中,它使用以下代码处理主线程上的每个帧:

// Set dispatch to be on the main thread so OpenGL can do things with the data
[dataOutput setSampleBufferDelegate:self queue:dispatch_get_main_queue()];

如果我将此代码更改为在后台线程中处理:

dispatch_queue_t videoQueue = dispatch_queue_create("com.test.queue", NULL);
[dataOutput setSampleBufferDelegate:self queue:videoQueue];

然后程序崩溃。

当我尝试使用Apple的文档中指定的共享创建第二个EAGLContext时,我只看到绿色或黑色屏幕。

如何修改此示例应用程序以在后台线程上运行?

1 个答案:

答案 0 :(得分:2)

在我修改样本之后,这实际上非常有趣。这里的问题是CVOpenGLESTextureCacheCreateTextureFromImage()函数。如果你在获得绿色纹理时查看控制台,你会看到以下内容被记录:

  

CVOpenGLESTextureCacheCreateTextureFromImage -6661错误

-6661,根据标题(我目前唯一能找到关于这些新函数的文档),是kCVReturnInvalidArgument错误。这个函数的一个参数显然是错误的。

事实证明,CVImageBufferRef是问题所在。当处理此纹理缓存更新的块正在发生时,它看起来像是被解除分配或以其他方式更改。

我尝试了几种方法来解决这个问题,并最终使用调度队列和调度信号量,就像我在this answer中描述的那样,让代理仍然在主线程上回调,并在委托中执行类似的操作以下内容:

- (void)captureOutput:(AVCaptureOutput *)captureOutput 
didOutputSampleBuffer:(CMSampleBufferRef)sampleBuffer 
       fromConnection:(AVCaptureConnection *)connection
{
    if (dispatch_semaphore_wait(frameRenderingSemaphore, DISPATCH_TIME_NOW) != 0)
    {
        return;
    }

    CVImageBufferRef pixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer);
    CVPixelBufferLockBaseAddress(pixelBuffer, 0);
    CFRetain(pixelBuffer);

    dispatch_async(openGLESContextQueue, ^{
        [EAGLContext setCurrentContext:_context];

        // Rest of your processing

        CVPixelBufferUnlockBaseAddress(pixelBuffer, 0);
        CFRelease(pixelBuffer);

        dispatch_semaphore_signal(frameRenderingSemaphore);
    });
}

通过在主线程上创建CVImageBufferRef,锁定它指向的字节并保留它,然后将其交给异步块,这似乎可以解决这个错误。可以从here下载显示此修改的完整项目。

我应该在这里说一件事:这似乎没有给你带来任何好处。如果查看GLCameraRipple示例的设置方式,应用程序中最重的操作(涟漪效应的计算)已经分派到后台队列。这也是使用新的快速上传路径为OpenGL ES提供相机数据,因此在主线程上运行时不会成为瓶颈。

在我对双核iPhone 4S的仪器配置文件中,我发现此示例应用程序的库存版本与我在后台队列上运行帧上传的修改版本之间的渲染速度或CPU使用率没有显着差异。不过,这是一个有趣的诊断问题。