使用GPU清除CVPixelBufferRef

时间:2014-11-17 05:08:50

标签: ios avfoundation core-video core-media

我正在使用AVComposition编写一些视频处理代码。仅提供必要的背景详细信息,我从苹果API收到CVPixelBuffer,我无法控制。这个CVPixel缓冲区包含一个以前渲染的视频帧,因为它们显然是由我无法控制的Apple API回收的。所以我的目标是将CVPixelBufferRef中的所有像素设置为[0,0,0,0](在RGBA颜色空间中)。我可以通过这个函数在CPU上执行此操作:

- (void)processPixelBuffer: (CVImageBufferRef)pixelBuffer
{
    CVPixelBufferLockBaseAddress( pixelBuffer, 0 );

    int bufferWidth = CVPixelBufferGetWidth(pixelBuffer);
    int bufferHeight = CVPixelBufferGetHeight(pixelBuffer);
    unsigned char *pixel = (unsigned char *)CVPixelBufferGetBaseAddress(pixelBuffer);

    for( int row = 0; row < bufferHeight; row++ ) {
        for( int column = 0; column < bufferWidth; column++ ) {
            pixel[0] = 0;
            pixel[1] = 0;
            pixel[2] = 0;
            pixel[3] = 0;
            pixel += 4;
        }
    }
    CVPixelBufferUnlockBaseAddress( pixelBuffer, 0 );
}

有什么办法可以用GPU完成同样的事情吗?另外,是否可以通过CoreImage执行此操作?因为我不知道openGL,所以设置起来相当复杂。

4 个答案:

答案 0 :(得分:2)

假设您的像素缓冲区已附加到an image buffer backed by an IO surface,您可以使用CVOpenGLESTextureCacheCreateTextureFromImageCVOpenGLESTextureCache为您提供CVOpenGLESTextureRef。这将能够提供纹理目标和名称,以便您可以在OpenGL中绑定事物。

在OpenGL中,您可以use a framebuffer object to render to a texture。完成后,您可以使用glClear来清除事物。调用glFinish与GL管道创建同步点,当您下次从CPU检查时,应清除内存。

答案 1 :(得分:2)

我遇到同样的问题(强制-[AVVideoCompositionRenderContext newPixelBuffer]提供的循环缓冲区为黑色)。

如果您使用Core Image进行繁重的工作怎么办?像这样:

EAGLContext *eaglContext = [[EAGLContext alloc] initWithAPI:kEAGLRenderingAPIOpenGLES2];
CIContext *ciContext = [CIContext contextWithEAGLContext:eaglContext];

CIImage *image = [CIImage imageWithColor:[CIColor colorWithRed:0 green:0 blue:0]];
CVPixelBufferRef destination = [request.renderContext newPixelBuffer];
[ciContext render:image toCVPixelBuffer:destination];
[request finishWithComposedVideoFrame:destination];
CVBufferRelease(destination);

你必须测试性能以了解它是否比memset更好,但这比我试图仅仅为了设置几个位而试图进入OpenGL API更好。

答案 2 :(得分:2)

你可以使用Accelerate vImageBufferFill来做到这一点(尽管不是GPU)。这在Swift 4中用黑色填充BGRA:

let width: Int = 3
let height: Int = 3

var pixelBuffer: CVPixelBuffer?
var imageBuffer: vImage_Buffer
var date: Date
let iterations: Int = 10000

let pixelBufferAttributes: [CFString: Any] = [kCVPixelBufferCGImageCompatibilityKey: true, kCVPixelBufferCGBitmapContextCompatibilityKey: true]
if CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_32BGRA, pixelBufferAttributes as CFDictionary, &pixelBuffer) != kCVReturnSuccess || pixelBuffer == nil { fatalError("Cannot create pixel buffer.") }
if CVPixelBufferLockBaseAddress(pixelBuffer!, []) != kCVReturnSuccess { fatalError("Cannot lock pixel buffer base address.") }
imageBuffer = vImage_Buffer(data: CVPixelBufferGetBaseAddress(pixelBuffer!), height: UInt(height), width: UInt(width), rowBytes: CVPixelBufferGetBytesPerRow(pixelBuffer!))
vImageBufferFill_ARGB8888(&imageBuffer, [0, 0, 0, 0xFF], UInt32(kvImageNoFlags))
if CVPixelBufferUnlockBaseAddress(pixelBuffer!, []) != kCVReturnSuccess { fatalError("Cannot unlock pixel buffer base address.") }

测试普通memset以在10000次迭代中用零填充小缓冲区在游乐场中更快。在发布版本中,实际数据结果可能没那么大差别。

  • 加速: 1629.0
  • memset: 314.0

enter image description here

答案 3 :(得分:1)

this page看起来您可以通过以下方式直接访问CVImageBufferRef作为OpenGL纹理:

glBindTexture( CVOpenGLTextureGetTarget( image ), CVOpenGLTextureGetName( image ) );

将纹理作为纹理后,可以将其用作FBO的绘图缓冲区,只需在其上调用glClear(GL_COLOR_BUFFER_BIT);即可。