LoginSignup
1
0

More than 1 year has passed since last update.

[Unity]macOSでデスクトップキャプチャ

Last updated at Posted at 2021-06-14

macOS で macOS でデスクトップキャプチャ

  • macOS のデスクトップを unity でキャプチャできるプラグインを作ってみました

  • リポジトリはこちらです

実装の仕組み

  1. macOS は AVCaptureScreenInput を使うことで、デスクトップをキャプチャする事が可能です
  2. Unity から直接 AVCaptureScreenInput にアクセスできないので、Swift で Native Plugin を実装します
  3. CMSampleBuffer を取得できるので、MTLTexture に変換します
  4. MTLTexture のポインタを Unity へ渡して、Texture2D.CreateExternalTexture を使って Texture2D を得ます

AVCaptureScreenInput

  • macOSは、AVCaptureScreenInput を使うことで、デスクトップをキャプチャする事が可能です
  • この記事と同じやり方で Native Plugin を実装します

  • AVCaptureScreenInput は他の AVCaptureInput と同じように使えます
  • AVCaptureVideoDataOutput を使って、キャプチャしたフレームを受け取ります
let captureSession = AVCaptureSession()

let displayID = CGDirectDisplayID(CGMainDisplayID())
let videoDataInput: AVCaptureScreenInput = AVCaptureScreenInput(displayID: displayID)!
captureSession.addInput(videoDataInput)

let videoDataOutput: AVCaptureVideoDataOutput = AVCaptureVideoDataOutput()
videoDataOutput.videoSettings = [kCVPixelBufferPixelFormatTypeKey: kCVPixelFormatType_32BGRA] as [String : Any]
videoDataOutput.setSampleBufferDelegate(self, queue: videoQueue)
videoDataOutput.alwaysDiscardsLateVideoFrames = true
captureSession.addOutput(videoDataOutput)

captureSession. startRunning()

CMSampleBuffer -> MTLTexture

  • AVCaptureVideoDataOutputSampleBufferDelegate を実装します
  • CVPixelBuffer を取得し、MTLTexture に変換します
extension DesktopCapture: AVCaptureVideoDataOutputSampleBufferDelegate {
    func captureOutput(_ output: AVCaptureOutput, didOutput sampleBuffer: CMSampleBuffer, from connection: AVCaptureConnection) {
        let imageBuffer: CVPixelBuffer = CMSampleBufferGetImageBuffer(sampleBuffer)!

        var textureCache : CVMetalTextureCache! = nil
        CVMetalTextureCacheCreate(kCFAllocatorDefault, nil, mtlDevice, nil, &textureCache)

        let width = CVPixelBufferGetWidth(imageBuffer)
        let height = CVPixelBufferGetHeight(imageBuffer)

        var imageTexture: CVMetalTexture! = nil
        _ = CVMetalTextureCacheCreateTextureFromImage(kCFAllocatorDefault, textureCache,
                                                      imageBuffer, nil, .bgra8Unorm_srgb,
                                                      width, height, 0, &imageTexture)

        let texture: MTLTexture = CVMetalTextureGetTexture(imageTexture)!        
    }

MTLTexture -> Texture

  • Unity から呼び出される mcDesktopCapture_getCurrentFrame 関数を実装します
  • retain して、テクスチャのポインタを Unity に返します
@_cdecl("mcDesktopCapture_getCurrentFrame")
public func mcDesktopCapture_getCurrentFrame() -> FrameEntity {
    let texturePtr = Unmanaged.passRetained(texture).toOpaque()
    let e = FrameEntity(width: texture.width, height: texture.height, texturePtr: texturePtr)
    return e
}
  • FrameEntity を取得して、Texture2D.CreateExternalTexture を使って、Textur2D を作成します
  • 作成した、Texture2D は通常の Texture 同様に利用可能です
FrameEntity frameEntity = mcDesktopCapture_getCurrentFrame();
Texture2D texture = Texture2D.CreateExternalTexture((int)frameEntity.width, (int)frameEntity.height, TextureFormat.ARGB32, false, false, frameEntity.texturePtr);
Renderer m_Renderer = GetComponent<Renderer>();
m_Renderer.material.SetTexture("_MainTex", texture);

おわりに

  • macOS では、API が用意されているため、デスクトップキャプチャの仕組みを容易に実装できました
  • macOS はアプリ単位で取得する方法が見つからず、画面全体をキャプチャすることにしました
1
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
1
0