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 --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.
       
 (HTM) 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.
       
 (HTM) jcs@ article about Bluetooth audio on OpenBSD