我目前正在使用 Metal 开发实时滤波器。 定义 CIImage 后,我将图像渲染到 MTLTexture。
下面是我的渲染代码。
context
是由 Metal 支持的 CIContext;
targetTexture
是附加到我的 MTKView 实例的 currentDrawable
属性的纹理的别名:
context?.render(drawImage, to: targetTexture, commandBuffer: commandBuffer, bounds: targetRect, colorSpace: colorSpace)
它渲染正确,因为我可以看到金属视图上显示的图像。
问题是在渲染图像(并显示它)之后,我想提取 CVPixelBuffer 并使用 AVAssetWriter 类将其保存到磁盘。
另一种选择是有两个渲染步骤,一个渲染到纹理,另一个渲染到 CVPixelBuffer。 (但尚不清楚如何创建这样的缓冲区,或者两个渲染步骤对帧速率的影响)
任何帮助将不胜感激,谢谢!
您可以尝试像这样从 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);
}
最上面的答案不适用于未使用金属缓冲区创建的纹理。在这种情况下,
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
}
+ (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);
}