boatbod
Member
I must have too much time on my hands as I spent the better part of today experimenting with liquidsoap to see if there was a way to set up a streaming server without having to pass the audio through the temperamental alsa subsystem prior to streaming it. The good news is I believe I have found an answer, the bad news is that it's rather complicated, so I recommend only going this route if you have some familiarity with linux.
Prerequisites:
Configuration:
Once you have liquidsoap and the latest op25 changes, you will find a new file called "op25.liq" sitting in the 'apps' directory. If you want to set up a connection to an icecast server you will eventually need to edit this file, however it defaults to capturing the audio data from op25 and simply playing it on the local default sound hardware. I recommend you try it as-is to check that things are working properly.
Setting up op25 to play to liquidsoap requires different command line options as follows:
Remove: "-U" (and "-O" if it exists)
Add: "-V -w"
Run rx.py in a terminal window like you normally would, but don't expect any audio until you also start the liquidsoap op25.liq script (in a separate terminal window).
At this point local audio should be playing properly, much like it would have done before. From here you can edit the appropriate mount point, host, port and password in the op25.liq script and send the stream to icecast (i.e. broadcastify, your local server, ...). The nice part of this is you can decide whether to play the stream locally or not simply by commenting/uncommenting the "out()" statement.
Known issues
Shutting down the stream is as simple as hitting "Ctrl-C" in the op25.liq terminal window. There is a small problem with thread termination in the helper application audio.py which causes delayed shutdown of some background processes. Normally this isn't an issue unless you restart the script immediately, at which point you may see "socket.error: [Errno 98] Address already in use" messages.
Unintended consequences
I had to modify audio.py so that it can collect UDP samples and send them to stdout. As a result it is now entirely possible to do the following, much like nc | aplay:
The consequence of this is that it is now possible to pipe raw audio output from op25 direct to any application that expects to see 2 channel, 8000Hz 16 bit LE samples.
Secondly, I would point out that it's entirely possible to split op25 and liquidsoap onto separate machines simply by sending the udp samples over the network using the "-W" wireshark host option.
Graham
Prerequisites:
Code:
sudo apt-get install liquidsoap liquidsoap-plugin-all
cd ~/op25
git pull
cd ~/op25/op25/gr-op25_repeater/apps
Configuration:
Once you have liquidsoap and the latest op25 changes, you will find a new file called "op25.liq" sitting in the 'apps' directory. If you want to set up a connection to an icecast server you will eventually need to edit this file, however it defaults to capturing the audio data from op25 and simply playing it on the local default sound hardware. I recommend you try it as-is to check that things are working properly.
Setting up op25 to play to liquidsoap requires different command line options as follows:
Remove: "-U" (and "-O" if it exists)
Add: "-V -w"
Code:
./rx.py --nocrypt --args "rtl=0" --gains 'lna:36' -S 960000 -q 0 -d -100 -v 1 -2 -T trunk-test.tsv -V -w 2> stderr.2
Run rx.py in a terminal window like you normally would, but don't expect any audio until you also start the liquidsoap op25.liq script (in a separate terminal window).
Code:
gnorbury@yoga2 ~/op25/op25/gr-op25_repeater/apps $ ./op25.liq
2019/01/01 22:12:48 >>> LOG START
2019/01/01 22:12:48 [protocols.external:3] Found "/usr/bin/wget".
2019/01/01 22:12:48 [main:3] Liquidsoap 1.1.1
2019/01/01 22:12:48 [main:3] Using: graphics=[distributed with Ocaml] pcre=7.0.4 dtools=0.3.1 duppy=0.5.1 duppy.syntax=0.5.1 cry=0.2.2 mm=0.2.1 xmlplaylist=0.1.3 lastfm=0.3.0 ogg=0.4.5 vorbis=0.6.1 opus=0.1.0 speex=0.2.0 mad=0.4.4 flac=0.1.1 flac.ogg=0.1.1 dynlink=[distributed with Ocaml] lame=0.3.2 shine=0.2.0 gstreamer=0.2.0 frei0r=0.1.0 voaacenc=0.1.0 theora=0.3.0 schroedinger=0.1.0 gavl=0.1.5 bjack=0.1.4 alsa=0.2.1 ao=0.2.0 samplerate=0.1.2 taglib=0.3.1 magic=0.7.3 camomile=0.8.4 inotify=1.0 faad=0.3.2 soundtouch=0.1.7 portaudio=0.2.0 pulseaudio=0.1.2 ladspa=0.1.4 dssi=0.1.1 sdl=0.9.1 camlimages=4.2.0 lo=0.1.0 yojson=1.2.3 gd=1.0a5
2019/01/01 22:12:48 [dynamic.loader:3] Could not find dynamic module for fdkaac encoder.
2019/01/01 22:12:48 [dynamic.loader:3] Could not find dynamic module for aacplus encoder.
2019/01/01 22:12:48 [dynamic.loader:2] Loaded plugin file /usr/lib/liquidsoap/1.1.1/plugins/pulseaudio.cmxs.
2019/01/01 22:12:48 [dynamic.loader:2] Loaded plugin file /usr/lib/liquidsoap/1.1.1/plugins/alsa.cmxs.
2019/01/01 22:12:48 [dynamic.loader:2] Loaded plugin file /usr/lib/liquidsoap/1.1.1/plugins/lame.cmxs.
2019/01/01 22:12:48 [dynamic.loader:2] Loaded plugin file /usr/lib/liquidsoap/1.1.1/plugins/cry.cmxs.
audio device: stdout
At this point local audio should be playing properly, much like it would have done before. From here you can edit the appropriate mount point, host, port and password in the op25.liq script and send the stream to icecast (i.e. broadcastify, your local server, ...). The nice part of this is you can decide whether to play the stream locally or not simply by commenting/uncommenting the "out()" statement.
Code:
#!/usr/bin/liquidsoap
# Example liquidsoap streaming from op25 to icecast
# (c) 2019, gnorbury@bondcar.com
#
set("log.stdout", true)
set("log.file", false)
set("log.level", 1)
# Make the native sample rate compatible with op25
set("frame.audio.samplerate", 8000)
input = mksafe(input.external(buffer=0.02, channels=2, samplerate=8000, restart_on_error=false, "./audio.py -s"))
# LOCAL AUDIO
# Uncomment the line below to enable local sound
#
out(input)
# ICECAST STREAMING
# Uncomment to enable output to an icecast server
# Change the "host", "password", and "mount" strings appropriately first!
#
#output.icecast(%mp3(bitrate=16, samplerate=22050, stereo=false), fallible=false, icy_metadata="false", host="localhost", port=8000, mount="mountpoint", password="hackme", mean(input))
Known issues
Shutting down the stream is as simple as hitting "Ctrl-C" in the op25.liq terminal window. There is a small problem with thread termination in the helper application audio.py which causes delayed shutdown of some background processes. Normally this isn't an issue unless you restart the script immediately, at which point you may see "socket.error: [Errno 98] Address already in use" messages.
Unintended consequences
I had to modify audio.py so that it can collect UDP samples and send them to stdout. As a result it is now entirely possible to do the following, much like nc | aplay:
Code:
./audio.py -s | aplay -c 2 -r 8000 -f S16_LE
Secondly, I would point out that it's entirely possible to split op25 and liquidsoap onto separate machines simply by sending the udp samples over the network using the "-W" wireshark host option.
Graham