使用GPU清除CVPixelBufferRef

5

我正在使用AVComposition编写一些视频处理代码。仅提供必要的背景细节,我从一个我无法控制的苹果API中接收到一个CVPixelBuffer。这个CVPixel buffer包含了以前渲染过的视频帧,因为它们显然是由我无法控制的苹果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个回答

6
您可以使用 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.") }

在Playground中,通过10000次迭代测试使用普通的memset填充小缓冲区以产生全0数据的速度更快。在发布版中,使用真实数据结果可能不会有太大差异。

  • 加速:1629.0
  • memset:314.0

输入图像描述


1
根据您的回答,我已经使用了memset_pattern16,非常好用,谢谢!还有一件事要补充,对于那些想要将CIImage渲染到CVPixelBuffer中的人来说:在渲染之前进行清零操作,而不是在渲染完成并处理完pixelBuffer后再进行清零。我的AVAssetWriter与AVAssetWrinterInputPixelBufferAdaptor可能会以延迟的方式使用我的缓冲区,导致我的图像在视频中出现部分黑色。 - iSpain17

4
假设您的像素缓冲区附加到由IO表面支持的图像缓冲区(链接1),则可以使用CVOpenGLESTextureCacheCreateTextureFromImage获取CVOpenGLESTextureCache以提供CVOpenGLESTextureRef。这将能够出售纹理目标和名称,以便您可以在OpenGL中绑定该项目。

在OpenGL中,你可以使用帧缓冲对象来渲染纹理。完成后,你可以使用glClear来清除它。调用glFinish创建与GL流水线的同步点,并在下次从CPU检查它时,你的内存应该被清除。


通过一些苹果的示例代码,我成功地使其运行起来了。不过,这个过程要复杂得多,因为我实际上需要进行一系列的OpenGL设置,而说实话,我并不理解其中的细节。 - wfbarksdale
也许这里有一个更好的问题,相比使用memset将像素缓冲区清零,这是否会真正提供任何性能改进? - wfbarksdale
1
我的直觉是你最好的希望是并行化——为缓冲区B发出glClear,然后在缓冲区A上进行CPU工作,只有当CPU想要转到缓冲区B时才调用glFinish。但如果你还没有多线程,通过GCD做同样的事情可能同样有效。GPU并不会天生地能够更有效地执行memset——你将受到内存带宽的限制。 - Tommy

3

我有一个类似的问题(需要强制一个由-[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

这个页面看来,您可以通过以下方式直接将CVImageBufferRef作为OpenGL纹理进行访问:

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

一旦您将其作为纹理使用,您可以将其用作FBO的绘制缓冲区,并简单地调用glClear(GL_COLOR_BUFFER_BIT);


似乎没有成功使用以下代码: glBindTexture(CVOpenGLESTextureGetTarget(dstPixels),CVOpenGLESTextureGetName(dstPixels)); glClear(GL_COLOR_BUFFER_BIT); glFinish(); - wfbarksdale
1
在清除之前,您仍需要将其附加到帧缓冲区才能起作用。但是,如果您使用的是iOS而不是MacOS,则可能需要使用下面Tommy的解决方案。 - user1118321
我越想越觉得,仅仅使用memset将像素缓冲区清零可能与使用glClear同样高效。你对此有何想法? - wfbarksdale

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接