2017-04-04 1 views
0

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) 

    } 

그것은 작동하지만 디스플레이가 원활하지입니다. 서버가 VM의 동일한 시스템에서 실행되므로 네트워크 문제가 발생하지 않습니다.

문제의 원인이라고 생각되는 모든 업데이트에 대해 전체 이미지를 다시 그려 넣습니다. 그렇다면 프레임 버퍼의 업데이트 된 부분 만 어떻게 다시 그릴 수 있습니까?

CoreGraphics은 충분히 빠르지 만 NSOpenGLView을 사용해야합니까?

+0

초당 1000 회 다시 그리지 않아도됩니다. 'Timer.scheduledTimer (withTimeInterval : 1/15,'덜해라.) 영화처럼 보일 필요가 없다. –

+0

나는 프레임 속도로 게임을 해봤지만, 받아 들일만한 성능을 얻을 수 없다. . – a2bk

답변

0

업데이트가있는 경우 HandleRFBServerMessageupdate을 반복마다 한 번씩 호출한다고 가정했습니다. 하지만 그렇지 않습니다.

나는 콘솔에 인쇄 된 그루터기로 update을 교체했으며 화면에 많은 활동이있을 때 천 번 이상 호출되었다는 것을 알게되었습니다.

그러나 setNeedsDisplayupdate으로 호출하면 몇 백 번만 호출되었습니다. 렌더링에 너무 많은 시간을 소비 했으므로 그 사이에 몇 프레임을 놓치고있었습니다.

도면 코드를 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) 
    } 
}