Pipewire experiments
Introduzione
Ricordate il mio video su Wayland, la prossima rivoluzione su Linux?
Ma oggi,Oggi, voglio parlarvi di un'altra grande rivoluzione, avvenuta silenziosamente... o forse no, dato che parliamo dello "Stack Audio" su Linux.
Ne avevo già accennato un po' di tempo fà con il mio video su PipeWire, il nuovo framework a basso livello per la gestione di flussi audio e video.
Oggi entriamo un po' più nel dettaglio di come funziona "sotto la scocca", e cosa cambia rispetto alle soluzioni che va a rimpiazzare.
Cenni sul pre-pw
OSS
Il supporto audio su Linux è iniziato insieme al kernel con OSS, Open Sound System, che offriva un'interfaccia in puro stile "è tutto un file": le applicazioni OSS utilizzavano /dev/dsp e /dev/audio, esposti dal modulo kernel, per accedere direttamente all'hardware, in modo esclusivo e in formato grezzo.
Niente mixing software, niente volume, niente insomma. Il licensing di OSS ha peraltro seguito un percorso complicato: è iniziato come modulo kernel GPL, poi è stato mantenuto da 4Front Technologies con licenza proprietaria, e solo più recentemente [sì, viene ancora mantenuto nel progetto OSSv4] è diventato a tutti gli effetti un software Open Source [ahahah OSS è tornato ad essere OSS].
ALSA
Subito dopo venne ALSA, Advanced Linux Sound Architecture, tutt'ora mantenuto e utilizzato. ALSA aggiunge un livello di astrazione allo stack audio Linux: le applicazioni possono usare una libreria per parlare con l'interfaccia esposta dal kernel (una API, in gergo tecnico), che a sua volta comunica con l'hardware audio. Supporta hardware MIDI, fino a 8 schede audio simultaneamente e un sistema di plugin che lo rende molto più versatile del predecessore. Già dagli inizi, ALSA rende disponibili anche alcune utility, come aplay e arecord per riprodurre o registrare al volo un file in formato non compresso.
Demo: ALSA riconosce una scheda audio
Qui si può mostrare direttamente la clip della demo successiva.
- su Debian Testing installare i pacchetti
libasound2,alsa-toolsealsa-utils - eseguire
$ aplay -l
Demo: registrazione e playback con aplay/arecord
$ arecord -f cd > prova.wav
$ aplay prova.wav
Ma mettiamo che io voglia avere più applicazioni che riproducono audio nello stesso momento: uno scenario piuttosto normale, e.g. un player musicale e una scheda del browser, o una notifica del sistema.
Demo: (tentativo di) riproduzione di due file audio in contemporanea
- per bypassare il plugin dmix (mixing software), attivo di default, creare il file
/etc/asound.confe scriverci dentro
pcm.!default {
type plug
slave.pcm hw
}
- provare a eseguire
aplay prova.wavin due sessioni parallele
Data la sua vicinanza all'hardware, ALSA presenta ancora parecchie limitazioni, prima tra tutte l'accesso esclusivo a un dispositivo audio: se un'applicazione lo utilizza, le altre non possono. Col tempo si sono visti i primi tentativi di mixing, per permettere a più applicazioni di riprodurre audio contemporaneamente, con risultati però non esattamente brillanti. Ed è qui che entrano in gioco i primi server audio su Linux.
PulseAudio e JACK
La principale novità dei server audio è nell'architettura. C'è un processo principale, il server appunto, che esegue in background, e i diversi client (le applicazioni) vi si connettono per avere accesso alle risorse audio. Il server si interfaccia a sua volta con il backend audio, come ALSA, per parlare con l'hardware. In questo modo c'è un singolo processo che parla con l'hardware, e il problema di mixare insieme l'audio di più applicazioni si sposta nel server audio. Qui si potrebbe aprire tutta una parentesi su come funziona il mixing, cos'è il resampling e perché è un problema, ma sarebbe troppo lungo [vi lascio una spiegazione fatta per bene in descrizione]. Vi basti sapere che combinare audio da diversi processi a livello del server audio produce risultati molto migliori rispetto ad ALSA, con altri vantaggi non indifferenti. È chiaro però che un'architettura di questo tipo è molto più complessa. Le due implementazioni di server audio più diffuse sono PulseAudio e JACK, e non a caso si concentrano su priorità differenti.
PulseAudio è più per il consumer [il comune mortale]. Offre diversi tool grafici per gestire al volo le applicazioni connesse, con funzionalità comode come la regolazione del volume per ogni processo.
Attraverso i moduli, PulseAudio implementa anche qualche funzionalità in più, come il supporto al bluetooth e la creazione di input/output audio virtuali.
Demo: modulo loopback di PA per collegare tra loro un source e un sink
- ottenere il source da usare (e.g. un microfono) dall'output di
$ pactl list sources short
55 alsa_output.pci-0000_05_00.6.HiFi__Speaker__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
56 alsa_input.pci-0000_05_00.6.HiFi__Mic2__source PipeWire s32le 2ch 48000Hz SUSPENDED
57 alsa_input.pci-0000_05_00.6.HiFi__Mic1__source PipeWire s32le 2ch 48000Hz SUSPENDED
4506 alsa_output.pci-0000_05_00.1.HiFi__HDMI4__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4507 alsa_output.pci-0000_05_00.1.HiFi__HDMI3__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4508 alsa_output.pci-0000_05_00.1.HiFi__HDMI2__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4509 alsa_output.pci-0000_05_00.1.HiFi__HDMI1__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4804 bluez_input.80:C3:BA:4B:D9:88 PipeWire float32le 1ch 48000Hz SUSPENDED
4807 bluez_output.80_C3_BA_4B_D9_88.1.monitor PipeWire s24le 2ch 48000Hz RUNNING
e.g. in questo caso sarebbe alsa_input.pci-0000_05_00.6.HiFi__Mic2__source
- ottenere il sink da usare (e.g. altoparlanti/cuffie) dall'output di
$ pactl list sinks short
55 alsa_output.pci-0000_05_00.6.HiFi__Speaker__sink PipeWire s32le 2ch 48000Hz SUSPENDED
4506 alsa_output.pci-0000_05_00.1.HiFi__HDMI4__sink PipeWire s32le 2ch 48000Hz SUSPENDED
4507 alsa_output.pci-0000_05_00.1.HiFi__HDMI3__sink PipeWire s32le 2ch 48000Hz SUSPENDED
4508 alsa_output.pci-0000_05_00.1.HiFi__HDMI2__sink PipeWire s32le 2ch 48000Hz SUSPENDED
4509 alsa_output.pci-0000_05_00.1.HiFi__HDMI1__sink PipeWire s32le 2ch 48000Hz SUSPENDED
4807 bluez_output.80_C3_BA_4B_D9_88.1 PipeWire s24le 2ch 48000Hz RUNNING
e.g. in questo caso sarebbe alsa_output.pci-0000_05_00.6.HiFi__Speaker__sink
- collegarli tra loro (in questo caso ottenendo l'effetto echo/feedback) con
$ pactl load-module module-loopback source=alsa_input.pci-0000_05_00.6.HiFi__Mic2__source sink=alsa_output.pci-0000_05_00.6.HiFi__Speaker__sink
Capite che, soprattutto in ambito professionale, quando si lavora con tante schede audio, magari con filtri e processing intermedio, la gestione di entrate e uscite diventa macchinosa.
Ed è infatti al mondo del pro-audio che si rivolge JACK, un altro sound server incentrato su bassa latenza e gestione patchbay dei dispositivi, ovvero con modello a grafo. A ogni nodo [elemento] del grafo corrisponde un input o un output audio, e le linee tra i nodi ci dicono il percorso che fa il flusso audio: se due nodi sono collegati, l'audio in output dal primo verrà dato in input al secondo, o viceversa.
Demo: grafo di JACK
- mostrare il grafo di JACK con
qjackctl
- spezzone del video vecchio con in cui si mostra la manipolazione del grafo con Carla, per esemplificare la gestione patchbay
PipeWire: l'anello di congiunzione
Da una parte PulseAudio: per tutti, stabile e ben integrato con praticamente tutte le distribuzioni Linux; dall'altra JACK, orientato ai professionisti, più performante ma meno user-friendly. È da questa frammentazione tra consumer audio e pro audio che nasce la necessità di un layer di compatibilità che permetta di gestire in modo intuitivo entrate e uscite, con l'immediatezza di PulseAudio e la bassa latenza e le pro-feature di JACK. PipeWire nasce nel 2015 da un progetto già iniziato in precedenza chiamato PulseVideo, che prometteva di uniformare la gestione audio e video su Linux [tenete a mente questo dettaglio perché ci torneremo]. Ecco come si presenta un grafo di PipeWire.
Demo: grafo PipeWire
Qui si può mostrare direttamente la clip della demo successiva.
- [senza mostrare, lo si riprende dopo] installare
pipewire,pipewire-pulse,pipewire-jack,pipewire-alsaehelvumoqpwgraph - mostrare il grafo di PipeWire aprendo Helvum o
qpwgraph
Se a primo impatto vi sembra simile al grafo di JACK, guardate meglio: qui ci sono nodi PipeWire, nodi ALSA, e se andiamo ad aprire un'applicazione che si appoggia su JACK...
Demo: lancio di una applicazione JACK e cambiamento del grafo PW
- installare e lanciare
qtractor - mostrare i cambiamenti nel grafo pw, con i nuovi nodi e link creati da
qtractor
...la vedremo comparire come nodo JACK. Ci sono anche dispositivi video, ad esempio la mia webcam, ma su questo ci torniamo dopo.
Come funziona
Facciamo un po' di ordine. Quello che stiamo vedendo è PipeWire. O meglio, il grafo di PipeWire, che tecnicamente non viene gestito direttamente da PipeWire, ma da un session manager. Ma andiamo per gradi. L'architettura di PipeWire è simile a quella di PulseAudio: c'è un processo centrale che fa da server chiamato demone, e i vari processi che vi si connettono fanno da client. I client in questo caso possono essere applicazioni PipeWire native, applicazioni PulseAudio o anche JACK, e tutto sarà gestito dallo stesso server di PipeWire [più facile, no?]. Per fare questo la comunicazione client/server di PipeWire viaggia su una Unix socket, e i messaggi seguono un protocollo chiamato PipeWire Native Protocol. Questo può incapsulare un messaggio PulseAudio, ALSA, JACK o chicchessia, e verrà poi decodificato dal modulo apposito caricato dal demone PipeWire. Sì, perché tutto in PipeWire è un modulo, persino il protocollo nativo stesso è gestito attraverso un modulo, che comunica poi al core, che a sua volta parla con il backend ALSA [anche lui un modulo] per arrivare all'hardware. Questa cross-compatibilità di PipeWire con tutti i sistemi audio preesistenti si traduce anche nella facilità di installazione: installo il pacchetto principale e i vari layer di compatibilità e sono pronto.
Demo: installazione dei pacchetti pw e output di pactl
- installare
pipewire pipewire-alsa pipewire-pulse pipewire-jack - mostrare l'output di
pactl list sources shorte/opactl list sinks short
55 alsa_output.pci-0000_05_00.6.HiFi__Speaker__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
56 alsa_input.pci-0000_05_00.6.HiFi__Mic2__source PipeWire s32le 2ch 48000Hz SUSPENDED
57 alsa_input.pci-0000_05_00.6.HiFi__Mic1__source PipeWire s32le 2ch 48000Hz RUNNING
4506 alsa_output.pci-0000_05_00.1.HiFi__HDMI4__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4507 alsa_output.pci-0000_05_00.1.HiFi__HDMI3__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4508 alsa_output.pci-0000_05_00.1.HiFi__HDMI2__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4509 alsa_output.pci-0000_05_00.1.HiFi__HDMI1__sink.monitor PipeWire s32le 2ch 48000Hz SUSPENDED
4804 bluez_input.80:C3:BA:4B:D9:88 PipeWire float32le 1ch 48000Hz SUSPENDED
4807 bluez_output.80_C3_BA_4B_D9_88.1.monitor PipeWire s24le 2ch 48000Hz RUNNING
PipeWire si è configurato come se fosse una scheda audio fisica, e come server PulseAudio a tutti gli effetti, quindi è pronto per scambiare audio in modo trasparente con client nativi ALSA e PulseAudio, che non si accorgeranno neanche della differenza.
Demo: alsamixer mostra una scheda audio pw
- eseguire
alsamixere se necessario switchare scheda audio con F6, fino a mostrare una scheda chiamata PipeWire
Tutto molto bello.
Entità di pw
Sotto la scocca, PipeWire mantiene una lista di oggetti che rappresentano tutte le entità che abbiamo visto nel grafo e le relazioni tra di loro. Vediamo le più importanti con un esempio.
Ho un'applicazione che riproduce dell'audio, per esempio una scheda di Firefox. Per prima cosa, PipeWire crea un nodo per il client, in questo caso il processo di Firefox. In generale, un nodo è una qualunque entità che riproduce o genera audio. Può essere un microfono, una cuffia, una scheda audio [device], o anche un'applicazione [client] come in questo caso. Mettiamo appunto che l'audio venga riprodotto attraverso la scheda audio integrata: per PipeWire, anche questa sarà un nodo. I nodi possono comunicare tra loro attraverso le porte, cioè punti di ingresso o di uscita. Il nodo Firefox avrà quindi una porta per far uscire l'audio, mentre il nodo della scheda audio avrà una porta per far entrare audio negli altoparlanti e un'altra per far uscire l'audio proveniente dal microfono. Tra due porte si può creare un link, in modo che l'audio in uscita da un nodo entri in un altro o viceversa. In questo caso vogliamo che l'audio che esce da Firefox entri nella scheda audio, quindi vogliamo che ci sia un link tra la porta in uscita del nodo di Firefox e la porta in entrata del nodo della scheda audio.
Session management
Vi potreste star chiedendo come fa PipeWire a decidere quali nodi, quali porte e soprattutto quali link creare. Come vi avevo anticipato, PipeWire non gestisce direttamente il grafo: se ne occupa il session manager, che ha il compito di rilevare i dispositivi e i client connessi e configurarli per poterli usare con PipeWire. È un pezzo di software separato da PipeWire ma fondamentale per il suo corretto funzionamento. Gli sviluppatori offrono un'implementazione dimostrativa [un po' come Weston per Wayland] chiamata pipewire-session-manager, e ognuno è libero di utilizzare la propria. Lo standard de facto, almeno sulle distro che ad oggi adottano PipeWire come sistema audio/video di default, è Wireplumber.
Demo: il session manager crea nodi, porte e link quando si collega un nuovo dispositivo
Per darvi un'idea di quello che fa WirePlumber dietro le quinte, teniamo d'occhio l'output di pw-cli mentre Firefox riproduce dell'audio e colleghiamo un nuovo dispositivo audio, in questo caso degli auricolari Bluetooth. Come vedete PipeWire rileva un nuovo device e WirePlumber inizia il suo sporco lavoro di configurazione: si collega a PipeWire come client e crea i nodi per il nuovo dispositivo, e infatti eccoli qui anche nel grafo. Fatemi un attimo fare qualche magheggio in modo che ci logghi solo gli oggetti creati e rimossi [altrimenti non si capisce una ceppa]. Se ora dico al SO di switchare l'output audio al nuovo dispositivo, ecco di nuovo WirePlumber all'opera: ha rimosso i link tra Firefox e il vecchio output e li ha creati con il nuovo output. Tac [si vede anche nel grafo]. Stessa cosa chiaramente quando lo vado a disconnettere. Tac [non ci sono più i nodi nel grafo].
Dato che si occupa di collegare tra loro client e dispositivi audio, il session manager decide anche quali processi possono avere accesso a quali risorse: in pratica implementa un sistema di permessi, molto più granulare rispetto a PulseAudio e JACK.
Il modello di PulseAudio è una sorta di fiducia implicita: c'è un'istanza per ogni utente e tutti i processi lanciati da quell'utente possono accedere a tutte le risorse. JACK è ancora più liberale perché il focus è sulla performance, quindi ognuno fa un po' quello che vuole quando si tratta di riprodurre o registrare.
PipeWire tenta di superare questo meccanismo "sulla fiducia" implementando un vero e proprio sistema di permessi, perché quando si tratta di accedere alle risorse di sistema, è sempre meglio non fidarsi di nessuno. Sapete a chi voi non dovreste mai dare la vostra fiducia, implicita o esplicita? A una rete non protetta, o al vostro ISP poco discreto, ed è per questo che esiste NordVPN che ha sponsorizzato questa porzione di video.
Diversamente dai suoi cugini più datati, in PipeWire ogni client può avere o non avere il permesso di accedere a ogni singolo oggetto. Questo almeno in teoria. In pratica, allo stato attuale, ci sono solo due casi: accesso diretto, senza restrizioni, che funziona un po' come PulseAudio ["sulla fiducia"], e accesso da sandbox. Sì perché, ora che ci sono i permessi di mezzo, come fanno le applicazioni in sandbox come Snap e Flatpak ad accedere alle risorse audio/video? È per risolvere questo problema che gli sviluppatori di PipeWire hanno introdotto il supporto ai portals, letteralmente "portali" che danno accesso a svariate risorse del SO, dal filesystem al network, fino alla cattura dello schermo, che altrimenti non sarebbe possibile.
Un portal è un servizio che esegue nel SO e che fa da ponte tra una app in sandbox e, in questo caso, il server di PipeWire. In questo modo le app isolate sanno sempre di doversi rivolgere al portal, e quest'ultimo può gestire come vuole i permessi, consentendo o negando l'accesso alle risorse a ciascuna applicazione.
PipeWire per il video
Ritroviamo il meccanismo dei portals anche per la gestione del video in PipeWire. Come ricorderete, infatti, uniformare audio e video era un po' il pallino degli sviluppatori, almeno agli albori del progetto. Già nel grafo noterete che, in effetti, un nodo per la webcam io lo vedo. Vediamo un attimo come funziona accederci tramite portal.
Demo: c'è un nodo webcam nel grafo
- aprire Helvum/qpwgraph e ricordare la presenza di un nodo relativo a una webcam collegata al PC, inizialmente non connesso ad altri nodi
Prendiamo Firefox per esempio [Firefox è nativa, non esegue in sandbox, ma può essere configurato per accedere a webcam e schermo tramite portal, quindi ce lo facciamo andare bene per questo esempio].
Demo: Firefox chiede il permesso di usare la webcam
- [senza mostrare, lo si riprende dopo] visitare
about:configin FF e impostare la variabilemedia.webrtc.camera.allow-pipewireatrue - visitare una pagina web che richiede l'accesso alla webcam, come quella di test di Mozilla [linkata]
- notare che viene richiesto il permesso all'uso di una webcam
- tornare su Helvum/qpwgraph e notare il collegamento del nodo webcam a FF
Quando una pagina richiede l'accesso alla webcam, Firefox fa una richiesta D-Bus al camera portal [una specie di mega socket condivisa da tutti i processi su Linux, semplificando al massimo]: prima chiede quali siano le webcam disponibili, e poi chiede di poterci accedere. Se l'utente accetta, il portal comunica a PipeWire di creare un link tra il nodo della webcam e Firefox, in modo che quest'ultimo riceva lo stream video. E il bello è che se apro un'altra scheda, posso fare la stessa cosa un'altra volta.
Demo: posso aprire un'altra scheda e avere accesso simultaneo alla webcam da due schede
- aprire la stessa pagina web su un'altra scheda e notare come ho accesso alla webcam su entrambe
PipeWire si occupa di sdoppiare il video e distribuirlo ai nodi connessi, esattamente come fa per l'audio, ed esattamente come ci aspetteremmo che fosse normale (!). I permessi sono gestiti dal portal, che fa da mediatore tra il client (Firefox) e la risorsa richiesta (la webcam), chiedendo conferma all'utente prima di concedere l'accesso.
Se vi state chiedendo come mai voi osserviate un comportamento diverso se provate a fare la stessa cosa, ve lo spiego subito. Il supporto per la camera via PipeWire è ancora opt-in in Firefox, così come in Chrome/Chromium.
Demo: come impostare FF per usare pw per la webcam
- mostrare come si imposta la variabile
media.webrtc.camera.allow-pipewireatrueinabout:config, come sopra
Questione diversa per la condivisione dello schermo, dove PipeWire è invece già il default, poiché è l'unico modo per convidividere lo schermo sui sistemi che utilizzano Wayland. Anche in questo caso l'accesso avviene tramite portal, stavolta un desktop portal, che tra le altre cose gestisce la possibilità di catturare lo schermo.
[opzionale] Demo: condivisione schermo con FF
- mostrare brevemente la condivisione dello schermo, sempre con la stessa pagina di test di Mozilla su FF
Qui purtroppo si apre una parentesi piuttosto ampia sulla compatibilità dei diversi compositor/DE alla condivisione delle risorse tramite portal.
Ogni compositor/DE implementa il proprio backend per il desktop portal, e diversi backend supportano diverse funzionalità di condivisione: alcuni permettono la condivisione di singole finestre, altri solo dell'intero schermo o di una porzione, altri meno gettonati non ne vogliono ancora sapere.
Conclusioni
Ad oggi, PipeWire è in forte crescita. È già ormai il default nelle distribuzioni più diffuse: in Fedora dalla versione 34, nel 2021, in Ubuntu dalla 22.10, nel 2022. Il progetto è ancora lontano dalle promesse di gestione unificata e trasparente di audio e video, e mancano alcuni pezzi che dovrebbero contraddistinguerlo dalle soluzioni che va a rimpiazzare, come la gestione dei permessi, che nessun session manager ha ancora implementato in maniera stabile. Aggiungendoci il fatto che esiste un solo session manager mantenuto [WirePlumber] e che il supporto al video e alla condivisione schermo è ancora frammentato e dipendente dal compositor, la sua adozione non è così smooth come voleva essere. Per quanto acerbo, sta ricevendo però costante attenzione e sviluppo, attestandosi come uno di quei rari casi nel mondo Linux in cui un progetto aspira a diventare lo standard, e magari ci riesce pure, invece di diventare l'ennesimo tassello di un puzzle sempre più grande e di cui non si vede più l'immagine completa.
Risorse
- immagine per copertina https://pipewire.org/assets/pipewire.gif (dominio pubblico CC0), già upscaled sulla homepage del progetto
- spiegone audio su Linux che include anche cenni a PCM, resampling, mixing e altro https://www.reddit.com/r/headphones/comments/k8nom8/a_complete_guide_of_and_debunking_of_audio_on/
- pagina test di Mozilla per camera/schermo https://mozilla.github.io/webrtc-landing/gum_test.html
- Wiki di Arch sui desktop-portal, con tabella di compatibilità https://wiki.archlinux.org/title/XDG_Desktop_Portal
Referenze
- https://docs.pipewire.org/ [documentazione ufficiale]
- https://bootlin.com/blog/an-introduction-to-pipewire/ [sunto di parti della documentazione ufficiale]
- https://fedoramagazine.org/introduction-to-pipewire/ [comandi utili per esempi]
- https://jgrulich.cz/2023/05/29/pipewire-camera-support-in-firefox/ [supporto pw in Firefox by chi lo ha implementato]














