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 |