iphone的cpu对于处理视频来说能力是非常有限的,所以在ios开发中,如果要进行视频处理,比如滤镜、美颜等,都会用到设备的GPU能力,也就是会用到opengl es的api,而CPU和GPU之间的数据传递效率十分低下,尤其是从GPU回传数据到CPU,更是缓慢。如用glReadPixels从GPU读取数据,如果用这种模式,想要做到实时很难。鉴于此,今天主要介绍一下ios中GPU和CPU的共享内存机制,从而避免数据的拷贝。
满足某种条件的CVPixelBufferRef本身就是共享内存,这个条件就CVPixelBufferRef具有kCVPixelBufferIOSurfacePropertiesKey属性,从ios camera采集出来和从videoToolBox硬解出来的buffer是具有这个属性,也就是这些buffer可以在CPU和GPU之间共享。我们也可以自己建立符合这个条件的buffer,创建方式如下:
//kCVPixelBufferIOSurfacePropertiesKey属性创建
CFDictionaryRef empty; // empty value for attr value.
CFMutableDictionaryRef attrs;
empty = CFDictionaryCreate(kCFAllocatorDefault, NULL, NULL, 0, &kCFTypeDictionaryKeyCallBacks, &kCFTypeDictionaryValueCallBacks); // our empty IOSurface properties dictionary
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及以上。参考连接:
http://allmybrain.com/2011/12/08/rendering-to-a-texture-with-ios-5-texture-cache-api/
https://github.com/BradLarson/GPUImage