Prologue
--------
If you were waiting for the next installment of the TTY office series,
I got a bit sidetracked.
A few weeks ago, Rogers Communications shut down six radio stations
across Canada. Two were in Vancouver. One was a 24-hour news station
(CKWX) and the other was a sports talk station (CISL, better known as
"Sportsnet 650"). These were AM stations. I tended to listen online
during the day and over the air at night, due to my distance from the
transmitters. After they shut down, I decided to set up my own
streaming sports talk station.
My setup makes use of ezstream and icecast2, both of which are
available in the Debian repositories.
Here's a flowchart to outline how these pieces of software function
together:
ezstream icecast2 various
(streaming -----> (streaming -----> receiving
client) server) clients
The streaming client sends the stream to the streaming server, which
then relays it to receiving clients. The streaming client and server
may be on different machines. Mine are. I have ezstream installed on
my laptop at home. It sends the stream to icecast2 on my vps in
Toronto. For clients, I use Transistor to listen on my Android phone
and gst123 to listen on my laptop.
Configuration
-------------
Here are my configuration files for ezstream and icecast2:
I called my ezstream configuration file ez-config.xml. It can be
placed in any convenient directory. I've used "password_one" as the
password in this file so you can connect it to the correct password in
the icecast.xml file. If you have icecast on the same device as
ezstream, substitute 127.0.0.1 for your.server.com in this file.
your.server.com8000password_one/stream.oggOggAM 750 CSPOBC's Sports Talk Connection!stdin
The icecast.xml configuration file is at /etc/icecast2/icecast.xml.
It's a long file and you only need to make a few changes. Here are the
changed sections of mine, separated by ellipses. In this file,
"password_one" should be the same password you used in the ezstream
configuration file. You use "password_three" to log in as
administrator in icecast's web portal, which you'll find by visiting
"your.server.com:8000" with your web browser. Unless you're dealing
with multiple icecast instances, you'll probably never use
"password_two". These particular passwords are used for illustrative
purposes. Change them to something more difficult to guess!
password_onepassword_twoArthurCarlsonpassword_three
...
your.server.com8000
...
/stream.ogg14400/static.ogg11
The fallback mount, static.ogg, is actually just a file placed in the
/usr/share/icecast2/web/ directory. It plays a one-minute recording of
radio static when the station is offline (when I'm not relaying any
streams through ezstream). Each time icecast2 concludes playing the
file, it will check to see if I'm streaming again and will attempt to
switch listeners over to the main stream ("stream.ogg"). The
switch-over only seems to work with well-behaved clients. For the
fallback stream, you can use any file you like, but make it short,
since it doesn't appear to try to hand clients back to the main stream
until the file finishes playing.
If you read up further about icecast2, you'll find that you can
actually set up multiple "mounts" (to which clients can send streams)
and prioritize among them by setting up chains of fallbacks. You can
also use "liquidsoap" to do this kind of thing in a more professional
way, but I haven't tried it.
To start icecast2:
$ systemctl start icecast2.service
To autostart icecast2 each time you start the server:
$ systemctl enable icecast2.service
You will listen to your station at: your.server.com:8000/stream.ogg
But first, you have to send it a stream.
Streaming "Recipes"
-------------------
Okay, here comes the fun part! Here are some one-line recipes you can
use to stream from various sources to your station. In several of
these recipes, I use yt-dlp to stream from YouTube and pipe the output
through ffmpeg and ezstream. Since I'm the only person using this
station, I'm not really concerned about copyright.
It's best to download the yt-dlp binary directly from their releases
on Github (https://github.com/yt-dlp/yt-dlp). Place it in a directory
in your path. I've renamed it from "yt-dlp_linux" to "yt-dlp". If you
leave it as yt-dlp_linux, just substitute that name in the commands
below. Once installed, you can update yt-dlp to the latest version by
typing "yt-dlp -U" without the quotation marks.
You'll also need ffmpeg. It's in the Debian repos.
Here are the recipes:
# Send a local file to your station at 64k (stereo):
ffmpeg -i path/to/file.mp3 -b:a 64k -f opus - | ezstream -c /path/to/ez-config.xml
# Relay another audio or video stream to your station as 16k mono audio:
ffmpeg -i https://rogers-hls.leanstream.co/rogers/tor590.stream/playlist.m3u8 -b:a 16k -f opus -ac 1 - | ezstream -c /path/to/ez-config.xml
# Relay a YouTube stream to your station at 64k (stereo):
yt-dlp -f wa* https://www.youtube.com/watch?v=6gtQck24Nf8 -o - | ffmpeg -i pipe: -b:a 64k -f opus - | ezstream -c /path/to/ez-config.xml
# Relay and record a YouTube stream simultaneously in 16k mono:
yt-dlp -f wa* https://www.youtube.com/watch?v=DbO0t3c5ukk -o - | ffmpeg -i pipe: -b:a 16k -f opus -ac 1 - | tee testfile.ogg | ezstream -c /path/to/ez-config.xml
# Relay a YouTube livestream to your station at 16k mono (with some
# sound modifications to make it sound like an AM station). See the
# switch explanations below.
timeout 120m yt-dlp --wait-for-video 30-60 -f wa* https://www.youtube.com/@SekeresPrice/live -o - | ffmpeg -i pipe: -b:a 16k -f opus -af "asubboost=boost=2.0:cutoff=60,equalizer=f=1500:width_type=h:width=200:g=1.2,lowpass=f=6000,volume=0.8" -ac 1 - | ezstream -c /path/to/ez-config.xml
What do the various switches and commands mean?
ffmpeg
-i input file (or stream or pipe)
-b:a 64k bitrate audio (set to 64k)
-f opus encode audio as opus (the format used inside ogg containers)
-ac 1 audio channel 1 (mono rather than stereo)
- output to stdout, so you can pipe it to the next application
-af audio filter
yt-dlp
--wait-for-video 30-60 wait for the livestream to start. Wait at least 30 seconds and at most 60 seconds between retries.
-f wa* get the worst quality audio-only (preferred) or audio-video stream from YouTube
-o - output to stdout
ezstream
-c location of configuration file
tee adds a "tee" into your pipe:
testfile.ogg
^
yt-dlp > ffmpeg > ezstream
timeout 120m kill the command if it's still running after 120 minutes
Here's one final recipe. It's a bit more complicated to set up. This
one relays the sound from your device (any application playing sound
from any source) to ezstream and on to your icecast station. So you
can just play something using an audio player, your browser, etc., and
you'll hear the sound on your icecast station.[1]
ffmpeg -f pulse -i