Aller au contenu
En production Phare

Téléop

Téléopération temps réel

Logiciel de téléopération temps réel : capture l'écran d'appareils Linux embarqués, l'encode en H.264 et le diffuse en QUIC (TLS 1.3) vers des techniciens, avec injection clavier/souris en retour.

~27 ms latence RTT mesurée
QUIC TLS 1.3 · mTLS
~34k lignes Rust · 6 crates
flux écran temps réel ~27 ms latence RTT bout-en-bout 100 % Rust · 6 crates vidéo ▸ ▸ H.264 ↩ voie retour · clavier / souris APPAREIL Linux embarqué capture X11 → I420 · NEON → H.264 V4L2 RELAIS CLOUD transport sécurisé QUIC TLS 1.3 · mTLS FEC · congestion TECHNICIEN navigateur WebTransport repli WebSocket décode H.264
Chaîne complète appareil → relais → technicien : capture X11 incrémentale, conversion BGRX→I420 en SIMD NEON, encodage H.264 matériel, transport QUIC (TLS 1.3, mTLS, FEC). La voie retour injecte clavier et souris. ~27 ms RTT mesurés à 1 Mbps, 0 ‰ de perte.

Le problème

Téléopérer un appareil n’a rien à voir avec diffuser une vidéo : on ferme une boucle perception-action où l’opérateur réagit, en continu, à ce qu’il voit à l’écran. Passé un seuil de latence de l’ordre de la centaine de millisecondes, l’interaction se dégrade et les erreurs de manipulation se multiplient. L’enjeu n’est donc pas « une image fluide » mais une latence de bout en bout maîtrisée, et tenue sur les réseaux que l’on rencontre vraiment sur le terrain : 4G partagée, Wi-Fi d’entreprise filtré, débit variable, pertes sporadiques.

Les cibles sont des appareils Linux embarqués, souvent en aarch64, sans GPU de bureau ni budget CPU confortable. Tout le pipeline (capture, conversion, encodage, transport) doit rester économe et prévisible sur ce matériel.

L’architecture du flux

Trois maillons composent la chaîne : l’appareil capture et encode son écran, un relais cloud assure le rendez-vous et le passage des pare-feux, le poste du technicien décode le flux et renvoie le clavier et la souris. J’ai écrit l’ensemble en Rust (édition 2024), sans dépendance C hormis la libc. Deux raisons à ce choix : des binaires statiques simples à cross-compiler vers aarch64, et la sûreté mémoire sur un chemin réseau qui traite des données potentiellement hostiles.

La capture, côté appareil

Réencoder l’écran entier à chaque trame coûterait trop cher sur la cible. La capture s’appuie sur XDamage pour ne récupérer que les régions effectivement modifiées d’une trame à l’autre : sur une interface de supervision, où l’écran reste statique l’essentiel du temps, cela réduit fortement le travail soumis à l’encodeur.

Le flux capturé est en BGRX, là où l’encodeur H.264 attend du I420. Cette conversion s’applique à chaque pixel modifié : c’est la boucle chaude qui fixe le budget CPU, je l’ai donc vectorisée en SIMD NEON pour la tenir sur l’embarqué. L’encodage passe ensuite par V4L2 et l’accélération matérielle de la plateforme, là où un encodeur logiciel saturerait le processeur.

Le transport, cœur du projet

Le choix du transport décide de la latence. En TCP, une seule perte bloque tout ce qui suit le temps de la retransmission, le head-of-line blocking, ce qui produit exactement les à-coups insupportables en téléopération. J’ai retenu QUIC, via quinn : TLS 1.3 intégré, flux multiplexés indépendants, et migration de connexion lorsque le réseau change sous l’opérateur.

En temps réel, une trame en retard ne vaut plus rien ; mieux vaut l’abandonner que la rejouer. Une couche de FEC répare les pertes isolées sans aller-retour, et un retour de congestion pilote l’encodeur : quand le lien se dégrade, on baisse le débit au lieu d’accumuler de la latence dans des files d’attente. Le visionnage se fait au navigateur en WebTransport, avec un repli WebSocket pour les réseaux d’entreprise qui bloquent QUIC sur UDP.

Robustesse et sécurité

Un décodeur alimenté par le réseau doit survivre à n’importe quelle entrée. Son chemin est sans panique : un paquet malformé est rejeté, jamais une cause de crash. Le unsafe strictement nécessaire (FFI de l’encodeur, conversions de format) est confiné à un seul module documenté et audité ; tout le reste demeure en Rust sûr. L’authentification est mutuelle (mTLS) : appareil et opérateur présentent chacun leur certificat, ce qui écarte d’emblée un relais ou un client illégitime.

Pour valider tout cela sans dépendre d’un terrain réel, j’ai construit un banc d’émulation réseau déterministe qui rejoue des conditions de perte, de RTT et de jitter reproductibles, doublé d’une suite de régression « golden-trace » : chaque évolution est comparée à des traces de référence, ce qui attrape une régression de latence ou de qualité avant qu’elle n’atteigne la production. La cross-compilation aarch64 tourne en intégration continue.

Résultat

En production chez CRE Technology. Sur le déploiement testé, la chaîne appareil → relais → technicien tient une latence RTT de l’ordre de 27 ms à 1 Mbps, sans perte observée. Environ 34 000 lignes de Rust réparties en 6 crates. Code privé.