Unityで取得したカメラのMTLTextureをCVPixelBufferに変換する
やりたいこと
カメラから取得したフレームをリアルタイムで処理したい。カメラの制御はUnity、フレーム画像の処理はiOSネイティブプラグインで行う。
前提となる話: UnityからカメラテクスチャをiOSネイティブプラグインに渡す
Unity側では、WebCamTextureのGetNativeTexturePtrメソッドを利用して、カメラフレームのテクスチャへのポインタを取得する。
_webCamTexture.GetNativeTexturePtr()
これをiOSネイティブプラグイン側で受け取ると、ObjC++的にはMTLTextureRef型として扱うことになる。SwiftではMTLTexture型。(厳密には、MTLTextureプロトコルに準拠する型)
関連記事:
MTLTexture -> CVPixelBuffer
実際はMTLTextureCVPixelBufferに変換するのではなく直接Metalで処理すべきなんだけど、とりいそぎCVPixelBufferの既存実装を流用したい、という状況。
この記事によると、MTLTextureをCVPixelBufferに変換するにはこうしましょうとある:
var outPixelbuffer: CVPixelBuffer?
if let datas = targetTexture.texture.buffer?.contents() {
CVPixelBufferCreateWithBytes(kCFAllocatorDefault, targetTexture.width,
targetTexture.height, kCVPixelFormatType_64RGBAHalf, datas,
targetTexture.texture.bufferBytesPerRow, nil, nil, nil, &outPixelbuffer);
}
で、やろうとしてみると、
guard let bufferPointer = texture.buffer?.contents() else {
print("no bufer")
return false }
という感じでMTLTextureのbufferプロパティにアクセスしようとしたら、値がnilだった。
MTLTextureをそのまま扱う他の処理はちゃんと動いてるので、テクスチャ自体は空ではない。どこか別のところにテクスチャデータが入っているはず。
Unityから送られてきたMTLTextureオブジェクトをprintしてみると、次のように出力された。
<CaptureMTLTexture: 0x280c3b280> -> <AGXA13FamilyTexture: 0x114bd8780>
label = CoreVideo 0x280c3b280
textureType = MTLTextureType2D
pixelFormat = MTLPixelFormatBGRA8Unorm
width = 1280
height = 720
depth = 1
arrayLength = 1
mipmapLevelCount = 1
sampleCount = 1
cpuCacheMode = MTLCPUCacheModeDefaultCache
storageMode = MTLStorageModeShared
hazardTrackingMode = MTLHazardTrackingModeTracked
resourceOptions = MTLResourceCPUCacheModeDefaultCache MTLResourceStorageModeShared MTLResourceHazardTrackingModeTracked
usage = MTLTextureUsageShaderRead MTLTextureUsageShaderWrite MTLTextureUsageRenderTarget MTLTextureUsagePixelFormatView
shareable = 0
framebufferOnly = 0
purgeableState = MTLPurgeableStateNonVolatile
swizzle = [MTLTextureSwizzleRed, MTLTextureSwizzleGreen, MTLTextureSwizzleBlue, MTLTextureSwizzleAlpha]
isCompressed = 0
parentTexture = <null>
parentRelativeLevel = 0
parentRelativeSlice = 0
buffer = <null>
bufferOffset = 0
bufferBytesPerRow = 0
iosurface = 0x281401590
iosurfacePlane = 0
allowGPUOptimizedContents = YES
bufferやparentTextureは<null>と出ているが、iosurfaceにはちゃんとオブジェクトが入っている。
IOSurface -> CVPixelBuffer
方針を切り替えて、iosurfaceプロパティから得られるIOSurfaceRef(IOSurfaceオブジェクトへの参照)を用いてCVPixelBufferを作成することにする。
最後まで読んでいただきありがとうございます!もし参考になる部分があれば、スキを押していただけると励みになります。 Twitterもフォローしていただけたら嬉しいです。 https://twitter.com/shu223/