ati*_*man 66 iphone opengl-es ios opengl-es-2.0
有没有比使用glReadPixels更快的方式来访问帧缓冲区?我需要对帧缓冲区中的小矩形渲染区域进行只读访问,以便在CPU中进一步处理数据.性能很重要因为我必须重复执行此操作.我在网上搜索并发现了一些方法,比如使用像素缓冲区对象和glMapBuffer,但似乎OpenGL ES 2.0不支持它们.
Bra*_*son 132
从iOS 5.0开始,现在有更快的方法从OpenGL ES中获取数据.这是不是很明显,但事实证明,在安装iOS 5.0加入了纹理缓存的支持并不只是摄像头帧的OpenGL ES的快速上传工作,但它可以反过来用于快速访问原始像素在OpenGL ES纹理中.
您可以利用此功能通过使用带有附加纹理的帧缓冲对象(FBO)来获取OpenGL ES渲染的像素,该纹理已从纹理缓存中提供.将场景渲染到FBO后,该场景的BGRA像素将包含在CVPixelBufferRef中,因此无需使用它们glReadPixels().
这比glReadPixels()在我的基准测试中使用要快得多.我发现在我的iPhone 4上,glReadPixels()是读取720p视频帧以编码到磁盘的瓶颈.它将编码限制在超过8-9 FPS的范围内.与快速纹理缓存更换这种读取允许我720p视频在20 FPS编码,现在,瓶颈已经从像素读取到的OpenGL ES处理和管道的实际电影编码部分移动.在iPhone 4S上,您可以以30 FPS的速度写入1080p视频.
我的实现可以在我的开源GPUImage框架中的GPUImageMovieWriter类中找到,但它的灵感来自Dennis Muhlestein关于该主题的文章和Apple的ChromaKey示例应用程序(仅在WWDC 2011上提供).
我首先配置我的AVAssetWriter,添加输入,并配置像素缓冲输入.以下代码用于设置像素缓冲区输入:
NSDictionary *sourcePixelBufferAttributesDictionary = [NSDictionary dictionaryWithObjectsAndKeys: [NSNumber numberWithInt:kCVPixelFormatType_32BGRA], kCVPixelBufferPixelFormatTypeKey,
[NSNumber numberWithInt:videoSize.width], kCVPixelBufferWidthKey,
[NSNumber numberWithInt:videoSize.height], kCVPixelBufferHeightKey,
nil];
assetWriterPixelBufferInput = [AVAssetWriterInputPixelBufferAdaptor assetWriterInputPixelBufferAdaptorWithAssetWriterInput:assetWriterVideoInput sourcePixelBufferAttributes:sourcePixelBufferAttributesDictionary];
Run Code Online (Sandbox Code Playgroud)
有了这个,我使用以下代码配置我将要渲染视频帧的FBO:
if ([GPUImageOpenGLESContext supportsFastTextureUpload])
{
CVReturn err = CVOpenGLESTextureCacheCreate(kCFAllocatorDefault, NULL, (__bridge void *)[[GPUImageOpenGLESContext sharedImageProcessingOpenGLESContext] context], NULL, &coreVideoTextureCache);
if (err)
{
NSAssert(NO, @"Error at CVOpenGLESTextureCacheCreate %d");
}
CVPixelBufferPoolCreatePixelBuffer (NULL, [assetWriterPixelBufferInput pixelBufferPool], &renderTarget);
CVOpenGLESTextureRef renderTexture;
CVOpenGLESTextureCacheCreateTextureFromImage (kCFAllocatorDefault, coreVideoTextureCache, renderTarget,
NULL, // texture attributes
GL_TEXTURE_2D,
GL_RGBA, // opengl format
(int)videoSize.width,
(int)videoSize.height,
GL_BGRA, // native iOS format
GL_UNSIGNED_BYTE,
0,
&renderTexture);
glBindTexture(CVOpenGLESTextureGetTarget(renderTexture), CVOpenGLESTextureGetName(renderTexture));
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture), 0);
}
Run Code Online (Sandbox Code Playgroud)
这将从与我的资产编写器输入关联的池中提取像素缓冲区,创建纹理并将其与其关联,并将该纹理用作我的FBO的目标.
一旦我渲染了一个帧,我就锁定了像素缓冲区的基地址:
CVPixelBufferLockBaseAddress(pixel_buffer, 0);
Run Code Online (Sandbox Code Playgroud)
然后简单地将其提供给我的资产编写器进行编码:
CMTime currentTime = CMTimeMakeWithSeconds([[NSDate date] timeIntervalSinceDate:startTime],120);
if(![assetWriterPixelBufferInput appendPixelBuffer:pixel_buffer withPresentationTime:currentTime])
{
NSLog(@"Problem appending pixel buffer at time: %lld", currentTime.value);
}
else
{
// NSLog(@"Recorded pixel buffer at time: %lld", currentTime.value);
}
CVPixelBufferUnlockBaseAddress(pixel_buffer, 0);
if (![GPUImageOpenGLESContext supportsFastTextureUpload])
{
CVPixelBufferRelease(pixel_buffer);
}
Run Code Online (Sandbox Code Playgroud)
请注意,这里我没有手动阅读任何内容.此外,纹理本身采用BGRA格式,这是AVAssetWriters在编码视频时优化使用的,因此不需要在这里进行任何颜色调整.原始BGRA像素仅被馈入编码器以制作电影.
除了在AVAssetWriter中使用它之外,我在这个答案中有一些代码用于原始像素提取.与使用相比,它在实践中也经历了显着的加速glReadPixels(),尽管比我在AVAssetWriter中使用的像素缓冲池要少.
遗憾的是,这一切都没有记录在任何地方,因为它为视频捕获性能提供了巨大的推动力.
| 归档时间: |
|
| 查看次数: |
34408 次 |
| 最近记录: |