Diffuser le contenu d’un ordinateur bruyant vers une Raspberry silencieuse

Introduction

D’un côté, j’ai un ordinateur au format tour plutôt encombrant, un peu bruyant et assez puissant pour jouer à mes jeux.

D’un autre côté, j’ai une Raspberry Pi 5B petite, silencieuse, mais pas assez performante pour jouer à tous mes jeux.

L’idée de cet article est de combiner ces deux machines pour tirer le meilleur de chacune d’elles pour une séance jeu au salon.

Comme elle est discrète et silencieuse, la Raspberry Pi est installée au salon, branchée à la télévision et à une manette de jeu (la mienne est filaire, je n’ai pas essayé le Bluetooth).

Quant à ma tour, elle restera dans à sa place dans mon bureau, avec la porte fermée pour ne rien entendre.

Les deux machines vont collaborer de cette manière : la tour va faire les calculs avec la carte graphique et suivre les ordres envoyés par la manette de la Raspberry.

Comme elles travaillent sur le même réseau, les performances sont bonnes et il n’y a pas besoin d’une troisième machinée hébergée dans un cloud pour les aider à communiquer ensemble. Le principe est finalement le même que celui du Cloud Gaming, mais mon activité reste privée et je ne suis pas cantonné uniquement aux jeux : si je branche un clavier et une souris, je peux piloter complètement la tour depuis la Raspberry Pi.

Sunshine / Moonlight

La communauté libre a déjà développé les outils pour gérer cette situation avec les projets Sunshine et Moonlight. Le premier doit être installé sur l’ordinateur qui diffuse le contenu (dans mon exemple, la tour) et le second sur celui qui l’affiche (dans mon exemple, la Raspberry Pi).

Pour les installer, il faut suivre leur documentation respective, elles sont bien détaillées et donnent les explications pour la plupart des distributions Linux.

D’une fois que ça a marché, je me suis demandé si je pouvais faire en sorte que la Raspberry Pi démarre directement Moonlight sans passer par l’écran de connexion.

C’est en effet possible, avec les services systemd. D’abord, il faut désactiver le démarrage automatique des services LigthDM et getty@tty1 pour pouvoir dédier la console tty1 à Moonlight :

sudo systemctl disable lightdm
sudo systemctl disable getty@tty1

Ensuite, j’ai créé ce service pour démarrer Moonlight avec mon compte utilisateur et l’attacher à la première console :

Fichier /etc/systemd/system/moonlight.service
[Unit]
Description=Moonlight streaming game
After=network-online.target pipewire.service
After=dev-dri-card1.device
Requires=dev-dri-card1.device
OnFailure=getty@tty1.service

[Service]
User=adrien
Environment=XDG_RUNTIME_DIR=/run/user/1000
Environment=PULSE_SERVER=unix:/run/user/1000/pulse/native
Environment=QT_QPA_EGLFS_KMS_CONFIG=/etc/moonlight_eglfs.json
ExecStart=/usr/bin/moonlight-qt
Restart=on-failure
StandardInput=tty
StandardOutput=journal+console
TTYPath=/dev/tty1

[Install]
WantedBy=default.target

La variable d’environnement XDG_RUNTIME_DIR est nécessaire pour indiquer à Moonlight où placer les fichiers temporaires liés à l’utilisateur. Comme Debian ne configure cette variable uniquement pour les sessions interactives 1, je suis allé au plus simple et j’ai décidé d’initialiser directement la variable dans le service systemd, mais il aurait été sûrement possible de configurer PAM pour ce service.

La variable d’environnement PULSE_SERVER permet d’annoncer à Moonlight que le service audio est disponible à partir de ce socket2.

La dernière variable d’environnement QT_QPA_EGLFS_KMS_CONFIG dit à Moonlight qu’il existe une configuration précise pour choisir l’écran à utiliser3 et qu’il ne doit pas essayer de le trouver automatiquement. Sans cette information, Moonlight s’arrête parce qu’il ne trouve pas d’écran, comme expliqué dans ce rapport de bug (le correctif lié à ce rapport ne semble pas suffire dans le cas de ce service systemd).

Le contenu du fichier est {"device":"/dev/dri/card1"} et il indique l’écran à utiliser pour l’affichage.

Comme l’affichage est vraiment nécessaire pour Moonlight, j’ai également annoncé à systemd que ce service nécessite cette carte via les instructions After et Requires pour le périphérique dev-dri-card1.device. Ainsi, tant que la carte n’est pas prête, systemd ne va pas démarrer Moonlight.

Enfin, la dernière partie intéressante est l’attachement du service au terminal tty1 pour afficher Moonlight directement à la fin du processus de démarrage. Pour ce faire, il faut utiliser les directives TTYPath, StandardInput et StandardOutput.

Si Moonlight n’arrive pas à démarrer, aucune action ne sera possible, parce que le gestionnaire par défaut de la console tty1 et le gestionnaire de connexion LightDM ont été désactivé. Pour éviter d’être dans ce cas, j’ai configuré systemd pour démarrer le service getty@tty1.service en cas d’échec (mot clé OnFailure). Si vous êtes plus à l’aise avec une interface graphique, vous pouvez y mettre lightdm.service à la place.

Finalement, il faut dire à systemd de démarrer ce service au démarrage:

sudo systemctl daemon-reload
sudo systemctl enable moonlight

Et voilà, au prochain redémarrage, Moonlight devrait se lancer automatiquement pour vous laisser jouer 🎮.

État de l’installation de Moonlight sur Raspberry Pi Bookworm

Je m’apprête à réinstaller ma Raspberry Pi avec la mise à jour qui utilise Debian Trixie.

Comme Raspberry Pi OS recommande de refaire une installation à neuf, je copie ici le reste de mon installation de Moonlight.

Fichier /etc/apt/sources.list.d/moonlight.list
# Source : Moonlight Game Streaming
# Site : https://github.com/moonlight-stream/moonlight-qt
# Repository : Moonlight Game Streaming / Moonlight
# Description : Open-source NVIDIA GameStream client

deb [signed-by=/usr/share/keyrings/moonlight.gpg] https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-qt/deb/raspbian bookworm main

deb-src [signed-by=/usr/share/keyrings/moonlight.gpg] https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-qt/deb/raspbian bookworm main
Récupération de la clé GPG de Moonlight, /usr/share/keyrings/moonlight.gpg
$ wget https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-qt/gpg.2F6AE14E1C660D44.key
$ gpg --dearmor gpg.2F6AE14E1C660D44.key
$ sudo cp gpg.2F6AE14E1C660D44.key.gpg /usr/share/keyrings/moonlight.gpg
Autres commandes exécutées lors de l’installation de Moonlight.
$ sudo apt install moonlight-qt
$ sudo apt install pipewire
$ sudo apt remove pulseaudio

Enfin, j’ai commenté le fichier /etc/sudoers.d/010_pi-nopasswd, parce que je préfère que sudo me demande mon mot de passe avant d’exécuter des commandes privilégiées. J'avais aussi modifié les préférences depuis le centre de contrôle pour ne pas ouvrir ma session automatiquement.

Notes

  1. Debian configure le système d’authentification PAM pour charger, uniquement pour les sessions interactives, le module pam_systemd.so qui initialise la variable XDG_RUNTIME_DIR. Dans le cas d’un service exécuté par systemd, la session est considérée comme non-interactive, alors ce système ne s’active pas. 

  2. Je n’ai pas trouvé quelle partie du système se charge d’habitude d’initialiser cette variable. Ce n’est pas important de savoir qui se charge d’initialiser cette variable, car Debian a configuré un service systemd utilisateur nommé pipewire-pulse.socket qui écoute le socket et démarre le serveur audio dès qu’un logiciel en a besoin. 

  3. Le KMS est un système où les modes d’affichage sont gérés par le noyau Linux. 

Mises à jour

Hier j’ai essayé de faire cette installation avec la dernière version de Raspberry OS crée sur Debian Trixie.

J’ai remarqué que j’avais oublié de documenter le besoin de désactiver le service getty@tty1.service qui occupe la console tty1 par défaut.

J’ai découvert le nouveau format pour décrire les sources logicielles pour APT, je le trouve bien plus lisible.

Version de /etc/apt/sources.list.d/moonlight.sources correspondant au fichier moonlight.list ci-dessus:
# Source : Moonlight Game Streaming
# Site : https://github.com/moonlight-stream/moonlight-qt
# Repository : Moonlight Game Streaming / Moonlight
# Description : Open-source NVIDIA GameStream client

Types: deb
URIs: https://dl.cloudsmith.io/public/moonlight-game-streaming/moonlight-qt/deb/raspbian
Suites: trixie
Components: main
Signed-By: /usr/share/keyrings/moonlight.gpg

Enfin, j’hésite à réinstaller l’ancienne version de Raspberry Pi OS (celle qui utilise Debian Bookworm), parce que Moonlight m’affiche un avertissement m’indiquant qu’il n’arrive pas à utiliser le matériel pour décoder le flux vidéo. Ça ressemble à ce rapport de bug et il faudrait que je m’assure que Moonlight utilise bien le ffmpeg fournit par Raspberry Pi OS (le rapport de bug informe que le problème existait aussi avec Bookworm, je ne m’en souviens pas).

J’ai trouvé le problème avec mon installation sur Trixie: je n'ai pas suivi la documentation de Moonlight et j'ai donc installé la mauvaise version de Moonlight.

Je n'ai délibérément pas suivi la documentation officielle parce qu'elle demande de télécharger un script bash par curl et de l'exécuter directement avec les droits d'administration.

J'avais téléchargé le script et je l'ai lu pour retrouver comment il ajoute la source APT dans Debian et la clé GPG liée.

Je n'avais pas fait attention que la documentation paramètre le nom de code de la distribution avec raspbian et non pas debian (elle se trouve à la fin de l'URI de la source).

J'ai désinstallé l'ancien paquet, mis à jour la source APT et installé le nouveau paquet adapté à la Raspberry Pi.

Maintenant, le démarrage de Moonlight se passe bien et je n'ai plus d'avertissement à propos du décodage vidéo manquant. Il ne me reste plus qu'à jouer pour confirmer que c'est bon, mais ce sera demain ;)

J'ai mis à jour l'article avec la bonne source partout.