CVMetalTextureCacheCreateTextureFromImage在macOS 10.13上返回-6660

时间:2017-10-03 17:08:06

标签: objective-c macos metal

我正在将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 docsmetalTexture = 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中运行得很好,我想知道是否有人有类似的问题,如果有的话,你有什么想法如何克服这个问题?

3 个答案:

答案 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,
                    &copy);

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]];