Skip to main content

Pipewire

Introduzione

Ricordate il mio video su "Wayland, la prossima rivoluzione su Linux"? Ecco, nel frattempo la rivoluzione è già avvenuta, e la maggior parte di voi avrà già (consciamente o meno) abbandonato Xorg.

Ma 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

diagramma OSS diagramma 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

diagramma ALSA diagramma 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-tools e alsa-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.conf e scriverci dentro
pcm.!default {
type plug
slave.pcm hw
}
  • provare a eseguire aplay prova.wav in 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

diagramma PulseAudio diagramma pa

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.

Demo: interfaccia grafica di `pavucontrol` per il controllo del volume dei singoli processi

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

Clip in cui si mostra la stessa operazione fatta con pactl ma col punta e clicca

  • 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-alsa e helvum o qpwgraph
  • mostrare il grafo di PipeWire aprendo Helvum o qpwgraph

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...

Clip

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

qpwgraph jack

...la vedremo comparire come nodo JACK. Ci sono anche dispositivi video, ad esempio la mia webcam, ma su questo ci torniamo dopo.

Come funziona

diagramma pw diagramma pw

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

Clip

  • installare pipewire pipewire-alsa pipewire-pulse pipewire-jack
  • mostrare l'output di pactl list sources short e/o pactl 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

Clip

  • eseguire alsamixer e se necessario switchare scheda audio con F6, fino a mostrare una scheda chiamata PipeWire

alsamixer

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.

diagramma entità pw diagramma entità pw

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

Clip

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

qpwgraph camera

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:config in FF e impostare la variabile media.webrtc.camera.allow-pipewire a true
  • 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

ff camera graph

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

Clip

  • mostrare come si imposta la variabile media.webrtc.camera.allow-pipewire a true in about: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

Clip

  • 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.

portal backends

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]