Piero V.

Libavformat + Libavcodec + OpenAL = AVStream

Oggi volevo parlarvi del mio ultimo programma, AVStream, uno streamer di audio che si basa su Libavformat, Libavcodec e OpenAL.

In pratica per un mio altro programma mi serviva la possibilità di poter far sentire dell’audio 3D. Così ho scoperto che OpenAL è la cosa migliore, perché, nata nel 1999, è si prefigge di essere l’OpenGL dell’audio, ovvero una specifica di API da usare per fare lo stream audio.

Il progetto è stato portato avanti da Creative, che poi però l’ha abbandonato. Attualmente solo Apple la include (anche se in una directory sbagliata) ufficialmente; una buona implementazione per gli altri sistemi, rilasciata sotto LGPL, è OpenAL Soft. In pratica come API non cambia niente, cioè sono tutte le funzioni della specifica OpenAL 1.1, solo che è tenuta aggiornata e perciò funziona con molte configurazioni diverse. Io l’ho provata su Linux con PulseAudio, OSS e ALSA.

Inizialmente volevo usare il codec libero Vorbis, però il tutorial che avevo trovato voleva far caricare in RAM tutto l’audio decompresso, quindi riprodurlo. Era una cosa inaccettabile però per i miei scopi: potrei aver bisogno di molti suoni, non potrei tenerli tutti in RAM, neanche se pesassero poco.

Così ho trovato un tutorial che spiegava come integrare uno stream Mplayer con OpenAL. Il che non è male, soprattutto perché Mplayer dà come output un file wav. Tolta la parte della pipe e del processo figlio sarebbe stata una cavolata avere uno stream di un file WAV. Così è stato. Sono riuscito a ottenere uno stream WAV che mantenesse la RAM a un buon livello: 640KiB circa sulla mia macchina.

In pratica il programma si basa su tre buffer che vengono riprodotti alternativamente e mentre uno viene riprodotto, gli altri due vengono caricati. Una volta messo in coda il buffer, si può utilizzare l’area di memoria per caricarne un altro, ergo non sale a dismisura la RAM. Con molta facilità sono riuscito a mettere ciò in un thread separato e a usare le funzionalità del suono 3D. Mi raccomando, occorre un file mono, altrimenti non funzioneranno.

Il nuovo problema era che però il WAV è un formato little endian e non volevo fare discriminazione di architetture, quindi, con un chroot MIPS, ho cercato di drizzare i dati. Non essendoci riuscito ho cercato un modo alternativo per farlo.

Ho visto che altri prima di me avevano usato libavcodec. Questa è una libreria che consente di decodificare (o codificare, ma non è questo il caso) file compressi in PCM da mandare in stream. Dopo essermi scervellato e dopo che neanche gli esempi provvisti con libavcodec ho cercato meglio e ho scoperto che serve anche l’ausilio di libavformat, libreria che fa il demux dei file, cioè separa i vari stream. Ho pensato che con il peso di queste due librerie risparmiavo peso sui WAV e fatica nel “drizzarli”.

Ed eccoci finalmente arrivati ad AVStream.

In pratica prima prendo il file che viene passato come argomento al programma, quindi avvio OpenAL, dopodiché con avformat prendo il formato dell’audio. Quindi comincio a bufferizzarlo con libavcodec, con un quasi integrale copia & incolla dell’esempio.

Sono riuscito a fare andare abbastanza formati, perciò sono soddisfatto. Tuttavia non so perché, ma con vorbis mi dà un errore, cioè “Extradata missing”. Non so cosa farci.

In più ho altri “problemini”: l’uso di RAM è costante, ma il solo programma secco, con nessun file ancora aperto, nel mio computer occupa 7-8MiB di RAM. Il secondo problema è che ho dovuto usare la funzione non standard di C usleep: per tenere basso il carico sulla CPU devo ridurre il ciclo while che controlla se ci sono buffer che sono già stati usati. Attualmente “dorme” per 1ms, però sono arrivato anche a tempi di 15ms, portando il carico dell CPU a un 2%-4%. Salendo comincia ad andare a scatti, perciò direi che 1ms va più che bene. Purtroppo non esiste un sistema che si basa su delle callback. Un altro problema è che la funzione non è in grado di capire quando si è verificato un errore nel leggere il frame o se sono effettivamente finiti; un altro ancora è che con i video che ho provato non sono riuscito a riprodurre solo l’audio.

Insomma, non è ancora un qualcosa di finito; è più che altro un proof of concept. È ancora pieno di informazioni di debug, e andrebbe sistemato. Poi mi manca anche l’OGG Vorbis, cosa che è molto importante per me. Perciò cercherò di far andare le API del vorbis con uno streaming. Mi è servito molto fare questo programmino.

Come sviluppi futuri vedo l’implementazione dell’ogg sicuramente, una riscrittura in C++, magari con l’uso di Boost al posto di usleep. Poi mi piacerebbe veramente implementare l’apertura dei file tramite callback: con vorbis è abbastanza banale, mentre con avformat mi sembra più elaborata, però è importante perché penso che mi potrebbe permettere di comprimere i file in un archivio zip, per esempio.

Comunque se ce la faccio con vorbis non andrò più avanti con avcodec.

Se volete qui potete scaricare il sorgente di AVStream. Troverete le indicazioni sul copyright e licenza direttamente dentro il file. Se fosse stato per me avrei rilasciato con il pubblico dominio, ma la LGPL è fatta diversamente purtroppo.