来自 CMSampleBuffer 的 MTLTexture 有 0 bytesPerRow

MTLTexture from CMSampleBuffer has 0 bytesPerRow

我正在将我的 AVCaptureVideoDataOuput 委托的 captureOutput 函数中的 CMSampleBuffer 参数转换为 MTLTexture,就像这样(旁注,我已将视频输出的像素格式设置为 kCVPixelFormatType_32BGRA):

func captureOutput(_ captureOutput: AVCaptureOutput!, didOutputSampleBuffer sampleBuffer: CMSampleBuffer!, from connection: AVCaptureConnection!) {

    let imageBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!
    let width = CVPixelBufferGetWidth(imageBuffer)
    let height = CVPixelBufferGetHeight(imageBuffer)
    var outTexture: CVMetalTexture? = nil

    var textCache : CVMetalTextureCache?
    CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, metalDevice, nil, &textCache)

    var textureRef : CVMetalTexture?
    CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textCache!, imageBuffer, nil, MTLPixelFormat.bgra8Unorm, width, height, 0, &textureRef)
    let texture = CVMetalTextureGetTexture(textureRef!)!

    print(texture.bufferBytesPerRow)
}

问题是当我打印纹理的每行字节数时,它总是打印 0,这是有问题的,因为我稍后尝试使用本文中的方法将纹理转换回 UIImage:https://www.invasivecode.com/weblog/metal-image-processing.为什么我收到的纹理看起来是空的?我知道 CMSampleBuffer 属性 很好,因为我可以将它转换成 UIIMage 并像这样绘制它:

    let myPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)
    let myCIimage = CIImage(cvPixelBuffer: myPixelBuffer!)
    let image = UIImage(ciImage: myCIimage)
    self.imageView.image = image

bufferBytesPerRow 属性 仅对使用 MTLBuffermakeTexture(descriptor:offset:bytesPerRow:) 方法创建的纹理有意义。如您所见,每行字节数是该方法的输入,用于告诉 Metal 如何解释缓冲区中的数据。 (当然,纹理描述符也提供了额外的信息。)这种方法只是一种恢复的方法。

请注意,从缓冲区创建的纹理还可以报告它们是从哪个缓冲区创建的以及提供给上述方法的偏移量。

以其他方式创建的纹理没有该信息。这些纹理没有固有的每行字节数。他们的数据不一定在内部组织在一个简单的光栅缓冲区中。

If/when 您想将数据从纹理获取到金属缓冲区或普通的旧字节数组, 可以自由选择字节-对您的目的有用的每行值,只要它至少是纹理像素格式的每像素字节乘以纹理的宽度。 (压缩格式更复杂。)getBytes(_:bytesPerRow:from:mipmapLevel:) and copy(from:sourceSlice:sourceLevel:sourceOrigin:sourceSize:to:destinationOffset:destinationBytesPerRow:destinationBytesPerImage:) 的文档进一步解释。