xdeskv1.0.0
ConceptsConcepts

How the screen is sentComment l'écran est envoyé

Why a still screen costs nothing, how scrolling and video are handled, and how the stream adapts to a slow or lossy link.Pourquoi un écran immobile ne coûte rien, comment le défilement et la vidéo sont traités, et comment le flux s'adapte à un lien lent ou avec pertes.

In one sentence: xdesk only sends the parts of the screen that changed, reuses what the viewer already has when you scroll, and sends less or blurrier pictures while the link is slow, then sharp ones again.

Definition: a per-session dirty-tile encoder (64×64 tiles hashed, JPEG / solid / palette / copy tiles, H.264 for large moves on a bottleneck link) driven by an ack-based flow control (frames in flight sized from the measured ping and bandwidth), over TLS websockets, encrypted UDP for the native viewers, or up to 4 websockets for the web viewers on lossy paths.

En une phrase : xdesk n'envoie que les parties de l'écran qui ont changé, réutilise ce que le viewer a déjà quand tu fais défiler, et envoie des images plus légères pendant que le lien est lent, puis nettes à nouveau.

Définition : un encodeur de tuiles modifiées par session (tuiles 64×64 hachées, tuiles JPEG / unies / palette / copy, H.264 pour les grands mouvements sur un lien goulot) piloté par un contrôle de flux à acquittements (images en vol dimensionnées depuis le ping et le débit mesurés), sur des websockets TLS, de l'UDP chiffré pour les viewers natifs, ou jusqu'à 4 websockets pour les viewers web sur les chemins avec pertes.

1. The ideaL'idée

Think of a whiteboard filmed for someone far away. If only one word changes, you don't send a new photo of the whole board: you send the corner with the word. If the text slides up, you say "move this part up" and only send the new line at the bottom.

The host keeps, per session, the hash of every tile the viewer has. A capture frame only produces tiles whose hash changed; a capture with nothing new produces nothing. The encoding of a tile is chosen from its content (solid, palette, JPEG) and from the history (copy of a previous region), and the pace from the acks of the viewer.

Imagine un tableau blanc filmé pour quelqu'un au loin. Si un seul mot change, tu n'envoies pas une nouvelle photo de tout le tableau : tu envoies le coin avec le mot. Si le texte remonte, tu dis « monte cette partie » et tu n'envoies que la nouvelle ligne en bas.

L'hôte garde, par session, le hash de chaque tuile que le viewer possède. Une capture ne produit que les tuiles dont le hash a changé ; une capture sans nouveauté ne produit rien. L'encodage d'une tuile est choisi selon son contenu (unie, palette, JPEG) et l'historique (copie d'une région précédente), et le rythme selon les acquittements du viewer.

2. How it worksComment ça marche

  1. First frame. The viewer has nothing yet: every 64×64 tile is sent.
  2. Hash. Something moves: the tiles are hashed and 3 of the 32 differ from what the viewer has.
  3. Encode. Only those 3 go: a one-color tile as 3 bytes, the changed tiles of one row merged in one JPEG (or a lossless palette for text).
  4. Scroll. The page scrolls by one row: the moved tiles are found higher in the previous frame and sent as copy tiles; only the revealed row is encoded.
  5. Motion. More than a quarter of the tiles change (a video): a lighter JPEG, or one H.264 frame of the whole screen while the link of the viewer is the bottleneck.
  6. Still. The screen stops moving: the same tiles are sent again, sharp.
copy 32 tiles sent the whole screen 3 tiles changed 29 unchanged: nothing sent 3 tiles sent 1 solid (3 bytes), 2 in one JPEG 24 copy tiles + 8 encoded copy = u16 sx, sy (4 bytes) lighter JPEG / H.264 quality lowered while it moves sharp tiles again quality back to the setting
  1. Première image. Le viewer n’a encore rien : toutes les tuiles de 64×64 sont envoyées.
  2. Hash. Quelque chose bouge : les tuiles sont hachées et 3 sur 32 diffèrent de ce qu’a le viewer.
  3. Encodage. Seules ces 3 partent : une tuile d’une seule couleur en 3 octets, les tuiles changées d’une rangée fusionnées en un JPEG (ou une palette sans perte pour du texte).
  4. Défilement. La page défile d’une rangée : les tuiles déplacées sont retrouvées plus haut dans l’image précédente et envoyées comme tuiles copy ; seule la rangée révélée est encodée.
  5. Mouvement. Plus d’un quart des tuiles changent (une vidéo) : un JPEG plus léger, ou une image H.264 de tout l’écran tant que le lien du viewer est le goulot.
  6. Immobile. L’écran ne bouge plus : les mêmes tuiles sont renvoyées, nettes.
copy 32 tuiles envoyées tout l’écran 3 tuiles changées 29 identiques : rien envoyé 3 tuiles envoyées 1 unie (3 octets), 2 en un JPEG 24 tuiles copy + 8 encodées copy = u16 sx, sy (4 octets) JPEG plus léger / H.264 qualité baissée pendant le mouvement tuiles nettes à nouveau qualité revenue au réglage

3. How fastÀ quelle vitesse

xdesk does not send faster than the viewer can receive: it waits for the viewer to say "got it" before sending more. On a LAN 2 pictures can be on the way, for a far viewer up to 8. When a queue builds up, the quality goes down until the queue is gone.

The capture thread ticks every 4 ms. A session gets a frame when its FPS cap allows it and its window is open: without a queue, the frames its rate needs during one ping (2 on a LAN, up to 8 far away); with a queue, only what the path holds (bandwidth × ping) and a lower JPEG quality until it drains, so no lag builds up. A viewer whose link can't follow even at the lowest quality gets a lower resolution (autoResolution), raised again once the link is calm. No frame is sent while the viewer shows a terminal.

xdesk n'envoie pas plus vite que le viewer ne peut recevoir : il attend que le viewer dise « reçu » avant d'en envoyer plus. Sur un LAN 2 images peuvent être en route, pour un viewer lointain jusqu'à 8. Quand une file se forme, la qualité baisse jusqu'à ce qu'elle disparaisse.

Le thread de capture tourne toutes les 4 ms. Une session reçoit une image quand son plafond de FPS le permet et que sa fenêtre est ouverte : sans file, les images dont son rythme a besoin pendant un ping (2 sur un LAN, jusqu'à 8 au loin) ; avec une file, seulement ce que le chemin contient (débit × ping) et une qualité JPEG plus basse jusqu'à ce qu'elle se vide, pour qu'aucun retard ne s'accumule. Un viewer dont le lien ne suit pas même à la qualité la plus basse reçoit une résolution plus basse (autoResolution), remontée une fois le lien calme. Aucune image n'est envoyée pendant que le viewer affiche un terminal.

4. Over UDP and extra socketsEn UDP et sockets supplémentaires

The Qt viewer, the terminal client and the Android app receive the video over UDP on the port of the server: AES-256-GCM with one key per direction sent inside TLS, pieces of 1150 bytes, one xor parity per 8 pieces while losses are seen, nacks, and frames that complete in any order. Control, inputs and terminals stay on TLS; the video goes back to TLS when UDP is blocked.

Link (emulated, 25 s)TCP + TLSUDP + GCM + parity
LAN cable30 fps, p95 4.9 ms30 fps, p95 13 ms (Python prototype)
Fiber, 40 ms30 fps, p99 69 ms30 fps, p99 50 ms
4G, 80 ms, 1 % loss27 fps, p95 163 ms27 fps, p95 160 ms
160 ms, 3 % loss, 6 Mbit/s17 fps, 15 stalls23 fps, 4 stalls
300 ms, 8 % loss, 2 Mbit/s5.4 fps, p50 506 ms7.8 fps, p50 243 ms

Browsers have no raw UDP: on a far lossy path a web viewer may get up to 4 websockets for the video, each with its own TCP window, kept only when they help. Goodput in Mbit/s:

Path (ping, loss, link)1 socket234
LAN, 2 ms, 300 Mbit/s286290293292
80 ms, 1 %, 30 Mbit/s2.55.87.110.1
160 ms, 3 %, 6 Mbit/s0.91.42.03.2
300 ms, 8 %, 2 Mbit/s0.40.61.11.3

The byte layout of each message.

Le viewer Qt, le client terminal et l'appli Android reçoivent la vidéo en UDP sur le port du serveur : AES-256-GCM avec une clé par sens envoyée dans TLS, morceaux de 1150 octets, une parité xor pour 8 morceaux tant que des pertes sont vues, nacks, et des images qui se complètent dans n'importe quel ordre. Contrôle, entrées et terminaux restent sur TLS ; la vidéo revient sur TLS quand l'UDP est bloqué.

Lien (émulé, 25 s)TCP + TLSUDP + GCM + parité
LAN câble30 fps, p95 4.9 ms30 fps, p95 13 ms (prototype Python)
Fibre, 40 ms30 fps, p99 69 ms30 fps, p99 50 ms
4G, 80 ms, 1 % de perte27 fps, p95 163 ms27 fps, p95 160 ms
160 ms, 3 % de perte, 6 Mbit/s17 fps, 15 blocages23 fps, 4 blocages
300 ms, 8 % de perte, 2 Mbit/s5.4 fps, p50 506 ms7.8 fps, p50 243 ms

Les navigateurs n'ont pas d'UDP brut : sur un chemin lointain avec pertes, un viewer web peut recevoir jusqu'à 4 websockets pour la vidéo, chacune avec sa propre fenêtre TCP, gardées seulement si elles aident. Débit utile en Mbit/s :

Chemin (ping, perte, lien)1 socket234
LAN, 2 ms, 300 Mbit/s286290293292
80 ms, 1 %, 30 Mbit/s2.55.87.110.1
160 ms, 3 %, 6 Mbit/s0.91.42.03.2
300 ms, 8 %, 2 Mbit/s0.40.61.11.3

Le format octet par octet de chaque message.

5. PitfallsPièges

  • Wayland without the PipeWire library falls back to portal screenshots: about 1 frame per second.
  • H.264 needs openh264 at run time (libopenh264.so.8, or the Cisco dll next to xdesk.exe on Windows); without it, large moves stay JPEG.
  • Extra sockets cannot lower the ping: they only give back the throughput a lossy path takes from one connection.
  • Wayland sans la bibliothèque PipeWire retombe sur les captures du portail : environ 1 image par seconde.
  • H.264 demande openh264 à l'exécution (libopenh264.so.8, ou la dll de Cisco à côté de xdesk.exe sous Windows) ; sans elle, les grands mouvements restent en JPEG.
  • Les sockets supplémentaires ne baissent pas le ping : elles rendent seulement le débit qu'un chemin avec pertes prend à une connexion.

6. Check yourselfVérifie-toi

Nothing moves on the host screen. How much is sent?

Nothing: a capture with no new frame is skipped, and unchanged tiles are never sent.

Why are copy tiles only used while frames keep their order?

A copy reads the frame as it was before the message: if an older frame arrived after a newer one, the copied pixels would be wrong.

Rien ne bouge sur l'écran de l'hôte. Combien est envoyé ?

Rien : une capture sans nouvelle image est sautée, et les tuiles inchangées ne sont jamais envoyées.

Pourquoi les tuiles copy ne servent-elles que tant que les images gardent leur ordre ?

Une copie lit l'image telle qu'elle était avant le message : si une image plus ancienne arrivait après une plus récente, les pixels copiés seraient faux.

7. GlossaryGlossaire

TermMeaning
Tilea 64×64 square of the screen, the unit that is hashed and sent
Ackthe viewer telling the host a frame arrived
Frames in flightframes sent but not acked yet
Parityan xor of 8 pieces that rebuilds one lost piece without asking again
Lanean extra websocket that only carries video frames

Sources: src/xdesk/platform/FrameEncoder.cpp, src/xdesk/host/Stream.cpp, include/xdesk/net/Datagram.hpp; the measures come from netem emulated links.

TermeSens
Tuileun carré de 64×64 de l'écran, l'unité hachée et envoyée
Ackle viewer qui dit à l'hôte qu'une image est arrivée
Images en volimages envoyées mais pas encore acquittées
Paritéun xor de 8 morceaux qui reconstruit un morceau perdu sans le redemander
Laneune websocket supplémentaire qui ne transporte que des images

Sources : src/xdesk/platform/FrameEncoder.cpp, src/xdesk/host/Stream.cpp, include/xdesk/net/Datagram.hpp ; les mesures viennent de liens émulés avec netem.