-- Leo's gemini proxy

-- Connecting to perso.pw:1965...

-- Connected

-- Sending request

-- Meta line: 20 text/gemini;

Stream your OpenBSD desktop audio to other devices


Author: Solène

Date: 05 May 2023

Tags: openbsd streaming icecast hacking


Comment on Mastodon


Introduction


Hi, back on OpenBSD desktop, I miss being able to use my bluetooth headphones (especially the Shokz ones that allow me to listen to music without anything on my ears).


Unfortunately, OpenBSD doesn't have a bluetooth stack, but I have a smartphone (and a few other computers), so why not stream my desktop sound to another device with bluetooh? Let's see what we can do!


I'll often refer to the "monitor" input source, which is the name of an input that provides "what you hear" from your computer.


While it would be easy to just allow a remote device to play music files, I want to stream the computer's monitor input, so it could be litteraly anything, and not just music files.


This method can be used on any Linux distribution, and certainly on other BSDs, but I will only cover OpenBSD.


The different solutions


Icecast


One simple setup is to use icecast, the program used by most web radios, and ices, a companion program to icecast, in order to stream your monitor input to the network.


The pros:


it works with anything that can read OGG from the network (any serious audio client or web browser can do this)

it's easy to set up

you can have multiple clients at once

secure (icecast is in a chroot, and other components are sending data or playing music)


The cons:


there is a ~10s delay, which prevents you from watching a video on your computer and listening the audio from another device (you could still set 10s offset, but it's not constant)

reencoding happens, which can slightly reduce the sound quality (if you are able to tell the difference)


Sndiod


The default sound server in OpenBSD, namely sndiod, supports network streaming!


Too bad, if you want to use Bluetooth as an output, you would have to run sndiod on Linux (which is perfectly fine), but you can't use Bluetooth with sndiod, even on Linux.


So, no sndiod. Between two OpenBSD, or OpenBSD and Linux, it works perfectly well without latency, and it's a super simple setup, but as Bluetooth can't be used, I won't cover this setup.


The pros:


easy to setup

works fine


The cons:


no android support


Pulseaudio


This sound server is available as a port on OpenBSD, and has two streaming modes: native-protocol-tcp and RTP, the former is exchanging pulseaudio internal protocol from one server to another which isn't ideal and prone to problems over a bad network, the latter being more efficient and resilient.


However, the RTP sender doesn't work on OpenBSD, and I have no interest in finding out why (the bug doesn't seem to be straightforward), but the native protocol works just fine.


The pros:


almost no latency (may depend of the network and remote hardware)

easy to setup


Snapcast


Snapcast is an amazing piece of software that you can use to broadcast your audio toward multiple other client (using snapcast or a web page) with the twist that the audio will be synchronized on each client, allowing a multi room setup at no cost.


Unfortunately, I've not been able to build it on OpenBSD :(


The pros:


multi room setup with synchronized clients

compatible with almost any client able to display an HTML5 page


The cons:


playback latency

not so easy to setup


Setup


Here are the instructions to setup different solutions.


Pulseaudio


Client setup (OpenBSD)


On the local OpenBSD, you need to install `pulseaudio` and `ffmpeg` packages.


You also need to set sndiod flags, using `rcctl set sndiod flags -s default -m play,mon -s mon`, this will allow you to use the monitor input through the device `snd/0.mon`.


Now, when you want to stream your monitor to a remote pulseaudio, run this command in your terminal:


ffmpeg -f sndio -i snd/0.mon -ar 44100 -f s16le - | pacat -s 10.42.42.199 --raw --process-time-msec=30 --latency-msec=30

The command is composed of two parts:


ffmpeg reading the monitor input and sending it to the pipe

pacat (pulseaudio cat) relaying the pipe input to the pulseaudio server 10.42.42.199, with some tweaks to reduce the latency


Server setup (the device with bluetooth)


The setup is easy, but note that this doesn't involve any authentication or encryption, so please use this on trusted network, or through a VPN.


On a system with pulseaudio, type:


pacmd load-module module-native-protocol-tcp auth-anonymous=1 auth-ip-acl=192.168.1.0/24

This will load the module accepting network connections, the `auth-anonymous` option is there to simplify connection to the server, otherwise you would have to share the pulseaudio cookie between computers, which I recommend doing but on a smartphone this can be really cumbersome to do, and out of scope here.


The other option is pretty obvious, just give a list of IPs you want to allow to connect to the server.


If you want the changes to be persistent, edit `/etc/pulse/default.pa` to add the line `load-module module-native-protocol-tcp auth-anonymous=1 auth-ip-acl=192.168.1.0/24`.


On Android, you can install pulseaudio using Termux (available on f-droid), using the commands:


pkg install pulseaudio
pulseaudio --start --exit-idle-time=3600
pacmd load-module module-native-protocol-tcp auth-anonymous=1 auth-ip-acl=192.168.1.0/24

There is a project named PulseDroid, the original project has been unmaintained for 13 years, but someone took it back quite recently, unfortunately no APK are provided, and I'm still trying to build it to try, it should provide an easier user experience to run pulseaudio on Android.


PulseDroid gitlab repository


Icecast


Using icecast, you will have to setup an icecast server, and locally use ices2 client to broadcast your monitor input. Then, any client can play the stream URL.


Install the component using:


pkg_add icecast ices--%ices2

Server part


As suggested by the file `/usr/local/share/doc/pkg-readmes/icecast`, run the following commands to populate icecast's chroot:


cp -p /etc/{hosts,localtime,resolv.conf} /var/icecast/etc
cp -p /usr/share/misc/mime.types /var/icecast/etc

Edit `/var/icecast/icecast.xml`:


in the `<authentication>` node, change all the passwords. The only one you will need is the `source` password used to send the audio to icecast, but set all other passwords to something random.

in the `<hostname>` node, set the IP or hostname of the computer with icecast.

add a `<bind-address>` node to `<listen-socket>` using the example for 127.0.0.1, but use the IP of the icecast server, this will allow other to connect.


Keep in mind this is the bare minimum for a working setup, if you want to open it to the wide Internet, I'd strongly recommend reading icecast documentation before. Using a VPN may be wiser if it's only for private use.


We can start icecast and set it to start at boot:


rcctl enable icecast
rcctl start icecast

Broadcast part


Then, to configure ices2, copy the file `/usr/local/share/examples/ices2/ices-sndio.xml` somewhere you feel comfortable for storing user configuration files. The example file is an almost working template to send sndio sources to icecast.


Edit the file, under the `<instance>` node:


modify `<hostname>` with the hostname used in icecast.

modify `<password>` with the source password defined earlier.

modify `<mount>` to something ending in `.ogg` of your liking, this will be the filename in the URL (can be `/stream.ogg` if you are out of ideas).

set `<yp>` to 0, otherwise the stream will appear on the icecast status page (you may want to have it displayed though).


Now, search for `<channels>` and set it to 2 because we want to broadcast stereo sound, and set `<downmix>` to 0 because we don't need to merge both channels into a mono output. (If those values aren't in sync, you will have funny results =D)


When you want to broadcast, run the command:


env AUDIORECDEVICE=snd/0.mon ices2 ices-sndio.xml

With any device, open the url `http://<hostname>:8000/file.ogg` with `file.ogg` being what you've put in `<mount>` earlier. And voilà, you have a working local audio streaming!


Limitations


Of course, the setup isn't ideal, you can't use your headset microphone or buttons (using MPRIS protocol).


Conclusion


With these two setup, you have a choice for occasionnaly streaming your audio to another device, which may have bluetooth support or something making it interesting enough to go through the setup.


I'm personally happy to be able to use bluetooth headphones through my smartphone to listen to my OpenBSD desktop sound.


Going further


If you want to directly attach bluetooth headphones to your OpenBSD, you can buy an USB dongle that will pair to the headphones and appear as a sound card to OpenBSD.


jcs@ article about Bluetooth audio on OpenBSD

-- Response ended

-- Page fetched on Sun May 5 22:21:10 2024