Search code examples
iosmetalcore-videocvpixelbuffermetalkit

CVMetalTextureCacheCreateTextureFromImage always return null


I'm trying to render I420 (YCbCr planner) via MetalKit

most of examples are using the CMSampleBuffer which from Camera,

but my goal is using a given I420 bytes.

I do something like this:

let data = NSMutableData(contentsOfURL: NSBundle.mainBundle().URLForResource("yuv_640_360", withExtension: "yuv")!)

// Cache for Y

CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, self.device!, nil, &videoTextureCache)

var pixelBuffer: CVPixelBuffer?

CVPixelBufferCreateWithBytes(kCFAllocatorDefault, Int(size.width), Int(size.height), kCVPixelFormatType_420YpCbCr8BiPlanarVideoRange, data.mutableBytes, Int(size.width), nil, nil, [
    "kCVPixelBufferMetalCompatibilityKey": true,
    "kCVPixelBufferOpenGLCompatibilityKey": true,
    "kCVPixelBufferIOSurfacePropertiesKey": []
    ]
    , &pixelBuffer)

// Y texture

var yTextureRef : Unmanaged<CVMetalTexture>?

let yWidth = CVPixelBufferGetWidthOfPlane(pixelBuffer, 0)

let yHeight = CVPixelBufferGetHeightOfPlane(pixelBuffer, 0)

let result = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, (videoTextureCache?.takeUnretainedValue())!, pixelBuffer, nil, MTLPixelFormat.R8Unorm, yWidth, yHeight, 0, &yTextureRef);

basically the code is almost same as other examples but I create my own CVPixelBuffer by myself.

I got no error when I creating CVPixelBuffer and CVMetalTexture,

but it always return null for yTexture.

How do I create the right CVPixelBuffer and use it to render ?


Solution

  • problem solved.

    iosurface is important, I found iosurface always be null if you create CVPixelBuffer by CVPixelBufferCreateWithBytes or CVPixelBufferCreateWithPlanarBytes.

    once you use a CVPixelBuffer which's iosurface is null, then MetalTexture always be null.

    should do something like this:

    let result = CVPixelBufferCreate(kCFAllocatorDefault, width, height, kCVPixelFormatType_420YpCbCr8Planar, [
        String(kCVPixelBufferIOSurfacePropertiesKey): [
            "IOSurfaceOpenGLESFBOCompatibility": true,
            "IOSurfaceOpenGLESTextureCompatibility": true,
            "IOSurfaceCoreAnimationCompatibility": true,
            ]
        ], &self.pixelBuffer)
    
    CVPixelBufferLockBaseAddress(self.pixelBuffer!, 0)
    
    for index in 0...2 {
    
        memcpy(CVPixelBufferGetBaseAddressOfPlane(self.pixelBuffer!, index), planesAddress[index], planesWidth[index] * planesHeight[index])
    
    }
    
    CVPixelBufferUnlockBaseAddress(self.pixelBuffer!, 0)