見出し画像

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を作成することにする。

ここから先は

768字
文章やサンプルコードは多少荒削りかもしれませんが、ブログや書籍にはまだ書いていないことを日々大量に載せています。たったの400円で、すぐに購読解除してもその月は過去記事もさかのぼって読めるので、少しでも気になる内容がある方にはオトクかと思います。

技術的なメモやサンプルコード、思いついたアイデア、考えたこと、お金の話等々、頭をよぎった諸々を気軽に垂れ流しています。

最後まで読んでいただきありがとうございます!もし参考になる部分があれば、スキを押していただけると励みになります。 Twitterもフォローしていただけたら嬉しいです。 https://twitter.com/shu223/