将远程帧缓冲区流式传输到 NSView

Stream remote framebuffer into NSView

我正在尝试使用 LibvnccClient 在 Swift 中实现一个简单的 VNC client

我创建了 NSView 的子类。我创建了一个 CGContext 并将数据指针传递给库以用作 framebuffer。 libvncclient 在屏幕内容更改时更新帧缓冲区并调用我提供的回调。

这里是相关代码

class VNCClient {

    var localClient: rfbClient
    var view: NSView

    init(view: NSView) {
        guard let client_ptr = rfbGetClient(8, 3, 4) else {
            fatalError("Trouble")
        }

        self.view = view
        self.localClient = client_ptr.pointee

        localClient.MallocFrameBuffer = resize
        localClient.GotFrameBufferUpdate = update
        let fbPointer = UnsafeMutablePointer<UInt8>(OpaquePointer((view as! RFBView).buffer))
        localClient.frameBuffer = fbPointer
        rfbClientSetClientData(&localClient, &viewTag, &self.view)
        var argc: Int32 = 0
        let b = rfbInitClient(&localClient, &argc, nil)
        if b == RFB_TRUE {
            print("Connected!")
        }
        Timer.scheduledTimer(withTimeInterval: 0.001, repeats: true) {_ in
            if WaitForMessage(&self.localClient, 1) > 0 {
                HandleRFBServerMessage(&self.localClient)
            }
        }
    }

func update(client: UnsafeMutablePointer<rfbClient>?, x: Int32, y: Int32, w: Int32, h: Int32) -> Void {
    let cl = client!.pointee
    let view_ptr = rfbClientGetClientData(client, &viewTag)
    let view = view_ptr?.assumingMemoryBound(to: RFBView.self).pointee
    view?.setNeedsDisplay(NSRect(x: Int(x), y: Int(y), width: Int(w), height: Int(h)))
}


class RFBView: NSView {

    let ctx = CGContext(data: nil, width: 800, height: 600, bitsPerComponent: 8, bytesPerRow: 4 * 800, space: CGColorSpaceCreateDeviceRGB(), bitmapInfo: CGImageAlphaInfo.noneSkipLast.rawValue)!

    var buffer: UnsafeMutableRawPointer? {
        return ctx.data
    }
    override func draw(_ dirtyRect: NSRect) {

        let image = ctx.makeImage()

        NSGraphicsContext.current()?.cgContext.draw(image!, in: frame)

   }

可以,但是显示不流畅。服务器 运行 在虚拟机中的同一台机器上,因此没有网络问题。

我正在为我认为是问题原因的每次更新重新绘制整个图像。那么我怎样才能只重绘帧缓冲区更新的部分呢?

CoreGraphics 是否足够快,或者我应该使用 NSOpenGLView

我假设如果有更新,HandleRFBServerMessage 每次迭代都会调用一次 update 函数。但事实并非如此。

我用打印到控制台的存根替换了 update,并注意到当屏幕上有很多 activity 时,它被调用了一千多次。

然而,在 update 中调用 setNeedsDisplay,它只被调用了几百次。由于它花费了太多时间进行渲染,因此错过了中间的几帧。

我将绘图代码移到了 Timer 中,现在可以正常使用了。

localClient.GotFrameBufferUpdate = { _, _, _, _, _ in needs_update = true }
Timer.scheduledTimer(withTimeInterval: 0.016, repeats: true) {_ in
    if needs_update {
        view.display()
        needs_update = false
    }
    if WaitForMessage(&self.localClient, 1) > 0 {
        HandleRFBServerMessage(&self.localClient)
    }
}