iOS技术交流收藏视频iOS Developer

Metal Camera开发4:渲染到CVPixelBuffer

2017-07-06  本文已影响987人  熊皮皮

Metal Camera开发1:读取渲染结果生成UIImage第5节:读取Metal渲染结果并生成UIImage介绍了读取屏幕(MTLTexture的数据)并由Core Graphics创建UIImage,类似于OpenGL ES,这一过程按我理解是存在多余的内存拷贝操作(GPU管理的内存块拷贝到CPU管理的内存块)。为提高性能与简化视频编码步骤,本文档介绍Metal渲染到CVPixelBuffer的实现。

文档结构:

  1. OpenGL ES渲染到CVPixelBuffer实现代码
  2. Metal渲染到CVPixelBuffer实现代码
  3. 参考资料
  4. 致谢
渲染到CVPixelBuffer

1. OpenGL ES渲染到CVPixelBuffer实现代码

1.1. 创建GL_TEXTURE_2D及glFramebufferTexture2D

GPUImage使用了此方式,基于我之前的代码测试可正常运行。最关键的地方是创建CVPixelBuffer需指定kCVPixelBufferIOSurfacePropertiesKey属性为空字典。基于OpenGL ES 3.0的实现代码如下所示。

CVReturn status = CVOpenGLESTextureCacheCreate(
            NULL, 
            NULL, 
            context, 
            NULL, 
            &textureCache);

NSDictionary *pixelBufferAttributes = @{(__bridge NSString*)kCVPixelBufferIOSurfacePropertiesKey: @{}};
status = CVPixelBufferCreate(
            NULL, 
            renderbufferWidth, 
            renderbufferHeight, 
            kCVPixelFormatType_32BGRA, 
            (__bridge CFDictionaryRef)pixelBufferAttributes, 
            &offlinePixelBuffer);

status = CVOpenGLESTextureCacheCreateTextureFromImage(
                                                      NULL,
                                                      textureCache,
                                                      offlinePixelBuffer,
                                                      NULL, // texture attributes
                                                      GL_TEXTURE_2D,
                                                      GL_RGBA, // OpenGL format
                                                      renderbufferWidth,
                                                      renderbufferHeight,
                                                      GL_BGRA, // native iOS format 
                                                      GL_UNSIGNED_BYTE,
                                                      0,
                                                      &offlineTexture);
glFramebufferTexture2D(
            GL_FRAMEBUFFER, 
            GL_COLOR_ATTACHMENT0, 
            GL_TEXTURE_2D, 
            CVOpenGLESTextureGetName(offlineTexture), 
            0);

有趣的是,format参数设置为GL_RGBA也可以正常创建CVOpenGLESTextureRef对象。无论format参数设置为什么值,使用时都得通过glFramebufferTexture2D将offlineTexture绑定到帧缓冲区。

1.2. 创建GL_RENDERBUFFER及glFramebufferRenderbuffer

这里尝试了使用CVOpenGLESTextureCacheCreateTextureFromImage创建成GL_RENDERBUFFER,配合glFramebufferRenderbuffer可正常工作。创建成GL_RENDERBUFFER必须将format参数设置为GL_BGRA,否则创建失败并报错Failed to create IOSurface image (texture)。另外,官方给的internalFormat为GL_RGBA8,这是OpenGL ES 1.0的宏定义,基于OpenGL ES 3.0开发时,换成GL_RGBA即可。参考代码如下。

status = CVOpenGLESTextureCacheCreateTextureFromImage(
                                                      NULL,
                                                      textureCache,
                                                      offlinePixelBuffer,
                                                      NULL, // texture attributes
                                                      GL_RENDERBUFFER,
                                                      GL_RGBA8, // OpenGL format
                                                      renderbufferWidth,
                                                      renderbufferHeight,
                                                      GL_BGRA, // native iOS format 
                                                      GL_UNSIGNED_BYTE,
                                                      0,
                                                      &offlineTexture);

glFramebufferRenderbuffer(
            GL_FRAMEBUFFER, 
            GL_COLOR_ATTACHMENT0, 
            GL_RENDERBUFFER, 
            CVOpenGLESTextureGetName(offlineTexture));

1.3. 关于GL_TEXTURE_2D与GL_RENDERBUFFER

关于应该使用GL_TEXTURE_2D还是GL_RENDERBUFFER,@红猪和我讨论了一段时间。建议直接参考官方文档Drawing to Other Rendering Destinations

2. Metal渲染到CVPixelBuffer实现代码

参考OpenGL ES渲染到CVPixelBuffer的实现,很容易模仿出Metal版本,如下代码已通过验证。

CVMetalTextureCacheCreate(
            kCFAllocatorDefault, 
            nil, 
            device!, 
            nil, 
            &textureCache)
let pixelBufferAttri = [kCVPixelBufferIOSurfacePropertiesKey as NSObject: [:] as CFDictionary] as CFDictionary
let s = CVPixelBufferCreate(
            kCFAllocatorDefault, 
            1080, 
            1920, 
            kCVPixelFormatType_32BGRA, 
            pixelBufferAttri, 
            &offlinePixelBuffer)

let result = CVMetalTextureCacheCreateTextureFromImage(
    kCFAllocatorDefault,
    textureCache!,
    offlinePixelBuffer!,
    nil,
    self.colorPixelFormat,
    1080,
    1920,
    0,
    &offlineTexture)

3. 参考资料

  1. Rendering to a texture with iOS 5 texture cache api. GPUImage在源码中标明了它参考此链接,目前来看,所有的实现都源自于此链接,似乎是因为WWDC Session 419主讲人没分享代码给大家,而此博主写了一份可行的示例代码,所以大家都用他的代码的。
  2. Capturing from the Camera using AV Foundation on iOS 5 苹果官方讲座描述了OpenGL ES渲染到CVPixelBuffer的细节。

4. 致谢

感谢@红猪和我一起讨论OpenGL ES渲染到CVPixelBuffer细节。

上一篇下一篇

猜你喜欢

热点阅读