| Title: Stream your OpenBSD desktop audio to other devices | |
| Author: Solène | |
| Date: 05 May 2023 | |
| Tags: openbsd streaming icecast hacking | |
| Description: In this article, you will learn how to set up icecast or | |
| pulseaudio to broadcast your OpenBSD system audio to a remote system | |
| (and make use of bluetooth) | |
| # 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… | |
| ``` | |
| 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.1… | |
| ``` | |
| 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.1… | |
| ``` | |
| 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: | |
| ```shell | |
| 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: | |
| ```shell | |
| 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 |