流式显示、转换它并在 window 中显示它?

Stream a display, transform it, and display it in a window?

我需要一个应用程序,作为一个体面的(我认为)Swift 开发者,我想我会尝试一下。

要点很简单:我想

  1. 捕获显示(CGDisplayStream?)
  2. 垂直翻转图像
  3. 在 window.
  4. 中显示翻转的流

我只是不确定从哪里开始?也许是 CGDisplayStream 或者 AVFoundation?有没有人有如何完成捕获显示流并将其显示在 window 中的示例代码?或者甚至是“使用这些classes/interfaces/apis”的指南来做到这一点?我已经 github 搜索了 CGDisplayStream.init 并没有真正想出任何例子:(

我不要求任何人为我写它,但我可以使用:从这里开始,使用这个,注意...

这是我目前正在试验的代码。我 认为? 我有步骤 1 和 2 的工作,但如果没有 #3 就很难说...

struct ContentView: View {
  @State var displayStream: CGDisplayStream?
  let backgroundQueue = DispatchQueue(label: "com.app.queue",
                                      qos: .background,
                                      target: nil)
  var body: some View {
    VStack{
      MetalView()
      Text("Hello")
        .frame(maxWidth: .infinity, maxHeight: .infinity)
        .onAppear{
          self.displayStream  = CGDisplayStream(dispatchQueueDisplay: 0, outputWidth: 100, outputHeight: 100,pixelFormat: Int32(k32BGRAPixelFormat), properties: nil, queue: backgroundQueue) { (status, code, iosurface, update) in
            
            switch(status){
            case .frameBlank:
              print("FrameBlank")
              break;
            case .frameIdle:
              print("FrameIdle")
              break;
            case .frameComplete:
              print("FrameComplete")
              var image = CIImage(ioSurface: iosurface!)
              image = image.transformed(by: CGAffineTransform(scaleX: -1, y: 1))
              break;
            case .stopped:
              print("Stopped")
              break;
            @unknown default:
              print("Unknown Error")
            }
            
            
          }
          
          displayStream?.start()
        }
        .onDisappear{
          displayStream?.stop()
          displayStream = nil
        }
    }
  }
}

我创建了一个 mtkview NSViewRepresentable,如下所示:

import MetalKit
import SwiftUI

struct MetalView: NSViewRepresentable {
  func makeCoordinator() -> Coordinator {
    Coordinator(self)
  }
  
  func makeNSView(context: NSViewRepresentableContext<MetalView>) -> MTKView {
    let mtkView = MTKView()
    mtkView.delegate = context.coordinator
    mtkView.preferredFramesPerSecond = 60
    mtkView.enableSetNeedsDisplay = true
    if let metalDevice = MTLCreateSystemDefaultDevice() {
      mtkView.device = metalDevice
    }
    mtkView.framebufferOnly = false
    mtkView.clearColor = MTLClearColor(red: 0, green: 0, blue: 0, alpha: 0)
    mtkView.drawableSize = mtkView.frame.size
    mtkView.enableSetNeedsDisplay = true
    return mtkView
  }
  
  func updateNSView(_ nsView: MTKView, context: NSViewRepresentableContext<MetalView>) {
  }
  
  class Coordinator : NSObject, MTKViewDelegate {
    var parent: MetalView
    var metalDevice: MTLDevice!
    var metalCommandQueue: MTLCommandQueue!
    
    init(_ parent: MetalView) {
      self.parent = parent
      if let metalDevice = MTLCreateSystemDefaultDevice() {
        self.metalDevice = metalDevice
      }
      self.metalCommandQueue = metalDevice.makeCommandQueue()!
      super.init()
    }
    
    func mtkView(_ view: MTKView, drawableSizeWillChange size: CGSize) {
    }
    
    func draw(in view: MTKView) {
      guard let drawable = view.currentDrawable else {
        return
      }
      let commandBuffer = metalCommandQueue.makeCommandBuffer()
      let rpd = view.currentRenderPassDescriptor
      rpd?.colorAttachments[0].clearColor = MTLClearColorMake(0, 1, 0, 1)
      rpd?.colorAttachments[0].loadAction = .clear
      rpd?.colorAttachments[0].storeAction = .store
      let re = commandBuffer?.makeRenderCommandEncoder(descriptor: rpd!)
      re?.endEncoding()
      commandBuffer?.present(drawable)
      commandBuffer?.commit()
    }
  }
}

这是我第一次用 metal 或 core image 做任何事情,我感觉自己摇摇欲坠 - 理解明智 -

看来您可以使用 Core Image 完成大部分工作:

  1. CGDisplayStream 的处理程序给你一个 IOSurface。您可以将其包装在 CIImageCIImage(ioSurface:).
  2. 使用 image.transformed(by:).
  3. 将任何(或多个)CGAffineTransform 应用于 CIImage
  4. 使用 CIContext 将生成的图像渲染为 MTKView。也许你可以从我的例子中得到一些启发 here.

注意:我不是 100% 确定 Core Image 是否会在您将其包装在 CIImage 中时为您正确保留 IOSurface。如果您仍然需要执行 handler documentation 中描述的任何操作,您应该 double-check。