CVMetalTextureCacheCreateTextureFromImage 在 macOS 10.13 上返回 -6660

Posted

技术标签:

【中文标题】CVMetalTextureCacheCreateTextureFromImage 在 macOS 10.13 上返回 -6660【英文标题】:CVMetalTextureCacheCreateTextureFromImage returns -6660 on macOS 10.13 【发布时间】:2018-03-14 23:08:48 【问题描述】:

我正在将屏幕从我的 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

作为从sampleBuffer 创建metalTexture 的解决方法,我现在 像这样创建一个中间 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 中运行良好,我想知道是否有人有类似的问题,如果有,你有什么想法可以解决这个问题吗?

【问题讨论】:

【参考方案1】:

我遇到了同样的问题,问题是在配置 AVCaptureVideoDataOutput 时没有要求金属兼容性。我猜系统开始在 macOS 10.13 中检查这一点,可能会在未请求时应用一些优化。

解决方案是将kCVPixelBufferMetalCompatibilityKey 添加到AVCaptureVideoDataOutputvideoSettings 属性中。

在 Objective-C 中:

outputCapture.videoSettings = @
  /* ... */
  (NSString *)kCVPixelBufferMetalCompatibilityKey: @YES
;

在斯威夫特中:

outputCapture.videoSettings = [
  /* ... */
  kCVPixelBufferMetalCompatibilityKey as String: true
]

我认为这需要雷达,要求 Apple 在发生这种情况时至少打印一条警告消息。如果我得到它,我会更新它。

【讨论】:

太棒了,这行得通!我现在需要检查像素缓冲区中的输出格式,因为我得到了一个分割图像和奇怪的颜色。我将在另一个答案中发布我的解决方法以供将来参考。 谢谢!这对我有帮助,因为它也适用于您制作自己的 CVPixelBuffer 时:CVPixelBufferCreate 通过添加到属性字典:let attrs = [kCVPixelBufferMetalCompatibilityKey: kCFBooleanTrue, ...<others>... ] as CFDictionary 如果您按照本技术说明中的说明使用CVPixelBufferCreateWithBytesCVPixelBufferCreateWithPlanarBytes 创建了您的cvpixelbuffer,这将不起作用; developer.apple.com/library/archive/qa/qa1781/_index.html设置密钥无效。 EliaCereda 谢谢你,它帮助了我。【参考方案2】:

我找到了一个解决方法,它将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);

【讨论】:

【参考方案3】:

另一种从CVPixelBufferRef 获得金属纹理的方法,您可以通过CIImage 并在金属设备上使用CIContext(希望最大限度地减少 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]];

【讨论】:

以上是关于CVMetalTextureCacheCreateTextureFromImage 在 macOS 10.13 上返回 -6660的主要内容,如果未能解决你的问题,请参考以下文章