Coin web de Frédéric Péters

fpeters@0d.be

Signal audio basse latence (édition 2025)

19 avril 2025, 19:37

À la radio, on a l’antenne pour la diffusion FM sur un bâtiment situé à un peu moins de deux kilomètres des studios, et entre les deux il y a une connexion wifi qui tient bon depuis 2009. Pendant les premières années le signal était simplement récupéré du stream audio de la radio, ce qui amenait un décalage de quelques secondes, assez perceptible quand depuis les studios on voulait vérifier la transmission.

En 2014 on a fait évoluer ça, je notais à l’époque dans nos échanges techniques,

Ce peut être netjack avec opus, solution évoquée lors de la réunion, mais j'ai regardé depuis et la situation est un peu complexe, jackd1 prend en charge CELT, qui est le codec dont Opus est dérivé, mais le paquet dans Debian n'est pas compilé avec cette option (CELT étant considéré comme obsolète), du côté de jackd2 on a la difficulté qu'on utilise jackd1 partout ailleurs et que l'interfaçage réseau n'est pas le même, et puis la gestion d'Opus nécessite une bibliothèque Opus gérant les "custom modes", ce qui n'est pas le cas, cf bug 686777 dans Debian.

Une autre possibilité est de voir côté OpenOB, dont je parlais en juillet dernier; comme c'est basé sur GStreamer, ça devrait être assez souple à adapter.

Et c’est finalement cette dernière option qui a été prise, avec une grande satisffaction puisqu’OpenOB a assuré le job depuis.

Mais la dernière version date de 2019 et pour ne rien risquer le système n’a pas été mis à jour depuis, et notamment tout tournait encore avec Debian 10 (la version sortie en 2019).

Fast forward to ce matin, je me suis dit qu’il était temps de prendre ce risque et j’ai fait les mises à jour vers Debian 12. Et le son s’est mis à saccader, ouch.

En solution de repli j’ai posé la simple diffusion du stream mais c’était donc revenir plus de dix ans en arrière, avec une latence pénible entre les studios et la diffusion.

J’ai regardé ce qui pouvait avoir évolué depuis pour la transmission d’audio basse latence sur du wifi et résultat pas grand chose, la page "How do I use JACK over a network?" parle toujours des deux versions de netjack (le bug 686777 dans Debian a été corrigé depuis, mais on utilise toujours jackd1 donc ça ne fait pas vraiment une possibilité); il y a aussi jack.trip mais ça n’est pas prévu pour du wifi, et ça termine par un section zita-j2n qui est juste marquée "TODO".

C’était intrigant, j’ai essayé zita-j2n mais je ne suis pas allé plus loin qu’une synchronisation qui n’arrivait pas à se faire.

Là je me suis dit que je pouvais essayer le debug d’OpenOB et peut-être commencer par utiliser les éléments de base de GStreamer (le "framework" multimédia utilisé) pour reconstituer en local une transmission.

Ça faisait bien longtemps que je n’avais plus utilisĂ© GStreamer et j’ai Ă©tĂ© Ă  deux doigts d’appeler Ă  l’aide mais in fine j’ai rĂ©ussi en local, la transmission :

gst-launch-1.0 -v \
	audiotestsrc wave=sine ! \
	audioconvert ! \
	opusenc ! \
	rtpopuspay ! \
	udpsink host=127.0.0.1 port=5200

et la rĂ©ception :

gst-launch-1.0 -v \
	udpsrc port=5200  !
	"application/x-rtp, media=(string)audio, clock-rate=(int)48000, encoding-name=(string)OPUS, sprop-stereo=(string)0, encoding-params=(string)2, payload=96" ! \
	rtpopusdepay ! \
	opusdec ! \
	audioconvert ! \
	autoaudiosink sync=false

avec la seule grosse galère le moment où il a fallu écrire la ligne "application/x-rtp(...)", sans quoi il y avait ce message,

ERREUR : de l’élĂ©ment /GstPipeline:pipeline0/GstCapsFilter:capsfilter0 : Filter caps do not completely specify the output format

(de manière utile le côté transmission affiche l’information, mais elle est un peu perdue dans le contenu verbeux)

Une fois que j’ai eu ça, j’ai pu attendre une pause dans les Ă©missions de la journĂ©e pour tester en vrai ce que ça donnait, dans une version lĂ©gèrement adaptĂ©e pour utiliser jack pour l’entrĂ©e/sortie audio, ce qui se fait juste en remplaçant le premier (resp. le dernier) Ă©lĂ©ment de la commande; pour la transmission :

jackaudiosrc client-name=panik_signal_transmitter name=pst connect=0

et pour la rĂ©ception :

jackaudiosink client-name=panik_signal_receiver name=psr connect=0

Et ça marche sans les saccades qu’il y avait avec OpenOB alors que d’après ce que je lis du code ça fait quelque chose de très similaire. Mais arrivé là, plutôt qu’enchaîner à chercher la petite différence dans OpenOB, je me suis dit que ça pouvait très bien tourner comme ça, j’ai donc mis en place le nécessaire pour que ça démarre automatiquement, etc.

Clairement ça n’a pas la finition d’OpenOB, tout est écrit en dur; ça me laisse trois pistes, 1/ prendre un peu de temps sur OpenOB et voir ce qui y coince, 2/ ou transformer mon petit script shell en petit script Python pour gérer quelques options, ou 3/ juste laisser en l’état puisque ça marche, désormais sur un système tout à fait à jour, ce qui était l’objectif en début de journée.

Avant ça :
🦥