Utiliser mpv piloté en tâche de fond est en effet la meilleure solution possible pour ce projet. mpv prend en charge nativement le décodage matériel GPU, les flux RTSP à très faible latence et les fichiers MP4 locaux, tout en proposant une API de contrôle extrêmement puissante.
Cependant, il y a une clarification importante sur l’architecture : Le lecteur vidéo (mpv) doit tourner sur un nano-ordinateur sous Linux avec sortie HDMI (type Raspberry Pi, Orange Pi, ou un petit PC Mini-ITX / NUC).
[ Ordinateur / Raspberry Pi ]
┌────────────────────────────────────────────────────────┐
│ Script Python (Client MQTT) │
│ │ │
│ ▼ (IPC Socket JSON) │
│ Lecteur mpv ───► [ Sortie HDMI ] ───► [ Écran ] │
└────────────────────────────────────────────────────────┘
Plutôt que de lancer et fermer l’application mpv à chaque commande (ce qui provoque des écrans noirs et des clignotements HDMI), l’astuce consiste à lancer mpv une seule fois au démarrage en mode démon (idle), puis à lui envoyer des commandes en temps réel via sa socket IPC JSON.
Sur votre machine Linux branchée en HDMI, installez les dépendances :
sudo apt update && sudo apt install mpv python3-paho-mqtt
Voici le script Python complet à exécuter au démarrage. Il écoute les commandes MQTT et pilote mpv via sa socket IPC :
import json
import socket
import time
import paho.mqtt.client as mqtt
# Configuration
MQTT_BROKER = "192.168.1.50" # Adresse de votre broker MQTT
MQTT_TOPIC_CMD = "display/hdmi/cmd"
MQTT_TOPIC_STATUS = "display/hdmi/status"
SOCKET_PATH = "/tmp/mpvsocket"
SOURCES = {
"camera": "rtsp://admin:password@192.168.1.100:554/stream1",
"video": "/home/pi/videos/ma_video.mp4"
}
current_source = "video"
def send_mpv_command(command_list):
"""Envoie un ordre JSON à la socket IPC de mpv."""
try:
client = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM)
client.connect(SOCKET_PATH)
data = json.dumps({"command": command_list}) + "\n"
client.sendall(data.encode('utf-8'))
response = client.recv(1024)
client.close()
return json.loads(response.decode('utf-8'))
except Exception as e:
print(f"Erreur socket MPV: {e}")
return None
def publish_status(mqtt_client, state):
payload = json.dumps({"state": state, "source": current_source})
mqtt_client.publish(MQTT_TOPIC_STATUS, payload, retain=True)
def on_message(client, userdata, msg):
global current_source
payload = msg.payload.decode('utf-8').strip().lower()
print(f"Commande reçue : {payload}")
if payload == "start":
url = SOURCES.get(current_source)
send_mpv_command(["loadfile", url, "replace"])
publish_status(client, "playing")
elif payload == "stop":
send_mpv_command(["stop"])
publish_status(client, "stopped")
elif payload.startswith("select:"):
source_name = payload.split(":")[1]
if source_name in SOURCES:
current_source = source_name
# Si déjà en cours de lecture, bascule immédiatement
url = SOURCES[current_source]
send_mpv_command(["loadfile", url, "replace"])
publish_status(client, "playing")
elif payload == "status":
res = send_mpv_command(["get_property", "pause"])
is_idle = send_mpv_command(["get_property", "idle-active"])
state = "stopped" if (is_idle and is_idle.get("data")) else "playing"
publish_status(client, state)
# Configuration du client MQTT
client = mqtt.Client(mqtt.CallbackAPIVersion.VERSION2)
client.on_message = on_message
client.connect(MQTT_BROKER, 1883, 60)
client.subscribe(MQTT_TOPIC_CMD)
print("Lancement du contrôleur MQTT-mpv...")
client.loop_forever()
Pour que le script fonctionne, mpv doit tourner avec les bonnes options réseau et d’affichage.
Commande de lancement de mpv :
mpv --idle=yes \
--input-ipc-server=/tmp/mpvsocket \
--fullscreen \
--no-osc \
--no-osd-bar \
--rtsp-transport=tcp \
--profile=low-latency \
--untimed
Explications des paramètres clés :
–idle=yes : Le lecteur reste ouvert sur un écran noir même s’il n’y a pas de vidéo chargée.
–input-ipc-server=/tmp/mpvsocket : Ouvre le canal de communication pour le script Python.
–profile=low-latency & --untimed : Indispensable pour la caméra IP RTSP afin de supprimer la mise en mémoire tampon (buffer) et d’avoir le flux en direct sans décalage.
–rtsp-transport=tcp : Force le flux RTSP en TCP pour éviter les sauts d’image ou artefacts en Wi-Fi.
Choix de la carte :
--stream-lavf-o=reconnect=1,reconnect_streamed=1,reconnect_delay_max=2 dans la commande mpv pour qu’il se reconnecte automatiquement en cas de perte de signal sans faire planter le lecteur.Pour automatiser le lancement au démarrage, la meilleure méthode consiste à créer deux services systemd au niveau utilisateur (user services). Cela permet à mpv d’accéder au serveur d’affichage graphique (X11 ou Wayland) et au serveur audio sans problème de permissions.
Pour qu’un service systemd puisse afficher de la vidéo sur l’écran HDMI, la session graphique doit être active.
Activez la connexion automatique (autologin) de votre utilisateur principal (ex: pi) via l’outil de votre distribution (ex: sudo raspi-config sur Raspberry Pi, ou via votre gestionnaire de connexion GDM/LightDM).
mkdir -p ~/.config/systemd/user
nano ~/.config/systemd/user/mpv-player.service
[Unit]
Description=Lecteur MPV en mode démon (IPC Socket)
After=graphical-session.target
[Service]
Type=simple
Environment=DISPLAY=:0
ExecStart=/usr/bin/mpv --idle=yes --input-ipc-server=/tmp/mpvsocket --fullscreen --no-osc --no-osd-bar --rtsp-transport=tcp --profile=low-latency --untimed
Restart=always
RestartSec=5
[Install]
WantedBy=graphical-session.target
Créez le fichier de service mqtt-control.service :
nano ~/.config/systemd/user/mqtt-control.service
Collez la configuration suivante (adaptez le chemin vers votre script Python) :
[Unit]
Description=Contrôleur MQTT pour MPV
# Attend que le service MPV soit prêt avant de se lancer
After=mpv-player.service
Requires=mpv-player.service
[Service]
Type=simple
ExecStart=/usr/bin/python3 /home/pi/scripts/mqtt_display.py
Restart=always
RestartSec=5
[Install]
WantedBy=graphical-session.target
Rechargez la configuration de systemd pour l’utilisateur :
systemctl --user daemon-reload
Activez les services pour qu’ils se lancent automatiquement au démarrage de la session :
systemctl --user enable mpv-player.service mqtt-control.service
Démarrez-les immédiatement pour tester :
systemctl --user start mpv-player.service mqtt-control.service
Pour vous assurer que systemd démarre vos services utilisateur dès le boot de la machine (sans attendre qu’une session SSH ne s’ouvre), activez l’option linger pour votre utilisateur :
sudo loginctl enable-linger $USER
Vérifier le statut des services :
systemctl --user status mpv-player.service
systemctl --user status mqtt-control.service
Consulter les logs en temps réel (débug) :
journalctl --user -u mqtt-control.service -f
Redémarrer le contrôleur Python :
systemctl --user restart mqtt-control.service