将 MTLTexture 转换为 CVPixelBuffer

问题描述 投票:0回答:3

我目前正在使用 Metal 开发实时滤波器。 定义 CIImage 后,我将图像渲染到 MTLTexture。

下面是我的渲染代码。

context
是由 Metal 支持的 CIContext;
targetTexture
是附加到我的 MTKView 实例的
currentDrawable
属性的纹理的别名:

context?.render(drawImage, to: targetTexture, commandBuffer: commandBuffer, bounds: targetRect, colorSpace: colorSpace)

它渲染正确,因为我可以看到金属视图上显示的图像。

问题是在渲染图像(并显示它)之后,我想提取 CVPixelBuffer 并使用 AVAssetWriter 类将其保存到磁盘。

另一种选择是有两个渲染步骤,一个渲染到纹理,另一个渲染到 CVPixelBuffer。 (但尚不清楚如何创建这样的缓冲区,或者两个渲染步骤对帧速率的影响)

任何帮助将不胜感激,谢谢!

ios swift avfoundation core-image metal
3个回答
4
投票

您可以尝试像这样从 MTLTexture 复制原始数据:

var outPixelbuffer: CVPixelBuffer?
if let datas = targetTexture.texture.buffer?.contents() {
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault, targetTexture.width, 
    targetTexture.height, kCVPixelFormatType_64RGBAHalf, datas, 
    targetTexture.texture.bufferBytesPerRow, nil, nil, nil, &outPixelbuffer);
}

0
投票

最上面的答案不适用于未使用金属缓冲区创建的纹理。在这种情况下,

texture.buffer
nil
。就我而言,我的流程为
CVPixelBuffer
->
MTLTexture
-> 处理纹理 ->
CVPixelBuffer

我在这里找到的工作片段:https://liveupdate.tistory.com/445

但不确定性能以及是否有更好的方法。

func makePixelBuffer(from texture: MTLTexture, width _: Int, height _: Int) -> CVPixelBuffer? {
    var pixelBuffer: CVPixelBuffer?

    CVPixelBufferCreate(
        kCFAllocatorDefault,
        texture.width,
        texture.height,
        kCVPixelFormatType_32BGRA,
        nil,
        &pixelBuffer
    )

    guard let pixelBuffer else {
        return nil
    }

    CVPixelBufferLockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

    guard let pixelBufferBytes = CVPixelBufferGetBaseAddress(pixelBuffer) else {
        return nil
    }
    let bytesPerRow = CVPixelBufferGetBytesPerRow(pixelBuffer)
    let region = MTLRegionMake2D(0, 0, texture.width, texture.height)

    texture.getBytes(pixelBufferBytes, bytesPerRow: bytesPerRow, from: region, mipmapLevel: 0)
    CVPixelBufferUnlockBaseAddress(pixelBuffer, CVPixelBufferLockFlags(rawValue: 0))

    return pixelBuffer
}

-1
投票
+ (void)getPixelBufferFromBGRAMTLTexture:(id<MTLTexture>)texture result:(void(^)(CVPixelBufferRef pixelBuffer))block {
    
    CVPixelBufferRef pxbuffer = NULL;
    NSDictionary *options = [NSDictionary dictionaryWithObjectsAndKeys:
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGImageCompatibilityKey,
                             [NSNumber numberWithBool:YES], kCVPixelBufferCGBitmapContextCompatibilityKey,
                             nil];
    
    size_t imageByteCount = texture.width * texture.height * 4;
    void *imageBytes = malloc(imageByteCount);
    NSUInteger bytesPerRow = texture.width * 4;
    MTLRegion region = MTLRegionMake2D(0, 0, texture.width, texture.height);
    [texture getBytes:imageBytes bytesPerRow:bytesPerRow fromRegion:region mipmapLevel:0];
    
    CVPixelBufferCreateWithBytes(kCFAllocatorDefault,texture.width,texture.height,kCVPixelFormatType_32BGRA,imageBytes,bytesPerRow,NULL,NULL,(__bridge CFDictionaryRef)options,&pxbuffer);
    
    if (block) {
        block(pxbuffer);
    }
    CVPixelBufferRelease(pxbuffer);
    free(imageBytes);
}
© www.soinside.com 2019 - 2024. All rights reserved.