我正在将iPhone设备的屏幕录制到Mac上。作为预览图层,我直接从AVCaptureVideoDataOutput
收集样本缓冲区,我正在创建纹理并使用Metal
渲染它们。我遇到的问题是,10.13
之前在macOS中工作的代码在更新到10.13
后停止工作。即,
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(_currentSampleBuffer);
if (!imageBuffer) return;
CVPixelBufferLockBaseAddress(imageBuffer,0);
size_t width = CVPixelBufferGetWidth(imageBuffer);
size_t height = CVPixelBufferGetHeight(imageBuffer);
CVMetalTextureRef metalTexture = NULL;
CVReturn result = CVMetalTextureCacheCreateTextureFromImage(nil,
self.textureCache,
imageBuffer,
nil,
self.pixelFormat,
width,
height,
0,
&metalTexture);
if (result == kCVReturnSuccess) {
self.texture = CVMetalTextureGetTexture(metalTexture);
}
返回result = -6660
,转换为通用kCVReturnError
,可以看作on the official Apple docs和metalTexture = NULL
。
我使用的像素格式是MTLPixelFormatBGRG422
,因为来自相机的样本是2vuy
。
作为从metalTexture
创建sampleBuffer
的解决方法,我现在就是
像这样创建一个中间NSImage
:
CVImageBufferRef imageBuffer = CMSampleBufferGetImageBuffer(_currentSampleBuffer);
NSCIImageRep *imageRep = [NSCIImageRep imageRepWithCIImage:[CIImage imageWithCVImageBuffer:imageBuffer]];
NSImage *image = [[NSImage alloc] initWithSize:[imageRep size]];
[image addRepresentation:imageRep];
并从中创建MTLTexture
。这显然是直接使用CVMetalTextureCacheCreateTextureFromImage
的一个低级解决方案。
有问题的代码再一次在macOS < 10.13
中运行得很好,我想知道是否有人有类似的问题,如果有的话,你有什么想法如何克服这个问题?
答案 0 :(得分:4)
我遇到了同样的问题,问题是在配置AVCaptureVideoDataOutput
时没有要求Metal兼容性。我想系统开始在macOS 10.13中检查这一点,可能在未请求时应用一些优化。
解决方案是将kCVPixelBufferMetalCompatibilityKey
添加到videoSettings
的{{1}}属性。
在Objective-C中:
AVCaptureVideoDataOutput
在斯威夫特:
outputCapture.videoSettings = @{
/* ... */
(NSString *)kCVPixelBufferMetalCompatibilityKey: @YES
};
我认为这需要雷达,要求Apple在发生这种情况时至少打印一条警告信息。如果我接触到它,我会更新它。
答案 1 :(得分:1)
我找到了一个解决方法,它将2vuy
格式保留在像素缓冲区中,但不好的是你制作影响性能的像素缓冲区数据的副本。我发布这个以供将来参考,或者如果其他人发现它有用。基本上我们拦截像素缓冲区,然后在复制数据时添加属性。
NSDictionary *attributes = @{
@"IOSurfaceCoreAnimationCompatibility": @YES
};
CVPixelBufferRef copy = NULL;
CVPixelBufferCreate(kCFAllocatorDefault,
CVPixelBufferGetWidth(pixelBuffer),
CVPixelBufferGetHeight(pixelBuffer),
CVPixelBufferGetPixelFormatType(pixelBuffer),
(__bridge CFDictionaryRef)attributes,
©);
CVPixelBufferLockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
CVPixelBufferLockBaseAddress(copy, 0);
void *baseAddress = CVPixelBufferGetBaseAddress(pixelBuffer);
void *copyBaseAddress = CVPixelBufferGetBaseAddress(copy);
memcpy(copyBaseAddress, baseAddress, CVPixelBufferGetDataSize(pixelBuffer));
CVPixelBufferUnlockBaseAddress(copy, 0);
CVPixelBufferUnlockBaseAddress(pixelBuffer, kCVPixelBufferLock_ReadOnly);
答案 2 :(得分:0)
从CVPixelBufferRef到MetalTexture的另一种方法是,您可以通过CIImage并将CIContext与Metal设备一起使用(希望可以最大程度地减少复制像素缓冲区所涉及的CPU);
确保目标金属纹理的尺寸合适。
CIContext* ciContext = [CIContext contextWithMTLDevice:mtlDevice
options:[NSDictionary dictionaryWithObjectsAndKeys:@(NO),kCIContextUseSoftwareRenderer,nil]
];
id<MTLCommandBuffer> metalCommandBuffer=[mtlCommandQueue commandBufferWithUnretainedReferences];
CIImage* ciImage = [[CIImage alloc] initWithCVPixelBuffer:cvPixelBuffer];
[ciContext render:ciImage
toMTLTexture:metal_texture
commandBuffer:mtlCommandBuffer
bounds:[ciImage extent])
colorSpace:[ciImage colorSpace]];