libvncserver: add server-side support for the Open H.264 encoding#736
Open
CMGS wants to merge 2 commits into
Open
libvncserver: add server-side support for the Open H.264 encoding#736CMGS wants to merge 2 commits into
CMGS wants to merge 2 commits into
Conversation
A deferUpdateTime of 0 is the natural setting for applications that push already-paced updates (e.g. video streams), but three code paths turned it into a busy loop: - clientOutput's wait-for-RFB_NORMAL path spun with zero-length sleeps - clientOutput issued a pointless zero-length sleep syscall per update - rfbRunEventLoop derived its default select() timeout from deferUpdateTime, making the listener thread (threaded mode) or the rfbProcessEvents loop (select mode) a zero-timeout poll pinning one core even with no clients connected Sleep at least 1 ms while waiting for the client to become ready, skip the defer sleep entirely when no deferral is configured, and fall back to a 100 ms default select() timeout; socket activity still wakes the loops immediately.
Add support for the Open H.264 encoding (50) implemented by the TigerVNC and noVNC viewers. The server does not encode: the application supplies pre-encoded H.264 access units through a new per-screen hook, making the encoding suitable for passing through streams that already exist in H.264 form (hardware encoders, Android screen capture, media pipelines). The hook contract is non-blocking. When a new access unit is available, the application calls rfbNotifyH264FrameAvailable(), which wakes only the clients streaming H.264, so damage tracking of clients using other encodings is not disturbed. This works with both the threaded and the select()-based event loops and never blocks the update path while holding the client's send mutex. The access unit is fetched only after the wake-up mark has been consumed, so a notification racing a running update is never lost. Since a video stream is push-oriented, a client whose preferred encoding is Open H.264 keeps its update subscription instead of needing one framebuffer update request per frame. Its whole modified region is dropped after each update - pixel damage is superseded by the next access unit - so a client whose update requests never covered the full screen cannot leave residue that would spin the update loop. Access units routinely exceed UPDATE_BUF_SIZE, so the rectangle payload is written directly to the socket. New rfbScreenInfo/rfbClientRec fields are appended to the end of the structs to keep the ABI backward-compatible.
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements the server side of the Open H.264 encoding (50), as requested in #669. The TigerVNC viewer and noVNC already decode this encoding, and QEMU recently gained a server-side implementation.
Design
LibVNCServer does not encode. The application supplies complete, pre-encoded H.264 access units through a new screen hook:
This fits the cases where H.264 pays off most — hardware encoders, Android/scrcpy capture, media pipelines that already produce H.264 — without adding a codec dependency to the library. A software-encoder integration could be layered on top of the same hook later.
Key points:
rfbNotifyH264FrameAvailable(), which wakes exactly the clients streaming H.264; damage tracking of clients on other encodings is not disturbed. Nothing blocks the update path, which the threaded loop runs while holding the client'ssendMutex.requestedRegion, so pushing a 30-60 fps stream does not cost one FramebufferUpdateRequest round trip per frame. The subscription ends when the client selects another preferred encoding via SetEncodings.clientData, and every client's stream starts with SPS/PPS + IDR. Clients consume at different rates or resync to a keyframe independently.rfbScreenInfo/rfbClientRecfields are appended at the end of the structs.UPDATE_BUF_SIZE, so the rectangle payload (big-endian u32 length, u32 reset flags, then the access unit) is written directly to the socket after flushingupdateBuf.The first commit fixes busy loops when
deferUpdateTimeis 0 — the natural setting for streaming applications, whose frames are already paced: theclientOutputwait/defer paths spun with zero-length sleeps, andrfbRunEventLoop's default select() timeout derived 0 fromdeferUpdateTime, pinning one core in the listener thread (or therfbProcessEventsloop) even with no clients connected.Testing
SetEncodings [50]and a single FramebufferUpdateRequest, every pushed access unit arrives as a full-frame rect with exact framing — x=0, y=0, w/h = screen size, encoding 50, big-endian length/flags, payload byte-identical and in order — with no further update requests;deferUpdateTime = 0and no clients, the server idles at 0% CPU (was 100% in the listener thread before the first commit).vncviewer -PreferredEncoding=H.264) and decodes to Tight/JPEG for ordinary clients.Happy to adjust naming or split things differently if you prefer.