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
- First frame. The viewer has nothing yet: every 64×64 tile is sent.
- Hash. Something moves: the tiles are hashed and 3 of the 32 differ from what the viewer has.
- 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).
- 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.
- 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.
- Still. The screen stops moving: the same tiles are sent again, sharp.
- Première image. Le viewer n’a encore rien : toutes les tuiles de 64×64 sont envoyées.
- Hash. Quelque chose bouge : les tuiles sont hachées et 3 sur 32 diffèrent de ce qu’a le viewer.
- 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).
- 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.
- 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.
- Immobile. L’écran ne bouge plus : les mêmes tuiles sont renvoyées, nettes.
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 + TLS | UDP + GCM + parity |
|---|---|---|
| LAN cable | 30 fps, p95 4.9 ms | 30 fps, p95 13 ms (Python prototype) |
| Fiber, 40 ms | 30 fps, p99 69 ms | 30 fps, p99 50 ms |
| 4G, 80 ms, 1 % loss | 27 fps, p95 163 ms | 27 fps, p95 160 ms |
| 160 ms, 3 % loss, 6 Mbit/s | 17 fps, 15 stalls | 23 fps, 4 stalls |
| 300 ms, 8 % loss, 2 Mbit/s | 5.4 fps, p50 506 ms | 7.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 socket | 2 | 3 | 4 |
|---|---|---|---|---|
| LAN, 2 ms, 300 Mbit/s | 286 | 290 | 293 | 292 |
| 80 ms, 1 %, 30 Mbit/s | 2.5 | 5.8 | 7.1 | 10.1 |
| 160 ms, 3 %, 6 Mbit/s | 0.9 | 1.4 | 2.0 | 3.2 |
| 300 ms, 8 %, 2 Mbit/s | 0.4 | 0.6 | 1.1 | 1.3 |
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 + TLS | UDP + GCM + parité |
|---|---|---|
| LAN câble | 30 fps, p95 4.9 ms | 30 fps, p95 13 ms (prototype Python) |
| Fibre, 40 ms | 30 fps, p99 69 ms | 30 fps, p99 50 ms |
| 4G, 80 ms, 1 % de perte | 27 fps, p95 163 ms | 27 fps, p95 160 ms |
| 160 ms, 3 % de perte, 6 Mbit/s | 17 fps, 15 blocages | 23 fps, 4 blocages |
| 300 ms, 8 % de perte, 2 Mbit/s | 5.4 fps, p50 506 ms | 7.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 socket | 2 | 3 | 4 |
|---|---|---|---|---|
| LAN, 2 ms, 300 Mbit/s | 286 | 290 | 293 | 292 |
| 80 ms, 1 %, 30 Mbit/s | 2.5 | 5.8 | 7.1 | 10.1 |
| 160 ms, 3 %, 6 Mbit/s | 0.9 | 1.4 | 2.0 | 3.2 |
| 300 ms, 8 %, 2 Mbit/s | 0.4 | 0.6 | 1.1 | 1.3 |
5. PitfallsPièges
- Wayland without the PipeWire library falls back to portal screenshots: about 1 frame per second.
- H.264 needs
openh264at run time (libopenh264.so.8, or the Cisco dll next toxdesk.exeon 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é dexdesk.exesous 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
| Term | Meaning |
|---|---|
| Tile | a 64×64 square of the screen, the unit that is hashed and sent |
| Ack | the viewer telling the host a frame arrived |
| Frames in flight | frames sent but not acked yet |
| Parity | an xor of 8 pieces that rebuilds one lost piece without asking again |
| Lane | an 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.
| Terme | Sens |
|---|---|
| Tuile | un carré de 64×64 de l'écran, l'unité hachée et envoyée |
| Ack | le viewer qui dit à l'hôte qu'une image est arrivée |
| Images en vol | images envoyées mais pas encore acquittées |
| Parité | un xor de 8 morceaux qui reconstruit un morceau perdu sans le redemander |
| Lane | une 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.