h4ck1nH4ck1n  2022-05-19 20:51 字节时代 隐藏边栏 |   抢沙发  23 
文章评分 1 次,平均分 5.0

iPhone 的 CPU 对于处理视频来说能力是非常有限的,如果要进行视频处理,比如滤镜、美颜等,都会用到设备的 GPU 能力,也就是会用到 openGL ES 的 api。

CPU 和 GPU 之间的数据传递效率十分低下,尤其是从 GPU 回传数据到 CPU,更是缓慢。比如使用 glReadPixels 从 GPU 读取数据这种模式,想要做到实时很难。

那么,在 iOS 中想要提高 GPU 和 CPU 数据传递效率,就需要使用到共享内存,避免数据拷贝。

根据文档可以知道包含 kCVPixelBufferIOSurfacePropertiesKey 属性的 CVPixelBufferRef 对象的内存是共享的。比如,从 iOS Camera 采集出来和从 VideoToolBox 硬解出来的 buffer 都具有这个属性,也就是这些 buffer 可以在 CPU 和 GPU 之间共享。

我们可以自己建立符合这个条件的 buffer,创建方式如下:

// 创建 kCVPixelBufferIOSurfacePropertiesKey 属性

CFDictionaryRef empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFMutableDictionaryRef attrs = CFDictionaryCreateMutable(kCFAllocatorDefault, 1, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks);
CFDictionarySetValue(attrs, kCVPixelBufferIOSurfacePropertiesKey, empty);

// 创建具有 kCVPixelBufferIOSurfacePropertiesKey 属性的 CVPixelBufferRef 实例 renderTarget

CVPixelBufferRef renderTarget;
CVReturn err = CVPixelBufferCreate(kCFAllocatorDefault, (int)_size.width, (int)_size.height, kCVPixelFormatType_32BGRA, attrs, &renderTarget);
...

这样我们就获得了自定义的具有共享内存性质的 CVPixelBufferRef renderTarget。

在 Camera 采集 buffer,硬解 VideoToolBox buffer 和我们自定义的具有共享内存性质的 CVPixelBufferRef renderTarget 基础上,可以通过以下接口创建具有共享内存的 texture,将 CVPixelBufferRef 与 Texture 关联起来,一方的变化将引起另一方的变化:

CVOpenGLESTextureRef renderTexture;

CVOpenGLESTextureCacheCreateTextureFromImage (
        kCFAllocatorDefault,
        textureCache,
        renderTarget,
        NULL, // texture attributes
        GL_TEXTURE_2D,
        GL_RGBA, // opengl format
        640,
        480,
        GL_BGRA, // native iOS format
        GL_UNSIGNED_BYTE,
        0,
        &renderTexture);

这样我们就可以在 openGL 中使用我们创建的具有共享内存的 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);
glBindFramebuffer(GL_FRAMEBUFFER, renderFrameBuffer);
glFramebufferTexture2D(GL_FRAMEBUFFER, GL_COLOR_ATTACHMENT0, GL_TEXTURE_2D, CVOpenGLESTextureGetName(renderTexture), 0);

这样如果在 openGL 中 texture 发生了变化,那么对应的 CVPixelBufferRef 也会发生变化。

如果要取回内容,可用下面方式:

if (kCVReturnSuccess == CVPixelBufferLockBaseAddress(renderTarget, kCVPixelBufferLock_ReadOnly)) {
    uint8_t* pixels = (uint8_t*)CVPixelBufferGetBaseAddress(renderTarget);
    CVPixelBufferUnlockBaseAddress(renderTarget, kCVPixelBufferLock_ReadOnly);
}

通过上述方法实现了 CPU 和 GPU 数据直接共享,避免数据拷贝,上述接口需要在 iOS 5 及以上系统才可用。

参考链接

本文为原创文章,版权归所有,欢迎分享本文,转载请保留出处!

h4ck1n
H4ck1n 关注:0    粉丝:0
这个人很懒,什么都没写

发表评论

表情 格式 链接 私密 签到

扫一扫二维码分享