流远程帧缓冲到NSView
问题描述:
我想用LibvnccClient
在Swift中实现一个简单的VNC client
。流远程帧缓冲到NSView
我已经创建了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)
}
它的工作原理,但显示效果并不顺利。服务器在虚拟机中的同一台计算机上运行,因此没有网络问题。
我正在重新绘制整个图像的每个更新,我认为是问题的原因。那么我怎么才能重新绘制更新后的framebuffer部分呢?
是CoreGraphics
不够快,或者我应该使用NSOpenGLView
?
答
我认为HandleRFBServerMessage
会调用update
函数每次迭代,如果有更新。但事实并非如此。
我将update
替换为打印到控制台的存根,并且发现当屏幕上出现大量活动时,它被称为超过一千次。
但是,setNeedsDisplay
呼叫update
,它只被称为几百次。由于它花费了太多的时间,所以在它们之间的几帧中错过了它。
我将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)
}
}
无需每秒重绘1000次。尝试15fps'Timer.scheduledTimer(withTimeInterval:1/15,'越小越好。它不需要看起来像电影 –
我尝试使用帧速率播放,但我无法获得可接受的性能。 – a2bk